生活随笔
收集整理的這篇文章主要介紹了
微信公众号开发,微信支付功能开发(网页JSAPI调用)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
1、微信支付的流程
如下三張手機(jī)截圖,我們在微信網(wǎng)頁端看到的支付,表面上看到的是 “點(diǎn)擊支付按鈕 - 彈出支付框 - 支付成功后出現(xiàn)提示頁面”,實(shí)際上的核心處理過程是:
- 點(diǎn)擊支付按鈕時(shí),執(zhí)行一個(gè)Ajax到后臺
- 后臺通過前臺的部分信息(如商品名額,金額等),將其組裝成符合微信要求格式的xml,然后調(diào)用微信的“統(tǒng)一下單接口”
- 調(diào)用成功后微信會(huì)返回一個(gè)組裝好的xml,我們提取之中的消息(預(yù)支付id也在其中)以JSON形式返回給前臺
- 前臺將該JSON傳參給微信內(nèi)置JS的方法中,調(diào)其微信支付
- 支付成功后,微信會(huì)將本次支付相關(guān)信息返回給我們的服務(wù)器
這些在《 微信支付官方文檔 - 場景介紹》和《 微信支付官方文檔 - 業(yè)務(wù)流程》都進(jìn)行了更詳細(xì)的說明。
2、微信支付功能開發(fā)詳解?
2.1 設(shè)置支付目錄和授權(quán)域名
登陸公眾號,進(jìn)行支付相關(guān)的目錄和域名設(shè)置,詳情參考《 微信支付官方文檔 - 開發(fā)步驟》,我這里簡單貼幾張官方的圖就行了,這步比較簡單,就不過多說明了,只提其中一點(diǎn):對于微信支付授權(quán)的目錄,發(fā)起微信支付的頁面必須精確地位于授權(quán)目錄下,假如支付頁面為 http://www.a.com/wx/pay/a.html,那么授權(quán)目錄必須為 http://www.a.com/wx/pay/,其他的如 http://www.a.com/wx/ , https://www.a.com/wx/pay/(http和https是不一樣的),http://a.com/wx/pay/(千萬別忘了www)都是不行的。填寫了這些非法目錄無法調(diào)起支付。
2.2 組裝xml,調(diào)用統(tǒng)一下單接口,獲取prepay_id
2.2.1 組裝xml
點(diǎn)擊支付按鈕后,寫一個(gè)Ajax將前臺部分信息發(fā)送給后臺,然后組裝xml,調(diào)用統(tǒng)一下單接口。該接口在《 微信支付官方文檔 - 統(tǒng)一下單》進(jìn)行了很詳細(xì)的解釋,我在這里進(jìn)行部分說明:
| 參數(shù) | 說明? ?? | 備注 |
| appId? ?? | 開發(fā)者應(yīng)用ID,在 “開發(fā) - 基本配置” 查看 | |
| mch_id | 微信支付的商戶號,在 “微信支付 - 商戶信息” 查看 | |
| device_info? ?? | 終端設(shè)備號(門店號或收銀設(shè)備ID) | PC網(wǎng)頁或公眾號內(nèi)支付,則傳 “WEB” |
| body | 商品或支付的簡單描述 | |
| trade_type | 可取值JSAPI,NATIVE,APP等,我們這里使用的是JSAPI | JSAPI 公眾號支付;NATIVE 原生掃碼支付;APP app支付 |
| nonce_str | 隨機(jī)字符串 | 參考算法:《微信支付官方文檔 - 安全規(guī)范》 |
| notify_url | 通知地址,微信支付成功后,微信服務(wù)器會(huì)發(fā)送信息到該url | |
| out_trade_no | 商戶系統(tǒng)內(nèi)部訂單號,由商戶自定義,訂單號要保持唯一性 | |
| total_fee | 訂單總金額,單位:分 | |
| openid | 用戶標(biāo)識,用戶在該公眾號下的唯一身份標(biāo)識 | |
| sign | 簽名 | 參考算法:《微信支付官方文檔 - 安全規(guī)范》 |
| key | API密鑰,在 “微信商戶平臺?- 賬戶中心 - API安全 - API密鑰” | |
其他的都比較簡單,重要的在于這兩個(gè)涉及算法的參數(shù),nonce_str 和 sign,這里說明一下:
- nonce_str 隨機(jī)字符串,用于保證簽名不可預(yù)測
- 算法:
- 官方建議調(diào)用隨機(jī)數(shù)函數(shù)生成,然后轉(zhuǎn)為字符串
- sign 簽名
- 算法:
- 所有發(fā)送或接收的數(shù)據(jù)按參數(shù)名ASCII碼從小到大排序,使用鍵值對形式拼接為字符串(如 key1=value1&key2=value2…)
- ASCII碼的字典排序,可以利用TreeMap幫我們自動(dòng)實(shí)現(xiàn)
- 將拼接好的字符串最后,再拼接上API密鑰,即key,得到新的字符串
- 將新的字符串進(jìn)行MD5加密,并將加密后字符串全部轉(zhuǎn)換為大寫
按照以上的這些說明,進(jìn)行xml的拼裝,貼上我自己的測試代碼(注:為方便測試,部分?jǐn)?shù)據(jù)我直接寫入方法了,如body、openId等):
String appId = WeChatAPI.getAppID();String body = "JSAPI支付測試";String merchantId = WeChatAPI.getMerchantID();String tradeNo = String.valueOf(new Date().getTime());String nonceStr1 = SignUtil.createNonceStr();String notifyUrl = "http://k169710n05.51mypc.cn/pay/do/afterPaySuccess.q";String openId = "okAkc0muYuSJUtvMf25UQHnqYvM4";String totalFee = "1";TreeMap<String, String> map = new TreeMap<String, String>();map.put("appid", appId);map.put("mch_id", merchantId);map.put("device_info", "WEB");map.put("body", body);map.put("trade_type", "JSAPI");map.put("nonce_str", nonceStr1);map.put("notify_url", notifyUrl);map.put("out_trade_no", tradeNo);map.put("total_fee", totalFee);map.put("openid", openId);String sign = SignUtil.createSign(map);String xml = "<xml>" + "<appid>" + appId + "</appid>" + "<body>" + body +"</body>" + "<device_info>WEB</device_info>" + "<mch_id>" + merchantId + "</mch_id>" + "<nonce_str>" + nonceStr1 + "</nonce_str>" + "<notify_url>" + notifyUrl +"</notify_url>" + "<openid>" + openId + "</openid>" + "<out_trade_no>" + tradeNo + "</out_trade_no>" + "<total_fee>" + totalFee + "</total_fee>" + "<trade_type>JSAPI</trade_type>" + "<sign>" + sign + "</sign>" + "</xml>";
注意:
- body參數(shù)如果直接填寫中文,在調(diào)用接口時(shí)會(huì)出現(xiàn)“簽名錯(cuò)誤”,要以ISO8859-1編碼
- 所以?String body = new String("body內(nèi)容字符串".getBytes("ISO8859-1"));
- 但即便如此,在支付完成后微信推送的“微信支付憑證”中,商品詳情中的中文也依然顯示的亂碼
- body參數(shù)內(nèi)容如果包含中文,那么在調(diào)用接口時(shí)會(huì)出現(xiàn)“簽名錯(cuò)誤”
- 在網(wǎng)上找了很多方法,有了如上刪除線部分的方法,但是仍然是有問題的,因?yàn)橹Ц冻晒蟮膽{證里中文是亂碼
- 后來終于在網(wǎng)上各種倒騰,找到了原因,確實(shí)是編碼問題,但問題不在body是否使用ISO8859-1,而在MD5的加密算法中是否使用UTF-8
- 所以?md.update(sourceStr.getBytes("UTF-8"));
兩個(gè)算法的代碼如下:
/** * 生成隨機(jī)數(shù) * <p>算法參考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3</p> * @return 隨機(jī)數(shù)字符串 */public static String createNonceStr() { SecureRandom random = new SecureRandom(); int randomNum = random.nextInt(); return Integer.toString(randomNum);}/** * 生成簽名,用于在微信支付前,獲取預(yù)支付時(shí)候需要使用的參數(shù)sign * <p>算法參考:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3</p> * @param params 需要發(fā)送的所有數(shù)據(jù)設(shè)置為的Map * @return 簽名sign */public static String createSign(TreeMap<String, String> params) { String signValue = ""; String stringSignTemp = ""; String stringA = ""; //獲得stringA Set<String> keys = params.keySet(); for (String key : keys) { stringA += (key + "=" + params.get(key) + "&"); } stringA = stringA.substring(0, stringA.length() - 1); //獲得stringSignTemp stringSignTemp = stringA + "&key=" + WeChatAPI.getMerchantKey(); //獲得signValue signValue = encryptByMD5(stringSignTemp).toUpperCase(); log.debug("預(yù)支付簽名:" + signValue); return signValue;}/** * MD5加密 * * @param sourceStr * @return */public static String encryptByMD5(String sourceStr) { String result = ""; try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(sourceStr.getBytes("UTF-8")); byte b[] = md.digest(); int i; StringBuffer buf = new StringBuffer(""); for (int offset = 0; offset < b.length; offset++) { i = b[offset]; if (i < 0) i += 256; if (i < 16) buf.append("0"); buf.append(Integer.toHexString(i)); } result = buf.toString(); } catch (NoSuchAlgorithmException e) { System.out.println(e); } return result;}
2.2.2 調(diào)用統(tǒng)一下單接口,獲取prepay_id
有了組裝好的xml,現(xiàn)在我們直接使用POST方式的請求發(fā)送給微信提供的接口就可以了,如果一切順利,我們會(huì)收到微信返回的xml字符串,格式示例如下:
<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[wx2421b1c4370ec43b]]></appid> <mch_id><![CDATA[10000100]]></mch_id> <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str> <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid> <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[wx201411101639507cbf6ffd8b07629950874]]></prepay_id> <trade_type><![CDATA[JSAPI]]></trade_type></xml> 其中我們最需要的就是 prepay_id,這個(gè)值在后續(xù)需要用到。這段過程比較簡單,其中提取prepay_id我是用的正則,我直接貼代碼好了:
String url = WeChatAPI.getUrl_prePay();String result = NetUtil.sendRequest(url, "POST", xml);String reg = "<prepay_id><!\\[CDATA\\[(\\w+)\\]\\]></prepay_id>";Pattern pattern = Pattern.compile(reg);Matcher matcher = pattern.matcher(result);String prepayId = "";while (matcher.find()) { prepayId = matcher.group(1); log.debug("預(yù)支付ID:" + prepayId);} 2.3 回傳參數(shù),調(diào)起微信支付JS
2.3.1 回傳參數(shù)
這時(shí)候,已經(jīng)有了預(yù)支付ID,但是后臺的處理還沒有結(jié)束,我們還沒有把該有的信息返回給前臺。那么前臺需要哪些東西呢?《 微信支付官方文檔 - 微信內(nèi)H5調(diào)起支付》有詳細(xì)的解釋,這里再貼一下:
| 參數(shù)???? | 說明? ?? | 備注 |
| appId | 開發(fā)者應(yīng)用ID,在 “開發(fā) - 基本配置” 查看 | |
| timeStamp? ?? | 時(shí)間戳,標(biāo)準(zhǔn)北京時(shí)間,秒級(10位數(shù)字) | |
| nonceStr? ?? | 隨機(jī)字符串 | 參考算法:《微信支付官方文檔 - 安全規(guī)范》 |
| package? ?? | 訂單詳情擴(kuò)展字符串,其實(shí)就是預(yù)支付ID | 示例: prepay_id=*** |
| signType? ?? | 簽名方式,暫支持MD5 | |
| paySign? ?? | 簽名 | 參考算法:《微信支付官方文檔 - 安全規(guī)范》 |
有了之前的經(jīng)驗(yàn),想必到這里對這些的獲取已經(jīng)沒有什么問題了,但是仍然有幾個(gè)
注意的地方:
- package的值是 “prepay_id=***”,而不是 "***" 的方式(***表示之前獲取的prepay_id)
- timeStamp注意使用標(biāo)準(zhǔn)北京時(shí)間,可以使用Calendar設(shè)置Locale為CHINA,因?yàn)槭敲爰壦杂浀贸?000
- paySign簽名要重新生成,算法還是之前的,但是參數(shù)需要除自己以外的?appId、timeStamp、nonceStr、package、signType
- 之前xml中參數(shù)appid是小寫,這里的appId是大寫的I
好了,因?yàn)榍芭_接受到參數(shù)以后會(huì)以JSON的形式發(fā)送給微信服務(wù)器,所以我們這里后臺,直接就把這些參數(shù)封裝到一個(gè)JSONObject中就行了,然后轉(zhuǎn)成JSON的形式發(fā)給前臺。下面貼一下我的測試代碼,簽名算法和之前一樣,我這里就不重復(fù)貼出來了:
Date beijingDate = Calendar.getInstance(Locale.CHINA).getTime();
String nonceStr2 = SignUtil.createNonceStr();JSONObject json = new JSONObject();json.put("appId", appId);json.put("timeStamp", beijingDate.getTime() / 1000);json.put("nonceStr", nonceStr2);json.put("package", "prepay_id=" + prepayId);json.put("signType", "MD5");TreeMap<String, String> map2 = new TreeMap<String, String>();map2.put("appId", appId);map2.put("timeStamp", String.valueOf(beijingDate.getTime() / 1000));map2.put("nonceStr", nonceStr2);map2.put("package", "prepay_id=" + prepayId);map2.put("signType", "MD5");String paySign = SignUtil.createSign(map2);json.put("paySign", paySign);String re = json.toJSONString();AjaxSupport.sendSuccessText(null, re);
2.3.2 使用微信內(nèi)置的JS調(diào)起微信支付
前臺的調(diào)用就很簡單了,看下官方給的示例代碼:
function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":"wx2421bk1c4370c43b", //公眾號名稱,由商戶傳入 "timeStamp":"1395712654", //時(shí)間戳,自1970年以來的秒數(shù) "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //隨機(jī)串 "package":"prepay_id=u802345jfgjsdfgsdg888", "signType":"MD5", //微信簽名方式: "paySign":"70EA570631E4B79628FBCS90534C63FF7FADD89" //微信簽名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) {} ? // 使用以上方式判斷前端返回,微信團(tuán)隊(duì)鄭重提示:res.err_msg將在用戶支付成功后返回ok,但并不保證它絕對可靠。 } ); }if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); }}else{ onBridgeReady();}
使用時(shí)直接替換掉invoke方法中的參數(shù)即可,實(shí)際上如果后臺直接是傳遞的JSON字符串到前臺,可以直接解析為JS對象作為參數(shù),下面貼我自己的代碼:
$().invoke("/pay/do/pay.q", null, function (re) { var result = JSON.parse(re); function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', result, function(res){ alert(JSON.stringify(res)); if(res.err_msg == "get_brand_wcpay_request:ok" ) { //doit 這里處理支付成功后的邏輯,通常為頁面跳轉(zhuǎn) } } ); } if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); }});
這里還有個(gè)
坑,是iOS和Android系統(tǒng)不同導(dǎo)致的,如上代碼:
- 如果你在 var result = JSON.parse(re); 之前再添加一個(gè)用于debug的輸出語句 ?alert(re);
- 你可以看到傳過來的各項(xiàng)參數(shù),其中timeStamp的值是沒有雙引號的,這會(huì)導(dǎo)致在iOS中支付出現(xiàn)錯(cuò)誤,提示缺少timeStamp參數(shù)
? ? 所以為了兼容,必須要將這個(gè)轉(zhuǎn)換成字符串,帶上雙引號:
$().invoke("/pay/do/pay.q", null, function (re) { var result = JSON.parse(re); result['timeStamp'] = result['timeStamp'] + ""; function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', result, function(res){ alert(JSON.stringify(res)); if(res.err_msg == "get_brand_wcpay_request:ok" ) { //doit 這里處理支付成功后的邏輯,通常為頁面跳轉(zhuǎn) } } ); } if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); }});
另外,在這個(gè)頁面調(diào)試有個(gè)小技巧,將微信回調(diào)的JS對象序列化為JSON字符串,進(jìn)行彈窗顯示:alert(JSON.stringify(res));
2.4 校驗(yàn)信息的正確性
實(shí)際上在完成上面的步驟以后,已經(jīng)可以進(jìn)行微信支付了。這最后一步主要是為了確認(rèn)支付信息的正確性,以及傳遞給我們本次支付的一些信息,以便業(yè)務(wù)處理。
支付成功后,微信會(huì)將本次支付的相關(guān)信息,以流的方式發(fā)送給我們指定的url地址,而我們指定的url地址,就是第一次組裝xml時(shí) <notify_url> 中填寫的地址,下面我們可以先回顧一下:
...String notifyUrl = "http://k169710n05.51mypc.cn/pay/do/afterPaySuccess.q";...String xml = "<xml>" + "<appid>" + appId + "</appid>" + "<body>" + body +"</body>" + "<device_info>WEB</device_info>" + "<mch_id>" + merchantId + "</mch_id>" + "<nonce_str>" + nonceStr1 + "</nonce_str>" + "<notify_url>" + notifyUrl +"</notify_url>" + "<openid>" + openId + "</openid>" + "<out_trade_no>" + tradeNo + "</out_trade_no>" + "<total_fee>" + totalFee + "</total_fee>" + "<trade_type>JSAPI</trade_type>" + "<sign>" + sign + "</sign>" + "</xml>";
而我們要做的,就是接受到這些信息后,進(jìn)行處理,并對微信服務(wù)器做出應(yīng)答。如果微信收到商戶的應(yīng)答不是成功或超時(shí),微信認(rèn)為通知失敗,微信會(huì)通過一定的策略定期重新發(fā)起通知,盡可能提高通知的成功率,但微信不保證通知最終能成功。詳情請參考《 微信支付官方文檔 - 支付結(jié)果通知》
需要做三件事:
- 解析微信發(fā)來的信息,通過重新簽名的方式驗(yàn)證信息的正確性,確認(rèn)信息是否是微信所發(fā)
- return_code和result_code都是SUCCESS的話,處理商戶自己的業(yè)務(wù)邏輯
- 應(yīng)答微信,告訴它說我們收到信息了,不用再發(fā)了(如果不進(jìn)行應(yīng)答,則微信服務(wù)器會(huì)通過一定的策略定期重新發(fā)起通知)
過程也很簡單,將微信發(fā)來的流信息解析出來之后,再次調(diào)用之前的簽名算法,用計(jì)算出來的算法,和微信發(fā)來的xml中的簽名sign進(jìn)行對比,如果相同,則說明是微信返回的通知,響應(yīng)給微信即可。
注意:驗(yàn)證調(diào)用返回或微信主動(dòng)通知簽名時(shí),傳送的sign參數(shù)不參與簽名,而是將生成的簽名與該sign值作校驗(yàn)。也就是說,微信發(fā)來的xml中包含元素sign,該元素內(nèi)容不參與簽名算法之中,而是和最后算法的結(jié)果進(jìn)行比較的,所以傳參進(jìn)行算法的時(shí)候不用加入sign值。
好了,現(xiàn)在我們先看下微信發(fā)回來的流信息是什么,實(shí)際上文檔里有說明,就是個(gè)xml,我們看下官方的示例:
<xml> <appid><![CDATA[wx2421b1c4370ec43b]]></appid> <attach><![CDATA[支付測試]]></attach> <bank_type><![CDATA[CFT]]></bank_type> <fee_type><![CDATA[CNY]]></fee_type> <is_subscribe><![CDATA[Y]]></is_subscribe> <mch_id><![CDATA[10000100]]></mch_id> <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str> <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid> <out_trade_no><![CDATA[1409811653]]></out_trade_no> <result_code><![CDATA[SUCCESS]]></result_code> <return_code><![CDATA[SUCCESS]]></return_code> <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign> <sub_mch_id><![CDATA[10000100]]></sub_mch_id> <time_end><![CDATA[20140903131540]]></time_end> <total_fee>1</total_fee> <trade_type><![CDATA[JSAPI]]></trade_type> <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id></xml>
其中除了sign的值,其他的值需要做成集合進(jìn)行簽名算法,然后結(jié)果和sign值對比,相同的話,給微信一個(gè)應(yīng)答,應(yīng)答的格式官方也給出了示例,如下:
<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg></xml>
總之,這一部分還是很簡單的,就直接上我的代碼了:
/** * 支付成功后的處理 * <p>微信支付成功后,對微信返回的信息進(jìn)行校驗(yàn)</p> * @return */public String afterPaySuccess() { HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); TreeMap<String, String> map = new TreeMap<String, String>(); try { //解析xml,存入map InputStream inputStream = request.getInputStream(); SAXReader saxReader = new SAXReader(); Document document = saxReader.read(inputStream); Element rootElement = document.getRootElement(); List<Element> elements = rootElement.elements(); String reg = "<!\\[CDATA\\[(.+)\\]\\]>"; Pattern pattern = Pattern.compile(reg); for (Element element : elements) { String key = element.getName(); String value = element.getText(); Matcher matcher = pattern.matcher(value); while (matcher.find()) { value = matcher.group(1); } map.put(key, value); } //如果微信結(jié)果通知為失敗 if ("FAIL".equals(map.get("return_code"))) { log.debug(map.get("return_msg")); return NONE; } //doit 處理商戶業(yè)務(wù)邏輯 //簽名對比,應(yīng)答微信服務(wù)器 String signFromWechat = map.get("sign"); map.remove("sign"); String sign = SignUtil.createSign(map); if (sign.equals(signFromWechat)) { String responseXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml>"; response.getWriter().write(responseXml); } } catch (IOException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } return NONE;}
另外,如果在執(zhí)行支付流程中,有部分?jǐn)?shù)據(jù)希望能放在支付完成后再處理,可以在組裝xml的時(shí)候放置在attach標(biāo)簽中;然后在支付完成后微信發(fā)送來的xml中,會(huì)將原數(shù)據(jù)在此返回。需要注意的是,該attach有字符串的長度限制(詳見文檔),所以試圖直接在支付處理時(shí)直接把某個(gè)類的JSON格式放進(jìn)來留做事后處理,是會(huì)出錯(cuò)的(我就是這樣踩了坑),所以用來傳遞一些核心數(shù)據(jù)就行了。
再另外,對于最后這部分,看看微信推薦我們的做法是:當(dāng)收到通知進(jìn)行處理時(shí),首先檢查對應(yīng)業(yè)務(wù)數(shù)據(jù)的狀態(tài),判斷該通知是否已經(jīng)處理過,如果沒有處理過再進(jìn)行處理,如果處理過直接返回結(jié)果成功。在對業(yè)務(wù)數(shù)據(jù)進(jìn)行狀態(tài)檢查和處理之前,要采用數(shù)據(jù)鎖進(jìn)行并發(fā)控制,以避免函數(shù)重入造成的數(shù)據(jù)混亂。另,商戶系統(tǒng)對于支付結(jié)果通知的內(nèi)容一定要做簽名驗(yàn)證,并校驗(yàn)返回的訂單金額是否與商戶側(cè)的訂單金額一致,防止數(shù)據(jù)泄漏導(dǎo)致出現(xiàn)“假通知”,造成資金損失。
3、參考鏈接
- 微信支付之JSAPI開發(fā)第一篇-基本概念
- 微信公眾號支付開發(fā)全過程 --JAVA
- 原文鏈接 http://www.cnblogs.com/deng-cc/p/7183239.html
總結(jié)
以上是生活随笔為你收集整理的微信公众号开发,微信支付功能开发(网页JSAPI调用)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。