Skip to content

PIX 退款

对收到的 PIX 交易发起退款(全额或部分)。

端点

POST /api/external/pix/refund

请求头

类型必填描述
AuthorizationStringApiKey {client_id}:{client_secret}
Content-TypeStringapplication/json
hmacStringbody 的 HMAC-SHA512 签名(了解详情
Idempotency-KeyString避免重复处理的唯一键(最大 256 字符)
X-Key-CaseString设为 camelCase 以使用 camelCase 接收响应字段(默认 snake_case)

请求体

字段类型必填描述示例
original_transaction_idString收到的原始 PIX 交易 ID(见下方别名)"7popu57v6us7p6pcicgq12345"
amountInteger请求中的退款值,以 centavos(分) 为单位。省略则退全额。3000(R$ 30,00)
reasonStringBACEN 退款代码(见下表)。默认:MD06。本地不验证 — 直接发送到 BACEN。"MD06"
descriptionString退款描述。在 BACEN 中用作 return_reason(最多 140 字符)。默认:"Devolução PIX""Devolução solicitada pelo cliente"

接受的别名

为了集成灵活性,后端接受每个字段的多个名称(第一个找到的获胜):

  • original_transaction_id(规范)
    • original_e2e_id — 接受原始交易的 E2E ID(前缀 E
    • originalTransactionId — camelCase
    • transaction_id — 回退
    • end_to_end_id — 回退
  • reason(规范)
    • return_code — 直接别名

所有权验证

后端验证原始交易是否属于与调用端点的 API Key 相同的 merchant。如果您尝试退回另一个 merchant 的交易,您会收到 HTTP 404 "original transaction not found" — 与不存在交易的响应相同(出于安全考虑)。

货币值(请求 × 响应)

请求中的 amountcentavos(分) 为单位(R$ 1,00 = 100)。响应和 webhook 中的 amountsubcentavos / 基础单位 为单位(R$ 1,00 = 10000)。示例:发送 3000 表示退款 R$ 30,00;响应返回 300000

绝不发送 float/decimal。请求始终发送 centavos 整数;展示响应金额时除以 10,000。

部分退款 -- 如何跟踪剩余值

对于部分退款,提供小于原始值的 amount。同一笔交易的退款总和不能超过收到的原始值。

后端在 webhook pix.refund.completedpix.return.received 中通过 total_refundedremaining_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 代码MD06BE08AM09SL02RR04FR01
  • webhook pix.return.received 中的友好描述:后端为 MD06BE08FR01SL02 维护本地映射(return_reasons)。此映射外的代码(例:RR04AM09)在 webhook 中回退到通用描述 "Return from counterparty ({code})"

控制器还接受 return_code 作为 reason 的别名。

如果您发送非 BACEN 代码,退款稍后会被 BACEN 拒绝(通过 webhook pix.payout.failed 带填充的 reason_code 的 422 异步)。

示例

bash
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(异步)

最常见路径 — 退款已接受并排队。

json
{
  "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 响应返回前同步结算。

json
{
  "worked": true,
  "transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
  "end_to_end_id": "D24313102202604071509K14UmbMt6ck",
  "amount": 300000,
  "status": "settled",
  "detail": "Devolução liquidada"
}
字段类型描述
workedBooleantrue 表示请求已被接受
transaction_idString退款标识符(前缀 PIXRET)。用于查询状态。
end_to_end_idStringSPI/BACEN 中退款的 End-to-End ID(前缀 D,不是 E
amountInteger退款值,以子分(÷ 10.000 得到雷亚尔)。300000 = R$ 30,00
statusStringaccepted(HTTP 202 — 处理中)或 settled(HTTP 200 — 已结算)。POST 此字段中绝不为 processing
detailString描述消息

错误响应 (404)

json
{
  "worked": false,
  "errors": {
    "not_found": "Transação original não encontrada"
  }
}

错误响应 (422) -- 余额不足

json
{
  "worked": false,
  "errors": {
    "unprocessable_entity": "insufficient balance"
  }
}

当安全余额(min(TB, PG) — 见 余额)小于请求的 amount 时发生。退款要求来源账户有 ≥ 要退回的值的余额。

错误响应 (422) -- 超出值

json
{
  "worked": false,
  "errors": {
    "unprocessable_entity": "Valor da devolução excede o valor original da transação"
  }
}

当退款总额(当前 + 之前的)会超过原始交易的 amount 时发生。允许部分退款,但总和不能超过收到的值。

错误响应 (400) -- original_transaction_id 缺失

json
{
  "worked": false,
  "errors": {
    "bad_request": "original_transaction_id is required"
  }
}

错误响应 (401)

json
{
  "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 相同的期限
  • 其他代码(AM09SL02RR04: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、取消销售、商业调整)。参见 违规(完整流程) 区分两种场景。

Owem Pay Instituição de Pagamento — ISPB 37839059