Gathering detailed insights and metrics for wechatpay-axios-plugin
Gathering detailed insights and metrics for wechatpay-axios-plugin
Gathering detailed insights and metrics for wechatpay-axios-plugin
Gathering detailed insights and metrics for wechatpay-axios-plugin
微信支付 WeChatPay OpenAPI v2&v3' SDK,以命令行方式与接口交互,play the openapi requests over command line
npm install wechatpay-axios-plugin
Typescript
Module System
Min. Node Version
Node Version
NPM Version
JavaScript (100%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
273 Stars
431 Commits
41 Forks
6 Watchers
1 Branches
5 Contributors
Updated on Jul 13, 2025
Latest Version
0.9.4
Package Id
wechatpay-axios-plugin@0.9.4
Unpacked Size
126.49 kB
Size
36.77 kB
File Count
19
NPM Version
10.9.2
Node Version
23.11.0
Published on
May 08, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
3
1
Promise based and chained WeChatPay OpenAPI SDK for NodeJS
NodeJs >= 12
$ npm install wechatpay-axios-plugin
1const { Wechatpay } = require('wechatpay-axios-plugin'); 2const { readFileSync } = require('fs'); 3 4// 商户号,支持「普通商户/特约商户」或「服务商商户」 5const merchantId = '190000****'; 6 7// 「商户API证书」的「证书序列号」 8const merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********'; 9 10// 「商户API私钥」`file://`协议的本地文件绝对路径 11const merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem'; 12 13// APIv3 的「平台证书」接入模式 {{{ 14// 「平台证书」的「证书序列号」 15// 可以从「平台证书」文件解析,也可以在 商户平台 -> 账户中心 -> API安全 查询到 16// const platformCertificateSerial = '7132D72A03E93CDDF8C03BBD1F37EEDF********'; 17 18// 「平台证书」`file://`协议的本地文件绝对路径 19// 「平台证书」文件可由内置的CLI工具下载到 20// const platformCertificateFilePath = 'file:///path/to/wechatpay/certificate.pem'; 21// }}} 22 23// APIv3 的「微信支付公钥」接入模式 {{{ 24// 「微信支付公钥」`file://`协议的本地文件绝对路径 25// 需要在 商户平台 -> 账户中心 -> API安全 下载 26const platformPublicKeyFilePath = 'file:///path/to/wechatpay/publickey.pem'; 27 28// 「微信支付公钥」的「微信支付公钥ID」 29// 需要在 商户平台 -> 账户中心 -> API安全 查询 30const platformPublicKeyId = 'PUB_KEY_ID_01142321349124100000000000********'; 31// }}} 32 33// 构造一个 APIv2 & APIv3 客户端实例 34const wxpay = new Wechatpay({ 35 mchid: merchantId, 36 serial: merchantCertificateSerial, 37 privateKey: merchantPrivateKeyFilePath, 38 // 根据商户号所能接入的APIv3模式(微信支付公钥/平台证书)按需配置certs对象内容 39 certs: { 40 // 「平台证书」 接入模式时,则填 platformCertificate* 配置项及配置行,多平台证书时配多行 41 // [platformCertificateSerial]: platformCertificateFilePath, 42 // 「微信支付公钥」 接入模式时,则填 platformPublicKey* 配置项及配置行,当前新商户只此模式 43 [platformPublicKeyId]: platformPublicKeyFilePath, 44 }, 45 // APIv2(密钥32字节) 46 secret: 'your_merchant_secret_key_string', 47 // 部分接口要求使用「商户API证书」的场景,需要额外配置如下{cert,key}或{pfx,passphrase}参数 48 merchant: { 49 cert: readFileSync('/path/to/merchant/apiclient_cert.pem'), 50 key: readFileSync(merchantPrivateKeyFilePath.slice(7)), 51 // 或者配置如下`passphrase`及`pfx`配置项 52 // passphrase: 'your_merchant_id', 53 // **注**: Node17.1开始使用OpenSSL3,老的p12文件需要额外格式转换 54 // pfx: readFileSync('/your/merchant/cert/apiclient_cert.p12'), 55 }, 56});
初始化字典说明如下:
mchid
为你的商户号,一般是10字节纯数字serial
为你的商户证书序列号,一般是40字节字符串privateKey
为你的商户API私钥,一般是通过官方证书生成工具生成的文件名是apiclient_key.pem
文件,支持纯字符串或者文件流buffer
格式certs{[serial_number]:string}
为key/value
键值对,键为平台证书序列号/微信支付公钥ID,值为平台证书/微信支付公钥pem格式的纯字符串或者文件流buffer
格式secret
为APIv2版的密钥
,商户平台上设置的32字节字符串merchant.cert
为你的商户证书,一般是文件名为apiclient_cert.pem
文件,支持纯字符串或者文件流buffer
格式merchant.key
为你的商户API私钥,一般是通过官方证书生成工具生成的文件名是apiclient_key.pem
文件,支持纯字符串或者文件流buffer
格式merchant.passphrase
一般为你的商户号merchant.pfx
为你的商户PKCS12
格式的证书,文件名一般为apiclient_cert.p12
,支持二进制文件流buffer
格式(注: Node17.1开始使用OpenSSL3,老的p12文件需要额外格式转换)注: APIv2&APIv3以及Axios初始参数,均融合在一个型参上。
1wxpay.v3.pay.transactions.native 2 .post({ 3 mchid: '1900006XXX', 4 out_trade_no: 'native12177525012014070332333', 5 appid: 'wxdace645e0bc2cXXX', 6 description: 'Image形象店-深圳腾大-QQ公仔', 7 notify_url: 'https://weixin.qq.com/', 8 amount: { 9 total: 1, 10 currency: 'CNY' 11 }, 12 }) 13 .then(({data: {code_url}}) => console.info(code_url)) 14 .catch(({response: { 15 status, 16 statusText, 17 data 18 } }) => console.error(status, statusText, data))
1// _placeholder_ 语法糖会转换成 '{placeholder}' 格式 2wxpay.v3.pay.transactions.id._transaction_id_ 3 .get({ 4 params: { 5 mchid: '1230000109' 6 }, 7 //当商户订单号有大写字符时,只能这样参数化传递 8 transaction_id: '1217752501201407033233368018' 9 }) 10 .then(({data}) => console.info(data)) 11 .catch(({response: { 12 status, 13 statusText, 14 data 15 } }) => console.error(status, statusText, data))
1// $placeholder$ 语法糖会转换成 '{placeholder}' 格式 2wxpay.v3.pay.transactions.outTradeNo.$out_trade_no$.close 3 .post({ 4 mchid: '1230000109' 5 }, { 6 //当商户订单号有大写字符时,只能这样参数化传递 7 out_trade_no: 'P1217752501201407033233368018' 8 }) 9 .then(({status, statusText}) => console.info(status, statusText)) 10 .catch(({response: { 11 status, 12 statusText, 13 data 14 } }) => console.error(status, statusText, data))
1 2;(async () => { 3 try { 4 const res = await wxpay.v3.refund.domestic.refunds.post({ 5 transaction_id: '1217752501201407033233368018', 6 out_refund_no: '1217752501201407033233368018', 7 reason: '商品已售完', 8 notify_url: 'https://weixin.qq.com', 9 funds_account: 'AVAILABLE', 10 amount: { 11 refund: 888, 12 from: [{ 13 account: 'AVAILABLE', 14 amount: 444, 15 }], 16 total: 888, 17 currency: 'CNY' 18 }, 19 }); 20 } catch({response: {status, statusText, data}}) { 21 console.error(status, statusText, data) 22 } 23})()
更多示例代码访问这里
1wxpay.v2.mmpaymkttransfers.sendredpack.post({ 2 nonce_str: Formatter.nonce(), // 自v0.9.0起可以无需声明 3 mch_billno: '10000098201411111234567890', 4 mch_id: '10000098', 5 wxappid: 'wx8888888888888888', 6 send_name: '鹅企支付', 7 re_openid: 'oxTWIuGaIt6gTKsQRLau2M0yL16E', 8 total_amount: '1000', 9 total_num: '1', 10 wishing: 'HAPPY BIRTHDAY', 11 client_ip: '192.168.0.1', 12 act_name: '回馈活动', 13 remark: '会员回馈活动', 14 scene_id: 'PRODUCT_4', 15}) 16.then(res => console.info(res.data)) 17.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
1const {Rsa} = require('wechatpay-axios-plugin') 2 3wxpay.v2.risk.getpublickey.post({ 4 mch_id: '1900000109', 5 sign_type: Hash.ALGO_MD5, 6 nonce_str: Formatter.nonce(), // 自v0.9.0起可以无需声明 7}, { 8 baseURL: 'https://fraud.mch.weixin.qq.com/', 9 // 声明请求是私有ssl协议,对应加载初始化的 merchant{key,cert} 参数 10 security: true, 11}) 12.then(res => { 13 const b64 = res.data.pub_key.trim().split(/\r?\n/).slice(1, -1).join('') 14 console.info(Rsa.fromPkcs1(b64, Rsa.KEY_TYPE_PUBLIC)) 15}) 16.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
更多示例代码访问这里
企业微信的企业支付,数据请求包需要额外的签名,仅需做如下简单扩展适配,即可支持;以下签名注入函数所需的两个参数agentId
agentSecret
来自企业微信工作台,以下为示例值。
1const {Hash} = require('wechatpay-axios-plugin') 2const agentId = '0' // 企业微信应用ID,0是企微内置的特殊应用 3const agentSecret = Hash.keyObjectFrom('from_wework_agent_special_string') // 自v0.9.0可用
1const {Hash,Formatter} = require('wechatpay-axios-plugin') 2 3wxpay.client.v2.defaults.transformRequest.unshift(function workwxredpack(data) { 4 const {act_name, mch_billno, mch_id, nonce_str, re_openid, total_amount, wxappid} = data 5 6 if (!(act_name && mch_billno && mch_id && nonce_str && re_openid && total_amount && wxappid)) { 7 return data 8 } 9 10 data.workwx_sign = Hash.md5( 11 Formatter.queryStringLike(Formatter.ksort({ 12 act_name, mch_billno, mch_id, nonce_str, re_openid, total_amount, wxappid 13 })), agentSecret, agentId 14 ).toUpperCase() 15 16 return data 17})
1wxpay.v2.mmpaymkttransfers.sendworkwxredpack.post({ 2 mch_billno: '123456', 3 wxappid: 'wx8888888888888888', 4 sender_name: 'XX活动', 5 sender_header_media_id: '1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K0', 6 re_openid: 'oxTWIuGaIt6gTKsQRLau2M0yL16E', 7 total_amount: '1000', 8 wishing: '感谢您参加猜灯谜活动,祝您元宵节快乐!', 9 act_name: '猜灯谜抢红包活动', 10 remark: '猜越多得越多,快来抢!', 11 mch_id: '1900000109', 12 nonce_str: Formatter.nonce(), // 需要显式声明 13}, { 14 // 声明请求是私有ssl协议,对应加载初始化的 merchant{key,cert} 参数 15 security: true, 16}) 17.then(res => console.info(res.data)) 18.catch(console.error)
1const {Hash,Formatter} = require('wechatpay-axios-plugin') 2wxpay.client.v2.defaults.transformRequest.unshift(function wwsptrans2pocket(data) { 3 const {amount, appid, desc, mch_id, nonce_str, openid, partner_trade_no, ww_msg_type} = data 4 5 if (!(amount && appid && desc && mch_id && nonce_str && openid && partner_trade_no && ww_msg_type)) { 6 return data 7 } 8 9 data.workwx_sign = Hash.md5( 10 Formatter.queryStringLike(Formatter.ksort({ 11 amount, appid, desc, mch_id, nonce_str, openid, partner_trade_no, ww_msg_type 12 })), agentSecret, agentId 13 ).toUpperCase() 14 15 return data 16})
1wxpay.v2.mmpaymkttransfers.promotion.paywwsptrans2pocket.post({ 2 appid: 'wxe062425f740c8888', 3 device_info: '013467007045764', 4 partner_trade_no: '100000982017072019616', 5 openid: 'ohO4Gt7wVPxIT1A9GjFaMYMiZY1s', 6 check_name: 'NO_CHECK', 7 re_user_name: '张三', 8 amount: '100', 9 desc: '六月出差报销费用', 10 spbill_create_ip: '10.2.3.10', 11 ww_msg_type: 'NORMAL_MSG', 12 act_name: '示例项目', 13 mch_id: '1900000109', 14 nonce_str: Formatter.nonce(), // 需要显式声明 15}, { 16 // 声明请求是私有ssl协议,对应加载初始化的 merchant{key,cert} 参数 17 security: true, 18}) 19.then(res => console.info(res.data)) 20.catch(console.error)
1// APIv2 日志 2wxpay.client.v2.defaults.transformRequest.push(data => (console.log(data), data)) 3wxpay.client.v2.defaults.transformResponse.unshift(data => (console.log(data), data)) 4// APIv3 日志 5wxpay.client.v3.defaults.transformRequest.push((data, headers) => (console.log(data, headers), data)) 6wxpay.client.v3.defaults.transformResponse.unshift((data, headers) => (console.log(data, headers), data))
1const {Transformer} = require('wechatpay-axios-plugin') 2const xml = Transformer.toXml({ 3 return_code: 'SUCCESS', 4 return_msg: 'OK', 5}) 6 7console.info(xml)
1const {Aes: {AesEcb}, Transformer, Hash} = require('wechatpay-axios-plugin')
2const v2Secret = Hash.keyObjectFrom('exposed_your_key_here_have_risks') // 自v0.9.0可用
3const xml = '<xml>' + ... '</xml>'
4const obj = Transformer.toObject(xml)
5const res = AesEcb.decrypt(obj.req_info, Hash.md5(v2Secret/*自v0.9.2开始支持*/))
6obj.req_info = Transformer.toObject(res)
7console.info(obj)
1const obj = Transformer.toObject(xml)
2const ciphertext = AesEcb.encrypt(obj.req_info, Hash.md5(v2Secret/*自v0.9.2开始支持*/))
3console.assert(
4 obj.req_info === ciphertext,
5 `The notify hash digest should be matched the local one`
6)
1const {Hash, Formatter} = require('wechatpay-axios-plugin') 2const v2Secret = Hash.keyObjectFrom('exposed_your_key_here_have_risks') // 自v0.9.0可用 3const params = { 4 appId: 'wx8888888888888888', 5 timeStamp: `${Formatter.timestamp()}`, 6 nonceStr: Formatter.nonce(), 7 package: 'prepay_id=wx201410272009395522657a690389285100', 8 signType: Hash.ALGO_HMAC_SHA256, 9} 10params.paySign = Hash.sign(params.signType, params, v2Secret) 11 12console.info(params)
1const {Hash, Formatter} = require('wechatpay-axios-plugin') 2const v2Secret = Hash.keyObjectFrom('exposed_your_key_here_have_risks') // 自v0.9.0可用 3const params = { 4 appid: 'wx8888888888888888', 5 partnerid: '1900000109', 6 prepayid: 'WX1217752501201407033233368018', 7 package: 'Sign=WXPay', 8 timestamp: `${Formatter.timestamp()}`, 9 noncestr: Formatter.nonce(), 10} 11params.sign = Hash.sign(Hash.ALGO_MD5, params, v2Secret) 12 13console.info(params)
1const {Rsa, Formatter} = require('wechatpay-axios-plugin')
2const merchantPrivateKeyInstance = Rsa.from('file:///your/merchant/priviate_key.pem') // 自v0.9.0可用
3
4const params = {
5 appId: 'wx8888888888888888',
6 timeStamp: `${Formatter.timestamp()}`,
7 nonceStr: Formatter.nonce(),
8 package: 'prepay_id=wx201410272009395522657a690389285100',
9 signType: 'RSA',
10}
11params.paySign = Rsa.sign(Formatter.joinedByLineFeed(
12 params.appId, params.timeStamp, params.nonceStr, params.package
13), merchantPrivateKeyInstance)
14
15console.info(params)
1const {Hash, Formatter} = require('wechatpay-axios-plugin') 2const v2Secret = Hash.keyObjectFrom('exposed_your_key_here_have_risks') // 自v0.9.0可用 3 4// flat the miniprogram data transferring structure for sign 5const busiFavorFlat = ({send_coupon_merchant, send_coupon_params = []} = {}) => { 6 return { 7 send_coupon_merchant, 8 ...send_coupon_params.reduce((des, row, idx) => ( 9 Object.keys(row).map(one => des[`${one}${idx}`] = row[one]), des 10 ), {}), 11 } 12} 13 14// the miniprogram data transferring structure 15const busiFavor = { 16 send_coupon_params: [ 17 {out_request_no:'1234567',stock_id:'abc123'}, 18 {out_request_no:'7654321',stock_id:'321cba'}, 19 ], 20 send_coupon_merchant: '10016226' 21} 22 23busiFavor.sign = Hash.sign(Hash.ALGO_HMAC_SHA256, busiFavorFlat(busiFavor), v2Secret) 24 25console.info(busiFavor)
1const {Hash, Formatter} = require('wechatpay-axios-plugin') 2const v2Secret = Hash.keyObjectFrom('exposed_your_key_here_have_risks') // 自v0.9.0可用 3const params = { 4 stock_id: '12111100000001', 5 out_request_no: '20191204550002', 6 send_coupon_merchant: '10016226', 7 open_id: 'oVvBvwEurkeUJpBzX90-6MfCHbec', 8 coupon_code: '75345199', 9} 10params.sign = Hash.sign(Hash.ALGO_HMAC_SHA256, params, v2Secret) 11 12console.info(params)
Q: APIv3
上请求参数敏感信息如何加密?返回参数敏感信息如何解密?
接口区分国内版还是国际版,国内版的
RSA
填充方案是RSA_PKCS1_OAEP_PADDING
,方法如下:加密:
Rsa.encrypt('原始信息', Rsa.from(platformPublicKeyFilePath, Rsa.KEY_TYPE_PUBLIC))
解密:
Rsa.decrypt('密文base64', Rsa.from(merchantPrivateKeyFilePath, Rsa.KEY_TYPE_PRIVATE))
国际版
RSA
填充方案是RSA_PKCS1_PADDING
,本类库放弃支持此种加/解密
填充方案,同时node18.19.0
也是最后一版默认支持加密
的版本(延展阅读见CVE-2023-46809及这里),如需使用,请自行寻替代方案。
Q: 如何安全地在应用内使用APIv2
及APIv3
对称密钥?
v0.9.0
提供了统一的对称密钥加载函数Hash.keyObjectFrom(thing: BinaryLike): KeyObject
,建议应用升级至v0.9.2
使用此函数进行统一对称密钥管理;
Q: 如何加载RSA
公/私钥和X509
证书公钥?
v0.9.0
提供了统一的加载函数Rsa.from(thing: KeyLike, type: 'public'|'private'): KeyObject
Rsa.from(thing, type)
支持从文件/字符串/字节流加载公/私钥和证书,特别地,支持file://
,private.pkcs8://
,private.pkcs1://
,public.pkcs1://
,public.spki://
协议的公/私钥字符串;Rsa.fromPkcs1
是个语法糖,支持加载PKCS#1
格式的公/私钥,入参是base64
字符串;Rsa.fromPkcs8
是个语法糖,支持加载PKCS#8
格式的私钥,入参是base64
字符串;Rsa.fromSpki
是个语法糖,支持加载SPKI
格式的公钥,入参是base64
字符串;
Q: APIv3消息通知,AES-256-GCM
加密字段,应该如何解密?
官方文档有介绍,APIv3平台证书及消息通知关键信息均使用
AesGcm
加解密,依赖APIv3密钥
,商户侧解密可参考bin/cli/cert.js
证书下载工具,例如:
1AesGcm.decrypt(ciphertext, secret, nonce, aad);
Q: 敏感信息或者幂等操作要求额外头信息上送时,应该如何构建请求参数?
DELETE
/GET
请求的第一个参数,POST
/PUT
/PATCH
请求的第二个参数,是 AxiosRequestConfig 对象,可以按需上送额外头参数,例如:
npm install && npm test
如果遇到困难或建议可以 提ISSUE 或 加群,交流技术,分享经验。
QQ群: 684379275
如果你觉得这个library
不错,你可以扫如下赞赏码
以资鼓励作者,博客更有部分"实战"内容,也可能对你的开发对接有所帮助。
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
0 existing vulnerabilities detected
Reason
license file detected
Details
Reason
security policy file detected
Details
Reason
4 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 5
Reason
Found 0/30 approved changesets -- score normalized to 0
Reason
no SAST tool detected
Details
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More