Hosted Checkout

Crear una sesión

La petición que crea una sesión de Hosted Checkout, campo por campo, y lo que te da la respuesta.

Llama a este endpoint desde tu backend cada vez que un cliente esté listo para pagar. Crea una sesión de checkout —para un pago o para un registro de tarjeta— y devuelve la url a la que envías al cliente.

POST https://api-stage.tonder.io/checkout/v1/sessions   # Sandbox
POST https://api.tonder.io/checkout/v1/sessions          # Producción

Cómo funciona

  1. Tu servidor llama a este endpoint con el cliente, el monto y los artículos.
  2. Tonder crea la sesión y, en las sesiones de pago, el pago. Recibes la url del checkout y el payment_id.
  3. Rediriges al cliente a la url, o la embebes.
  4. El cliente paga en la página de Tonder. Puede reintentar en la misma sesión.
  5. El cliente regresa a tu sitio y confirmas el resultado con webhooks o consultando la sesión.

Una sesión es de un solo uso y es válida por un máximo de 24 horas. Su monto, moneda y artículos no se pueden cambiar; en su lugar, crea una sesión nueva.

Headers

HeaderValorRequeridoDescripción
AuthorizationToken <your_api_key>SíTu llave API, desde Developers en el Dashboard. Llama a la API solo desde tu backend.
Content-Typeapplication/jsonSíEl cuerpo es JSON.
x-idempotency-keyCualquier cadena únicaNoEvita crear la misma sesión dos veces cuando reintentas una petición dentro de 5 segundos. Consulta Idempotencia.

Cuerpo

CampoTipoRequeridoValoresDescripción
session_typestringNopayment, enrollmentPor defecto payment. enrollment guarda una tarjeta sin cobrar; consulta Guardar una tarjeta sin cobrar.
customerobjectSíVer ClienteEl cliente que paga. Las tarjetas guardadas pertenecen a este cliente.
amount_totalnumberSolo en pagosMayor que 0Total a cobrar, en formato decimal (150.00). No se valida contra la suma de line_items.
currencystringSolo en pagosp. ej. MXNLa moneda de tu negocio.
line_itemsarraySolo en pagosVer ArtículosLo que el cliente está pagando.
payment_method_typesarrayNoVer Métodos de pagoMétodos a ofrecer. Por defecto ["card"].
payment_method_configobjectNoVer Tarjetas guardadasOpciones para tarjetas guardadas y Mercado Pago.
checkout_ui_versionstringNov1, v2Diseño del checkout. v2 es el actual. Si se omite, aplica el valor por defecto de tu negocio y después v2.
submit_typestringNopay, deposit, subscribeTexto del monto y del botón de pago. Un texto de botón personalizado en ui_config tiene precedencia.
localestringNoes, en, zhIdioma en el que abre el checkout. El cliente puede cambiarlo en la página.
ui_configobjectNoVer Personalizar el checkoutApariencia solo para esta sesión.
ui_config_versionstringNop. ej. V1Cuál de las apariencias guardadas de tu negocio usar. Por defecto V1. No tiene relación con checkout_ui_version.
success_url, pending_url, cancel_urlstringNoURLVer URLs de redirección.
return_urlstringSí, si no hay success_urlURLRedirección de respaldo.
expires_atnumberNoTiempo Unix en segundosEntre 30 minutos y 24 horas a partir de ahora. Por defecto 24 horas.
external_idstringNo—Tu referencia del pedido. Se genera por ti si no envías una.
metadataobjectNo—Cualquier dato que quieras recibir de vuelta en las respuestas y los webhooks. Repite aquí tu referencia del pedido como metadata.external_id.
checkout_typestringNohosted, embeddedPor defecto hosted. Usa embedded para un iframe.
post_message_enabledbooleanNotrue, falseEnvía eventos a tu página. Requiere checkout_type: "embedded".
redirect_on_completionstringNoalways, neverPor defecto always. never mantiene al cliente en el checkout para que tu página maneje la navegación.

Cliente

CampoTipoRequeridoDescripción
first_name, last_name, emailstringSíIdentidad del cliente.
phonestringNoTeléfono del cliente, en E.164. Si Tonder configuró tu cuenta para requerir además el teléfono para las tarjetas guardadas, envíalo en cada sesión que incluya saved_cards, siempre el mismo valor. No reemplaza al email, que siempre identifica al cliente. Consulta A qué cliente pertenece una tarjeta guardada.
addressobjectNocountry (requerido dentro del objeto), state, city, streets, postal_code.
identificationobjectNotype y number (ambos requeridos dentro del objeto).

Artículos

CampoTipoRequeridoDescripción
namestringSíNombre del artículo.
quantitynumberSíNúmero entero, 1 o más.
unit_pricenumberSíPrecio por unidad, mayor que 0.
description, product_idstringNoDescripción del artículo y tu referencia del producto.
discount, taxesnumberNoMontos por línea, mostrados en el resumen del pedido.
tax_ratenumberNoTasa que se muestra junto a los impuestos. Solo informativa.

Los artículos son solo para mostrarse. Al cliente siempre se le cobra amount_total. El checkout muestra el desglose completo solo cuando los artículos suman amount_total; de lo contrario, muestra únicamente el total.

Ejemplo

curl -X POST 'https://api-stage.tonder.io/checkout/v1/sessions' \
  -H 'Authorization: Token YOUR_TEST_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'x-idempotency-key: ORD-001-attempt-1' \
  -d '{
    "customer": { "first_name": "Maria", "last_name": "Garcia", "email": "maria@example.com" },
    "amount_total": 150.00,
    "currency": "MXN",
    "external_id": "ORD-001",
    "line_items": [
      { "name": "Premium Plan", "quantity": 1, "unit_price": 150.00, "product_id": "plan-premium-01" }
    ],
    "payment_method_types": ["card", "saved_cards", "oxxopay"],
    "submit_type": "pay",
    "locale": "en",
    "return_url": "https://your-store.com/checkout/complete",
    "metadata": { "external_id": "ORD-001" }
  }'
curl -X POST 'https://api.tonder.io/checkout/v1/sessions' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'x-idempotency-key: ORD-001-attempt-1' \
  -d '{
    "customer": { "first_name": "Maria", "last_name": "Garcia", "email": "maria@example.com" },
    "amount_total": 150.00,
    "currency": "MXN",
    "external_id": "ORD-001",
    "line_items": [
      { "name": "Premium Plan", "quantity": 1, "unit_price": 150.00, "product_id": "plan-premium-01" }
    ],
    "payment_method_types": ["card", "saved_cards", "oxxopay"],
    "submit_type": "pay",
    "locale": "en",
    "return_url": "https://your-store.com/checkout/complete",
    "metadata": { "external_id": "ORD-001" }
  }'
{
  "id": "cs_97_41528_42434df6c3644b9610df7b64a1fa9925e3b9562b05972097",
  "url": "https://stage-payflow.tonder.io/checkout/cs_97_41528_42434df6c3644b9610df7b64a1fa9925e3b9562b05972097",
  "status": "pending",
  "session_type": "payment",
  "checkout_type": "hosted",
  "payment_id": 41528,
  "checkout_ui_version": "v2",
  "submit_type": "pay",
  "locale": "en",
  "amount_total": 150.00,
  "currency": "MXN",
  "external_id": "ORD-001",
  "return_url": "https://your-store.com/checkout/complete",
  "payment_method_types": ["card", "saved_cards", "oxxopay"],
  "ui_config": {},
  "ui_config_version": "V1",
  "created_at": 1751483563558,
  "modified_at": 1751483563558,
  "expires_at": 1751569963
}

Qué te da la respuesta

CampoDescripción
idEl id de la sesión. Úsalo con Consultar una sesión.
urlLa página de checkout a la que rediriges o que embebes. Nunca cambia, aunque después cambies tus valores por defecto.
statuspending, completed o expired.
payment_idUn número que identifica el pago. Existe desde la creación, nunca cambia y se mantiene igual cuando el cliente reintenta con otra tarjeta. 0 en las sesiones de registro de tarjeta.
checkout_ui_version, submit_type, localeLos valores aplicados a esta sesión, fijos desde la creación.
payment_flowdirect cuando tu cuenta se procesa con la API Direct; en otro caso no está presente.
direct_transaction_idSolo en cuentas de API Direct, una vez que el cliente ha intentado pagar: el id del intento más reciente.

La lista completa de identificadores y estados está en la Referencia.

Intentos de pago

Un cliente puede intentar pagar más de una vez en la misma sesión: una tarjeta es rechazada y reintenta con otra. La sesión conserva solo el intento más reciente: transaction_status (y direct_transaction_id en cuentas de API Direct) se reemplaza con cada intento nuevo, así que un primer Declined puede convertirse en Success. El payment_id se mantiene igual durante toda la sesión, así que todos los intentos pertenecen al mismo pago.

Errores

Estado HTTPCódigoSignificadoQué hacer
400E004La petición es inválida: falta un campo requerido, un valor no está permitido, o se envió un campo de pago en una sesión de registro de tarjeta.Lee metadata.errors (una entrada por problema), corrige la petición y envíala de nuevo.
401 / 403—Llave API faltante o inválida.Revisa el header Authorization y que la llave pertenezca a este entorno.
500E001Error inesperado.Reintenta con la misma x-idempotency-key; contacta a soporte si persiste.
{
  "code": "E004",
  "message": "The request was invalid. Please check your input and try again.",
  "metadata": {
    "errors": [
      {
        "keyword": "required",
        "params": { "missingProperty": "currency" },
        "message": "must have required property 'currency'",
        "field": "currency"
      }
    ]
  },
  "name": "TNDR004",
  "statusCode": 400
}

Tarjetas guardadas

Incluye saved_cards en payment_method_types y el checkout muestra las tarjetas que el cliente ya guardó, junto al formulario de tarjeta nueva. Listarlas, seleccionarlas y eliminarlas se maneja por ti: no hay un endpoint extra que llamar.

Esta es la vía de Hosted Checkout. Si renderizas tu propio formulario con el SDK Web, las tarjetas guardadas funcionan distinto; consulta Tarjetas guardadas (secure_token).

A qué cliente pertenece una tarjeta guardada

Las tarjetas guardadas siempre pertenecen al cliente identificado por el customer.email de la sesión. Tonder puede configurar tu cuenta para requerir además customer.phone para las tarjetas guardadas: el cliente se sigue identificando por su email, y el teléfono es lo que habilita sus tarjetas guardadas. Pregunta a soporte de Tonder si tu cuenta lo requiere — no se puede consultar por API.

Cuando tu cuenta requiere teléfono:

  • Envía customer.email y customer.phone en cada sesión que incluya saved_cards, incluida aquella en la que la tarjeta se guarda por primera vez (y en cada sesión de registro de tarjeta). Sin customer.phone no hay tarjetas guardadas: el cliente no las ve.
  • El teléfono no reemplaza al email. El mismo teléfono con otro email es otro cliente y no ve esas tarjetas.
  • Tonder compara el teléfono exactamente como lo envías. No se normaliza: +525512345678 y 5512345678 no son el mismo valor. Normaliza el número en tu propia aplicación antes de crear la sesión y envía siempre el mismo valor para el mismo cliente, en E.164 — +, código de país y número, por ejemplo +525512345678.
"customer": {
  "first_name": "Maria",
  "last_name": "Garcia",
  "email": "maria@example.com",
  "phone": "+525512345678"
}

La casilla de guardar tarjeta

Pon payment_method_config.saved_cards.show_save_card_checkbox en true para mostrar una casilla "guardar esta tarjeta para futuros pagos" en el formulario de tarjeta nueva, para que el cliente pueda elegir guardar su tarjeta.

CampoTipoDescripción
show_save_card_checkboxbooleanMuestra una casilla "guardar esta tarjeta para futuros pagos" en el formulario de tarjeta nueva. No se muestra cuando Card-on-File está habilitado, porque entonces las tarjetas se guardan automáticamente.
curl -X POST 'https://api-stage.tonder.io/checkout/v1/sessions' \
  -H 'Authorization: Token YOUR_TEST_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "customer": { "first_name": "Maria", "last_name": "Garcia", "email": "maria@example.com" },
    "amount_total": 150.00,
    "currency": "MXN",
    "line_items": [
      { "name": "Premium Plan", "quantity": 1, "unit_price": 150.00 }
    ],
    "payment_method_types": ["card", "saved_cards"],
    "payment_method_config": {
      "saved_cards": { "show_save_card_checkbox": true }
    },
    "external_id": "ORD-001",
    "return_url": "https://your-store.com/checkout/complete"
  }'
curl -X POST 'https://api.tonder.io/checkout/v1/sessions' \
  -H 'Authorization: Token YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "customer": { "first_name": "Maria", "last_name": "Garcia", "email": "maria@example.com" },
    "amount_total": 150.00,
    "currency": "MXN",
    "line_items": [
      { "name": "Premium Plan", "quantity": 1, "unit_price": 150.00 }
    ],
    "payment_method_types": ["card", "saved_cards"],
    "payment_method_config": {
      "saved_cards": { "show_save_card_checkbox": true }
    },
    "external_id": "ORD-001",
    "return_url": "https://your-store.com/checkout/complete"
  }'

Card-on-File

Card-on-File (COF) guarda las tarjetas automáticamente y permite que el cliente vuelva a pagar sin volver a capturar su CVV. Funciona sobre la infraestructura de suscripciones y tokenización de Tonder.

Cuando Card-on-File está habilitado para tu cuenta, el checkout:

  1. Ejecuta una verificación 3D Secure al guardar la tarjeta.
  2. Crea una suscripción para esa tarjeta.
  3. Acepta pagos con ella en visitas posteriores sin pedir el CVV.

Si Card-on-File está habilitado para tu cuenta, el checkout se encarga de todo esto por sí solo. Lo único que tu integración puede necesitar enviar es customer.phone, cuando tu cuenta requiere además el teléfono para las tarjetas guardadas — consulta A qué cliente pertenece una tarjeta guardada. Es la misma suscripción que el SDK expone como subscription_id; consulta la Referencia del SDK Web.

Enviar show_save_card_checkbox: true con Card-on-File activo no mostrará la casilla. Es el comportamiento esperado: Card-on-File tiene precedencia y las tarjetas se guardan automáticamente de cualquier forma.

Referencia de la API

POST
/checkout/v1/sessions

Autorización

Authorization
Authorization<token>

Your public API key, with the word Token and a space in front of it. Example: Token 6534bc0a7e1f4d2b9c8e3a5f7b1d2c4e6f8a9b0c — that whole value is the header. Sending the key alone returns 401 'Authentication credentials were not provided'. In this playground you may paste just the key; the prefix is added for you.

In: header

Cuerpo de la petición

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Cuerpo de la respuesta

application/json

application/json

application/json

curl -X POST "https://example.com/checkout/v1/sessions" \  -H "Content-Type: application/json" \  -d '{    "customer": {      "first_name": "Maria",      "last_name": "Garcia",      "email": "maria@example.com"    },    "amount_total": 150,    "currency": "MXN",    "external_id": "ORD-001",    "line_items": [      {        "name": "Premium Plan",        "quantity": 1,        "unit_price": 150,        "product_id": "plan-premium-01"      }    ],    "payment_method_types": [      "card",      "saved_cards",      "oxxopay"    ],    "submit_type": "pay",    "locale": "en",    "return_url": "https://your-store.com/checkout/complete",    "metadata": {      "external_id": "ORD-001"    }  }'
{
  "id": "cs_97_41528_42434df6c3644b9610df7b64a1fa9925e3b9562b05972097",
  "url": "https://stage-payflow.tonder.io/checkout/cs_97_41528_42434df6c3644b9610df7b64a1fa9925e3b9562b05972097",
  "status": "pending",
  "session_type": "payment",
  "checkout_type": "hosted",
  "payment_id": 41528,
  "checkout_ui_version": "v2",
  "submit_type": "pay",
  "locale": "en",
  "amount_total": 150,
  "currency": "MXN",
  "external_id": "ORD-001",
  "return_url": "https://your-store.com/checkout/complete",
  "payment_method_types": [
    "card",
    "saved_cards",
    "oxxopay"
  ],
  "ui_config": {},
  "ui_config_version": "V1",
  "created_at": 1751483563558,
  "modified_at": 1751483563558,
  "expires_at": 1751569963
}
{
  "code": "E004",
  "message": "The request was invalid. Please check your input and try again.",
  "metadata": {
    "errors": [
      {
        "keyword": "required",
        "params": {
          "missingProperty": "currency"
        },
        "message": "must have required property 'currency'",
        "field": "currency"
      }
    ]
  },
  "name": "TNDR004",
  "statusCode": 400
}
{
  "code": "E004",
  "message": "The request was invalid. Please check your input and try again.",
  "metadata": {},
  "name": "TNDR004",
  "statusCode": 400
}

Siguientes pasos

¿Te resultó útil esta página?

En esta página