濟南小程序開發公司小貓科技:專注微信開發,濟南小程序開發,濟南微信小程序定制開發等業務!
手機版手機網站二維碼 微信版 微信二維碼 業務咨詢電話:159-5318-4521

微信小程序通過云函數進行微信支付

發表于:19-10-17 21:46 閱讀()

微信小程序通過云函數進行微信支付

微信小程序微信支付

官方流程圖如下:

微信小程序微信支付官方流程圖鏈接

微信小程序通過云函數進行微信支付

我簡化的流程:

  1. 本地發起下單請求調用云函數并傳送數據
  2. 云函數處理數據并返回5個參數
  3. 本地接受5個參數,發起支付請求
  4. 交易結束

主要代碼:

//第一步,本地發起下單請求并傳送數據。這一步,在你的wxml中的某個元素
//中綁定事件<button bindtap='pay'></button>。通過這個pay函數,
//觸發云函數并傳遞一些數據
pay: function(){
	//需要上傳給云函數的數據
	let uploadData = {
		//此次需要支付的金額,單位是分。例如¥1.80=180
		"total_fee": "180",
		//用戶端的ip地址
		"spbill_create_ip": "123.123.123.123"
	}
	//調用云函數
	wx.cloud.callFunction({
		//云函數的名字,這里我定義為payment
		name: "payment",
		//需要上傳的數據
		data: uploadData
	}).then(res => {
		//這個res就是云函數返回的5個參數
		//通過wx.requestPayment發起支付
		wx.requestPayment({
			timeStamp: res.result.data.timeStamp,
			nonceStr: res.result.data.nonceStr,
			package: res.result.data.package,
			signType: res.result.data.signType,
			paySign: res.result.data.paySign,
			success: res => {
				//支付成功
			},
			fail: err => {
				//支付失敗
			}
	})
}
復制代碼

以上就是本地端的全部代碼,接下來我們只需要搞定云函數的代碼就完成全部的工作了。

云函數的內容是:

  1. 調用小程序登陸API -> Openid
  2. 生成商戶訂單
  3. 調用統一下單API -> prepay_id
  4. 將組合數據再次簽名,返回5個參數

我創建的云函數命名為payment,此時云函數結構應該為

payment
|__index.js
|__package.json
復制代碼

##每一步的詳細做法

1. 調用小程序登陸API -> Openid

這一步目的是獲取用戶的Openid

在云函數的index.js中加上以下代碼

//獲取云實例
const cloud = require('wx-server-sdk')
//云初始化
cloud.init()
//獲取微信調用上下文信息,其中包括Openid,Appid等
const wxContext = cloud.getWXContext()
//獲取用戶openid
const openid = wxContext.OPENID
復制代碼

到這里我們已經達成我們第一步的目的了。

2. 生成商戶訂單

微信支付開發文檔-統一下單

這一步的目的是為了生成調用支付統一下單API的訂單。根據官方文檔,我們需要以下數據:

  1. appid(小程序ID)
  2. openid(用戶OPENID)
  3. mch_id(商戶號)
  4. nonce_str(隨機字符串)
  5. body(商品描述)
  6. out_trade_no(商戶訂單號)
  7. total_fee(標價金額)
  8. spbill_create_ip(終端IP)
  9. notify_url(通知地址)
  10. trade_type(交易類型)
  11. key(密鑰)
  12. sign(簽名)

我們一個一個解決。

1. appid

小程序管理員進入公眾平臺、使用小程序帳戶登錄后,點擊左側菜單中的「設置」,在「開發設置」一項,就可以查詢到小程序的AppID。

示例值wxd678efh567hg6787

在云函數的index.js中加上以下代碼:

const appid='wxwxd678efh567hg6787'
復制代碼

2. openid

第一步已經獲得。

示例值oUpF8uMuAJO_M2pxb1Q9zNjWeS6o

3. mch_id

登陸微信支付商戶平臺pay.weixin.qq.com,點擊上方「賬戶中心」,在「個人信息」中的「登陸賬號」就是mch_id。

示例值1230000109

在云函數的index.js中加上以下代碼:

const mch_id='1230000109'
復制代碼

4. nonce_str

任意生成的隨機數,不超過32位。你可以自己寫個函數。

示例值5K8264ILTKCH16CQ2502SI8ZNMTM67VS

我在云函數中創建了一個新的JS文件(random.js)來保存這個函數,此時云函數的結構如下

payment
|__index.js
|__package.json
|__random.js
復制代碼

其中random.js的內容為:

function random(){
 var result = ''
 const wordList = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
 '3', '4', '5', '6', '7', '8', '9', '0']
 for(let i=0;i<31;i++){
 result += wordList[Math.round(Math.random()*36)]
 }
 return result
}
module.exports = random()
復制代碼

然后在云函數index.js中加上以下代碼:

const random = require("random.js")
復制代碼

5. body

格式為:商家名稱-銷售商品類目

示例值騰訊-游戲

在云函數index.js中加上以下代碼:

const body = "騰訊-游戲"
復制代碼

6. out_trade_no

商戶系統內部訂單號,要求32個字符內,只能是數字、大小寫字母_-|*且在同一個商戶號下唯一。由自己定義,推薦用當下時間+商品編號組成。

示例值20150806125346

在云函數index.js中的exports.main函數中加上以下代碼:

//這里我只使用了當下時間。只要這個數字不是重復的就可以。
const out_trade_no = Date.parse(new Date()).toString()
復制代碼

7. total_fee

訂單總金額,單位為分。比如當前需支付¥6.80,則total_fee為680。

示例值88

這里需要用到我們上傳過來的值,先不管

8. spbill_create_ip

支持IPV4和IPV6兩種格式的IP地址。調用微信支付API的機器IP。

示例值123.12.12.123

這里需要用到我們上傳過來的值,先不管

9. notify_url

異步接收微信支付結果通知的回調地址,通知url必須為外網可訪問的url,不能攜帶參數。在這里可以填上你自己服務器的url。

示例值http://www.weixin.qq.com/wxpay/pay.php

在云函數index.js中加上以下代碼:

//隨便填寫個服務器就行,我在使用中沒有遇到什么問題
const notify_url = 'http://www.weixin.qq.com/wxpay/pay.php'
復制代碼

10. trade_type

小程序的trade_type為JSAPI。

示例值JSAPI

在云函數index.js中加上以下代碼:

const trade_type = 'JSAPI'
復制代碼

11. key

key為商戶平臺設置的密鑰key,是由你自己設置的。key設置路徑:微信商戶平臺(pay.weixin.qq.com)-->賬戶設置-->API安全-->密鑰設置。

示例值1a79a4d60de6718e8e5b326e338ae533

在云函數index.js中加上以下代碼:

const key = '1a79a4d60de6718e8e5b326e338ae533'
復制代碼

12. sign

將以上除key外所有信息按照參數名ASCII碼從大到小拼接成字符串,用&分割,將key放在最后。

字符串示例值:

appid=wxd678efh567hg6787&body=微信-游戲&mch_
id=1230000109&nonce_str=5K8264ILTKCH16CQ2502SI8ZNMTM6
7VS&notify_url=http://www.weixin.qq.com/wxpay/pay.php&
openid=oUpF8uMuAJO_M2pxb1Q9zNjWeS6o&out_trade_no=2015080
6125346&spbill_create_ip=123.12.12.123&total_fee=88&trad
e_type=JSAPI&key=1a79a4d60de6718e8e5b326e338ae533
復制代碼

此字符串的MD5碼的大寫就是sign。因為上面的total_fee與spbill_create_ip我們還沒處理,所以這個數據放到下面再處理。

MD5碼示例值C380BEC2BFD727A4B6845133519F3AD6

  •  

到此為止你的云函數結構應該為:

payment
|__index.js
|__package.json
|__random.js
復制代碼

其中index.js的內容應該為:

//云函數入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const openid = cloud.getWXContext().OPENID
const appid = 'wxwxd678efh567hg6787'
const mch_id = '1230000109'
const random = require('random.js')
const body = "騰訊-游戲"
const notify_url = 'http://www.weixin.qq.com/wxpay/pay.php'
const trade_type = 'JSAPI'
const key = '1a79a4d60de6718e8e5b326e338ae533'
//云函數入口函數
exports.main = async (event, content) => {
	const out_trade_no = Date.parse(new Date()).toString()
}
復制代碼

接下來我們處理上面沒有處理的total_fee與spbill_create_ip,以及sign。 其中total_fee和spbill_create_ip是由客戶端上傳的,這兩個數據就在云函數入口函數的參數event中,所以我們在云函數入口函數里面加上以下代碼

const total_fee = event.total_fee
const spbill_create_ip = event.spbill_create_ip
復制代碼

最后,我們需要處理sign,按照12.sign提到的規則,在云函數入口函數里面加上以下代碼

let stringA = `appid=${appid}&body=${body}&
mch_id=${mch_id}&nonce_str=${random}&
notify_url=${notify_url}&openid=${openid}&
out_trade_no=${out_trade_no}&
spbill_create_ip=${spbill_create_ip}&
total_fee=${total_fee}&trade_type=${trade_type}&
key=1a79a4d60de6718e8e5b326e338ae533`
復制代碼

我們現在需要將這個字符串進行MD5碼加密,所以需要安裝一個npm包來完成這個任務。右鍵點擊云函數pyament,選擇「在終端打開」,輸入下面的命令:

npm install --save crypto
復制代碼

完成后在云函數入口文件處加上以下代碼

const crypto = require("crypto")
復制代碼

這樣我們就成功地將crypto這個加密工具包引入我們的云函數里了。然后我們需要在云函數入口函數里使用它對剛剛的stringA進行MD5加密。所以我們在let stringA = ...這行代碼下面添加以下代碼

var sign = crypto.createHash('md5').update(stringA).digest('hex').toUpperCase()
復制代碼

以上除key的11個信息就是我們調用統一下單API所需要的全部數據了

我們現在需要將這些數據轉成xml格式,例如:

<xml>
<appid>wxd930ea5d5a258f4f</appid>
<mch_id>10000100</mch_id>
<device_info>1000</device_info>
<body>test</body>
<nonce_str>ibuaiVcKdpRxkhJA</nonce_str>
<sign>9A0A8659F005D6984697E2CA0A9CF3B7</sign>
...
</xml>
復制代碼

在云函數中新建一個requestData.js,寫下如下函數,用來完成將數據轉成xml的任務

function requestData(
 appid,
 mch_id,
 nonce_str,
 sign,
 body,
 out_trade_no,
 total_fee,
 spbill_create_ip,
 notify_url,
 trade_type,
 openid
){
 let data = "<xml>"
 data += "<appid>"+appid+"</appid>"
 data += "<mch_id>"+mch_id+"</mch_id>"
 data += "<nonce_str>"+nonce_str+"</nonce_str>"
 data += "<sign>"+sign+"</sign>"
 data += "<body>"+body+"</body>"
 data += "<out_trade_no>"+out_trade_no+"</out_trade_no>"
 data += "<total_fee>"+total_fee+"</total_fee>"
 data += "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"
 data += "<notify_url>"+notify_url+"</notify_url>"
 data += "<trade_type>"+trade_type+"</trade_type>"
 data += "<openid>"+openid+"</openid>"
 data += "</xml>"
 return data
}
module.exports = requestData
復制代碼

此時云函數的結構為

payment
|__index.js
|__package.json
|__package-lock.json //由npm install產生的文件
|__random.js
|__requestData.js
復制代碼

我們需要將requestData.js文件導入到我們的項目。在云函數入口文件那里添加以下代碼

const requestData = require("requestData.js")
復制代碼

現在,我們可以生成調用支付統一下單API的訂單了,這個dataBody就是訂單。

let dataBody = reqData(
 appid,
 mch_id,
 random,
 sign,
 body,
 out_trade_no,
 total_fee,
 spbill_create_ip,
 notify_url,
 trade_type,
 openid
 )
復制代碼

到這里我們已經達成我們第二部的目的了。

3.調用統一下單API -> prepay_id

官方文檔

我們需要對官方提供的鏈接https://api.mch.weixin.qq.com/pay/unifiedorder發起統一下單,所以這里我們需要一個npm包來幫我們完成request請求,并且由于發起請求后的返回值是xml格式的,所以我們還需要一個npm包來幫助我們解析xml格式文件。故右鍵點擊云函數payment,選擇「在終端打開」,輸入下面命令:

npm install --save request
npm install --save xmlreader
復制代碼

在云函數入口文件中引入上面兩個包:

const request = require("request")
const xmlreader = require("xmlreader")
復制代碼

然后就可以在云函數入口函數中發起對統一下單API的request請求了,由于request是異步請求,所以我們需要返回一個Promise。

return new Promise(reslove => {
	request({
		//官方統一下單api的url
		url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
		//請求方法,post
		method: "POST",
		//需要傳送的訂單,就是剛剛我們生成的dataBody
		body: dataBody
	}, body => {
		//body就是我們收到的數據,我們需要得到其中的prepay_id
		//使用xmlreader解析body,獲得其中的prepay_id
		xmlreader.read(body, res => {
			//此時我們已經完成第三步的目的了
			let prepay_id = res.xml.prepay_id.text()
		}
	}
}
復制代碼

第三步目的完成

4.將組合數據再次簽名,返回5個參數

已知wx.requestPayment()需要五個參數,分別是

  1. timeStamp
  2. nonceStr
  3. package
  4. signType
  5. paySign

其中,timeStamp為時間戳,可由Date.parse(new Date()).toString()取得。 nonceStr為隨機字符串,可由我們的隨機函數取得。 package就是上一步獲得的prepay_id, signType是簽名類型,我們選擇的是MD5。所以我們現在只剩下paySign未知,得到paySign的方法為

paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K826
4ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx20170
33010242291fcfe0db70013231072&signType=MD5&timeStamp=
1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D
9B4E54AB1950F51E0649E8810ACD6
復制代碼

所以我們在上一步的代碼中接著寫

return new Promise(reslove => {
	request({
		url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
		method: "POST",
		body: dataBody
	}, body => {
		xmlreader.read(body, res => {
			let prepay_id = res.xml.prepay_id.text()
			let timeStamp = Date.parse(new Date()).toString()
			let str = `appId=${appid}&nonceStr=${random}&package=prepay_id=${prepay_id}&signType=MD5&timeStamp=${timeStamp}&key=1a79a4d60de6718e8e5b326e338ae533`
			let paySign = crypto.createHash('md5').update(str).digest('hex')
			//返回上面的五個參數
			reslove({
				data: {
					timeStamp: timeStamp,
 		nonceStr: random,
 		package: `prepay_id=${prepay_id}`,
 		signType: 'MD5',
 		paySign: paySign
 	}
 })
		}
	}
}
復制代碼

至此,微信小程序支付流程結束。

此時云函數結構為:

payment
|__index.js
|__package.json
|__package-lock.json
|__random.js
|__requestData.js
復制代碼

index.js:

//云函數入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const openid = cloud.getWXContext().OPENID
const appid = 'wxwxd678efh567hg6787'
const mch_id = '1230000109'
const random = require('random.js')
const body = "騰訊-游戲"
const notify_url = 'http://www.weixin.qq.com/wxpay/pay.php'
const trade_type = 'JSAPI'
const key = '1a79a4d60de6718e8e5b326e338ae533'
const crypto = require("crypto")
const requestData = require("requestData")
const request = require("request")
const xmlreader = require("xmlreader")
//云函數入口函數
exports.main = async (event, content) => {
 const out_trade_no = Date.parse(new Date()).toString()
 const total_fee = event.total_fee
 const spbill_create_ip = event.spbill_create_ip
 let stringA = `appid=${appid}&body=${body}&mch_id=${mch_id}&nonce_str=${random}&notify_url=${notify_url}&openid=${openid}&out_trade_no=${out_trade_no}&spbill_create_ip=${spbill_create_ip}&total_fee=${total_fee}&trade_type=${trade_type}&key=1a79a4d60de6718e8e5b326e338ae533`
 var sign = crypto.createHash('md5').update(stringA).digest('hex').toUpperCase()
 let dataBody = reqData(
	 appid,
	 mch_id,
	 random,
	 sign,
	 body,
	 out_trade_no,
	 total_fee,
	 spbill_create_ip,
	 notify_url,
	 trade_type,
	 openid
	 )
	return new Promise(reslove => {
	 request({
	 url: 'https://api.mch.weixin.qq.com/pay/unifiedorder',
	 method: "POST",
	 body: dataBody
	 }, body => {
	 xmlreader.read(body, res => {
	 let prepay_id = res.xml.prepay_id.text()
	 let timeStamp = Date.parse(new Date()).toString()
	 let str = `appId=${appid}&nonceStr=${random}&package=prepay_id=${prepay_id}&signType=MD5&timeStamp=${timeStamp}&key=1a79a4d60de6718e8e5b326e338ae533`
	 let paySign = crypto.createHash('md5').update(str).digest('hex')
	 //返回上面的五個參數
	 reslove({
	 data: {
	 timeStamp: timeStamp,
	 nonceStr: random,
	 package: `prepay_id=${prepay_id}`,
	 signType: 'MD5',
	 paySign: paySign
	 }
	 })
	 }
	 }
}
復制代碼

random.js:

function random(){
 var result = ''
 const wordList = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
 '3', '4', '5', '6', '7', '8', '9', '0']
 for(let i=0;i<31;i++){
 result += wordList[Math.round(Math.random()*36)]
 }
 return result
}
module.exports = random()
復制代碼

requestData.js:

function requestData(
 appid,
 mch_id,
 nonce_str,
 sign,
 body,
 out_trade_no,
 total_fee,
 spbill_create_ip,
 notify_url,
 trade_type,
 openid
){
 let data = "<xml>"
 data += "<appid>"+appid+"</appid>"
 data += "<mch_id>"+mch_id+"</mch_id>"
 data += "<nonce_str>"+nonce_str+"</nonce_str>"
 data += "<sign>"+sign+"</sign>"
 data += "<body>"+body+"</body>"
 data += "<out_trade_no>"+out_trade_no+"</out_trade_no>"
 data += "<total_fee>"+total_fee+"</total_fee>"
 data += "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"
 data += "<notify_url>"+notify_url+"</notify_url>"
 data += "<trade_type>"+trade_type+"</trade_type>"
 data += "<openid>"+openid+"</openid>"
 data += "</xml>"
 return data
}
module.exports = requestData
復制代碼



top
湖北快三技巧大全