转自:http://www.wxapp-union.com/article-5407-1.html
微信小程序微信支付官方流程图链接我简化的流程:本地发起下单请求调用云函数并传送数据云函数处理数据并返回5个参数本地接受5个参数,发起支付请求交易结束主要代码://第一步,本地发起下单请求并传送数据。这一步 ...
我简化的流程:
主要代码: //第一步,本地发起下单请求并传送数据。这一步,在你的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 => { //支付失败 } }) } 复制代码 以上就是本地端的全部代码,接下来我们只需要搞定云函数的代码就完成全部的工作了。云函数的内容是:
我创建的云函数命名为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 小程序管理员进入公众平台、使用小程序帐户登录后,点击左侧菜单中的「设置」,在「开发设置」一项,就可以查询到小程序的AppID。 示例值 在云函数的index.js中加上以下代码: const appid='wxwxd678efh567hg6787' 复制代码 2. openid 第一步已经获得。 示例值 3. mch_id 登陆微信支付商户平台pay.weixin.qq.com,点击上方「账户中心」,在「个人信息」中的「登陆账号」就是mch_id。 示例值 在云函数的index.js中加上以下代码: const mch_id='1230000109' 复制代码 4. nonce_str 任意生成的随机数,不超过32位。你可以自己写个函数。 示例值 我在云函数中创建了一个新的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个字符内,只能是数字、大小写字母_-|*且 在同一个商户号下唯一 。由自己定义,推荐用当下时间+商品编号组成。 示例值 在云函数index.js中的exports.main函数中加上以下代码: //这里我只使用了当下时间。只要这个数字不是重复的就可以。 const out_trade_no = Date.parse(new Date()).toString() 复制代码 7. total_fee 订单总金额,单位为分。比如当前需支付¥6.80,则total_fee为680。 示例值
8. spbill_create_ip 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP。 示例值
9. notify_url 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。在这里可以填上你自己服务器的url。 示例值 在云函数index.js中加上以下代码: //随便填写个服务器就行,我在使用中没有遇到什么问题 const notify_url = 'http://www.weixin.qq.com/wxpay/pay.php' 复制代码 10. trade_type 小程序的trade_type为JSAPI。 示例值 在云函数index.js中加上以下代码: const trade_type = 'JSAPI' 复制代码 11. key key为商户平台设置的密钥key,是由你自己设置的。key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置。 示例值 在云函数index.js中加上以下代码: const key = '1a79a4d60de6718e8e5b326e338ae533' 复制代码 12. sign 将以上除key外所有信息按照参数名ASCII码从大到小拼接成字符串,用&分割,将key放在最后。 字符串示例值: appid=wxd678efh567hg6787&body=微信-游戏&mch_ id=1230000109&nonce_str=5K8264ILTKCH16CQ2502SI8ZNMTM6 7VS¬ify_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码示例值 到此为止你的云函数结构应该为: 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}™_type=${trade_type}& key=1a79a4d60de6718e8e5b326e338ae533` 复制代码 我们现在需要将这个字符串进行MD5码加密,所以需要安装一个npm包来完成这个任务。右键点击云函数pyament,选择「在终端打开」,输入下面的命令: npm install --save crypto 复制代码 完成后在云函数入口文件处加上以下代码 const crypto = require("crypto") 复制代码 这样我们就成功地将crypto这个加密工具包引入我们的云函数里了。然后我们需要在云函数入口函数里使用它对刚刚的stringA进行MD5加密。所以我们在 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我们需要对官方提供的链接 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()需要五个参数,分别是
其中,timeStamp为时间戳,可由 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}¬ify_url=${notify_url}&openid=${openid}&out_trade_no=${out_trade_no}&spbill_create_ip=${spbill_create_ip}&total_fee=${total_fee}™_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 复制代码 |