多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

yansongda/pay Airwallex 退款实战:从参数到源码的完整解析

yansongda/pay Airwallex 退款实战:从参数到源码的完整解析 金融科技后端【免费下载链接】pay可能是我用过的最优雅的 Alipay/WeChat/Douyin/Unipay/江苏银行 的支付 SDK 扩展包了项目地址https://gitcode.com/gh_mirrors/pa/pay点击查看免费下载Airwallex 是国际知名的跨境支付服务商其 Payment Intents 模式与 Stripe 类似退款需要基于原始payment_intent_id发起。本篇文章以 web/docs/v3/airwallex/refund.md 为核心指南结合 yansongda/pay 开源仓库的源码与测试用例系统讲解 Airwallex 退款接口的调用方法、全部参数语义、底层插件链路与常见异常处理。读完本文你将掌握用一行Pay::airwallex()-refund($order)完成完整退款流程并能从源码层面理解 SDK 的鉴权、请求组装与响应校验机制。方法签名与返回值原文档给出的调用契约非常简洁方法名参数返回值refundarray $orderCollection其中refund方法由 Airwallex Provider 提供参数为退款订单数组返回值是Yansongda\Supports\Collection类型的集合对象可直接链式调用get()、toArray()等方法取用退款结果。从源码看该方法定义于 src/Provider/Airwallex.php其实现逻辑为public function refund(array $order): Collection|Rocket { Event::dispatch(new MethodCalled(Pay::PROVIDER_AIRWALLEX, __METHOD__, $order, null)); return $this-__call(refund, [$order]); }即每次调用refund()都会先触发MethodCalled事件供 src/Event/MethodCalled.php 对应的监听器做埋点、日志或风控随后通过魔术方法__call动态装载RefundShortcut完成整个请求流程。快速上手最小退款示例沿用原文档的完整示例先初始化配置再发起退款use Yansongda\Pay\Pay; Pay::config($config); $order [ payment_intent_id int_xxx, amount 10, // reason requested_by_customer, // metadata [ // refund_no R20260414001, // ], ]; $result Pay::airwallex()-refund($order);其中$config为 Airwallex 渠道的完整配置详见下文「前置条件Airwallex 配置」。执行成功后$result中会包含 Airwallex 返回的refund_id、status、amount、currency等退款单信息业务侧应将其落库并与原始支付单关联供后续退款查询与对账使用。参数说明与源码级解析原文档明确了四个核心参数其中payment_intent_id为必填。结合 RefundPlugin 的实现参数语义可以进一步细化为下表参数必填类型说明payment_intent_id是string待退款的 Payment Intent ID即下单成功时返回的int_开头标识id二选一stringpayment_intent_id的别名传入后会被自动映射为payment_intent_idamount否number退款金额。不传时按 Airwallex 平台规则处理部分场景支持全额退款reason否string退款原因例如requested_by_customer客户申请metadata否array自定义扩展信息可用于关联业务订单号如[refund_no R20260414001]request_id否string请求幂等 ID不传时 SDK 自动生成 UUID v4必填参数校验RefundPlugin 在组装请求时会优先读取payment_intent_id若为空则回退读取id$paymentIntentId $payload-get(payment_intent_id, $payload-get(id)); if (empty($paymentIntentId)) { throw new InvalidParamsException( Exception::PARAMS_NECESSARY_PARAMS_MISSING, 参数异常: Airwallex 退款缺少必要参数 -- [payment_intent_id] or [id] ); }也就是说两种写法等价// 写法一 $result Pay::airwallex()-refund([payment_intent_id int_xxx, amount 10]); // 写法二使用 id 别名 $result Pay::airwallex()-refund([id int_xxx, amount 10]);这一行为在测试 tests/Plugin/Airwallex/V1/Pay/RefundPluginTest.php 中得到验证testUseIdAndOptionalFields传入id后断言最终请求负载中的payment_intent_id被正确映射。请求组装细节校验通过后插件会组装退款请求负载并过滤掉所有null值字段避免向 Airwallex 发送空参数$rocket-mergePayload(array_filter([ _method POST, _url /api/v1/pa/refunds/create, request_id $payload-get(request_id, self::getAirwallexRequestId()), payment_intent_id $paymentIntentId, amount $payload-get(amount), reason $payload-get(reason), metadata $payload-get(metadata), ], static fn ($value) !is_null($value)));要点如下接口路径POST /api/v1/pa/refunds/create对应 Airwallex 官方 Refunds API幂等设计request_id默认由Str::uuidV4()生成见 src/Traits/AirwallexTrait.php保证同一退款请求不会因网络重试被重复受理业务侧如需自定义幂等键可显式传入request_id金额单位amount遵循 Airwallex 平台默认的最小货币单位约定建议与下单时的币种、精度保持一致。前置条件Airwallex 配置退款属于需要身份认证的商户 API必须先配置好 Airwallex 渠道参数详见 web/docs/v3/quick-start/airwallex.mduse Yansongda\Pay\Pay; $config [ airwallex [ default [ // 「必填」Airwallex Client ID client_id , // 「必填」Airwallex API Key api_key , // 「必填」Webhook Secret用于回调验签 webhook_secret , // 「选填」支付完成后的返回地址 return_url https://example.com/airwallex/return, // 「选填」Airwallex API 版本 api_version 2024-06-14, // 「选填」平台模式代商户调用时使用 // on_behalf_of open_id_xxx, // 「选填」默认为正式模式。可选值 // MODE_NORMAL: 正式环境 // MODE_SANDBOX: 沙箱环境 mode Pay::MODE_NORMAL, ], ], ]; Pay::config($config);配置校验逻辑位于 src/Config/AirwallexConfig.phpclient_id与api_key为必填项缺失时抛出InvalidConfigException。mode决定请求的 Base URL见 src/Provider/Airwallex.phpMODE_NORMAL/MODE_SERVICEhttps://api.airwallex.comMODE_SANDBOXhttps://api-demo.airwallex.com退款开发调试阶段务必使用沙箱环境避免误退真实资金。底层调用链RefundShortcut 插件管道Pay::airwallex()-refund($order)并非直接发 HTTP 请求而是通过 Artful 管道Pipeline串联一组插件。插件链定义于 src/Shortcut/Airwallex/RefundShortcut.phpreturn [ StartPlugin::class, // 初始化 Rocket ObtainAccessTokenPlugin::class, // 获取并注入 Access Token RefundPlugin::class, // 组装退款请求参数 AddPayloadBodyPlugin::class, // 将负载序列化为请求体 AddRadarPlugin::class, // 构建 PSR-7 RequestURL 鉴权头 ResponsePlugin::class, // 校验 HTTP 响应状态码 ParserPlugin::class, // 解析响应为 Collection ];1. 获取 Access TokenObtainAccessTokenPlugin 调用getAirwallexAccessToken()src/Traits/AirwallexTrait.php若配置中已有未过期的accessToken则直接复用否则通过/api/v1/authentication/login用client_idapi_key换取新令牌并将过期时间提前 60 秒作为安全余量缓存回配置实现同一进程内多请求复用。2. 组装请求与鉴权头AddRadarPlugin 根据负载构建最终 HTTP 请求其中鉴权方式二选一客户端模式_auth_type client使用x-client-idx-api-key请求头常规模式使用Authorization: Bearer token。同时会按配置附带x-api-version如2024-06-14与平台代调用的x-on-behalf-of头。3. 响应校验与解析ResponsePlugin 会检查 HTTP 状态码非 2xx 响应直接抛出InvalidResponseException对应Exception::RESPONSE_CODE_WRONG。随后ParserPlugin将 JSON 响应解析为Collection返回给业务层。整套链路在测试 tests/Shortcut/Airwallex/RefundShortcutTest.php 中有完整断言。退款结果处理与后续操作退款结果字段退款成功后可从Collection中读取关键字段$refundId $result-get(refund_id); // Airwallex 退款单 IDref_ 开头 $status $result-get(status); // 退款状态 $amount $result-get(amount); // 退款金额 $currency $result-get(currency); // 退款币种Airwallex 的退款单状态通常包含pending受理中、succeeded成功、failed失败等建议结合异步 Webhook 回调确认最终结果而不是仅依赖同步响应。查询退款单如需主动查询退款进度可复用query方法并传入_action refund与退款单 ID参见 web/docs/v3/quick-start/airwallex.md 的「查询」章节$result Pay::airwallex()-query([ _action refund, refund_id ref_xxx, ]);退款回调验签Airwallex 会通过 Webhook 推送退款结果。SDK 提供verifyAirwallexWebhookSign()src/Traits/AirwallexTrait.php做 HMAC-SHA256 验签校验x-timestamp与x-signature请求头并检查时间戳是否在 300 秒5 分钟窗口内防重放攻击。需要先配置webhook_secret否则会抛出InvalidConfigException。收到回调后可调用$result Pay::airwallex()-callback(); return Pay::airwallex()-success();常见异常与排查异常触发场景解决建议InvalidParamsExceptionPARAMS_NECESSARY_PARAMS_MISSING未传payment_intent_id/id补全必填参数后再调用InvalidConfigExceptionCONFIG_AIRWALLEX_INVALID缺少client_id或api_key检查 src/Config/AirwallexConfig.php 对应配置InvalidResponseExceptionRESPONSE_CODE_WRONGAirwallex 返回非 2xx核对金额、币种、Payment Intent 状态与 API 权限退款金额超限退款金额大于可退余额先通过query查询原始 Payment Intent 的已支付金额与已退金额小结Airwallex 退款在 yansongda/pay 中是一个高度封装的调用业务侧只需传入payment_intent_id或id与可选的amount、reason、metadataSDK 内部通过「取 Token → 组装请求 → 鉴权 → 校验响应 → 解析」的插件管道完成全部脏活。理解 RefundPlugin 的参数映射与 RefundShortcut 的插件顺序有助于在排查退款异常、自定义幂等键或对接平台模式on_behalf_of时快速定位问题。生产环境建议开启沙箱模式联调并配合 Webhook 回调做退款状态的最终确认。赞分享金融科技后端【免费下载链接】pay可能是我用过的最优雅的 Alipay/WeChat/Douyin/Unipay/江苏银行 的支付 SDK 扩展包了项目地址https://gitcode.com/gh_mirrors/pa/pay点击查看免费下载相关推荐yansongda/pay退款功能实现详解从申请到查询全流程在现代化的电商系统中支付退款功能是保障用户体验和资金安全的重要环节。yansongda/pay 作为一款优雅的支付SDK扩展包为开发者提供了简洁高效的退款解金融科技后端QQ空间说说怎么导出到本地GetQzonehistory 新手向完整教程QQ空间说说怎么导出到本地GetQzonehistory 新手向完整教程 想找 2015 年的一条说说翻遍 QQ 空间也没有导出入口。GetQzonehis金融科技后端yansongda/pay银联支付实战从扫码到刷卡全流程yansongda/pay银联支付实战从扫码到刷卡全流程 想要快速集成银联支付功能yansongda/pay扩展包为你提供了 最优雅的解决方案 作为一款专金融科技后端上一篇React 应用启动初始化只执行一次模块级守卫 vs useEffect([]) 挂载副作用Mediago 实战下一篇不用C不用CythonCodon编译纯Python为高性能Python扩展模块完整教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表