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.
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í.
