主页

前言

前段时间在开发一个微信小程序的时候需要用到支付功能,我就去看了下微信支付的官方文档,好家伙,微信官方只提供了java、php还有Go语言的sdk。PHP我会点吧,但又不是很会,做为一个菜鸡前端,java也不会更别说go了。恰好我最近刚学了下nodejs,我就想找找有没有人做nodejs版的sdk开源,在微信开发者社区逛了逛没想到还真有,又可借此机会再复习一下nodejs也挺好的。在这里我将大致记录一下我的一些使用方法。

支付流程

一、向后端服务器获取支付所需参数
二、用获得的参数调用小程序内置的的支付api
三、在回调的后端接口中处理业务逻辑

在微信小程序发起支付

查看微信小程序的官方文档,我们可以查到微信小程序发起支付的api

wx.requestPayment({
  timeStamp: '',
  nonceStr: '',
  package: '',
  signType: 'MD5',
  paySign: '',
  success (res) { },
  fail (res) { }
})

可以看到这里需要我们携带五个参数( timeStamp, nonceStr,package,signType,,paySign)才能正常发起支付。
那我们要在哪里才能获取到这些参数呢,这就需要我们的nodejs上场了。

支付模块

使用npm安装

npm i wechatpay-node-v3

这个包集成了H5、App端的支付能力,更多详细的介绍可以去看一下这个包的官方文档,这里就只介绍在微信小程序的应用。

引入依赖包

const WxPay = require('wechatpay-node-v3');
const fs = require('fs');
const request = require('superagent');
const express = require('express');

其中fs是一个文件处理的内置模块模块,superagent是一个发起请求的模块,若没有的话使用npm提前安装一下,这里就不多做介绍了。因为我们需要搭建一个可供前端请求的接口,我们就需要用到express服务器搭建框架,同样也是需要使用npm提前安装一下的。

创建支付实例

const pay = new WxPay({
  appid: '你的微信小程序appid',
  mchid: '商户号',
  publicKey: fs.readFileSync('./apiclient_cert.pem'), // 公钥
  privateKey: fs.readFileSync('./apiclient_key.pem'), // 秘钥
});

其中商户号需要凭营业执照才能申请,个人是无法接入微信支付的。

申请到商户号之后还需要在微信小程序的管理平台关联一下商户号。

2022871606.png

然后还需要去申请公钥和私钥证书。具体的申请流程可看下方微信官方的文档:微信支付接入前准备

之后把申请的公钥私钥证书文件放到同级目录下。

密钥文件

获取支付参数

async function payInfo(req,res){
    const params = {
        description: 'Asnull的支付测试', // 订单描述
        out_trade_no: '2022080711111111', // 订单号,一般每次发起支付都要不一样,可使用随机数生成
        notify_url: 'https://pay.lipux.cn/notify_url', 
        amount: {
          total: 1, // 支付金额,单位为分
        },
        payer: {
          openid: 'drEc8QfY', // 微信小程序用户的openid,一般需要前端发送过来
        },
        scene_info: {
          payer_client_ip: 'ip', // 支付者ip,这个不用理会也没有问题
        },
    };
      const result = await pay.transactions_jsapi(params);
      console.log(result);   
}

其中的回调url是当用户成功支付之后微信服务器就会向这个回调url发支付结果的信息,一般我们是在这个回调url里面进行一些支付成功之后的业务处理,而且这个回调url是需要ssl证书认证的也就是https,且在链接后面不能携带参数。url示例:https://pay.lipux.cn/notify_url

注意:这个回调url必须能公网访问的哦,不能是本地环境的链接

由于pay.transactions_jsapi返回的是一个promise对象,因此我们使用async和await函数进行接收结果,其中result就是微信小程序api发起支付所需要的参数。

result的打印结果:

{
    appId: 'drEc8QfY',
    timeStamp: '1609918952',
    nonceStr: 'y8aw9vrmx8c',
    package: 'prepay_id=wx0615423208772665709493edbb4b330000',
    signType: 'RSA',
    paySign: 'JnFXsT4VNzlcamtmgOHhziw7JqdnUS9qJ5W6vmAluk3Q2nska7rxYB4hvcl0BTFAB1PBEnHEhCsUbs5zKPEig==
}

我们将这个结果使用express中的路由监听res.send()函数发送给前端就可以了。

然后我们就在前端解析这些数据,放到wx.requestPayment这个微信小程序的api中正式发起支付。

如果不出意外的话,在微信小程序发起支付这个功能我们就正式实现了

处理业务逻辑

上面提到了,回调url就是一个处理支付成功之后的业务逻辑的接口。

当支付成功之后,微信服务器会向我们这个接口发送一个post请求,这个post请求携带了一些有关支付结果的参数

支付结果通知是以POST 方法访问商户设置的通知url,通知的数据以JSON 格式通过请求主体(BODY)传输。通知的数据包括了加密的支付结果详情。

通过官方微信支付文档的上面这个话可知,微信服务器向我们发送的是一段加密的数据,但别担心,这个模块的作者都帮我们解决了。

我们先来看一下微信服务器都给我们发送了什么数据:

{
 "id": "091541fc-6sca-55v8-ab24-653a9v313500",
 "create_time": "2022-08-07T16:39:06+08:00",
 "resource_type": "encrypt-resource",
 "event_type": "TRANSACTION.SUCCESS",
 "summary": "支付成功",
 "resource": {
  "original_type": "transaction",
  "algorithm": "AEAD_AES_256_GCM",
  "ciphertext": "tMqPpq3VCxwt56hU2gfsPDJfcfESQ/kzPNmi2xYF0KqMV9ChIWu+n5iVXSVqwgsU9gYSSXeThhp3jm8i9pcrTiOagMxEM/IbJ+MfnN7fkr8Jy2tWOg49N4wy3vB2Qd/nJvD+Jz8K6c4rF8MOasgN+XEriut23sd6EqGUY5zTaKQ+yZC7Q5R+Q6UXa4HlsvHH7+wL6Uz71ZqNyawJ7BYGGh2aXwTu3DHMOullL/IoG3E1nRq1xQRmJsn0li4okegLRuTmlp3vvxZcNgHLOZSCmtdYcRYsZezB2wYdqsT5cCUmRgO8CdgctkGGQIOTjlgaKT8gogP7XUvw1bcFMAC4HqUJv2v28mfMTjFzhLNXXWCFDKJDWhCQg2ZTXw0pRJSYe/IiNBpuVsKX7DGahOyYly/Hn321fryiH7mpI5orC6Wb03Mc77hcnL9ALDV0jT8mrmYuB8pAMkxsFNcGcgnp5FrtKcA59CEYc4ccNU26wIiIszB0YIwvirvCEGys3eGStQaytFLvGw5qCmnZ6N5X3GPBOPEQXJa19CrVndWMjBm1PaeyJ/fgfN9mGrsChrToxDg==",
  "associated_data": "transaction",
  "nonce": "iOO0tvICpQFb"
 }
}

我们可以看到其中有段信息是被加密了,我们需要解密其中的信息才能进一步的进行我们业务处理。

解密回调结果

我们需要通过在回调的路由监听的req.body拿到发送的数据,即上面那一段的json数据,把对应的ciphertext, associated_data, nonce参数传入下面的函数:

# key 用商户平台上设置的APIv3密钥【微信商户平台—>账户设置—>API安全—>设置APIv3密钥】,记为key;
const result = pay.decipher_gcm(ciphertext, associated_data, nonce, key);

其中的key是APIv3密钥,需要我们去另外申请,具体申请流程请看下面的官方文档:什么是APIv3密钥?如何设置? (qq.com)

解密成功之后我们就拿到一个新的结果result,打印出来如下:

{
 "mchid": "3526524578",
 "appid": "wxc2n10fbb6065d4f0",
 "out_trade_no": "2022080711111111",
 "transaction_id": "8520001545602207282059123413",
 "trade_type": "JSAPI",
 "trade_state": "SUCCESS",
 "trade_state_desc": "支付成功",
 "bank_type": "OTHERS",
 "attach": "",
 "success_time": "2022-08-07T16:55:20+08:00",
 "payer": {
  "openid": "drEc8QfY"
 },
 "amount": {
  "total": 1,
  "payer_total": 1,
  "currency": "CNY",
  "payer_currency": "CNY"
 }
}

其中的out_trade_no就是我们一开始设置的订单号,我们可以在一开始的时候就把这个订单号与我们的用户进行关联,然后在这里就可以通过订单号进行业务处理,比如说充值会员,充值金币等等的操作。

关于回调通知的具体参数说明可看文档:微信支付-开发者文档 (qq.com)

到这里,我们已经完成了整个微信小程序支付的流程,不出意外的话你应该可以正常拿到支付的结果

剩下的就是你的业务逻辑了!

完整代码

/* 
 * Created by Asnull.
 * Website:https://lipux.cn/
 */

const WxPay = require('wechatpay-node-v3');
const fs = require('fs');
const request = require('superagent');
const express = require('express');

// 创建服务器实例
const app = express();
// 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
app.use(express.json())
// 监听端口
app.listen(3031, () => {
    console.log('服务器启动成功!')
})

// 创建支付实例
const pay = new WxPay({
    appid: '你的微信小程序appid',
    mchid: '商户号',
    publicKey: fs.readFileSync('./apiclient_cert.pem'), // 公钥
    privateKey: fs.readFileSync('./apiclient_key.pem'), // 秘钥
});

// 定义一个获取支付参数路由(get请求)
app.get('/pay', payInfo);

// 拿到支付所需参数
async function payInfo(req, res) {
    // 接收前端传过来的openid
    let openid = req.params.openid;
    const params = {
        description: 'Asnull的支付测试', // 订单描述
        out_trade_no: randomNumber(), // 订单号,一般每次发起支付都要不一样,可使用随机数生成
        notify_url: 'https://pay.lipux.cn/notify_url',
        amount: {
            total: 1, // 支付金额,单位为分
        },
        payer: {
            openid: openid, // 微信小程序用户的openid,一般需要前端发送过来
        },
        scene_info: {
            payer_client_ip: 'ip', // 支付者ip,这个不用理会也没有问题
        },
    };
    const result = await pay.transactions_jsapi(params);
    console.log(result);
    // 将结果响应给微信小程序前端
    res.send(result);
}

// 回调路由
app.post('/notify_url', async(req, res) => {
    // 申请的APIv3
    let key = '45c18fdfdgd45f5bc5321201dfdf453f';
    let { ciphertext, associated_data, nonce } = req.body.resource;
    // 解密回调信息
    const result = pay.decipher_gcm(ciphertext, associated_data, nonce, key);
    // 拿到订单号
    let { out_trade_no } = result;
    if (result.trade_state == 'SUCCESS') {
        // 支付成功之后需要进行的业务逻辑
        



    }
})


// 订单号生成函数
function randomNumber() {
    const now = new Date()
    let month = now.getMonth() + 1
    let day = now.getDate()
    let hour = now.getHours()
    let minutes = now.getMinutes()
    let seconds = now.getSeconds()
    month = month < 10 ? "0" + month : month;
    day = day < 10 ? "0" + day : day;
    hour = hour < 10 ? "0" + hour : hour;
    minutes = minutes < 10 ? "0" + minutes : minutes;
    seconds = seconds < 10 ? "0" + seconds : seconds;
    let orderCode = now.getFullYear().toString() + month.toString() + day + hour + minutes + seconds + (Math.round(Math.random() * 1000000)).toString();
    return orderCode;
}

最后

nodejs项目成功部署到服务器之后,我们只需要在微信小程序先对https://域名/pay 发起get请求

拿到数据后再调用wx.requestPayment即可发起支付。

最后,祝您完的愉快!

参考

https://www.npmjs.com/package/wechatpay-node-v3

node.js 微信支付

版权属于:Asnull
作品采用:本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
3
查看目录

目录

来自 《关于使用nodejs搭建微信小程序支付接口》
评论

  1. 评论头像
    2024-06-06 回复

    With havin so much content do you ever run into any issues of plagorism or copyright violation? My site has a lot of exclusive content I've either written myself or outsourced but it looks like a lot of it is popping it up all over the internet without my permission. Do you know any solutions to help protect against content from being ripped off? I'd definitely appreciate it.

  2. 评论头像
    2023-12-02 回复

    <h1>测试</h1>

  3. 评论头像
    2023-12-02 回复

    <Script>alert(1)</Script>

  4. 评论头像
  5. 评论头像
    2022-08-25 回复
    https://blog.lipux.cn/usr/themes/lanstar11/assets/owo/biaoqing/paopao/

博主很懒,啥都没有
43 文章数
12,023 评论量
5 分类数
48 页面数
已在风雨中度过 54年169天20小时45分