微信小程序____微信支付


引言

相信大家都會(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à)。

參考閱讀微信支付官方參考文檔

對(duì)比JSAPI、JSSDK的異同點(diǎn)

因?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í)符

注意:

  1. 會(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è)密鑰。

  2. UnionID 只在滿(mǎn)足一定條件的情況下返回。具體參看UnionID機(jī)制說(shuō)明

  3. 臨時(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  
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容