微信支付链路+封装对接微信API工具类(下)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 微信支付链路+封装对接微信API工具类(下)

封装获取微信平台证书列表+解密得到微信公钥

wx回调本地地址时使用wx证书的公 钥进行解密。

WxPayment
    //验证签名 timestamp,nonce,serial,signature均在HTTP头中获取,body为请求参数
    async verifySign({ timestamp, nonce, serial, body, signature }) {
        // 拼接参数
        let data = `${timestamp}\n${nonce}\n${typeof body == 'string' ? body : JSON.stringify(body)}\n`;
        // 用crypto模块解密
        let verify = crypto.createVerify('RSA-SHA256');
        // 添加摘要内容
        verify.update(Buffer.from(data));
        // 从初始化的平台证书中获取公钥
        for (let cert of this.certificates) {
            if (cert.serial_no == serial) {
                return verify.verify(cert.public_key, signature, 'base64');
            } else {
                throw new Error('平台证书序列号不相符')
            }
        }
    }
       //解密证书列表 解出CERTIFICATE以及public key
    async decodeCertificates() {
        let result = await this.getCertificates();
        if (result.status != 200) {
            throw new Error('获取证书列表失败')
        }
        let certificates = typeof result.data == 'string' ? JSON.parse(result.data).data : result.data.data
        for (let cert of certificates) {
            let output = this.dec ode(cert.encrypt_certificate)
            cert.decrypt_certificate = output.toString()
            let beginIndex = cert.decrypt_certificate.indexOf('-\n')
            let endIndex = cert.decrypt_certificate.indexOf('\n-')
            let str = cert.decrypt_certificate.substring(beginIndex + 2, endIndex)
            // 生成X.509证书
            let x509Certificate = new x509.X509Certificate(Buffer.from(str, 'base64'));
            let public_key = Buffer.from(x509Certificate.publicKey.rawData).toString('base64')
            // 平台证书公钥
            cert.public_key = `-----BEGIN PUBLIC KEY-----\n` + public_key + `\n-----END PUBLIC KEY-----`
        }
        return this.certificates = certificates
    }
    //解密
    decode(params) {
        const AUTH_KEY_LENGTH = 16;
        // ciphertext = 密文,associated_data = 填充内容, nonce = 位移
        const { ciphertext, associated_data, nonce } = params;
        // 密钥
        const key_bytes = Buffer.from(this.apiv3_private_key, 'utf8');
        // 位移
        const nonce_bytes = Buffer.from(nonce, 'utf8');
        // 填充内容
        const associated_data_bytes = Buffer.from(associated_data, 'utf8');
        // 密文Buffer
        const ciphertext_bytes = Buffer.from(ciphertext, 'base64');
        // 计算减去16位长度
        const cipherdata_length = ciphertext_bytes.length - AUTH_KEY_LENGTH;
        // upodata
        const cipherdata_bytes = ciphertext_bytes.slice(0, cipherdata_length);
        // tag
        const auth_tag_bytes = ciphertext_bytes.slice(cipherdata_length, ciphertext_bytes.length);
        const decipher = crypto.createDecipheriv(
            'aes-256-gcm', key_bytes, nonce_bytes
        );
        decipher.setAuthTag(auth_tag_bytes);
        decipher.setAAD(Buffer.from(associated_data_bytes));
        const output = Buffer.concat([
            decipher.update(cipherdata_bytes),
            decipher.final(),
        ]);
        return output;
    } 

封装验证微信发起的签名+解密数据得到用户订单信息

开发微信服务器回调接口

  callback: async (req) => {
    let timestamp = req.header('Wechatpay-Timestamp')
    let nonce = req.header('Wechatpay-Nonce')
    let serial = req.header('Wechatpay-Serial')
    let signature = req.header('Wechatpay-Signature')
    let body = req.body
    // 1.校验收到的请求是否来自微信服务器和平台证书是否一致
    let result = await payment.verifySign({
      timestamp,
      nonce,
      serial,
      signature,
      body
    })
    if (!result) {
      return
    }
    // 2.解密body中的数据,拿到用户订单信息
    let bufferoOne = payment.decode(body.resource)
    let json = JSON.parse(bufferoOne.toString('utf8'))
    let { out_trade_no, trade_state } = json
    console.log(json)
    if (trade_state === 'SUCCESS') {
      // 3.根据微信服务器返回的订单信息更新数据库中改订单的支付状态
      await DB.ProductOrder.update({ order_state: 'PAY' }, { where: { out_trade_no } })
      // 4.更新redis课程热门排行榜数据
      let productItem = await DB.ProductOrder.findOne({ where: { out_trade_no }, raw: true })
      let memberInfo = {
        id: productItem.product_id,
        title: productItem.product_title,
        img: productItem.product_img,
      }
      let time = dayjs(Date.now()).format('YYYY-MM-DD')
      await redisConfig.zincrby({ key: `${time}:rank:hot_product`, increment: 1, member: JSON.stringify(memberInfo) })
    }
    return BackCode.buildSuccess()
  },

这里redis的逻辑是

  1. DB.ProductOrder.findOne({ where: { out_trade_no }, raw: true }): 这是一个数据库查询操作,通过out_trade_no作为查询条件,在ProductOrder表中查找匹配的记录,并返回一个包含产品项信息的对象。raw: true选项表示返回原始数据,而不进行模型实例化。
  2. 接下来,将产品项的信息存储到memberString对象中,包括产品的 ID、标题和图片。
  3. dayjs(Date.now()).format('YYYY-MM-DD'): 这行代码使用了第三方库 dayjs,用于获取当前日期并以 'YYYY-MM-DD' 的格式进行格式化。生成的日期字符串被存储在time变量中。
  4. redisConfig.zincrby({ key: {time}:rank:hot_product, increment: 1, member: JSON.stringify(memberString) }): 这是一个使用 Redis 的命令,通过zincrby方法向 Redis 的有序集合中更新数据。key参数表示要更新的有序集合的键名,这里使用了{time}:rank:hot_product作为键名;increment参数表示每次增加的分值,这里设置为 1;member参数表示要添加或更新的成员,将前面存储的产品项信息以 JSON 字符串的形式作为成员。

每天凌晨清除昨天的数据

定时插件

yarn add node-schedule@2.1.0

配置

// 定时任务工具
const schedule = require('node-schedule')
let rule = new schedule.RecurrenceRule()
class ScheduleTool {
  // 每天凌晨0点执行
  static dayJob(handle) {
    rule.hour = 0
    schedule.scheduleJob(rule, handle)
  }
}
module.exports = ScheduleTool

定时任务执行

ScheduleTool.dayJob(() => {
  let yesterday = dayjs().subtract(1, 'day').format('YYYY-MM-DD')
  redisConfig.del(`${yesterday}:rank:hot_product`)
})

我们要拿到的就是body.resource

image.png

pay.weixin.qq.com/wiki/doc/ap…

订单状态确认-查询订单⽀付状态逻辑封装+快速验证

node服务器主动进行接口调用查询订单

查询订单逻辑封装

//通过out_trade_no查询订单
async getTransactionsByOutTradeNo(params) {
  return await this.wxSignRequest({ pathParams: params, type: 'getTransactionsByOutTradeNo' })
}

验证订单状态查询

const { payment } = require('./config/wechatPay');
(async () => {
    let wechatOrder = await payment.getTransactionsByOutTradeNo({ out_trade_no: '123456789wqjeqjwdiqhdhqd' })
    console.log(wechatOrder.data)
})()
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
13天前
|
API 开发者
微信native支付对接案例详解
本文详细介绍了微信Native支付的对接流程,包括效果展示、产品介绍、接入前准备、开发指引、API列表、支付通知等,并强调了只有通过微信认证的服务号才能对接微信支付。每年需支付300元认证费用。
27 3
|
4月前
|
小程序 数据安全/隐私保护
Taro@3.x+Vue@3.x+TS开发微信小程序,网络请求封装
在 `src/http` 目录下创建 `request.ts` 文件,并配置 Taro 的网络请求方法 `Taro.request`,支持多种 HTTP 方法并处理数据加密。
177 0
Taro@3.x+Vue@3.x+TS开发微信小程序,网络请求封装
|
4月前
|
Web App开发 缓存 小程序
【Azure API 管理】从微信小程序访问APIM出现200空响应的问题中发现CORS的属性[terminate-unmatched-request]功能
【Azure API 管理】从微信小程序访问APIM出现200空响应的问题中发现CORS的属性[terminate-unmatched-request]功能
|
4月前
|
小程序 前端开发 JavaScript
微信小程序实现微信支付(代码和注释很详细)
微信小程序实现微信支付(代码和注释很详细)
|
4月前
|
前端开发 JavaScript API
微信公众号项目,实现微信支付(具体流程和参数)
微信公众号项目,实现微信支付(具体流程和参数)
|
5月前
|
安全 API Windows
支付系统13------支付系统的资料在技术库里的在线支付当中,怎样获取微信平台证书那?怎样获取微信平台证书那?第一步打开我们的微信支付平台的文档中心
支付系统13------支付系统的资料在技术库里的在线支付当中,怎样获取微信平台证书那?怎样获取微信平台证书那?第一步打开我们的微信支付平台的文档中心
|
5月前
|
存储
支付系统36-------订单表优化,添加payment_type字段,无论是微信支付还是支付宝支付都放到 t_order_info表中了,payment_type用来判断支付宝还是微信支付的
支付系统36-------订单表优化,添加payment_type字段,无论是微信支付还是支付宝支付都放到 t_order_info表中了,payment_type用来判断支付宝还是微信支付的
|
6月前
|
API
个人微信api接口源代码
个人微信api接口源代码
|
5月前
|
JavaScript
支付系统---微信支付23-数据绑定和事件,使用Vue调试要添加扩展,F12与console平级就出现了Vue,在支付资料,微信支付,04资料,工具里
支付系统---微信支付23-数据绑定和事件,使用Vue调试要添加扩展,F12与console平级就出现了Vue,在支付资料,微信支付,04资料,工具里
|
5月前
|
API
支付系统----微信支付24--APlv3介绍,首先我们要引入支付参数,包含商户号,APPID、API秘钥,数字证书,用代码加载到应用程序当中,之后加载商户私钥,商户需要用私钥进行验签,微信平台接收商
支付系统----微信支付24--APlv3介绍,首先我们要引入支付参数,包含商户号,APPID、API秘钥,数字证书,用代码加载到应用程序当中,之后加载商户私钥,商户需要用私钥进行验签,微信平台接收商