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

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

一、相关官方文档

微信支付官方文档: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("\n", "").replaceAll("\r", "").replaceAll("\t", "");
            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("\n", "").replaceAll("\r", "").replaceAll("\t", "");
            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("\n", "").replaceAll("\r", "").replaceAll("\t", "");
            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

 

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

           
posted @ 2017-08-18 17:10  正则吃饺子  阅读(644)  评论(0编辑  收藏  举报