Reembolsos
Emite reembolsos totales o parciales sobre transacciones capturadas, haz seguimiento del estado del ciclo de vida del reembolso y concilia los importes reembolsados con la API de Tokeflow.
Un reembolso devuelve los fondos de una transacción que ya ha sido capturada. Con Tokeflow emites reembolsos contra una única API normalizada: la capa de orquestación transmite la solicitud al proveedor de pagos conectado, hace seguimiento del resultado y lo refleja tanto en el registro del reembolso como en la transacción principal.
Tokeflow estandariza los reembolsos entre proveedores: una sola forma de solicitud, un solo ciclo de vida de estado, un solo modelo de conciliación. El proveedor de pagos conectado realiza el movimiento efectivo de los fondos.
Tokeflow es una capa de orquestación de pagos. Registra, enruta y hace seguimiento de las solicitudes de reembolso y normaliza el resultado; el proveedor de pagos conectado ejecuta el reembolso y liquida los fondos.
Cómo funcionan los reembolsos
Todo reembolso es hijo de una transacción capturada. Puedes reembolsar el importe capturado restante completo, o emitir varios reembolsos parciales a lo largo del tiempo hasta que la transacción quede totalmente reembolsada.
- Reembolso total — omite
amountpara reembolsar todo el importe capturado restante. - Reembolso parcial — pasa
amount(en unidades menores) para reembolsar menos que el importe capturado restante. El saldo restante permanece capturado y puede reembolsarse más adelante. - Múltiples reembolsos parciales — repite los reembolsos parciales hasta que el importe capturado restante llegue a cero. Cada llamada crea un nuevo registro de reembolso.
Un reembolso solo puede iniciarse cuando la transacción principal está en estado authorized o refund_pending. Una vez que se inicia un reembolso, la transacción pasa a refund_pending mientras el proveedor confirma el resultado.
Los resultados de reembolso suelen ser asíncronos. La respuesta síncrona confirma que la solicitud fue aceptada (refund_pending); suscríbete a los webhooks para conocer el resultado final en lugar de hacer sondeo.
Ciclo de vida del reembolso
Un registro de reembolso tiene su propio estado, independiente de la transacción principal.
| Status | Significado |
|---|---|
pending | Reembolso iniciado, aún no confirmado por el proveedor. |
succeeded | Reembolso confirmado por el proveedor. |
failed | El reembolso falló en el proveedor; consulta failure_reason. |
cancelled | El reembolso se canceló antes de procesarse. |
Cómo se asigna el estado del reembolso a la transacción
La transacción principal refleja el estado agregado de los reembolsos:
- Mientras cualquier reembolso esté
pending, la transacción está enrefund_pending. - Cuando el importe capturado ha sido reembolsado en su totalidad, la transacción pasa a
refunded. - Cuando solo se ha reembolsado parte del importe capturado, la transacción pasa a
partially_refunded.
Reembolsos en duda y el saldo reembolsable
Un reembolso reserva su importe en cuanto se inicia: el saldo reembolsable que la API aplica cuenta los reembolsos pending además de los succeeded. Mientras un reembolso anterior aún se está confirmando, un segundo reembolso que llevaría el total por encima del importe capturado se rechaza con 400 (validation_error), de modo que una solicitud reintentada o enviada por duplicado no puede mover el mismo dinero dos veces.
Un reembolso solo sale de pending cuando el proveedor da una respuesta definitiva:
- Si el proveedor rechaza el reembolso, pasa a
failed(consultafailure_reason) y su importe reservado se libera — corrige la solicitud e inténtalo de nuevo. - Si el proveedor no devuelve ningún resultado definitivo — un timeout o un error del lado del proveedor — el reembolso permanece
pendingy la transacción permanecerefund_pending. El importe no se libera, porque los fondos pueden ya estar moviéndose; la resolución no es automática.
Como el resultado puede resolverse de forma asíncrona, suscríbete a los webhooks para el resultado final en lugar de reenviar el reembolso.
Endpoints
POST/api/v1/transactions/:id/refund
Reembolsa una transacción capturada. Admite reembolsos totales y múltiples reembolsos parciales.
Auth: clave de Organization (con merchant_id) o clave de Merchant. Ámbito requerido: transactions:write.
Reembolsa una transacción capturada previamente. Omite amount para un reembolso total del importe capturado restante, o pasa amount para un reembolso parcial.
La transacción debe estar en estado authorized o refund_pending, y el amount del reembolso no debe superar el importe capturado restante. Otros estados devuelven 422 business_rule_error; un importe superior al saldo restante devuelve 400 validation_error.
Parámetros de ruta
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Sí | ID de transacción (tx_…) a reembolsar. |
Parámetros de consulta
| Field | Type | Required | Description |
|---|---|---|---|
merchant_id | string | Condicional | Merchant de destino (mrc_…). Requerido al usar una clave de Organization; se infiere de una clave de Merchant. |
Cuerpo
| Field | Type | Required | Description |
|---|---|---|---|
amount | integer | No | Importe a reembolsar en unidades menores. Omítelo para un reembolso total. Debe ser ≥ 1 y ≤ el importe capturado restante. |
reason | enum | No | Motivo por el que se emitió el reembolso. Uno de duplicate, fraudulent, requested_by_customer. |
Envía un encabezado Idempotency-Key en las solicitudes de reembolso para que un reintento automático nunca emita un segundo reembolso. Reproducir la misma clave devuelve el resultado original. Consulta Idempotencia y límites de frecuencia.
curl -X POST "https://api.tokeflow.com/api/v1/transactions/tx_777/refund" \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3f1c9a7e-2b4d-4e0a-9c6f-7d1e2a3b4c5d" \
-d '{
"amount": 5000,
"reason": "requested_by_customer"
}'Para un reembolso total, omite amount:
curl -X POST "https://api.tokeflow.com/api/v1/transactions/tx_777/refund" \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Content-Type: application/json" \
-d '{ "reason": "duplicate" }'Respuesta 200 OK
La respuesta confirma que el reembolso fue aceptado y refleja los totales de conciliación en la transacción. Los campos monetarios están en unidades menores.
{
"success": true,
"data": {
"id": "tx_777",
"refund_id": "ref_abc123",
"status": "refund_pending",
"amount_captured": 15000,
"amount_refunded": 5000,
"total_refunded": 5000,
"updated_at": "2026-01-15T12:30:00.000Z"
},
"request_id": "req_8f3c2a1b9d4e",
"timestamp": "2026-01-15T12:30:00.000Z"
}| Field | Type | Description |
|---|---|---|
id | string | ID de la transacción principal (tx_…). |
refund_id | string | El registro de reembolso creado por esta solicitud (ref_…). |
status | enum | Estado de la transacción después de la solicitud — refund_pending. |
amount_captured | integer | Importe total capturado en la transacción, sin cambios por el reembolso. |
amount_refunded | integer | Importe reembolsado por esta solicitud. |
total_refunded | integer | Importe acumulado reembolsado en todos los reembolsos de la transacción. |
updated_at | string | Marca de tiempo ISO 8601 UTC de la actualización. |
Al reproducir una Idempotency-Key, este endpoint devuelve el resultado original con HTTP 200 en lugar de crear un nuevo reembolso.
GET/api/v1/transactions/:id/refunds
Enumera todos los reembolsos de una transacción, del más reciente al más antiguo.
Auth: clave de Organization (con merchant_id) o clave de Merchant. Ámbito requerido: transactions:read.
Úsalo para ver el historial de reembolsos, comprobar el estado de los reembolsos parciales o correlacionar los eventos de reembolso con las notificaciones de webhook.
Parámetros de ruta
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Sí | ID de transacción (tx_…). |
Parámetros de consulta
| Field | Type | Required | Description |
|---|---|---|---|
page | integer | No | Número de página (indexado desde 1). Predeterminado 1, mínimo 1. |
limit | integer | No | Elementos por página. Predeterminado 20, máximo 100. |
merchant_id | string | Condicional | Merchant de destino (mrc_…). Requerido con una clave de Organization. |
curl "https://api.tokeflow.com/api/v1/transactions/tx_777/refunds?page=1&limit=20" \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Accept: application/json"Respuesta 200 OK
{
"success": true,
"data": [
{
"id": "ref_def456",
"payment_transaction_id": "tx_777",
"amount": 4000,
"currency": "BRL",
"status": "succeeded",
"reason": "requested_by_customer",
"provider_refund_id": "rfnd_7Qx9KpL2",
"failure_reason": null,
"created_at": "2026-01-15T13:10:00.000Z",
"updated_at": "2026-01-15T13:11:42.000Z"
},
{
"id": "ref_abc123",
"payment_transaction_id": "tx_777",
"amount": 5000,
"currency": "BRL",
"status": "succeeded",
"reason": "requested_by_customer",
"provider_refund_id": "rfnd_3Mt0VbN8",
"failure_reason": null,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:31:05.000Z"
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 20,
"total": 2,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
},
"request_id": "req_5a2f7b1c8e0d",
"timestamp": "2026-01-15T13:12:00.000Z"
}GET/api/v1/transactions/:id/refunds/:refundId
Recupera un único reembolso.
Auth: clave de Organization (con merchant_id) o clave de Merchant. Ámbito requerido: transactions:read.
Úsalo en flujos de soporte — por ejemplo, para inspeccionar un reembolso tras una notificación de webhook, o cuando un reembolso falla o tarda en liquidarse.
Parámetros de ruta
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Sí | ID de transacción (tx_…). |
refundId | string | Sí | ID de reembolso (ref_…). |
Parámetros de consulta
| Field | Type | Required | Description |
|---|---|---|---|
merchant_id | string | Condicional | Merchant de destino (mrc_…). Requerido con una clave de Organization. |
curl "https://api.tokeflow.com/api/v1/transactions/tx_777/refunds/ref_abc123" \
-H "Authorization: Bearer sk_live_mer_…" \
-H "Accept: application/json"Respuesta 200 OK
{
"success": true,
"data": {
"id": "ref_abc123",
"payment_transaction_id": "tx_777",
"amount": 5000,
"currency": "BRL",
"status": "succeeded",
"reason": "requested_by_customer",
"provider_refund_id": "rfnd_3Mt0VbN8",
"failure_reason": null,
"created_at": "2026-01-15T12:30:00.000Z",
"updated_at": "2026-01-15T12:31:05.000Z"
},
"request_id": "req_9c4e1d7a2b6f",
"timestamp": "2026-01-15T12:32:00.000Z"
}El objeto de reembolso
| Field | Type | Description |
|---|---|---|
id | string | ID de reembolso (ref_…). |
payment_transaction_id | string | ID de la transacción principal (tx_…). |
amount | integer | Importe del reembolso en unidades menores. |
currency | string | Código de moneda ISO 4217 (p. ej. BRL). |
status | enum | pending, succeeded, failed o cancelled. |
reason | enum | null | duplicate, fraudulent o requested_by_customer. null si no se proporciona. |
provider_refund_id | string | null | Identificador opaco del reembolso en el proveedor conectado. Trátalo como una cadena opaca. null hasta que se asigne. |
failure_reason | string | null | Detalle legible del fallo cuando status es failed; de lo contrario, null. |
created_at | string | Marca de tiempo de creación ISO 8601 UTC. |
updated_at | string | Marca de tiempo de la última actualización ISO 8601 UTC. |
Errores
Los endpoints de reembolso usan el sobre de error estándar.
| HTTP | Type | Cuándo |
|---|---|---|
400 | validation_error | amount supera el importe capturado restante, o tiene un formato incorrecto. |
401 | authentication_error | Clave de API ausente o inválida. |
403 | authorization_error | La clave carece del ámbito requerido, o la transacción está fuera del ámbito de la clave. |
404 | not_found_error | Transacción o reembolso no encontrado, o no perteneciente al merchant. |
409 | conflict_error | Ya hay un reembolso en conflicto en curso. |
422 | business_rule_error | La transacción no está en estado authorized ni refund_pending. |
429 | rate_limit_error | Se superó el límite de frecuencia por clave; espera y reintenta. |
{
"error": {
"type": "business_rule_error",
"code": "TRANSACTION_NOT_REFUNDABLE",
"message": "Transaction must be in 'authorized' or 'refund_pending' status to be refunded.",
"details": { "current_status": "failed" },
"request_id": "req_2b8d4f6a1c3e",
"timestamp": "2026-01-15T12:40:00.000Z"
}
}Relacionado
- Transacciones — crea, captura e inspecciona transacciones.
- Webhooks — recibe los eventos
transaction.refundedytransaction.partially_refunded. - Idempotencia y límites de frecuencia — haz que las solicitudes de reembolso sean seguras de reintentar.
- Errores — sobre de error y tipos.