- 金融科技
- 后端
【免费下载链接】pay
可能是我用过的最优雅的 Alipay/WeChat/Douyin/Unipay/江苏银行 的支付 SDK 扩展包了
导读
本文聚焦 yansongda/pay(本仓库即该项目源码)中Pay::wechat()->close($order)关闭微信支付订单的完整用法,覆盖 JSAPI、App、H5、小程序、Native 五类普通订单以及合单订单的关闭场景。文章不仅给出可直接运行的代码示例,还会结合CloseShortcut、WechatAction常量与各场景ClosePlugin源码,讲清_action路由规则、必传参数校验、服务商模式差异与插件调用链,帮助读者在真实项目中安全、准确地关闭微信支付订单。
一、方法概览
微信支付关闭订单对应快捷方法如下(摘自 web/docs/v3/wechat/close.md):
| 方法名 | 参数 | 返回值 |
|---|---|---|
| close | array $order | null |
从当前仓库源码看,src/Provider/Wechat.php 中close()方法的实际签名是public function close(array $order): Collection|Rocket:它会先派发MethodCalled事件(便于日志与埋点),再通过__call('close', [$order])走 Shortcut 路由,最后返回一个空的Collection实例。因此实际调用中你无需关心返回值,关单结果以微信服务端返回为准。
该方法同时覆盖两类场景:
- 关闭普通订单:通过
_action指定 JSAPI / App / H5 / 小程序 / Native 五种场景; - 关闭合单订单:通过
combine_out_trade_no+sub_orders参数发起合单关单。
二、关闭普通订单
2.1 最小可用示例
Pay::config($config); $order = [ 'out_trade_no' => '1514027114', // '_action' => 'jsapi', // jsapi 关单,默认 // '_action' => 'app', // app 关单 // '_action' => 'combine', // 合单关单 // '_action' => 'h5', // h5 关单 // '_action' => 'miniapp', // 小程序关单 // '_action' => 'native', // native 关单 ]; $result = Pay::wechat()->close($order);要点说明:
out_trade_no为必传参数:它是商户系统内部订单号,是关闭订单的唯一业务标识。底层插件在装载时会先校验该参数,缺失会直接抛出参数异常(详见下文“参数校验”一节)。- 不传
_action时默认按 JSAPI 关单:在 src/Shortcut/Wechat/CloseShortcut.php 中,$params['_action'] ?? WechatAction::CLOSE_DEFAULT默认值为'default',而defaultPlugins()返回的就是 JSAPI 的插件链,与文档注释“jsapi 关单,默认”一致。 - 所有“客观参数”由扩展包自动补齐:如
mchid(直连商户)或sp_mchid/sub_mchid(服务商模式)等请求体字段均由各场景ClosePlugin根据配置自动注入,你只需传入订单类主观参数。
2.2 订单配置参数
所有订单配置参数和官方无任何差别,兼容所有功能,所有参数请参考以下 API 查看「请求参数」一栏:
- JSAPI订单
- APP订单
- 合单订单
- H5订单
- 小程序订单
- Native订单
关于
miniapp的提醒:文档注释中写的是'_action' => 'miniapp',但当前仓库源码里对应常量是WechatAction::CLOSE_MINI = 'mini'(见 src/Action/WechatAction.php),CloseShortcut的match也仅匹配'mini'。实际开发中请传'_action' => 'mini',若传'miniapp'会落入默认分支并抛出InvalidParamsException。
三、关闭合单订单
3.1 示例一:显式合单参数
Pay::config($config); $order = [ 'combine_out_trade_no' => '1514027114', 'sub_orders' => '123456' ]; $result = Pay::wechat()->close($order);3.2 示例二:显式_action指定合单
//$order = [ // 'out_trade_no' => '1514027114', // 'sub_orders' => '123456', // '_action' => 'combine', //]; $result = Pay::wechat()->close($order);合单关单的核心机制:在 src/Shortcut/Wechat/CloseShortcut.php 中,只要参数里出现combine_out_trade_no或sub_orders中的任意一个,CloseShortcut就会优先自动路由到合单关单插件链,无需再显式传_action => 'combine'。这一分支判断在_action解析之前执行,因此两种写法效果等价。
3.3 订单配置参数
合单关单同样保持与官方接口完全一致的参数语义,combine_out_trade_no、sub_orders等字段会原样透传给请求体,所有参数请参考这里,查看「请求参数」一栏。
按官方接口语义,
sub_orders应为子订单信息数组;文档示例中的'123456'仅为示意,实际项目中请按官方「请求参数」要求组装为合法结构。
四、_action路由与插件调用链源码解析
关闭订单的入口是 src/Shortcut/Wechat/CloseShortcut.php 中getPlugins()的分发逻辑,其路由规则与 src/Action/WechatAction.php 中定义的动作常量一一对应:
_action值 | 常量 | 场景 | 对应插件 |
|---|---|---|---|
| 不传(默认) | CLOSE_DEFAULT = 'default' | JSAPI 关单 | Jsapi\ClosePlugin |
jsapi | CLOSE_JSAPI = 'jsapi' | JSAPI 关单 | Jsapi\ClosePlugin |
app | CLOSE_APP = 'app' | App 关单 | App\ClosePlugin |
h5 | CLOSE_H5 = 'h5' | H5 关单 | H5\ClosePlugin |
mini | CLOSE_MINI = 'mini' | 小程序关单 | Mini\ClosePlugin |
native | CLOSE_NATIVE = 'native' | Native 关单 | Native\ClosePlugin |
combine(或参数含combine_out_trade_no/sub_orders) | CLOSE_COMBINE = 'combine' | 合单关单 | Combine\ClosePlugin |
每种动作最终返回一组插件链,以 JSAPI 为例(同样适用于 App、H5、小程序、Native、合单,仅中间的业务插件不同):
StartPlugin::class, // 请求生命周期开始 JsapiClosePlugin::class, // 组装关单请求 URL 与请求体 AddPayloadBodyPlugin::class, // 序列化请求体 AddPayloadSignaturePlugin::class, // 生成微信支付 V3 签名 AddRadarPlugin::class, // 平台证书管理(验签公钥等) VerifySignaturePlugin::class,// 校验响应签名 ResponsePlugin::class, // 统一响应处理 ParserPlugin::class, // 解析最终结果该插件链可通过 tests/Shortcut/Wechat/CloseShortcutTest.php 中的testDefault()、testCombine()、testCombineParams()等用例逐一验证。若传入不支持的_action(如'virtual'、'foo'),match会落入default分支,抛出InvalidParamsException,错误码为Exception::PARAMS_SHORTCUT_ACTION_INVALID(测试用例testVirtual()、testFoo()已覆盖此行为)。
五、底层 ClosePlugin 实现细节
5.1 普通关单:Jsapi / App / Mini 插件
以 src/Plugin/Wechat/V3/Pay/Jsapi/ClosePlugin.php 为例(App 与 Mini 的实现完全一致):
- 校验
out_trade_no:从rocket->getPayload()取出out_trade_no,为空则抛出InvalidParamsException(错误码PARAMS_NECESSARY_PARAMS_MISSING),提示信息形如“Jsapi 关闭订单,参数缺少out_trade_no”。 - 组装请求端点:
- 直连商户:
POST /v3/pay/transactions/out-trade-no/{out_trade_no}/close - 服务商模式:
POST /v3/pay/partner/transactions/out-trade-no/{out_trade_no}/close
- 直连商户:
- 按模式注入商户号:
- 直连模式:请求体只含
mchid(取自配置); - 服务商模式(
Pay::MODE_SERVICE === $config->getMode()):请求体为sp_mchid(配置)与sub_mchid(优先取请求参数中的sub_mchid,缺省回落到配置的sub_mchid)。
- 直连模式:请求体只含
从源码结构看,H5、Native 的ClosePlugin与上述实现保持一致,均校验out_trade_no并请求同一关单端点(对应文件位于 src/Plugin/Wechat/V3/Pay/H5/ClosePlugin.php 与 src/Plugin/Wechat/V3/Pay/Native/ClosePlugin.php)。
5.2 合单关单:Combine 插件
src/Plugin/Wechat/V3/Pay/Combine/ClosePlugin.php 的处理略有不同:
- 校验
combine_out_trade_no:缺失时抛出“合单关单,参数缺少combine_out_trade_no”的InvalidParamsException。 - 组装请求端点:
POST /v3/combine-transactions/out-trade-no/{combine_out_trade_no}/close(直连与服务商模式 URL 相同)。 - 自动注入
combine_appid:优先取请求参数中的combine_appid,缺省回落为配置中的mp_app_id(公众号 appid)。 - 参数清理:通过
exceptPayload('combine_out_trade_no')将combine_out_trade_no从最终请求体中剔除(它只用于拼接 URL),而sub_orders等其他字段原样保留透传。
六、参数校验与异常处理小结
关闭订单过程中可能出现的两类核心异常(均由 src/Exception/Exception.php 定义错误码):
| 场景 | 异常信息 | 错误码 |
|---|---|---|
普通关单缺out_trade_no | “Jsapi/App/Mini 关闭订单,参数缺少out_trade_no” | PARAMS_NECESSARY_PARAMS_MISSING |
合单关单缺combine_out_trade_no | “合单关单,参数缺少combine_out_trade_no” | PARAMS_NECESSARY_PARAMS_MISSING |
_action非法 | “不支持的_action [...]” | PARAMS_SHORTCUT_ACTION_INVALID |
建议在实际业务中先通过查询订单(Pay::wechat()->find())确认订单状态再决定是否关单,避免对已支付或已关闭的订单重复发起关闭请求。
七、总结
Pay::wechat()->close($order)以极简的数组入参覆盖了微信支付 V3 全部六种关单场景:默认 JSAPI 关单、通过_action切换 App/H5/小程序/Native,以及通过combine_out_trade_no/sub_orders自动路由合单关单。其背后是CloseShortcut的轻量路由 + 各场景ClosePlugin的端点组装,配合插件链自动完成签名、验签、证书与响应解析。理解 src/Shortcut/Wechat/CloseShortcut.php、src/Action/WechatAction.php 与各 ClosePlugin 源码,即可在直连与服务商两种模式下稳定地集成关单能力。
- 金融科技
- 后端
【免费下载链接】pay
可能是我用过的最优雅的 Alipay/WeChat/Douyin/Unipay/江苏银行 的支付 SDK 扩展包了
相关推荐
支付宝关闭订单实战指南:Yansongda Pay close 方法全场景解析
支付宝关闭订单实战指南:Yansongda Pay close 方法全场景解析 本文基于 Yansongda Pay(本仓库)讲解支付宝「关闭订单」能力:如何通
金融科技后端微信支付订单关闭接口实战:Yansongda Pay SDK 的 close 方法详解与插件链源码解析
微信支付订单关闭接口实战:Yansongda Pay SDK 的 close 方法详解与插件链源码解析 本指南以仓库文档 web/docs/v2/wechat/
金融科技后端【特别福利】 yansongda/pay 微信支付关闭订单异常问题解析
【特别福利】 yansongda/pay 微信支付关闭订单异常问题解析 前言:为什么你的微信支付关单总是失败? 还在为微信支付关闭订单时频繁出现的异常而困扰吗?
后端金融科技
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考