PAYATHON 2026

商家扣款免密支付扫码签约提示商户状态不正常

支付老李

结论

二维码能正常生成,只能说明签约请求的格式和基础参数通过了校验,不能证明当前应用对应的商户主体已经具备签约资格。

扫码后提示“商户状态不正常”,通常是支付宝在签约页进行二次校验时,发现请求中的 app_id、应用所属商户、商家扣款产品权限与 sign_scene 模板不匹配,或者相关能力尚未生效。排查时应先确认实际调用接口的应用,不能只看商户平台中的“某个账号”是否已经开通商家扣款。

常见原因

1. 调用接口的应用未开通商家扣款

商家扣款一般会同时校验商户主体和应用授权能力。以下情况都可能导致扫码失败:

  • 商家扣款开通在一个商户账号下,接口使用的 app_id 却属于另一个账号。
  • 产品已经开通,但当前应用还没有添加或关联相应能力。
  • 使用第三方应用代商户调用时,实际被授权商户没有开通该产品。
  • 混用了沙箱和生产环境。
  • 切换过应用、商户账号或密钥,但服务端配置没有同步更新。

因此,即使商户平台显示“商家扣款已开通”,也不能据此判断当前请求一定具备权限。

2. sign_scene 不属于当前应用或商户

自定义 sign_scene 模板必须与实际发起签约的商户和应用一致。常见问题有:

  • 请求中的 sign_scene 与模板对应的准确值不同。
  • 模板仍在审核、尚未启用或还没有生效。
  • 模板创建在其他商户账号或应用下。
  • 模板对应的业务模式与当前签约产品不一致。
  • 代码中存在空格、大小写错误,或者仍在使用旧配置。

后台能够看到模板,不代表当前 app_id 有权使用它。

3. 商户或应用状态异常

还需要检查以下情况:

  • 商户账号是否被冻结、受限,或者存在待处理的资质问题。
  • 应用是否已经上线。
  • 应用是否被停用,产品权限是否被暂停。
  • 商户签约协议是否已经失效。
  • 商户主体、应用主体或产品准入状态近期是否发生变化。

“商户状态不正常”通常是签约页给出的汇总提示,页面文案未必对应某一项具体的后台校验结果。

4. 请求参数中混入不可见字符

问题描述中的 channel = QRCODE 在 channel 后出现了不可见字符。这可能只是复制或展示时产生的异常,但仍建议检查原始 JSON 和日志,确认参数名确实是:

{
  "channel": "QRCODE"
}

参数名中不应含有 \u0000、全角空格或其他控制字符。服务端组装 JSON 时,应使用标准序列化库,不要手工拼接字符串。

排查步骤

第一步:确认实际请求身份

从真实请求日志中核对以下信息:

  • 网关地址使用的是生产环境还是沙箱环境。
  • app_id 是否属于预期应用。
  • 应用私钥是否属于该 app_id。
  • 如果是第三方代调用,确认 app_auth_token 对应的授权商户。
  • 当前商户 PID、应用所属主体和产品开通主体是否一致。

除了配置文件,还要检查最终发送到网关的实际请求。

第二步:检查产品与应用的关联状态

登录请求中 app_id 对应的开放平台账号,依次检查:

  1. 应用是否已经上线且状态正常。
  2. 应用是否具备商家扣款相关能力。
  3. 商家扣款产品是否处于有效状态。
  4. 当前业务模式是否允许扫码签约。
  5. 如果采用服务商模式,被代调用商户是否也符合产品准入条件。

第三步:检查签约场景模板

进入商家扣款的功能管理页面,逐项确认:

  • 模板已经审核通过并启用。
  • 请求中的 sign_scene 与后台值完全一致。
  • 模板属于当前调用的应用或商户。
  • 模板配置的业务内容与实际扣款场景一致。
  • 模板是否仍在等待生效。

如果刚刚新增或修改模板,可以等后台状态明确变为可用后再生成二维码。旧二维码可能仍带有旧参数,配置修改后应重新调用接口生成。

第四步:记录完整响应和扫码时间

至少保留以下信息:

  • app_id
  • 商户 PID
  • sign_scene
  • external_agreement_no
  • 请求时间
  • 网关返回的 code、sub_code、sub_msg
  • request_id 或支付宝返回的链路标识
  • 二维码生成时间和扫码时间
  • 扫码页面截图

记录信息时要注意脱敏,不要在日志或工单中暴露应用私钥、用户令牌、完整身份证号等敏感信息。

第五步:使用最小请求重新验证

可以先移除非必要的扩展参数,只保留签约必需字段。下面是请求业务参数的结构示例,具体字段应以当前接口文档和商户实际开通的产品为准:

{
  "personal_product_code": "实际开通产品对应的产品码",
  "sign_scene": "后台已生效的签约场景值",
  "external_agreement_no": "唯一签约协议号",
  "access_params": {
    "channel": "QRCODE"
  },
  "sign_notify_url": "https://example.com/alipay/agreement/notify"
}

Java 中可以使用 JSON 序列化对象,避免参数中混入不可见字符:

Map<String, Object> accessParams = new HashMap<>();
accessParams.put("channel", "QRCODE");

Map<String, Object> bizContent = new HashMap<>();
bizContent.put("personal_product_code", personalProductCode);
bizContent.put("sign_scene", signScene);
bizContent.put("external_agreement_no", externalAgreementNo);
bizContent.put("access_params", accessParams);
bizContent.put(
    "sign_notify_url",
    "https://example.com/alipay/agreement/notify"
);

String json = objectMapper.writeValueAsString(bizContent);

不要根据示例猜测 personal_product_code。该字段应填写商户实际开通产品及对应接口文档规定的值。

如何解决

根据排查结果采取相应处理:

  • app_id 错误:改用已经开通商家扣款且关联关系正确的应用。
  • 产品开通在其他主体:为当前调用主体和应用申请对应产品,或者调整调用链路。
  • 第三方代调用身份错误:核对 app_auth_token,确认授权商户具备产品权限。
  • sign_scene 不匹配:填写当前应用下已经审核并启用的模板场景值。
  • 模板刚刚修改:等待配置生效,然后重新生成二维码。
  • 商户或应用受限:在商户平台处理待办、资质问题或风控限制。
  • 参数含有控制字符:清理参数来源,使用标准 JSON 序列化后重新请求。
  • 后台显示均正常但仍然失败:携带链路信息联系支付宝技术支持,查询服务端的具体拦截原因。

注意事项

  • 二维码返回成功和用户签约成功属于两个不同阶段。
  • 不要重复使用已经过期或在配置变更前生成的二维码。
  • external_agreement_no 应在商户侧保持唯一,避免复用历史协议号影响排查。
  • 扫码时应使用符合产品要求的支付宝账号。如果页面明确提示“商户状态不正常”,排查重点仍应放在商户、应用和产品权限上。
  • 在没有后台错误码或支付宝侧链路查询结果的情况下,仅凭页面提示无法确定唯一原因。最有效的排查方法是核对实际 app_id、商户 PID、产品权限、sign_scene 归属和调用环境。

备注:内容仅供参考。