• <正则吃饺子> :关于微信支付的简单总结说明(一)


    关于支付,一直想参与开发,现在根据项目中已有及参见的微信开发文档,将自己对于微信开发的流程进行简单的总结,以备后用和帮助后来者。

    一、相关官方文档

    微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html

    二、参考博文

    (待添加,等我找找..)

    三、自己参看文档时候的简单文档整理,

    ps:只为了方便自己记忆和联想

    四、根据官方文档中,标记后台主要做的工作流程

    五、参看项目代码时候,整理的demo,来源于网络

    (1)MD5Util  ----  生成签名时候使用

    package com.weixin.test;
    
    import java.security.MessageDigest;
    
    /**
     * 微信测试MD5
     * @author Administrator
     *
     */
    public class MD5Util {
    
        private static String byteArrayToHexString(byte b[]) {
            StringBuffer resultSb = new StringBuffer();
            for (int i = 0; i < b.length; i++)
                resultSb.append(byteToHexString(b[i]));
    
            return resultSb.toString();
        }
    
        private static String byteToHexString(byte b) {
            int n = b;
            if (n < 0)
                n += 256;
            int d1 = n / 16;
            int d2 = n % 16;
            return hexDigits[d1] + hexDigits[d2];
        }
    
        public static String MD5Encode(String origin, String charsetname) {
            String resultString = null;
            try {
                resultString = new String(origin);
                MessageDigest md = MessageDigest.getInstance("MD5");
                if (charsetname == null || "".equals(charsetname))
                    resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
                else
                    resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
            } catch (Exception exception) {
            }
            return resultString;
        }
    
        private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
    
    }
    View Code

    (2)PayTest

    package com.weixin.test;
    
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
    import java.util.SortedMap;
    import java.util.TreeMap;
    
    public class PayTest {
    
        // http://mch.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3
        private static String Key = "192006250b4c09247ec02edce69f6a2d";
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            System.out.println(">>>模拟微信支付<<<");
            System.out.println("==========华丽的分隔符==========");
            // 微信api提供的参数
            String appid = "wxd930ea5d5a258f4f";
            String mch_id = "10000100";
            String device_info = "1000";
            String body = "test";
            String nonce_str = "ibuaiVcKdpRxkhJA";
    
            SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
            parameters.put("appid", appid);
            parameters.put("mch_id", mch_id);
            parameters.put("device_info", device_info);
            parameters.put("body", body);
            parameters.put("nonce_str", nonce_str);
    
            String characterEncoding = "UTF-8";
            String weixinApiSign = "9A0A8659F005D6984697E2CA0A9CF3B7";
            System.out.println("微信的签名是:" + weixinApiSign);
            String mySign = createSign(characterEncoding, parameters);
            System.out.println("我     的签名是:" + mySign);
    
            if (weixinApiSign.equals(mySign)) {
                System.out.println("恭喜你成功了~");
            } else {
                System.out.println("不行啊,再接再厉~");
            }
    
            String userAgent = "Mozilla/5.0(iphone;CPU iphone OS 5_1_1 like Mac OS X) AppleWebKit/534.46(KHTML,like Geocko) Mobile/9B206 MicroMessenger/5.0";
    
            char agent = userAgent.charAt(userAgent.indexOf("MicroMessenger") + 15);
    
            System.out.println("微信的版本号:" + new String(new char[] { agent }));
        }
    
        /**
         * 微信支付签名算法sign
         * @param characterEncoding
         * @param parameters
         * @return
         */
        @SuppressWarnings("unchecked")
        public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {
            StringBuffer sb = new StringBuffer();
            Set es = parameters.entrySet();// 所有参与传参的参数按照accsii排序(升序)
            Iterator it = es.iterator();
            while (it.hasNext()) {
                Map.Entry entry = (Map.Entry) it.next();
                String k = (String) entry.getKey();
                Object v = entry.getValue();
                if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                    sb.append(k + "=" + v + "&");
                }
            }
            sb.append("key=" + Key);
            String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
            return sign;
        }
    
    }
    View Code

    六、结合项目,微信支付流程解析

    主要为微信支付官方文档提供的流程中,对应于 4、5、6、10、11

    (1)网页内请求生成支付订单。主要工作就是前端(app)在将商品添加到购物车后下单购买或者立即购买时,在后台系统中所生成的一个未支付订单。

    下面只是项目中一个业务处理,可以忽略,仅做参考和方便记忆。

    /**
         * 生成订单
         *
         * @param order
         * @param baseUser
         * @return
         */
        @Override
        public Response<String> createRechargeOrder(RechargeOrder order, BaseUser baseUser) {
            Response<String> response = new Response<>();
    
            try {
                if (baseUser == null) {
                    throw new BusinessException(ResultCode.ERROR_USER_UNLOGIN.getName());
                }
                if (order == null) {
                    throw new ArgumentException(ResultCode.ERROR_ORDER_SAVE_NULL.getName());
                }
                if (!checkAmount(order.getActuralAmount(), order.getRechargeAmount())) {
                    throw new ArgumentException("支付金额异常");
                }
    
                order.setCustomerName(baseUser.getName());
                order.setCustomerId(baseUser.getId());
                if("customer".equals(baseUser.getType())){
                    Response<MemberCustomerUser> res = customerService.getCustomerById(baseUser.getId());
                    if (null != res && res.isSuccess()) {
                        order.setCustomerPhone(res.getResult().getMobilePhone());
                    }
                }else if("shop".equals(baseUser.getType())){
                    MemberShopUser user = new MemberShopUser();
                    user.setId(baseUser.getId());
                    Response<MemberShopUser> res = terminalShopService.getShopUserById(user);
                    if (null != res && res.isSuccess()) {
                        order.setCustomerPhone(res.getResult().getMobilePhone());
                    }
                }
                if (StringUtils.isBlank(order.getSource())) {
                    order.setSource("");
                }
                //订单状态:01未支付
                order.setOrderCode("10");
                order.setOrderName("未支付");
                //付款状态:待付款
                order.setPayStatus("0");
                rechargeOrderManager.createRechargeOrder(order);
                //记录订单状态日志
                if (order.getOrderId() != null && !"".equals(order.getOrderId())) {
                    ConvenienceOrderLog orderLog = new ConvenienceOrderLog();
                    orderLog.setOrderId(String.valueOf(order.getOrderId()));
                    orderLog.setStatusCode(10);
                    orderLog.setStatusName("未支付");
                    orderLog.setStatusUserId(baseUser.id);
                    orderLog.setStatusUser(baseUser.name);
                    rechargeOrderManager.saveConvenienceOrderLog(orderLog);
                }
                response.setResult(String.valueOf(order.getOrderId()));
            } catch (ArgumentException a) {
                //参数异常不写log日志
                response.setError(a.getMessage());
            } catch (BusinessException b) {
                response.setError(b.getMessage());
                log.error("保存订单异常!原因:{}", Throwables.getStackTraceAsString(b));
            } catch (Exception e) {
                response.setError(e.getMessage());
                log.error("保存订单异常!原因:{}", Throwables.getStackTraceAsString(e));
            }
            return response;
        }
    View Code

    (2)调用统一下单api,交易请求,获取微信后台返回的交易支付预付单,对返回信息处理后,返回给前端(app)

    /**
         * 微信支付 交易请求,生成预付单 -====== 公众号
         *
         * @param orderId
         * @param baseUser
         * @return
         */
        @Override
        public Response<Map<String, String>> weChatPayRequest(String orderId, String openId, BaseUser baseUser) {
            Response<Map<String, String>> response = new Response<>();
            Map<String, String> map = new HashMap<>();
            try {
                if (Strings.isNullOrEmpty(orderId)) {
                    throw new ArgumentException("交易编号为空");
                }
                if (Strings.isNullOrEmpty(openId)) {
                    throw new ArgumentException("openId为空");
                }
                if (baseUser == null) {
                    throw new BusinessException(ResultCode.ERROR_USER_UNLOGIN.getName());
                }
                Response<String> res = this.getOrderAmount(orderId, "1", baseUser);
                int amount = 0;
                if (res.isSuccess() && res.getResult() != null) {
                    amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue();
                } else {
                    throw new BusinessException(res.getError());
                }
                //生成请求参数XML--公众号
                String xml = this.createWeChatPayXml(orderId, openId, amount);
                //微信支付统一下单接口
                String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
                ClientCustomSSL clientCustomSSL = new ClientCustomSSL();
                //发起请求
                String result = clientCustomSSL.doPayRequest(payRequestURL, xml);
                result = result.replaceAll("
    ", "").replaceAll("
    ", "").replaceAll("	", "");
                log.error(result);
                //解析返回结果,重新处理以发给前端(app)
                Document doc = DocumentHelper.parseText(result);
                Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc);
                if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) {
                    map.put("appid", (String) resultMap.get("appid"));
                    map.put("mch_id", (String) resultMap.get("mch_id"));
                    map.put("prepay_id", (String) resultMap.get("prepay_id"));
                    map.put("package", "prepay_id=" + resultMap.get("prepay_id"));
                    Long l = new Date().getTime() / 1000;
                    Integer timestamp = Integer.parseInt(l.toString());
                    map.put("timestamp", timestamp.toString());
                    map.put("nonceStr", "aaronlovem" + timestamp.toString());
                    // createPaySignStr()  ---- 生成支付签名-公众号,注意 :需要跟发起支付时候的,生成签名的参数规则一致。总之,需要跟前端提交时候,微信生成的签名一致。
                    String paySign = createPaySignStr(map.get("nonceStr").toString(), map.get("package"), map.get("timestamp"));
                    map.put("paySign", paySign);
                    map.put("openId", openId);
                    response.setResult(map);
                } else {
                    response.setSuccess(false);
                    response.setError((String) resultMap.get("return_msg"));
                }
            } catch (ArgumentException a) {
                response.setError(a.getMessage());
            } catch (BusinessException b) {
                response.setError(b.getMessage());
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(b));
            } catch (RuntimeException c) {
                response.setError(c.getMessage());
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(c));
            } catch (Exception e) {
                response.setError("微信支付异常!" + Throwables.getStackTraceAsString(e));
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(e));
            }
            //返回给前端,微信支付订单信息
            return response;
        }
    View Code

    一个生成签名的方式,还有其他方式,仅做记录,如下:

    /**
         * 生成支付签名-公众号
         *
         * @return
         */
        public String createPaySignStr(String nonceStr, String packageStr, String timestamp) throws Exception {
            String[] arr = new String[5];
            arr[0] = "appId=suwxb1d7b09dsaf81c92eb";
            arr[1] = "nonceStr=" + nonceStr;
            arr[2] = "package=" + packageStr;
            arr[3] = "signType=MD5";
            arr[4] = "timeStamp=" + timestamp;
            Arrays.sort(arr);
            String param = "";
            for (int i = 0; i < arr.length; i++) {
                param += arr[i] + "&";
            }
            param += "key=我是key呵呵呵呵呵";
            log.error("微信支付请求参数:" + param);
            return MD5Util.MD5Encode(param, null).toUpperCase();
        }
    
        /**
         * 生成支付签名-B端、C端
         *
         * @param noncestr
         * @param packagestr
         * @param prepayid
         * @param timestamp
         * @return
         */
        public String createWeChatGoPayXmlForAPP(String noncestr, String packagestr, String prepayid, String timestamp, String type) {
            String appId = "";
            String partnerId = "";
            String key = "";
            if ("B2C".equals(type)) {
                appId = "suwxb1d7bf09af81c92eb";
                partnerId = "1322218523201";
                key = "我是key";
            } else if ("B2B".equals(type)) {
                appId = "suwxb1d7b09af81c92eb";
                partnerId = "1352713d383902";
                key = "我是key啊";
            }
            String[] arr = new String[6];
            arr[0] = "appid=" + appId;
            arr[1] = "noncestr=" + noncestr;
            arr[2] = "package=" + packagestr;
            arr[3] = "partnerid=" + partnerId;
            arr[4] = "prepayid=" + prepayid;
            arr[5] = "timestamp=" + timestamp;
            Arrays.sort(arr);
            String param = "";
            for (int i = 0; i < arr.length; i++) {
                param += arr[i] + "&";
            }
            param += "key=" + key;
            log.error("微信支付请求参数:" + param);
            return MD5Util.MD5Encode(param, null).toUpperCase();
        }
    View Code

    b、c的交易请求,app支付:大体与公众号支付类似,参数不一致

    /**
         * 微信支付 交易请求,生成预付单,C端
         *
         * @param orderId
         * @param baseUser
         * @return
         */
        @Override
        public Response<Map<String, String>> weChatPayRequestForC(String orderId, BaseUser baseUser) {
            Response<Map<String, String>> response = new Response<Map<String, String>>();
            Map<String, String> map = new HashMap<String, String>();
            try {
                if (Strings.isNullOrEmpty(orderId)) {
                    throw new ArgumentException("交易编号为空");
                }
                Response<String> res = this.getOrderAmount(orderId, "1", baseUser);
                int amount = 0;
                if (res.isSuccess() && res.getResult() != null) {
                    amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue();
                } else {
                    throw new BusinessException(res.getError());
                }
                String xml = this.createWeChatPayXmlForAPP(orderId, amount, "B2C");
                String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
                ClientCustomSSL clientCustomSSL = new ClientCustomSSL();
                String result = clientCustomSSL.doPayRequest(payRequestURL, xml);
                result = result.replaceAll("
    ", "").replaceAll("
    ", "").replaceAll("	", "");
                log.error(result);
                Document doc = DocumentHelper.parseText(result);
                Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc);
                if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) {
                    Map<String, String> param = new HashMap<String, String>();
                    param.put("appid", (String) resultMap.get("appid"));
                    param.put("partnerid", (String) resultMap.get("mch_id"));
                    param.put("prepayid", (String) resultMap.get("prepay_id"));
                    param.put("package", "Sign=WXPay");
                    Long l = new Date().getTime() / 1000;
                    Integer timestamp = Integer.parseInt(l.toString());
                    param.put("noncestr", "xxxxxB2Capp" + timestamp.toString());
                    param.put("timestamp", timestamp.toString());
                    param.put("sign", this.createWeChatGoPayXmlForAPP(param.get("noncestr").toString(), param.get("package").toString(), param.get("prepayid").toString(), param.get("timestamp").toString(), "B2C"));
                    response.setResult(param);
                } else {
                    response.setSuccess(false);
                    response.setError((String) resultMap.get("return_msg"));
                }
            } catch (ArgumentException a) {
                response.setError(a.getMessage());
            } catch (BusinessException b) {
                response.setError(b.getMessage());
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(b));
            } catch (RuntimeException c) {
                response.setError(c.getMessage());
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(c));
            } catch (Exception e) {
                response.setError("微信支付异常!" + Throwables.getStackTraceAsString(e));
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(e));
            }
            return response;
        }
    
        @Override
        public Response<Map<String, String>> weChatPayRequestForB(String orderId, BaseUser baseUser) {
            Response<Map<String, String>> response = new Response<Map<String, String>>();
            Map<String, String> map = new HashMap<String, String>();
            try {
                if (Strings.isNullOrEmpty(orderId)) {
                    throw new ArgumentException("交易编号为空");
                }
                Response<String> res = this.getOrderAmount(orderId, "1", baseUser);
                int amount = 0;
                if (res.isSuccess() && res.getResult() != null) {
                    amount = new BigDecimal(res.getResult()).multiply(new BigDecimal(100)).intValue();
                } else {
                    throw new BusinessException(res.getError());
                }
                String xml = this.createWeChatPayXmlForAPP(orderId, amount, "B2B");
                String payRequestURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
                ClientCustomSSL clientCustomSSL = new ClientCustomSSL();
                String result = clientCustomSSL.doPayRequest(payRequestURL, xml);
                result = result.replaceAll("
    ", "").replaceAll("
    ", "").replaceAll("	", "");
                log.error(result);
                Document doc = DocumentHelper.parseText(result);
                Map<String, Object> resultMap = XmlMapHandle.Dom2Map(doc);
                if (((String) resultMap.get("return_code")).equalsIgnoreCase("SUCCESS") && ((String) resultMap.get("result_code")).equals("SUCCESS")) {
                    Map<String, String> param = new HashMap<String, String>();
                    param.put("appid", (String) resultMap.get("appid"));
                    param.put("partnerid", (String) resultMap.get("mch_id"));
                    param.put("prepayid", (String) resultMap.get("prepay_id"));
                    param.put("package", "Sign=WXPay");
                    Long l = new Date().getTime() / 1000;
                    Integer timestamp = Integer.parseInt(l.toString());
                    param.put("noncestr", "xxxxB2Bapp" + timestamp.toString());
                    param.put("timestamp", timestamp.toString());
                    param.put("sign", this.createWeChatGoPayXmlForAPP(param.get("noncestr").toString(), param.get("package").toString(), param.get("prepayid").toString(), param.get("timestamp").toString(), "B2B"));
                    response.setResult(param);
                } else {
                    response.setSuccess(false);
                    response.setError((String) resultMap.get("return_msg"));
                }
            } catch (ArgumentException a) {
                response.setError(a.getMessage());
            } catch (BusinessException b) {
                response.setError(b.getMessage());
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(b));
            } catch (RuntimeException c) {
                response.setError(c.getMessage());
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(c));
            } catch (Exception e) {
                response.setError("微信支付异常!" + Throwables.getStackTraceAsString(e));
                log.error("weChatPayRequest 异常!原因:{}", Throwables.getStackTraceAsString(e));
            }
            return response;
        }
    View Code

    (3)告知微信处理通知支付结果。此为,我们在后台发起微信支付请求生成预订单时候,传入的回调函数的方法。微信后台在处理完微信支付结果后,会发起请求,调用我们已经准备好的回调函数地址。

    回调函数中,可以具体处理下我们商户系统中的一些业务,比如更新订单状态,提醒消息等等.....处理完成,返回微信后台处理结果。

    对应于微信支付下单api接口参数中的,如下:

     /**
         * 微信支付回调函数
         *
         * @param weChatPayReturn
         * @return
         */
        @Override
        public String getWeChatPayReturnForOrder(WeChatPayReturn weChatPayReturn) {
            log.error("微信普通订单回调,参数为:" + weChatPayReturn.toString());
            System.out.println("微信普通订单回调,参数为:" + weChatPayReturn.toString());
            boolean flag = false;
            try {
                if (null != weChatPayReturn) {
                    //保存微信回调信息
                    rechargeOrderManager.saveRechargeOrderWeChatPayInfo(weChatPayReturn);
                    if (true) {
                        //判断该笔订单是否在商户网站中已经做过处理
                        //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                        //如果有做过处理,不执行商户的业务程序
                        flag = rechargeOrderManager.getWeChatPayReturnForOrder(weChatPayReturn);
                        if (flag) {
                            String successStr = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
                            return successStr;
                        } else {
                            return "fail";
                        }
                    }
                } else {
                    throw new ArgumentException("微信回调对象为空!");
                }
            } catch (ArgumentException a) {
                //参数异常不写log日志
                log.error("微信回调对象异常!原因:{}", Throwables.getStackTraceAsString(a));
            } catch (BusinessException b) {
                log.error("微信回调对象异常!原因:{}", Throwables.getStackTraceAsString(b));
            } catch (Exception e) {
                log.error("微信回调对象异常!原因:{}", Throwables.getStackTraceAsString(e));
            }
            return "fail";
        }
    View Code

    注意:文章中所列出的代码不一定是最好的,只提供流程思路,方便理解和记忆。具体的实现方式,自己把握。比如,在生成支付签名时候,有多种方式来实现。

               
  • 相关阅读:
    前后端分离开发中动态菜单的两种实现方案
    Spring Security 前后端分离登录,非法请求直接返回 JSON
    Spring Boot2 系列教程(九)Spring Boot 整合 Thymeleaf
    原创的离线版 Redis 教程,给力!
    Spring Boot2 系列教程(八)Spring Boot 中配置 Https
    Anaconda创建环境、删除环境、激活环境、退出环境
    开源一个功能完整的SpringBoot项目框架
    Spring Boot和Spring Cloud学习资源推荐
    Ubuntu 18.04下安装Steam顶级在线游戏平台
    终极手撕之架构大全:分布式+开源框架+微服务+性能优化,够不够?
  • 原文地址:https://www.cnblogs.com/zhengzeze/p/7390742.html
Copyright © 2020-2023  润新知