PAYATHON 2026

EasySDK 调用支付宝支付返回 62001 用户取消支付

支付老李

结论

62001 通常是支付宝客户端返回的支付结果码,表示支付流程被取消或中断。它不是 alipay.trade.app.pay 网关接口的签名错误,也不能据此认定公钥或私钥配置有问题。

errCode:-100 和 code:-100 更可能是客户端支付插件、跨端框架或业务封装层生成的失败状态,并非支付宝开放平台接口的标准业务返回码。具体含义要以实际使用的客户端 SDK 或插件文档为准。

如果用户已经打开支付宝收银台,之后点击返回、关闭收银台或放弃付款,出现 62001 属于正常结果。如果支付页面尚未正常打开就立即报错,则要继续检查服务端生成的订单字符串、客户端调用方式、应用环境和请求参数类型。

为什么会同时出现三种错误码

一次 App 支付通常涉及三个环节:

  1. 服务端调用 alipay.trade.app.pay,生成签名后的订单字符串。
  2. App 将订单字符串原样交给支付宝客户端 SDK。
  3. 客户端 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 就立即关单。

建议的排查顺序

  1. 记录服务端 EasySDK 返回的完整订单字符串,对外展示日志时隐藏签名等敏感内容。
  2. 确认 App 传给支付 SDK 的内容与服务端返回值逐字一致。
  3. 暂时删除 passback_params,使用最小参数重新测试。
  4. 记录客户端支付 SDK 的原始返回值,区分支付宝结果码和框架封装码。
  5. 检查正式环境、沙箱环境及应用身份是否混用。
  6. 确认 App 支付能力、包名、应用签名或 Bundle ID 配置。
  7. 使用 alipay.trade.query 查询订单是否已经创建,并确认其最终状态。
  8. 如果错误只出现在某个框架或支付插件中,再根据该插件对 errCode:-100 的定义排查客户端集成。

用户确实退出收银台时,可以将 62001 按取消支付处理。如果支付界面没有正常出现,就不能以“用户取消”结束排查。此时应优先确认订单字符串从服务端传到客户端后是否发生变化、客户端插件如何封装错误,以及应用与支付环境是否匹配。

备注:内容仅供参考。