支付宝订单查询实战:Pay 统一 `query()` 入口覆盖支付、退款、转账全场景

原创2026-10-04 15:52:091,968 阅读
文章标签:后端金融科技

支付宝订单查询实战:Pay 统一 query() 入口覆盖支付、退款、转账全场景

本文围绕 yansongda/pay 中支付宝模块的订单查询能力展开,系统讲解如何通过统一的 Pay::alipay()->query() 方法查询普通支付订单、退款订单与转账订单,并深入源码剖析其背后的快捷路由(Shortcut)与插件链机制。读完本文,你将掌握 _action 参数在各查询场景下的用法、out_request_no 的自动路由规则,以及查询结果的 Collection 返回形式,能够直接在自己的支付系统中落地订单对账与状态轮询逻辑。

方法总览

支付宝订单查询统一收敛在 query 方法上,其签名如下:

方法名 参数 返回值
query array $order Collection

调用时先通过 Pay::config() 注入支付宝配置,再经由 Pay::alipay() 快捷门面发起查询,返回的 Collection 对象(来自 Yansongda\Supports\Collection)封装了支付宝网关的原始响应数据,可像数组一样按字段取值(如 $result->get('trade_status'))。

:::warning 注意 更多查询能力(如协议支付、预授权、刷脸等场景)请直接查看源码,文档中仅列举最常用的功能。 :::

查询普通支付订单

默认场景下,query() 对应的是支付宝「统一收单线下交易查询」接口(alipay.trade.query),用于查询网页支付、APP 支付、小程序、扫码等各类已下单交易的当前状态。

Pay::config($this->config);

$order = [
    'out_trade_no' => '1514027114',
    // '_action' => 'web', // 默认值,查询普通支付网页订单
    // '_action' => 'app', // 查询 APP 订单
    // '_action' => 'mini', // 查询小程序订单
    // '_action' => 'pos', // 查询刷卡订单
    // '_action' => 'scan', // 查询扫码订单
    // '_action' => 'h5', // 查询 H5 订单

];

$result = Pay::alipay()->query($order);

_action 路由取值

_action 决定 query() 内部装配哪一组插件,从而命中对应渠道的查询接口。从源码 src/Action/AlipayAction.php 中 QUERY_* 常量集合可以确认,普通支付查询支持以下取值:

_action 值 对应场景 底层插件
web(默认) 网页支付订单 Pay\Web\QueryPlugin
app APP 支付订单 Pay\App\QueryPlugin
mini 小程序订单 Pay\Mini\QueryPlugin
pos 刷卡订单 Pay\Pos\QueryPlugin
scan 扫码订单 Pay\Scan\QueryPlugin
h5 H5 移动端订单 Pay\H5\QueryPlugin

除上述常规场景外,从 QueryShortcut.php 的 match 分支还可以看到两个进阶取值:

  • agreement:查询支付宝签约(协议支付)订单,对应 Pay\Agreement\Pay\QueryPlugin;
  • authorization:查询预授权订单,对应 Pay\Authorization\Pay\QueryPlugin;
  • face:查询刷脸支付订单,对应 Pay\Face\QueryPlugin。

当 _action 未设置时,源码中 AlipayAction::QUERY_DEFAULT 即 'default',且 defaultPlugins() 内部直接复用 webPlugins(),因此默认行为等价于 _action => 'web'(见 QueryShortcut.php)。

配置参数

查询订单时,$order 数组中的业务字段与支付宝官方「请求参数」完全一致,SDK 不做任何二次加工,直接透传进 biz_content。以网页支付为例,底层插件 Web/QueryPlugin.php 仅做两件事:

$rocket->mergePayload([
    'method' => 'alipay.trade.query',
    'biz_content' => $rocket->getParams(),
]);

即:指定接口 method 为 alipay.trade.query,并把 $order 整体作为 biz_content 提交。因此 out_trade_no、trade_no 等字段名与官方文档逐字对齐:

  • 网页订单:alipay.trade.query(out_trade_no / trade_no 二选一必填);
  • APP 订单、小程序订单、刷卡订单、扫码订单、H5 订单:均为统一收单 alipay.trade.query 接口,差异只体现在 _action 路由与验签/响应处理细节上。

查询退款订单

退款查询走「统一收单交易退款查询」接口(alipay.trade.fastpay.refund.query),用于核实某笔退款是否到账、查询退款金额与实际退款状态。

Pay::config($this->config);

$order = [
    'out_trade_no' => '1514027114',
    'out_request_no' => '1514027114',
    // '_action' => 'refund', // 默认值,查询退款网页订单
    // '_action' => 'refund_web', // 默认值,查询退款网页订单
    // '_action' => 'refund_app', // 查询 APP 退款订单
    // '_action' => 'refund_mini', // 查询小程序退款订单
    // '_action' => 'refund_pos', // 查询刷卡退款订单
    // '_action' => 'refund_scan', // 查询扫码退款订单
    // '_action' => 'refund_h5', // 查询 H5 退款订单
];

$result = Pay::alipay()->query($order);

自动路由:out_request_no 触发退款查询

与支付查询不同,退款查询有一个隐式的自动路由规则:只要 $order 中包含了 out_request_no 字段(退款请求号,即发起退款时使用的单号),query() 会自动切换到退款查询插件链,无需显式声明 _action。见 QueryShortcut.php:

if (isset($params['out_request_no'])) {
    return $this->refundPlugins();
}

refundPlugins() 内部同样复用 refundWebPlugins()(QueryShortcut.php),因此默认退款查询等价于 _action => 'refund_web'。

退款 _action 取值

_action 值 对应场景 底层插件
refund / refund_web(默认) 网页退款订单 Pay\Web\QueryRefundPlugin
refund_app APP 退款订单 Pay\App\QueryRefundPlugin
refund_mini 小程序退款订单 Pay\Mini\QueryRefundPlugin
refund_pos 刷卡退款订单 Pay\Pos\QueryRefundPlugin
refund_scan 扫码退款订单 Pay\Scan\QueryRefundPlugin
refund_h5 H5 退款订单 Pay\H5\QueryRefundPlugin

进阶场景还支持 refund_authorization(预授权退款查询),对应 Pay\Authorization\Pay\QueryRefundPlugin。完整的常量清单可查阅 AlipayAction.php。

配置参数

退款查询的参数同样与官方 alipay.trade.fastpay.refund.query 接口完全一致。底层插件 Web/QueryRefundPlugin.php 装配的请求为:

$rocket->mergePayload([
    'method' => 'alipay.trade.fastpay.refund.query',
    'biz_content' => $rocket->getParams(),
]);

必填参数为 out_trade_no(商户订单号)与 out_request_no(退款请求号),两者共同定位一笔具体的退款记录。网页、APP、小程序、刷卡、扫码、H5 各场景均调用同一退款查询接口,区别同样仅在路由层。

查询转账订单

转账查询走「支付宝单笔转账查询」接口(alipay.fund.trans.common.query),适用于商户发起转账(如分账、代发)后核实转账状态。

Pay::config($this->config);

$order = [
    'out_biz_no' => '202209032319',
    '_action' => 'transfer'
];

$result = Pay::alipay()->query($order);

转账查询需要显式指定 _action => 'transfer',否则会按默认的支付订单查询处理。该分支在 QueryShortcut.php 中路由至 transferPlugins(),底层插件为 Fund/Transfer/Fund/QueryPlugin.php:

$rocket->mergePayload([
    'method' => 'alipay.fund.trans.common.query',
    'biz_content' => $rocket->getParams(),
]);

配置参数

转账查询参数与官方 alipay.fund.trans.common.query 接口的「请求参数」完全一致,核心字段为:

  • out_biz_no:商户转账订单号(发起转账时传入的外部业务单号);
  • order_id:支付宝转账单据号(与 out_biz_no 二选一,均可不传时按 pay_fund_order_id 兜底)。

biz_content 中其余字段同样支持官方接口的全部可选参数,SDK 不做裁剪,兼容所有功能。

源码原理:一次查询背后的插件链

query() 之所以能在一个入口下覆盖十余种场景,核心依赖 QueryShortcut 的插件装配机制。以默认的网页支付查询为例,QueryShortcutTest.php 中的断言精确给出了完整插件链顺序:

[
    StartPlugin::class,                 // 初始化 Rocket、装载配置
    WebQueryPlugin::class,              // 装配 method 与 biz_content
    FormatPayloadBizContentPlugin::class, // 格式化业务参数
    AddPayloadSignaturePlugin::class,   // 生成请求签名
    AddRadarPlugin::class,              // 组装请求 URL(雷达)
    VerifySignaturePlugin::class,       // 校验响应签名
    ResponsePlugin::class,              // 统一响应处理
    ParserPlugin::class,                // 解析为 Collection
]

整条链路严格遵循「发起(Start)→ 业务装配(Query)→ 参数格式化 → 加签 → 请求 → 验签 → 响应解析」的顺序,任意场景的插件链都共享这组通用骨架,仅替换中间的业务 Query 插件。这种设计保证了:

  1. 参数零改造:$order 中的业务字段原样进入 biz_content,与官方接口一一对应,学习成本低;
  2. 路由可扩展:新增渠道只需在 AlipayAction 增加常量并在 QueryShortcut 的 match 中注册分支(QueryShortcut.php);
  3. 非法入参有兜底:_action 传入未定义值时抛出 InvalidParamsException(异常码 PARAMS_SHORTCUT_ACTION_INVALID),测试 testFooParam 对该行为做了明确验证。

常见问题与建议

  • 查询状态判断:支付查询返回的 trade_status 字段是核心判据(TRADE_SUCCESS/TRADE_FINISHED 表示交易成功),退款查询则关注 refund_status 与 total_amount 等字段,取值均以支付宝官方接口文档为准;
  • 异步对账:查询接口建议与回调通知配合使用——收到 callback 后用 query() 主动复核,可有效避免漏单与状态不一致;
  • _action 与 out_request_no 的优先级:当两者同时出现时,out_request_no 的自动路由优先于 _action,这是 QueryShortcut.php 中先判 out_request_no 再走 match 的执行顺序决定的;
  • 扩展阅读:支付发起见 pay.md,退款操作见 refund.md,撤销交易见 cancel.md,关闭交易见 close.md,响应数据结构见 response.md。
登录后查看全文
pay