EasySDK 调用支付宝支付返回 62001 用户取消支付
结论
62001 通常是支付宝客户端返回的支付结果码,表示支付流程被取消或中断。它不是 alipay.trade.app.pay 网关接口的签名错误,也不能据此认定公钥或私钥配置有问题。
errCode:-100 和 code:-100 更可能是客户端支付插件、跨端框架或业务封装层生成的失败状态,并非支付宝开放平台接口的标准业务返回码。具体含义要以实际使用的客户端 SDK 或插件文档为准。
如果用户已经打开支付宝收银台,之后点击返回、关闭收银台或放弃付款,出现 62001 属于正常结果。如果支付页面尚未正常打开就立即报错,则要继续检查服务端生成的订单字符串、客户端调用方式、应用环境和请求参数类型。
为什么会同时出现三种错误码
一次 App 支付通常涉及三个环节:
- 服务端调用
alipay.trade.app.pay,生成签名后的订单字符串。 - App 将订单字符串原样交给支付宝客户端 SDK。
- 客户端 SDK 或上层支付插件返回支付结果。
日志里的几个字段可能分别来自不同环节:
62001:支付宝客户端支付流程的结果状态,通常表示用户取消。errCode:-100:客户端插件或框架对支付失败的统一封装。code:-100:业务代码再次包装后的错误码。
排查时要保留支付宝 SDK 未经处理的原始回调,不能只看应用最后抛出的 -100。
先确认支付流程在哪一步中断
已进入支付宝收银台
如果用户已经看到订单信息、支付方式或密码输入页面,随后返回或关闭页面,62001 一般就是取消支付。
取消支付不代表订单一定不存在,也不建议直接将订单标记为关闭。客户端结果可能受到网络或进程切换等因素影响,最终状态应由服务端通过订单查询或异步通知确认。
未进入收银台便立即失败
这种情况不能直接归因于用户主动取消,应重点检查:
- 服务端是否返回了完整的订单字符串;
- 客户端是否对订单字符串进行了二次 URL 编码或解码;
- 是否误将整个服务端 JSON 响应传给支付 SDK;
- 正式应用是否误用了沙箱网关或沙箱客户端;
app_id、私钥和支付宝公钥是否属于同一应用;- 应用是否已签约并开通 App 支付产品;
- Android 包名、应用签名或 iOS Bundle ID 是否与开放平台配置一致;
- 客户端支付宝 SDK、支付插件及其回调处理是否正确;
- 设备上的支付宝客户端是否可用。
EasySDK 的正确调用方式
PHP 服务端只负责生成订单字符串,不负责判断用户是否取消。典型调用方式如下:
<?php
use Alipay\EasySDK\Kernel\Factory;
use Alipay\EasySDK\Kernel\Config;
function getOptions(): Config
{
$options = new Config();
$options->protocol = 'https';
$options->gatewayHost = 'openapi.alipay.com';
$options->signType = 'RSA2';
$options->appId = '你的 app_id';
$options->merchantPrivateKey = '应用私钥';
$options->alipayPublicKey = '支付宝公钥';
$options->notifyUrl = 'https://example.com/alipay/notify';
return $options;
}
Factory::setOptions(getOptions());
$result = Factory::payment()
->app()
->pay('购买', '2022091053975110', '1988.00');
// 将订单字符串返回给 App。
// 不要在返回后再次拼接、排序或修改其中的参数。
$orderString = $result->body;
客户端应将 $result->body 对应的字符串原样传给支付宝支付 SDK。如果服务端返回 JSON,可以把订单字符串放在单独的字段中:
{
"order_string": "alipay_sdk=...&app_id=...&biz_content=...&sign=..."
}
客户端应取出 order_string 字段的值,而不是把整段 JSON 传给 SDK,也不要再次调用 URL 编码函数。
检查请求中的 passback_params
给出的 biz_content 解码后大致包含:
{
"subject": "购买",
"out_trade_no": "2022091053975110",
"total_amount": "1988.00",
"passback_params": 10
}
其中,passback_params 被传成了数字 10。这个参数用于异步通知回传,建议按字符串处理。如果包含特殊字符,还要按照接口要求进行 URL 编码。例如:
{
"passback_params": "10"
}
或者:
$passbackParams = urlencode('order_type=10&source=app');
不过,只把 10 写成数字,通常不足以证明它就是导致 62001 的直接原因。可以先删除这个可选参数,用最小参数重新测试:
{
"subject": "购买",
"out_trade_no": "2022091053975110",
"total_amount": "1988.00"
}
如果最小请求可以正常拉起收银台并完成支付,再逐项恢复可选参数。
公钥、私钥之外还要检查什么
即使密钥内容正确,也要确认密钥之间的对应关系没有配错:
- 使用的是应用私钥,而不是支付宝公钥或应用公钥;
- 普通公钥模式下配置的是支付宝公钥,而不是应用公钥;
- 证书模式不能与普通公钥模式混用;
- 当前私钥对应的应用公钥已上传到同一个
app_id; - 正式环境使用
openapi.alipay.com; - 沙箱环境的应用、密钥、网关和支付宝客户端相互匹配;
- 服务端和客户端使用的是同一个开放平台应用。
订单参数也需要检查:
out_trade_no在商户系统内唯一;total_amount使用合法的金额格式,且大于零;subject非空;- 订单字符串没有被截断;
sign中的+、/、=没有在传输过程中被错误替换;- 客户端没有对已经编码的参数再次编码;
- 服务器时间准确,时区和时间同步正常。
不要只依赖客户端支付结果
无论客户端返回成功、失败还是取消,都不能将其作为最终入账依据。客户端进程可能被中断,也可能没有收到正确的回调。
服务端应处理支付宝异步通知。交易状态不明确时,还应调用交易查询接口:
alipay.trade.query
查询时可以使用商户订单号:
{
"out_trade_no": "2022091053975110"
}
只有服务端确认交易状态为支付成功,才能执行发货、充值或开通权益。如果查询结果仍是待支付,可以允许用户重新支付。需要关闭订单时,应由服务端按照业务规则调用关单接口,不能因为收到 62001 就立即关单。
建议的排查顺序
- 记录服务端 EasySDK 返回的完整订单字符串,对外展示日志时隐藏签名等敏感内容。
- 确认 App 传给支付 SDK 的内容与服务端返回值逐字一致。
- 暂时删除
passback_params,使用最小参数重新测试。 - 记录客户端支付 SDK 的原始返回值,区分支付宝结果码和框架封装码。
- 检查正式环境、沙箱环境及应用身份是否混用。
- 确认 App 支付能力、包名、应用签名或 Bundle ID 配置。
- 使用
alipay.trade.query查询订单是否已经创建,并确认其最终状态。 - 如果错误只出现在某个框架或支付插件中,再根据该插件对
errCode:-100的定义排查客户端集成。
用户确实退出收银台时,可以将 62001 按取消支付处理。如果支付界面没有正常出现,就不能以“用户取消”结束排查。此时应优先确认订单字符串从服务端传到客户端后是否发生变化、客户端插件如何封装错误,以及应用与支付环境是否匹配。
备注:内容仅供参考。
