准备好所需资料,弄懂接口规则,详情的 Node.js 小程序支付过程
const crypto = require('crypto')
const fs = require('fs')
const stringRandom = require('string-random')
const moment = require('moment')
const x509_1 = require('@fidm/x509')
// 支付签名拼接
getSignature(method, url, timestamp, nonce_str, body) {
let str = method + '\n' + url + '\n' + timestamp + '\n' + nonce_str + '\n'
if (body && body instanceof Object) body = JSON.stringify(body)
if (body) str = str + body + '\n'
if (method === 'GET') str = str + '\n'
return this.sha256WithRsa(str)
},
// SHA256-RSA加密后转base64
sha256WithRsa(data) {
const privateKey = fs.readFileSync(`public/apiclient_key.pem`)
if (!privateKey) throw '缺少私钥'
return crypto.createSign('RSA-SHA256').update(data).sign(privateKey, 'base64')
},
// 获取序列号
getSN() {
const publicKey = fs.readFileSync(`public/apiclient_cert.pem`)
if (!publicKey) throw '缺少公钥'
const certificate = x509_1.Certificate.fromPEM(publicKey)
return certificate.serialNumber
},
// 发起请求 支付数据
async WechatPayPost() {
const { appId, mchId } = this.app.config.wechatInfo // 小程序AppId和商户Id
const schema = 'WECHATPAY2-SHA256-RSA2048' // 认证信息
const timestamp = moment().unix() // 时间戳
const nonce_str = stringRandom(16) // 随机数
const notifyUrl = 'https://www.googleapis.com/' // 回调地址
const serial_no = this.getSN() // 获取证书序号
// 请求体Body
const params = {
appid: appId, // 应用ID string[1,32]
mchid: mchId, // 直连商户号 string[1,32]
description: '小程序支付调试', // 商品描述 string[1,127]
out_trade_no: nonce_str, // 商户订单号[6,32] 只能是数字、大小写字母_-*,并且同一个商户号下唯一
attach: '自定义数据', // 附加数据 string[1,128] 在查询API和支付通知中原样返回,可作为自定义参数使用
notify_url: notifyUrl, // 通知地址https string[1,256]
amount: { // 订单金额 单位分
total: 1
},
payer: { // 支付者信息 open_id
openid: '微信open_id' // string[1,128]
}
}
// 获取签名
const sign = this.getSignature('POST', '/v3/pay/transactions/jsapi', timestamp, nonce_str, params)
// 获取prepay_id 特别注意拼接签名信息后是 双引号
const authorization = `${schema} mchid="${mchId}",nonce_str="${nonce_str}",timestamp="${timestamp}",serial_no="${serial_no}",signature="${sign}"`
const res = await wechatAPI.GetPrePayId( params,authorization)
// 小程序支付需要字段
let preData = {
status: res.status,
appId: appId,
timeStamp: moment().unix(),
nonceStr: stringRandom(16),
package: `prepay_id=${res.prepay_id}`,
signType: 'RSA',
paySign: ''
}
// 拼接所需字段
const str = [preData.appId, preData.timeStamp, preData.nonceStr, preData.package, ''].join('\n')
// 补充签名
preData.paySign = this.sha256WithRsa(str)
// 返回小程序支付所需字段
return preData
}
API请求
const axios = require('axios')
const GetPrePayId = (
'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi',
data,
authorization) => {
return new Promise((resolve, reject) => {
axios
.post(url, data, {
headers: {
Authorization: authorization
}
})
.then((res) => {
resolve({
status: res.status,
...res.body
})
})
.catch((err) => {
const fail = JSON.parse(JSON.stringify(err))
reject(fail)
})
})
}
小程序支付接入前准备
pay.weixin.qq.com/wiki/doc/ap…
v3支付官网接口规则
pay.weixin.qq.com/wiki/doc/ap…
关键字使用JSON作为数据交互的格式,不再使用XML,SHA256-RSA加密,只能使用HTTPS访问
证书密钥使用说明
pay.weixin.qq.com/wiki/doc/ap…
商户API私钥,APIv3密钥