收起左侧

微信小程序通过云函数进行微信支付

1
回复
367
查看
[复制链接]

尚未签到

475

主题

475

帖子

0

积分

冒泡

Rank: 1

积分
0
发表于 2019-10-17 13:17:53 | 显示全部楼层 |阅读模式
微信小程序通过云函数进行微信支付

微信小程序微信支付

官方流程图如下:
微信小程序微信支付官方流程图链接

                               
登录/注册后可看大图


我简化的流程:

  • 本地发起下单请求调用云函数并传送数据
  • 云函数处理数据并返回5个参数
  • 本地接受5个参数,发起支付请求
  • 交易结束
主要代码:
//第一步,本地发起下单请求并传送数据。这一步,在你的wxml中的某个元素
//中绑定事件。通过这个pay函数,
//触发云函数并传递一些数据
pay: function(){
t//需要上传给云函数的数据
tlet uploadData = {
tt//此次需要支付的金额,单位是分。例如1.80=180
tt"total_fee": "180",
tt//用户端的ip地址
tt"spbill_create_ip": "123.123.123.123"
t}
t//调用云函数
twx.cloud.callFunction({
tt//云函数的名字,这里我定义为payment
ttname: "payment",
tt//需要上传的数据
ttdata: uploadData
t}).then(res => {
tt//这个res就是云函数返回的5个参数
tt//通过wx.requestPayment发起支付
ttwx.requestPayment({
ttttimeStamp: res.result.data.timeStamp,
tttnonceStr: res.result.data.nonceStr,
tttpackage: res.result.data.package,
tttsignType: res.result.data.signType,
tttpaySign: res.result.data.paySign,
tttsuccess: res => {
tttt//支付成功
ttt},
tttfail: err => {
tttt//支付失败
ttt}
t})
}
复制代码
以上就是本地端的全部代码,接下来我们只需要搞定云函数的代码就完成全部的工作了。
云函数的内容是:

  • 调用小程序登陆API -> Openid
  • 生成商户订单
  • 调用统一下单API -> prepay_id
  • 将组合数据再次签名,返回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的订单。根据官方文档,我们需要以下数据:

  • appid(小程序ID)
  • openid(用户OPENID)
  • mch_id(商户号)
  • nonce_str(随机字符串)
  • body(商品描述)
  • out_trade_no(商户订单号)
  • total_fee(标价金额)
  • spbill_create_ip(终端IP)
  • notify_url(通知地址)
  • trade_type(交易类型)
  • key(密钥)
  • 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账户设置-->API安全-->密钥设置。</p>
示例值1a79a4d60de6718e8e5b326e338ae533
在云函数index.js中加上以下代码:
const key = &#x27;1a79a4d60de6718e8e5b326e338ae533&#x27;
复制代码
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(&#x27;wx-server-sdk&#x27;)
cloud.init()
const openid = cloud.getWXContext().OPENID
const appid = &#x27;wxwxd678efh567hg6787&#x27;
const mch_id = &#x27;1230000109&#x27;
const random = require(&#x27;random.js&#x27;)
const body = "腾讯-游戏"
const notify_url = &#x27;http://www.weixin.qq.com/wxpay/pay.php&#x27;
const trade_type = &#x27;JSAPI&#x27;
const key = &#x27;1a79a4d60de6718e8e5b326e338ae533&#x27;
//云函数入口函数
exports.main = async (event, content) => {
tconst 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(&#x27;md5&#x27;).update(stringA).digest(&#x27;hex&#x27;).toUpperCase()
复制代码
以上除key的11个信息就是我们调用统一下单API所需要的全部数据了
我们现在需要将这些数据转成xml格式,例如:

wxd930ea5d5a258f4f
10000100
1000
test
ibuaiVcKdpRxkhJA
9A0A8659F005D6984697E2CA0A9CF3B7
...

复制代码
在云函数中新建一个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 = ""
data += ""+appid+""
data += ""+mch_id+""
data += ""+nonce_str+""
data += ""+sign+""
data += ""+body+""
data += ""+out_trade_no+""
data += ""+total_fee+""
data += ""+spbill_create_ip+""
data += ""+notify_url+""
data += ""+trade_type+""
data += ""+openid+""
data += ""
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 => {
trequest({
tt//官方统一下单api的url
tturl: &#x27;https://api.mch.weixin.qq.com/pay/unifiedorder&#x27;,
tt//请求方法,post
ttmethod: "POST",
tt//需要传送的订单,就是刚刚我们生成的dataBody
ttbody: dataBody
t}, body => {
tt//body就是我们收到的数据,我们需要得到其中的prepay_id
tt//使用xmlreader解析body,获得其中的prepay_id
ttxmlreader.read(body, res => {
ttt//此时我们已经完成第三步的目的了
tttlet prepay_id = res.xml.prepay_id.text()
tt}
t}
}
复制代码
第三步目的完成
4.将组合数据再次签名,返回5个参数
已知wx.requestPayment()需要五个参数,分别是

  • timeStamp
  • nonceStr
  • package
  • signType
  • 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 => {
trequest({
tturl: &#x27;https://api.mch.weixin.qq.com/pay/unifiedorder&#x27;,
ttmethod: "POST",
ttbody: dataBody
t}, body => {
ttxmlreader.read(body, res => {
tttlet prepay_id = res.xml.prepay_id.text()
tttlet timeStamp = Date.parse(new Date()).toString()
tttlet str = `appId=${appid}&nonceStr=${random}&package=prepay_id=${prepay_id}&signType=MD5&timeStamp=${timeStamp}&key=1a79a4d60de6718e8e5b326e338ae533`
tttlet paySign = crypto.createHash(&#x27;md5&#x27;).update(str).digest(&#x27;hex&#x27;)
ttt//返回上面的五个参数
tttreslove({
ttttdata: {
ttttttimeStamp: timeStamp,
ttnonceStr: random,
ttpackage: `prepay_id=${prepay_id}`,
ttsignType: &#x27;MD5&#x27;,
ttpaySign: paySign
t}
})
tt}
t}
}
复制代码
至此,微信小程序支付流程结束。
此时云函数结构为:
payment
|__index.js
|__package.json
|__package-lock.json
|__random.js
|__requestData.js
复制代码
index.js:
//云函数入口文件
const cloud = require(&#x27;wx-server-sdk&#x27;)
cloud.init()
const openid = cloud.getWXContext().OPENID
const appid = &#x27;wxwxd678efh567hg6787&#x27;
const mch_id = &#x27;1230000109&#x27;
const random = require(&#x27;random.js&#x27;)
const body = "腾讯-游戏"
const notify_url = &#x27;http://www.weixin.qq.com/wxpay/pay.php&#x27;
const trade_type = &#x27;JSAPI&#x27;
const key = &#x27;1a79a4d60de6718e8e5b326e338ae533&#x27;
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(&#x27;md5&#x27;).update(stringA).digest(&#x27;hex&#x27;).toUpperCase()
let dataBody = reqData(
t appid,
t mch_id,
t random,
t sign,
t body,
t out_trade_no,
t total_fee,
t spbill_create_ip,
t notify_url,
t trade_type,
t openid
t )
treturn new Promise(reslove => {
t request({
t url: &#x27;https://api.mch.weixin.qq.com/pay/unifiedorder&#x27;,
t method: "POST",
t body: dataBody
t }, body => {
t xmlreader.read(body, res => {
t let prepay_id = res.xml.prepay_id.text()
t let timeStamp = Date.parse(new Date()).toString()
t let str = `appId=${appid}&nonceStr=${random}&package=prepay_id=${prepay_id}&signType=MD5&timeStamp=${timeStamp}&key=1a79a4d60de6718e8e5b326e338ae533`
t let paySign = crypto.createHash(&#x27;md5&#x27;).update(str).digest(&#x27;hex&#x27;)
t //返回上面的五个参数
t reslove({
t data: {
t timeStamp: timeStamp,
t nonceStr: random,
t package: `prepay_id=${prepay_id}`,
t signType: &#x27;MD5&#x27;,
t paySign: paySign
t }
t })
t }
t }
}
复制代码
random.js:
function random(){
var result = &#x27;&#x27;
const wordList = [&#x27;a&#x27;, &#x27;b&#x27;, &#x27;c&#x27;, &#x27;d&#x27;, &#x27;e&#x27;, &#x27;f&#x27;, &#x27;g&#x27;, &#x27;h&#x27;, &#x27;i&#x27;, &#x27;j&#x27;, &#x27;k&#x27;, &#x27;l&#x27;,
&#x27;m&#x27;, &#x27;n&#x27;, &#x27;o&#x27;, &#x27;p&#x27;, &#x27;q&#x27;, &#x27;r&#x27;, &#x27;s&#x27;, &#x27;t&#x27;, &#x27;u&#x27;, &#x27;v&#x27;, &#x27;w&#x27;, &#x27;x&#x27;, &#x27;y&#x27;, &#x27;z&#x27;, &#x27;1&#x27;, &#x27;2&#x27;,
&#x27;3&#x27;, &#x27;4&#x27;, &#x27;5&#x27;, &#x27;6&#x27;, &#x27;7&#x27;, &#x27;8&#x27;, &#x27;9&#x27;, &#x27;0&#x27;]
for(let i=0;i

尚未签到

589

主题

2586

帖子

0

积分

冒泡

Rank: 1

积分
0
发表于 2019-10-17 13:20:09 | 显示全部楼层
转发了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫描微信二维码

查看官方公众号

了解更多详情

2241998733

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:成都市锦江区通汇街342号
电邮:wxcydz#qq.com(#换成@)
微信:2241998733

无限创意电子仅提供平台服务,如有贷款产品及展示信息均为互联网采集。贷款属于借贷行为,本社区不提供任何代办服务。借款有风险,申请需谨慎,风险自理,责任自担。 © 2018-现在 VIP团队( 蜀ICP备17006511号-1 )