云開發(fā) 微信支付

2020-07-22 15:32 更新

微信支付云調(diào)用(云支付),可以免鑒權(quán)快速調(diào)用微信支付的開放能力,開發(fā)者無需關(guān)心證書、簽名、也無需依賴第三方模塊,免去了泄漏證書,支付等敏感信息的風(fēng)險(xiǎn);還支持云函數(shù)作為微信支付進(jìn)行支付和退款的回調(diào)地址,不再需要定時(shí)輪詢,更加高效。只需在開發(fā)者工具1.02.2005111 (2020年5月11日版)的云開發(fā)控制臺(tái)綁定微信支付商戶號(hào),在綁定完成后可在云開發(fā)中原生接入微信支付。

一、云支付快速入門

1、開通微信支付云調(diào)用

要開通微信支付云調(diào)用,首先需要小程序已經(jīng)開通了微信支付,而微信支付是不支持個(gè)人小程序的,需要企業(yè)賬戶才行,其次需要小程序已經(jīng)綁定了商戶號(hào)。滿足這兩個(gè)條件之后,我們可以在云開發(fā)控制臺(tái)(注意開發(fā)者工具的版本)- 設(shè)置- 全局設(shè)置中開通。

點(diǎn)擊添加商戶號(hào)后進(jìn)行賬號(hào)綁定,這時(shí)候綁定了微信支付的商戶號(hào)管理員的微信會(huì)收到一條授權(quán)確認(rèn)的模板消息,點(diǎn)擊模板消息會(huì)彈出服務(wù)商助手小程序,確認(rèn)授權(quán)之后就可以在云開發(fā)控制臺(tái)看到綁定狀態(tài)為“已綁定”,而JS API權(quán)限也會(huì)顯示“已授權(quán)”。

jsapi和api退款權(quán)限授權(quán),需要前往微信支付商戶平臺(tái)-產(chǎn)品中心-我的授權(quán)產(chǎn)品中進(jìn)行確認(rèn)授權(quán)完成授權(quán)后才可以調(diào)用微信支付相關(guān)接口能力。如果你在你的產(chǎn)品中心看不到我的授權(quán)產(chǎn)品,可以點(diǎn)擊鏈接:授權(quán)產(chǎn)品

2、微信支付流程說明

用微信支付云調(diào)用來實(shí)現(xiàn)完整的支付功能,大體上會(huì)經(jīng)過以下4個(gè)步驟(后面在代碼的寫法上有些步驟會(huì)整合到一起):

  • 1、用戶在小程序端點(diǎn)擊支付時(shí)使用wx.cloud.callFunction調(diào)用云函數(shù)(比如云函數(shù)名為pay),并將商品名稱、商品價(jià)格等信息傳遞給pay云函數(shù);

  • 2、在pay云函數(shù)中調(diào)用統(tǒng)一下單接口CloudPay.unifiedOrder(),參數(shù)包括接收的商品信息、云函數(shù)環(huán)境id,以及需要填寫結(jié)果通知回調(diào)函數(shù)(比如函數(shù)名為paynotice)用來接收異步支付結(jié)果;pay云函數(shù)會(huì)返回的成功結(jié)果對象中會(huì)包含payment字段;

  • 3、在小程序端wx.cloud.callFunction的success回調(diào)函數(shù)(也就是拿到云函數(shù)返回的對象)里調(diào)用wx.requestPayment接口發(fā)起支付,而從pay云函數(shù)返回的payment對象(字段)就包含這個(gè)接口所需要的所有信息(參數(shù));這時(shí)會(huì)彈出微信支付的界面;

  • 4、用戶在小程序端支付成功,paynotice就會(huì)接受到異步的支付結(jié)果,我們可以在paynotice云函數(shù)里進(jìn)行發(fā)送訂閱消息以及將支付成功的信息更新到數(shù)據(jù)庫等操作

3、微信支付的簡單案例

我們可以在小程序的wxml頁面比如pay.wxml頁面,點(diǎn)擊某個(gè)button組件時(shí),通過事件處理函數(shù)比如callPay,來調(diào)用pay云函數(shù),代碼如下:

  1. <button bindtap ="callPay">發(fā)起支付</button>

然后再在pay.js里輸入事件處理函數(shù)callPay,調(diào)用的支付云函數(shù)名稱為pay(名稱任意),注意成功的回調(diào)函數(shù)的寫法如下,這里把支付流程的第1步和第3步整到了一起:

尤其是const payment = res.result.paymentwx.requestPayment({...payment})不要改(僅對小白用戶而言)。因?yàn)橛胁簧傩“子脩羯痘A(chǔ)也沒有,但是對微信支付比較感興趣,所以本節(jié)內(nèi)容,會(huì)介紹的比較瑣碎一些。

  1. callPay(){
  2. wx.cloud.callFunction({
  3. name: 'pay', //云函數(shù)的名稱,在后面我們會(huì)教大家怎么建
  4. success: res => {
  5. console.log(res)
  6. const payment = res.result.payment
  7. wx.requestPayment({
  8. ...payment,
  9. success (res) {
  10. console.log('支付成功', res) //為方便,只打印結(jié)果,如果要寫支付成功之后的處理函數(shù),寫在這后面
  11. },
  12. fail (err) {
  13. console.error('支付失敗', err)
  14. //支付失敗之后的處理函數(shù),寫在這后面
  15. }
  16. })
  17. },
  18. fail: console.error,
  19. })
  20. },

然后再在云函數(shù)根目錄文件夾cloudfunctions右鍵,選擇“新建Nodejs云函數(shù)”,新建一個(gè)云函數(shù)pay,然后再在index.js里輸入以下代碼,然后進(jìn)行一些修改(注意參數(shù)名稱不要改,大小寫也要原樣寫,不懂你就復(fù)制):

  • body為你的商家名(店名)-銷售商品的類名,代碼里有參考;

  • outTradeNo是商戶訂單號(hào),32個(gè)字符內(nèi),只能是數(shù)字、大小寫字母_-,如果你是在調(diào)試學(xué)習(xí),注意每次都改一下這個(gè),免得重復(fù);

  • subMchId你的商戶ID或子商戶ID,填寫云開發(fā)控制臺(tái)- 設(shè)置- 全局設(shè)置- 微信支付配置里的商戶號(hào)也可以;

  • totalFee是支付的金額,單位是分,填寫100,就是一塊錢,注意這個(gè)是數(shù)值格式,不要寫成了字符串格式(不要加單引號(hào)或者雙引號(hào));

  • envId是你的結(jié)果通知回調(diào)云函數(shù)所在的環(huán)境ID,functionName結(jié)果通知云函數(shù)的名稱(可以自定義);可以在云開發(fā)控制臺(tái)- 設(shè)置- 環(huán)境設(shè)置里看到,注意是環(huán)境ID,不是環(huán)境名稱,最好直接復(fù)制過來;

  • 其他地方,不懂的話,不要改,直接copy完事

修改完之后,點(diǎn)擊pay云函數(shù)目錄下的index.js,然后右鍵選擇“云函數(shù)增量上傳:更新文件”或者右鍵云函數(shù)根目錄文件夾cloudfunctions,選擇“上傳并部署:云端安裝依賴(不上傳Node_modules)

  1. const cloud = require('wx-server-sdk')
  2. cloud.init({
  3. env: cloud.DYNAMIC_CURRENT_ENV
  4. })
  5. exports.main = async (event, context) => {
  6. const res = await cloud.cloudPay.unifiedOrder({
  7. "body": "HackWeek案例-教學(xué)費(fèi)用",
  8. "outTradeNo" : "122775224070323234368128", //不能重復(fù),否則報(bào)錯(cuò)
  9. "spbillCreateIp" : "127.0.0.1", //就是這個(gè)值,不要改
  10. "subMchId" : "1520057521", //你的商戶ID或子商戶ID
  11. "totalFee" : 100, //單位為分
  12. "envId": "xly-xrlur", //你的云開發(fā)環(huán)境ID
  13. "functionName": "paysuc", //支付成功的回調(diào)云函數(shù),先可以隨便寫,比如寫paysuc,后面會(huì)教你怎么建
  14. "nonceStr":"F8B31E62AD42045DFB4F2", //隨便弄的32位字符串,建議自己生成
  15. "tradeType":"JSAPI" //默認(rèn)是JSAPI
  16. })
  17. return res
  18. }

然后就可以在開發(fā)者工具的模擬器里點(diǎn)擊"發(fā)起支付"的按鈕了,這時(shí)會(huì)彈出支付的二維碼,掃碼支付就可以了;也可以使用預(yù)覽或真機(jī)調(diào)試。

這里的outTradeNo是自己生成的,我們可以使用時(shí)間戳Date.now().toString(),或者加隨機(jī)數(shù)Date.now().toString()+Math.floor(Math.random()*1000).toString()等來處理,而nonceStr是32位以內(nèi)的字符串,我們可以使用用戶的openid和時(shí)間戳拼接而成(你也可以使用其他方法),比如下面是用戶的openid先替換掉-字符,然后將字母都大寫,最后加上時(shí)間戳的字符串"oUL-m5FuRmuVmxvbYOGuXbuEDsn8".replace('-','').toUpperCase()+Date.now().toString()

二、查詢訂單與申請退款

我們可以在云函數(shù)里調(diào)用cloudPay.queryOrder()來查詢訂單的支付狀態(tài),以及調(diào)用cloudPay.refund()來對已經(jīng)支付成功的訂單發(fā)起退款。下面的代碼只是查詢訂單與申請退款簡單的demo,真正要在實(shí)際開發(fā)中使用這些接口,都是需要結(jié)合云開發(fā)數(shù)據(jù)庫的,尤其是申請退款開發(fā)時(shí)一定要慎重對待。

使用開發(fā)者工具新建一個(gè)queryorder的云函數(shù),然后在index.js里輸入以下代碼,將云函數(shù)部署到云端之后,調(diào)用該云函數(shù)就能查詢訂單信息了:

  1. const cloud = require('wx-server-sdk')
  2. cloud.init({
  3. env: cloud.DYNAMIC_CURRENT_ENV,
  4. })
  5. exports.main = async(event, context) => {
  6. const res = await cloud.cloudPay.queryOrder({
  7. "sub_mch_id":"1520057521",
  8. //商戶訂單號(hào),需是云支付成功交易的訂單號(hào)
  9. "out_trade_no":"122775224070323234368128",
  10. //微信訂單號(hào)可以不必寫
  11. // "transaction_id":"4200000530202005179572346100",
  12. //任意的32位字符
  13. "nonce_str":"C380BEC2BFD727A4B6845133519F3AD6"
  14. })
  15. return res
  16. }

使用開發(fā)者工具新建一個(gè)refundorder的云函數(shù),然后在index.js里輸入以下代碼,退款的金額少于交易的金額時(shí),可以實(shí)現(xiàn)部分退款;注意調(diào)用該云函數(shù),退款會(huì)直接原路返回給用戶,因此一定要有管理員審核或只能管理員來調(diào)用該接口:

  1. const cloud = require('wx-server-sdk')
  2. cloud.init({
  3. env: cloud.DYNAMIC_CURRENT_ENV,
  4. })
  5. exports.main = async(event, context) => {
  6. const res = await cloud.cloudPay.refund({
  7. "sub_mch_id":"1520057521",
  8. "nonce_str":"5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
  9. "out_trade_no":"122775224070323234368128",//商戶訂單號(hào),需是云支付成功交易的訂單號(hào)
  10. "out_refund_no":"122775224070323234368128001",//退款單號(hào),可以自定義,建議與訂單號(hào)相關(guān)聯(lián)
  11. "total_fee":100,
  12. "refund_fee":20,
  13. })
  14. return res
  15. }

三、支付成功的回調(diào)函數(shù)

在前面發(fā)起支付的云函數(shù)里我們寫過一個(gè)參數(shù)functionName結(jié)果通知云函數(shù)paysuc,paysuc云函數(shù)在訂單支付成功之后才會(huì)被調(diào)用。我們可以在支付成功的回調(diào)函數(shù)里處理一些任務(wù),比如把訂單支付的重要信息存儲(chǔ)到數(shù)據(jù)庫、給用戶發(fā)送支付成功的訂閱消息、以及獲取用戶的UnionID等等。要處理這些任務(wù),首先需要了解訂單支付成功之后,paysuc云函數(shù)會(huì)接收到哪些數(shù)據(jù)。我們可以打印paysuc云函數(shù)的event對象,可以了解到event對象里包含類似于如下結(jié)構(gòu)的信息,這些都是我們在paysuc云函數(shù)處理任務(wù)的關(guān)鍵:

  1. "appid": "wxd2********65e",
  2. "bankType": "OTHERS",
  3. "cashFee": 200,
  4. "feeType": "CNY",
  5. "isSubscribe": "N",
  6. "mchId": "1800008281",
  7. "nonceStr": "F8B31E62AD42045DFB4F2",
  8. "openid": "oPoo44....t8gCOUKSncFI",
  9. "outTradeNo": "1589720989221",
  10. "resultCode": "SUCCESS",
  11. "returnCode": "SUCCESS",
  12. "subAppid": "wxda99a********57046",
  13. "subIsSubscribe": "N",
  14. "subMchId": "1520057521",
  15. "subOpenid": "oUL********GuXbuEDsn8",
  16. "timeEnd": "20200517211001",
  17. "totalFee": 2,
  18. "tradeType": "JSAPI",
  19. "transactionId": "42000********178943055343",
  20. "userInfo": {
  21. "appId": "wxd********046",
  22. "openId": "oUL-m5F********GuXbuEDsn8"
  23. }

要發(fā)送訂閱消息,首先我們需要去申請訂單支付成功的訂閱消息模板,比如模板如下,我們需要注意訂閱消息里每一個(gè)屬性對應(yīng)的具體的格式,以及格式的具體要求,比如支付金額以及支付時(shí)間的格式:

  1. 商品名稱{{thing6.DATA}}
  2. 支付金額{{amount7.DATA}}
  3. 訂單號(hào){{character_string9.DATA}}
  4. 支付時(shí)間{{date10.DATA}}
  5. 溫馨提示{{thing5.DATA}}

要發(fā)訂閱消息,需要調(diào)用接口wx.requestSubscribeMessage來獲取用戶授權(quán)以及要有相應(yīng)的授權(quán)次數(shù),在前面我們已經(jīng)了解到只有用戶發(fā)生點(diǎn)擊行為或者發(fā)起支付回調(diào)后,才可以調(diào)起訂閱消息界面,因此我們可以在上面的發(fā)起支付的回調(diào)函數(shù)里直接調(diào)用這個(gè)接口:

  1. callPay(){
  2. wx.cloud.callFunction({
  3. name: 'pay', //云函數(shù)的名稱,在后面我們會(huì)教大家怎么建
  4. success: res => {
  5. console.log(res)
  6. const payment = res.result.payment
  7. wx.requestPayment({
  8. ...payment,
  9. success (res) {
  10. console.log('支付成功', res) //為方便,只打印結(jié)果,如果要寫支付成功之后的處理函數(shù),寫在這后面
  11. this.subscribeMessage() //調(diào)用subscribeMessage()函數(shù),如果你不是箭頭函數(shù),注意this指代的對象
  12. },
  13. })
  14. },
  15. })
  16. },
  17. subscribeMessage() {
  18. wx.requestSubscribeMessage({
  19. tmplIds: [
  20. //訂閱消息模板ID,一次可以寫三個(gè),可以是同款通知、到貨通知、新品上新通知等,通常用戶不會(huì)拒絕,多寫幾個(gè)就能獲取更多授權(quán)
  21. "p5ypZiN4TcZrzke4Q_MBB1qri33rb80z-tb16Sg-Kpg",
  22. ],
  23. success(res) {
  24. console.log("訂閱消息API調(diào)用成功:",res)
  25. },
  26. fail(res) {
  27. console.log("訂閱消息API調(diào)用失?。?,res)
  28. }
  29. })
  30. },

然后在paysuc云函數(shù)的index.js里寫如下代碼,訂閱消息所需的全部參數(shù)都是來自于event對象,我們只需要稍加修改格式即可。

  1. const cloud = require('wx-server-sdk')
  2. cloud.init({
  3. env: cloud.DYNAMIC_CURRENT_ENV
  4. })
  5. exports.main = async (event, context) => {
  6. const {cashFee,subOpenid,outTradeNo,timeEnd} = event
  7. try {
  8. const result = await cloud.openapi.subscribeMessage.send({
  9. touser: subOpenid,
  10. page: 'index',
  11. templateId: "p5ypZiN4TcZrzke4Q_MBB1qri33rb80z-tb16Sg-Kpg",
  12. data: {
  13. "thing6": {
  14. "value": '零基礎(chǔ)小程序云開發(fā)訓(xùn)練營'
  15. },
  16. "amount7": {
  17. "value": cashFee/100 +'元'
  18. },
  19. "character_string9": {
  20. "value": outTradeNo
  21. },
  22. "date10": {
  23. "value": timeEnd.slice(0,4)+'年'+timeEnd.slice(4,6)+'月'+timeEnd.slice(6,8)+'日'+' '+timeEnd.slice(8,10)+':'+timeEnd.slice(10,12)
  24. },
  25. "thing5": {
  26. "value": "多謝您的支持哦~愛你哦~"
  27. }
  28. }
  29. })
  30. return result
  31. } catch (err) {
  32. console.log(err)
  33. return err
  34. }
  35. }
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)