Apple Pay a través del Widget
Esta guía le guiará en la integración de Apple Pay utilizando los SDK de DEUNA
| Widget de Pago | Caja de Pago | |
|---|---|---|
| ¿Qué hace?** | Gestiona el proceso de pago completo: interfaz de usuario, procesamiento de pagos y confirmación | Tokeniza la tarjeta únicamente: devuelve un ID de tarjeta para que lo utilice |
| ¿Quién realiza la compra? | DEUNA (internamente) | Usted (a través de la API de Compra de su backend) |
| Botón de Apple Pay | Renderizado dentro del iframe de DEUNA | Renderizado por usted, en su propia interfaz de usuario |
| Método del SDK | initPaymentWidget | initElements({ types: ['APPLE_PAY'] }) |
| Utilice cuando | quiera una experiencia de pago sencilla | necesite controlar el flujo de pago o desee guardar la tarjeta para más adelante |
1. Requisitos previos#
Antes de comenzar, asegúrese de que los siguientes elementos estén disponibles:
| Requisito | Notas |
|---|---|
| Cuenta de DEUNA | Cuenta de comerciante activa en el panel de control de DEUNA. |
publicApiKey | Clave de API pública emitida por DEUNA. Es necesaria para DeunaSDK.initialize. |
orderToken | generada en su backend a través de la API de Pedidos de DEUNA. Es necesaria para iniciar un pago. |
userToken (opcional) | Solo se requiere cuando desee tokenizar la tarjeta contra un usuario de DEUNA conocido. |
| Dominio HTTPS | Apple Pay requiere que la página se sirva a través de HTTPS. localhost solo está permitido para el desarrollo. |
| Requisitos de compatibilidad | Consulte la documentación oficial de Apple Pay en la Web y implementación de Apple Pay Consulte la documentación. Apple Pay requiere Safari en iOS 10+ o macOS 10.12+. En navegadores que no son Safari (Chrome, Edge, Firefox), está disponible un flujo de código QR para usuarios con un iPhone que ejecute iOS 18 o posterior. |
1.1. Verificación del dominio (requerido para ambos caminos)
Cada dominio que renderice un botón de Apple Pay, ya sea a través del Widget de Pago o del Vault, debe estar registrado con Apple a través de DEUNA. Esto se aplica a los dominios de producción y a cualquier entorno de desarrollo o previsualización.
Paso a paso
- Obtener el archivo de asociación del dominio del panel de control de DEUNA (o de Apple Pay si utiliza su propio ID de comerciante). El archivo suele tener el nombre
apple-developer-merchantid-domain-association(sin extensión) o.txt. - Hospedar el archivo sin modificar en:
Asegúrese de que:Plain text
https://<your-domain>/.well-known/apple-developer-merchantid-domain-association- La respuesta se sirve a través de HTTPS.
Content-Typeestext/plain; charset=utf-8.- No hay redirección, no hay muro de autenticación, no hay 404. La respuesta debe ser
200 OK.
- Verificar el dominio en el panel de control de DEUNA (o en el portal de desarrolladores de Apple). Apple recuperará el archivo de su servidor; si coincide, el dominio está registrado.
- Repetir por dominio. Cada nombre de host que cargue el widget (por ejemplo,
checkout.mystore.com,staging.mystore.com) debe servir el archivo y registrarse por separado.
Implementación de referencia (Next.js - Ejemplo)
Este repositorio ya implementa el punto final de verificación. Se han configurado dos cosas:
next.config.js — se redirigen tanto la ruta canónica como .txt a una ruta de API:
async rewrites() {
return [
{
source: '/.well-known/apple-developer-merchantid-domain-association',
destination: '/api/apple-verification',
},
{
source: '/.well-known/apple-developer-merchantid-domain-association.txt',
destination: '/api/apple-verification',
},
];
}src/pages/api/apple-verification.ts — devuelve el contenido del archivo como text/plain:
import { NextApiRequest, NextApiResponse } from 'next';
export default function domainVerification(
_req: NextApiRequest,
res: NextApiResponse,
) {
const fileContents = process.env.NEXT_PUBLIC_APPLE_VALIDATION_CONTENT_FILE;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.status(200).send(fileContents);
}El contenido del archivo se almacena en una variable de entorno (NEXT_PUBLIC_APPLE_VALIDATION_CONTENT_FILE) para que puedan ser rotadas según el entorno sin necesidad de realizar cambios de código.
Cómo verificar que funciona
curl -i https://<your-domain>/.well-known/apple-developer-merchantid-domain-associationconst response = await fetch("https://<your-domain>/.well-known/apple-developer-merchantid-domain-association", {
method: "GET"
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const data = await response.json();import requests
response = requests.request(
"GET",
"https://<your-domain>/.well-known/apple-developer-merchantid-domain-association",
)
response.raise_for_status()
data = response.json()<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://<your-domain>/.well-known/apple-developer-merchantid-domain-association",
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true
]);
$response = curl_exec($curl);
curl_close($curl);import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://<your-domain>/.well-known/apple-developer-merchantid-domain-association"))
.method("GET", HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(
request, HttpResponse.BodyHandlers.ofString()
);
System.out.println(response.body());
}
}package main
import (
"fmt"
"io"
"net/http"
)
func main() {
request, err := http.NewRequest("GET", "https://<your-domain>/.well-known/apple-developer-merchantid-domain-association", nil)
if err != nil { panic(err) }
response, err := http.DefaultClient.Do(request)
if err != nil { panic(err) }
defer response.Body.Close()
body, _ := io.ReadAll(response.Body)
fmt.Println(string(body))
}Esperar:
- HTTP
200 OK Content-Type: text/plain; charset=utf-8- Un cuerpo que comience con
7B22...(el blob de Apple)
2. Integraciones#
Asegúrese de seguir la guía de inicio rápido para nuestros SDKs según su integración específica:
2.1. Widget de Pago
Características específicas de Apple Pay
- No se necesitan configuraciones adicionales en el código. Apple Pay está habilitado o deshabilitado desde el panel de control de DEUNA. Si el comerciante lo tiene habilitado y el dispositivo del usuario lo admite, el botón aparece automáticamente.
userTokenEs opcional: solo se necesita cuando desea asociar el pago con una cuenta de usuario de DEUNA conocida.- Las llamadas de retorno del widget estándar (
onSuccess,onError,onClosed,onPaymentProcessing) funcionan de la misma manera que para otros métodos de pago.
¿Cuándo necesito llamar a getWalletsAvailable en el Widget de Pago?
Si abre el widget con todos los métodos de pago habilitados, no necesita llamar a este método: DEUNA renderiza el selector de métodos de pago (incluidas las tarjetas) y resuelve la disponibilidad internamente.
Solo necesita getWalletsAvailable cuando renderiza su propio botón de Apple Pay fuera del selector y utiliza el Widget de Pago en modo independiente para pasar directamente a ese método de pago. En ese caso, llámelo previamente para decidir si mostrar su botón.
const { DeunaSDK } = window; // from https://cdn.deuna.io/web-sdk/v1.6/index.js
await DeunaSDK.initialize({
env: 'sandbox',
publicApiKey: '<YOUR_PUBLIC_API_KEY>',
});
// If Apple Pay is available, it should return it within an array, such as: ["APPLE_PAY"]
const available = await DeunaSDK.getWalletsAvailable();
if (available.includes('APPLE_PAY')) {
// Render button for Apple Pay and append a listener (EXAMPLE)
btn.addEventListener('click', () => {
DeunaSDK.initPaymentWidget({
orderToken: '<order-token>', // REQUIRED
paymentMethods: [
{
paymentMethod: 'wallet',
processors: ['apple_pay'],
},
], // In case only the specific payment method is to be used
callbacks: {
onSuccess: (order) => {
console.log('Payment completed:', order);
},
onError: (error) => {
console.error('Payment failed:', error.metadata.message);
},
onClosed: (action) => {
console.log('Widget closed:', action);
},
},
});
});
} else {
console.error('Apple Pay is not available on this device.');
}2.2. Almacén de Pagos
SDK Web
Permita que el SDK Web resuelva las credenciales de Apple Pay desde el backend de DEUNA utilizando su publicApiKey y (opcionalmente) un orderToken.
Para obtener más información, consulte la getWalletsAvailable documentation.
const { DeunaSDK } = window; // from https://cdn.deuna.io/web-sdk/v1.6/index.js
await DeunaSDK.initialize({
env: 'sandbox',
publicApiKey: '<YOUR_PUBLIC_API_KEY>',
});
// If Apple Pay is available, it should return it within an array, such as: ["APPLE_PAY"]
const available = await DeunaSDK.getWalletsAvailable();
if (available.includes('APPLE_PAY')) {
// Render button for Apple Pay and append a listener (EXAMPLE)
btn.addEventListener('click', () => {
deuna.initElements({
types: [{ name: 'APPLE_PAY' }],
orderToken: '<order-token>', // REQUIRED FOR MERCHANTS
userInfo: {
email: '<email>',
firstName: '<firstName>',
lastName: '<lastName>',
},
callbacks: {
// Called after user approves the Google Pay sheet.
// Send the token to your backend, return the result.
onSuccess: async (payload) => {
const cardId = payload.data.id;
// use the cardId to process payment
},
onError: (error) => {
console.error('Payment failed:', error.metadata.message);
},
},
});
});
} else {
console.error('Apple Pay is not available on this device.');
}SDK iOS
Requisitos de Wallet de Apple Pay (iOS)
Para utilizar la Wallet de Apple Pay con el SDK en iOS, asegúrese de que todo esté configurado correctamente.
-
Configuración de la aplicación y el equipo
- Su aplicación debe estar firmada con un Equipo de Desarrolladores de Apple que tenga Apple Pay habilitado.
- El identificador del PRODUCT_BUNDLE_IDENTIFIER debe coincidir con el App ID configurado en Apple Developer.
- La aplicación debe estar instalada en un dispositivo iOS real (Apple Pay no funciona completamente en el simulador).
-
Configuración del ID del comerciante
- Crear o utilizar un ID de comerciante en Apple Developer (ejemplo: merchant.test.deuna.dev.pay).
- Asigne ese ID de comerciante al ID de la aplicación de la aplicación en Identificadores > ID de la aplicación > Apple Pay.
- En Xcode, habilite "Firmar y capacidades > Apple Pay" y seleccione el mismo ID de comerciante.
-
Certificados requeridos
Para el ID de comerciante utilizado por el SDK, debe tener:- Certificado de procesamiento de pagos de Apple Pay (requerido para el procesamiento de tokens de Apple Pay dentro de la aplicación).
-
Perfiles de configuración
- Regenere los perfiles de configuración después de cambiar las capacidades o las asignaciones de ID de comerciante.
- Reinstale la aplicación después de los cambios en el perfil/capacidad (Limpiar la carpeta de construcción + eliminar la aplicación del dispositivo + reinstalar).
-
Requisitos de consistencia de SDK/backend
-
El ID de comerciante devuelto por las credenciales del backend (external_merchant_id) debe coincidir exactamente con el ID de comerciante habilitado en la autorización de la aplicación.
-
El flujo de wallet del SDK debe incluir el contexto del usuario (userInfo) cuando sea necesario para que su backend devuelva el token de usuario y el ID de usuario.
-
Si faltan el token/ID de usuario, el tokenización puede fallar incluso si la interfaz de usuario de Apple Pay se abre.
-
Paso 1 — Verificar la disponibilidad Llama a getWalletsAvailable() una vez antes del paso de pago. El SDK valida la configuración del comerciante de DEUNA y la disponibilidad de Apple Pay en el dispositivo.
import DeunaSDK
let deunaSDK = DeunaSDK(
environment: .sandbox,
publicApiKey: "YOUR_PUBLIC_API_KEY"
)
deunaSDK.getWalletsAvailable(
params: GetWalletsAvailableParams(
orderToken: "<order-token>", // optional at this stage
userInfo: DeunaSDK.UserInfo(
email: "user@example.com",
firstName: "Jane",
lastName: "Doe"
) // recommended for wallet auth flows
)
) { wallets, error in
if let error = error {
// handle fetch error
return
}
let applePayAvailable = wallets.contains(.applePay)
// show or hide your Apple Pay button based on applePayAvailable
}getWalletsAvailable() Almacena el resultado. Las llamadas posteriores devuelven los wallets almacenados inmediatamente, por lo que es seguro llamarlo en cada carga de pantalla.
Paso 2 — Lanzar Apple Pay Cuando el usuario hace clic en tu botón de Apple Pay, llama a initElements con APPLE_PAY como tipo. El SDK obtiene las credenciales del pedido y lanza la hoja de Apple Pay nativa.
deunaSDK.initElements(
userToken: "YOUR_ORDER_TOKEN", // required for apple pay wallet
callbacks: ElementsCallbacks(
onSuccess: { payload in
// payload contains tokenized card data
},
onError: { error in
// error.metadata?.code / message
},
onClosed: { _ in
// user dismissed the sheet
},
onEventDispatch: nil
),
closeEvents: [],
userInfo: DeunaSDK.UserInfo(
email: "user@example.com",
firstName: "Jane",
lastName: "Doe"
),
styleFile: nil,
types: [["name": "APPLE_PAY"]],
language: nil,
orderToken: "<order-token>",
widgetExperience: nil,
behavior: nil,
fraudCredentials: nil,
customUserAgent: nil,
domain: nil
)SDK de React Native
Utiliza esta ruta cuando quieras renderizar la hoja de Apple Pay nativa directamente, sin WebView. El SDK verifica la disponibilidad del dispositivo, obtiene las credenciales de Apple Pay del backend de DEUNA y lanza la hoja de pago. El resultado es un payload de tarjeta tokenizado que se entrega a tu callback onSuccess.
Requisitos previos:
- Habilita la funcionalidad de Apple Pay en tu objetivo de la aplicación (Xcode → objetivo → Firma y capacidades → + Capacidad → Apple Pay).
- Añade tu ID de comerciante bajo esa capacidad (por ejemplo, merchant.io.your-domain.com).
- El mismo ID de comerciante debe ser devuelto por las credenciales de DEUNA (external_merchant_id). Confirma esto con tu equipo de DEUNA.
- En Portal de desarrolladores de Apple, ese ID de comerciante debe tener un certificado de procesamiento de pagos de Apple Pay activo.
- Después de cualquier cambio en la capacidad o en el comerciante, genera de nuevo tus perfiles de aprovisionamiento y reinstala la aplicación en un dispositivo real.
exposición — declara la autorización en app.json y ejecuta npx expo prebuild para aplicarla:
{
"expo": {
"ios": {
"entitlements": {
"com.apple.developer.in-app-payments": ["merchant.io.your-domain.com"]
}
}
}
}Reaccionar CLI nativa— añádela a ios/<AppName>/<AppName>.entitlements:
<key>com.apple.developer.in-app-payments</key>
<array>
<string>merchant.io.your-domain.com</string>
</array>import { useState, useEffect } from 'react';
import { DeunaSDK } from '@deuna/react-native-sdk';
// 1) Initialize the SDK
const sdk = new DeunaSDK({
publicApiKey: '<YOUR_PUBLIC_API_KEY>',
environment: 'sandbox', // 'production' | 'sandbox'
});
// 2) Check availability
const available = await sdk.getWalletsAvailable();
// 3) Launch
if (available.includes('apple_pay')) {
sdk.initElements({
orderToken: '<order-token>', // required
types: [{ name: 'apple_pay' }],
userInfo: { // required
email: '<email>',
firstName: '<firstName>',
lastName: '<lastName>',
},
callbacks: {
onSuccess: (payload) => {
const cardId = payload?.card_id;
// use cardId to process payment on your backend
},
onError: (error) => {
console.error('Payment failed:', error.metadata.message);
},
onClosed: (action) => {
console.log('Sheet dismissed by', action);
},
},
});
}4. Posibles problemas#
| Problema | Solución |
|---|---|
| El botón no se renderiza | Verifica getWalletsAvailable() el resultado, el soporte del navegador y HTTPS. |
| La hoja de pago se abre y luego se congela | La validación del comerciante falló — verifica el archivo de verificación del dominio y las credenciales de DEUNA. |
session.begin() Se rechazó silenciosamente | Has perdido la acción del usuario. No await realices tareas prolongadas entre el clic y session.begin(). Utiliza la orderToken + userInfo ruta SSR para resolver de forma previa transactionInfo. |
| Funciona en Safari, no en Chrome | Esperado — la hoja nativa requiere Safari. Otros navegadores vuelven a la secuencia de código QR (iOS 18+). |
Referencia rápida
| API | Propósito |
|---|---|
DeunaSDK.getWalletsAvailable() | Verifica qué billeteras están disponibles en el dispositivo. |
DeunaSDK.initElements({ types, orderToken?, callbacks }) | Inicializa Apple Pay. Renderiza tu propio botón de Apple Pay |
DeunaSDK.initPaymentWidget({ orderToken, callbacks, ... }) | Abre el widget de pago completo de DEUNA (incluido Apple Pay). |
.well-known/apple-developer-merchantid-domain-association | El archivo de verificación del dominio que debes alojar en cada dominio |