Sign Up for Free

RunKit +

Try any Node.js package right in your browser

This is a playground to test code. It runs a full Node.js environment and already has all of npm’s 400,000 packages pre-installed, including tenpay with all npm packages installed. Try it out:

var tenpay = require("tenpay")

This service is provided by RunKit and is not affiliated with npm, Inc or the package authors.

tenpay v2.1.18

微信支付 for nodejs

微信支付 for nodejs

travis npm node issues commit

功能概述

  • 通知类中间件 - 支付结果通知, 退款结果通知
  • 前端支付支持 - 支持JSAPI, WeixinJSBridge, 小程序, APP, H5
  • 支付模式支持 - 付款码/公众号/小程序/APP/H5/扫码支付
  • 支付工具支持 - 微信红包, 企业付款(支持付款到零钱和银行卡)
  • 营销功能支持 - 微信代金券
  • 对帐账单支持 - 支持微信对帐单, 微信资金帐单
  • 服务商模式支持 - 所有api均可自行传入sub_appid, sub_mch_id
  • 微信支付仿真测试系统 - 支持沙盒模式, 用于完成支付验收流程

交流群

QQ群:157964097,使用疑问,开发,贡献代码请加群。

使用前必读

版本要求

nodejs >= 8.3.0

关于传入值和微信返回值的数据类型

因涉及金额等敏感问题, API和中间件并没有对数据字段做类型转换

微信返回值XML做JSON转换之后的字段均为字符串类型, 请自行转换后再进行数据运算

重点关注问题

  • 字符串与数字运算结果问题 '1' + 0 = '10'
  • 金额单位问题 微信支付中传入的金额单位为分

关于错误

API和中间件均对所有错误进行了处理, 统一通过error返回, 包括:

  • 网络类错误 - 网络中断, 连接超时等
  • 微信返回值检验错误 - 微信返回值非法(伪造请求等, 可能性非常低)
  • 业务逻辑错误 - 订单重复, 退款金额大于支付金额等
  • 其它错误 - 应传参数未传入等

关于返回值

未出错时正常返回为JSON格式数据

  • 特殊情况: downloadBilldownloadFundflow下载的帐单返回值为字符串文本

安装

npm i tenpay

# 如已安装旧版, 重新安装最新版
npm i tenpay@latest

实例化

const tenpay = require('tenpay');
const config = {
  appid: '公众号ID',
  mchid: '微信商户号',
  partnerKey: '微信支付安全密钥',
  pfx: require('fs').readFileSync('证书文件路径'),
  notify_url: '支付回调网址',
  spbill_create_ip: 'IP地址'
};
// 方式一
const api = new tenpay(config);
// 方式二
const api = tenpay.init(config);

// 调试模式(传入第二个参数为true, 可在控制台输出数据)
const api = new tenpay(config, true);

// 沙盒模式(用于微信支付验收)
const sandboxAPI = await tenpay.sandbox(config);

config说明:

  • appid - 公众号ID(必填)
  • mchid - 微信商户号(必填)
  • partnerKey - 微信支付安全密钥(必填, 在微信商户管理界面获取)
  • pfx - 证书文件(选填, 在微信商户管理界面获取)
    • 当不需要调用依赖证书的API时可不填此参数
    • 若业务流程中使用了依赖证书的API则需要在初始化时传入此参数
  • notify_url - 支付结果通知回调地址(选填)
    • 可以在初始化的时候传入设为默认值, 不传则需在调用相关API时传入
    • 调用相关API时传入新值则使用新值
  • refund_url - 退款结果通知回调地址(选填)
    • 可以在初始化的时候传入设为默认值, 不传则使用微信商户后台配置
    • 调用相关API时传入新值则使用新值
  • spbill_create_ip - IP地址(选填)
    • 可以在初始化的时候传入设为默认值, 不传则默认值为127.0.0.1
    • 调用相关API时传入新值则使用新值

关于可选参数的最佳实践:

  • 如业务流程中用到含证书请求的API, 则必须在初始时传入pfx参数
  • 如回调地址不需要按业务变化, 建议在初始化时传入统一的回调地址
  • 如IP地址不需要按业务变化, 建议在初始化时传入统一的IP地址

中间件・微信消息通知

  • middleware参数: pay<支付结果通知, 默认> refund<退款结果通知> nativePay<扫码支付模式一回调>
  • 需自行添加bodyParser接收post data
  • 中间件会对通知消息进行合法性验证, 并将消息解析为json格式放入req.weixin(Express)或ctx.request.weixin(Koa)
  • reply()会自动封装SUCCESS消息, reply('some error_msg')会自动封装FAIL消息

Express中使用

app.use(bodyParser.text({type: '*/xml'}));

// 支付结果通知/退款结果通知
router.post('/xxx', api.middlewareForExpress('pay'), (req, res) => {
  let info = req.weixin;

  // 业务逻辑...

  // 回复消息(参数为空回复成功, 传值则为错误消息)
  res.reply('错误消息' || '');
});

// 扫码支付模式一回调
router.post('/xxx', api.middlewareForExpress('nativePay'), (req, res) => {
  let info = req.weixin;

  // 业务逻辑和统一下单获取prepay_id...

  // 响应成功或失败(第二个可选参数为输出错误信息)
  res.replyNative(prepay_id, err_msg);
});

Koa中使用

app.use(bodyParser({
  enableTypes: ['json', 'form', 'text'],
  extendTypes: {
    text: ['text/xml', 'application/xml']
  }
}));

router.post('/xxx', api.middleware('refund'), async ctx => {
  let info = ctx.request.weixin;

  // 业务逻辑...

  // 回复消息(参数为空回复成功, 传值则为错误消息)
  ctx.reply('错误消息' || '');

  // 扫码支付模式一模式
  ctx.replyNative(prepay_id);
});

API 列表

  • 某些API预设了某些必传字段的默认值, 调用时不传参数则使用默认值
  • 初始化时已传入的参数无需调用时重复传入, 如appid mchid
  • 签名(sign)会在调用API时自动处理, 无需手动传入
  • 随机字符串(nonce_str)会在调用API时自动处理, 无需手动传入

getPayParams: 获取微信JSSDK支付参数(自动下单, 兼容小程序)

let result = await api.getPayParams({
  out_trade_no: '商户内部订单号',
  body: '商品简单描述',
  total_fee: '订单金额(分)',
  openid: '付款用户的openid'
});
相关默认值:
  • trade_type - JSAPI

getPayParamsByPrepay: 获取微信JSSDK支付参数(通过预支付会话标识, 兼容小程序)

// 该方法需先调用api.unifiedOrder统一下单, 获取prepay_id;
let result = await api.getPayParamsByPrepay({
  prepay_id: '预支付会话标识'
});

getAppParams: 获取APP支付参数(自动下单)

let result = await api.getAppParams({
  out_trade_no: '商户内部订单号',
  body: '商品简单描述',
  total_fee: '订单金额(分)'
});
相关默认值:
  • trade_type - APP

getAppParamsByPrepay: 获取APP支付参数(通过预支付会话标识)

// 该方法需先调用api.unifiedOrder统一下单<注意传入trade_type: 'APP'>, 获取prepay_id;
let result = await api.getAppParamsByPrepay({
  prepay_id: '预支付会话标识'
});

getNativeUrl: 扫码支付(模式一)

let result = await api.getNativeUrl({
  product_id: '商品ID'
});

扫码支付(模式二)

// 使用统一下单API可直接获取code_url, 需自行生成二维码图片
let {prepay_id, code_url} = await api.unifiedOrder({
  out_trade_no: '商户内部订单号',
  body: '商品简单描述',
  total_fee: '订单金额(分)',
  openid: '用户openid',
  trade_type: 'NATIVE',
  product_id: '商品id'
});

micropay: 刷卡支付

let result = await api.micropay({
  out_trade_no: '商户内部订单号',
  body: '商品简单描述',
  total_fee: '订单金额(分)',
  auth_code: '授权码'
});

unifiedOrder: 微信统一下单

let result = await api.unifiedOrder({
  out_trade_no: '商户内部订单号',
  body: '商品简单描述',
  total_fee: '订单金额(分)',
  openid: '用户openid'
});
相关默认值:
  • trade_type - JSAPI
  • notify_url - 默认为初始化时传入的值或空
  • spbill_create_ip - 默认为初始化时传入的值或127.0.0.1

orderQuery: 查询订单

let result = await api.orderQuery({
  // transaction_id, out_trade_no 二选一
  // transaction_id: '微信的订单号',
  out_trade_no: '商户内部订单号'
});

reverse: 撤消订单

let result = await api.reverse({
  // transaction_id, out_trade_no 二选一
  // transaction_id: '微信的订单号',
  out_trade_no: '商户内部订单号'
});

closeOrder: 关闭订单

let result = await api.closeOrder({
  out_trade_no: '商户内部订单号'
});

refund: 申请退款

let result = await api.refund({
  // transaction_id, out_trade_no 二选一
  // transaction_id: '微信的订单号',
  out_trade_no: '商户内部订单号',
  out_refund_no: '商户内部退款单号',
  total_fee: '订单金额(分)',
  refund_fee: '退款金额(分)'
});
相关默认值:
  • op_user_id - 默认为商户号(此字段在小程序支付文档中出现)
  • notify_url - 默认为初始化时传入的refund_url, 无此参数则使用商户后台配置的退款通知地址

refundQuery: 查询退款

let result = await api.refundQuery({
  // 以下参数 四选一
  // transaction_id: '微信的订单号',
  // out_trade_no: '商户内部订单号',
  // out_refund_no: '商户内部退款单号',
  refund_id: '微信退款单号'
});

downloadBill: 下载对帐单

/**
 * 新增一个format参数(默认: false), 用于自动转化帐单为json格式
 * json.total_title: 统计数据的标题数组 - ["总交易单数","总交易额","总退款金额", ...],
 * json.total_data: 统计数据的数组 - ["3", "88.00", "0.00", ...],
 * json.list_title: 详细数据的标题数组 - ["交易时间","公众账号ID","商户号", ...],
 * json.list_data: 详细数据的二维数据 - [["2017-12-26 19:20:39","wx12345", "12345", ...], ...]
 */
let result = await api.downloadBill({
  bill_date: '账单日期'
}, true);
相关默认值:
  • bill_type - ALL
  • format - false

downloadFundflow: 下载资金帐单

/**
 * 新增一个format参数(默认: false), 用于自动转化帐单为json格式
 * json.total_title: 统计数据的标题数组 - ["资金流水总笔数","收入笔数","收入金额", ...],
 * json.total_data: 统计数据的数组 - ["20.0", "17.0", "0.35", ...],
 * json.list_title: 详细数据的标题数组 - ["记账时间","微信支付业务单号","资金流水单号", ...],
 * json.list_data: 详细数据的二维数据 - [["2018-02-01 04:21:23","12345", "12345", ...], ...]
 */
let result = await api.downloadFundflow({
  bill_date: '账单日期'
}, true);
相关默认值:
  • account_type - Basic
  • format - false

sendCoupon: 发放代金券

let result = await api.sendCoupon({
  coupon_stock_id: '代金券批次id',
  partner_trade_no: '商户单据号',
  openid: '用户openid'
});

queryCouponStock: 查询代金券批次

let result = await api.queryCouponStock({
  coupon_stock_id: '代金券批次id'
});

queryCouponInfo: 查询代金券信息

let result = await api.queryCouponInfo({
  coupon_id: '代金券id',
  openid: '用户openid',
  stock_id: '批次号'
});

transfers: 企业付款

let result = await api.transfers({
  partner_trade_no: '商户内部付款订单号',
  openid: '用户openid',
  re_user_name: '用户真实姓名',
  amount: '付款金额(分)',
  desc: '企业付款描述信息'
});
相关默认值:
  • check_name - FORCE_CHECK
  • spbill_create_ip - 默认为初始化时传入的值或127.0.0.1

transfersQuery: 查询企业付款

let result = await api.transfersQuery({
  partner_trade_no: '商户内部付款订单号'
});

payBank: 企业付款到银行卡

let result = await api.payBank({
  partner_trade_no: '商户内部付款订单号',
  bank_code: '收款方开户行',
  enc_bank_no: '收款方银行卡号',
  enc_true_name: '收款方用户名',
  amount: '付款金额(分)',
  desc: '企业付款到银行卡描述信息'
});

queryBank: 查询企业付款到银行卡

let result = await api.queryBank({
  partner_trade_no: '商户内部付款订单号'
});

sendRedpack: 发放普通红包

let result = await api.sendRedpack({
  // mch_billno, mch_autono 二选一
  // mch_billno: '商户内部付款订单号',
  mch_autono: '10位当日唯一数字, 用于自动生成mch_billno',
  send_name: '商户名称',
  re_openid: '用户openid',
  total_amount: '红包金额(分)',
  wishing: '红包祝福语',
  act_name: '活动名称',
  remark: '备注信息'
});
相关默认值和其它说明:
  • mch_billno - 商户内部订单号(传入则mch_autono失效)
  • mch_autono - 当日10位唯一数字, 用于自动处理商户内部订单号逻辑
  • total_num - 1
  • client_ip - 默认为初始化时的spbill_create_ip参数值或127.0.0.1
  • scene_id - 空, 当红包金额大于2元时必传(微信文档说明为200元, 实测为2元)

sendGroupRedpack: 发放裂变红包

let result = await api.sendGroupRedpack({
  // mch_billno, mch_autono 二选一
  // mch_billno: '商户内部付款订单号',
  mch_autono: '10位当日唯一数字, 用于自动生成mch_billno',
  send_name: '商户名称',
  re_openid: '种子用户openid',
  total_amount: '红包金额(分)',
  wishing: '红包祝福语',
  act_name: '活动名称',
  remark: '备注信息'
});
相关默认值和其它说明:
  • mch_billno - 商户内部订单号(传入则mch_autono失效)
  • mch_autono - 当日10位唯一数字, 用于自动处理商户内部订单号逻辑
  • total_num - 3, 分裂红包要求值为3~20之间
  • amt_type - ALL_RAND
  • scene_id - 空, 当红包金额大于2元时必传(文档中未说明)

redpackQuery: 查询红包记录

api.redpackQuery({
  mch_billno: '商户内部付款订单号'
});
相关默认值:
  • bill_type - MCHT
RunKit is a free, in-browser JavaScript dev environment for prototyping Node.js code, with every npm package installed. Sign up to share your code.
Sign Up for Free