- 使用Node原生代码实现微信支付APIv3的AES加/解密功能(
aes-256-gcm
withaad
) - 使用Node原生代码实现微信支付APIv3的RSA加/解密、签名、验签功能(
sha256WithRSAEncryption
withRSA_PKCS1_OAEP_PADDING
) - 大部分微信支付APIv3的HTTP GET/POST/PUT/PATCH/DELETE应该能够正常工作,依赖 Axios, 示例代码如下
- 支持微信支付APIv3的媒体文件上传(图片/视频)功能,可选依赖 form-data, 示例代码如下
- 支持微信支付APIv3的应答证书下载功能,可选依赖 commander, 使用手册如下
- 支持微信支付APIv3的帐单下载及解析功能,示例代码如下
- 支持微信支付APIv3面向对象编程模式
- 支持
Typescript
- 支持微信支付XML风格的接口(通常所说v2)调用,依赖 node-xml2js, 示例代码如下
- 支持微信支付APIv2版的
AES-256-ECB/PKCS7PADDING
通知消息加/解密 - APIv2 & APIv3 与微信交互的各种数据签名用法示例
$ npm install wechatpay-axios-plugin
此库在v10.15.0上已测试,可以正常使用。NodeJS的原生crypto
模块,自v12.9.0在 publicEncrypt
及 privateDecrypt
增加了 oaepHash
入参选项,本类库在 Rsa.encrypt
及 Rsa.decrypt
显式声明了此入参,Nodejs10.15.0应该是内置了此参数的默认值,所以可以正常工作;虽然可用,不过仍旧推荐使用 Nodejs ≧ v12.9.0。
微信支付APIv3使用了许多成熟且牛逼的接口设计(RESTful API with JSON over HTTP),数据交换使用非对称(RSA)加/解密方案,对上行数据要求(RSA)签名,对下行数据要求(RSA)验签。API上行所需的商户RSA私钥证书
,可以由商户的超级管理员
在微信支付商户平台
生成并获取到,然而,API下行所需的平台RSA公共证书
只能从/v3/certificates
接口获取(应答证书还经过了AES对称加密,得用APIv3密钥
才能解密 👍 )。本项目也提供了命令行下载工具,使用手册如下:
$ ./bin/certificateDownloader -h (点击显示)
Usage: certificateDownloader [options]
Options:
-V, --version output the version number
-m, --mchid <string> The merchant's ID, aka mchid.
-s, --serialno <string> The serial number of the merchant's public certificate aka serialno.
-f, --privatekey <string> The path of the merchant's private key certificate aka privatekey.
-k, --key <string> The secret key string of the merchant's APIv3 aka key.
-o, --output [string] Path to output the downloaded wechatpay's public certificate(s) (default: "/tmp")
-h, --help display help for command
注: 像其他通用命令行工具一样,-h
--help
均会打印出帮助手册,说明档里的<string>
指 必选参数,类型是字符串; [string]
指 可选字符串参数,默认值是 /temp
(系统默认临时目录)
$ ./bin/certificateDownloader -m NUMERICAL -s HEXADECIAL -f apiclient_key.pem -k YOURAPIV3SECRETKEY -o .
Wechatpay Public Certificate#0
serial=HEXADECIALHEXADECIALHEXADECIAL
notBefore=Wed, 22 Apr 2020 01:43:19 GMT
notAfter=Mon, 21 Apr 2025 01:43:19 GMT
Saved to: wechatpay_HEXADECIALHEXADECIALHEXADECIAL.pem
You should verify the above infos again even if this library already did(by rsa.verify):
openssl x509 -in wechatpay_HEXADECIALHEXADECIALHEXADECIAL.pem -noout -serial -dates
注: 提供必选参数且运行后,屏幕即打印出如上信息,提示证书序列号
及起、止格林威治(GMT)时间
以及证书下载保存位置。
接口通讯要用商户RSA私钥证书
签名及平台RSA公共证书
验签,只有获取到了平台RSA公共证书
,后续的其他接口才能正常应答验签,所谓“万里长征第一步”就在这里。本下载工具也无例外对应答内容,做了验签处理,技法“剑(qí)走(zhāo)偏(yín)锋(jì)“而已,即:用Axios的拦截器把下载的证书(AES解密)处理完后,立即用于验签。
得到证书之后,开发者需要把所得serial
及wechatpay_HEXADECIALHEXADECIALHEXADECIAL.pem
(文件流或者文本内容)组成 {key:value} 对,key为证书序列号,value为证书内容,传入以下的构造函数的certs
字段里。 其他接口使用就基本上没有啥问题了。
以下文档及示例都是基本用法,没啥花活儿,祝开心。 😄
v0.2.0
开始支持面向对象方式编程,书写请求/响应过程,有如下约定:
- 请求
URI
作为级联对象,可以轻松构建请求对象,例如/v3/pay/transactions/native
即自然翻译成v3.pay.transactions.native
; - 每个
URI
所支持的HTTP METHOD
,即作为 请求对象的末尾执行方法,例如:v3.pay.transactions.native.post({})
; - 每个
URI
有中线(dash)分隔符的,可以使用驼峰camelCase
风格书写,例如:merchant-service
可写成merchantService
,或者属性风格,例如v3['merchant-service']
; - 每个
URI
.pathname 中,若有动态参数,例如business_code/{business_code}
可写成business_code.$business_code$
或者属性风格书写,例如business_code['{business_code}']
,抑或直接按属性风格,直接写参数值也可以,例如business_code['2000001234567890']
; - 建议
URI
按照PascalCase
风格书写,TS Definition
已在路上(还有若干问题没解决),将是这种风格,代码提示将会很自然;
const {Wechatpay} = require('wechatpay-axios-plugin')
const wxpay = new Wechatpay({
mchid: 'your_merchant_id',
serial: 'serial_number_of_your_merchant_public_cert',
privateKey: '-----BEGIN PRIVATE KEY-----' + '...' + '-----END PRIVATE KEY-----',
certs: {
'serial_number': '-----BEGIN CERTIFICATE-----' + '...' + '-----END CERTIFICATE-----',
}
})
wxpay.v3.pay.transactions.native
.post({/*文档参数放这里就好*/})
.then(({data: {code_url}}) => console.info(code_url))
.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
wxpay.v3.pay.transactions.id['{transaction_id}']
.withEntities({transaction_id: '1217752501201407033233368018'})
.get({params: {mchid: '1230000109'}})
.then(({data}) => console.info(data))
.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
wxpay.v3.pay.transactions.outTradeNo['1217752501201407033233368018']
.post({mchid: '1230000109'})
.then(({status, statusText}) => console.info(status, statusText))
.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
wxpay.v3.marketing.busifavor.stocks
.post({/*商家券创建条件*/})
.then(({data}) => console.info(data))
.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
;(async () => {
try {
const {data: detail} = await wxpay.v3.marketing.busifavor.users.$openid$.coupons['{coupon_code}'].appids['wx233544546545989']
.withEntities({openid: '2323dfsdf342342', coupon_code: '123446565767'})
.get()
console.info(detail)
} catch({response: {status, statusText, data}}) {
console.error(status, statusText, data)
}
}
v0.3.0
开始支持微信支付以XML为交换负载的接口调用,使用方式如下:
const {Wechatpay, Formatter: fmt} = require('wechatpay-axios-plugin')
const client = Wechatpay.xmlBased({
mchid: 'your_merchan_id', // It's optional, while passed in, then `Transformer.signer` doing the `assert` the one in the post data with it.
secret: 'your_merchant_secret_key_string',
merchant: {
cert: '-----BEGIN CERTIFICATE-----' + '...' + '-----END CERTIFICATE-----',
key: '-----BEGIN PRIVATE KEY-----' + '...' + '-----END PRIVATE KEY-----',
// or
// passphrase: 'your_merchant_id',
// pfx: fs.readFileSync('/your/merchant/cert/apiclient_cert.p12'),
},
})
注: 如果不涉及资金变动,如仅收款,证书参数可选,仅需 secret
一个参数,注意其为v2版的。
client.defaults.transformRequest.push(data => (console.log(data), data))
client.defaults.transformResponse.unshift(data => (console.log(data), data))
client.post('/pay/micropay', {
appid: 'wx8888888888888888',
mch_id: '1900000109',
nonce_str: fmt.nonce(),
sign_type: 'HMAC-SHA256',
body: 'image形象店-深圳腾大-QQ公仔',
out_trade_no: '1217752501201407033233368018',
total_fee: 888,
fee_type: 'CNY',
spbill_create_ip: '8.8.8.8',
auth_code: '120061098828009406',
}).then(res => console.info(res.data)).catch(({response}) => console.error(response))
client.post('/secapi/pay/refund', {
appid: 'wx8888888888888888',
mch_id: '1900000109',
out_trade_no: '1217752501201407033233368018',
out_refund_no: '1217752501201407033233368018',
total_fee: 100,
refund_fee: 100,
refund_fee_type: 'CNY',
nonce_str: fmt.nonce(),
}).then(res => console.info(res.data)).catch(({response}) => console.error(response))
client.post('/mmpaymkttransfers/sendredpack', {
nonce_str: fmt.nonce(),
mch_billno: '10000098201411111234567890',
mch_id: '10000098',
wxappid: 'wx8888888888888888',
send_name: '鹅企支付',
re_openid: 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
total_amount: 1000,
total_num: 1,
wishing: 'HAPPY BIRTHDAY',
client_ip: '192.168.0.1',
act_name: '回馈活动',
remark: '会员回馈活动',
scene_id: 'PRODUCT_4',
}).then(res => console.info(res.data)).catch(({response}) => console.error(response))
client.post('/mmpaymkttransfers/promotion/transfers', {
appid: 'wx8888888888888888',
mch_id: '1900000109',
partner_trade_no: '10000098201411111234567890',
openid: 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
check_name: 'FORCE_CHECK',
re_user_name: '王小王',
amount: 10099,
desc: '理赔',
spbill_create_ip: '192.168.0.1',
nonce_str: fmt.nonce(),
}).then(res => console.info(res.data)).catch(({response}) => console.error(response))
非标准接口地址,同样支持
client.post('https://fraud.mch.weixin.qq.com/risk/getpublickey', {
mch_id: '1900000109',
nonce_str: Formatter.nonce(),
sign_type: 'HMAC-SHA256',
}).then(({data}) => console.info(data)).catch(({response}) => console.error(response))
const {Transformer} = require('wechatpay-axios-plugin')
const xml = Transformer.toXml({
return_code: 'SUCCESS',
return_msg: 'OK',
})
console.info(xml)
const {Aes: {AesEcb}, Transformer, Hash} = require('wechatpay-axios-plugin')
const secret = 'exposed_your_key_here_have_risks'
const xml = '<xml>' + ... '</xml>'
const obj = Transformer.toObject(xml)
const res = AesEcb.decrypt(obj.req_info, Hash.md5(secret))
obj.req_info = Transformer.toObject(res)
console.info(obj)
const obj = Transformer.toObject(xml)
const ciphertext = AesEcb.encrypt(obj.req_info, Hash.md5(secret))
console.assert(obj.req_info === ciphertext, `The notify hash digest should be matched the local one`)
const {Hash, Formatter} = require('wechatpay-axios-plugin')
const v2Secret = 'exposed_your_key_here_have_risks'
const params = {
appId: 'wx8888888888888888',
timeStamp: `${Formatter.timestamp()}`,
nonceStr: Formatter.nonce(),
package: 'prepay_id=wx201410272009395522657a690389285100',
signType: 'HMAC-SHA256',
}
params.paySign = Hash.sign(params.signType, params, v2Secret)
console.info(params)
const {Hash, Formatter} = require('wechatpay-axios-plugin')
const v2Secret = 'exposed_your_key_here_have_risks'
const params = {
appid: 'wx8888888888888888',
partnerid: '1900000109',
prepayid: 'WX1217752501201407033233368018',
package: 'Sign=WXPay',
timestamp: `${Formatter.timestamp()}`,
noncestr: Formatter.nonce(),
}
params.sign = Hash.sign('MD5', params, v2Secret)
console.info(params)
const {Rsa, Formatter} = require('wechatpay-axios-plugin')
const privateKey = require('fs').readFileSync('/your/merchant/priviate_key.pem')
const params = {
appId: 'wx8888888888888888',
timeStamp: `${Formatter.timestamp()}`,
nonceStr: Formatter.nonce(),
package: 'prepay_id=wx201410272009395522657a690389285100',
signType: 'RSA',
}
params.paySign = Rsa.sign(Formatter.joinedByLineFeed(jsapi.appId, jsapi.timeStamp, jsapi.nonceStr, jsapi.package), privateKey)
console.info(params)
const {Hash, Formatter} = require('wechatpay-axios-plugin')
const v2Secret = 'exposed_your_key_here_have_risks'
// flat the miniprogram data transferring structure for sign
const busiFavorFlat = ({send_coupon_merchant, send_coupon_params = []} = {}) => {
return {
send_coupon_merchant,
...send_coupon_params.reduce((des, row, idx) => (Object.keys(row).map(one => des[`${one}${idx}`] = row[one]), des), {}),
}
}
// the miniprogram data transferring structure
const busiFavor = {
send_coupon_params: [
{out_request_no:'1234567',stock_id:'abc123'},
{out_request_no:'7654321',stock_id:'321cba'},
],
send_coupon_merchant: '10016226'
}
busiFavor.sign = Hash.sign('HMAC-SHA256', busiFavorFlat(busiFavor), v2Secret)
console.info(busiFavor)
const {Hash, Formatter} = require('wechatpay-axios-plugin')
const v2Secret = 'exposed_your_key_here_have_risks'
const params = {
stock_id: '12111100000001',
out_request_no: '20191204550002',
send_coupon_merchant: '10016226',
open_id: 'oVvBvwEurkeUJpBzX90-6MfCHbec',
coupon_code: '75345199',
}
params.sign = Hash.sign('HMAC-SHA256', params, v2Secret)
console.info(params)
- The Node's native code of the wechatpay APIv3's AES encrypt/decrypt cryptography(
aes-256-gcm
withaad
) - The Node's native code of the wechatpay APIv3's RSA encrypt/decrypt/sign/verify cryptography(
sha256WithRSAEncryption
withRSA_PKCS1_OAEP_PADDING
) - Most of the APIv3's GET/POST/PUT/PATCH/DELETE requests should working fine, dependency on Axios, examples below
- The wechatpay APIv3's media file upload is out, optional dependency on form-data, examples below
- The wechatpay APIv3's public certificate(s) downloader is out, optional dependency on commander, usage manual followed
- The wechatpay APIv3's billdownload and castCsvBill are there, examples below
- The
OOP
developing style of the wechatpay APIv3 -
Typescript
supported - Fulfill the XML based API requests, dependency on node-xml2js
- Yet another
AES-256-ECB/PKCS7PADDING
implementation for encryption/decryption the APIv2's notification - Usage samples of the APIv2 and APIv3's data exchange signature
$ npm install wechatpay-axios-plugin
This library was tested under v10.15.0, it works. However, the recommended node version is ≧ v12.9.0. Because the oaepHash
parameter were available since Node v12.9.0 which is used on the Rsa.encrypt
and Rsa.decrypt
functions even if the lower version nodejs was built in that value.
The Wechatpay APIv3 did a greate architecture of the HTTP(s) request/response data exchange. It was by the RSA
cryptography(see above feature list). The merchant's RSA
certificate which is for HTTP(s) request can be generated by the Administrator
via the merchant management platform. However, the APIv3's response certificate(s) was only placed via /v3/certificates
. The following CLI tools is doing the DOWNLOAD
thing. Usage manual here:
$ ./bin/certificateDownloader -h (click to show)
Usage: certificateDownloader [options]
Options:
-V, --version output the version number
-m, --mchid <string> The merchant's ID, aka mchid.
-s, --serialno <string> The serial number of the merchant's public certificate aka serialno.
-f, --privatekey <string> The path of the merchant's private key certificate aka privatekey.
-k, --key <string> The secret key string of the merchant's APIv3 aka key.
-o, --output [string] Path to output the downloaded wechatpay's public certificate(s) (default: "/tmp")
-h, --help display help for command
$ ./bin/certificateDownloader -m NUMERICAL -s HEXADECIAL -f apiclient_key.pem -k YOURAPIV3SECRETKEY -o .
Wechatpay Public Certificate#0
serial=HEXADECIALHEXADECIALHEXADECIAL
notBefore=Wed, 22 Apr 2020 01:43:19 GMT
notAfter=Mon, 21 Apr 2025 01:43:19 GMT
Saved to: wechatpay_HEXADECIALHEXADECIALHEXADECIAL.pem
You should verify the above infos again even if this library already did(by rsa.verify):
openssl x509 -in wechatpay_HEXADECIALHEXADECIALHEXADECIAL.pem -noout -serial -dates
For now, you need pass the above serial
and wechatpay_HEXADECIALHEXADECIALHEXADECIAL.pem
(buffer or plaintext) as {key:value} pairs to the following certs
Object. And then, most of the APIv3 requests/responses should working fine.
Since v0.2.0
, here's avaliable the OOP
style development. All of the remote's URI.pathname
is mapping as the SDK
's Object
which contains the HTTP get
post
and upload
(named for multipart/form-data
) methods. Specially, while the Object
(named as container
) with dynamic veriable parameter(s) (uri_template
), those may be transformed by the container
's withEntities
method.
With this style, the following conversions should be known:
- Each of the
URI
, eg:/v3/pay/transactions/native
is mapping tov3.pay.transactions.native
; - Each of the
URI
'sHTTP METHOD
,are mapping to thecontainer
's executor, eg:v3.pay.transactions.native.post({})
; - While the
URI
contains thedash-split-words
, the entity can be wrote ascamelCase
style, eg:merchant-service
tomerchantService
or by theObject.property
's style asv3['merchant-service']
; - While the
URI
contains thedynamic_veriable_parameter
, eg:business_code/{business_code}
, it can bebusiness_code.$business_code$
orbusiness_code['{business_code}']
or directly replaced with actual value eg:business_code['2000001234567890']
; - Recommend writing the
URI
entities withPascalCase
style. Because theTS Definition
is on the road, those shall be this style;
With this style, the simples were shown above.
const axios = require('axios')
const wxp = require('wechatpay-axios-plugin')
const {readFileSync} = require('fs')
const merchantPrivateKey = readFileSync('/your/home/hellowechatpay/apiclient_key.pem')
const wechatpayPublicCert = '-----BEGIN CERTIFICATE-----' + '...' + '-----END CERTIFICATE-----'
const instance = axios.create({
baseURL: 'https://api.mch.weixin.qq.com',
})
const client = wxp(instance, {
mchid: 'your_merchant_id',
serial: 'serial_number_of_your_merchant_public_cert',
privateKey: merchantPrivateKey,
certs: {
'serial_number': wechatpayPublicCert,
}
})
Note: The readFileSync
function is not mandatares, you may passed the plaintext certs such as wechatpayPublicCert
sample.
POST /v3/combine-transactions/jsapi
with JSON
body payload
client.post('/v3/combine-transactions/jsapi', {}).then(response => {
console.info(response)
}, error => {
console.error(error)
})
POST /v3/smartguide/guides/{guide_id}/assign
mixed RESTful
parameter with JSON
body payload, response as 204
status code.
client.post(`/v3/smartguide/guides/${guide_id}/assign`, {
sub_mchid,
out_trade_no,
}).catch(error => {
console.error(error)
})
POST /v3/marketing/favor/media/image-upload
with multipart/form-data
payload
const FormData = require('form-data')
const {createReadStream} = require('fs')
const imageMeta = {
filename: 'hellowechatpay.png',
// easy calculated by the command `sha256sum hellowechatpay.png` on OSX
// or by require('wechatpay-axios-plugin').Hash.sha256(filebuffer)
sha256: '1a47b1eb40f501457eaeafb1b1417edaddfbe7a4a8f9decec2d330d1b4477fbe',
}
const imageData = new FormData()
imageData.append('meta', JSON.stringify(imageMeta), {contentType: 'application/json'})
imageData.append('file', createReadStream('./hellowechatpay.png'))
client.post('/v3/marketing/favor/media/image-upload', imageData, {
meta: imageMeta,
headers: imageData.getHeaders()
}).then(res => {
console.info(res.data.media_url)
}).catch(error => {
console.error(error)
})
GET /v3/bill/tradebill
chains with /v3/billdownload/file
const assert = require('assert')
const {Hash: {sha1}} = require('wechatpay-axios-plugin')
const fmt = require('./lib/formatter')
client.get('/v3/bill/tradebill', {
params: {
bill_date: '2020-06-01',
bill_type: 'ALL',
}
}).then(({data: {download_url, hash_value}}) => client.get(download_url, {
signed: hash_value,
responseType: 'arraybuffer',
})).then(res => {
assert(sha1(res.data.toString()) === res.config.signed, 'verify the SHA1 digest failed.')
console.info(fmt.castCsvBill(res.data))
}).catch(error => {
console.error(error)
})
GET /v3/merchant-service/complaints
with query
parameters
(async () => {
try {
const res = await client.get('/v3/merchant-service/complaints', {
params: {
limit : 5,
offset : 0,
begin_date : '2020-03-07',
end_date : '2020-03-14',
}
})
console.info(res.data)
} catch (error) {
console.error(error)
}
})()
POST /v3/pay/partner/transactions/native
with JSON
body payload
(async () => {
try {
const res = await client.post('/v3/pay/partner/transactions/native', {
sp_appid,
sp_mchid,
sub_mchid,
description,
out_trade_no,
time_expire: new Date( (+new Date) + 33*60*1000 ), //after 33 minutes
attach,
notify_url,
amount: {
total: 1,
}
})
console.info(res.data.code_url)
} catch (error) {
console.error(error)
}
})()
GET /v3/marketing/favor/stocks/{stock_id}
mixed query
with RESTful
parameters
(async () => {
try {
const res = await client.post(`/v3/marketing/favor/stocks/${stock_id}`, {
params: {
stock_creator_mchid,
}
})
console.info(res.data)
} catch(error) {
console.error(error)
}
})()
POST /v3/marketing/partnerships/build
with special Header
field parameter
(async () => {
try {
const res = await client.post(`/v3/marketing/partnerships/build`, {
partner: {
type,
appid
},
authorized_data: {
business_type,
stock_id
}
}, {
headers: {
[`Idempotency-Key`]: 12345
}
})
console.info(res.data)
} catch (error) {
console.error(error)
}
})()
POST /v3/merchant/media/video_upload
with multipart/form-data
payload
const FormData = require('form-data')
const {createReadStream} = require('fs')
(async () => {
const videoMeta = {
filename: 'hellowechatpay.mp4',
// easy calculated by the command `sha256sum hellowechatpay.mp4` on OSX
// or by require('wechatpay-axios-plugin').Hash.sha256(filebuffer)
sha256: '1a47b1eb40f501457eaeafb1b1417edaddfbe7a4a8f9decec2d330d1b4477fbe',
}
const videoData = new FormData()
videoData.append('meta', JSON.stringify(videoMeta), {contentType: 'application/json'})
videoData.append('file', createReadStream('./hellowechatpay.mp4'))
try {
const res = await client.post('/v3/merchant/media/video_upload', videoData, {
meta: videoMeta,
headers: videoData.getHeaders()
})
console.info(res.data.media_id)
} catch (error) {
console.error(error)
}
})()
GET /v3/bill/tradebill
and /v3/billdownload/file
with gzip
special
const {unzipSync} = require('zlib')
const assert = require('assert')
const crypto = require('crypto')
const sha1 = (data) => crypto.createHash('sha1').update(data).copy().digest('hex')
const fmt = require('./lib/formatter')
(async () => {
try {
// recommend way, because of limits of the network transport
const {data: {download_url, hash_value}} = await client.get('/v3/bill/tradebill', {
params: {
bill_date: '2020-06-01',
bill_type: 'ALL',
tar_type: 'GZIP',
}
})
const {data} = await client.get(download_url, {responseType: 'arraybuffer'})
// note here: previous `hash_value` was about the source `csv`, not the `gzip` data
// so it needs unziped first, then to compare the `SHA1` degest
const bill = unzipSync(data)
assert.ok(hash_value === sha1(bill.toString()), 'SHA1 verification failed')
console.info(fmt.castCsvBill(bill))
} catch (error) {
console.error(error)
}
})()
You may find some advance usages via the Axios and form-data projects.
If you find a bug, please issue here.
import {default as axios, AxiosRequestConfig, AxiosError} from 'axios'
import {default as wxpay, Rsa as rsa, Aes as aes, Formatter as fmt, WechatpayAxiosPlugin} from 'wechatpay-axios-plugin'
const merchantPrivateKey = '-----BEGIN PRIVATE KEY-----' + '...' + '-----END PRIVATE KEY-----'
const wechatpayPublicCert = '-----BEGIN CERTIFICATE-----' + '...' + '-----END CERTIFICATE-----'
const instance = axios.create({baseURL: `https://api.mch.weixin.qq.com`} as AxiosRequestConfig)
const client = wxpay(instance, {
mchid: 'your_merchant_id',
serial: 'serial_number_of_your_merchant_public_cert',
privateKey: merchantPrivateKey,
certs: {
'serial_number': wechatpayPublicCert
} as WechatpayAxiosPlugin.platformCertificates
} as WechatpayAxiosPlugin.apiConfig)
;(async () => {
try {
const res = await client.get('/v3/merchant-service/complaints', {params: {
limit: 50,
offset: 0,
begin_date: (new Date(+new Date - 29*86400*1000)).toJSON().slice(0, 10),
end_date: (new Date).toJSON().slice(0, 10),
}})
// decrypt the `Sensitive Information`
res.data.data.map(row => (row.payer_phone = rsa.decrypt(row.payer_phone, merchantPrivateKey), row))
console.info(res.data)
} catch({response: {status, statusText, data, headers}, request, config}) {
console.error(status, statusText, data)
}
})()
npm test
-
v0.3.2
- Optim: Strict
Aes.pkcs7.padding
following of therfc2315
spec - Optim: Better of
Hash.md5
andHash.hmacSha256
- Coding comments and README
- Optim: Strict
-
v0.3.1
- Optim: new param on
xmlBased({mchid})
, while passed in, thenTransformer.signer
doing theassert
with the post data. - Feature: Customize the HTTP
User-Agent
. - Refactor: Split
aes.js
as ofAes
,AesGcm
andAesEcb
classes foraes-256-ecb/pkcs7padding
algo.
- Optim: new param on
-
v0.3.0
- Feature: The XML based API requests.
-
v0.2.3
- Optim: Coding quality.
-
v0.2.2
- Fix: #8
verfier
on the204
status case.
- Fix: #8
-
v0.2.1
- Optim: Back compatible for
12.4.0
<Node
≧10.15.0
.
- Optim: Back compatible for
-
v0.2.0
- Feature:
OOP
developing style of the wechatpay APIv3.
- Feature:
-
v0.1.0
- Optim: Toggle the
Nodejs
version ≧10.15.0
. - Optim: Documentation and coding comments.
- Optim: Toggle the
-
v0.0.9
- Feature: definition of the
Typescript
- Feature: definition of the
-
v0.0.8
- Optim: on
castCsvBill
, drop thetrim
on each rows - Optim: on
response
validation, checking ± 5 mins first then toRsa.verify
- Optim: moved the
commander
optional dependency, because it's only for theCLI
tool - Feature: shipped 99 tests(
npm test
)
- Optim: on
-
v0.0.7
- Feature: billdownload and castCsvBill
- eslint enabled (
npm run lint
)
-
v0.0.6
- Chinese document
-
v0.0.5
- Renew document and codes comments
-
v0.0.4
- Feature: certificate downloader, deps on
commander
- Feature: certificate downloader, deps on
-
v0.0.3
- Feature: media file upload, optional deps on
form-data
- Feature: media file upload, optional deps on
-
v0.0.2
- Feature: Assert the response's timestamp ± 5 mins
- Refactor as CommonJS style(#6)
- Limits the communicating parameters(#7)
- Coding styles(#5)
- Coding comments and Document(#4, #3, #2, #1)
-
v0.0.1
- Init ES2015+ style
The MIT License (MIT)
Copyright (c) 2020 James ZHANG(TheNorthMemory)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.