Trampas verificadas
12.1 Los campos desconocidos se ignoran en silencio
Un cuerpo con un nombre de campo mal escrito devuelve 201 Created sin ninguna advertencia, y el pago se crea sin esa funcionalidad.
{ "amount": 100, "service_fee": true } <- wrong name
-> 201 Created, service_fee_amount: 0 <- the fee pass-through was NOT applied
Este es el modo de falla más peligroso de la API, porque el resultado parece exitoso. Es
especialmente peligroso para código generado por IA, que tiende a escribir con total confianza los
nombres de campo de otras pasarelas (card_token, payment_method, customer_id, amount_cents).
Defensa obligatoria: después de crear un pago, verificá en la respuesta que los campos
calculados (sales_tax_amount, service_fee_amount, total, reusable) coincidan con lo que
esperabas. Y validá el cuerpo de tu petición contra el esquema de openapi.yaml, que declara
additionalProperties: false justamente para atrapar lo que la API deja pasar.
12.2 amount no tiene tope y hace coerción de tipos
La API acepta montos absurdos (11 dígitos) y strings numéricos ("150.00"). Imponé un máximo
razonable antes de enviar.
12.3 Dos tipos distintos de 404
| Respuesta | Significado |
|---|---|
{"message":"Pago no encontrado."} |
La ruta existe; el pago no. Id equivocado, o un id del otro entorno. |
{"message":"The route ... could not be found."} |
La ruta no existe. URL mal formada o un endpoint inexistente. |
Distinguirlos ahorra horas: el segundo nunca se arregla cambiando el id.
12.4 Los timestamps vienen en hora local de Honduras sin offset
expires_at, created_at y paid_at vuelven como YYYY-MM-DD HH:MM:SS en hora de Honduras
(UTC-6), sin marca de zona horaria en el string. Parsearlos como UTC corre cada valor seis horas -
suficiente para que un vencimiento futuro parezca vencido, o para que una orden parezca pagada antes
de haber sido creada. Agregá el offset explícitamente al parsear.
12.5 Una propina elegida por el cliente no está en el total que te dieron
Con tip_customer_selectable, el total que se devuelve al crear excluye la propina porque el
cliente todavía no la eligió. Por eso el monto realmente cobrado puede ser mayor que el total que
guardaste. Conciliá contra el payload del webhook o contra una consulta nueva, nunca contra la
respuesta de creación.
12.6 metadata puede volver como un arreglo vacío
Cuando no se envió metadata, la API puede devolver "metadata": [] en vez de {}. Los
deserializadores estrictos que esperan un objeto van a fallar. Tratá ambas formas como "sin
metadata".
12.7 No hardcodees el dominio del checkout que aparece en la documentación oficial
La documentación oficial de ROKI muestra checkout_url en pay.roki.app; la API en realidad
devuelve aura.roki.systems/pay/link/{slug}. Nunca hardcodees ni pongas en lista blanca un dominio
de checkout - siempre redirigí al checkout_url exacto que devolvió la API.
12.8 success_url acepta http://
Aunque la documentación oficial exige HTTPS, la API acepta URLs sin cifrar. Usá HTTPS de todas formas - es una URL de retorno en un flujo de pago.
