合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
# 微信小程序连接微信支付流程教程 太坑了,我必须写下来。 撰写支付系统前要准备一下资料,要注意有微信小程序平台和微信支付平台两个平台,两个设置是独立的。准备资料会需要好几天,可以的话要提早准备。 1. App Id 和 App Secret :这个要从微信小程序平台提取,注册好了就会有。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210706230359855.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80OTMyMzk1NA==,size_16,color_FFFFFF,t_70) 2. Merchant Id (mchid) : 这个要从微信支付平台提取,并且要从那个平台连接上你的小程序,一切报备要做。 3. CA 证书:这个从微信支付平台得到,在安全中心里。微信会提供一个证书(这个过程还挺麻烦的,直接参考官方文档),还有需要你自己设置的两个api key,注意你需要的是证据zip里的apiclient\_key.pem,序列号还有你的api v3密钥。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210706230414116.png) 4. https 域名的服务器,这个服务器主体会绑定域名,所以要注意,你可以考虑用一个服务器做proxy转接,真正的逻辑在另一台电脑。小程序中只能用已经报备好的https域名进行对接!还有就是一些的api只能通过后端使用,前端不应该使用,因为关系到你的密钥。 # 进入正题 **1\. 申请微信支付** 小程序认证以后,可以在小程序后台,微信支付菜单栏,申请微信支付。 ![](https://segmentfault.com/img/bVF8NT?w=639&h=300) 填写企业信息和对公账户,微信支付会打一笔随机金额到对公账户,输入金额完成验证后,在线签署协议,这样整个微信支付的申请流程完成了。 微信支付申请完,会发送微信支付商户号,商户平台用户名密码等信息到注册者邮箱。 **2\. 准备工作** **2.1 配置小程序密钥** ![](https://segmentfault.com/img/bVF8NX?w=646&h=312) 在小程序后台设置页,点击生成,管理员验证二维码后,会随机生成 AppSecret。请妥善保管好 Appsecret,不要明文存储于服务器,AppSecret 用于和微信服务器交互。比如获取用户的 openid 接口就需要用到。 **2.2 设置密钥和下载证书** 用申请微信支付获得的用户名和密码,登录商户平台 (pay.weixin.qq.com),在账户中心,API 安全中下载证书和设置[密钥](https://so.csdn.net/so/search?q=%E5%AF%86%E9%92%A5&spm=1001.2101.3001.7020)。 密钥是 32 位,设置以后需要妥善保管,因为无法查看密钥,所有微信支付相关的接口都会使用这个密钥加密。 **2.3 配置 Https 服务器** 小程序的前端是使用微信提供的框架开发,但是后台依然是开发者自己的服务器。小程序发起的是 https 请求,意味着小程序开发者必须配置 https 服务器。 配置 https 服务器之前,先要获取证书,证书可以向相关机构购买,腾讯云目前可以向用户提供免费的证书。 证书安装指引在这里查看: [https://www.qcloud.com/doc/pr...](https://www.qcloud.com/doc/product/400/4143) **3\. 微信支付流程** 微信支付有多种支付方式,包括刷卡支付,公众号支付,扫码支付,APP 支付,在这里微信支付的所有接口:[https://pay.weixin.qq.com/wik...](https://pay.weixin.qq.com/wiki/doc/api/index.html) 小程序是在微信里调起支付的,其实是公众号支付。关于公众号支付的详细文档可在这里查看:[https://pay.weixin.qq.com/wik...](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1) 所有公众号支付相关的链接都可以在此链接找到,开发者首先需要大概了解这些接口。 小程序公众号支付的主要流程如下(本图只考虑了正常流程,异常流程参考公众号支付文档): ![](https://segmentfault.com/img/bVF8N6?w=639&h=390) **3.1 关于 openid** 上述流程中请求 openid,用的是小程序最新 api 中的接口,开发者可以查看小程序的登录接口。 开发者从第三方服务器获得的 openid,在统一下单的接口里面需要使用到。 **3.2 关于小程序调起微信支付** 上述流程中的小程序调起微信支付,用的是小程序微信支付接口 wx.requestPayment,该接口的详细描述可以查看小程序的微信支付 API。 这个接口中的 package 和 timeStamp 参数是从开发者的第三方服务器返回的,package 是第三方服务器从统一下单接口回复中获得。 接口中其他的参数,appId,noceStr,signType 以及 paySign 则由小程序这边存储或者计算而得。 其中 paySign 是签名,签名算法文档在这里: [https://pay.weixin.qq.com/wik...](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3&t=20161107) 整个公众号支付的主要流程基本都是在第三方服务器上实现,开发者需要熟读公众号支付文档,了解消息交互流程以及每个接口。 这里并没有描述正常微信支付必不可少的一些功能:异常处理,查询订单,发起退款,下载对账单等等。 1. 使用 wx.login 得到一个暂时的登入码。注意,每一次wx.login会把之前的登入码作废,所以需要的时候直接用wx.checkSession来检查。 2. 在后端使用`https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${appsecret}&js_code=${登入码}&grant_type=authorization_code`([https://developers.weixin.qq.com/miniprogram/en/dev/api-backend/open-api/login/auth.code2Session.html](https://developers.weixin.qq.com/miniprogram/en/dev/api-backend/open-api/login/auth.code2Session.html)). 这个会让你得到一个openid,这个openid不会改变,同一个用户会有一个openid。 3. 准备预付,参考([https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3\_5\_1.shtml](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml)和[https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4\_0.shtml](https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml)),会使用RSA制造签名,然后向https://api.mch.weixin.qq.com 这个统一下单api发出请求。NodeJS 参考以下代码。\[1\]。这样会得到一个prepay\_id. 4. 使用3.得到的prepay\_id,根据[https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3\_5\_4.shtml](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml)和3.的方法做多一次签名,是给前端的。参考\[2\]。 5. 在前端在wx.requestPayment使用randomString,timestamp,signature,还有prepay\_id,记得表明signType是RSA. \[1\] ~~~js const { v4: uuidv4 } = require('uuid') var crypto = require('crypto') var fs = require('fs') const pem = fs.readFileSync('./certs/apiclient_key.pem') const key = pem.toString('ascii') let minifiedRawData = JSON.stringify(JSON.parse(JSON.stringify(rawData))) const currentUnixTime = parseInt((new Date().getTime() / 1000).toFixed(0)) const randomString = uuidv4() //这个随机就可以,但是前端后端发出请求必须用同一个 let sign = crypto.createSign('RSA-SHA256') sign.update( `POST\n/v3/pay/transactions/jsapi\n${currentUnixTime}\n${randomString}\n${minifiedRawData}\n` ) const token = sign.sign(key, 'base64') const r = await axios.post( `https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi`, rawData, { headers: { Authorization: `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",nonce_str="${randomString}",signature="${token}",timestamp="${currentUnixTime}",serial_no="${certSerialNumber}"` } } ) ~~~ \[2\] ~~~js const r = await axios.post( `https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi`, rawData, { headers: { Authorization: `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",nonce_str="${randomString}",signature="${token}",timestamp="${currentUnixTime}",serial_no="${certSerialNumber}"` } } ) let frontendSign = crypto.createSign('RSA-SHA256') frontendSign.update( `${appid}\n${currentUnixTime}\n${randomString}\nprepay_id=${r.data.prepay_id}\n` ) r.data.timestamp = currentUnixTime r.data.requestId = randomString r.data.signature = frontendSign.sign(key, 'base64') ~~~ ![](https://img.kancloud.cn/25/d9/25d93b3a65b3ac97f0b794dac15b67fd_713x629.png)