同步操作将从 TheNorthMemory/wechatpay-axios-plugin 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
The WeChatPay OpenAPI v2&v3' Smart Development Kit
crypto
实现微信支付APIv3的AES加/解密功能(aes-256-gcm
with aad
)crypto
实现微信支付APIv3的RSA加/解密、签名、验签功能(sha256WithRSAEncryption
with RSA_PKCS1_OAEP_PADDING
)Typescript
AES-256-ECB/PKCS7PADDING
通知消息加/解密NodeJS原生crypto
模块,自v12.9.0在 publicEncrypt
及 privateDecrypt
增加了 oaepHash
入参选项,本类库封装的 Rsa.encrypt
及 Rsa.decrypt
显式声明了此入参,测试下来在NodeJS10.15.0上可正常工作;虽然在v10.15上可用,不过仍旧推荐使用 NodeJS >= v12.9.0。
$ npm install wechatpay-axios-plugin
微信支付APIv3使用 (RESTful API with JSON over HTTP)接口设计,数据交换采用非对称(RSA-OAEP)加/解密方案。API上行所需的商户RSA私钥证书
,可以由商户超级管理员
使用专用证书生成工具生成并获取到,然而,API下行所需的平台RSA证书
只能从/v3/certificates
接口获取(应答证书还经过了对称(AES-GCM)加密,须采用APIv3密钥
才能解密)。本项目也提供了命令行下载工具,使用手册如下:
wxpay crt
The WeChatPay APIv3's Certificate Downloader
cert
-m, --mchid The merchant's ID, aka mchid. [string] [required]
-s, --serialno The serial number of the merchant's certificate aka serialno. [string] [required]
-f, --privatekey The path of the merchant's private key certificate aka privatekey. [string] [required]
-k, --key The secret key string of the merchant's APIv3 aka key. [string] [required]
-o, --output Path to output the downloaded WeChatPay's platform certificate(s) [string] [default: "/tmp"]
Options:
--version Show version number [boolean]
--help Show help [boolean]
-u, --baseURL The baseURL [string] [default: "https://api.mch.weixin.qq.com/"]
注: 像其他通用命令行工具一样,--help
均会打印出帮助手册,说明档里的[required]
指 必选参数; [string]
指 字符串类型,[default]
指默认值
The WeChatPay Platform Certificate#0
serial=HEXADECIAL
notBefore=Wed, 22 Apr 2020 01:43:19 GMT
notAfter=Mon, 21 Apr 2025 01:43:19 GMT
Saved to: wechatpay_HEXADECIAL.pem
You may confirm the above infos again even if this library already did(by Rsa.verify):
openssl x509 -in wechatpay_HEXADECIAL.pem -noout -serial -dates
注: 提供必选参数且运行后,屏幕即打印出如上信息,提示证书序列号
及起、止格林威治(GMT)时间
及证书下载保存位置。
v0.5版,命令行工具做了加强,增加了基础请求方法,可以用来做快速接入体验,用法如下:
wxpay req <uri>
Play the WeChatPay OpenAPI requests over command line
<uri>
-c, --config The configuration [required]
-b, --binary Point out the response as `arraybuffer` [boolean]
-m, --method The request HTTP verb [choices: "DELETE", "GET", "POST", "PUT", "PATCH", "delete", "get", "post", "put", "patch"] [default: "POST"]
-h, --headers Special request HTTP header(s)
-d, --data The request HTTP body
-p, --params The request HTTP query parameter(s)
Options:
--version Show version number [boolean]
--help Show help [boolean]
-u, --baseURL The baseURL [string] [default: "https://api.mch.weixin.qq.com/"]
./node_modules/.bin/wxpay v3/pay/transactions/native \
-c.mchid 1230000109 \
-c.serial HEXADECIAL \
-c.privateKey /path/your/merchant/mchid.key \
-c.certs.HEXADECIAL /path/the/platform/certificates/HEXADECIAL.pem \
-d.appid wxd678efh567hg6787 \
-d.mchid 1230000109 \
-d.description 'Image形象店-深圳腾大-QQ公仔' \
-d.out_trade_no '1217752501201407033233368018' \
-d.notify_url 'https://www.weixin.qq.com/wxpay/pay.php' \
-d.amount.total 100 \
-d.amount.currency CNY
./node_modules/.bin/wxpay v2/pay/micropay \
-c.mchid 1230000109 \
-c.serial any \
-c.privateKey any \
-c.certs.any \
-c.secret your_merchant_secret_key_string \
-d.appid wxd678efh567hg6787 \
-d.mch_id 1230000109 \
-d.device_info 013467007045764 \
-d.nonce_str 5K8264ILTKCH16CQ2502SI8ZNMTM67VS \
-d.detail 'Image形象店-深圳腾大-QQ公仔' \
-d.spbill_create_ip 8.8.8.8 \
-d.out_trade_no '1217752501201407033233368018' \
-d.total_fee 100 \
-d.fee_type CNY \
-d.auth_code 120061098828009406
./node_modules/.bin/wxpay v2/tools/authcodetoopenid \
-c.mchid 1230000109 \
-c.serial any \
-c.privateKey any \
-c.certs.any \
-c.secret your_merchant_secret_key_string \
-d.appid wxd678efh567hg6787 \
-d.mch_id 1230000109 \
-d.nonce_str 5K8264ILTKCH16CQ2502SI8ZNMTM67VS \
-d.auth_code 120061098828009406
本类库自0.2
开始,按照 URL.pathname
以/
做切分,映射成对象属性,0.4
版开始,支持APIv2的pathname
映射,编码书写方式有如下约定:
pathname
作为级联对象,可以轻松构建请求对象,例如 /v3/pay/transactions/native
即自然翻译成 v3.pay.transactions.native
;pathname
所支持的 HTTP METHOD
,即作为 请求对象的末尾执行方法,例如: v3.pay.transactions.native.post({})
;pathname
级联对象默认为HTTPPOST
函数,其同时隐式内置GET/POST/PUT/PATCH/DELETE
操作方法链,支持全大写及全小写(未来有可能会删除)两种编码方式,说明见变更历史
;pathname
有中线(dash)分隔符的,可以使用驼峰camelCase
风格书写,例如: merchant-service
可写成 merchantService
,或者属性风格,例如 v3['merchant-service']
;pathname
中,若有动态参数,例如 business_code/{business_code}
可写成 business_code.$business_code$
或者属性风格书写,例如 business_code['{business_code}']
,抑或按属性风格,直接写值也可以,例如 business_code['2000001234567890']
;/v2
对象,其特殊标识为APIv2级联对象,之后串接切分后的pathname
,如 /v2/pay/micropay
即以XML形式请求远端接口;pathname
按照 PascalCase
风格书写, TS Definition
已在路上(还有若干问题没解决),将是这种风格,代码提示将会很自然;以下示例用法,均以Promise
或Async/Await
结合此种编码模式展开,级联对象操作符的调试信息见文档末。
const {Wechatpay, Formatter} = require('wechatpay-axios-plugin')
const wxpay = new Wechatpay({
// 商户号
mchid: 'your_merchant_id',
// 商户证书序列号
serial: 'serial_number_of_your_merchant_public_cert',
// 商户私钥证书 PEM格式的文本字符串或者文件buffer
privateKey: '-----BEGIN PRIVATE KEY-----\n-FULL-OF-THE-FILE-CONTENT-\n-----END PRIVATE KEY-----',
certs: {
// CLI `wxpay crt -m {商户号} -s {商户证书序列号} -f {商户私钥证书路径} -k {APIv3密钥(32字节)} -o {保存地址}` 生成
'serial_number': '-----BEGIN CERTIFICATE-----\n-FULL-OF-THE-FILE-CONTENT-\n-----END CERTIFICATE-----',
},
// APIv2密钥(32字节) v0.4 开始支持
secret: 'your_merchant_secret_key_string',
// 接口不要求证书情形,例如仅收款merchant对象参数可选
merchant: {
// 商户证书 PEM格式的文本字符串或者文件buffer
cert: '-----BEGIN CERTIFICATE-----\n-FULL-OF-THE-FILE-CONTENT-\n-----END CERTIFICATE-----',
// 商户私钥证书 PEM格式的文本字符串或者文件buffer
key: '-----BEGIN PRIVATE KEY-----\n-FULL-OF-THE-FILE-CONTENT-\n-----END PRIVATE KEY-----',
// or
// passphrase: 'your_merchant_id',
// pfx: fs.readFileSync('/your/merchant/cert/apiclient_cert.p12'),
},
// APIv2沙箱环境地址
// baseURL: 'https://api.mch.weixin.qq.com/sandboxnew',
// 建议初始化设置此参数,详细说明见Axios官方README
// maxRedirects: 0,
})
初始化字典说明如下:
mchid
为你的商户号,一般是10字节纯数字serial
为你的商户证书序列号,一般是40字节字符串privateKey
为你的商户私钥证书,一般是通过官方证书生成工具生成的文件名是apiclient_key.pem
文件,支持纯字符串或者文件流buffer
格式certs{[serial_number]:string}
为通过下载工具下载的平台证书key/value
键值对,键为平台证书序列号,值为平台证书pem格式的纯字符串或者文件流buffer
格式secret
为APIv2版的密钥
,商户平台上设置的32字节字符串merchant.cert
为你的商户证书,一般是文件名为apiclient_cert.pem
文件,支持纯字符串或者文件流buffer
格式merchant.key
为你的商户私钥证书,一般是通过官方证书生成工具生成的文件名是apiclient_key.pem
文件,支持纯字符串或者文件流buffer
格式merchant.passphrase
一般为你的商户号merchant.pfx
为你的商户PKCS12
格式的证书,文件名一般为apiclient_cert.p12
,支持二进制文件流buffer
格式注: 0.4版本做了重构及优化,APIv2&v3以及Axios初始参数,均融合在一个型参上。
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}']
.get({params: {mchid: '1230000109'}, transaction_id: '1217752501201407033233368018'})
.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.combineTransactions.jsapi
.post({/*文档参数放这里就好*/})
.then(res => console.info(res.data))
.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
wxpay.v3.pay.transactions.h5
.post({/*文档参数放这里就好*/})
.then(({data: {h5_url}}) => console.info(h5_url))
.catch(console.error)
const assert = require('assert')
const {Hash: {sha1}} = require('wechatpay-axios-plugin')
wxpay.v3.bill.tradebill.get({
params: {
bill_date: '2021-02-12',
bill_type: 'ALL',
}
}).then(({data: {download_url, hash_value}}) => wxpay.v3.billdownload.file.get({
params: (new URL(download_url)).searchParams,
signed: hash_value,
responseType: 'arraybuffer',
})).then(res => {
assert(sha1(res.data.toString()) === res.config.signed, 'verify the SHA1 digest failed.')
console.info(Formatter.castCsvBill(res.data))
}).catch(error => {
console.error(error)
})
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']
.get({openid: '2323dfsdf342342', coupon_code: '123446565767'})
console.info(detail)
} catch({response: {status, statusText, data}}) {
console.error(status, statusText, data)
}
}
;(async () => {
try {
const res = await wxpay.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)
}
})()
;(async () => {
try {
const {status, statusText} = await wxpay.v3.smartguide.guides.$guide_id$.assign
.post({sub_mchid, out_trade_no}, {guide_id})
console.info(status, statusText)
} catch({response: {status, statusText, data}}) {
console.error(status, statusText, data)
}
}
;(async () => {
try {
const res = await wxpay.v3.merchantService.complaints.get({
params: {
limit : 5,
offset : 0,
begin_date : '2020-03-07',
end_date : '2020-03-14',
}
})
console.info(res.data)
} catch (error) {
console.error(error)
}
})()
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'))
;(async () => {
try {
const res = await wxpay.v3.marketing.favor.media.imageUpload.post(imageData, {
meta: imageMeta,
headers: imageData.getHeaders()
})
console.info(res.data.media_url)
} catch (error) {
console.error(error)
}
})()
;(async () => {
try {
const res = await wxpay.v3.marketing.favor.stocks.$stock_id$.post({
params: {
stock_creator_mchid,
},
stock_id,
})
console.info(res.data)
} catch(error) {
console.error(error)
}
})()
(async () => {
try {
const res = await wxpay.v3.marketing.partnerships.build.post({
partner: {
type,
appid
},
authorized_data: {
business_type,
stock_id
}
}, {
headers: {
[`Idempotency-Key`]: 12345
}
})
console.info(res.data)
} catch (error) {
console.error(error)
}
})()
(async () => {
try {
let res = await wxpay.v3.marketing.favor.stocks.$stock_id$.useFlow.get({stock_id})
res = await wxpay.v3.billdownload.file.get({
params: (new URL(res.data.url)).searchParams,
responseType: 'arraybuffer'
})
// 备注:此接口下载的文件格式与商户平台下载的不完全一致,Formatter.castCsvBill解析有差异
console.info(res.data.toString())
} catch (error) {
console.error(error)
}
})()
const FormData = require('form-data')
const {createReadStream} = require('fs')
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'))
;(async () => {
try {
const res = await wxpay.v3.merchant.media.video_upload.post(videoData, {
meta: videoMeta,
headers: videoData.getHeaders()
})
console.info(res.data.media_id)
} catch (error) {
console.error(error)
}
})()
const {unzipSync} = require('zlib')
const assert = require('assert')
const {Hash: {sha1}} = require('wechatpay-axios-plugin')
;(async () => {
try {
const {data: {download_url, hash_value}} = await wxpay.v3.bill.fundflowbill.GET({
params: {
bill_date: '2020-02-12',
bill_type: 'BASIC',
tar_type: 'GZIP',
}
})
const {data} = await wxpay.v3.billdownload.file.GET({
params: (new URL(download_url)).searchParams,
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(Formatter.castCsvBill(bill))
} catch (error) {
console.error(error)
}
})()
wxpay.v2.pay.micropay({
appid: 'wx8888888888888888',
mch_id: '1900000109',
nonce_str: Formatter.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: {status, statusText, data}}) => console.error(status, statusText, data))
wxpay.v2.pay.unifiedorder({
appid: 'wx2421b1c4370ec43b',
attach: '支付测试',
body: 'H5支付测试',
mch_id: '10000100',
nonce_str: Formatter.nonce(),
notify_url: 'http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php',
openid: 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',
out_trade_no: '1415659990',
spbill_create_ip: '14.23.150.211',
total_fee: 1,
trade_type: 'MWEB',
scene_info: JSON.stringify({
h5_info: {
type:"IOS",
app_name: "王者荣耀",
package_name: "com.tencent.tmgp.sgame"
}
}),
}).then(({data: {mweb_url}}) => console.info(mweb_url)).catch(console.error);
wxpay.v2.secapi.pay.refund.post({
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: Formatter.nonce(),
})
.then(res => console.info(res.data))
.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
wxpay.v2.mmpaymkttransfers.sendredpack.POST({
nonce_str: Formatter.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: {status, statusText, data}}) => console.error(status, statusText, data))
wxpay.v2.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: Formatter.nonce(),
})
.then(res => console.info(res.data))
.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
wxpay.v2.risk.getpublickey({
mch_id: '1900000109',
sign_type: 'MD5',
nonce_str: Formatter.nonce(),
}, {
baseURL: 'https://fraud.mch.weixin.qq.com'
})
.then(res => console.info(res.data))
.catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))
企业微信的企业支付,数据请求包需要额外的签名,仅需做如下简单扩展适配,即可支持;以下签名注入函数所需的两个参数agentId
agentSecret
来自企业微信工作台,以下为示例值。
const agentId = 1001001
const agentSecret = 'from_wework_agent_special_string'
const {Hash} = require('wechatpay-axios-plugin')
Wechatpay.client.v2.defaults.transformRequest.unshift(function workwxredpack(data, headers) {
const {act_name, mch_billno, mch_id, nonce_str, re_openid, total_amount, wxappid} = data
if (!(act_name && mch_billno && mch_id && nonce_str && re_openid && total_amount && wxappid)) {
return data
}
data.workwx_sign = Hash.md5(
Formatter.queryStringLike(Formatter.ksort({
act_name, mch_billno, mch_id, nonce_str, re_openid, total_amount, wxappid
})), agentSecret, agentId
).toUpperCase()
return data
})
wxpay.v2.mmpaymkttransfers.sendworkwxredpack({
mch_billno: '123456',
wxappid: 'wx8888888888888888',
sender_name: 'XX活动',
sender_header_media_id: '1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K0',
re_openid: 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
total_amount: 1000,
wishing: '感谢您参加猜灯谜活动,祝您元宵节快乐!',
act_name: '猜灯谜抢红包活动',
remark: '猜越多得越多,快来抢!',
mch_id: '1900000109',
nonce_str: Formatter.nonce(),
})
.then(res => console.info(res.data))
.catch(console.error)
Wechatpay.client.v2.defaults.transformRequest.unshift(function wwsptrans2pocket(data, headers) {
const {amount, appid, desc, mch_id, nonce_str, openid, partner_trade_no, ww_msg_type} = data
if (!(amount && appid && desc && mch_id && nonce_str && openid && partner_trade_no && ww_msg_type)) {
return data
}
data.workwx_sign = Hash.md5(
Formatter.queryStringLike(Formatter.ksort({
amount, appid, desc, mch_id, nonce_str, openid, partner_trade_no, ww_msg_type
})), agentSecret, agentId
).toUpperCase()
return data
})
wxpay.v2.mmpaymkttransfers.promotion.paywwsptrans2pocket({
appid: 'wxe062425f740c8888',
device_info: '013467007045764',
partner_trade_no: '100000982017072019616',
openid: 'ohO4Gt7wVPxIT1A9GjFaMYMiZY1s',
check_name: 'NO_CHECK',
re_user_name: '张三',
amount: '100',
desc: '六月出差报销费用',
spbill_create_ip: '10.2.3.10',
ww_msg_type: 'NORMAL_MSG',
act_name: '示例项目',
mch_id: '1900000109',
nonce_str: Formatter.nonce(),
})
.then(res => console.info(res.data))
.catch(console.error)
// APIv2 日志
Wechatpay.client.v2.defaults.transformRequest.push(data => (console.log(data), data))
Wechatpay.client.v2.defaults.transformResponse.unshift(data => (console.log(data), data))
// APIv3 日志
Wechatpay.client.v3.defaults.transformRequest.push((data, headers) => (console.log(data, headers), data))
Wechatpay.client.v3.defaults.transformResponse.unshift((data, headers) => (console.log(data, headers), data))
非标准接口地址,也可以这样调用
Wechatpay.client.v2.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(
params.appId, params.timeStamp, params.nonceStr, params.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)
Q: 敏感信息或者幂等操作要求额外头信息上送时,应该如何构建请求参数?
DELETE
/GET
请求的第一个参数,POST
/PUT
/PATCH
请求的第二个参数,是 AxiosRequestConfig 对象,可以按需上送额外头参数,例如:wxpay.v3.applyment4sub.applyment.$noop$( {}, { noop: '', headers: { 'Wechatpay-Serial': '123456' } }, ).then(console.info).catch(console.error);可参考 #17
Q: 接口地址为slash(/
)结尾的,应该如何构建请求参数?
动态参数
uri_template
或者属性property
方式构建,可参考 #16
npm install && npm test
[Function (anonymous)] {
v2: [Function: /v2] {
risk: [Function: /v2/risk] {
getpublickey: [Function: /v2/risk/getpublickey]
},
pay: [Function: /v2/pay] { micropay: [Function: /v2/pay/micropay] },
secapi: [Function: /v2/secapi] {
pay: [Function: /v2/secapi/pay] {
refund: [Function: /v2/secapi/pay/refund]
}
},
mmpaymkttransfers: [Function: /v2/mmpaymkttransfers] {
sendredpack: [Function: /v2/mmpaymkttransfers/sendredpack],
promotion: [Function: /v2/mmpaymkttransfers/promotion] {
transfers: [Function: /v2/mmpaymkttransfers/promotion/transfers],
paywwsptrans2pocket: [Function: /v2/mmpaymkttransfers/promotion/paywwsptrans2pocket]
},
sendworkwxredpack: [Function: /v2/mmpaymkttransfers/sendworkwxredpack]
}
},
v3: [Function: /v3] {
pay: [Function: /v3/pay] {
transactions: [Function: /v3/pay/transactions] {
native: [Function: /v3/pay/transactions/native],
id: [Function: /v3/pay/transactions/id] {
'{transaction_id}': [Function: /v3/pay/transactions/id/{transaction_id}]
},
outTradeNo: [Function: /v3/pay/transactions/out-trade-no] {
'1217752501201407033233368018': [Function: /v3/pay/transactions/out-trade-no/1217752501201407033233368018]
}
},
partner: [Function: /v3/pay/partner] {
transactions: [Function: /v3/pay/partner/transactions] {
native: [Function: /v3/pay/partner/transactions/native]
}
}
},
marketing: [Function: /v3/marketing] {
busifavor: [Function: /v3/marketing/busifavor] {
stocks: [Function: /v3/marketing/busifavor/stocks],
users: [Function: /v3/marketing/busifavor/users] {
'$openid$': [Function: /v3/marketing/busifavor/users/{openid}] {
coupons: [Function: /v3/marketing/busifavor/users/{openid}/coupons] {
'{coupon_code}': [Function: /v3/marketing/busifavor/users/{openid}/coupons/{coupon_code}] {
appids: [Function: /v3/marketing/busifavor/users/{openid}/coupons/{coupon_code}/appids] {
wx233544546545989: [Function: /v3/marketing/busifavor/users/{openid}/coupons/{coupon_code}/appids/wx233544546545989]
}
}
}
}
}
},
favor: [Function: /v3/marketing/favor] {
media: [Function: /v3/marketing/favor/media] {
imageUpload: [Function: /v3/marketing/favor/media/image-upload]
},
stocks: [Function: /v3/marketing/favor/stocks] {
'$stock_id$': [Function: /v3/marketing/favor/stocks/{stock_id}] {
useFlow: [Function: /v3/marketing/favor/stocks/{stock_id}/use-flow]
}
}
},
partnerships: [Function: /v3/marketing/partnerships] {
build: [Function: /v3/marketing/partnerships/build]
}
},
combineTransactions: [Function: /v3/combine-transactions] {
jsapi: [Function: /v3/combine-transactions/jsapi]
},
bill: [Function: /v3/bill] {
tradebill: [Function: /v3/bill/tradebill],
fundflowbill: [Function: /v3/bill/fundflowbill]
},
billdownload: [Function: /v3/billdownload] {
file: [Function: /v3/billdownload/file]
},
smartguide: [Function: /v3/smartguide] {
guides: [Function: /v3/smartguide/guides] {
'$guide_id$': [Function: /v3/smartguide/guides/{guide_id}] {
assign: [Function: /v3/smartguide/guides/{guide_id}/assign]
}
}
},
merchantService: [Function: /v3/merchant-service] {
complaints: [Function: /v3/merchant-service/complaints]
},
merchant: [Function: /v3/merchant] {
media: [Function: /v3/merchant/media] {
video_upload: [Function: /v3/merchant/media/video_upload]
}
}
}
}
v0.5.1
wxpay v3/pay/transactions/native
请求了README
文档,适配最新CLI用法v0.5.0
peerDependencies
,使用完整功能需手动安装 form-data
或/及 yargs
v0.4.6
eslint
及eslint-config-airbnb-base
utils.merge
依赖函数测试校验v0.4.5
secret
(for APIv2)如未设置,HMAC-SHA256
数据签名时,可能引发 #14v0.4.4
Wechatpay
在多次实例化时赋值Symbol(CLIENT)
异常问题,增加wechatpay.test.js
测试用例覆盖v0.4.3
v0.4.2
v0.4.1
AES-GCM
在Node10
上的解密兼容性问题,程序在Node10
上有可能崩溃,建议Node10
用户升级至此版本v0.4.0
Wechatpay
类,同时支持 APIv2&v3's 链式调用Wechatpay.client
返回值为Wechatpay.client.v3
,Wechatpay.client.v2
为 xmlBased
接口客户端withEntities
方法,其在链式多次调用时,有可能达不到预期,详情见 #10,感谢 @ali-pay 报告此问题tsd
声明v0.3.4
Wechatpay
class(#9), thanks @ipoav0.3.3
v0.3.2
Aes.pkcs7.padding
strictly following the rfc2315
specHash.md5
and Hash.hmacSha256
v0.3.1
xmlBased({mchid})
, while passed in, then Transformer.signer
doing the assert
with the post data.User-Agent
.aes.js
as of Aes
, AesGcm
and AesEcb
classes for aes-256-ecb/pkcs7padding
algo.v0.3.0
v0.2.3
v0.2.2
verfier
on the 204
status case.v0.2.1
12.4.0
< Node
≧ 10.15.0
.v0.2.0
OOP
developing style of the wechatpay APIv3.v0.1.0
Nodejs
version ≧ 10.15.0
.v0.0.9
Typescript
v0.0.8
castCsvBill
, drop the trim
on each rowsresponse
validation, checking ± 5 mins first then to Rsa.verify
commander
optional dependency, because it's only for the CLI
toolnpm test
)v0.0.7
npm run lint
)v0.0.6
v0.0.5
v0.0.4
commander
v0.0.3
form-data
v0.0.2
v0.0.1
The MIT License (MIT)
Copyright (c) 2020-2021 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.
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。