退款端点允许商户为已成功的支付发起退款。退款分为两种模型:退款工单(请求)和退款记录(PSP 确认后写入的执行结果)。
业务规则
- 退款必须在原始支付成功后 14 天内请求
- 退款金额可以是部分金额(
> 0 且 ≤ payment.amount),但必须使用与支付相同的货币
- 原始支付必须已经完全被 PSP 确认 — 在 payment webhook 到达后立即调用退款可能会瞬时失败;几秒后重试
- 已有 pending 或 succeeded 退款的支付不能同时创建另一个退款工单
- 商户通过服务端 API Key 提交退款时,工单自动批准直接进入 processing — 不经过人工审核
退款工单状态值
| 状态 | 商户视角含义 |
|---|
pending | 等待 Waffo 审核(仅 buyer 自主发起时可见) |
approved | 已批准,即将分发到 PSP |
rejected | 已拒绝 — 通过 GraphQL 查询 rejectReason |
processing | 已分发到 PSP,等待上游完成 |
succeeded | 资金已返还给消费者 |
failed | PSP 返回失败 — 可重新审核并重新提交 |
商户发起的退款会跳过 pending 和 approved,创建后直接显示为 processing。
业务侧标识 — 关联你的业务记录
你可以在两个创建端点上各自附加一个业务侧标识(最大 128 字符),对外用扁平 key 命名,以便同一份载荷同时承载两者不产生歧义:
| 创建端点 | 请求体字段 | 落库为 |
|---|
checkout/create-session | orderMerchantExternalId | orders.merchant_external_id(首笔 + 订阅续期均继承) |
refund-ticket/create-ticket | refundTicketMerchantExternalId | refund_tickets.merchant_external_id(PSP 成功后继承到 refund 记录) |
端到端透传(请求体、webhook 载荷、GraphQL 类型,三处使用同一组扁平双 key 命名):
checkout/create-session (请求体: orderMerchantExternalId)
│
├──► OnetimeOrder / SubscriptionOrder . orderMerchantExternalId
├──► Payment . orderMerchantExternalId (首笔 + 订阅续期)
│
refund-ticket/create-ticket (请求体: refundTicketMerchantExternalId)
│
├──► RefundTicket . refundTicketMerchantExternalId
│
└──► Refund 同时承载两者:
• orderMerchantExternalId (从原始订单继承)
• refundTicketMerchantExternalId (从原始退款工单继承)
这些标识不是幂等键 — Waffo 不强制唯一性。同一 reference 可以附加到多条记录上,唯一性由你自己保证。
请求体、webhook 载荷、GraphQL 三处使用同一组字段名,所以你在 checkout / refund-ticket 创建时写入的值可以原样读回,无需做命名转换。
通过 GraphQL 使用这些标识查询 Payments、Refunds、Refund Tickets — 详见 GraphQL 订单与支付 指南。
创建退款工单
为已成功的支付请求退款。支持全额、部分退款。
重新提交退款工单
修订并重新提交被拒绝或失败的退款工单。
使用 GraphQL 查询退款工单和执行的退款记录。在 Refund 上,两个业务号以扁平字段暴露:refund.orderMerchantExternalId 和 refund.refundTicketMerchantExternalId(与 webhook 载荷同名)。按退款工单业务号过滤执行的退款,使用 refunds(filter: { refundTicketMerchantExternalId: ... })。