跳转到主要内容
退款端点允许商户为已成功的支付发起退款。退款分为两种模型:退款工单(请求)和退款记录(PSP 确认后写入的执行结果)。

业务规则

  • 退款必须在原始支付成功后 14 天内请求
  • 退款金额可以是部分金额> 0≤ payment.amount),但必须使用与支付相同的货币
  • 原始支付必须已经完全被 PSP 确认 — 在 payment webhook 到达后立即调用退款可能会瞬时失败;几秒后重试
  • 已有 pending 或 succeeded 退款的支付不能同时创建另一个退款工单
  • 商户通过服务端 API Key 提交退款时,工单自动批准直接进入 processing — 不经过人工审核

退款工单状态值

状态商户视角含义
pending等待 Waffo 审核(仅 buyer 自主发起时可见)
approved已批准,即将分发到 PSP
rejected已拒绝 — 通过 GraphQL 查询 rejectReason
processing已分发到 PSP,等待上游完成
succeeded资金已返还给消费者
failedPSP 返回失败 — 可重新审核并重新提交
商户发起的退款会跳过 pendingapproved,创建后直接显示为 processing

业务侧标识 — 关联你的业务记录

你可以在两个创建端点上各自附加一个业务侧标识(最大 128 字符),对外用扁平 key 命名,以便同一份载荷同时承载两者不产生歧义:
创建端点请求体字段落库为
checkout/create-sessionorderMerchantExternalIdorders.merchant_external_id(首笔 + 订阅续期均继承)
refund-ticket/create-ticketrefundTicketMerchantExternalIdrefund_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.orderMerchantExternalIdrefund.refundTicketMerchantExternalId(与 webhook 载荷同名)。按退款工单业务号过滤执行的退款,使用 refunds(filter: { refundTicketMerchantExternalId: ... })