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

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
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
目录
相关文章
|
2月前
|
缓存 测试技术 API
API的封装步骤流程
API封装流程是一个系统化的过程,旨在将内部功能转化为可复用的接口供外部调用。流程包括明确需求、设计接口、选择技术和工具、编写代码、测试、文档编写及部署维护。具体步骤为确定业务功能、数据来源;设计URL、请求方式、参数及响应格式;选择开发语言、框架和数据库技术;实现数据连接、业务逻辑、错误处理;进行功能、性能测试;编写详细文档;部署并持续维护。通过这些步骤,确保API稳定可靠,提高性能。
|
3月前
|
开发框架 缓存 前端开发
基于SqlSugar的开发框架循序渐进介绍(23)-- Winform端管理系统中平滑增加对Web API对接的需求
基于SqlSugar的开发框架循序渐进介绍(23)-- Winform端管理系统中平滑增加对Web API对接的需求
|
7天前
|
JSON API 数据格式
淘宝 / 天猫官方商品 / 订单订单 API 接口丨商品上传接口对接步骤
要对接淘宝/天猫官方商品或订单API,需先注册淘宝开放平台账号,创建应用获取App Key和App Secret。之后,详细阅读API文档,了解接口功能及权限要求,编写认证、构建请求、发送请求和处理响应的代码。最后,在沙箱环境中测试与调试,确保API调用的正确性和稳定性。
|
29天前
|
监控 安全 测试技术
如何确保API对接过程中的数据安全?
确保API对接过程中的数据安全至关重要。最佳实践包括:使用HTTPS协议、强化身份验证和授权、数据加密、输入验证、访问控制、限流限速、日志记录和监控、安全测试、数据脱敏、错误处理、API网关、Web应用程序防火墙(WAF)、审计和合规性。这些措施能有效提升API的安全性,保护数据免受恶意攻击和泄露风险。
|
2月前
|
API PHP
ThinkPHP 通用的API格式封装
本文介绍了在ThinkPHP框架中如何统一封装API返回格式的方法,包括创建状态码枚举类、编写统一格式化函数以及在BaseController和Error控制器中重写`__call`方法来处理不存在的方法或控制器调用,以实现统一的错误处理和返回格式。
ThinkPHP 通用的API格式封装
|
1月前
|
JSON JavaScript API
(API接口系列)商品详情数据封装接口json数据格式分析
在成长的路上,我们都是同行者。这篇关于商品详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦!
|
1月前
|
API 定位技术
api接口如何对接?(带你了解api接口的相关知识)
API接口是在产品和研发领域广泛应用的专业术语,主要用于公司内部系统衔接及公司间合作。本文将详细讲解API接口的概念、必要性及其核心要素。首先介绍API接口的基本原理与应用场景,随后阐述其重要性,最后解析API接口的核心组成部分,帮助读者深入理解API接口的工作机制。适合产品小白和求职者阅读,提升专业知识。
|
2月前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
97 11
|
3月前
|
小程序 数据安全/隐私保护
Taro@3.x+Vue@3.x+TS开发微信小程序,网络请求封装
在 `src/http` 目录下创建 `request.ts` 文件,并配置 Taro 的网络请求方法 `Taro.request`,支持多种 HTTP 方法并处理数据加密。
122 0
Taro@3.x+Vue@3.x+TS开发微信小程序,网络请求封装
|
3月前
|
小程序 前端开发 JavaScript
微信小程序实现微信支付(代码和注释很详细)
微信小程序实现微信支付(代码和注释很详细)