PIX 退款
对收到的 PIX 交易发起退款(全额或部分)。
端点
POST /api/external/pix/refund请求头
| 头 | 类型 | 必填 | 描述 |
|---|---|---|---|
Authorization | String | 是 | ApiKey {client_id}:{client_secret} |
Content-Type | String | 是 | application/json |
hmac | String | 是 | body 的 HMAC-SHA512 签名(了解详情) |
Idempotency-Key | String | 否 | 避免重复处理的唯一键(最大 256 字符) |
X-Key-Case | String | 否 | 设为 camelCase 以使用 camelCase 接收响应字段(默认 snake_case) |
请求体
| 字段 | 类型 | 必填 | 描述 | 示例 |
|---|---|---|---|---|
original_transaction_id | String | 是 | 收到的原始 PIX 交易 ID(见下方别名) | "7popu57v6us7p6pcicgq12345" |
amount | Integer | 否 | 请求中的退款值,以 centavos(分) 为单位。省略则退全额。 | 3000(R$ 30,00) |
reason | String | 否 | BACEN 退款代码(见下表)。默认:MD06。本地不验证 — 直接发送到 BACEN。 | "MD06" |
description | String | 否 | 退款描述。在 BACEN 中用作 return_reason(最多 140 字符)。默认:"Devolução PIX"。 | "Devolução solicitada pelo cliente" |
接受的别名
为了集成灵活性,后端接受每个字段的多个名称(第一个找到的获胜):
original_transaction_id(规范)original_e2e_id— 接受原始交易的 E2E ID(前缀E)originalTransactionId— camelCasetransaction_id— 回退end_to_end_id— 回退
reason(规范)return_code— 直接别名
所有权验证
后端验证原始交易是否属于与调用端点的 API Key 相同的 merchant。如果您尝试退回另一个 merchant 的交易,您会收到 HTTP 404 "original transaction not found" — 与不存在交易的响应相同(出于安全考虑)。
货币值(请求 × 响应)
请求中的 amount 以 centavos(分) 为单位(R$ 1,00 = 100)。响应和 webhook 中的 amount 以 subcentavos / 基础单位 为单位(R$ 1,00 = 10000)。示例:发送 3000 表示退款 R$ 30,00;响应返回 300000。
绝不发送 float/decimal。请求始终发送 centavos 整数;展示响应金额时除以 10,000。
部分退款 -- 如何跟踪剩余值
对于部分退款,提供小于原始值的 amount。同一笔交易的退款总和不能超过收到的原始值。
后端在 webhook pix.refund.completed 和 pix.return.received 中通过 total_refunded 和 remaining_refundable 跟踪累计值:
total_refunded= 已为该原始end_to_end_id执行的所有退款之和remaining_refundable=amount_original - total_refunded(还可以退回多少)is_partial= 如果此退款是部分的,则为true
在进行第二次部分退款之前,请查询最后的 pix.refund.completed webhook 以了解 remaining_refundable — 如果您超出,后端返回 HTTP 422 "Valor da devolução excede o valor original da transação"。
退款代码
| 代码 | 描述 |
|---|---|
MD06 | 双方协议退款 |
BE08 | 欺诈 |
AM09 | 金额错误 |
SL02 | 清算错误 |
RR04 | 交易未被识别(BACEN) |
FR01 | 收款方端报告的欺诈(MED 流程中观察到的 BACEN 代码) |
reason 代码的验证
Owem 不在本地验证 reason 字段。代码通过 PACS.004 转发到 BACEN。
- PACS.004 退款的有效 BACEN 代码:
MD06、BE08、AM09、SL02、RR04、FR01。 - webhook
pix.return.received中的友好描述:后端为MD06、BE08、FR01、SL02维护本地映射(return_reasons)。此映射外的代码(例:RR04、AM09)在 webhook 中回退到通用描述"Return from counterparty ({code})"。
控制器还接受 return_code 作为 reason 的别名。
如果您发送非 BACEN 代码,退款稍后会被 BACEN 拒绝(通过 webhook pix.payout.failed 带填充的 reason_code 的 422 异步)。
示例
BODY='{"amount":3000,"description":"Devolução acordo","original_transaction_id":"7popu57v6us7p6pcicgq12345","reason":"MD06"}'
HMAC=$(echo -n "$BODY" | openssl dgst -sha512 -hmac "$CLIENT_SECRET" | awk '{print $2}')
curl -X POST https://api.owem.com.br/api/external/pix/refund \
-H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/json" \
-H "hmac: $HMAC" \
-d "$BODY"成功响应 -- 202(异步)
最常见路径 — 退款已接受并排队。
{
"worked": true,
"transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
"end_to_end_id": "D24313102202604071509K14UmbMt6ck",
"amount": 300000,
"status": "accepted",
"detail": "Devolucao processada"
}HTTP 202 — 通过 GET /transactions/:id(使用返回的 transaction_id)或等待 webhook pix.refund.completed / pix.payout.returned 跟踪结果。
带前缀 D 的 E2E ID
与普通的 PIX OUT 交易(E2E 前缀 E)不同,退款的 E2E 带前缀 D(来自 "Devolução")。此前缀标识 BACEN 中的 ISO 20022 pacs.004 类型。内部 transaction_id 也带前缀 PIXRET 以便识别。
成功响应 -- 200(快速通道)
罕见路径 — 退款在 HTTP 响应返回前同步结算。
{
"worked": true,
"transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
"end_to_end_id": "D24313102202604071509K14UmbMt6ck",
"amount": 300000,
"status": "settled",
"detail": "Devolução liquidada"
}| 字段 | 类型 | 描述 |
|---|---|---|
worked | Boolean | true 表示请求已被接受 |
transaction_id | String | 退款标识符(前缀 PIXRET)。用于查询状态。 |
end_to_end_id | String | SPI/BACEN 中退款的 End-to-End ID(前缀 D,不是 E) |
amount | Integer | 退款值,以子分(÷ 10.000 得到雷亚尔)。300000 = R$ 30,00 |
status | String | accepted(HTTP 202 — 处理中)或 settled(HTTP 200 — 已结算)。POST 此字段中绝不为 processing。 |
detail | String | 描述消息 |
错误响应 (404)
{
"worked": false,
"errors": {
"not_found": "Transação original não encontrada"
}
}错误响应 (422) -- 余额不足
{
"worked": false,
"errors": {
"unprocessable_entity": "insufficient balance"
}
}当安全余额(min(TB, PG) — 见 余额)小于请求的 amount 时发生。退款要求来源账户有 ≥ 要退回的值的余额。
错误响应 (422) -- 超出值
{
"worked": false,
"errors": {
"unprocessable_entity": "Valor da devolução excede o valor original da transação"
}
}当退款总额(当前 + 之前的)会超过原始交易的 amount 时发生。允许部分退款,但总和不能超过收到的值。
错误响应 (400) -- original_transaction_id 缺失
{
"worked": false,
"errors": {
"bad_request": "original_transaction_id is required"
}
}错误响应 (401)
{
"worked": false,
"errors": {
"unauthorized": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
}
}退款期限(BACEN)
由 BACEN 定义的期限(Owem 后端本地不强制执行 — 直接发送到 SPI 并在那里验证):
MD06(协议):收到原始交易后最多 90 天BE08(欺诈):遵循 BACEN 监管 MED 期限(争议流程,见 违规)FR01(报告的欺诈):与 MED BACEN 相同的期限- 其他代码(
AM09、SL02、RR04):BACEN 案例到案例的验证;对于操作逻辑,视为最多 30 天
后端不阻止逾期退款 — 它们在 POST 中通过(HTTP 202 accepted),稍后由 BACEN 通过特定 reason_code 的 webhook pix.payout.failed 拒绝。如果您需要 fail-fast,您的系统必须在调用 API 前验证原始交易的年龄。
PACS.004 x MED:由 BACEN 违规发起的退款
如果您发起的退款是 BACEN 违规的结果(通过 pix.refund.requested 通知),不要调用此端点。当决定 analysis_result=AGREED(未在期限内提交抗辩或通过 merchant portal 手动接受)时,后端会自动执行 MED 退款。并行调用 POST /pix/refund 可能产生重复。
此端点用于您发起的自愿退款(例:退回多收的 PIX、取消销售、商业调整)。参见 违规(完整流程) 区分两种场景。