node框架egg中使用V3小程序支付

525 阅读2分钟

准备好所需资料,弄懂接口规则,详情的 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密钥