微信支付对接

最近涉及到微信支付这块的内容,分为两种情形的使用:网站直接调起进行支付操作、手机客户端发起支付

1.手机客户端发起支付

大致流程:客户端发起支付请求 -> 服务端接收请求,本地下单处理 -> 服务端对微信做预下单处理,组织下单参数吐回客户端 -> 客户端调起微信支付

这里主要记录下微信预下单处理过程:

 1 private String weixinClientPay(PayRequestWeixin data) throws UnsupportedEncodingException, JSONException {
 2          Map<String, Object> map = new HashMap<String, Object>();
 3          String dealcode = data.getTopupDealcode();//获取本地下单的订单号
 4          Deal topupOrder  = sdkDealService.getTopupOrder(dealcode, Boolean.FALSE);//获取本地下单的订单信息
 5          BigDecimal amount = topupOrder.getAmount();//以分为单位
 6          String product =  StringUtils.isNotBlank(data.getItem_name())?data.getItem_name():(ConvertUtils.toCNYStr(amount)+ PaymentChannelConstants.CNY);
 7          //接收财付通通知的URL
 8          String notify_url = topupOrder.getNotifyUrl();
 9          String return_url = topupOrder.getReturnUrl();
10  
11          String out_trade_no = topupOrder.getDealId();
12  
13          Map<String, Object> paramsMap = data.getParmas();
14          PackageRequestHandler packageReqHandler = (PackageRequestHandler)paramsMap.get(WeixinPayConstants.packageReqHandler);//获取package的请求类 
15          PrepayIdRequestHandler prepayReqHandler = (PrepayIdRequestHandler)paramsMap.get(WeixinPayConstants.prepayReqHandler);//获取prepayid的请求类
16          ClientRequestHandler clientHandler = (ClientRequestHandler)paramsMap.get(WeixinPayConstants.clientHandler);//返回客户端支付参数的请求类
17          packageReqHandler.setKey(ConstantUtil.SDK_PARTNER_KEY); //商户号对应的密钥
18  
19          int retcode;
20          String retmsg = "";
21          String retVal = "";
22          //获取token值 
23          String token = AccessTokenRequestHandler.getAccessToken("");
24          if (!"".equals(token)) {
25              //设置package订单参数
26              packageReqHandler.setParameter("bank_type", "WX");//银行渠道
27              packageReqHandler.setParameter("body",product); //商品描述   
28              packageReqHandler.setParameter("notify_url", notify_url); //接收财付通通知的URL  
29              packageReqHandler.setParameter("partner", ConstantUtil.PARTNER); //商户号
30              packageReqHandler.setParameter("out_trade_no", out_trade_no); //商家订单号   
31              packageReqHandler.setParameter("total_fee", ConvertUtils.toMobiStr(amount)); //商品金额,以分为单位  
32              packageReqHandler.setParameter("spbill_create_ip", data.getIp()); //订单生成的机器IP,指用户浏览器端IP  
33              packageReqHandler.setParameter("fee_type", "1"); //币种,1人民币   66
34              packageReqHandler.setParameter("input_charset", "UTF-8"); //字符编码
35              packageReqHandler.setParameter("return_url", return_url);
36              packageReqHandler.setParameter("attach", "sdk");
37  
38              //获取package包
39              String packageValue = packageReqHandler.getRequestURL();
40  
41              String noncestr = WXUtil.getNonceStr();
42              String timestamp = WXUtil.getTimeStamp();
43              String traceid = "";
44              ////设置获取prepayid支付参数
45              prepayReqHandler.setParameter("appid", ConstantUtil.SDK_APP_ID);//微信开发平台应用id
46              prepayReqHandler.setParameter("appkey", ConstantUtil.APP_KEY);//应用对应的凭证
47  
48              prepayReqHandler.setParameter("noncestr", noncestr);
49              prepayReqHandler.setParameter("package", packageValue);
50              prepayReqHandler.setParameter("timestamp", timestamp);
51              prepayReqHandler.setParameter("traceid", traceid);
52  
53              //生成获取预支付签名
54              String sign = prepayReqHandler.createSHA1Sign();
55              //增加非参与签名的额外参数
56              prepayReqHandler.setParameter("app_signature", sign);
57              prepayReqHandler.setParameter("sign_method",ConstantUtil.SIGN_METHOD);//签名算法常量值:sha1
58              String gateUrl = ConstantUtil.GATEURL + token;//获取预支付id的接口url:https://api.weixin.qq.com/pay/genprepay?access_token=
59              prepayReqHandler.setGateUrl(gateUrl);
60  
61              //获取prepayId
62              String prepayid = prepayReqHandler.sendPrepay();
63              //吐回给客户端的参数
64              if (null != prepayid && !"".equals(prepayid)) {
65                  //输出参数列表
66                  clientHandler.setParameter("appid", ConstantUtil.SDK_APP_ID);
67                  clientHandler.setParameter("appkey", ConstantUtil.APP_KEY);
68                  clientHandler.setParameter("partnerid", ConstantUtil.PARTNER);
69                  
70                  clientHandler.setParameter("noncestr", noncestr);
71                  clientHandler.setParameter("package", "Sign=WXPay");
72                  clientHandler.setParameter("prepayid", prepayid);
73                  clientHandler.setParameter("timestamp", timestamp);
74                  //生成签名
75                  sign = clientHandler.createSHA1Sign();
76                  clientHandler.setParameter("sign", sign);
77  
78                  retcode = 0;
79                  retmsg = "OK";
80                  
81                  String appId = ConstantUtil.SDK_APP_ID;
82                  String partner = ConstantUtil.PARTNER;//商户号
83                  
84                  retVal = "<?xml version="1.0" encoding="utf-8"?><root><retcode>"+retcode+"</retcode><retmsg>"+retmsg+"</retmsg>"+"<appid>"+ appId+"</appid><noncestr>"+noncestr+"</noncestr>" +
85                          "<package>Sign=WXPay</package><partnerid>"+partner+"</partnerid><prepayid>"+prepayid+
86                          "</prepayid><sign>"+sign+"</sign><timestamp>"+timestamp+"</timestamp>"+"</root>";
87              } else {
88                  retcode = -2;
89                 retmsg = "错误:获取prepayId失败";
90              }
91          } else {
92              retcode = -1;
93              retmsg = "错误:获取不到Token";
94          }
95          log.info("weixin client pay, retmsg: " + retmsg + ", retVal:
" + retVal);
96      }
ps:省略部分输出性代码

其中获取accessToken:String token = AccessTokenRequestHandler.getAccessToken("");

 1 protected static String getAccessToken() {
 2         String requestUrl = ConstantUtil.TOKENURL + "?grant_type=" + ConstantUtil.GRANT_TYPE + "&appid="
 3               + ConstantUtil.SDK_APP_ID + "&secret=" + ConstantUtil.SDK_APP_SECRET;
 4         
 5         String resContent = "";
 6         TenpayHttpClient httpClient = new TenpayHttpClient();
 7         httpClient.setMethod("GET");
 8         httpClient.setReqContent(requestUrl);
 9         if (httpClient.call()) {
10             resContent = httpClient.getResContent();
11             if (resContent.indexOf(ConstantUtil.ACCESS_TOKEN) > 0) {
12                 access_token = getJsonValue(resContent, ConstantUtil.ACCESS_TOKEN);
13             } else {
14                 log.info("获取access_token值返回错误!!!");
15             }
16         } else {
17             log.info("后台调用通信失败");
18             log.info(httpClient.getResponseCode());
19             log.info(httpClient.getErrInfo());
20             // 有可能因为网络原因,请求已经处理,但未收到应答。
21         }
22         return access_token;
23     }

预下单:String prepayid = prepayReqHandler.sendPrepay();

 1 // 提交预支付
 2     public String sendPrepay() throws JSONException {
 3         String prepayid = "";
 4         StringBuffer sb = new StringBuffer("{");
 5         Set es = super.getAllParameters().entrySet();
 6         Iterator it = es.iterator();
 7         while (it.hasNext()) {
 8             Map.Entry entry = (Map.Entry) it.next();
 9             String k = (String) entry.getKey();
10             String v = (String) entry.getValue();
11             if (null != v && !"".equals(v) && !"appkey".equals(k)) {
12                 sb.append(""" + k + "":"" + v + "",");
13             }
14         }
15         String params = sb.substring(0, sb.lastIndexOf(","));
16         params += "}";
17         String requestUrl = super.getGateUrl();
18         TenpayHttpClient httpClient = new TenpayHttpClient();
19         httpClient.setReqContent(requestUrl);
20         String resContent = "";
21         this.setDebugInfo(this.getDebugInfo() + "
" + "post data:" + params);
22         if (httpClient.callHttpPost(requestUrl, params)) {
23             resContent = httpClient.getResContent();
24             if (2 == resContent.indexOf("prepayid")) {
25                 prepayid = getJsonValue(resContent, "prepayid");
26             }
27         }
28         return prepayid;
29     }

大致过程,其中省略部分数据初始化以及微信支付等常量设置。

2. 页面调起进行支付操作

首先jsp:

  1 //引入weixinjs
  2 <script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.0.0.js" ></script>
  3 //设置
  4 $(function(){
  5     wx.config({
  6         debug: false, 
  7         appId: '${wxConf["appId"]}', // 必填,公众号的唯一标识
  8         timestamp: '${wxConf["timestamp"]}', // 必填,生成签名的时间戳
  9         nonceStr:  '${wxConf["noncestr"]}', // 必填,生成签名的随机串
 10         signature: '${wxConf["signature"]}',// 必填,签名,见附录1
 11         jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone', 'chooseWXPay'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
 12     });
 13     
 14     wx.ready(function() {
 15         // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
 16         wx.onMenuShareTimeline({
 17             title: '${WEIXIN_SHARE_TITLE}',
 18             desc:'${WEIXIN_SHARE_DESC}',
 19             link: '${WEIXIN_SHARE_URL}',
 20             imgUrl: '${WEIXIN_SHARE_ICON}',
 21             trigger: function (res) {
 22             },
 23             success: function (res) {
 24             },
 25             cancel: function (res) {
 26             },
 27             fail: function (res) {
 28             }
 29         });
 30         wx.onMenuShareAppMessage({
 31                 title: '${WEIXIN_SHARE_TITLE}',
 32                 desc:'${WEIXIN_SHARE_DESC}',
 33                 link: '${WEIXIN_SHARE_URL}',
 34                 imgUrl: '${WEIXIN_SHARE_ICON}',
 35                 trigger: function (res) {
 36                 },
 37                 success: function (res) {
 38                 },
 39                 cancel: function (res) {
 40                 },
 41                 fail: function (res) {
 42                 }
 43         });
 44         //分享QQ
 45         wx.onMenuShareQQ({
 46              title: '${WEIXIN_SHARE_TITLE}',
 47              desc:'${WEIXIN_SHARE_DESC}',
 48              link: '${WEIXIN_SHARE_URL}',
 49              imgUrl: '${WEIXIN_SHARE_ICON}',
 50             success: function () { 
 51                // 用户确认分享后执行的回调函数
 52             },
 53             cancel: function () { 
 54                // 用户取消分享后执行的回调函数
 55             }
 56         });
 57         //分享微博
 58         wx.onMenuShareWeibo({
 59              title: '${WEIXIN_SHARE_TITLE}',
 60              desc:'${WEIXIN_SHARE_DESC}',
 61              link: '${WEIXIN_SHARE_URL}',
 62              imgUrl: '${WEIXIN_SHARE_ICON}',
 63             success: function () { 
 64                // 用户确认分享后执行的回调函数
 65             },
 66             cancel: function () { 
 67                 // 用户取消分享后执行的回调函数
 68             }
 69         });
 70         //分享QQ空间
 71         wx.onMenuShareQZone({
 72              title: '${WEIXIN_SHARE_TITLE}',
 73              desc:'${WEIXIN_SHARE_DESC}',
 74              link: '${WEIXIN_SHARE_URL}',
 75              imgUrl: '${WEIXIN_SHARE_ICON}',
 76             success: function () { 
 77                // 用户确认分享后执行的回调函数
 78             },
 79             cancel: function () { 
 80                 // 用户取消分享后执行的回调函数
 81             }
 82         });
 83         
 84     });
 85     wx.error(function(res) {
 86         // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
 87         //alert("微信JS执行失败." + res.errMsg);
 88     });
 89     wx.checkJsApi({
 90         jsApiList: [
 91             'onMenuShareTimeline','onMenuShareAppMessage','chooseWXPay'
 92         ]
 93     });
 94 });
 95 
 96 function pay(appId,mobile,amount,dealCode){
 97   //ajax提交数据到后台处理
 98   $.ajax({
 99                 url:"/xxxx/xxx.shtml?m=payWithWeixin&channel=weixinpay&subchannel=JSPAY&amount="+amount+"&topupPage=7&mobile="+mobile+"&openId="+openId+"&remark=MMACTIVE_"+dealCode
100             }).done(function(data){
101                 data = data.replace(/"/g, "");
102                 //alert(data);
103                 var arr = data.split("|");
104                 wx.chooseWXPay({
105                     timestamp: arr[1], // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
106                     nonceStr: arr[2], // 支付签名随机串,不长于 32 位
107                     package: arr[3], // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
108                     signType: 'MD5', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
109                     paySign: arr[4], // 支付签名
110                     success: function (res) {
111                         if(typeof(doWeixinPayResultCallBack)){
112                             //微信支付成功回调114                             //可处理自己的相关的页面代码操作115                         }
116                         window.location.reload();
117                     }
118                 });
119             });
120 }

数据提交到后台,数据的处理封装省略,直接上微信支付预下单处理:

 1 private String jsApiPay(PayRequest data) throws Exception {
 2         String dealcode = data.getTopupDealcode();
 3         String nonce_str = WXUtil.getNonceStr();
 4         String timeStamp = WXUtil.getTimeStamp();
 5         TopupOrder topupOrder  = dealService.getTopupOrder(dealcode, Boolean.FALSE);
 6         String product =  topupOrder.getAmount().doubleValue()+ PaymentChannelConstants.CNY;
 7         StringBuilder stringBuilder = new StringBuilder();
 8         stringBuilder.append("appid=").append(JSAPI_APP_ID);
 9         stringBuilder.append("&body=").append(product);
10         stringBuilder.append("&mch_id=").append(JSAPI_MCH_ID);
11         stringBuilder.append("&nonce_str=").append(nonce_str);
12         stringBuilder.append("&notify_url=").append(topupOrder.getNotifyURL());
13         stringBuilder.append("&openid=").append(data.getOpenId());
14         stringBuilder.append("&out_trade_no=").append(topupOrder.getDealcode());
15         stringBuilder.append("&spbill_create_ip=").append(data.getIp());
16         stringBuilder.append("&total_fee=").append(topupOrder.getAmount().multiply(BigDecimal.valueOf(100)).setScale(0));//金额 以分为单位
17         stringBuilder.append("&trade_type=JSAPI");
18         String sign = Md5Encrypt.encrypt(stringBuilder.toString()+"&key="+JSAPI_KEY).toUpperCase();
19         
20         stringBuilder = new StringBuilder();
21         stringBuilder.append("<xml>");
22         stringBuilder.append("<appid>"+JSAPI_APP_ID+"</appid>");
23         stringBuilder.append("<body><![CDATA["+ product +"]]></body>");
24         stringBuilder.append("<mch_id>"+JSAPI_MCH_ID+"</mch_id>");
25         stringBuilder.append("<nonce_str>"+nonce_str+"</nonce_str>");
26         stringBuilder.append("<notify_url>"+topupOrder.getNotifyURL()+"</notify_url>");
27         stringBuilder.append("<openid>"+data.getOpenId()+"</openid>");
28         stringBuilder.append("<out_trade_no>"+ topupOrder.getDealcode()+"</out_trade_no>");
29         stringBuilder.append("<spbill_create_ip>"+data.getIp()+"</spbill_create_ip>");
30         stringBuilder.append("<total_fee>"+topupOrder.getAmount().multiply(BigDecimal.valueOf(100)).setScale(0).toString()+"</total_fee>");
31         stringBuilder.append("<trade_type>JSAPI</trade_type>");
32         stringBuilder.append("<sign><![CDATA["+sign+"]]></sign>");
33         stringBuilder.append("</xml>");
34         
35         String prepayId = getResponseData(UNIFIED_ORDER_URL, new String(stringBuilder.toString().getBytes(), "ISO8859-1"));//解决中文问题
36         //String prepayId = getResponseData(UNIFIED_ORDER_URL, new String(stringBuilder.toString()));
37         prepayId =  (String)XMLUtil.doXMLParse(prepayId).get("prepay_id");
38         prepayId = "prepay_id=" + prepayId;
39         
40         stringBuilder = new StringBuilder();
41         stringBuilder.append("appId=").append(JSAPI_APP_ID)
42         .append("&nonceStr=").append(nonce_str)
43         .append("&package=").append(prepayId)
44         .append("&signType=").append("MD5")
45         .append("&timeStamp=").append(timeStamp)
46         .append("&key=").append(JSAPI_KEY);
47 
48         String paySign = Md5Encrypt.encrypt(stringBuilder.toString()).toUpperCase();
49         String payInfo = MessageFormat.format(""{0}"|"{1}"|"{2}"|"{3}"|"{4}"|"{5}"",JSAPI_APP_ID,timeStamp,nonce_str,prepayId,paySign,topupOrder.getDealcode());
50         return payInfo;
51     }
52 其中:
53 public static String getNonceStr() {
54         Random random = new Random();
55         return MD5Util.MD5Encode(String.valueOf(random.nextInt(10000)), "UTF-8");
56     }
57 
58     public static String getTimeStamp() {
59         return String.valueOf(System.currentTimeMillis() / 1000);
60     }
原文地址:https://www.cnblogs.com/eric-fang/p/4962926.html