Web
Every method, option and error in the Web SDK.
Reference for Tonder's Web SDK (@tonder.io/web-sdk,
TypeScript, types included). The step-by-step guide is at Web SDK; source
code and README at github.com/tonderio/web-sdk.
import { createTonder, AppError, ErrorKeyEnum } from '@tonder.io/web-sdk';Prefer loading it with a <script> tag instead of npm? The approved stage and production CDN URLs
are in Web SDK → Installation.
createTonder(config)
Creates an SDK instance for one shopper/session.
interface TonderConfig {
api_key: string;
environment: 'sandbox' | 'stage' | 'production';
session?: {
customer?: {
email: string;
first_name?: string;
last_name?: string;
phone?: string;
};
secure_token?: string;
};
presentation_mode?: 'redirect' | 'embedded';
events?: {
presentation?: {
on_open?(): void;
on_close?(): void;
};
};
customization?: TonderCustomization;
}| Field | Required | Description |
|---|---|---|
api_key | Yes | Public Tonder key for browser integrations. |
environment | Yes | 'sandbox', 'stage', or 'production'. |
session.customer | For pay() and saved-card operations | Customer identity. |
session.secure_token | For saved-card operations | Short-lived token minted by your backend. |
presentation_mode | No | 'redirect' (default) or 'embedded'. |
events.presentation.on_open | No | Called when an embedded hosted-payment view opens. |
events.presentation.on_close | No | Called when the shopper closes the embedded view. |
customization.card_fields | No | Secure card-field customization (below). |
Throws INIT_ERROR when config or api_key is missing, or environment is invalid.
customization.card_fields
All fields are optional; omitted values use the SDK defaults.
| Field | Type | Description |
|---|---|---|
labels | CardLabels | Text shown above each secure field (cardholder_name, card_number, cvv, expiration_month, expiration_year). |
placeholders | CardPlaceholders | Placeholder text inside each secure field. |
styles | CardStyles | Global (card_form) and per-field styles for inputs, labels, errors, and the card icon (enable_card_icon). |
error_messages | CardFieldErrorMessages | Validation-message overrides (required, invalid, and per field). |
Style values use CSS-in-JS keys supported by the secure renderer (e.g. font_size,
font_family, color, border_color). styles applies inside the secure iframe; the mount
container's layout (e.g. .card-field { max-height: 90px; }) is controlled by your own CSS.
const tonder = createTonder({
api_key: 'pk_test_...',
environment: 'sandbox',
customization: {
card_fields: {
labels: { card_number: 'Card number', cvv: 'Security code' },
placeholders: { card_number: '4111 1111 1111 1111', expiration_month: 'MM' },
styles: {
card_form: {
input_styles: {
base: { color: '#111827', font_family: 'Inter, sans-serif', font_size: '16px' },
focus: { border_color: '#2563eb' },
invalid: { color: '#b91c1c' },
},
label_styles: { base: { color: '#374151', font_weight: '600' } },
error_styles: { base: { color: '#b91c1c' } },
},
enable_card_icon: true,
},
error_messages: { required: 'Complete this field.', invalid: 'Check this field.' },
},
},
});Methods
| Method | What it does | Needs init() |
|---|---|---|
tonder.init() | Fetches merchant configuration and prepares the SDK. Safe to call more than once. | — |
tonder.create('card_fields', options?) | Creates the secure card-fields component. | No |
card_fields.mount() | Mounts the secure fields into the containers. | Yes |
card_fields.unmount() | Unmounts this component's fields. | No |
card_fields.reveal(input) | Reveals display-safe saved-card values (never the CVV). | Yes |
tonder.pay(input) | Creates a payment. | Yes |
tonder.getTransaction(id) | Reads the current transaction state. | No |
tonder.enrollCard() | Saves the mounted new card for session.customer. | Yes |
tonder.getCustomerCards() | Lists the customer's saved cards. | Yes |
tonder.removeCustomerCard(card_id) | Removes a saved card. | Yes |
tonder.getPaymentMethods() | Lists the business's active payment methods. | No |
tonder.getPaymentMethodBanks() | Lists SafetyPay banks grouped by channel. | No |
tonder.create('card_fields', options?)
Without options, the SDK mounts the full new-card form using the default containers:
| Field | Default container |
|---|---|
cardholder_name | #collect-cardholder-name |
card_number | #collect-card-number |
expiration_month | #collect-expiration-month |
expiration_year | #collect-expiration-year |
cvv | #collect-cvv (or #collect-cvv-<card_id> for saved cards) |
interface CardFieldsOptions {
fields?: (CardField | { field: CardField; container_id?: string })[];
card_id?: string; // for a saved card's CVV
unmount_context?: 'all' | 'none' | 'current' | 'create' | string;
events?: Partial<Record<CardField, {
on_change?(state: CardFieldState): void;
on_blur?(state: CardFieldState): void;
on_focus?(state: CardFieldState): void;
on_ready?(state: CardFieldState): void;
}>>;
}tonder.pay(input)
interface PayInput {
amount: number;
currency?: string;
return_url: string;
payment_method:
| { type: 'card' }
| { type: 'saved_card'; card_id: string }
| { type: string; config?: Record<string, unknown> };
metadata?: Record<string, unknown>;
billing_address?: {
street?: string;
street2?: string;
state?: string;
country?: string;
zip_code?: string;
};
client_reference: string;
idempotency_key?: string;
}| Field | Required | Description |
|---|---|---|
amount | Yes | Payment amount (decimal units). Must be greater than 0. |
currency | No | Currency code. Defaults to MXN. |
return_url | Yes | URL used after hosted authentication or redirect completion. |
payment_method | Yes | New card, saved card, or an enabled alternative payment method. |
billing_address | No | The buyer's billing address. Every field inside it is optional. |
client_reference | Yes | Your order reference — shown in dashboards, exports, webhooks, and reports. |
idempotency_key | No | Stable key per payment attempt so retries don't create duplicate charges. |
metadata | No | Non-sensitive context for reconciliation and reports. |
metadata keys with reporting meaning:
| Key | Report usage |
|---|---|
operation_date | Business operation date/time for reporting and reconciliation. |
customer_email | Customer email shown in transaction reports. |
customer_id | Merchant customer identifier for report filtering. |
business_user | Internal user, POS terminal, or automation that initiated the payment. |
Returns Promise<RawTransaction>. A transaction that needs 3DS or hosted instructions can
include next_action.redirect_to_url.url; APM/SPEI responses may include clabe, bank_name,
payment_instructions, and voucher_pdf.
tonder.getCustomerCards()
interface Card {
card_id: string;
card_number: string; // masked
expiration_month: string;
expiration_year: string;
card_scheme: string;
subscription_id: string | null;
}subscription_id is returned only when Card-on-File is enabled for the business. When it is
null, mount the saved-card CVV field before calling pay() with that card.
tonder.getPaymentMethods()
interface PaymentMethodInfo {
id: number;
payment_method: string; // the value you pass as payment_method.type in pay()
label: string;
logo: string;
category: string;
}Returns Promise<PaymentMethodInfo[]> with the business's enabled alternative payment methods. Use
it to build the method list in your UI instead of hard-coding it: payment_method is the value you
then pass as payment_method.type in pay().
[
{
"id": 7,
"payment_method": "oxxopay",
"label": "Oxxo Pay",
"logo": "https://...",
"category": "cash"
}
]tonder.getPaymentMethodBanks()
interface PaymentMethodBank {
id: number;
name: string;
code: string;
country: string;
channel: 'WP' | 'OL'; // WP = cash, OL = transfer
logo?: string;
}
interface PaymentMethodBanks {
cash: PaymentMethodBank[];
transfer: PaymentMethodBank[];
}For safetypayCash / safetypayTransfer, build payment_method.config from country
(bank.country), channel (bank.channel), and bank_ids: [{ id: bank.code }] — the bank
routing code, not the internal bank.id.
Types
import type {
TonderConfig,
PayInput,
RawTransaction,
Customer,
Card,
EnrollResult,
PaymentMethodInfo,
PaymentMethodBank,
PaymentMethodBanks,
CardFieldsOptions,
CardFieldsComponent,
TonderEvents,
PresentationEvents,
} from '@tonder.io/web-sdk';RawTransaction
pay() and getTransaction() return transaction fields in snake_case, matching Tonder's API
and webhook payloads:
interface RawTransaction {
id: string;
operation_type: string;
status: string;
amount: number;
currency: string;
client_reference?: string;
metadata?: Record<string, unknown>;
provider?: string;
created_at?: string;
status_code?: number;
next_action?: {
redirect_to_url?: {
url: string;
verify_transaction_status_url?: string;
};
};
decline_code?: string;
decline_reason?: string;
payment_instructions?: Record<string, unknown>;
voucher_pdf?: string;
clabe?: string;
bank_name?: string;
[key: string]: unknown;
}Errors
SDK failures are thrown as AppError. Payment declines are not thrown: they're returned as
transactions — read transaction.status. Branch on error.code; don't parse error.message.
try {
const transaction = await tonder.pay({ /* ... */ });
} catch (error) {
if (error instanceof AppError) {
console.error(error.code, error.status_code, error.details.system_error);
if (error.code === ErrorKeyEnum.MISSING_CUSTOMER) {
// Recreate the SDK with session.customer.
}
} else {
throw error;
}
}Main codes:
| Code | When it happens | How to fix |
|---|---|---|
INIT_ERROR | SDK initialization failed. | Check api_key, environment, and network access. |
NOT_INITIALIZED | A method needs init() completed. | Call await tonder.init() before the operation. |
MISSING_CUSTOMER | session.customer is missing. | Create the SDK with session.customer.email. |
SECURE_TOKEN_REQUIRED | Saved-card operations require session.secure_token. | Mint the token on your backend and pass it in createTonder(). |
INVALID_PAYMENT_REQUEST | amount, return_url, client_reference, or payment_method is invalid. | Validate the request before calling pay(). |
INVALID_APM_CONFIG | safetypayCash/safetypayTransfer is missing config.country, config.channel, or config.bank_ids. | Build config from the selected getPaymentMethodBanks() bank. |
MOUNT_COLLECT_ERROR | Secure fields could not mount or collect valid data. | Ensure the containers exist and the fields are complete. |
SECURE_FIELDS_LOAD_ERROR | The browser could not load the secure fields. | Check CSP, ad blockers, and network access. |
PAYMENT_PROCESS_ERROR | The payment could not be created/processed. | Inspect error.details; retry only when safe/idempotent. |
FETCH_TRANSACTION_ERROR | Transaction lookup failed. | Verify the id and reconcile from backend/webhooks. |
POLL_TIMEOUT_ERROR | Embedded 3DS completed but reconciliation didn't reach a final status in time. | Don't fulfill from the client result; reconcile with getTransaction() or webhooks. |
SAVE_CARD_ERROR / REMOVE_CARD_ERROR / CARD_ON_FILE_DECLINED | Card save/removal/enrollment failed. | Ask for another card or inspect error.details. |
FETCH_PAYMENT_METHODS_ERROR / FETCH_PAYMENT_METHOD_BANKS_ERROR | The methods/banks catalog could not be retrieved. | Retry, or pass known method codes directly to pay(). |
The complete list (including rare/compatibility codes) is in the SDK README.
Payment statuses
Read payment state from transaction.status:
| Status | Meaning | What to do |
|---|---|---|
Success | Payment completed. | Confirm the order. |
Authorized | Payment authorized by the processor path. | Continue per your Tonder setup and reconcile with webhooks. |
Pending | Not final yet (redirect 3DS, asynchronous APM/SPEI). | Wait for the webhook or read later with getTransaction(). |
Processing | Still being processed by the provider. | Don't fulfill yet. |
Declined | Issuer/processor declined the payment. | Show a recoverable message. |
Failed | Payment failed. | Show a recoverable message or offer another method. |
Cancelled | Payment was cancelled or voided. | Don't fulfill; let the shopper start a new payment. |
Expired | Not completed in time. | Ask the customer to start a new payment. |
