javascript
微信JSAPI之V3版本支付踩坑
前言:坑爹的微信文檔讓我不得不想發篇文章來狠狠的吐槽一下!各種各樣幾乎完全沒法區分的官方文檔混在一起,比起騰訊云的接口文檔簡直不要太差!完全不一樣的加密標準加上大同小異的入參和一樣的接口名JSAPI接口文檔讓人根本不知道應該用哪一個,就連請求的url都不一樣!最后也是最重要的一點,一個完整的可運行的demo都沒有!!!算了,不說了,忍著點。
因業務需求,需要開發微信公眾號支付功能,簡單的了解一下便可知道要對接的接口是微信JSAPI支付接口,且已更新至更為安全的V3版本。故接下來主要是圍繞微信JSAPI的微信公眾號支付,嗯,V3版本的。正確的接口文檔地址在這里,千萬不要進錯啦
https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
前期準備:一個微信公眾號,一個微信商戶號,一套前后臺系統。在這里提幾個名詞,記得區分開,很容易混亂的。為了方便大家簡單入手,我們選擇直連模式,跳過服務商這一業務環節。 APPID:騰訊家產品用得多的對這個肯定不陌生,但現在有公眾號和商戶號,是指哪一個呢?這里指的是公眾號的開發者ID,在微信公眾平臺登錄后,點擊左邊菜單欄里的開發-基本配置就能看到,如下
AppSecret:在上面也能看到,是微信公眾號里的開發者密碼,注意了,這里申請完之后就看不到了,申請時保存好,忘了就只能重置再設置了。 mchid:這個就是傳說中的商戶號id,登錄商戶平臺,一次進入賬戶中心-商戶信息,第一欄里的微信支付商戶號就是了。
API證書:商戶平臺-賬戶中心-API安全頁面,即可看到API證書,這個證書就是后面文檔里提到的商戶證書,申請流程這里就不再贅述了,申請時會有詳細指引。作用呢就是發起支付的時候加密參數用的。 商戶證書序列號:申請完上面的API證書就會得到這個。申請之后點擊查看證書可以看到,或者調用下載商戶證書的接口返回的時候也能得到。 API秘鑰:跟上面的在同一個頁面,這個也就是商家秘鑰了,功能是解密商家發起訪問時用商戶證書加密的數據的,自己用不到,平臺解密你的數據用的。如果有人更新了商戶證書,通過這個也是可以下載商戶證書的。 APIv3秘鑰:跟上面的在同一個頁面,這個也就是微信支付平臺秘鑰了,功能是下載平臺證書和回調的時候解密平臺返回參數用的。 接下來就可以進行公眾號和商戶號的綁定了,多對多關系,放心綁,怎么綁這里就不說了,平臺指引下很好找的。 綁定完成后就可以進行最后兩步準備工作了,商戶平臺-產品中心-開發配置,配置公眾號支付的授權目錄,直接填你網站的根目錄就行了,不過得保證公網可訪問。然后就是公眾平臺-安全中心,配置IP白名單,只有這里的ip在訪問公眾號的時候能拿到公眾號的token。
到這,終于可以開始開發了。首先摸清流程,后臺生成訂單,調用統一下單接口,拿到返回數據加密給前臺,前臺拿到數據拉起微信支付,支付成功回調后臺接口,后臺檢驗結果完成支付成功后的業務處理,整個流程就完了,走到這一步后面的開發就很簡單了。
統一下單:入參接口文檔已經寫得很清楚了,后面著重講解加密和驗簽。組裝好JSON參數,我們開始加密
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream("商戶證書內容,你證書 里apiclient_key.pem里面中間那一段".getBytes("utf-8")));//使用自動更新的簽名驗證器,不需要傳入證書 log.info("開始更新簽名驗證器。。。"); AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials("商戶號", new PrivateKeySigner("商戶證書序列號", merchantPrivateKey)),"APIV3秘鑰".getBytes("utf-8"));CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create().withMerchant("商戶號", "商戶證書序列號", merchantPrivateKey).withValidator(new WechatPay2Validator(verifier)).build();HttpPost post = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");StringEntity params = new StringEntity("請求參數JSON串");post.setEntity(params);post.addHeader("Accept", "application/json");post.addHeader("Content-Type", "application/json");HttpResponse response = httpClient.execute(post);String resp = EntityUtils.toString(response.getEntity());log.info("微信統一下單返回: " + resp);到這就完成了統一下單的內容了,注意上面兩處設置的請求頭是不能省的。PemUtil、AutoUpdateCertificatesVerifier、AutoUpdateCertificatesVerifier、WechatPay2Credentials、PrivateKeySigner、WechatPayHttpClientBuilder等等還有后面沒見過的類,在這里的都能找到 https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient/tree/master/src/main/java/com/wechat/pay/contrib/apache/httpclient 剩下的基本都是Apache包里的東西,一般都不會引用錯的。
然后就可以對返回的prepayId進行加密了
long timestamp = System.currentTimeMillis() / 1000; String nonceStr = RandomStringUtils.random(32, "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"); PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream("商戶證書內容,你證書里apiclient_key.pem里面中間那一段".getBytes("utf-8")));//使用自動更新的簽名驗證器,不需要傳入證書 log.info("開始更新簽名驗證器。。。"); AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials("商戶號", new PrivateKeySigner("商戶證書序列號", merchantPrivateKey)),"APIV3秘鑰".getBytes("utf-8"));CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create().withMerchant("商戶號", "商戶證書序列號", merchantPrivateKey).withValidator(new WechatPay2Validator(verifier)).build();/* 構造簽名的數據格式為APPID + \n時間戳 + \n隨機字符串 + \nprepayId + \n */ String message = "APPID" + "\n"+ timestamp + "\n"+ nonceStr + "\nprepay_id="+ prepayId + "\n"; Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(merchantPrivateKey); sign.update(message.getBytes("utf-8")); String signature = Base64.getEncoder().encodeToString(sign.sign()); log.info("加密后簽名為:" + signature); httpClient.close(); JSONObject jsonObject = new JSONObject(); jsonObject.put("appId","APPID"); jsonObject.put("timeStamp", timestamp); jsonObject.put("nonceStr", nonceStr); jsonObject.put("package", "prepay_id=" + prepayId); jsonObject.put("signType", "RSA"); jsonObject.put("paySign", signature);到此就拿到了前臺去拉起微信支付的參數jsonObject,然后就是回調接口等回調了~~~回調不解釋,直接上代碼
String wechatpaySignature = request.getHeader("Wechatpay-Signature"); //微信服務器的時間戳 String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp"); //微信服務器提供的隨機串 String wechatpayNonce = request.getHeader("Wechatpay-Nonce"); //微信服務器應答主體 String body = getBody(request); PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream("商戶證書內容,你證書 里apiclient_key.pem里面中間那一段".getBytes("utf-8")));//使用自動更新的簽名驗證器,不需要傳入證書 log.info("開始更新簽名驗證器。。。"); AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials("商戶號", new PrivateKeySigner("商戶證書序列號", merchantPrivateKey)),"APIV3秘鑰".getBytes("utf-8")); X509Certificate validCertificate = verifier.getValidCertificate(); try {//獲取請求頭中的時間戳,隨機串和請求體body組成簽名串final String signatureStr = responseSign(wechatpayTimestamp, wechatpayNonce, body);//使用微信支付平臺公鑰對驗簽名串和簽名進行SHA256 with RSA簽名驗證Signature signer = Signature.getInstance("SHA256withRSA");signer.initVerify(validCertificate);signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));if (signer.verify(Base64Utils.decodeFromString(wechatpaySignature))) {String ciphertext = JSONObject.parseObject(body).getJSONObject("resource").getString("ciphertext");String associatedData = JSONObject.parseObject(body).getJSONObject("resource").getString("associated_data");String nonce = JSONObject.parseObject(body).getJSONObject("resource").getString("nonce");String result = new AesUtil("APIV3秘鑰".getBytes()).decryptToString(associatedData.getBytes(), nonce.getBytes(), ciphertext);log.info("微信支付回調解密結果:" + result);return result; } return ""; } catch (Exception e) {log.error("驗簽異常,異常原因:" + e.getMessage(), e);return ""; }到這,驗簽通過就能解析到支付回調的具體數據,后面就是支付成功之后該有的業務邏輯了。至此就算對接完成了,文檔什么清晰之后就舒服多了,不過關于微信小程序、微信公眾號、微信商戶號什么的各種各樣的APPID也確實超級多,后面開通了更多的服務會有更多的這種id,不理清楚其實很頭疼的。
我是本篇的小編DJ,大家工作或者開發中遇到Bug需要幫助,歡迎加wx:xmzl1988
總結
以上是生活随笔為你收集整理的微信JSAPI之V3版本支付踩坑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《高级无线网络—4G技术》——2.4 多
- 下一篇: gradle idea java ssm