PAYATHON 2026

天猫精灵二维码支付 my.call('qrcodePay') 创建订单失败

支付老李

结论

问题发生在“创建支付订单”阶段,而不是二维码展示阶段。返回信息为:

createOrderFailure
resMsg=商户密钥不能为空

这说明收银台已经成功唤起,但支付服务端创建订单时没有取得有效的商户密钥,所以无法生成二维码。code=-2 表示订单创建失败,二维码没有显示只是后续结果。

建议先检查服务端的商户配置,以及 content 中用于识别商户身份的字段。使用 RSA2 私钥为 content 签名,不等于已经完成商户密钥配置。

content 是否必须是淘宝订单

不能简单地说“必须是淘宝订单”。

qrcodePay 的 content 应当包含该支付产品支持的订单内容,并按接口要求生成和签名。它通常不能只是任意一个淘宝订单号,也不能把普通淘宝订单数据自行拼装、签名后直接传入。

具体支持哪些订单类型,取决于当前应用接入的支付产品、商户权限和接口协议。需要逐项确认:

  • 应用是否已经开通二维码支付能力;
  • 商户是否已与应用正确绑定;
  • 订单是否通过对应的服务端下单接口创建;
  • content 的字段、格式和签名方式是否符合接口文档;
  • 淘宝订单是否属于当前接口明确支持的交易类型。

如果使用淘宝侧订单,还需要确认它能否通过天猫精灵对应的支付链路付款。不能只根据“这是淘宝订单”来判断是否可用。

为什么 RSA2 签名成功仍会提示商户密钥为空

RSA2 私钥与错误信息中的“商户密钥”不一定指同一项配置。

RSA2 私钥用于证明请求由合法商户签发。服务端还需要根据请求中的商户号、应用 ID 或其他身份字段,找到对应的商户配置。如果身份字段缺失或填写错误,或者后台没有配置相应密钥,即使 RSA2 签名成功,服务端仍可能返回“商户密钥不能为空”。

常见原因有:

  1. 商户平台尚未配置 RSA2 公钥或商户密钥。
  2. 应用与商户号尚未完成绑定。
  3. content 中的商户号、应用 ID 等字段为空,或与后台配置不一致。
  4. 使用了其他环境的密钥,例如测试环境订单使用了生产环境密钥。
  5. 使用公钥签名,或者平台上的公钥与本地私钥不匹配。
  6. 签名算法填写为 RSA,但接口实际要求 RSA2。
  7. 完成签名后又修改了订单字段、字段顺序、编码或空格。
  8. content 被重复 URL 编码或重复 JSON 序列化,导致验签原文发生变化。
  9. 私钥只保存在客户端,服务端创建订单时并未从商户配置中加载到密钥。

IDEA 版本通常不会影响服务端读取商户密钥,因此 IDEA 1.13.1505 不是优先排查项。

建议的排查顺序

1. 检查商户后台配置

先确认支付平台后台已经完成以下配置:

  • 商户号已启用;
  • 应用具有二维码支付权限;
  • 应用 ID 已与商户号绑定;
  • RSA2 公钥已上传并生效;
  • 接口要求的商户密钥、应用密钥或渠道密钥已填写;
  • 当前请求环境与密钥所属环境一致。

如果后台有单独的“商户密钥”配置项,就必须按平台要求填写。RSA2 私钥不能自动替代这个配置项。

2. 检查 content 中的商户身份

打印签名前的原始订单参数,重点核对用于定位商户的字段。实际字段名应以当前接口文档为准,可能是 merchantId、sellerId、appId 或其他名称。不要自行猜测字段名并直接提交。

检查这些字段是否存在以下问题:

  • 字段缺失或值为空字符串;
  • 使用了测试商户号;
  • 值的前后带有空格;
  • 与上传公钥时使用的商户账号不一致;
  • 签名后又被修改。

3. 核对 RSA2 密钥对

可以使用 OpenSSL 检查私钥能否正常解析,并导出对应的公钥:

openssl pkey -in merchant_private_key.pem -check
openssl pkey -in merchant_private_key.pem -pubout -out derived_public_key.pem

将导出的公钥与商户平台中配置的公钥进行比较。两者必须来自同一密钥对。

如果平台要求使用 PKCS#8 私钥,而现有私钥是其他格式,可以先转换:

openssl pkcs8 -topk8 -inform PEM \
  -in merchant_private_key.pem \
  -outform PEM \
  -nocrypt \
  -out merchant_private_key_pkcs8.pem

不要将私钥上传到前端、日志系统或代码仓库。

4. 核对待签名字符串

RSA2 一般对应 SHA256withRSA,但参数排序、空值处理、字符集以及签名字段是否参与计算,都必须以支付接口文档为准。

调试时应同时记录:

签名前的业务参数
最终待签名字符串
签名算法
字符集
Base64 签名结果
提交给 qrcodePay 的最终 content
商户号或应用 ID
请求环境

不要记录完整私钥或商户密钥。

如果接口要求对排序后的参数字符串签名,就必须对最终字符串进行签名。不能先签名 JSON,再将 JSON 转换成另一种格式后提交。

RSA2 签名示例

下面的代码只演示 Java 中标准的 RSA2 签名过程。待签名字符串仍需按照实际接口的参数排序和编码规则生成。

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

public class Rsa2Signer {

    public static String sign(String unsignedContent, String privateKeyPem)
            throws Exception {

        String privateKeyBase64 = privateKeyPem
                .replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s+", "");

        byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64);

        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        PrivateKey privateKey = KeyFactory
                .getInstance("RSA")
                .generatePrivate(keySpec);

        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(unsignedContent.getBytes(StandardCharsets.UTF_8));

        return Base64.getEncoder().encodeToString(signature.sign());
    }
}

调用时,要保证提交的内容与签名原文完全一致:

String unsignedContent = buildContentAccordingToApiSpec();
String sign = Rsa2Signer.sign(unsignedContent, privateKeyPem);

// 按接口规定将 sign 放入指定字段。
// 此后不要再修改参与签名的参数。

不要在小程序端或设备端保存 RSA2 私钥。应由业务服务端创建订单并完成签名,再将接口要求的 content 返回给前端:

my.call('qrcodePay', {
  content: contentFromYourServer
}, function (result) {
  console.log(result);
});

这里的字段结构只用于说明调用方式。实际参数名称和回调格式应以当前版本的接口文档为准。

如何区分配置问题和签名问题

如果只是 RSA2 验签失败,服务端通常会返回签名错误、验签失败或非法签名等信息。当前返回的是“商户密钥不能为空”,因此更可能是服务端没有找到对应的商户配置,或者相关配置项为空。

建议按以下顺序检查:

商户号/应用 ID → 商户绑定关系 → 商户密钥配置 → RSA2 公钥 → content → 签名

确认商户后台配置无误后,再逐字比较“最终待签名字符串”和“最终提交的 content”。不少问题并非出在 RSA2 算法本身,而是签名完成后发生了字段重排、编码变化或二次转义。

如果以上检查均正常,但仍然返回相同错误,需要携带请求时间、应用 ID、脱敏后的商户号、订单号和完整错误码,联系接口提供方查询服务端日志。不要在工单中提交 RSA2 私钥或完整商户密钥。

备注:内容仅供参考。