跳到正文
PayHub开发者文档

创建退款

契约 v1.0.0

createRefund
POST/v1/refunds
所需权限refunds:write需要幂等键

对已成功的支付申请退款,资金退回原支付路径。merchant_refund_no 在商户、环境内唯一,并要求幂等键; 可退本金与资金的校验和占用在同一原子操作中完成,并发部分退款累计不超过可退本金。 币种继承原支付;全额退款也必须显式提交金额。商户资金不足返回 422 insufficient_refund_funds。 默认进入 pending_review 人工审核;结果未知时持续占用资金并查证,不自动重复创建。

请求头

请求头必填格式说明
Authorization是Bearer <API_KEY>

服务端密钥,只能在商户服务端使用;商户与环境由密钥推导。缺失、无效、撤销或过期返回 401。

Content-Type是application/json

请求体为 UTF-8 编码的 JSON。

Idempotency-Key是1–255 个可见 ASCII 字符

幂等键,1–255 个可见 ASCII 字符。作用域为商户、环境、方法与路径:同键且安全业务参数一致时复用原业务对象,参数冲突返回 409 idempotency_conflict。 一次提交使用一个键,网络重试沿用原键;换卡等新的提交使用新键。 同键并发时只有一个请求取得执行权,其余请求等待其事务提交后返回同一对象(200);等待超时返回 409 operation_in_progress(retryable=true),稍后用原键重试。 被 400、401、403、422 拒绝的请求不占用幂等键,修正后用新键提交。

请求体

JSON 对象 CreateRefundRequest

名称类型必填说明
payment_id
string必填

原支付号。

约束:长度 1–64

示例:pay_demo_001

merchant_refund_no
string必填

商户退款号,1-64 个字母、数字、点、下划线、冒号或横线;同一商户同一环境内唯一。

约束:格式 ^[A-Za-z0-9._:-]{1,64}$

示例:REFUND-DEMO-001

amount
integer(int64)必填

退款本金,最小货币单位;不得超过可申请退款本金。

约束:≥ 1

示例:4000

reason
string必填

退款原因。

约束:长度 1–256

示例:requested_by_customer

metadata
object否

商户自定义键值,最多 20 个键;键最长 40 字符,值为最长 200 字符的字符串。禁止放入卡号等敏感数据,平台会做检查并拒绝。

约束:最多 20 个键值最长 200

示例:{"business_order_type":"one_time_purchase"}

请求示例

curl
curl -X POST 'https://api.payhub.bbpays.site/v1/refunds' \
  -H 'Authorization: Bearer <TEST_SERVER_API_KEY>' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: <UNIQUE_OPERATION_KEY>' \
  -d '{
  "payment_id": "pay_demo_001",
  "merchant_refund_no": "REFUND-DEMO-001",
  "amount": 2500,
  "reason": "requested_by_customer",
  "metadata": {
    "ticket": "CS-DEMO-001"
  }
}'

需要替换的占位符

  • <TEST_SERVER_API_KEY>:测试环境服务端 API Key:在商户后台「API Key」页面创建
  • <UNIQUE_OPERATION_KEY>:你为这次提交生成的幂等键(如 UUID),网络重试沿用同一个

说明

服务端 API Key 只能在你的服务端使用,文档站不提供在线调试。获取测试 Key:商户后台 API Key(在新标签页打开商户后台)(测试环境;需要所有者、管理员或开发者)

响应

同键幂等复用,返回原退款的最新状态。

同键重放:返回原退款的最新状态
{
  "id": "ref_demo_001",
  "version": 1,
  "payment_id": "pay_demo_001",
  "merchant_refund_no": "REFUND-DEMO-001",
  "amount": 2500,
  "currency": "USD",
  "status": "processing",
  "reason": "requested_by_customer",
  "returned_amount": 0,
  "failure": null,
  "metadata": {
    "ticket": "CS-DEMO-001"
  },
  "created_at": "2026-09-24T02:00:00Z",
  "completed_at": null,
  "request_id": "req_demo_023"
}

响应字段

返回对象 字段说明见 对象小节。

名称类型必填说明
id
string必填

平台退款号。

示例:ref_demo_001

version
integer(int64)必填

对象版本,每次有效业务变更递增(状态、失败原因、退回金额)。比较版本来丢弃旧事件,不要按状态名排序。

约束:≥ 1

示例:1

payment_id
string必填

原支付号。

示例:pay_demo_001

merchant_refund_no
string必填

商户退款号,同一商户同一环境内唯一。

示例:REFUND-DEMO-001

amount
integer(int64)必填

退款本金,最小货币单位。

约束:≥ 1

示例:4000

currency
string必填

退款币种,继承原支付。

约束:格式 ^[A-Z]{3}$

示例:USD

status
string必填

退款状态。

  • pending_review:已受理,等待审核;保持资金占用
  • processing:已批准并提交,或正在确认上游结果;保持占用
  • succeeded:上游已确认退款成功;占用转为正式扣减
  • failed:已明确退款未成功;解除占用
  • rejected:审核驳回,未提交上游;解除占用
  • canceled:提交上游前被有权限的人员取消;解除占用
  • returned:曾确认成功的退款之后全部或部分资金被真实退回
  • pending_review:已受理,等待审核;保持资金占用
  • processing:已批准并提交,或正在确认上游结果;保持占用
  • succeeded:上游已确认退款成功;占用转为正式扣减
  • failed:已明确退款未成功;解除占用
  • rejected:审核驳回,未提交上游;解除占用
  • canceled:提交上游前被有权限的人员取消;解除占用
  • returned:曾确认成功的退款之后全部或部分资金被真实退回
  • 将来可能新增取值;不认识的状态不要当作成功。

示例:pending_review

reason
string必填

退款原因。

示例:requested_by_customer

returned_amount
integer(int64)必填

退款成功后被真实退回的本金累计。

约束:≥ 0

示例:0

failure
RefundFailure可为空必填

status 为 failed 或 rejected 时的原因,否则为 null。

code
string必填

失败原因码。

示例:refund_rejected

message
string必填

脱敏说明。

示例:退款申请未通过审核。

metadata
object否

商户自定义键值,最多 20 个键;键最长 40 字符,值为最长 200 字符的字符串。禁止放入卡号等敏感数据,平台会做检查并拒绝。

约束:最多 20 个键值最长 200

示例:{"business_order_type":"one_time_purchase"}

created_at
string(date-time)必填

受理时间。

示例:2026-09-23T10:00:00Z

completed_at
string(date-time)可为空必填

进入 succeeded、failed、rejected 或 canceled 的时间;尚未结束时为 null。

示例:null

request_id
string必填

本次请求的唯一标识,用于排查与支持。

示例:req_demo_001

可能的错误码

HTTP错误码何时出现如何处理
400invalid_request缺 Idempotency-Key;订单号含非法字符;payment_method_data 与 payment_method_id 同时提交;confirm=false 携带卡信息;confirm=true 缺 return_url;收银会话两组身份参数同时提交或都缺;缺少上游要求的 browser_info / billing_details 字段;游标、时间格式、limit 不合法按 error.field 修正后重新提交,不要自动重扣
400sensitive_data_rejecteddescription、metadata 中出现卡号样式的数字等删除敏感内容后重新提交;检查你的系统为何把卡数据写进这些字段
403capability_disabled未开通直接传卡;支付或退款申请能力被关闭;该币种未开放改用托管收银台或联系平台运营
404not_foundID 错误;用测试 Key 查询生产对象;查询平台异常退款(不通过开放 API 返回)核对 ID 与 Key 的环境;平台不会透露对象是否属于其他商户
409idempotency_conflict复用了键但金额、币种、订单号等不同属于集成缺陷:检查幂等键生成逻辑;不要改参数后重放原键;新操作使用新键
409operation_in_progress并发发送了同键请求稍后用同一个键重试,或查询 related_resource_id;不要换键
409refund_no_conflict换幂等键重复提交同一退款号查询 related_resource_id 指向的退款
422refund_amount_exceeds可申请退款本金 = 已确认扣款 − 已成功退款本金 − 未结束退款占用 − 争议独占本金;并发部分退款累计超额查询支付的 captured_amount、refunded_amount、refund_reserved_amount 后按可退金额重新申请
422insufficient_refund_funds资金尚未结算或已占用打款;例:支付 100.00、手续费 3.50、只有净额 96.50 时全额退款差 3.50message 给出差额,本次不生成退款单;线下补款并由平台财务登记后,或经平台批准动用你的保证金 / 冻结资金后(授权 7 天内有效),用同一商户退款号与金额、新的幂等键重新提交;平台不会自动缩减退款金额,也不替你垫付
422refund_not_allowed支付未成功;超过上游允许的退款期限;交易处于争议中且上游不允许退款按 message 处理;争议交易在商户后台争议页面处理
401unauthenticated通用未带 Authorization;Key 复制不完整;轮换后旧 Key 已过期;Key 被撤销检查凭据,在 商户后台 API Key 页面 核对 Key 状态与环境
403forbidden通用只读 Key 创建退款;getEvent 缺少事件所属业务的读权限;请求来源 IP 不在 Key 的白名单时返回更具体的 ip_not_allowed使用具备所需 scope 的 Key;不要绕过限制
429rate_limited通用商户、Key 维度的频率限制;换 Key 不能绕过商户总限额等待 Retry-After 秒后用原幂等键重试
500internal_error通用—用原幂等键重试或按商户订单号 / 退款号查询;不能换键重扣
502 / 503upstream_unavailable通用上游或平台依赖故障,平台无法确认本次请求是否已生效同上

注意事项

  • merchant_refund_no 在商户、环境内唯一;换键重复提交同一退款号返回 409 refund_no_conflict。
  • 币种继承原支付;全额退款也必须显式提交 amount。
  • 可申请退款本金 = 已确认扣款 − 已成功退款本金 − 未结束退款占用 − 争议独占本金,超出返回 422 refund_amount_exceeds。
  • 可退本金与商户资金是两个校验:资金不足时不受理、不生成退款单,返回 422 insufficient_refund_funds,message 中给出差额,error.details 另带 shortfall(差额,最小货币单位)与 currency。平台不会自动缩减退款金额,也不替你垫付。
  • 生产环境默认进入 pending_review 人工审核;测试环境默认自动批准,直接返回 processing。
  • 退款原路退回,不接受其他卡号;提交上游后结果未知时平台持续查证,不会自动重复创建。
这页有帮助吗?