Códigos de error
Cómo leer un error
Sección titulada «Cómo leer un error»Hay tres capas distintas y conviene no confundirlas:
| Capa | Ejemplo | Qué significa |
|---|---|---|
| HTTP | 422 |
Cómo respondió nuestra API |
error.code |
payment_already_used |
Qué pasó, con una clave estable. Es lo que tu código debe leer |
| Validación | errors.payment_card.cvv |
Tu petición no cumple las reglas. Se arregla en tu código |
| Código de red | 51, M12, V68 |
La operación llegó a la red y la red decidió. No siempre se arregla reintentando |
Un pago rechazado no es un error de tu integración: la petición fue correcta y el
resultado fue «no». Devuelve 200 u 422 según el caso, y siempre trae el estado de la
transacción para que sepas en qué quedó.
El objeto error
Sección titulada «El objeto error»Todo error de la API v1 (HTTP 4xx) trae un objeto error con la misma forma, además
de los campos que ya traía (message, errors en un 422, changed/canceled/applied: false en las acciones de suscripción). Nada de lo anterior desaparece.
{ "message": "Este cobro ya tiene una transacción y no permite reintentos", "error": { "type": "invalid_request_error", "code": "payment_already_used", "message": "Este cobro ya tiene una transacción y no permite reintentos", "param": "payment.id" }}| Campo | Para qué |
|---|---|
error.type |
Familia del error. Ver la tabla de abajo |
error.code |
Clave estable en inglés. Decide con esta, no con el texto de message ni solo con el HTTP |
error.message |
Texto legible, para tu log. Puede cambiar de redacción |
error.param |
El campo que causó el error, cuando aplica (payment.id, metadata, el primer campo inválido de un 422). Si no, null |
error.type |
HTTP | Cuándo |
|---|---|---|
invalid_request_error |
400, 404, 409, 422 |
La petición no se puede aplicar: regla de negocio, recurso inexistente, validación |
authentication_error |
401 |
Falta el token o no es válido |
card_error |
402 |
La tarjeta fue rechazada al cobrar |
authorization_error |
403 |
Token válido, pero sin permiso para esto |
rate_limit_error |
429 |
Ya hay una operación igual en curso |
idempotency_error |
409 |
Choque de Idempotency-Key |
Si el error no tiene un código propio, error.code toma el de su HTTP:
| HTTP | error.code por defecto |
|---|---|
400 |
bad_request |
401 |
unauthenticated |
402 |
card_declined |
403 |
forbidden |
404 |
not_found |
405 |
method_not_allowed |
409 |
conflict |
422 |
validation_failed (con param = el primer campo inválido) |
429 |
too_many_requests |
Códigos de negocio
Sección titulada «Códigos de negocio»Los devuelve el checkout por API cuando el
par payment.id + payment.token no permite pagar.
error.code |
HTTP | param |
Qué pasó | Qué hacer |
|---|---|---|---|---|
invalid_payment_credentials |
403 | payment.token |
El id no existe o el token no le corresponde. Es el mismo código en ambos casos, para no revelar qué ids existen |
Revisar el par que guardaste de generate-payment |
payment_already_used |
403 | payment.id |
El cobro ya tiene una transacción (pagada o no) y no admite reintentos | Generar un cobro nuevo |
payment_already_paid |
403 | payment.id |
El cobro ya fue pagado | No cobrar de nuevo |
payment_in_progress |
403 | payment.id |
Tiene transacciones en progreso que agotan su límite | Esperar el resultado de la que está en curso |
payment_in_progress |
429 | — | Otra transacción de este cobro se está procesando ahora mismo (tipo rate_limit_error) |
Esperar y consultar el estado |
payment_expired |
403 | payment.id |
Pasó la fecha límite del cobro («Este cobro ya expiro») | Generar un cobro nuevo |
payment_inactive |
403 | payment.id |
El cobro fue desactivado | Generar un cobro nuevo |
Idempotencia
Sección titulada «Idempotencia»error.code |
HTTP | Qué pasó |
|---|---|---|
idempotency_key_reused |
409 | La Idempotency-Key ya se usó con parámetros distintos |
idempotency_key_in_progress |
409 | La petición original con esa clave todavía se está procesando |
Suscripciones
Sección titulada «Suscripciones»error.code |
Qué pasó |
|---|---|
subscription_already_canceled |
La suscripción ya estaba cancelada |
subscription_canceled |
La operación no aplica a una suscripción cancelada (cambio de plan, cambio de tarjeta, mode: "direct") |
subscription_inactive |
La suscripción está inactiva; usa la invitación o la renovación con pago |
subscription_same_plan |
La suscripción ya está en ese plan |
invitation_already_pending |
Ya hay una invitación de cambio de plan o de renovación pendiente |
subscriber_without_card |
El suscriptor no tiene tarjeta guardada |
subscriber_already_subscribed |
El suscriptor ya está suscrito a ese plan |
subscriber_has_active_subscriptions |
No se puede eliminar: tiene suscripciones activas |
subscriber_office_unresolved |
No se pudo determinar la sucursal del suscriptor |
plan_inactive |
El plan está inactivo |
plan_has_active_subscriptions |
No se puede eliminar: el plan tiene suscripciones activas |
group_has_plans |
No se puede eliminar: el grupo tiene planes |
coupon_invalid |
Cupón inválido o no disponible |
card_declined |
402 en un cambio de plan con always_invoice: el cobro inmediato del prorrateo fue rechazado y el plan no cambió. Trae además error.decline_code, el response_code de la transacción |
Estas respuestas conservan los campos que ya traían (changed: false, canceled: false,
applied: false), así que el código que los lee sigue funcionando.
Códigos HTTP
Sección titulada «Códigos HTTP»| Código | Cuándo | Qué hacer |
|---|---|---|
200 |
La operación se procesó. Revisa el estado del cuerpo: puede ser un rechazo | Leer status / success |
201 |
Se creó algo (una reserva autorizada, por ejemplo) | Guardar el id |
202 |
Estado indeterminado: no sabemos aún el resultado | No reintentar. Esperar el webhook o consultar |
400 |
Regla de negocio incumplida (por ejemplo, borrar un suscriptor con suscripción activa) | Leer error.code |
401 |
Falta el token o no es válido | Ver Autenticación |
402 |
Rechazo de tarjeta en un cobro inmediato de suscripción | Leer error.decline_code |
403 |
Token válido pero sin permiso, comercio deshabilitado, o cobro que ya no acepta pagos | Leer error.code |
404 |
El recurso no existe, o es de otro comercio, o del otro ambiente | Revisar el id y el tipo de token |
409 |
Conflicto: cobro ya aplicado, o Idempotency-Key reutilizada con otros parámetros |
Leer error.code |
422 |
Validación, o la red rechazó | Leer errors (o error.param) o el estado |
429 |
Ya hay una operación igual en curso | Esperar el resultado, no reintentar |
5xx |
Error de nuestro lado o de la red | Reintentable con espera |
Errores de validación
Sección titulada «Errores de validación»Siempre tienen la misma forma: un message con el primer error, un objeto errors con
todos, indexados por el nombre del campo, y el objeto error con
code: "validation_failed" y param apuntando al primer campo inválido.
{ "message": "El campo payment_card.cvv es obligatorio.", "errors": { "payment_card.cvv": ["El campo payment_card.cvv es obligatorio."], "customer_payer.email": ["El campo customer_payer.email debe ser un correo válido."] }, "error": { "type": "invalid_request_error", "code": "validation_failed", "message": "El campo payment_card.cvv es obligatorio.", "param": "payment_card.cvv" }}Las claves de errors usan notación de punto para los campos anidados. Úsalas para
marcar el campo exacto en tu formulario en lugar de mostrar un mensaje genérico.
Conflictos de idempotencia
Sección titulada «Conflictos de idempotencia»Las escrituras de suscripciones
aceptan el header Idempotency-Key. Cuando esa clave choca, responde 409 con
error.type: "idempotency_error" y un error.code propio.
{ "message": "La Idempotency-Key ya fue usada con parámetros distintos.", "error": { "type": "idempotency_error", "code": "idempotency_key_reused", "message": "La Idempotency-Key ya fue usada con parámetros distintos.", "param": null }}{ "message": "Una solicitud con esta Idempotency-Key aún está en proceso.", "error": { "type": "idempotency_error", "code": "idempotency_key_in_progress", "message": "Una solicitud con esta Idempotency-Key aún está en proceso.", "param": null }}Cuando en cambio repites exactamente la misma llamada con la misma clave, te
devolvemos la respuesta original con el header Idempotency-Replayed: true y sin volver
a ejecutar el cobro.
| Detalle | Comportamiento |
|---|---|
| Vigencia de la clave | 24 horas |
| Métodos afectados | POST, PUT, PATCH, DELETE |
| Rutas | Las escrituras de /api/v1/subscriptions/*, /api/v1/payment/* y /api/v1/mit/pre-authorizations/*. Ver el detalle en Convenciones |
| Alcance de la clave | Por comercio y usuario del token |
Respuestas 5xx |
No se memorizan, para que puedas reintentar un fallo transitorio |
Rechazos de la red
Sección titulada «Rechazos de la red»Los más frecuentes. El código llega en response_code de la transacción y, cuando no fue
aprobada, también en error.code con su message, su retryable y su action. Este
error de la transacción es distinto del objeto error de la API: viene
dentro de una respuesta 200 (o de un webhook), no en un 4xx. Llega igual en la
respuesta del checkout, en los webhooks de transacción y en el
webhook retry de suscripción (transaction.error).
{ "status": "Rechazada", "status_key": "rejected", "response_code": "51", "error": { "code": "51", "message": "…", "retryable": false, "action": "contact_issuer" }}description es el mensaje para el tarjetahabiente y error.message el que nombra la
causa para tu log. En los rechazos de esta tabla ambos coinciden con la causa y con
error.action: la description pide lo mismo que la acción. Los códigos que no están
aquí conservan el mensaje del catálogo de la red.
| Código | Qué pasó | description (tarjetahabiente) |
error.message (tu log) |
retryable |
action |
|---|---|---|---|---|---|
00 |
Aprobada | — | — | — | — |
05 |
El emisor no autorizó, sin causa específica | Tu banco no autorizó el pago. Comunícate con tu banco o usa otro medio de pago. | Transacción declinada por el emisor sin causa específica. | false |
contact_issuer |
14 |
Tarjeta inválida | Esta tarjeta no es válida. Usa otra tarjeta. | Transacción declinada. Tarjeta inválida. | false |
use_another_card |
41 |
Tarjeta reportada como extraviada | Esta tarjeta no se puede usar. Usa otra tarjeta. | Transacción declinada. Tarjeta reportada como extraviada. | false |
use_another_card |
43 |
Tarjeta bloqueada por el emisor | Esta tarjeta está bloqueada. Usa otra tarjeta. | Transacción declinada. Tarjeta bloqueada por el emisor. | false |
use_another_card |
51 |
Fondos insuficientes | Transacción declinada. Fondos insuficientes | Transacción declinada. Fondos insuficientes | false |
contact_issuer |
54 |
Tarjeta vencida | Tu tarjeta está vencida. Usa otra tarjeta. | Transacción declinada. Tarjeta vencida. | false |
use_another_card |
57 |
El emisor no permite este tipo de transacción | Esta tarjeta no permite este pago. Usa otra tarjeta. | Transacción declinada. Tipo de transacción no permitido para la tarjeta. | false |
use_another_card |
61 |
Excede el límite de monto de la tarjeta | El pago supera el límite de tu tarjeta. Comunícate con tu banco o usa otra tarjeta. | Transacción declinada. Excede el límite de monto. | false |
contact_issuer |
62 |
Tarjeta restringida | Esta tarjeta tiene restricciones. Usa otra tarjeta. | Transacción declinada. Tarjeta restringida. | false |
use_another_card |
65 |
Excede la frecuencia de transacciones | Superaste el número de pagos permitidos con esta tarjeta. Comunícate con tu banco o usa otra tarjeta. | Transacción declinada. Excede la frecuencia de transacciones. | false |
contact_issuer |
91 |
El emisor no está disponible | Tu banco no respondió. Intenta de nuevo en unos minutos. | Transacción rechazada. El emisor no está disponible. | true |
retry_later |
96 |
Falla del sistema del emisor o de la red | No pudimos procesar el pago. Intenta de nuevo en unos minutos. | Transacción rechazada. Falla del sistema del emisor o de la red. | true |
retry_later |
98 |
CVV inválido | El código de seguridad (CVV) es incorrecto. Verifica los datos de la tarjeta. | Transacción declinada. CVV inválido. | false |
check_card_data |
error.action toma uno de estos valores (los códigos de
reserva de cupo pueden traer su propio texto de acción):
action |
Qué hacer |
|---|---|
contact_issuer |
El cliente debe hablar con su banco, o usar otro medio |
use_another_card |
Pedir otra tarjeta |
retry_later |
Reintentar más tarde, con espera |
check_card_data |
Pedir al tarjetahabiente que revise número, fecha o CVV |
Puedes provocar cada causal en pruebas con las tarjetas por causal.
Qué es seguro reintentar
Sección titulada «Qué es seguro reintentar»error.retryable |
Ejemplos | Qué hacer |
|---|---|---|
false |
05, 14, 41, 43, 51, 54, 57, 61, 62, 65, 98 (fondos, vencida, bloqueada, límite, CVV) |
No reintentes. El resultado será el mismo y algunos emisores penalizan la insistencia. Pide otro medio de pago |
true |
19, 90, 91, 96, E99, S01, S10, S11 (la red o un servicio interno falló), salvo que el catálogo diga otra cosa |
Reintenta con espera creciente |
| — | T01 y cualquier 202 |
Estado indeterminado: no reintentes. No sabemos si la operación se procesó. Consulta el estado antes de hacer nada |
Reserva de cupo
Sección titulada «Reserva de cupo»Estos son propios de las reservas de cupo.
| Código | HTTP | Qué pasó |
|---|---|---|
MIT_PRODUCTION_KEY_REQUIRED |
422 | Intentaste autorizar con llave de prueba; una reserva retiene fondos reales |
MIT_AMOUNT_EXCEEDS_AUTHORIZED |
422 | El cobro final supera lo reservado |
MIT_EXPIRED |
422 | La reserva venció; hay que crear una nueva |
MIT_ALREADY_CONFIRMED |
409 | Esta reserva ya tiene su cobro aplicado |
MIT_VOID_WINDOW_CLOSED |
422 | Ya cerró la ventana para liberar (24 h antes del vencimiento). Deja que venza |
MIT_FRANCHISE_NOT_ENABLED |
422 | Esa franquicia no está habilitada para reservas en tu comercio |
MIT_INDETERMINATE |
202 | No recibimos respuesta de la red. No reintentes |
MIT_IN_PROGRESS |
429 | Ya hay una autorización en curso para esta reserva |
M01 |
409 | La reserva ya fue confirmada |
M02 |
422 | La operación referenciada no es una reserva pre-autorizada |
M04 |
422 | La reserva está vencida en la red |
M05 |
422 | La reserva no fue aprobada, así que no hay cupo que cobrar |
M06 |
422 | La red no encuentra la operación. Solo conserva 30 días |
M08 / M10 / M11 |
422 | La franquicia o el banco emisor no permiten reservas |
M12 |
422 | El valor del cobro final no corresponde al reservado |
309 |
422 | El tipo de transacción MIT no admite 3DS |
310 |
422 | El ECI enviado no es válido: debe ser 05, 06 o 07 |
319 / 320 |
422 | Enviaste el objeto 3DS de la otra franquicia |
Anulación y reversión
Sección titulada «Anulación y reversión»| Código | Qué pasó |
|---|---|
A01 |
Pasó la fecha permitida para anular |
A03 – A06 |
La operación no está en un estado que admita anulación |
A07 |
No se puede anular una operación incremental |
V39 |
No se puede liberar una reserva que ya fue cobrada |
V40 |
La reserva no está en fecha válida para liberarse |
V42 |
La operación indicada no es anulable |
Errores de transporte y cifrado
Sección titulada «Errores de transporte y cifrado»| Código | HTTP | Qué pasó | Qué hacer |
|---|---|---|---|
T01 |
202 | Se agotó el tiempo de espera con la red. No sabemos si la operación quedó registrada | No reintentar. Consultar el estado |
E99 |
502 | Error interno de la red | Reintentable con espera |
S10 / S11 |
500 | Fallo de cifrado con la red | Contactar a soporte |
Catálogo en vivo
Sección titulada «Catálogo en vivo»En lugar de copiar esta tabla a tu código, consúltala:
GET /api/v1/resources/mit/response-codes
Cada entrada trae:
| Campo | Para qué |
|---|---|
| code | El código |
| message | El mensaje pensado para el comercio |
| action | Qué hacer |
| retryable | Si tiene sentido reintentar |
Qué mostrarle a tu cliente
Sección titulada «Qué mostrarle a tu cliente»Tenemos dos mensajes para cada código y sirven para cosas distintas:
| Campo | Audiencia | Ejemplo |
|---|---|---|
Mensaje al pagador (description) |
El tarjetahabiente | «Tu tarjeta está vencida. Usa otra tarjeta.» |
Mensaje al comercio (error.message) |
Tu operador o tu log | «Transacción declinada. Tarjeta vencida.» |
En la respuesta de una transacción, description trae el mensaje que se le puede
mostrar al cliente tal cual. En un rechazo con código catalogado pide lo mismo que
error.action (ver Rechazos de la red).