关于微信支付的一些坑

956 阅读3分钟

其实关于微信支付,网上已经有很多的案例.今天主要是介绍下关于微信支付,退款,转账中遇到的那些容易而且极易被忽略的一些坑.至于key,mcid,oppid和证书下载网上已经很全面了 项目依赖

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

配置类:

public class WXConfigUtil implements WXPayConfig {

    private final byte[] certData;

    private String mchId;

    private String key;


    public WXConfigUtil(String certPath, String key, String mchId) throws Exception {
        //从微信商户平台下载的安全证书存放的路径
        File file = new File(certPath);
        InputStream certStream = new FileInputStream(file);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int n;
        while ((n = certStream.read(b)) != -1) {
            bos.write(b, 0, n);
        }
        certStream.close();
        bos.close();
        //将读入的证书加载到cerData(这一步在网上的文章几乎都是没用)
        this.certData = bos.toByteArray();
        this.mchId = mchId;
        this.key = key;
    }

    @Override
    public String getAppID() {
        return WechatConstant.APP_ID;
    }

    @Override
    public String getMchID() {
        return mchId;
    }

    @Override
    public String getKey() {
        return key;
    }

    @Override
    public InputStream getCertStream() {
        //请求时获取证书
        return new ByteArrayInputStream(this.certData);
    }

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

    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }

}

上面的配置其实比较关键的就是证书读取的配置.废话也不多数,下面就是统一的下单接口的处理: 支付:(这里已app支付为例)

  Map<String, String> response;
        try {
            WXConfigUtil wxConfigUtil = new WXConfigUtil(certPath, key, mchid);
            WXPay wxPay = new WXPay(wxConfigUtil);
            Map<String, String> data = new HashMap<>();
            //随机生成商户订单号

            System.out.println("商户订单号------------" + orderNo);

            //商户号
            data.put("mch_id", wxConfigUtil.getMchID());
            //随机字符串
            data.put("nonce_str", WXPayUtil.generateNonceStr());
            //商品描述
            data.put("body", "描述");
            //商品订单号
            data.put("out_trade_no", orderNo);
            // 总金额(单位分)
            data.put("total_fee",100);
            //终端IP
            data.put("spbill_create_ip", "124.xx.xx.xx");
            //回调地址
            data.put("notify_url", notifyUrl);
            //appid
            data.put("appid", wxConfigUtil.getAppID());
            //交易类型
            data.put("trade_type", "APP");
            data.put("attach", wechatVo.getUserId().toString());
            //生成签名
          String   sign = WXPayUtil.generateSignature(data, wxConfigUtil.getKey(), WXPayConstants.SignType.MD5);
            data.put("sign", sign);
            String str = WXPayUtil.mapToXml(data);
            System.out.println("map转xml" + str);
            System.out.println("我给的数据是" + data);
            System.out.println("第一次签名------------------" + sign);
            //使用官方API请求预付订单
            response = wxPay.unifiedOrder(data);
            } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        //返回结果,可以自己封装
        return response;

企业转账到个人:

  Map<String, String> restmap = null;
        try {
            WXConfigUtil wxConfigUtil = new WXConfigUtil(certPath, key, mchid);
            WXPay wxPay = new WXPay(wxConfigUtil);
            Map<String, String> param = new HashMap<String, String>();
            //公众账号appid
            param.put("mch_appid", wxConfigUtil.getAppID());
            //商户号
            param.put("mchid", wxConfigUtil.getMchID());
            //随机字符串
            param.put("nonce_str", WXPayUtil.generateNonceStr());
            //商户订单号
            param.put("partner_trade_no", "");
            //用户openid
            param.put("openid", wechatVo.getOpenId());
            //校验用户姓名选项 OPTION_CHECK
            param.put("check_name", "NO_CHECK");
            param.put("amount","");
            //企业付款描述信息
            param.put("desc", "withdraw");
            //服务器Ip地址
            param.put("spbill_create_ip", "124.xx.xx.xx");
            param.put("sign", WXPayUtil.generateSignature(param, wxConfigUtil.getKey()));
            //携带证书请求
            String restxml = wxPay.requestWithCert("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers", param, 15000, 15000);
            assert restxml != null;
            restmap = WXPayUtil.xmlToMap(restxml);
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        //返回请求结果
        return restmap;

退款:

     HashMap<String, String> data = new HashMap<String, String>();
        String orderNo = "VxRefund_" + System.currentTimeMillis();
        try {
            WXConfigUtil wxConfigUtil = new WXConfigUtil(certPath, key, mchid);
            WXPay wxPay = new WXPay(wxConfigUtil);
            data.put("appid", wxConfigUtil.getAppID());
            data.put("mch_id", wxConfigUtil.getMchID());
            data.put("nonce_str", WXPayUtil.generateNonceStr());
            //微信订单号     流水号____
            data.put("transaction_id", "支付的流水号");
            //商户退款单号 退款单号 ___
            data.put("out_refund_no", orderNo);
            //支付金额,微信支付提交的金额是不能带小数点的,且是以分为单位,这边需要转成字符串类型,否则后面的签名会失败
            data.put("total_fee",  "");
            //退款总金额,订单总金额,单位为分,只能为整数
            data.put("refund_fee", "");
            data.put("op_user_id", wxConfigUtil.getMchID());
            //MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
            String sign = WXPayUtil.generateSignature(data, wxConfigUtil.getKey());
            //生成签名
            data.put("sign", sign);
            //使用携带证书请求
            String s = wxPay.requestWithCert("https://api.mch.weixin.qq.com/secapi/pay/refund", data, 15000, 15000);
            Map<String, String> response = WXPayUtil.xmlToMap(s);
           if ("SUCCESS".equals(returnCode) && returnCode.equals(resultCode)) {
               //todo 自己的业务逻辑
            } else {
                return ResultInfo.error("退款失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResultInfo.successful("退款成功");

统一回调:

 StringBuilder sb = new StringBuilder();
        try {
            //在获取wx回调数据
            BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
            String line = null;
            new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //sb为微信返回的xml
        String notityXml = sb.toString();

        System.out.println("======================微信支付异步结果逻辑处理开始=================================");
        WXConfigUtil config = null;
        try {
            config = new WXConfigUtil(certPath, key, mchid);
        } catch (Exception e) {
            e.printStackTrace();
        }
        WXPay wxpay = new WXPay(config);
        String xmlBack = "";
        Map<String, String> notifyMap = null;
        try {
            // 调用官方SDK转换成map类型数据
            notifyMap = WXPayUtil.xmlToMap(notityXml);
            System.out.println("返回的map----------------" + notifyMap);
            //验证签名是否有效,有效则进一步处理
            System.out.println("返回的错误代码--------" + notifyMap.get("err_code") + "返回的错误信息--------" + notifyMap.get("err_code_des"));
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                //状态
                String returnCode = notifyMap.get("return_code");
                //商户订单号
                String outTradeNo = notifyMap.get("out_trade_no");
                String userId = notifyMap.get("attach");
                if ("SUCCESS".equals(returnCode)) {
                    if (outTradeNo != null) {
                        //业务数据持久化
                        String transactionId = notifyMap.get("transaction_id");
                       //todo 业务逻辑
                      //..........
                        System.err.println("-------------------------------支付成功----------------------");
                        xmlBack = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
                    } else {
                        xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                    }
                }
            } else {
                // 签名错误,如果数据里没有sign字段,也认为是签名错误
                //失败的数据要不要存储?
                xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
            }
        } catch (Exception e) {
            xmlBack = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
        }
        return ResultInfo.successful(xmlBack);

以上只是一些浅显的业务逻辑,希望对大家有所帮助