Code Monkey home page Code Monkey logo

node-tenpay's Introduction

微信支付 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

node-tenpay's People

Contributors

befinal avatar fourcels avatar fralonra avatar xingjian8854 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-tenpay's Issues

交易过程中出错之后,直接throw了Error

if (json.return_code !== 'SUCCESS') throw new Error(json.return_msg || 'XMLDataError');
if (type !== 'middleware_refund') {
if (json.result_code !== 'SUCCESS') throw new Error(json.err_code || 'XMLDataError');
}

代码里,交易过程中出错了之后,直接throw了Error,这样json里返回的微信返回却看不到了,是不是直接callback(null, json)返回就好呢,让大家自己来处理这些错误,因为有时候是想要记录这些错误并处理的

调用refund接口报错

{ResponseError: socket hang up (req "error"), POST https://api.mch.weixin.qq.com/secapi/pay/refund -1 (connected: true, keepalive socket: false)
headers: {}
    at createHangUpError (_http_client.js:331:15)
    at TLSSocket.socketOnEnd (_http_client.js:423:23)
    at emitNone (events.js:111:20)
    at TLSSocket.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:138:11)
    at process._tickCallback (internal/process/next_tick.js:180:9)
From previous event:
    at Payment.request (C:\project\nextown\nodejs-nextown-api\node_modules\tenpay\lib\index.js:282:10)
    at Payment.refund (C:\project\nextown\nodejs-nextown-api\node_modules\tenpay\lib\index.js:136:15)
    at Finance.refundDispatchOrder (C:\project\nextown\nodejs-nextown-api\src\models\finance.js:40:41)
    at <anonymous>
  code: 'ECONNRESET',
  name: 'ResponseError',
  data: undefined,
  path: '/secapi/pay/refund',
  status: 500,
  headers: {},
  res:
   { status: -1,
     statusCode: 500,
     headers: {},
     size: 0,
     aborted: false,
     rt: 216,
     keepAliveSocket: false,
     data: undefined,
     requestUrls: [ 'https://api.mch.weixin.qq.com/secapi/pay/refund' ],
     timing: null,
     remoteAddress: '182.254.78.168',
     remotePort: 443 } }

在调用退款接口时出现如下错误,请怎么回事啊

tenpay版本:1.1.4
node版本:8.11.1

README.md 里的扫码支付 是不是标错了 ?

micropay: 扫码支付 ? 看 lib/index.js 下的注释。。不像

  // 扫码支付, 生成URL(模式一)
  getNativeUrl(params) {
    let pkg = {
      ...params,
      appid: this.appid,
      mch_id: this.mchid,
      time_stamp: '' + (Date.now() / 1000 |0),
      nonce_str: util.generate()
    };

    let url = 'weixin://wxpay/bizpayurl'
            + '?sign=' + this._getSign(pkg)
            + '&appid=' + pkg.appid
            + '&mch_id=' + pkg.mch_id
            + '&product_id=' + pkg.product_id
            + '&time_stamp=' + pkg.time_stamp
            + '&nonce_str=' + pkg.nonce_str;
    return url;
}

  // 刷卡支付
  micropay(params) {
    let pkg = {
      ...params,
      appid: this.appid,
      mch_id: this.mchid,
      nonce_str: util.generate(),
      sign_type: params.sign_type || 'MD5',
      spbill_create_ip: params.spbill_create_ip || this.spbill_create_ip
    };

    return this._request(pkg, 'micropay');
}

scene_id 的 200 金额限制单位是 分 而不是 元

https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_4&index=3

官方文档并没有写清楚,但是测试时发送大于2元时就已经需要 scene_id 了,毕竟最大红包金额也只是 200 元。或者代码不需要做判断处理,调用时有传入参数则传到接口。

node-tenpay/lib/index.js

Lines 339 to 354 in 8542215

sendRedpack(params) {
let pkg = {
...params,
wxappid: this.appid,
mch_id: this.mchid,
nonce_str: util.generate(),
client_ip: params.client_ip || this.spbill_create_ip,
mch_billno: params.mch_billno || (params.mch_autono ? this.mchid + util.getFullDate() + params.mch_autono : ''),
total_num: params.total_num || 1
};
delete pkg.mch_autono;
let needs = ['mch_billno', 'send_name', 're_openid', 'total_amount', 'wishing', 'act_name', 'remark'];
if (pkg.total_amount >= 20000) needs.push('scene_id');
return this._request(pkg, {type: 'sendredpack', needs, cert: true});
}

另外错误判断只针对特定错误,导致其他出错情况下不会 throw error,比如后台没有配置

{
 "return_msg": "参数错误:该场景未开通,请登陆商户平台:产品中心-现金红包-产品设置-使用场景 进行开通",
  "result_code": "FAIL",
  "err_code": "PARAM_ERROR",
  "err_code_des": "参数错误:该场景未开通,请登陆商户平台:产品中心-现金红包-产品设置-使用场景 进行开通"
}

付款通知接口

lib\index.js
switch (type) { case 'middleware_refund': if (json.appid !== this.appid) throw new Error('appid不匹配'); if (json.mch_id !== this.mchid) throw new Error('mch_id不匹配'); let key = util.md5(this.partnerKey).toLowerCase(); let info = util.decrypt(json.req_info, key); json.req_info = await util.parseXML(info); break; case 'transfers': if (json.mchid !== this.mchid) throw new Error('mchid不匹配'); break; case 'sendredpack': case 'sendgroupredpack': if (json.wxappid !== this.appid) throw new Error('wxappid不匹配'); if (json.mch_id !== this.mchid) throw new Error('mchid不匹配'); break; case 'gethbinfo': case 'gettransferinfo': if (json.mch_id !== this.mchid) throw new Error('mchid不匹配'); break; default: if (json.appid !== this.appid) throw new Error('appid不匹配'); if (json.mch_id !== this.mchid) throw new Error('mch_id不匹配'); if (json.sign !== this._getSign(json)) throw new Error('sign签名错误'); } return json;

微信官方都没有req_info 这个字段,带req_info 的这行代码报错,怎么回事?

运行报错You may need an appropriate loader to handle this file type

npm run dev报以下错误:
Module parse failed: Unexpected token (196:6)
You may need an appropriate loader to handle this file type.
| micropay(params) {
| let pkg = {
| ...params,
| appid: this.appid,
| mch_id: this.mchid,

@ ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/page/reg_order_confirm.vue 33:19-36
@ ./src/page/reg_order_confirm.vue
@ ./src/router/index.js
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://0.0.0.0:

调用api.refund 接口

api.refund({
out_trade_no: order._id,
out_refund_no: refund._id,
total_fee: 1,
refund_fee: 1
})
//值都是正确的
报错
server error { AssertionError [ERR_ASSERTION]: invalid status code: -1
at Object.set status [as status] (D:\document\takeout-api\node_modules\koa\lib\response.js:86:5)
at Object.status (D:\document\takeout-api\node_modules\delegates\index.js:92:31)
at D:\document\takeout-api\middlewares\error-handling.js:9:18
at
at process._tickCallback (internal/process/next_tick.js:188:7)
generatedMessage: false,
name: 'AssertionError [ERR_ASSERTION]',
code: 'ERR_ASSERTION',
actual: undefined,
expected: true,
operator: '==' } { request:
{ method: 'POST',
url: '/order/cancel',
header:
{ host: 'localhost:4000',
connection: 'keep-alive',
'content-length': '33',
pragma: 'no-cache',
'cache-control': 'no-cache',
'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1 wechatdevtools/1.02.1806120 MicroMessenger/6.5.7 Language/zh_CN webview/',
origin: 'http://127.0.0.1:26462',
authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjoiU3N2ZnhpbmRHZWtITXM4cVVQZit2QT09IiwiaWF0IjoxNTI5MDI3NTk0LCJleHAiOjE1MjkyODY3OTR9.Zx7r9-2RQXC02IWsWYiFSh6V4uV243U5xfJvKcQa-zM',
'content-type': 'application/json; charset=UTF-8',
accept: '/',
referer: 'https://servicewechat.com/wx67518436f322a9c4/devtools/page-frame.html',
'accept-encoding': 'gzip, deflate, br' } },
response:
{ status: 404,
message: 'Not Found',
header:
{ 'x-dns-prefetch-control': 'off',
'x-frame-options': 'SAMEORIGIN',
'strict-transport-security': 'max-age=15552000; includeSubDomains',
'x-download-options': 'noopen',
'x-content-type-options': 'nosniff',
'x-xss-protection': '1; mode=block',
vary: 'Origin',
'access-control-allow-origin': 'http://127.0.0.1:26462' } },
app: { subdomainOffset: 2, proxy: false, env: 'development' },
originalUrl: '/order/cancel',
req: '',
res: '',
socket: '' }

promise then 中rs 是不是不需要在判断 return_code,result_code的状态了。

// 申请退款 await api.refund(refund_order).then(rs => { // console.log('>>>>>>退款结果 result is :', JSON.stringify(rs)) if (rs.return_code == 'SUCCESS' && rs.result_code == 'SUCCESS') { // 获取之前充值押金的的价格 const refund_data = { user_id: user_id, wallet_id: wallet_id, out_trade_no: out_trade_no, out_refund_no: out_refund_no, reason: reason, refund_price: order.finally_price } console.log('>>>>>>退款结果1 result is :', JSON.stringify(rs)) // 退款表 return WalletDepositRefundModel.create(refund_data) } }).catch(err => { // 日志 console.log('>>>>>>退款结果3 result is :', JSON.stringify(err)) })

谢谢楼主分享,现在有个小疑问是不是then里面不需要判断 return_code,result_code的状态,都是SUCCESS,所有的错误都在 catch 里面捕捉了。我看了util.js 里面的validate方法,好像需要return_code,result_code 都是SUCCESS才是正确的。我测试了一下也是这样,再来确认下。

请教下getPayParams 还能用吗?

大神,上周getPayParams 方法还可以返回支付参数,今天直接返回 INVALID_REQUEST 了,公众号配置也没动过,微信改啥了吗?

在调用 transfers API的时候报错

你好,
感谢你这个lib,非常棒 🙏
想要实现企业付款的功能,但是在用 api.transfers的时候遇到一个错误,不知道是什么问题:

config是这样初始化的:

const config = {
  appid: process.env.WX_APP_ID,
  mchid: process.env.WX_MCHID,
  partnerKey: process.env.WX_PAY_SECRET, // 微信商户平台 API secret,非小程序 secret
  notify_url: process.env.WX_NOTIFY_URL,
  pfx: require('fs').readFileSync('./apiclient_cert.pem')
}

const wxpay = WXPay.init(config)

然后在我的routes里面这样使用:

  app.route('/test-transfer')
    .post(async ({ body: { openid, fullname } }, res, next) => {
      const partner_trade_no = _generateOutTradeNo()
      try {
        let result = await wxpay.transfers({
          partner_trade_no,
          openid,
          re_user_name: fullname,
          amount: 1,
          desc: '扫街费'
        })
        console.log(result)
        res.json({ result })
      } catch (error) {
        console.log(error)
        next(error)
      }
    })

返回的错误是这样的:

Error: wrong tag, POST https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers -1 (connected: false, keepalive socket: false) headers: {}

App支付回调实例

请问有 App 支付回调 处理的例子吗 只看到了 中间件相关的 如果 没有中间件 如何处理呢

退款通知,现在支持吗

谢谢分享,请问现在支持退款通知吗,和支付通知的配置一样吗。
提供部分退款通知消息解密的方法。
`// xml 解析
var parseXML = function (xml, callback) {
xml2js.parseString(xml, {
trim: true,
explicitArray: false
}, function (err, result) {
err ? callback(err) : callback(null, result ? result.root : {});
});
};

// 解密
var decrypt = function (text, trade_type) {
var result = '';
if (trade_type == 'APP') {
let key1 = CryptoJS.MD5(partner_key);
result = CryptoJS.AES.decrypt(text, CryptoJS.enc.Utf8.parse(key1), {
mode: CryptoJS.mode.ECB
});
}
return result.toString(CryptoJS.enc.Utf8);
};`

扫码支付模式二的支持?

  • 模式二
  • 扫码支付没看到有输入价格的地方,看.net的扫码支付,发现模式二可以提供输入价格
  • 对比.net 的实现来说,扫码支付二 的实现跟 JS支付 和 APP支付 都是 ( 商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url),是否可以提供实现?
   /**
        * 生成直接支付url,支付url有效期为2小时,模式二
        * @param productId 商品ID
        * @return 模式二URL
        */
        public string GetPayUrl(string productId)
        {
            Log.Info(this.GetType().ToString(), "Native pay mode 2 url is producing...");

            WxPayData data = new WxPayData();
            data.SetValue("body", "test");//商品描述
            data.SetValue("attach", "test");//附加数据
            data.SetValue("out_trade_no", WxPayApi.GenerateOutTradeNo());//随机字符串
            data.SetValue("total_fee", 1);//总金额
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));//交易起始时间
            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));//交易结束时间
            data.SetValue("goods_tag", "jjj");//商品标记
            data.SetValue("trade_type", "NATIVE");//交易类型
            data.SetValue("product_id", productId);//商品ID

            WxPayData result = WxPayApi.UnifiedOrder(data);//调用统一下单接口
            string url = result.GetValue("code_url").ToString();//获得统一下单接口返回的二维码链接

            Log.Info(this.GetType().ToString(), "Get native pay mode 2 url : " + url);
            return url;
        }

app支付函数编码问题

lib/index.js的182行,getAppParamsByPrepay方法中,微信支付文档的随机字符串字段为:noncestr,而方法中为nonceStr,会导致微信后台在检查签名的时候报错~

服务商模式获取微信JSSDK支付参数错误

let result = await api.getPayParams({
      out_trade_no: Date.now(),
      body: '商品简单描述',
      total_fee: '1',
      sub_openid: subOpenid,
      sub_appid: subAppid,
      sub_mch_id: subMchid
});

小程序中调用支付结果错误

{err_desc: "Unable to verify signature", err_code: "-1", errMsg: "requestPayment:fail"}

npm deprecated warn

npm WARN deprecated [email protected]: If using 2.x branch, please upgrade to at least 2.1.6 to avoid a serious bug with socket data flow and an import issue introduced in 2.1.0

请问支付成功后回调的网址问题, 谢谢

微信支付结果通知 • 中间件

// Express
var middleware = api.middlewareForExpress();
app.use('/xxx', middleware, function (req, res) {
	var payInfo = req.weixin;
})

请问 /xxx 与 notify_url - 支付结果通知回调地址是一致的吗?
谢谢

支付通知中间件

微信的文档说明:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7&index=8
微信支付通知xml数据中有个result_code字段为FAIL时,表示用户支付未成功。这个未成功的消息,业务服务器应该有权知道。

但是tenpay的支付通知中间件,在这种情况下会返回给微信支付服务器return res.reply('XMLDataError'),暗示业务服务器未收到消息,而不是收到消息。

总感觉这在逻辑上是错的。

server error Error: ORDERPAID

server error Error: ORDERPAID
at Payment._parse (E:\nodeproject\everprint\node_modules_tenpay@2.1.6@tenpay\lib\index.js:74:51)
at
at process._tickCallback (internal/process/next_tick.js:188:7) { request: ~~~~~
这个是已经支付过了的意思吗?是腾讯返回的吗?

Todo List

  • 添加沙盒模式
  • 添加微信代金券相关API

_getPayParams 中为什么返回了timeStamp与timestamp 俩个值一样的参数,timestamp有何用?

// JSSDK支付签名(原始方法)
Payment.prototype._getPayParams = function(params, callback) {
var that = this;
that.unifiedOrder(params, function(err, result) {
if (err) return callback(err);
var pkg = Object.assign({}, {
appId: that.appid,
timeStamp: '' + (Date.now() / 1000 |0),
nonceStr: util.generateNonceStr(),
package: 'prepay_id=' + result.prepay_id,
signType: 'MD5'
});
pkg.paySign = that.getSign(pkg);
pkg.timestamp = pkg.timeStamp;
callback(null, pkg);
})
};

请问这是什么错误?

{"code":-1,"reason":"/data/release/node-weapp-demo/node_modules/tenpay/lib/index.js:52\n ...config,\n ^^^\n\nSyntaxError: Unexpected token ...\n at createScript (vm.js:74:10)\n at Object.runInThisContext (vm.js:"}

body使用中文时签名错误

在计算字符串md5时需要指定编码为utf8
Payment.prototype.getSign = function(pkg) {
pkg = Object.assign({}, pkg);
delete pkg.sign;
var str = ulits.toQueryString(pkg) + '&key=' + this.partnerKey;
return crypto.createHash('md5').update(str, 'utf8').digest('hex').toUpperCase();
};

哥们,关于微信支付getAppParamsByPrepay function的错误

getAppParamsByPrepay(params) {
let pkg = {
appid: this.appid,
partnerid: this.mchid,
prepayid: params.prepay_id,
package: 'Sign=WXPay',
nonceStr: util.generate(),
timestamp: '' + (Date.now() / 1000 |0)
};
pkg.sign = this._getSign(pkg);
return pkg;
}

这里的nonceStr应该是noncestr。希望改正

XML格式错误

router.post('/', function (req, res, next) {
  let openid = req.body.openid
  let products = req.body.products
  let totalAmount = req.body.totalAmount
  let address = req.body.address
  Customer.findOne({openid:openid}, function(err, customer){
    if(customer){
      let order = new Order({
        user: customer._id,
        phone: customer.phone,
        items: products,
        totalAmount: totalAmount,
        shippingAddress: address,
        status: 'created',
        createdAt: new Date().getTime(),
        updatedAt: new Date().getTime()
      })
      order.save((err, order)=>{
        if(order){
          let result = unifiedOrder(order._id, '服装', totalAmount,customer.openId)
          console.log(result)
          return res.send(result)
       }
     })
    }
  })
})


async function unifiedOrder(orderId, name, totalAmount, openid){
  try {
    let result = await wxpay.unifiedOrder({
      out_trade_no: orderId,
      body: name,
      total_fee: totalAmount,
      openid: openid
    });
    console.log(result)
  } catch (error) {
    console.error(error);
  }
}

Error Console
Promise { <pending> } Error: XML格式错误 at Payment._parse (/Users/haochen/wechat/kunkka/node_modules/tenpay/lib/index.js:43:47) at <anonymous> at process._tickCallback (internal/process/next_tick.js:160:7)

不太清楚那里出错了 才会导致返回xml格式错误?在local做测试时返回的

关于h5支付必需字段scene_info

这边生成的是这样的
image
而官网给出的是这样
<scene_info>{"h5_info": {"type":"IOS","app_name": "王者荣耀","package_name": "com.tencent.tmgp.sgame"}}</scene_info>
请问有什么解决的好办法吗?
谢谢!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.