Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2付款接口无法使用的问题 #35

Closed
Starrah opened this issue Aug 12, 2021 · 9 comments · Fixed by #36
Closed

v2付款接口无法使用的问题 #35

Starrah opened this issue Aug 12, 2021 · 9 comments · Fixed by #36
Labels
good first issue Good for newcomers

Comments

@Starrah
Copy link
Contributor

Starrah commented Aug 12, 2021

事情是这样的:微信的向用户付款到零钱的接口,目前没有v3、只有v2,文档在这里
令人吐血的是,这个接口的商户号字段名称是"mchid",而非绝大多数接口使用的"mch_id"。
image

因此我的代码如下:

        const reqObj = {
            mch_appid: wxmpAppId,
            mchid: wxpayMchid,
            nonce_str: nanoid(32),
            partner_trade_no: dbObj._id,
            openid: user.openId,
            check_name: "NO_CHECK",
            amount: Math.round(amount * 100),
            desc: description
        }
        const resp = await wxpay.v2.mmpaymkttransfers.promotion.transfers.post(reqObj)

这样的代码产生如下报错:

"AssertionError [ERR_ASSERTION]: The data.mch_id(undefined) doesn't matched init one(*****)\n" +
    '    at signer (*****/node_modules/wechatpay-axios-plugin/lib/transformer.js:37:14)\n' +
    '    at transform (*****/node_modules/axios/lib/core/transformData.js:16:12)\n' +
    '    at Object.forEach (*****/node_modules/axios/lib/utils.js:247:10)\n' +
    '    at transformData (*****/node_modules/axios/lib/core/transformData.js:15:9)\n' +
    '    at dispatchRequest (*****/node_modules/axios/lib/core/dispatchRequest.js:30:17)\n' +
    ......

经过我分析,原因是:

static signer(data) {
const { sign_type: type = 'MD5', mch_id: mchid } = data;
if (Transformer.mchid) {
assert.ok(Transformer.mchid === mchid, `The data.mch_id(${mchid}) doesn't matched init one(${Transformer.mchid})`);
}

这段代码是判断请求数据中的mch_id字段的值和创建WechatPay对象时配置的mchid是否一致,但偏偏有些接口的商户号字段不叫mch_id而叫mchid......(这波微信的锅很大)

我的建议是将上面第35行改为:

const { sign_type: type = 'MD5 } = data;
const mchid = data.mch_id || data.mchid;

应该就可以解决这个问题。

不知道您觉得是否可以,如果可以的话,我可以发布pull request上来。

最后,对您长期以来编写和维护这样一个很好用的库,表示衷心的感谢!

@TheNorthMemory TheNorthMemory added the good first issue Good for newcomers label Aug 12, 2021
@TheNorthMemory
Copy link
Owner

这个接口我还真没仔细看,确实 const mchid = data.mch_id || data.mchid; 这么调整是可以的,欢迎提 PR.

@Starrah
Copy link
Contributor Author

Starrah commented Aug 12, 2021

已PR #36. 如无问题的话可以merge。
(另外小声问下您大概什么时间更npm上的版本 这个问题有点影响我现在的业务)
非常感谢!

TheNorthMemory added a commit that referenced this issue Aug 12, 2021
- fix #35, `v2.mmpaymkttransfers.promotion.transfers.post` not works well issue.
@TheNorthMemory
Copy link
Owner

v0.7.10 已发布。

TheNorthMemory added a commit to TheNorthMemory/wechatpay-php that referenced this issue Aug 12, 2021
…n#35

It used to get the merchant id only from data.mch_id, which is not suitable for some API like https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2 .
After the modification, the merchant id can be passed through the SDK client by the xml[mch_id] or xml[mchid] data.
@Starrah
Copy link
Contributor Author

Starrah commented Aug 12, 2021

另外,给将来可能会使用这个接口的朋友们提个醒:
这个接口特别绝,不只是mchid命名的问题,还有一点是它的请求是没有签名sign字段的......文档里确实也是这么写的......
这样直接参照我上面的代码使用本包会报错

the response's sign(undefined) doesn't matched the local calculated(xxxxx)

如果要解决此问题,可能只能是写一个特判的逻辑、根据请求的路径决定这个接口需不需要验签之类的办法......但这样我觉得太麻烦也太奇怪了。作者您如果愿意的话可以考虑解决一下,但不解决我觉得也没啥问题、毕竟是这个接口本身太奇怪了,所以我就先不开个Issue了,只是放在这给之后可能用到的朋友们提个醒。

我目前解决的方法是这样的:

import {Transformer} from "wechatpay-axios-plugin"; // ts的写法,如果是js的话可以写成const Transformer = require("wechatpay-axios-plugin").Transformer

        const reqObj = {
            mch_appid: wxmpAppId,
            mchid: wxpayMchid,
            nonce_str: nanoid(32),
            partner_trade_no: dbObj._id,
            openid: user.openId,
            check_name: "NO_CHECK",
            amount: Math.round(amount * 100),
            desc: description
        }
        const resp = await wxpay.v2.mmpaymkttransfers.promotion.transfers.post(reqObj, {
                transformResponse: [Transformer.toObject]
        }) // 覆盖掉默认的transformResponse配置

其原理是这样的:

/**
* @property {array} response - @see {import('axios').AxiosTransformer}
*/
static get response() {
return [this.toObject, this.verifier];
}

WechatPay对象里面的post等请求方法,默认配置的transformResponse是Transformer.toObject, Transformer.verifier两个,前一个是用来把xml转成js对象的,后一个是验签的。现在既然接口不返回签名,那就不验签,手动指定transformResponse为只留Transformer.toObject一个、不使用Transformer.verifier就行了。

@TheNorthMemory
Copy link
Owner

v2上有几个接口,是比较嗝咛,返回值没sign,入参还有不需要nonce_str的。就事论事,在返回值没有sign场景,README上已经写了一个例子(获取RSA公钥),就是在当前请求上,给个transformResponse 覆盖默认中间件:

  // 返回值无`sign`字段,无需数据校验
  transformResponse: [Transformer.toObject],

@Starrah
Copy link
Contributor Author

Starrah commented Aug 13, 2021

v2上有几个接口,是比较嗝咛,返回值没sign,入参还有不需要nonce_str的。就事论事,在返回值没有sign场景,README上已经写了一个例子(获取RSA公钥),就是在当前请求上,给个transformResponse 覆盖默认中间件:

  // 返回值无`sign`字段,无需数据校验
  transformResponse: [Transformer.toObject],

哦哦确实,我之前没看到那里。这么解决挺好的

@qaoo8
Copy link

qaoo8 commented Sep 26, 2021

@TheNorthMemory

v2 刷卡支付验收用例3 沙箱测试报错, 请帮忙看看如何解决,谢谢!

用例说明: “用例3:【刷卡-正常】订单金额0.03元(含0.01元代金券和0.02元免充值现金券),用户支付成功 “
使用版本: 0.7.13
刷卡支付验收用例1和2 沙箱测试成功。 用例网址
我的代码:

const express = require("express");
const app = express();

const { Wechatpay, Formatter } = require("wechatpay-axios-plugin");
const { readFileSync } = require("fs");

const mchid = "16*********";
const appid = "wx*****************";
//已更换为沙箱密钥
const sandbox_signkey = "33*************************";
const mch_id = mchid;

// 商户号
const merchantId = "16*********";
// 商户证书序列号
const merchantCertificateSerial = "17*********";
// 商户私钥
const merchantPrivateKeyFilePath = "/home/test/apiclient_key.pem";
const merchantPrivateKeyInstance = readFileSync(merchantPrivateKeyFilePath);
// 平台证书
const platformCertificateFilePath =
  "/home/test/platformTools/wechat********.pem";
const platformCertificateInstance = readFileSync(platformCertificateFilePath);
// 平台证书序列号
const platformCertificateSerial = "26*********";
const wxpay = new Wechatpay({
  mchid: merchantId,
  serial: merchantCertificateSerial,
  privateKey: merchantPrivateKeyInstance,
  certs: { [platformCertificateSerial]: platformCertificateInstance },
  // APIv2密钥(32字节)
  secret: sandbox_signkey,
});

app.use(express.json());

app.post("/pay_test", async (req, res) => {
  let authcode = req.body.paycode;
  let totalfee = JSON.parse(req.body.payamount);
  // 模拟一个商户订单号
  let out_trade_no = `No${+new Date()}_100`;

  try {
    // 付款码沙箱用例:请求支付
    console.log("付款码沙箱用例:请求支付");
    console.log(
      (
        await wxpay.v2.sandboxnew.pay.micropay({
          appid,
          mch_id,
          nonce_str: Formatter.nonce(),
          out_trade_no,
          body: "sandbox_goods_test",
          total_fee: totalfee,
          spbill_create_ip: "127.0.0.1",
          auth_code: authcode,
        })
      ).data
    );
    // 付款码沙箱用例:获取支付结果
    console.log("付款码沙箱用例:获取支付结果");
    console.log(
      (
        await wxpay.v2.sandboxnew.pay.orderquery({
          appid,
          mch_id,
          nonce_str: Formatter.nonce(),
          out_trade_no,
        })
      ).data
    );
  } catch (error) {
    console.log("以下打印回调报错:");
    console.log(error);
  }

  res.send("remote server sandbox test done!");
});

const port = process.env.PORT || 8888;
app.listen(port, () => {
  console.log("Server listening on post", port);
});

请求支付成功,获取支付结果时报错信息:

付款码沙箱用例:请求支付(成功)
{
  coupon_fee: '3',
  cash_fee_type: 'CNY',
  nonce_str: 'k29n9z4bBZp20vIFr4TL96VLi2Hwgewm',
  time_end: '202109*********',
  sign: '******************',
  coupon_id_0: '10000',
  coupon_id_1: '10001',
  coupon_fee_0: '1',
  coupon_fee_1: '2',
  fee_type: 'CNY',
  attach: 'sandbox_attach',
  device_info: 'sandbox',
  out_trade_no: 'TestNo1632**********_10',
  transaction_id: '************************',
  openid: '****************',
  trade_type: 'MICROPAY',
  return_code: 'SUCCESS',
  err_code_des: 'ok',
  mch_id: '*******************',
  settlement_total_fee: '1',
  coupon_batch_id_1: '56789',
  coupon_batch_id_0: '12345',
  cash_fee: '0',
  is_subscribe: 'Y',
  return_msg: 'OK',
  bank_type: 'CMC',
  coupon_type_1: 'NO_CASH',
  coupon_type_0: 'CASH',
  total_fee: '3',
  appid: '*****************',
  coupon_count: '2',
  result_code: 'SUCCESS',
  err_code: 'SUCCESS'
}
付款码沙箱用例:获取支付结果
以下打印回调报错:
AssertionError [ERR_ASSERTION]: the response's sign(***F5477552*********) doesn't matched the local calculated(****69C84B**********)
    at Object.verifier (/home/test/pay-sandbox-server-0.713/node_modules/wechatpay-axios-plugin/lib/transformer.js:93:12)
    at transform (/home/test/pay-sandbox-server-0.713/node_modules/axios/lib/core/transformData.js:18:15)
    at Object.forEach (/home/test/pay-sandbox-server-0.713/node_modules/axios/lib/utils.js:245:10)
    at Object.transformData (/home/test/pay-sandbox-server-0.713/node_modules/axios/lib/core/transformData.js:17:9)
    at onAdapterResolution (/home/test/pay-sandbox-server-0.713/node_modules/axios/lib/core/dispatchRequest.js:57:35)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    at async /home/test/pay-sandbox-server-0.713/index.js:77:9 {
  generatedMessage: false,
  code: 'ERR_ASSERTION',
  actual: false,
  expected: true,
  operator: '=='
}

@TheNorthMemory
Copy link
Owner

@qaoo8 稍等我来按你的方式本地校验一下

@qaoo8
Copy link

qaoo8 commented Sep 26, 2021

@TheNorthMemory 感谢!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
3 participants