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 刷卡支付验收用例3 沙箱测试报错 #39

Closed
qaoo8 opened this issue Sep 26, 2021 · 5 comments
Closed

v2 刷卡支付验收用例3 沙箱测试报错 #39

qaoo8 opened this issue Sep 26, 2021 · 5 comments
Labels
good first issue Good for newcomers

Comments

@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: '=='
}

Originally posted by @qaoo8 in #35 (comment)

@qaoo8 qaoo8 changed the title @TheNorthMemory v2 刷卡支付验收用例3 沙箱测试报错 Sep 26, 2021
@TheNorthMemory
Copy link
Owner

测试校验了一下,这个是官方沙箱环境的一个bug,他们在做返回值签名的时候,把金额0给漏了,即:

<cash_fee><![CDATA[0]]></cash_fee>

这个按照APIv2签名规范

  • 参数名ASCII码从小到大排序(字典序);
  • 如果参数的值为空不参与签名;
  • 参数名区分大小写;
  • 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。

0分金额需要参与签名,而沙箱环境给把这个值给过滤掉算签名了,你可以按照如下方法复现:

  1. 在实例化完wxpay代码之后,加入一个中间件打印返回XML:
Wechatpay.client.v2.defaults.transformResponse.unshift(data => (console.log(data), data));
  1. 按照 v2付款接口无法使用的问题 #35 的方法,对返回值忽略验签,即:
  res = await wxpay.v2.sandboxnew.pay.orderquery({
    appid,
    mch_id,
    nonce_str: Formatter.nonce(),
    out_trade_no,
  }, {
      transformResponse: [Transformer.toObject],
  });
  console.info(res.data);
  1. 按照打印的 XML以及你的沙箱密钥,到官方验签工具上去校验,肯定不过(预期结果);

  2. 去掉XML中的<cash_fee><![CDATA[0]]></cash_fee>或者置空值,验签通过;

我用PHP也验证了一下,均是这个问题,确信这个是官方沙箱环境的一个BUG。

@TheNorthMemory TheNorthMemory added the good first issue Good for newcomers label Sep 26, 2021
@qaoo8
Copy link
Author

qaoo8 commented Sep 26, 2021

@TheNorthMemory
更新代码,测试通过, 谢谢!
const { Wechatpay, Formatter, Transformer } = require("wechatpay-axios-plugin");

console.log("付款码沙箱用例:获取支付结果");
    console.log(
      (
        await wxpay.v2.sandboxnew.pay.orderquery(
          {
            appid,
            mch_id,
            nonce_str: Formatter.nonce(),
            out_trade_no,
          },
          { transformResponse: [Transformer.toObject] }
        )
      ).data
    );

@qaoo8 qaoo8 closed this as completed Sep 26, 2021
@qaoo8
Copy link
Author

qaoo8 commented Sep 28, 2021

@TheNorthMemory 您好!
关于 <cash_fee><![CDATA[0]]></cash_fee> 的问题,我认为在技术上是一个BUG,但在实际操作中,可能是微信支付的一个特别设置,为了避免付款金额为0元时显示为收款成功。

@TheNorthMemory
Copy link
Owner

@TheNorthMemory 您好!
关于 <cash_fee><![CDATA[0]]></cash_fee> 的问题,我认为在技术上是一个BUG,但在实际操作中,可能是微信支付的一个特别设置,为了避免付款金额为0元时显示为收款成功。

APIv2上,保证收单逻辑是正确的是return_coderesult_code,这俩值都是SUCCESS的时候,即表示业务逻辑(收单)成功;这个0元金额的,不应该是在数据签名出现错误(被忽略了),估摸着这仅是沙箱环境的问题,生产环境(未知)。

@qaoo8
Copy link
Author

qaoo8 commented Sep 29, 2021

@TheNorthMemory
0 元付款的生产环境返回参数(供参考):

 { 
     return_code: 'SUCCESS',
     result_code: 'FAIL',
     err_code: 'PARAM_ERROR',
     err_code_des: 'total_fee参数格式错误' 
 }

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
Development

No branches or pull requests

2 participants