Migrar desde otra pasarela
La costumbre se viene con el codigo. Estos 70 nombres de campo de Stripe y Mercado Pago estan mapeados a como se llaman en ROKI, o a un honesto "no existe equivalente, hace esto en su lugar".
Importa mas de lo que parece: este API acepta un campo que no existe, responde 201 y lo ignora. Un nombre traido de otra pasarela no falla, funciona a medias. La respuesta trae un arreglo
warningsque nombra cada campo descartado - esta ausente cuando el cuerpo va limpio, asi que leerlo es la comprobacion mas barata que hay.
Desde PixelPay
La diferencia grande no es un nombre de campo: es que a ROKI no le llega el numero de tarjeta. El SDK de PixelPay recibe card_number, card_cvv y card_holder y los manda desde tu servidor; en ROKI el numero se escribe en una superficie de ROKI -el checkout alojado o el iframe que incrusta tu tienda- y tu servidor nunca lo ve. Si estas portando el camino que arma esa peticion, ese camino se borra entero, y con el casi todo lo que PCI te exige. Los nombres de abajo salen de su SDK publicado (@pixelpay/sdk-core 2.5.7), que es la fuente que se puede leer y verificar.
| Escribiste | En ROKI | Que hacer |
|---|---|---|
customer_email | customer.email | En Stripe y en PixelPay esto es "customer_email"; en ROKI es "customer.email". En ROKI vive dentro del objeto opcional de prellenado customer (customer.email, junto a name, phone, identity_number), no crea ningún registro de cliente, sigue siendo editable a menos que también mandés lock_customer_fields: true, y se devuelve como eco... |
order_amount | amount | Los dos van en unidades decimales, asi que el valor no cambia - pero PixelPay lo manda como texto y el campo de ROKI es numerico. ROKI tambien acepta numeros en texto, asi que no va a fallar; es el unico campo de esta migracion que pasa casi intacto. |
order_id | external_reference | ROKI no exige que sea unico: la proteccion contra duplicados viene de la cabecera Idempotency-Key, no de este campo. Si dependias de que PixelPay rechazara un order_id repetido, esa comprobacion ya no ocurre y tenes que mandar la cabecera. |
order_currency | currency_code | Un codigo de tres letras lo rechaza el patron. Ademas toma por omision la moneda de la terminal, asi que la mayoria de las integraciones puede no mandarlo. |
order_note | description | Si te pasas es un 422, no un recorte. |
order_callback | success_url | Diferencia importante: llegar a success_url NO significa que el pago ocurrio - significa que el navegador volvio. Lo unico que confirma que el dinero se movio es el webhook firmado. Si tu integracion de PixelPay acreditaba la orden en el callback, esa logica tiene que mudarse al webhook. |
order_tax_amount | sales_tax_value | Mandar el valor sin el tipo es la forma de que el impuesto salga mal en silencio. |
order_extras | metadata | La misma idea: pares clave/valor tuyos, devueltos intactos al consultar y en cada webhook, y nunca visibles para el cliente. |
order_shipping_amount | no existe | Sumale el envio vos mismo a "amount": ROKI cobra exactamente el monto que le mandas, y no hay nada que le agregue una linea encima. |
order_content | no existe | Se cobra solo el total. Guarda el detalle del carrito de tu lado y poné un rotulo legible en "name". |
customer_name | customer.name | En PixelPay esto es "customer_name"; en ROKI va adentro del objeto "customer", como "customer.name". Es el campo mas peligroso de toda la migracion: ROKI tambien tiene un "name" de primer nivel, pero ese es el ROTULO del pago que el cliente lee en el checkout, no el comprador. Poner ahi el nombre del comprador devuelve 201 y funciona - y cada cliente ve su propio nombre donde deberia ir la descripcion de la orden. |
customer_identification | customer.identity_number | En PixelPay esto es "customer_identification"; en ROKI es "customer.identity_number". Ademas es el identificador preferido para listar las tarjetas guardadas de un cliente, asi que conviene mandarlo aunque el checkout no lo necesite. |
billing_phone | customer.phone | En PixelPay esto es "billing_phone"; lo mas parecido en ROKI es "customer.phone". ROKI no tiene bloque de facturacion: los demas campos billing_* no tienen a donde ir. |
billing_address | no existe | El checkout alojado de ROKI tiene su propia seccion de facturacion opcional que el mismo recoge, y el API no acepta ninguno de estos al crear. Sacalos de tu peticion. |
card_number | no existe | ROKI nunca recibe datos de tarjeta por este API - ni en este campo ni en ningun otro. El numero se escribe en una superficie de ROKI: el checkout alojado, o el iframe que tu tienda incrusta. Si estas portando codigo que manda un PAN a tu propio servidor, ese camino entero desaparece, y con el casi todo lo que PCI te exige. |
card_token | no existe | ROKI soporta tarjetas guardadas, pero Connect no expone cobrar con una desde el API: el cliente elige su tarjeta guardada en el checkout de ROKI. Los cobros recurrentes sin el cliente presente se habilitan por comercio y todavia no son parte de esta superficie. |
payment_uuid | transaction_id | Dos cosas muerden aca. NO es el "id" numerico que te dan al crear: pasarlo a esas rutas devuelve 404 con code "transaction_id_expected". Y aparece cuando se INTENTA el cobro, no cuando sale bien: un pago puede estar pendiente y ya traer transaction_id, asi que "tiene transaction_id" NO quiere decir "se pago". Mira el status. Sobre un pago asi las rutas se niegan con claridad: receipt_unavailable y void_invalid. |
void_reason | no existe | La anulacion de ROKI no recibe ninguno de los dos: el transaction_id en la URL es toda la peticion. Si guardas un motivo para tus propios registros, guardalo de tu lado. |
installment_type | no existe | ROKI Connect no expone cuotas. No los mandes: el API devuelve 201 y los nombra en "warnings", asi que un plan que creias vendido nunca se aplico. |
points_redeem_amount | no existe | ROKI Connect no tiene equivalente. Restale el descuento que quieras a "amount" antes de mandarlo. |
authentication_request | no existe | En ROKI no se pide: 3DS2 corre en el checkout de ROKI cuando la tarjeta y el monto lo ameritan. No hay campo para encenderlo, y no hay nada que portar. |
apple_pay | no existe | ROKI Connect no expone esas billeteras. Mandarlas da 201 con el campo nombrado en "warnings" - el cobro sale como un pago con tarjeta normal. |
tap_to_pay_device | no existe | ROKI Connect es un API de pagos en linea y no expone Tap to Pay. No hay nada que mapear: es otro producto, no otro nombre de campo. |
customer_fingerprint | no existe | ROKI no tiene campo equivalente: el checkout corre en la pagina de ROKI, asi que las senales que necesita las recoge ahi en vez de confiar en un valor que manda tu servidor. |
secure_payload | no existe | En ROKI ese ida y vuelta ocurre adentro del checkout de ROKI, asi que no hay payload que llevar ni paso que reproducir. |
source | no existe | ROKI no tiene ese campo, pero "metadata" acepta lo que quieras guardar: pone ahi tu canal y vuelve intacto al consultar y en cada webhook. |
Desde Stripe
| Escribiste | En ROKI | Que hacer |
|---|---|---|
amount | amount | Mismo nombre, distinta unidad: el amount de ROKI va en unidades decimales (1500.00 = L 1,500.00, mínimo 0.01), así que dividí el entero de Stripe entre 100 antes de mandarlo, y aplicá tu propio tope porque ROKI no impone máximo y crearía en silencio un... |
amount_cents | amount | ROKI no tiene campo de centavos en ningún lado: convertí a unidades decimales y mandá amount; este es uno de los pocos nombres equivocados que falla con ruido en vez de en silencio, devolviendo 422 'El campo amount es obligatorio.' porque amount es obligatorio. |
currency | currency_code | POST /payments quiere el código *numérico* ISO 4217 como string ('340' = HNL), no el alfabético; la forma alfabética ('HNL') se acepta solo en /payments/token-charge, /payment-methods/{id}/charge y /confirm, y la respuesta hace eco de... |
payment_method_types | no existe | ROKI es solo tarjeta y el modo de integración se elige según el endpoint que llamás - POST /payments para el checkout hospedado, POST /confirm para campos de tarjeta embebidos, POST /payment-methods/{id}/charge para una tarjeta guardada - así que eliminá el campo por completo,... |
success_url | success_url | Nombre idéntico en POST /payments, pero el endpoint de componentes embebidos POST /confirm lo llama success_redirect_url; solo se acepta http/https (los esquemas de app personalizados como myapp:// devuelven 422), y que un cliente caiga ahí nunca es prueba de... |
cancel_url | cancel_url | Mismo nombre en POST /payments; el equivalente en componentes embebidos es failed_redirect_url en POST /confirm, que significa 'el cargo falló', no 'el cliente se echó para atrás', así que los dos no son intercambiables. |
customer_email | customer.email | En Stripe y en PixelPay esto es "customer_email"; en ROKI es "customer.email". En ROKI vive dentro del objeto opcional de prellenado customer (customer.email, junto a name, phone, identity_number), no crea ningún registro de cliente, sigue siendo editable a menos que también mandés lock_customer_fields: true, y se devuelve como eco... |
metadata | metadata | Mismo nombre y mismo propósito - se devuelve intacto al consultarlo y en cada webhook, nunca se le muestra al cliente - pero tiene que ser un objeto JSON o te da 422 ('El campo metadata debe ser un array.'), y cuando no mandás ninguno ROKI puede devolver... |
client_reference_id | external_reference | El external_reference de ROKI es obligatorio (máx 191) y, a pesar del nombre, NO es único - dos creaciones con el mismo valor producen dos links pagaderos por separado - así que la protección contra duplicados tiene que venir de Idempotency-Key; podés filtrar por él... |
capture_method | no existe | ROKI no separa autorización y captura y no tiene endpoint de captura - todo cargo aprobado se captura de inmediato - así que expresá en su lugar la reversión: intentá POST /payments/{transaction_id}/void (solo monto completo, antes de liquidar) y recurrí a POST... |
receipt_email | no existe | ROKI nunca envía un recibo por correo desde el API: consultá GET /payments/{transaction_id}/receipt para obtener el receipt_url público (o /receipt/download para el PDF) y entregalo vos mismo - y tené presente que customer.email solo prellena el formulario del checkout,... |
setup_future_usage | no existe | No podés pedir el guardado de la tarjeta desde el API de ROKI de ninguna manera - el cliente tiene que marcar 'save my card' en el checkout propio de ROKI, tras lo cual el webhook payment_method.saved entrega un pm_* opaco que después cobrás vía POST... |
payment_intent | transaction_id | ROKI divide esto en dos identificadores: el id numérico para GET /payments/{id}, y el UUID transaction_id - null hasta que el pago realmente se cobra - que es el que requieren void, refund y los recibos; pasar el id numérico a esas rutas... |
customer | no existe | ROKI no tiene recurso Customer ni ids de cliente, así que no hay nada que pasar aquí - identificá a las personas por los datos mismos: mandá el objeto inline de prellenado customer (name, email, phone, identity_number) en POST /payments, y buscá lo guardado... |
description | description | Mismo nombre pero limitado a 120 caracteres en ROKI, y no es la etiqueta que el cliente ve en el checkout - esa es name, el campo obligatorio (máx 191) para el que Stripe no tiene equivalente, así que una migración directa deja tu página de checkout sin etiqueta. |
statement_descriptor | no existe | ROKI no expone descriptor de estado de cuenta por pago - viene de la configuración de la terminal del comercio - así que quitá el campo en vez de mandarlo, porque se ignoraría en silencio con un 201; name y description solo afectan lo que el... |
line_items | no existe | ROKI recibe un solo total ya calculado: sumá el carrito en tu propio backend y mandá amount en unidades decimales más una única etiqueta name, guardá el desglose por línea en metadata para conciliación, y usá sales_tax_type/sales_tax_value cuando necesités... |
mode | no existe | ROKI no tiene parámetro mode ni motor de suscripciones - POST /payments es siempre un checkout hospedado de pago único - así que manejá el cobro recurrente desde tu propio scheduler llamando a POST /payments/token-charge con un payment_token guardado en cada ciclo,... |
url | checkout_url | ROKI devuelve checkout_url en el 201 de POST /payments - redirigí al string exacto que devuelve y nunca hardcodeés el dominio o el slug, ni los metás en lista de permitidos ni los validés con patrones, porque el valor real (aura.roki.systems/pay/link/{12 caracteres en minúsculas... |
amount_total | total | El total de ROKI es la cifra autoritativa y va en unidades decimales (subtotal + sales_tax_amount + service_fee_amount) - leelo de la respuesta en vez de recalcularlo, y acordate de que la propina la elige el cliente en el checkout, así que el amount... |
expires_at | expires_at | Mismo nombre, formato completamente distinto: ROKI quiere un string como '2026-08-13 18:00:00' (o ISO 8601) en hora local de Honduras UTC-6 sin marcador de offset y tiene que estar en el futuro o te da 422 - pasar un timestamp Unix, o parsear el... |
payment_method | payment_token | Las tarjetas guardadas de ROKI llevan el mismo prefijo pm_, lo que hace de este la suposición equivocada más convincente de la migración: el campo es payment_token en el cuerpo de POST /payments/token-charge (o el segmento de ruta {payment_method_id} de POST... |
application_fee_amount | no existe | ROKI no tiene campo de comisión de plataforma o marketplace; su única bandera de comisión es el booleano service_fee_enabled, que significa algo distinto - le traslada al cliente los costos de procesamiento propios de ROKI mediante cálculo inverso para que al comercio le quede neto amount... |
locale | Accept-Language | ROKI no tiene campo en el cuerpo para esto - mandá en su lugar el header Accept-Language: es|en (español es el default si el valor falta, está vacío o no está soportado) - pero fijate en su alcance más limitado: localiza solo los mensajes del API y de validación, el ruteo... |
Desde Mercado Pago
| Escribiste | En ROKI | Que hacer |
|---|---|---|
items | no existe | ROKI no tiene líneas de detalle en absoluto - sumá el carrito en tu propio backend y mandá un solo amount, y si necesitás conservar el desglose ponelo en metadata, que ROKI devuelve intacto al consultarlo y en cada webhook. |
unit_price | no existe | En ROKI no existe nada por unidad - multiplicá por quantity, sumá todos los ítems, y mandá el resultado como amount (unidades decimales, mínimo 0.01, el API no impone máximo, así que poné el tope de tu lado). |
quantity | no existe | Incorporá quantity al total antes de llamar a ROKI, y nunca dejés la key en el cuerpo como un extra por las dudas: ROKI ignora en silencio los campos desconocidos y devuelve 201 igual, así que el error es invisible. |
transaction_amount | amount | El calce 1:1 más cercano de toda la migración - también unidades decimales y no centavos (150.50 = L 150.50), también obligatorio, mínimo 0.01 - pero ROKI puede sumar encima impuesto de venta, propina y comisión de servicio, así que la cifra autoritativa de lo cobrado es total en el... |
payer | customer | El customer de ROKI acepta solo name, email, phone e identity_number (sin surname ni address), y es solo un PRELLENADO del checkout hospedado que el cliente todavía puede editar a menos que también mandés lock_customer_fields: true. |
external_reference | external_reference | Nombre idéntico pero OBLIGATORIO en ROKI (máx 191) y explícitamente NO único - mandarlo dos veces sin Idempotency-Key crea dos pagos pagaderos por separado - y se puede filtrar vía GET /payments?external_reference=. |
notification_url | no existe | Los webhooks de ROKI no son por pago - registrá la URL del endpoint y copiá su secreto de firma una vez por ambiente en https://aura.roki.systems/merchant/connect/webhooks, y después verificá el HMAC-SHA256 de ROKI-Signature sobre timestamp + "." +... |
back_urls | no existe | No hay objeto anidado en ROKI - lo reemplazan los dos campos planos de nivel superior success_url y cancel_url, y ambos aceptan solo http/https (un esquema de app personalizado como myapp://done devuelve 422, así que el viaje de regreso en móvil necesita Universal Links / App... |
auto_return | no existe | No hay switch que configurar - el checkout hospedado de ROKI siempre ofrece el regreso a success_url - y, como en Mercado Pago, el redirect nunca es prueba de un cargo, así que tratalo puramente como UX y confirmá del lado del servidor. |
init_point | checkout_url | El equivalente limpio y el corazón de la migración - ROKI lo devuelve directo de POST /payments sin paso de preferencia, hay exactamente una URL para ambos ambientes, y el slug es un string a secas de 12 caracteres en minúsculas sin plink_... |
sandbox_init_point | no existe | ROKI no tiene segunda URL - el ambiente lo decide únicamente el prefijo de la llave del API (sk_test_ = sandbox, sk_live_ = producción) sobre rutas idénticas, así que usá checkout_url en ambos y solo cambiá la llave. |
preference_id | id | ROKI no tiene objeto de preferencia - POST /payments crea el pago directamente - y el id numérico devuelto sirve solo para GET /payments/{id}, así que persistilo, porque el único otro camino de vuelta a un pago es paginar GET /payments. |
payment_method_id | no existe | El falso amigo más peligroso de esta tabla - ROKI sí tiene un payment_method_id, pero es un PARÁMETRO DE RUTA en la URL que lleva una referencia opaca de tarjeta guardada (pm_7k2n9xqf31ab), no un selector de marca, y ROKI Connect no ofrece selección de método... |
token | payment_token | Misma arquitectura, pero un solo nombre de ROKI cubre dos valores no intercambiables: el modo 2 manda el tok_* del SDK de iframe a POST https://aura.roki.systems/api/connect/embed/confirm junto con publishable_key (ojo que la base es /api/connect/embed, no... |
installments | no existe | ROKI Connect no expone planes de cuotas en absoluto y no hay workaround - y como los campos desconocidos se descartan en silencio, dejar installments en el cuerpo devuelve 201 mientras al cliente se le cobra completo, así que sacalo... |
currency_id | currency_code | De nivel superior en vez de por ítem, e inconsistente dentro del propio ROKI: POST /payments requiere el código ISO 4217 NUMÉRICO ("340" = HNL) mientras que POST /payments/token-charge y el confirm de embed aceptan el alfabético "HNL" - y solo las monedas... |
expires | no existe | ROKI no tiene switch de encendido/apagado - simplemente mandá expires_at para fijar un vencimiento, u omitilo por completo para un link sin vencimiento. |
expiration_date_to | expires_at | Tiene que estar en el futuro y se interpreta en hora de Honduras (UTC-6) SIN offset en el string en ninguna dirección, así que parsear expires_at, created_at o paid_at como UTC corre cada valor seis horas - suficiente para hacer que un link vigente parezca... |
capture | no existe | ROKI no separa autorización y captura - todo cargo aprobado se captura - así que lo reemplaza la ruta de reversión: intentá POST /payments/{transaction_id}/void (solo monto completo, antes de liquidar) y recurrí a POST /payments/{transaction_id}/refund (completo... |
binary_mode | no existe | No es configurable en ROKI - un pago queda pendiente hasta que se cobra, y en el modo 2 un desafío 3-D Secure devuelve status: "pending" con un authentication_url que el cliente debe completar, y el resultado final llega por webhook. |
status_detail | no existe | ROKI devuelve solo status (pending, paid, partially_refunded, refunded, voided, expired, disabled); el detalle del rechazo llega en una forma completamente distinta, como IsoResponseCode pasado directo del procesador más un array Errors[] con mayúscula inicial de {Code,... |
