Mono Colombia

Recaudos

Simula que un pagador paga tus recaudos, y provoca las fallas que necesitas probar.

En producción el dinero llega cuando una persona real decide pagarte. En el sandbox esa persona eres tú: una sola petición hace que un pago aterrice en uno de tus recaudos, y todo lo que sigue se comporta como si el dinero fuera real. Así construyes y pruebas un recaudo de punta a punta antes de tocar producción.

El pago simulado corre sobre el riel real: Bre-B, la red de transferencias inmediatas de Colombia. Los mismos estados, los mismos webhooks, los mismos tiempos. Sólo el dinero no es real.

Antes de empezar

Necesitas:

  • Una API key de sandbox, y las peticiones apuntando a https://api.sandbox.cuentamono.com.
  • El scope payins en tus credenciales.
  • Algo que se pueda pagar: una plantilla en estado ready, o una intención de pago en estado ready_to_pay.

Ese último punto es el que más tropieza. Una plantilla o una intención de pago no es pagable en el momento en que la creas. Mono todavía tiene que registrar su llave Bre-B ante la red. Espera primero el webhook payin_template_ready o payin_intent_ready_to_pay. Simular contra un objetivo que aún no tiene llave responde 400 con payment_token_not_found.

Sólo sandbox. En cualquier otro ambiente este endpoint responde 403 con feature_not_enabled, así que una llamada que se filtre al código de producción no puede acuñar pagos.

Qué hace realmente una simulación

Mono opera directamente sobre Bre-B, así que el sandbox no finge el pago: recorre el mismo flujo de aceptación que una transferencia real. Nombras un objetivo y un monto, y Mono construye un pago entrante contra la llave Bre-B de ese objetivo, exactamente como si un pagador hubiera escaneado su QR.

La respuesta es un recibo, no un veredicto. Te dice que existe un pago; su resultado te llega unos segundos después por los webhooks de recaudos, igual que uno real.

Las dos formas de un pago

Cuál envías depende de qué estás pagando. Una plantilla acepta muchos pagos y no sabe nada de este en particular, así que la petición tiene que traer su propio método y monto. Una intención de pago fue declarada para un pago específico y ya fija ambos.

Si pagas una...EnvíasPorque
Plantillatemplate_id + method + amountLa plantilla acepta muchos pagos; este trae los suyos
Intención de pagointent_idLa intención de pago ya fijó el método y el monto

Las dos formas son excluyentes. Nombrar ambos objetivos, o enviar un method junto a un intent_id, responde 422.

Pagar una plantilla

POST /v1/core/payins/sandbox/payments
{
  "template_id": "cpitpl_5tgliBmzjZ6mpQPRbQjfKj",
  "method": "co_breb",
  "amount": { "amount": 5000000, "currency": "COP" }
}

method tiene que ser uno que la plantilla acepte, o la llamada responde 400 con method_not_allowed. Hoy co_breb es el único método de pago que ofrece Recaudos de Core. Los montos son enteros en centavos, así que el ejemplo de arriba son 50.000 COP.

Pagar una intención de pago

POST /v1/core/payins/sandbox/payments
{
  "intent_id": "cpint_5tgliBmzjZ6mpQPRbQjfKj"
}

Esa es toda la petición. La intención de pago aporta el método y el monto, y eso es justo lo que la hace útil para probar: si envías esto, estás verificando el monto que la intención de pago exige, no uno que retecleaste.

Leer la respuesta

El código de estado te dice si nació un pago:

EstadoCuerpoQué pasó
201{ "payin_id": "cpin_...", "state_reason": null }El pago entró. Síguelo por los webhooks.
201{ "payin_id": "cpin_...", "state_reason": "bank_rejected" }El pago existe y ya fue rechazado por sus propias reglas.
200{ "payin_id": null, "state_reason": "amount_below_minimum" }El pago se rehusó antes de quedar registrado.

Ese 200 merece atención. Cuando la autorización de pago de Mono rehúsa el pago (un monto fuera de los límites de la plantilla, un pagador que no espera), el pago no queda registrado y no se emite ningún webhook. En producción la app del banco del pagador es la que reporta el rechazo, así que esta respuesta es el único lugar donde tu sistema puede enterarse. Provocar esos rechazos es buena parte de para qué existe este endpoint.

Elegir quién paga

Si omites payer, Mono se inventa uno: un nombre colombiano al azar y un documento CC de 10 dígitos al azar. Suficiente para la mayoría de pruebas. Envíalo cuando el pagador sea el punto de la prueba:

{
  "template_id": "cpitpl_5tgliBmzjZ6mpQPRbQjfKj",
  "method": "co_breb",
  "amount": { "amount": 5000000, "currency": "COP" },
  "payer": {
    "document": { "type": "CC", "number": "1234567890" },
    "name": "Juan Garcia"
  }
}

Así ejercitas la lista expected_payers de una plantilla. Envía un documento de la lista para confirmar que el pago pasa; envía uno que no esté para confirmar que obtienes payer_not_allowed. El type del documento acepta CC, CE, NIT, PA y TI.

Provocar fallas

Hay dos tipos de falla, y se provocan de dos maneras distintas. Tener clara la diferencia ahorra mucha confusión.

Fallas del riel: el campo error

Son condiciones de infraestructura que el riel reporta después de que el pago ya fue aceptado. Nombra una en error y el sandbox le pide al riel que la produzca:

{
  "template_id": "cpitpl_5tgliBmzjZ6mpQPRbQjfKj",
  "method": "co_breb",
  "amount": { "amount": 5000000, "currency": "COP" },
  "error": "risk_control"
}

La llamada sigue respondiendo 201 con un payin_id y un state_reason nulo: el pago fue aceptado, así que Mono ya lo registró. La falla llega 2 a 5 segundos después, por el webhook:

errorEl pago terminaSignificado
risk_controlfailed / risk_controlLos controles de riesgo del proveedor lo frenaron
provider_unavailablefailed / provider_unavailableEl proveedor de pagos estaba fuera de servicio
breb_timeoutfailed / breb_timeoutEl riel Bre-B no respondió a tiempo
unknownfailed / unknownUna falla sin clasificar

Las cuatro dejan el pago en failed y te llegan como payin_failed. Si el pago iba dirigido a una intención de pago, esta reporta aparte: se rearma para un reintento con payin_intent_attempt_failed, o se da por vencida con payin_intent_failed.

Rechazos de negocio: por construcción

Todo lo que atrapa la validación de Mono no tiene palanca, y no la necesita: lo provocas enviando un pago que el objetivo genuinamente rehúsa. Se rehúsan de forma sincrónica, así que vuelven en la respuesta misma como un 200 sin pago registrado.

Para obtenerEnvía
amount_below_minimumUn monto por debajo del minimum_payin_amount de la plantilla
amount_exceeds_maximumUn monto por encima de su maximum_payin_amount
amount_exceeds_total_maximumUn monto que la pase de su total_maximum_amount
payer_not_allowedUn documento en payer que no esté en expected_payers
template_expired / intent_expiredUn pago después del expires_at del objetivo
template_paidUn segundo pago a una plantilla single_use
template_disabledUn pago a una plantilla que actualizaste a enabled: false
template_deletedUn pago a una plantilla que eliminaste

Cada código, con su causa y qué hacer al respecto, está en Errores de pago.

Una razón que no alcanzas desde aquí

intent_amount_mismatch queda fuera de alcance en el sandbox. Un pago a una intención de pago siempre toma el monto de la intención de pago misma, así que no hay manera de enviar uno distinto. Para ese caso, confía en la validación propia de la intención de pago.

Simular el QR que escaneó el pagador

Un pago Bre-B puede llevar el identificador del QR del que salió. Envíalo cuando tu conciliación lea ese campo:

{
  "template_id": "cpitpl_5tgliBmzjZ6mpQPRbQjfKj",
  "method": "co_breb",
  "amount": { "amount": 5000000, "currency": "COP" },
  "method_params": { "payment_id": "CO.COM.CRB.TRXIDP1234567891234" }
}

Si lo omites, el riel asigna uno. method_params es específico del método: para co_breb el único campo es payment_id.

Errores del endpoint mismo

Son fallas en la petición, no resultados de pago simulados.

EstadoCódigoCausa
400method_not_allowedLa plantilla no acepta el method que enviaste.
400payin_invalid_method_paramsLos method_params no corresponden al método.
400payment_token_not_foundEl objetivo aún no tiene llave Bre-B: no está ready / ready_to_pay.
401missing_authorization_header, invalid_tokenProblema de credenciales.
403feature_not_enabledNo es un ambiente sandbox, o Recaudos no está habilitado para tu cuenta.
404template_not_found, payin_intent_not_foundNo existe el objetivo. El de otro tenant se lee como uno que no existe.
422No aplicaLa petición viola el esquema: ambos objetivos, o un campo faltante.

Una prueba completa, de punta a punta

Todo junto, un recaudo que puedes correr de una sentada:

  1. Crea una plantilla. Espera el payin_template_ready.
  2. Simula un pago por debajo del mínimo. Espera 200, amount_below_minimum, ningún webhook.
  3. Simula un pago válido. Espera 201, y luego payin_created.
  4. Unos segundos después, espera payin_approved_in_provider y payin_subaccount_credited.
  5. Simula otro con "error": "breb_timeout". Espera 201, y luego payin_failed.
  6. Lee la plantilla. Su paid_amount cuenta el paso 3 y no los pasos 2 ni 5.

Si tu integración maneja los seis, maneja un recaudo real.

Siguientes pasos

En esta página