最近涉及到微信支付这块的内容,分为两种情形的使用:网站直接调起进行支付操作、手机客户端发起支付
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("¬ify_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 }