Apple Pay
Acepta Apple Pay en la web con el botón del Web SDK: habilitación de dominios, disponibilidad, eventos y conciliación.
Apple Pay se acepta en la web mediante el botón del Web SDK (@tonder.io/web-sdk). El SDK
renderiza el botón, presenta la hoja de pago de Apple y procesa el cargo; la transacción cae en
/process/ con la misma forma que un pago con tarjeta y dispara los mismos webhooks — tu
conciliación no cambia.
Apple Pay no existe en el SDK legacy ni como payment_method.type de API Direct. La única
superficie de integración es el componente de botón del Web SDK.
Pruébalo en vivo: el demo de Apple Pay corre el código de esta página — ábrelo en Safari (macOS, iOS o iPadOS) para ver el botón real y el resultado de disponibilidad.
Paso 1: pide a Tonder habilitarlo y registrar tus dominios
No es un paso de código, y es la razón más común por la que una integración correcta falla en producción.
- Tonder trata con Apple — tú nunca los contactas y no necesitas cuenta de desarrollador de Apple.
- Tu parte es enviar a Tonder la lista de dominios que mostrarán el botón — los subdominios cuentan por separado — y alojar el archivo de verificación que Tonder te da.
Empieza este paso antes de escribir código: condiciona todo lo demás. Los pasos exactos y los modos de falla están en el README del SDK.
Paso 2: renderiza el botón
Apple Pay es el único flujo que no termina en una llamada a pay(). Apple exige que la hoja de
pago se abra en el mismo tick que el toque, así que el SDK es dueño del clic, y el resultado
llega por los callbacks de events.payment en lugar de una promesa devuelta.
Esos callbacks no son un mecanismo exclusivo de Apple Pay — se disparan para todos los métodos que el SDK cobra, así que un solo conjunto de handlers cubre el checkout completo.
El SDK renderiza el botón dentro de un elemento tuyo. Debe existir antes de mount() y debe
quedar vacío — no le pongas un botón, un texto ni un icono dentro:
<div id="tonder-apple-pay-button"></div>const tonder = createTonder({
api_key: tonderPublicConfig.api_key,
environment: 'stage', // cambia a 'production' al salir a producción
session: { customer: { email: 'ada@example.com' } },
// Se disparan para TODOS los métodos, no solo Apple Pay. Con pay() corren
// junto a la promesa que devuelve; Apple Pay no tiene promesa, así que aquí
// estos callbacks son el único canal.
events: {
payment: {
on_completed: (transaction) => handleResult(transaction),
on_error: (error) => showError(error.code),
on_cancel: () => { /* el comprador cerró la hoja — no es un error */ },
},
},
});
await tonder.init();
// Pregunta antes de renderizar. La respuesta es un OBJETO, nunca un booleano:
// { available: true }, o { available: false, code, message }. Usar el objeto
// como condición siempre sería verdadero — lee .available.
const availability = tonder.isApplePayAvailable();
if (availability.available) {
const button = tonder.create('apple_pay_button', {
// Se llama SÍNCRONAMENTE cuando el comprador toca, así que lee lo que el
// carrito tenga en ese momento — monto, moneda y referencias pueden
// cambiar después del mount sin volver a montar el botón. No debe ser
// async: Apple exige que la hoja abra en el mismo tick que el toque.
payment: () => ({
amount: 250,
currency: 'MXN',
return_url: 'https://merchant.example.com/return',
client_reference: 'ORD-001',
}),
});
await button.mount();
} else {
// No adivines la razón: solo APPLE_PAY_UNSUPPORTED_BROWSER significa
// "ofrece otro método"; los otros dos códigos son tuyos por arreglar.
console.info('Apple Pay oculto:', availability.code, availability.message);
}Cuando la disponibilidad es false
code | Significa | Qué hacer |
|---|---|---|
APPLE_PAY_UNSUPPORTED_BROWSER | El navegador no puede correr Apple Pay | Ofrece otro método de pago — es el único código que es problema del comprador |
APPLE_PAY_NOT_ENABLED | Apple Pay no está habilitado para tu negocio | Tuyo por arreglar — ver el paso 1 |
NOT_INITIALIZED | Llamaste antes de que init() terminara | Tuyo por arreglar — espera el await tonder.init() |
Personaliza el botón
Safari dibuja el control de forma nativa, así que Apple solo permite cambiar estas llaves en
customization.apple_pay_button — cualquier otra cosa se ignora:
| Llave | Ejemplo |
|---|---|
type | 'check-out' (el call to action) |
style | 'black' |
locale | 'es-MX' (el idioma de la etiqueta) |
width / height | '100%' / '48px' |
border_radius | '8px' |
El SDK lee customization una sola vez en createTonder() — cambiarla implica construir una
instancia nueva y volver a montar. Lo mismo aplica a api_key y session.customer.
Un intento liquidado consume sus referencias, sea cual sea el resultado. En on_completed,
acuña un client_reference y un idempotency_key nuevos para que el siguiente toque sea una
orden nueva con su propio alcance de idempotencia.
Dónde va el botón en tu cajero
Apple Pay gana su lift de conversión dejando que el comprador se salte el formulario de tarjeta por completo — así que el layout que funciona es el exprés: el botón primero, un divisor, y tus demás métodos debajo, intactos.
Apple Pay es un método de pago más, no un reemplazo. Mantén visible tu botón de tarjeta de
crédito y débito — y todos los demás métodos — junto a él. Un comprador cuyo navegador o wallet no
puede usar Apple Pay sigue necesitando una forma de pagar: ese es exactamente el caso de
APPLE_PAY_UNSUPPORTED_BROWSER.
Las prácticas siguientes siguen las Human Interface Guidelines de Apple:
| Práctica | Por qué |
|---|---|
| Ofrécelo temprano — posición exprés, arriba del formulario de tarjeta | Su valor es saltarse el formulario; esconderlo tras un paso de "elige tu método" lo tira a la basura |
| Dale al menos la prominencia de tu botón de pago principal | El mismo ancho o mayor, y nunca fuera del viewport cuando el botón de tarjeta no lo está |
Ajústalo a los controles de tu cajero con customization.apple_pay_button | width, height y border_radius son la vía sancionada para integrarlo |
| Renderízalo solo cuando está disponible | Nunca muestres un botón de Apple Pay deshabilitado o en gris |
| Nunca lo reconstruyas ni lo decores | El contenedor queda vacío y el control nativo de Safari es el único renderizado permitido |
El error a evitar es el reemplazo: cambiar el botón de tarjeta por Apple Pay deja varado a todo comprador para quien no está disponible. Los cajeros que convierten conservan ambos.
Lo que queda fuera de esta guía — un id de contenedor personalizado y liberar el botón en un cambio de ruta — está en Apple Pay en el README del SDK.
pay({ payment_method: { type: 'apple_pay' } }) se rechaza a propósito. Apple Pay no puede
cobrarse vía pay() — el requisito de gesto de Apple es la razón. Usa el componente de botón.
Paso 3: tu conciliación no cambia
Nada que hacer. El cargo de Apple Pay cae en /api/v1/process/ como tus cargos con tarjeta,
produce una transacción con la misma forma y dispara el mismo webhook. Tu handler existente ya lo
cubre.
Envía client_reference en el payment del botón exactamente como hoy y tu correlación sigue
funcionando. Ver Modelo de webhooks.
Prueba Apple Pay
| Prueba | Qué demuestra que funcionó |
|---|---|
| La hoja abre en un dispositivo real | El Simulador de iOS no puede probar Apple Pay web |
| Un rechazo | Llega en on_completed con estado declinado, no en on_error |
client_reference | Aparece en el webhook y correlaciona tu orden |
La fila del rechazo es la que sorprende: on_completed significa que el cargo llegó a una
respuesta final, no que la respuesta fue sí.
Tarjetas de prueba del sandbox
Las tarjetas de prueba de Tonder (4000 0000 0000 0077 y el resto de
Casos de prueba de tarjeta) no se pueden agregar a Wallet — el
sandbox de Apple solo aprovisiona las credenciales de prueba de Apple. Para abrir una hoja de Apple
Pay real en sandbox:
En App Store Connect ve a Users and Access → Sandbox → Testers e invita a un tester.
Cierra sesión de iCloud en el iPhone o iPad de prueba e inicia sesión con la cuenta de sandbox tester. La región del dispositivo debe ser una donde Apple Pay esté disponible.
Wallet → Agregar tarjeta de crédito o débito, captura manualmente una de las tarjetas de abajo, luego abre tu cajero en Safari y paga.
Apple las publica como sus FPAN más recientes (agosto de 2025). Aquí solo se reproducen las redes que Tonder procesa; la lista completa está en Apple Pay sandbox testing.
Visa
| Número de tarjeta | Vencimiento | CVV |
|---|---|---|
4622 9431 2318 9285 | 12/2028 | 096 |
4622 9431 2318 9293 | 12/2028 | 413 |
4622 9431 2318 9301 | 12/2028 | 752 |
4622 9431 2318 9319 | 12/2028 | 356 |
4622 9431 2318 9327 | 12/2028 | 994 |
4622 9431 2318 9335 | 12/2028 | 777 |
4622 9431 2318 9343 | 12/2028 | 868 |
4622 9431 2318 9350 | 12/2028 | 792 |
4622 9431 2318 9368 | 12/2028 | 161 |
4622 9431 2318 9376 | 12/2028 | 732 |
Mastercard
| Número de tarjeta | Vencimiento | CVC |
|---|---|---|
5204 2452 5046 0049 | 01/30 | 111 |
5204 2452 5052 2095 | 01/30 | 111 |
5204 2452 5110 7599 | 01/30 | 111 |
5204 2452 5305 0839 | 01/30 | 111 |
5204 2452 5471 8095 | 01/30 | 111 |
American Express — solo se aprovisiona con la región del dispositivo en Estados Unidos o
Reino Unido. Si Wallet pide un OTP, escribe 111111.
| Número de tarjeta | Región | Vencimiento | CID |
|---|---|---|---|
37273 57230 32000 | Estados Unidos | 12/28 | 7777 |
37272 79248 51007 | Estados Unidos | 12/28 | 1111 |
37272 67850 11008 | Estados Unidos | 12/28 | 1111 |
37677 17299 24003 | Estados Unidos | 12/28 | 1111 |
37677 47309 52005 | Estados Unidos | 12/28 | 1111 |
37420 05569 95003 | Reino Unido | 12/28 | 1111 |
37420 05590 81009 | Reino Unido | 12/28 | 1111 |
37420 05605 82003 | Reino Unido | 12/28 | 1111 |
37420 07381 38001 | Reino Unido | 12/28 | 7777 |
Estas tarjetas funcionan solo en el sandbox de Apple. No se aprovisionan con un Apple ID de
producción y nunca deben usarse contra app.tonder.io. El vencimiento que Wallet muestra para la
tarjeta del dispositivo (DPAN) no tiene que coincidir con el de arriba.
