ROKIConnect
Conectores Empezar API SDK Migrar MCP Portal EN

Trampas verificadas

12.1 Los campos desconocidos se ignoran - pero ahora la respuesta te avisa

Un cuerpo con un nombre de campo mal escrito sigue devolviendo 201 Created, y el pago se sigue creando sin esa funcionalidad. Lo que cambió el 17/08/2026 es que la respuesta trae un arreglo warnings que nombra cada campo descartado:

{ "amount": 100, "service_fee": true }     <- wrong name
-> 201 Created
   "service_fee_amount": 0                  <- the fee pass-through was NOT applied
   "warnings": ["Campo desconocido ignorado: service_fee"]

Leé ese arreglo. Es la comprobación de correctitud más barata que ofrece esta API.

Comportamiento del campo, verificado:

Situación warnings
Cuerpo limpio ausente de la respuesta - ni null, ni []
Un campo desconocido arreglo de una cadena
Varios una cadena por campo
Accept-Language: en traducido: "Unknown field ignored: save_card"

Como el texto está localizado, ramificá sobre que el arreglo no esté vacío, nunca sobre su redacción.

Este era el modo de falla más peligroso de la API, porque el resultado parecía exitoso, y sigue siendo peligroso para el código que ignore el campo nuevo - sobre todo el generado por IA, que escribe con total confianza los nombres de otras pasarelas (card_token, payment_method, customer_id, amount_cents).

Defensa, de la más fuerte a la más débil:

  1. Validá antes de enviar contra el esquema de openapi.yaml, que declara additionalProperties: false justamente para esto. Una petición que nunca sale de tu proceso no puede crear un pago equivocado.
  2. Registrá warnings en cada respuesta y fallá ruidosamente en desarrollo. Un log de producción vacío es la única prueba de que tus nombres de campo están bien.
  3. Verificá los campos calculados (sales_tax_amount, service_fee_amount, total, reusable) contra lo que pretendías. Los avisos atrapan un nombre que inventaste; no atrapan un valor que la API calculó distinto de lo que asumiste.

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 Tres tipos distintos de 404 - distinguilos por code, no por el mensaje

Todo error trae un code estable junto al message traducible. En un 404 es la única forma confiable de saber qué pasó de verdad:

code Significado Cómo se arregla
payment_not_found La ruta existe y el identificador está bien formado; no hay registro para esa clave. Casi siempre un id del otro entorno. Usá la clave del mismo entorno que el id.
transaction_id_expected La ruta existe y rechazó el identificador: pasaste el id numérico del pago (o cualquier cosa que no sea un UUID) donde va el UUID de la transacción. Mandá transaction_id, no id (13.1).
not_found La ruta no está en el router. URL mal formada, falta /v1, o el endpoint fue eliminado. Arreglá la URL. Cambiar el identificador no sirve de nada.

Distinguirlos ahorra horas, y cada uno apunta a un arreglo distinto.

No los clasifiques por el texto del mensaje. Dos razones, ambas comprobadas:

  1. Hasta el 2026-08-21 el error del id numérico devolvía el 404 de ruteo, y por eso todo el ecosistema aprendió a comparar contra "could not be found". Ya no devuelve eso. El código que siga con esa comparación archiva el error de identificador más común bajo "no existe el pago", y manda al desarrollador a buscar un id equivocado cuando lo equivocado era el tipo de identificador.
  2. Los mensajes de payment_not_found y transaction_id_expected se traducen con Accept-Language; solo la prosa de not_found queda siempre en inglés. Una comparación de texto que funciona en un idioma se rompe en el otro.

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.