引言
相信大家都會(huì)對(duì)微信支付非常熟悉,什么樣的開(kāi)發(fā)場(chǎng)景能少得了支付的環(huán)節(jié)呢?
Let's do it~
開(kāi)發(fā)接入官方文檔閱讀
不知幾何時(shí)起,有人就跟我提過(guò),微信小程序的開(kāi)發(fā)離不開(kāi)微信官方文檔的說(shuō)明,需要反復(fù)看個(gè)多遍,其義自見(jiàn),請(qǐng)?jiān)试S我多說(shuō)了這些廢話(huà)。
參考閱讀微信支付官方參考文檔

因?yàn)樾〕绦蛘{(diào)起支付頁(yè)面的協(xié)議是HTTPS(程序訪(fǎng)問(wèn)商戶(hù)服務(wù)都是通過(guò)HTTPS)
開(kāi)發(fā)部署的時(shí)候需要安裝HTTPS服務(wù)器(此處就不延伸擴(kuò)展安裝https服務(wù)了)
現(xiàn)提供另外一種方式,阿里云申請(qǐng)免費(fèi)SSL證書(shū)并在Nginx上進(jìn)行安裝
業(yè)務(wù)說(shuō)明



業(yè)務(wù)流程

商戶(hù)系統(tǒng)和微信支付系統(tǒng)主要交互:
1、小程序內(nèi)調(diào)用登錄接口,獲取到用戶(hù)的openid,api參見(jiàn)公共api【小程序登錄API】
2、商戶(hù)server調(diào)用支付統(tǒng)一下單,api參見(jiàn)公共api【統(tǒng)一下單API】
3、商戶(hù)server調(diào)用再次簽名,api參見(jiàn)公共api【再次簽名】
4、商戶(hù)server接收支付通知,api參見(jiàn)公共api【支付結(jié)果通知API】
5、商戶(hù)server查詢(xún)支付結(jié)果,api參見(jiàn)公共api【查詢(xún)訂單API】
微信支付 Java后端
前面贅述了那么多官方文檔的開(kāi)發(fā)簡(jiǎn)介,下面開(kāi)始真正的實(shí)戰(zhàn)!
微信小程序支付的前置準(zhǔn)備:
- 1.微信商戶(hù)平臺(tái)賬號(hào)
- 2.微信小程序賬號(hào)
- 3.微信小程序開(kāi)通支付接口(個(gè)人暫時(shí)不支持開(kāi)通支付)
前臺(tái)需要的操作:
- 1.登錄獲取code,傳給開(kāi)發(fā)者后臺(tái)
- 6.獲取后臺(tái)傳過(guò)來(lái)的值調(diào)用wx.requestPayment方法
后臺(tái)需要的操作:
- 2.通過(guò)前臺(tái)傳過(guò)來(lái)的code來(lái)獲取用戶(hù)的openId
- 3.生成sign
- 4.獲取perpay_id
- 5.再生成一次前臺(tái)需要的paySign
一、小程序登錄API
具體操作:
- 小程序調(diào)用wx.login() 獲取 臨時(shí)登錄憑證code ,并回傳到開(kāi)發(fā)者服務(wù)器。
#示例代碼
//app.js
App({
onLaunch: function() {
wx.login({
success: function(res) {
if (res.code) {
//發(fā)起網(wǎng)絡(luò)請(qǐng)求
wx.request({
url: 'https://test.com/onLogin',
data: {
code: res.code
}
})
} else {
console.log('登錄失??!' + res.errMsg)
}
}
});
}
})
- 將臨時(shí)登錄憑證code回傳到開(kāi)發(fā)者服務(wù)器
wx.request({
url: 'https://URL',//后臺(tái)URL
data: {},//登錄獲取的code
method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
// header: {}, // 設(shè)置請(qǐng)求的 header//后臺(tái)規(guī)定的請(qǐng)求頭
success: function(res){
// success
},
fail: function(res) {
// fail
},
complete: function(res) {
// complete
}
})
- 登錄憑證校驗(yàn)
此波操作是在開(kāi)發(fā)者服務(wù)器進(jìn)行的。
臨時(shí)登錄憑證校驗(yàn)接口是一個(gè) HTTPS 接口,開(kāi)發(fā)者服務(wù)器使用 臨時(shí)登錄憑證code 獲取 session_key 和 openid 等。
開(kāi)發(fā)者服務(wù)器通過(guò)code獲取openid只需要訪(fǎng)問(wèn)這個(gè)鏈接:
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
請(qǐng)求參數(shù)
| 參數(shù) | 必填 | 說(shuō)明 |
|---|---|---|
| appid | 是 | 小程序唯一標(biāo)識(shí) |
| secret | 是 | 小程序的 app secret |
| js_code | 是 | 登錄時(shí)獲取的 code |
| grant_type | 是 | 填寫(xiě)為 authorization_code |
在不滿(mǎn)足UnionID下發(fā)條件的情況下,返回參數(shù)
| 參數(shù) | 說(shuō)明 |
|---|---|
| openid | 用戶(hù)唯一標(biāo)識(shí) |
| session_key | 會(huì)話(huà)密鑰 |
在滿(mǎn)足UnionID下發(fā)條件的情況下,返回參數(shù)
| 參數(shù) | 說(shuō)明 |
|---|---|
| openid | 用戶(hù)唯一標(biāo)識(shí) |
| session_key | 會(huì)話(huà)密鑰 |
| unionid | 用戶(hù)在開(kāi)放平臺(tái)的唯一標(biāo)識(shí)符 |
注意:
會(huì)話(huà)密鑰session_key 是對(duì)用戶(hù)數(shù)據(jù)進(jìn)行加密簽名的密鑰。為了應(yīng)用自身的數(shù)據(jù)安全,開(kāi)發(fā)者服務(wù)器不應(yīng)該把會(huì)話(huà)密鑰下發(fā)到小程序,也不應(yīng)該對(duì)外提供這個(gè)密鑰。
UnionID 只在滿(mǎn)足一定條件的情況下返回。具體參看UnionID機(jī)制說(shuō)明
臨時(shí)登錄憑證code只能使用一次
返回說(shuō)明
//正常返回的JSON數(shù)據(jù)包
{
"openid": "OPENID",
"session_key": "SESSIONKEY",
}
//滿(mǎn)足UnionID返回條件時(shí),返回的JSON數(shù)據(jù)包
{
"openid": "OPENID",
"session_key": "SESSIONKEY",
"unionid": "UNIONID"
}
//錯(cuò)誤時(shí)返回JSON數(shù)據(jù)包(示例為Code無(wú)效)
{
"errcode": 40029,
"errmsg": "invalid code"
}
統(tǒng)一下單API
商戶(hù)在小程序中先調(diào)用該接口在微信支付服務(wù)后臺(tái)生成預(yù)支付交易單,返回正確的預(yù)支付交易后調(diào)起支付。
- 接口鏈接
URL地址:
https://api.mch.weixin.qq.com/pay/unifiedorder
這個(gè)鏈接必須是xml格式的,前臺(tái)是沒(méi)法調(diào)用的。
請(qǐng)求參數(shù)
| 字段名 | 變量名 | 必填 | 類(lèi)型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 小程序ID | appid | 是 | String(32) | wxd678efh567hg6787 | 微信分配的小程序ID |
| 商戶(hù)號(hào) | mch_id | 是 | String(32) | 1230000109 | 微信支付分配的商戶(hù)號(hào) |
| 設(shè)備號(hào) | device_info | 否 | String(32) | 013467007045764 | 自定義參數(shù),可以為終端設(shè)備號(hào)(門(mén)店號(hào)或收銀設(shè)備ID),PC網(wǎng)頁(yè)或公眾號(hào)內(nèi)支付可以傳"WEB" |
| 隨機(jī)字符串 | nonce_str | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 隨機(jī)字符串,長(zhǎng)度要求在32位以?xún)?nèi)。推薦隨機(jī)數(shù)生成算法 |
| 簽名 | sign | 是 | String(32) | C380BEC2BFD727A4B6845133519F3AD6 | 通過(guò)簽名算法計(jì)算得出的簽名值,詳見(jiàn)簽名生成算法 |
| 簽名類(lèi)型 | sign_type | 否 | String(32) | MD5 | 簽名類(lèi)型,默認(rèn)為MD5,支持HMAC-SHA256和MD5。 |
| 商品描述 | body | 是 | String(128) | 騰訊充值中心-QQ會(huì)員充值 | 商品簡(jiǎn)單描述,該字段請(qǐng)按照規(guī)范傳遞,具體請(qǐng)見(jiàn)參數(shù)規(guī)定 |
| 商品詳情 | detail | 否 | String(6000) | 商品詳細(xì)描述,對(duì)于使用單品優(yōu)惠的商戶(hù),改字段必須按照規(guī)范上傳,詳見(jiàn)“單品優(yōu)惠參數(shù)說(shuō)明” | |
| 附加數(shù)據(jù) | attach | 否 | String(127) | 深圳分店 | 附加數(shù)據(jù),在查詢(xún)API和支付通知中原樣返回,可作為自定義參數(shù)使用。 |
| 商戶(hù)訂單號(hào) | out_trade_no | 是 | String(32) | 20150806125346 | 商戶(hù)系統(tǒng)內(nèi)部訂單號(hào),要求32個(gè)字符內(nèi),只能是數(shù)字、大小寫(xiě)字母_-|*且在同一個(gè)商戶(hù)號(hào)下唯一。詳見(jiàn)商戶(hù)訂單號(hào) |
| 標(biāo)價(jià)幣種 | fee_type | 否 | String(16) | CNY | 符合ISO 4217標(biāo)準(zhǔn)的三位字母代碼,默認(rèn)人民幣:CNY,詳細(xì)列表請(qǐng)參見(jiàn)貨幣類(lèi)型 |
| 標(biāo)價(jià)金額 | total_fee | 是 | Int 88 | 訂單總金額,單位為分,詳見(jiàn)支付金額 | |
| 終端IP | spbill_create_ip | 是 | String(16) | 123.12.12.123 | APP和網(wǎng)頁(yè)支付提交用戶(hù)端ip,Native支付填調(diào)用微信支付API的機(jī)器IP。 |
| 交易起始時(shí)間 | time_start | 否 | String(14) | 20091225091010 | 訂單生成時(shí)間,格式為yyyyMMddHHmmss,如2009年12月25日9點(diǎn)10分10秒表示為20091225091010。其他詳見(jiàn)時(shí)間規(guī)則 |
| 交易結(jié)束時(shí)間 | time_expire | 否 | String(14) | 20091227091010 | 訂單失效時(shí)間,格式為yyyyMMddHHmmss,如2009年12月27日9點(diǎn)10分10秒表示為20091227091010。訂單失效時(shí)間是針對(duì)訂單號(hào)而言的,由于在請(qǐng)求支付的時(shí)候有一個(gè)必傳參數(shù)prepay_id只有兩小時(shí)的有效期,所以在重入時(shí)間超過(guò)2小時(shí)的時(shí)候需要重新請(qǐng)求下單接口獲取新的prepay_id。其他詳見(jiàn)時(shí)間規(guī)則 建議:最短失效時(shí)間間隔大于1分鐘 |
| 訂單優(yōu)惠標(biāo)記 | goods_tag | 否 | String(32) | WXG 訂單優(yōu)惠標(biāo)記,使用代金券或立減優(yōu)惠功能時(shí)需要的參數(shù),說(shuō)明詳見(jiàn)代金券或立減優(yōu)惠 | |
| 通知地址 | notify_url | 是 | String(256) | http://www.weixin.qq.com/wxpay/pay.php | 異步接收微信支付結(jié)果通知的回調(diào)地址,通知url必須為外網(wǎng)可訪(fǎng)問(wèn)的url,不能攜帶參數(shù)。 |
| 交易類(lèi)型 | trade_type | 是 | String(16) | JSAPI | 小程序取值如下:JSAPI,詳細(xì)說(shuō)明見(jiàn)參數(shù)規(guī)定 |
| 商品ID | product_id | 否 | String(32) | 12235413214070356458058 | trade_type=NATIVE時(shí)(即掃碼支付),此參數(shù)必傳。此參數(shù)為二維碼中包含的商品ID,商戶(hù)自行定義。 |
| 指定支付方式 | limit_pay | 否 | String(32) | no_credit | 上傳此參數(shù)no_credit--可限制用戶(hù)不能使用信用卡支付 |
| 用戶(hù)標(biāo)識(shí) | openid | 否 | String(128) | oUpF8uMuAJO_M2pxb1Q9zNjWeS6o | trade_type=JSAPI,此參數(shù)必傳,用戶(hù)在商戶(hù)appid下的唯一標(biāo)識(shí)。openid如何獲取,可參考【獲取openid】。 |
舉例如下:
<xml>
<appid>wx2421b1c4370ec43b</appid>
<attach>支付測(cè)試</attach>
<body>JSAPI支付測(cè)試</body>
<mch_id>10000100</mch_id>
<detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"蘋(píng)果手機(jī)" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"蘋(píng)果手機(jī)" } ] }]]></detail>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>JSAPI</trade_type>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>
注:參數(shù)值用XML轉(zhuǎn)義即可,CDATA標(biāo)簽用于說(shuō)明數(shù)據(jù)不被XML解析器解析。
總結(jié)一下請(qǐng)求參數(shù)必填項(xiàng)
| 字段名 | 變量名 | 描述 |
|---|---|---|
| 小程序ID | appid | 微信分配的小程序ID |
| 商戶(hù)號(hào) | mch_id | 微信支付分配的商戶(hù)號(hào) |
| 隨機(jī)字符串 | nonce_str | 隨機(jī)字符串,長(zhǎng)度要求在32位以?xún)?nèi)。推薦隨機(jī)數(shù)生成算法 |
| 簽名 | sign | 通過(guò)簽名算法計(jì)算得出的簽名值,詳見(jiàn)簽名生成算法 |
| 商品描述 | body | 商品簡(jiǎn)單描述,該字段請(qǐng)按照規(guī)范傳遞,具體請(qǐng)見(jiàn)參數(shù)規(guī)定 |
| 商戶(hù)訂單號(hào) | out_trade_no | 商戶(hù)系統(tǒng)內(nèi)部訂單號(hào),要求32個(gè)字符內(nèi),只能是數(shù)字、大小寫(xiě)字母_-*且在同一個(gè)商戶(hù)號(hào)下唯一。詳見(jiàn)商戶(hù)訂單號(hào) |
| 標(biāo)價(jià)金額 | total_fee | 訂單總金額,單位為分,詳見(jiàn)支付金額 |
| 終端IP | spbill_create_ip | APP和網(wǎng)頁(yè)支付提交用戶(hù)端ip,Native支付填調(diào)用微信支付API的機(jī)器IP。 |
| 通知地址 | notify_url | 異步接收微信支付結(jié)果通知的回調(diào)地址,通知url必須為外網(wǎng)可訪(fǎng)問(wèn)的url,不能攜帶參數(shù)。 |
| 交易類(lèi)型 | trade_type | 小程序取值如下:JSAPI,詳細(xì)說(shuō)明見(jiàn)參數(shù)規(guī)定 |
| 用戶(hù)標(biāo)識(shí) | openid | trade_type=JSAPI,此參數(shù)必傳,用戶(hù)在商戶(hù)appid下的唯一標(biāo)識(shí)。openid如何獲取,可參考【獲取openid】。 |
以上提及的參數(shù)格式參考微信支付參數(shù)規(guī)定
返回結(jié)果
| 字段名 | 變量名 | 必填 | 類(lèi)型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 返回狀態(tài)碼 | return_code | 是 | String(16) | SUCCESS | SUCCESS/FAIL 此字段是通信標(biāo)識(shí),非交易標(biāo)識(shí),交易是否成功需要查看result_code來(lái)判斷 |
| 返回信息 | return_msg | 否 | String(128) 簽名失敗 | 返回信息,如非空,為錯(cuò)誤原因 | 簽名失敗 參數(shù)格式校驗(yàn)錯(cuò)誤 |
以下字段在return_code為SUCCESS的時(shí)候有返回
| 字段名 | 變量名 | 必填 | 類(lèi)型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 小程序ID | appid | 是 | String(32) | wx8888888888888888 | 調(diào)用接口提交的小程序ID |
| 商戶(hù)號(hào) | mch_id | 是 | String(32) | 1900000109 | 調(diào)用接口提交的商戶(hù)號(hào) |
| 設(shè)備號(hào) | device_info | 否 | String(32) | 013467007045764 | 自定義參數(shù),可以為請(qǐng)求支付的終端設(shè)備號(hào)等 |
| 隨機(jī)字符串 | nonce_str | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 微信返回的隨機(jī)字符串 |
| 簽名 | sign | 是 | String(32) | C380BEC2BFD727A4B6845133519F3AD6 | 微信返回的簽名值,詳見(jiàn)簽名算法 |
| 業(yè)務(wù)結(jié)果 | result_code | 是 | String(16) | SUCCESS | SUCCESS/FAIL |
| 錯(cuò)誤代碼 | err_code | 否 | String(32) | SYSTEMERROR 詳細(xì)參見(jiàn)下文錯(cuò)誤列表 | |
| 錯(cuò)誤代碼描述 | err_code_des | 否 | String(128) | 系統(tǒng)錯(cuò)誤 錯(cuò)誤信息描述 |
以下字段在return_code 和result_code都為SUCCESS的時(shí)候有返回
| 字段名 | 變量名 | 必填 | 類(lèi)型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 交易類(lèi)型 | trade_type | 是 | String(16) | JSAPI | 交易類(lèi)型,取值為:JSAPI,NATIVE,APP等,說(shuō)明詳見(jiàn)參數(shù)規(guī)定 |
| 預(yù)支付交易會(huì)話(huà)標(biāo)識(shí) | prepay_id | 是 | String(64) | wx201410272009395522657a690389285100 | 微信生成的預(yù)支付會(huì)話(huà)標(biāo)識(shí),用于后續(xù)接口調(diào)用中使用,該值有效期為2小時(shí) |
| 二維碼鏈接 | code_url | 否 | String(64) | URl:weixin://wxpay/s/An4baqw | trade_type為NATIVE時(shí)有返回,用于生成二維碼,展示給用戶(hù)進(jìn)行掃碼支付 |
舉例如下:
<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[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
<trade_type><![CDATA[JSAPI]]></trade_type>
</xml>
此大項(xiàng)步驟用于生成sign和得到prepay_id(此處后臺(tái)用JAVA由于后臺(tái)代碼太多,我就放在最后面,有需要的可以自己進(jìn)行查看)
其中獲取prepay_id生成的sign需要的參數(shù)是我遇到的一個(gè)坑,請(qǐng)一定要注意你往微信傳的值都是sign需要的value,其中openId在參數(shù)必填里面寫(xiě)的是“否”,但是微信小程序支付用到的trade_type是JSAPI,所以微信小程序的openId也是必須的參數(shù)。
當(dāng)這些都準(zhǔn)備好了之后你就可以獲取perpay_id。
再次簽名即為小程序調(diào)起支付API
小程序調(diào)起支付數(shù)據(jù)簽名字段列表:
| 字段名 | 變量名 | 必填 | 類(lèi)型 | 示例值 | 描述 |
|---|---|---|---|---|---|
| 小程序ID | appId | 是 | String | wxd678efh567hg6787 | 微信分配的小程序ID |
| 時(shí)間戳 | timeStamp | 是 | String | 1490840662 | 時(shí)間戳從1970年1月1日00:00:00至今的秒數(shù),即當(dāng)前的時(shí)間 |
| 隨機(jī)串 | nonceStr | 是 | String | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 隨機(jī)字符串,不長(zhǎng)于32位。推薦隨機(jī)數(shù)生成算法 |
| 數(shù)據(jù)包 | package | 是 | String | prepay_id=wx2017033010242291fcfe0db70013231072 | 統(tǒng)一下單接口返回的 prepay_id 參數(shù)值,提交格式如:prepay_id=wx2017033010242291fcfe0db70013231072 |
| 簽名方式 | signType | 是 | String | MD5 | 簽名類(lèi)型,默認(rèn)為MD5,支持HMAC-SHA256和MD5。注意此處需與統(tǒng)一下單的簽名類(lèi)型一致 |
舉例如下:
paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D9B4E54AB1950F51E0649E8810ACD6
調(diào)用wx.requestPayment(OBJECT)發(fā)起微信支付
Object參數(shù)說(shuō)明:
| 參數(shù) | 類(lèi)型 | 必填 | 說(shuō)明 |
|---|---|---|---|
| timeStamp | String | 是 | 時(shí)間戳從1970年1月1日00:00:00至今的秒數(shù),即當(dāng)前的時(shí)間 |
| nonceStr | String | 是 | 隨機(jī)字符串,長(zhǎng)度為32個(gè)字符以下。 |
| package | String | 是 | 統(tǒng)一下單接口返回的 prepay_id 參數(shù)值,提交格式如:prepay_id=*** |
| signType | String | 是 | 簽名類(lèi)型,默認(rèn)為MD5,支持HMAC-SHA256和MD5。注意此處需與統(tǒng)一下單的簽名類(lèi)型一致 |
| paySign | String | 是 | 簽名,具體簽名方案參見(jiàn)微信公眾號(hào)支付幫助文檔; |
| success | Function | 否 | 接口調(diào)用成功的回調(diào)函數(shù) |
| fail | Function | 否 | 接口調(diào)用失敗的回調(diào)函數(shù) |
| complete | Function | 否 | 接口調(diào)用結(jié)束的回調(diào)函數(shù)(調(diào)用成功、失敗都會(huì)執(zhí)行) |
回調(diào)結(jié)果:
| 回調(diào)類(lèi)型 | errMsg | 說(shuō)明 |
|---|---|---|
| success | requestPayment:ok | 調(diào)用支付成功 |
| fail | requestPayment:fail cancel | 用戶(hù)取消支付 |
| fail | requestPayment:fail (detail message) | 調(diào)用支付失敗,其中 detail message 為后臺(tái)返回的詳細(xì)失敗原因 |
示例代碼:
wx.requestPayment(
{
'timeStamp': '',
'nonceStr': '',
'package': '',
'signType': 'MD5',
'paySign': '',
'success':function(res){},
'fail':function(res){},
'complete':function(res){}
})
本步驟在于從上步驟獲取到的preppay_id作為請(qǐng)求參數(shù),返回paySign參數(shù)。
有了prepay_id之后,再獲取paySign(前臺(tái)需要的)。這里不一定需要后臺(tái)生成,前臺(tái)也有相關(guān)的代碼來(lái)生成。
所需要的參數(shù):
appId,nonceStr,package,signType,timeStamp,key
這是我遇到的坑二,因?yàn)樯喜襟E中獲取到prepay_id時(shí)會(huì)給你返回一個(gè)sign,我之前以為是這個(gè)。
支付結(jié)果通知API
支付完成后,微信會(huì)把相關(guān)支付結(jié)果和用戶(hù)信息發(fā)送給商戶(hù),商戶(hù)需要接收處理,并返回應(yīng)答。
對(duì)后臺(tái)通知交互時(shí),如果微信收到商戶(hù)的應(yīng)答不是成功或超時(shí),微信認(rèn)為通知失敗,微信會(huì)通過(guò)一定的策略定期重新發(fā)起通知,盡可能提高通知的成功率,但微信不保證通知最終能成功。 (通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒)
注意:同樣的通知可能會(huì)多次發(fā)送給商戶(hù)系統(tǒng)。商戶(hù)系統(tǒng)必須能夠正確處理重復(fù)的通知。
推薦的做法是,當(dāng)收到通知進(jìn)行處理時(shí),首先檢查對(duì)應(yīng)業(yè)務(wù)數(shù)據(jù)的狀態(tài),判斷該通知是否已經(jīng)處理過(guò),如果沒(méi)有處理過(guò)再進(jìn)行處理,如果處理過(guò)直接返回結(jié)果成功。在對(duì)業(yè)務(wù)數(shù)據(jù)進(jìn)行狀態(tài)檢查和處理之前,要采用數(shù)據(jù)鎖進(jìn)行并發(fā)控制,以避免函數(shù)重入造成的數(shù)據(jù)混亂。
特別提醒:商戶(hù)系統(tǒng)對(duì)于支付結(jié)果通知的內(nèi)容一定要做簽名驗(yàn)證,并校驗(yàn)返回的訂單金額是否與商戶(hù)側(cè)的訂單金額一致,防止數(shù)據(jù)泄漏導(dǎo)致出現(xiàn)“假通知”,造成資金損失。
該鏈接是通過(guò)【統(tǒng)一下單API】中提交的參數(shù)notify_url設(shè)置,如果鏈接無(wú)法訪(fǎng)問(wèn),商戶(hù)將無(wú)法接收到微信通知。
通知url必須為直接可訪(fǎng)問(wèn)的url,不能攜帶參數(shù)。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”
代碼貼士
得到sign的代碼:
/**
* 微信支付簽名算法sign
* @param characterEncoding
* @param parameters
* @return
*/
@Test
public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();//所有參與傳參的參數(shù)按照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=" + WeChatPayUtils.key);
System.out.println("字符串:"+sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
MD5Util工具類(lèi)代碼如下:
package cn.cqzdkj.utils;
import java.security.MessageDigest;
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" };
}
然后在需要的地方直接調(diào)用上面的createSign這個(gè)方法就行了。
SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
parameters.put("appid", appid);
parameters.put("mch_id", mch_id);
parameters.put("nonce_str",nonce_str);
parameters.put("body", body);
parameters.put("out_trade_no", "20170215");
parameters.put("total_fee", 1);
parameters.put("spbill_create_ip", "x.x.x.x");
parameters.put("notify_url","http://xxxxx.com");
parameters.put("trade_type", "JSAPI");
parameters.put("openid", "oGY_ZvxxxxxM");
parameters.put("sign","");
String characterEncoding = "UTF-8";
String mySign = createSign(characterEncoding,parameters);
System.out.println("我 的簽名是:"+mySign);
小程序端完整代碼如下:
/**
* 支付函數(shù)
* @param {[type]} _payInfo [description]
* @return {[type]} [description]
*/
pay:function(_payInfo,success,fail){
var payInfo = {
body:'',
total_fee:0,
order_sn:''
}
Object.assign(payInfo, _payInfo);
if(payInfo.body.length==0){
wx.showToast({
title:'支付信息描述錯(cuò)誤'
})
return false;
}
if(payInfo.total_fee==0){
wx.showToast({
title:'支付金額不能0'
})
return false;
}
if(payInfo.order_sn.length==0){
wx.showToast({
title:'訂單號(hào)不能為空'
})
return false;
}
var This = this;
This.getOpenid(function(openid){
payInfo.openid=openid;
This.request({
url:'api/pay/prepay',
data:payInfo,
success:function(res){
var data = res.data;
console.log(data);
if(!data.status){
wx.showToast({
title:data['errmsg']
})
return false;
}
This.request({
url:'api/pay/pay',
data:{prepay_id:data.data.data.prepay_id},
success:function(_payResult){
var payResult = _payResult.data;
console.log(payResult);
wx.requestPayment({
'timeStamp': payResult.timeStamp.toString(),
'nonceStr': payResult.nonceStr,
'package': payResult.package,
'signType': payResult.signType,
'paySign': payResult.paySign,
'success': function (succ) {
success&&success(succ);
},
'fail': function (err) {
fail&&fail(err);
},
'complete': function (comp) {
}
})
}
})
}
})
})
}
#參考于https://blog.csdn.net/sinat_35861727/article/details/73692794