yuexueyu / wechatpay-axios-plugin

Wechatpay Axios Plugin 微信支付 APIv2 APIv3 nodejs SDK

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

微信支付 Axios 插件版

GitHub version GitHub issues nodejs version types NPM module version NPM module downloads per month NPM module license

主要功能

  • 使用Node原生代码实现微信支付APIv3的AES加/解密功能(aes-256-gcm with aad)
  • 使用Node原生代码实现微信支付APIv3的RSA加/解密、签名、验签功能(sha256WithRSAEncryption with RSA_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在 publicEncryptprivateDecrypt 增加了 oaepHash 入参选项,本类库在 Rsa.encryptRsa.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解密)处理完后,立即用于验签。

得到证书之后,开发者需要把所得serialwechatpay_HEXADECIALHEXADECIALHEXADECIAL.pem(文件流或者文本内容)组成 {key:value} 对,key为证书序列号,value为证书内容,传入以下的构造函数的certs字段里。 其他接口使用就基本上没有啥问题了。

以下文档及示例都是基本用法,没啥花活儿,祝开心。 😄

面向对象模式

v0.2.0 开始支持面向对象方式编程,书写请求/响应过程,有如下约定:

  1. 请求 URI 作为级联对象,可以轻松构建请求对象,例如 /v3/pay/transactions/native 即自然翻译成 v3.pay.transactions.native;
  2. 每个 URI 所支持的 HTTP METHOD,即作为 请求对象的末尾执行方法,例如: v3.pay.transactions.native.post({});
  3. 每个 URI 有中线(dash)分隔符的,可以使用驼峰camelCase风格书写,例如: merchant-service可写成 merchantService,或者属性风格,例如 v3['merchant-service'];
  4. 每个 URI.pathname 中,若有动态参数,例如 business_code/{business_code} 可写成 business_code.$business_code$ 或者属性风格书写,例如 business_code['{business_code}'],抑或直接按属性风格,直接写参数值也可以,例如 business_code['2000001234567890'];
  5. 建议 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-----',
  }
})

Native下单API

wxpay.v3.pay.transactions.native
  .post({/*文档参数放这里就好*/})
  .then(({data: {code_url}}) => console.info(code_url))
  .catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))

查询订单API

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))

关单API

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))

创建商家券API

wxpay.v3.marketing.busifavor.stocks
  .post({/*商家券创建条件*/})
  .then(({data}) => console.info(data))
  .catch(({response: {status, statusText, data}}) => console.error(status, statusText, data))

查询用户单张券详情API

;(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)
  }
}

支持 XML based 接口调用

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))

获取RSA公钥

非标准接口地址,同样支持

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)

aes-256-ecb/pcks7padding

v0.3.1开始支持解密

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)

v0.3.2开始支持加密

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`)

APIv2数据签名

JSAPI

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)

APP

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)

APIv3数据签名

JSAPI

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)

商家券-小程序发券v2版签名规则

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)

商家券-H5发券v2版签名规则

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)

Wechatpay Axios Plugin

Features

  • The Node's native code of the wechatpay APIv3's AES encrypt/decrypt cryptography(aes-256-gcm with aad)
  • The Node's native code of the wechatpay APIv3's RSA encrypt/decrypt/sign/verify cryptography(sha256WithRSAEncryption with RSA_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

Installing

$ npm install wechatpay-axios-plugin

Requirements

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.

First of all

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.

OOP style development

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:

  1. Each of the URI, eg: /v3/pay/transactions/native is mapping to v3.pay.transactions.native;
  2. Each of the URI's HTTP METHOD,are mapping to the container's executor, eg: v3.pay.transactions.native.post({});
  3. While the URI contains the dash-split-words, the entity can be wrote as camelCase style, eg: merchant-service to merchantService or by the Object.property's style as v3['merchant-service'];
  4. While the URI contains the dynamic_veriable_parameter, eg: business_code/{business_code}, it can be business_code.$business_code$ or business_code['{business_code}'] or directly replaced with actual value eg: business_code['2000001234567890'];
  5. Recommend writing the URI entities with PascalCase style. Because the TS Definition is on the road, those shall be this style;

With this style, the simples were shown above.

Examples

Initialization

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.

Promise style

combined orders payment

POST /v3/combine-transactions/jsapi with JSON body payload

client.post('/v3/combine-transactions/jsapi', {}).then(response => {
  console.info(response)
}, error => {
  console.error(error)
})

payment as customer service

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)
})

image file upload

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)
})

download trade bill and cast

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)
})

Async/Await style

retrieve the customer complaints

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)
  }
})()

order payment by scanning qrcode

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)
  }
})()

detail the coupon stock

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)
  }
})()

the coupon stock associated

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)
  }
})()

video file upload

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)
  }
})()

download trade bill with gzip and cast

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.

Typescript basic example

Initialization

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)

Sensitive Information Decryption Usage

;(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)
  }
})()

Unit Test

npm test

Changelog

  • v0.3.2

    • Optim: Strict Aes.pkcs7.padding following of the rfc2315 spec
    • Optim: Better of Hash.md5 and Hash.hmacSha256
    • Coding comments and README
  • v0.3.1

    • Optim: new param on xmlBased({mchid}), while passed in, then Transformer.signer doing the assert with the post data.
    • Feature: Customize the HTTP User-Agent.
    • Refactor: Split aes.js as of Aes, AesGcm and AesEcb classes for aes-256-ecb/pkcs7padding algo.
  • v0.3.0

    • Feature: The XML based API requests.
  • v0.2.3

    • Optim: Coding quality.
  • v0.2.2

    • Fix: #8 verfier on the 204 status case.
  • v0.2.1

    • Optim: Back compatible for 12.4.0 < Node10.15.0.
  • v0.2.0

    • Feature: OOP developing style of the wechatpay APIv3.
  • v0.1.0

    • Optim: Toggle the Nodejs version ≧ 10.15.0.
    • Optim: Documentation and coding comments.
  • v0.0.9

    • Feature: definition of the Typescript
  • v0.0.8

    • Optim: on castCsvBill, drop the trim on each rows
    • Optim: on response validation, checking ± 5 mins first then to Rsa.verify
    • Optim: moved the commander optional dependency, because it's only for the CLI tool
    • Feature: shipped 99 tests(npm test)
  • 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
  • v0.0.3

    • Feature: media file upload, optional deps on form-data
  • 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

License

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.

About

Wechatpay Axios Plugin 微信支付 APIv2 APIv3 nodejs SDK

License:MIT License


Languages

Language:JavaScript 100.0%