微信App支付源码&坑注释

微信App支付源码&坑注释

部分的代码,因为代码是copy的我自己代码,然后再进行部分的编辑和注释,所以在使用的时候有可能有欠缺,不过整体来说,应该不影响使用的.如果有疑问,可以留言.在微信App支付开发中,流程大致分为:1.调用统一下单接口,生成预支付订单,并且进行二次加签,返回给客户端进行调起微信.2:回调逻辑.遇见问题,多多百度,多尝试.

Controller:

  /**
     * @Description: 订单微信支付
     */
    @PostMapping(value = "/payRequest")
    @ApiOperation("统一下单微信支付")
    public Result payRequest(@RequestBody MallOrderPayDto mallOrderPayDto, HttpServletRequest request){
        Map<String, String> map = new HashMap<String, String>();
        try {
            map =  MallTenPayService.payRequest(mallOrderPayDto);
            return super.success(map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.error();
    }


    /**
     * @Description: 订单微信支付回调
     */
    @PostMapping(value = "/tenPayOrderCallBack")
    @ApiOperation("微信支付回调")
    public Map<String, String> tenPayOrderCallBack(HttpServletRequest request){
        Map<String, String> map = new HashMap<String, String>();
        try {
            TenPayVO tenPayVO = MallTenPayService.tenPayCallBack(request);
            friendsMallTenPayService.tenPayOrderCallBack(tenPayVO);
            map.put("return_code", "SUCCESS");
            map.put("return_msg", "OK");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

Service::

public interface MallTenPayService {
    public Map<String, String> payRequest(OrderPayDto orderPayDto) throws Exception ;
    public TenPayVO tenPayCallBack(HttpServletRequest request) throws Exception;
    public void tenPayOrderCallBack(TenPayVO tenPayVO) throws Exception;
}

ServiceImpl:

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class FriendsMallTenPayServiceImpl implements FriendsMallTenPayService {

    //订单支付微信回调地址
    public static String TENPAY_ORDER_CALLBACK = "http://127.0.0.1:8080/api/tenpay/tenPayOrderCallBack";

    @Override
    public Map<String, String> payRequest(MallOrderPayDto mallOrderPayDto) throws Exception {
		
		/**
		* 本处两个map,在代码上是不应该出现的,但是在调试的时候,遇见了坑,为了代码更
		* 清晰一点,就new 了两个Map,一个是加签使用,一个是返回给客户端使用.
		* newMap:返回给服务端的
		* newData:二次加签的
		**/
        Map<String,String> newMap  = Maps.newHashMap();

        Map<String, String> newData = new HashMap<String, String>();
        
        TenpayConfig tenpayConfig = new TenpayConfig();
        //AppId:在微信开发平台就可以获取的
        tenpayConfig.setAppId("wx111111111111");
    
        /**
        * Key:特别声明下,此处是个坑,官方文档也没有具体的描述,很多第一次使用的同学
        * 会直接使用开发平台的key,然后加签失败.该key需要登录商户平台进行设置的.
        * 一定一定要登录商户平台去设置哦,不是开发平台.
        **/
        tenpayConfig.setKey("XJEJWchdjqfjlwjfwpq13ndo");
        //商户号,开发平台可以获取
        tenpayConfig.setMchId("3213131212");
        //固定写死,不要问为什么,官方要求的
        tenpayConfig.setPackageValue("Sign=WXPay");

        MyPayConfig myPayConfig = new MyPayConfig(tenpayConfig);
        // WXPay是目前可以使用的官方jar.
        WXPay wxPay = new WXPay(myPayConfig);
        
        log.info("封装wxpay支付请求参数==================");
      
		// 开始第一次加签
        Map<String, String> paramsMap = new HashMap<>();
        //随机字符串
        String nonce_str =  WXPayUtil.generateNonceStr();
        paramsMap.put("nonce_str", nonce_str);
        //描述
        paramsMap.put("body", "支付描述");
        //商户订单号(支付编号)
        //支付编号
        String orderNum = PayUtils.getOrderNumber();
        paramsMap.put("out_trade_no", orderNum);
        //支付金额,金额单位为 分
        double price = monery.doubleValue();
        int totalFee = (int) (price * 100);
        paramsMap.put("total_fee", String.valueOf(totalFee));
        //回调地址
        paramsMap.put("notify_url", TENPAY_ORDER_CALLBACK);
        //交易类型
        paramsMap.put("trade_type", "APP");
        //用户端ip
        String spbillCreateIp = "";
        InetAddress inetAddress = InetAddress.getLocalHost();
        if (inetAddress != null) {
            spbillCreateIp = inetAddress.getHostAddress();
        }
        paramsMap.put("spbill_create_ip", spbillCreateIp);
		
        Map<String,String> resp = wxPay.unifiedOrder(paramsMap);

        log.info("reponseMap:{}",resp);

        if (resp.get("result_code") != null && "SUCCESS".equals(resp.get("result_code"))) {

            String prepayid = resp.get("prepay_id");

            nonce_str = resp.get("nonce_str");
            log.info("nonce_str:{}",nonce_str);
			
			// 二次加签开始
			
			/**
			* 	巨坑描述: 微信的二次加签是真的超级坑的,文档的描述很简单,我加签要那
			*  些那些参数,具体怎么要,你猜,参数key? 你继续猜.我猜了..不,你猜错了.
			*  官方文档上写的是驼峰要求的,实际编码却需要这种全小写的,也是坑
			*  死人不偿命的那种.这么大公司,缺这维护文档的五毛钱吗?
			* 如果从上看下来的,就会发现不同.一次加签,使用的是下划线,
			*  二次加签必须使用全小写的key,要不也可以加签成功,并且使用sign验证工具
			*  检查也是可以通过验证的.这就有点麻爪了.本人就被此处的问题,卡了6个小
			*  时,一脸懵逼.很自信的对客户端的同学说,我的sign没问题,我在官网都验证了
			*  ,但是客户端同学就是调不起来微信,一直返回-1.
			*  另外,需要注意的一个点就是,最终给客户端返回的Sign不能是第一次加签
			*  返回的sign值,是必须进行二次加签后的Sign.
			*  
			**/
			//AppId和上面的一致
            newData.put("appid", tenpayConfig.getAppId());
            //订单号和上面的一致
            newData.put("noncestr", nonce_str);
            //还是要写死
            newData.put("package","Sign=WXPay");
            //商户号和上面的一致
            newData.put("partnerid",tenpayConfig.getMchId());
            //这个是微信返回的预支付订单号
            newData.put("prepayid",prepayid );
            //新的时间戳:要10位的长度.切记
            newData.put("timestamp", System.currentTimeMillis() / 1000 + "");
            log.info("newData:{}",newData);
			
			//和上面的签名方式一样,也是调用微信支付的一个工具类直接生成的
            String wxUyilSign = WXPayUtil.generateSignature(newData, tenpayConfig.getKey(), WXPayConstants.SignType.MD5);
            log.info("paySign:{}",wxUyilSign);
			//开始封装给客户端返回的结果
            newMap.put("appId", tenpayConfig.getAppId());
            newMap.put("partnerId",tenpayConfig.getMchId());
            newMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
            newMap.put("nonceStr", nonce_str);
            newMap.put("prepayId", prepayid);
            newMap.put("package","Sign=WXPay");
            newMap.put("paySign",wxUyilSign);

        } else {
            throw new Exception("签名错误");
        }
  
  
        log.info("weixin 参数封装完成,开始入库流水=================订单号:{}",orderNum);

        return newMap;
    }

    @Override
    public TenPayVO tenPayCallBack(HttpServletRequest request) throws Exception {
        log.info("weixinpay callBack start ==============================");
        InputStream inputStream = request.getInputStream();
        StringBuffer resXml = new StringBuffer();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        String str;
        while ((str = bufferedReader.readLine()) != null) {
            resXml.append(str);
        }
        bufferedReader.close();
        inputStream.close();
        log.info("微信回调报文: " + resXml);
        TenPayVO tenPayVO = this.tenPayCallBackInfo(resXml.toString(), "xml", "");
        return tenPayVO;
    }



    /**
     * @Description: 微信支付回调返回结果
     * @Param: [xml, rootName, rowName]
     */
    private TenPayVO tenPayCallBackInfo(String xml, String rootName, String rowName) throws Exception {
        Map<String, Object> resHashMap = (Map<String, Object>) TenPayUtils.readXml(xml, "xml", "");
        SortedMap<String, Object> resMap = new TreeMap<String, Object>(resHashMap);
        //微信返回状态码
        if (!resMap.get("return_code").equals("SUCCESS")) {
            log.error("微信支付回调连接失败: " + resMap.get("return_msg"));
            throw new Exception("微信支付回调连接失败");
        }
        //业务结果
        if (!resMap.get("result_code").equals("SUCCESS")) {
            log.error("err_code: " + resMap.get("err_code"), "err_code_des: " + resMap.get("err_code_des"));
            throw new Exception(resMap.get("err_code_des").toString());
        }
        TenPayUtils tenPayUtils = new TenPayUtils();
        //校验签名
        String sign = tenPayUtils.createSign(resMap, "UTF-8");
        if (!sign.equals(resMap.get("sign"))) {
            log.error("微信支付回调签名不正确");
            throw new Exception("微信支付回调签名不正确");
        }
        TenPayVO tenPayVO = new TenPayVO();
        //商户订单号
        tenPayVO.setOutTradeNo((String) resMap.get("out_trade_no"));
        //业务结果
        tenPayVO.setResultCode((String) resMap.get("result_code"));
        //签名方式
        tenPayVO.setSignType("ASCII");
        //签名
        tenPayVO.setSign((String) resMap.get("sign"));
        //交易类型
        tenPayVO.setTradeType("APP");
        //交易状态
        tenPayVO.setTradeState((String) resMap.get("trade_state"));
        //商户号
        tenPayVO.setMchId((String) resMap.get("mch_id"));
        //付款银行
        tenPayVO.setBankType((String) resMap.get("bank_type"));
        //交易金额
        BigDecimal totalFee = new BigDecimal((String) resMap.get("total_fee"));
        totalFee = totalFee.divide(new BigDecimal(100));
        tenPayVO.setTotalFee(totalFee);
        //币种
        if (resMap.containsKey("fee_type")) {
            tenPayVO.setFeeType((String) resMap.get("fee_type"));
        }
        //微信支付订单号
        tenPayVO.setTransactionId((String) resMap.get("transaction_id"));
        //支付完成时间
        tenPayVO.setTimeEnd((String) resMap.get("time_end"));
        return tenPayVO;
    }


    /**
     * @Description: 微信支付订单回调
     * @Param: [tenPayVO]
     */
    @Override
    public void tenPayOrderCallBack(TenPayVO tenPayVO) throws Exception {
        if (tenPayVO != null && tenPayVO.getResultCode().equals("SUCCESS") && tenPayVO.getTradeState().equals("SUCCESS")) {
            //根据交易编号加锁,处理高并发
            synchronized (tenPayVO.getOutTradeNo()) {
                log.info("1.微信支付 回调开始============:{}",tenPayVO);
                if (tenPayVO != null && StringUtil.isNotEmpty(tenPayVO.getOutTradeNo())) {
                    synchronized (tenPayVO.getOutTradeNo()) {
                        MallUserRechargeDetail order = friendsMallUserRechargeDetailService.selectByOrderId(tenPayVO.getOutTradeNo());
                        if (order.getStatus().equals(0)) {
                                //交易支付成功
                                log.info("2.支付状态成功:TRADE_SUCCESS");

                                //修改订单状态
                                log.info("9.订单状态修改结果,订单号:{},订单状态:{}",tenPayVO.getOutTradeNo(),orderFlag);
                                //后续的业务逻辑就自己来吧

                        } else {
                            log.info("24.该订单已支付处理,交易编号为: " + tenPayVO.getOutTradeNo());
                        }
                    }
                }
            }
        }
    }
}

实体类:

public class MyPayConfig implements WXPayConfig {
    private TenpayConfig payConfig;
    private byte[] certData;

    public MyPayConfig(TenpayConfig config) throws IOException {
        this.payConfig = config;
//        File file = new File(payConfig.getCertPath());
//        InputStream certStream = new FileInputStream(file);
//        this.certData = new byte[(int) file.length()];
//        certStream.read(this.certData);
//        certStream.close();
    }

    @Override
    public String getAppID() {
        return payConfig.getAppId();
    }

    @Override
    public String getMchID() {
        return payConfig.getMchId();
    }

    @Override
    public String getKey() {
        return payConfig.getKey();
    }

    @Override
    public InputStream getCertStream() {
        return null;
    }

//    @Override
//    public InputStream getCertStream() {
//        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
//        return certBis;
//    }

    @Override
    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }
}
/**
 * 微信支付配置类
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TenpayConfig {
    //appId
    private String appId;
    //商户号
    private String mchId;
    //商户的key(API密匙)
    private String key;
    //API支付请求地址
    private String payUrl;
    //API查询请求地址
    private String queryUrl;
    //Sign=WXPay
    private String packageValue;

}

**依赖:**

<dependency>
		<groupId>com.github.wxpay</groupId>
		<artifactId>wxpay-sdk</artifactId>
		<version>0.0.3</version>
	</dependency>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值