云開(kāi)發(fā) 微信支付

2020-07-22 15:32 更新

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

一、云支付快速入門(mén)

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

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

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

jsapi和api退款權(quán)限授權(quán),需要前往微信支付商戶(hù)平臺(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、微信支付流程說(shuō)明

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

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

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

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

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

3、微信支付的簡(jiǎn)單案例

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

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

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

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

callPay(){
  wx.cloud.callFunction({
    name: 'pay',  //云函數(shù)的名稱(chēng),在后面我們會(huì)教大家怎么建
    success: res => {
      console.log(res)
      const payment = res.result.payment
      wx.requestPayment({
        ...payment,
        success (res) {
          console.log('支付成功', res) //為方便,只打印結(jié)果,如果要寫(xiě)支付成功之后的處理函數(shù),寫(xiě)在這后面
        },
        fail (err) {
          console.error('支付失敗', err) 
          //支付失敗之后的處理函數(shù),寫(xiě)在這后面
        }
      })
    },
    fail: console.error,
  })
},

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

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

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

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

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

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

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

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

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})


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

然后就可以在開(kāi)發(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()等來(lái)處理,而nonceStr是32位以?xún)?nèi)的字符串,我們可以使用用戶(hù)的openid和時(shí)間戳拼接而成(你也可以使用其他方法),比如下面是用戶(hù)的openid先替換掉-字符,然后將字母都大寫(xiě),最后加上時(shí)間戳的字符串"oUL-m5FuRmuVmxvbYOGuXbuEDsn8".replace('-','').toUpperCase()+Date.now().toString()

二、查詢(xún)訂單與申請(qǐng)退款

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

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

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV,
})
exports.main = async(event, context) => {
  const res = await cloud.cloudPay.queryOrder({
    "sub_mch_id":"1520057521",
    //商戶(hù)訂單號(hào),需是云支付成功交易的訂單號(hào)
    "out_trade_no":"122775224070323234368128",
    //微信訂單號(hào)可以不必寫(xiě)
    // "transaction_id":"4200000530202005179572346100",
    //任意的32位字符
    "nonce_str":"C380BEC2BFD727A4B6845133519F3AD6"
  })
  return res
}

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

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

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

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

"appid": "wxd2********65e", 
"bankType": "OTHERS",
"cashFee": 200,
"feeType": "CNY",
"isSubscribe": "N",
"mchId": "1800008281",
"nonceStr": "F8B31E62AD42045DFB4F2",
"openid": "oPoo44....t8gCOUKSncFI",
"outTradeNo": "1589720989221",
"resultCode": "SUCCESS",
"returnCode": "SUCCESS",
"subAppid": "wxda99a********57046",
"subIsSubscribe": "N",
"subMchId": "1520057521",
"subOpenid": "oUL********GuXbuEDsn8",
"timeEnd": "20200517211001",
"totalFee": 2,
"tradeType": "JSAPI",
"transactionId": "42000********178943055343",
"userInfo": {
    "appId": "wxd********046",
    "openId": "oUL-m5F********GuXbuEDsn8"
}

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

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

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

callPay(){
  wx.cloud.callFunction({
    name: 'pay',  //云函數(shù)的名稱(chēng),在后面我們會(huì)教大家怎么建
    success: res => {
      console.log(res)
      const payment = res.result.payment
      wx.requestPayment({
        ...payment,
        success (res) {
          console.log('支付成功', res) //為方便,只打印結(jié)果,如果要寫(xiě)支付成功之后的處理函數(shù),寫(xiě)在這后面
          this.subscribeMessage() //調(diào)用subscribeMessage()函數(shù),如果你不是箭頭函數(shù),注意this指代的對(duì)象
        },
      })
    },
  })
},
subscribeMessage() {
  wx.requestSubscribeMessage({
    tmplIds: [
      //訂閱消息模板ID,一次可以寫(xiě)三個(gè),可以是同款通知、到貨通知、新品上新通知等,通常用戶(hù)不會(huì)拒絕,多寫(xiě)幾個(gè)就能獲取更多授權(quán)
      "p5ypZiN4TcZrzke4Q_MBB1qri33rb80z-tb16Sg-Kpg",
    ],
    success(res) {
      console.log("訂閱消息API調(diào)用成功:",res)
    },
    fail(res) {
      console.log("訂閱消息API調(diào)用失?。?,res)
    }
  })
},

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

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})


exports.main = async (event, context) => {
  const {cashFee,subOpenid,outTradeNo,timeEnd} = event
  try {
    const result = await cloud.openapi.subscribeMessage.send({
      touser: subOpenid,
      page: 'index',
      templateId: "p5ypZiN4TcZrzke4Q_MBB1qri33rb80z-tb16Sg-Kpg",
      data: {
        "thing6": {
          "value": '零基礎(chǔ)小程序云開(kāi)發(fā)訓(xùn)練營(yíng)'
        },
        "amount7": {
          "value": cashFee/100 +'元'
        },
        "character_string9": {
          "value": outTradeNo
        },
        "date10": {
          "value": timeEnd.slice(0,4)+'年'+timeEnd.slice(4,6)+'月'+timeEnd.slice(6,8)+'日'+' '+timeEnd.slice(8,10)+':'+timeEnd.slice(10,12)
        },
        "thing5": {
          "value": "多謝您的支持哦~愛(ài)你哦~"
        }
    }
    })
    return result
  } catch (err) {
    console.log(err)
    return err
  }
}

以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)