Devolucion PIX
Inicia una devolucion (total o parcial) de una transaccion PIX recibida.
Endpoint
POST /api/external/pix/refundHeaders
| Header | Tipo | Obligatorio | Descripcion |
|---|---|---|---|
Authorization | String | Si | ApiKey {client_id}:{client_secret} |
Content-Type | String | Si | application/json |
hmac | String | Si | Firma HMAC-SHA512 del body (mas informacion) |
Idempotency-Key | String | No | Clave unica para evitar procesamiento duplicado (max 256 chars) |
X-Key-Case | String | No | Defina como camelCase para recibir los campos de la respuesta en camelCase (default es snake_case) |
Request Body
| Campo | Tipo | Obligatorio | Descripcion | Ejemplo |
|---|---|---|---|---|
original_transaction_id | String | Si | ID de la transaccion PIX original recibida (vea aliases abajo) | "7popu57v6us7p6pcicgq12345" |
amount | Integer | No | Valor a devolver en centavos en el request. Si se omite, devuelve el valor total de la transaccion original. | 3000 (R$ 30,00) |
reason | String | No | Codigo de devolucion BACEN (vea tabla abajo). Default: MD06. No validado localmente — enviado directo al BACEN. | "MD06" |
description | String | No | Descripcion de la devolucion. Usada como return_reason en el BACEN (hasta 140 caracteres). Default: "Devolução PIX". | "Devolución solicitada por el cliente" |
Aliases aceptados
Para flexibilidad de integracion, el backend acepta multiples nombres para cada campo (primero encontrado gana):
original_transaction_id(canonico)original_e2e_id— acepta el E2E ID (prefijoE) de la transaccion originaloriginalTransactionId— camelCasetransaction_id— fallbackend_to_end_id— fallback
reason(canonico)return_code— alias directo
Validacion de ownership
El backend valida si la transaccion original pertenece al mismo merchant de la API Key que esta llamando el endpoint. Si usted intenta devolver una transaccion de otro merchant, recibe HTTP 404 "original transaction not found" — misma respuesta de transaccion inexistente (por seguridad).
Valores monetarios (request × response)
El amount del request es en centavos (R$ 1,00 = 100). El amount de la response y de los webhooks es en subcentavos / unidades base (R$ 1,00 = 10000). Ejemplo: envie 3000 para devolver R$ 30,00; la response retorna 300000.
NUNCA envie float/decimal. Siempre envie entero en centavos en el request y divida valores de respuesta por 10.000 para mostrar en reales.
Devolucion parcial -- como acompanar el valor remanescente
Para devolucion parcial, informe un amount menor que el valor original. El valor total de las devoluciones de una misma transaccion no puede exceder el valor original recibido.
El backend rastrea el acumulado en total_refunded y remaining_refundable en los webhooks pix.refund.completed y pix.return.received:
total_refunded= suma de todas las devoluciones ya ejecutadas para aquelend_to_end_idoriginalremaining_refundable=amount_original - total_refunded(cuanto aun puede ser devuelto)is_partial=truesi esa devolucion fue parcial
Antes de hacer una segunda devolucion parcial, consulte el ultimo webhook pix.refund.completed para saber el remaining_refundable — si usted excede, el backend retorna HTTP 422 "Valor da devolução excede o valor original da transação".
Codigos de Devolucion
| Codigo | Descripcion |
|---|---|
MD06 | Devolucion por acuerdo entre las partes |
BE08 | Fraude |
AM09 | Valor incorrecto |
SL02 | Error de liquidacion |
RR04 | Transaccion no reconocida (BACEN) |
FR01 | Fraude reportada en la ponta receptora (codigo BACEN observado en flujos MED) |
Validacion del reason code
Owem no valida el campo reason localmente. El codigo es encaminado al BACEN via PACS.004.
- Codigos BACEN validos para devolucion PACS.004:
MD06,BE08,AM09,SL02,RR04,FR01. - Descripciones amigables en el webhook
pix.return.received: el backend mantiene un mapa local (return_reasons) paraMD06,BE08,FR01,SL02. Codigos fuera de ese mapa (ej:RR04,AM09) caen en descripcion generica"Return from counterparty ({code})"en el webhook.
El controller tambien acepta return_code como alias de reason.
Si usted envia un codigo no-BACEN, la devolucion sera rechazada por el BACEN posteriormente (422 asincronico via webhook pix.payout.failed con reason_code llenado).
Ejemplo
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"Respuesta de Exito -- 202 (async)
Camino mas comun — devolucion aceptada y encolada.
{
"worked": true,
"transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
"end_to_end_id": "D24313102202604071509K14UmbMt6ck",
"amount": 300000,
"status": "accepted",
"detail": "Devolucao processada"
}HTTP 202 — acompane el resultado via GET /transactions/:id (use el transaction_id retornado) o aguarde el webhook pix.refund.completed / pix.payout.returned.
E2E ID con prefijo D
Diferente de una transaccion PIX OUT comun (E2E prefijo E), una devolucion tiene E2E con prefijo D (de "Devolucion"). Ese prefijo identifica el tipo ISO 20022 pacs.004 en el BACEN. El transaction_id interno tambien tiene prefijo PIXRET para facilitar identificacion.
Respuesta de Exito -- 200 (fast-track)
Camino raro — devolucion liquidada sincronicamente antes de que la respuesta HTTP volviera.
{
"worked": true,
"transaction_id": "PIXRETD24313102202604071509K14UmbMt6ck",
"end_to_end_id": "D24313102202604071509K14UmbMt6ck",
"amount": 300000,
"status": "settled",
"detail": "Devolução liquidada"
}| Campo | Tipo | Descripcion |
|---|---|---|
worked | Boolean | true indica que la solicitud fue aceptada |
transaction_id | String | Identificador de la devolucion (prefijo PIXRET). Use para consultar status. |
end_to_end_id | String | End-to-End ID de la devolucion en el SPI/BACEN (prefijo D, no E) |
amount | Integer | Valor de la devolucion en subcentavos (÷ 10.000 para reales). 300000 = R$ 30,00 |
status | String | accepted (HTTP 202 — en procesamiento) o settled (HTTP 200 — liquidada). Nunca processing en este campo del POST. |
detail | String | Mensaje descriptivo |
Respuesta de Error (404)
{
"worked": false,
"errors": {
"not_found": "Transação original não encontrada"
}
}Respuesta de Error (422) -- Saldo insuficiente
{
"worked": false,
"errors": {
"unprocessable_entity": "insufficient balance"
}
}Ocurre cuando el saldo seguro (min(TB, PG) — ver Saldo) es menor que el amount solicitado. Devolucion exige que la cuenta origen tenga saldo ≥ valor a devolver.
Respuesta de Error (422) -- Valor excedido
{
"worked": false,
"errors": {
"unprocessable_entity": "Valor da devolução excede o valor original da transação"
}
}Ocurre cuando el total de devoluciones (actual + anteriores) excederia el amount de la transaccion original. Devoluciones parciales son permitidas, pero la suma no puede sobrepasar el valor recibido.
Respuesta de Error (400) -- original_transaction_id ausente
{
"worked": false,
"errors": {
"bad_request": "original_transaction_id is required"
}
}Respuesta de Error (401)
{
"worked": false,
"errors": {
"unauthorized": "Missing API key credentials. Use Authorization: ApiKey <client_id>:<client_secret>"
}
}Plazo para devolucion (BACEN)
Plazos definidos por el BACEN (no enforced localmente por el backend Owem — enviado directo al SPI y validado alli):
MD06(acuerdo): hasta 90 dias corridos despues del recibimiento de la transaccion originalBE08(fraude): siguiendo los plazos del MED regulatorio BACEN (flujo de contestacion, ver Infracciones)FR01(fraude reportada): mismo plazo MED BACEN- Demas codigos (
AM09,SL02,RR04): validacion caso-a-caso en el BACEN; para logica operacional, trate como plazo maximo de 30 dias
El backend no bloquea devoluciones fuera del plazo — pasan en el POST (HTTP 202 accepted) y son rechazadas despues por el BACEN via webhook pix.payout.failed con reason_code especifico. Su sistema debe validar la edad de la transaccion original antes de llamar la API si necesita fail-fast.
PACS.004 x MED: devoluciones iniciadas por infraccion BACEN
Si la devolucion que usted esta iniciando es consecuencia de una infraccion BACEN (notificacion via pix.refund.requested), no llame este endpoint. La devolucion MED es ejecutada automaticamente por el backend cuando el analysis_result=AGREED es decidido (defensa no enviada en el plazo O acatamiento manual via merchant portal). Llamar POST /pix/refund en paralelo puede generar duplicidad.
Este endpoint es para devoluciones voluntarias iniciadas por usted (ej: devolver un PIX recibido de mas, cancelar una venta, acuerdo comercial). Ver Infracciones (flujo completo) para distinguir los dos escenarios.