Migrating from another gateway
Your habits come with your code. These 70 field names from Stripe and Mercado Pago are mapped to what ROKI calls them, or to an honest "there is no equivalent, do this instead".
It matters more than it sounds: this API accepts a field that does not exist, answers 201 and ignores it. A name brought from another gateway does not fail, it half works. The response carries a
warningsarray naming every dropped field - absent when the body was clean, so reading it is the cheapest check there is.
From PixelPay
The big difference is not a field name: it is that the card number never reaches ROKI. PixelPay's SDK takes card_number, card_cvv and card_holder and sends them from your server; in ROKI the number is typed on a ROKI surface - the hosted checkout, or the iframe your store embeds - and your server never sees it. If you are porting the code that builds that request, that whole path disappears, and with it most of what PCI asks of you. The names below come from their published SDK (@pixelpay/sdk-core 2.5.7), which is the source that can actually be read and checked.
| You wrote | In ROKI | What to do |
|---|---|---|
customer_email | customer.email | In Stripe and in PixelPay this is "customer_email"; in ROKI it is "customer.email". In ROKI it lives inside the optional customer prefill object (customer.email, alongside name, phone, identity_number), it creates no customer record, it stays editable unless you also send lock_customer_fields: true, and it is echoed back... |
order_amount | amount | Both are decimal units, so the value does not change - but PixelPay sends it as a string and ROKI's field is a number. ROKI accepts numeric strings too, so a string will not fail; it is the one field of this migration you can move across almost untouched. |
order_id | external_reference | ROKI does not require it to be unique - duplicate protection comes from the Idempotency-Key header, not from this field. If you were relying on PixelPay rejecting a repeated order_id, that check no longer happens and you have to send the header. |
order_currency | currency_code | A three-letter code is rejected by the pattern. It also defaults to the terminal's currency, so most integrations can drop the field entirely. |
order_note | description | Anything longer is a 422, not a truncation. |
order_callback | success_url | Important difference: landing on success_url does NOT mean the payment happened - it means the browser came back. Only the signed webhook confirms money moved. If your PixelPay integration credited the order on the callback, that logic has to move to the webhook. |
order_tax_amount | sales_tax_value | Sending the value without the type is how the tax silently ends up wrong. |
order_extras | metadata | Same idea: merchant-defined key/value pairs returned untouched on retrieval and in every webhook, never shown to the customer. |
order_shipping_amount | no equivalent | Add shipping into "amount" yourself - ROKI charges exactly the amount you send, and there is nothing that adds a line on top of it. |
order_content | no equivalent | Only the total is charged. Keep your cart detail on your side and put a readable label in "name". |
customer_name | customer.name | In PixelPay this is "customer_name"; in ROKI it goes inside the "customer" object as "customer.name". This is the most dangerous field of the whole migration: ROKI also has a top-level "name", but that is the payment LABEL the customer reads at checkout, not the buyer. Putting the buyer's name there returns 201 and works - and every customer sees their own name where the order description should be. |
customer_identification | customer.identity_number | In PixelPay this is "customer_identification"; in ROKI it is "customer.identity_number". It is also the preferred identifier when listing a customer's saved cards, so it is worth sending even when the checkout does not need it. |
billing_phone | customer.phone | In PixelPay this is "billing_phone"; the closest thing in ROKI is "customer.phone". ROKI has no billing block at all - the other billing_* fields have nowhere to go. |
billing_address | no equivalent | ROKI's hosted checkout has an optional billing section it collects itself, and the API takes none of these on creation. Drop them from your request. |
card_number | no equivalent | ROKI never receives card data through this API - not on this field, not on any other. The number is typed on a ROKI surface: the hosted checkout, or the iframe your store embeds. If you are porting code that posts a PAN to your own backend, that whole path disappears, and with it most of what PCI asks of you. |
card_token | no equivalent | ROKI supports saved cards, but Connect does not expose charging one from the API: the customer picks their saved card on the ROKI checkout. Recurring charges without the customer present are enabled per merchant and are not part of this surface yet. |
payment_uuid | transaction_id | Two things bite here. It is NOT the numeric "id" you get at creation: passing that to those routes returns 404 with code "transaction_id_expected". And it appears when a charge is ATTEMPTED, not when it succeeds - a payment can be pending and already carry a transaction_id, so "it has a transaction_id" does not mean "it was paid". Check status. On such a payment the routes refuse clearly: receipt_unavailable and void_invalid. |
void_reason | no equivalent | ROKI's void takes neither: the transaction_id in the URL is the whole request. If you keep a reason for your own records, keep it on your side. |
installment_type | no equivalent | ROKI Connect does not expose installments. Do not send them: the API returns 201 and names them in "warnings", so a plan you thought you sold was never applied. |
points_redeem_amount | no equivalent | ROKI Connect has no equivalent. Subtract whatever discount you want from "amount" before sending it. |
authentication_request | no equivalent | In ROKI you do not ask for it: 3DS2 runs on ROKI's checkout when the card and the amount call for it. There is no field to turn it on, and nothing to port. |
apple_pay | no equivalent | ROKI Connect does not expose these wallets. Sending them gets a 201 with the field named in "warnings" - the charge goes through as a normal card payment. |
tap_to_pay_device | no equivalent | ROKI Connect is an online payment API and does not expose Tap to Pay. There is nothing to map: this is a different product, not a different field name. |
customer_fingerprint | no equivalent | ROKI has no equivalent field: the checkout runs on ROKI's own page, so whatever signals it needs it gathers there rather than trusting a value your server sends. |
secure_payload | no equivalent | In ROKI that whole handshake happens inside ROKI's checkout, so there is no payload to carry and no step to reproduce. |
source | no equivalent | ROKI has no such field, but "metadata" takes anything you want to keep: put your channel there and it comes back untouched on retrieval and in every webhook. |
From Stripe
| You wrote | In ROKI | What to do |
|---|---|---|
amount | amount | Same name, different unit: ROKI's amount is decimal units (1500.00 = L 1,500.00, minimum 0.01), so divide Stripe's integer by 100 before sending, and enforce your own ceiling because ROKI enforces no maximum and would silently create a... |
amount_cents | amount | ROKI has no cents field anywhere: convert to decimal units and send amount; this is one of the few wrong names that fails loudly rather than silently, returning 422 'El campo amount es obligatorio.' because amount is required. |
currency | currency_code | POST /payments wants the ISO 4217 *numeric* code as a string ('340' = HNL), not the alphabetic one; the alphabetic form ('HNL') is accepted only on /payments/token-charge, /payment-methods/{id}/charge and /confirm, and the response echoes... |
payment_method_types | no equivalent | ROKI is card-only and the integration mode is chosen by which endpoint you call - POST /payments for hosted checkout, POST /confirm for embedded card fields, POST /payment-methods/{id}/charge for a saved card - so drop the field entirely,... |
success_url | success_url | Identical name on POST /payments, but the embedded-components endpoint POST /confirm calls it success_redirect_url; only http/https is accepted (custom app schemes like myapp:// return 422), and a customer landing there is never proof of... |
cancel_url | cancel_url | Same name on POST /payments; the embedded-components equivalent is failed_redirect_url on POST /confirm, which means 'the charge failed', not 'the customer backed out', so the two are not interchangeable. |
customer_email | customer.email | In Stripe and in PixelPay this is "customer_email"; in ROKI it is "customer.email". In ROKI it lives inside the optional customer prefill object (customer.email, alongside name, phone, identity_number), it creates no customer record, it stays editable unless you also send lock_customer_fields: true, and it is echoed back... |
metadata | metadata | Same name and same purpose - returned untouched on retrieval and in every webhook, never shown to the customer - but it must be a JSON object or you get 422 ('El campo metadata debe ser un array.'), and when you send none ROKI may return... |
client_reference_id | external_reference | ROKI's external_reference is required (max 191) and, despite the name, is NOT unique - two creates with the same value produce two separately payable links - so duplicate protection has to come from Idempotency-Key; you can filter by it... |
capture_method | no equivalent | ROKI has no authorize/capture split and no capture endpoint - every approved charge is captured immediately - so express reversal instead: try POST /payments/{transaction_id}/void (full amount only, before settlement) and fall back to POST... |
receipt_email | no equivalent | ROKI never emails a receipt from the API: fetch GET /payments/{transaction_id}/receipt for the public receipt_url (or /receipt/download for the PDF) and deliver it yourself - and be aware customer.email only prefills the checkout form, it... |
setup_future_usage | no equivalent | You cannot request card saving from the ROKI API at all - the customer must tick 'save my card' on ROKI's own checkout, after which the payment_method.saved webhook delivers an opaque pm_* that you charge later via POST... |
payment_intent | transaction_id | ROKI splits this into two handles: the numeric id for GET /payments/{id}, and the UUID transaction_id - null until the payment is actually charged - which is what void, refund and receipts require; passing the numeric id to those routes... |
customer | no equivalent | ROKI has no Customer resource and no customer ids, so there is nothing to pass here - identify people by the data itself: send the inline customer prefill object (name, email, phone, identity_number) on POST /payments, and look up saved... |
description | description | Same name but capped at 120 characters in ROKI, and it is not the label the customer sees at checkout - that is the required name field (max 191), which Stripe has no equivalent for, so a straight port leaves your checkout page unlabelled. |
statement_descriptor | no equivalent | ROKI exposes no per-payment statement descriptor - it comes from the merchant's terminal configuration - so remove the field rather than sending it, because it would be silently ignored with a 201; name and description only affect what the... |
line_items | no equivalent | ROKI takes one pre-computed total: sum the cart in your own backend and send amount in decimal units plus a single name label, keep the per-line breakdown in metadata for reconciliation, and use sales_tax_type/sales_tax_value when you need... |
mode | no equivalent | ROKI has no mode parameter and no subscription engine - POST /payments is always a one-time hosted checkout - so drive recurring billing from your own scheduler by calling POST /payments/token-charge with a saved payment_token each cycle,... |
url | checkout_url | ROKI returns checkout_url on the 201 from POST /payments - redirect to the exact string returned and never hardcode, allow-list or pattern-match the domain or slug, because the live value (aura.roki.systems/pay/link/{12-char lowercase... |
amount_total | total | ROKI's total is authoritative and in decimal units (subtotal + sales_tax_amount + service_fee_amount) - read it back from the response instead of recomputing it, and remember a customer-selected tip is chosen at checkout, so the amount... |
expires_at | expires_at | Same name, completely different format: ROKI wants a string like '2026-08-13 18:00:00' (or ISO 8601) in Honduras local time UTC-6 with no offset marker and it must be in the future or you get 422 - passing a Unix timestamp, or parsing the... |
payment_method | payment_token | ROKI's saved cards carry the same pm_ prefix, which makes this the most convincing wrong guess in the migration: the field is payment_token in the body of POST /payments/token-charge (or the {payment_method_id} path segment of POST... |
application_fee_amount | no equivalent | ROKI has no platform or marketplace fee field; its only fee flag is the boolean service_fee_enabled, which means something different - it passes ROKI's own processing costs to the customer by reverse calculation so the merchant nets amount... |
locale | Accept-Language | ROKI has no body field for this - send the Accept-Language: es|en header instead (Spanish is the default for a missing, empty or unsupported value) - but note its narrower reach: it localizes API and validation messages only, routing... |
From Mercado Pago
| You wrote | In ROKI | What to do |
|---|---|---|
items | no equivalent | ROKI has no line items at all - sum the cart in your own backend and send one amount, and if you need the breakdown preserved put it in metadata, which ROKI returns untouched on retrieval and in every webhook. |
unit_price | no equivalent | Nothing per-unit exists in ROKI - multiply by quantity, sum every item, and send the result as amount (decimal units, minimum 0.01, no maximum enforced by the API, so cap it on your side). |
quantity | no equivalent | Fold quantity into the total before calling ROKI, and never leave the key in the body as a hopeful extra: ROKI silently ignores unknown fields and still returns 201, so the mistake is invisible. |
transaction_amount | amount | The closest 1:1 match in the whole migration - also decimal units and not cents (150.50 = L 150.50), also required, minimum 0.01 - but ROKI may add sales tax, tip and service fee on top, so the authoritative charged figure is total in the... |
payer | customer | ROKI customer accepts only name, email, phone and identity_number (no surname, no address), and it is only a PREFILL of the hosted checkout that the customer can still edit unless you also send lock_customer_fields: true. |
external_reference | external_reference | Identical name but REQUIRED in ROKI (max 191) and explicitly NOT unique - sending it twice without an Idempotency-Key creates two separate payable payments - and it is filterable via GET /payments?external_reference=. |
notification_url | no equivalent | ROKI webhooks are not per payment - register the endpoint URL and copy its signing secret once per environment at https://aura.roki.systems/merchant/connect/webhooks, then verify the ROKI-Signature HMAC-SHA256 over timestamp + "." +... |
back_urls | no equivalent | There is no nested object in ROKI - the two flat top-level fields success_url and cancel_url replace it, and both accept only http/https (a custom app scheme like myapp://done returns 422, so mobile return trips need Universal Links / App... |
auto_return | no equivalent | There is no switch to set - the ROKI hosted checkout always offers the return to success_url - and as in Mercado Pago the redirect is never proof of a charge, so treat it purely as UX and confirm server-side. |
init_point | checkout_url | The clean equivalent and the heart of the migration - ROKI returns it straight from POST /payments with no preference step, there is exactly one URL for both environments, and the slug is a bare 12-character lowercase string with no plink_... |
sandbox_init_point | no equivalent | ROKI has no second URL - the environment is decided solely by the API key prefix (sk_test_ = sandbox, sk_live_ = production) over identical routes, so use checkout_url in both and just swap the key. |
preference_id | id | ROKI has no preference object - POST /payments creates the payment itself - and the returned numeric id serves only GET /payments/{id}, so persist it, because the only other way back to a payment is paging GET /payments. |
payment_method_id | no equivalent | The most dangerous false friend in this table - ROKI does have a payment_method_id, but it is a URL PATH PARAMETER carrying an opaque saved-card reference (pm_7k2n9xqf31ab), not a brand selector, and ROKI Connect offers no method selection... |
token | payment_token | Same architecture, but one ROKI name covers two non-interchangeable values: mode 2 sends the iframe SDK's tok_* to POST https://aura.roki.systems/api/connect/embed/confirm alongside publishable_key (note the base is /api/connect/embed, not... |
installments | no equivalent | ROKI Connect exposes no installment plans whatsoever and there is no workaround - and because unknown fields are silently dropped, leaving installments in the body returns 201 while the customer is charged in full, so strip it out... |
currency_id | currency_code | Top-level rather than per-item, and inconsistent inside ROKI itself: POST /payments requires the NUMERIC ISO 4217 code ("340" = HNL) while POST /payments/token-charge and the embed confirm accept the alphabetic "HNL" - and only currencies... |
expires | no equivalent | ROKI has no on/off switch - simply send expires_at to set an expiry, or omit it entirely for a link with no expiry. |
expiration_date_to | expires_at | Must be in the future and is interpreted in Honduras time (UTC-6) with NO offset in the string in either direction, so parsing expires_at, created_at or paid_at as UTC shifts every value by six hours - enough to make a live link look... |
capture | no equivalent | ROKI has no auth/capture split - every approved charge captures - so the reversal path replaces it: try POST /payments/{transaction_id}/void (full amount only, pre-settlement) and fall back to POST /payments/{transaction_id}/refund (full... |
binary_mode | no equivalent | Not configurable in ROKI - a payment is pending until it is charged, and in mode 2 a 3-D Secure challenge returns status: "pending" with an authentication_url the customer must complete, the final outcome arriving by webhook. |
status_detail | no equivalent | ROKI returns only status (pending, paid, partially_refunded, refunded, voided, expired, disabled); decline detail arrives in a different shape entirely, as processor-passthrough IsoResponseCode plus a capitalized Errors[] array of {Code,... |
