支付宝拉起支付偶发 suspected-attack 错误的原因
明确结论
suspected-attack 并不表示 biz_content 中不能使用 out_trade_no 或 total_amount。这两个字段是 alipay.trade.wap.pay 的标准业务参数,必须放在 biz_content 中参与签名。
这个错误通常说明请求尚未进入正常的接口参数校验,就被支付宝的安全或风控系统拦截了。仅凭页面显示的 suspected-attack,无法判断具体触发了哪条规则,支付宝通常也不会在前端展示完整的风控策略。
对比两段请求可以看到,失败请求与成功请求并不只是时间不同,至少还有以下差异:
| 参数 | 成功请求 | 失败请求 |
|---|---|---|
app_id | 2019111369118975 | 2021003144627575 |
seller_id | 2088631573806393 | 2088441884661923 |
product_code | QUICK_WAP_PAY | QUICK_LYTX_WAP_PAY |
| 金额、商品名称 | 1 元、10钻石 | 68 元、时光礼包 |
建议优先核查以下问题:
QUICK_LYTX_WAP_PAY是否确实是该应用和商户已经签约并可使用的产品代码。app_id=2021003144627575与seller_id=2088441884661923是否属于同一签约关系,或者两者之间是否存在有效的第三方应用授权。- 问题中提供的商户 ID
2088031650971125为什么与两段请求中的seller_id都不一致。 - 对应的应用、商户、域名或具体业务是否触发了支付宝风控。
为什么不是 out_trade_no 导致的
out_trade_no 和 total_amount 本来就是 alipay.trade.wap.pay 的标准业务字段。正确结构如下:
{
"out_trade_no": "ZLXCXZFBW20240513114723218337",
"total_amount": "68.00",
"subject": "时光礼包68元礼包",
"product_code": "QUICK_WAP_PAY"
}
某些安全提示会提到“请求中不能出现 out_trade_no、total_fee 等关键字”,这通常不是针对支付宝网关要求的 biz_content,而是针对商户自己的回跳地址、来源地址,或其他不应携带支付参数的页面。例如,不建议把支付参数直接写进 return_url:
https://example.com/pay/result?out_trade_no=xxx&total_fee=68
回跳页面只适合展示结果或引导用户。订单状态必须由服务端根据支付宝异步通知或主动查询结果确认,不能依据浏览器地址中的金额和支付状态做判断。
因此,不要为了避开 suspected-attack 而删除 biz_content.out_trade_no 或 biz_content.total_amount。删除后,请求反而会缺少必填参数。
首先检查 product_code
标准手机网站支付通常使用:
"product_code": "QUICK_WAP_PAY"
失败请求使用的是:
"product_code": "QUICK_LYTX_WAP_PAY"
product_code 不是可以自行命名的业务标识。只有支付宝明确为对应产品、应用和商户开通该代码后,才能使用。
建议先确认这个值来自哪里:
- 如果它是业务代码自行拼接出来的,应改成当前手机网站支付文档规定的值。
- 如果它来自支付宝专项产品接入文档,应确认失败请求中的
app_id和收款商户已经签约该产品。 - 不要直接套用其他接口、历史项目或示例中的
product_code。
普通 WAP 支付可以先按以下结构重新生成请求:
{
"out_trade_no": "ZLXCXZFBW20240513114723218337",
"total_amount": "68.00",
"subject": "时光礼包68元礼包",
"product_code": "QUICK_WAP_PAY"
}
修改 product_code 后必须重新生成签名,不能直接改动已经签名的 HTML。
核对 app_id、收款商户和授权关系
请求中出现了三个不同的商户标识,需要先弄清它们各自对应的账号和关系:
成功请求 seller_id:2088631573806393
失败请求 seller_id:2088441884661923
问题中提供的商户 ID:2088031650971125
如果这是普通商户自己的应用,app_id 应属于对应的支付宝开放平台账号,而且该应用已经签约手机网站支付。此时通常不需要手工传入 seller_id,支付宝会根据应用及其签约关系识别收款方。
如果是服务商代商户调用,则必须满足以下条件:
app_id是服务商应用;- 商户已经向该应用完成有效授权;
- 请求使用了正确的应用授权凭证;
seller_id是实际获得授权并收款的商户;- 该商户已经签约相应的支付产品。
不能把某个应用的 app_id 与另一个没有授权关系的商户 seller_id 直接组合。即使签名正确,这种组合仍可能在权限、产品或风控环节被拒绝。
可以先做一次最小化验证。如果不是服务商代调用场景,暂时删除 seller_id,让支付宝根据 app_id 的签约关系确定收款方,然后重新生成完整请求和签名。
建议的请求生成方式
不建议长期自行拼接网关 URL。应使用支付宝官方 SDK,由 SDK 处理参数编码、排序和签名。下面是结构示例,具体类名和配置方式应以项目实际使用的 SDK 版本为准:
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setReturnUrl("https://ydpay.finish.com");
request.setNotifyUrl("https://example.com/alipay/notify");
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo("ZLXCXZFBW20240513114723218337");
model.setTotalAmount("68.00");
model.setSubject("时光礼包68元礼包");
model.setProductCode("QUICK_WAP_PAY");
request.setBizModel(model);
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
String paymentHtml = response.getBody();
如果确实需要指定收款商户,应先确认应用与商户之间的授权关系,再设置:
model.setSellerId("2088441884661923");
还要注意以下事项:
- 金额建议使用字符串
"68.00",避免浮点数格式产生不确定性。 return_url和notify_url应使用正式的 HTTPS 地址。- 示例
action中https://前有一个空格,也需要清除。 - 任何参数发生变化后,都必须重新签名。
- 不要修改 SDK 输出的已签名参数。
- 不要重复使用过期的
timestamp或历史支付表单。 - 页面只能提交一次。需要重复提交时,应重新生成订单号和支付请求。
商户后台应检查什么
登录 b.alipay.com 后没有看到待办事项,并不能完全排除应用、产品或风控问题。建议分别检查商家平台和开放平台:
- 确认失败请求对应的
app_id=2021003144627575状态正常,应用没有下架或冻结,密钥配置有效。 - 确认该应用已经绑定或签约“手机网站支付”能力。
- 确认实际收款商户号,以及它与应用之间的归属或授权关系。
- 检查产品签约状态、主体认证、经营信息和网站域名等是否已经失效。
- 检查是否存在风险处置、投诉、交易限制、结算限制或待补充材料。
- 如果使用服务商模式,检查商户授权是否已经过期、撤销,或授权范围是否不足。
- 检查商品名称和实际经营内容是否符合商户签约行业及应用经营内容。
不能只根据 suspected-attack 判断商户主体一定存在异常,也无法由此确定必须提交哪种材料。具体需要处理什么,取决于支付宝后台显示的限制类型。
推荐的排查顺序
先使用失败请求对应的 app_id 发起一笔最小化测试:
{
"out_trade_no": "唯一的新订单号",
"total_amount": "0.01",
"subject": "支付测试",
"product_code": "QUICK_WAP_PAY"
}
测试时暂时不要传 seller_id,并使用官方 SDK 重新生成表单。如果能够正常拉起支付,再逐项恢复原参数:
- 恢复实际金额;
- 恢复实际商品名称;
- 必要时恢复
seller_id; - 最后测试专项
product_code。
通过这种方式,可以判断问题主要出在产品代码、商户授权关系,还是具体交易内容。每次测试都必须使用新的 out_trade_no,不能复用已有订单号。
如果最小化请求仍然返回 suspected-attack,就不要再盲目修改字段。应向支付宝技术支持提交以下信息:
- 失败发生的准确时间;
app_id;- 实际收款商户 ID;
- 接口名
alipay.trade.wap.pay; out_trade_no;- 支付宝页面显示的完整错误码和截图;
- SDK 版本;
- 脱敏后的完整请求参数;
- 是否属于服务商代调用;
- 同一环境下成功与失败请求的对照记录。
不要提交私钥和完整的 sign。支付宝支持人员可以根据发生时间、应用和订单号查询内部网关及风控记录,这是确认 suspected-attack 具体触发规则的可靠方式。
综合现有信息,最需要优先排查的是失败请求使用了不同的 app_id、不同的 seller_id,以及非常规的 QUICK_LYTX_WAP_PAY,而不是 biz_content 中包含了 out_trade_no。
备注:内容仅供参考。
