Apple Pay

This page will help you get started with Apple Pay.

⚠️

Content Security Policy (CSP)

Para que esta wallet funcione correctamente en tu sitio, debés incluir en tu Content Security Policy los dominios que devuelve el endpoint de Koin. Si no los agregás, el botón o el checkout pueden no cargar.

Changelog

FechaVersiónBreaking changesCambios
24 feb 20262.1.0NOAhora se puede forzar el lenguaje mediante el método initialize

V2.1.0

Requisitos

Previo a poder utilizar la SDK en ambiente productivo, es necesario realizar el registro pertinente en Apple del dominio del merchant.

Se deja a continuación un ejemplo con el flujo de registro:

  1. Cliente informa que la SDK será utilizada bajo el dominio https://www.cliente.com.br
  2. KOIN registra dicho dominio en Apple para el merchant
  3. KOIN comparte el archivo de validación (apple-developer-merchantid-domain-association.txt) con el cliente (archivo unico por merchant).
  4. El cliente deberá disponibilizar dicho archivo en https://www.cliente.com.br/.well-known/apple-developer-merchantid-domain-association.txt
  5. Una vez el archivo se encuentre disponible en esa URL, KOIN dispara la validación en Apple para dar por finalizado el registro del merchant.

Integración

Agregar el script de wallets de Koin.

  1. Producción: https://payments.koin.com.br/checkout/sdk/v1/methods.js
  2. Sandbox: https://portal-dev.koin.com.br/checkout/sdk/v1/methods.js

API

  1. Luego de agregar el script de wallets de Koin a tu página tendrás disponible dentro de window un objeto llamado KoinPayments.
  2. Crear una instancia de la clase wallet u order para el método de pago APPLE_PAY de la siguiente manera:
//ApplePay initialization methods
//Initialize APPLE_PAY by Wallet
const applepay = await window.KoinPayments.wallet(initData);

//Initialize APPLE_PAY by Order
const applepay = await window.KoinPayments.order(orderId);

El objeto initData, para el método de inicialización vía Wallet, va a ser lo que devuelve payments en la llamada al InitWallet y debería tener la siguiente estructura:

{
  "provider": {
    "code": "APPLE_PAY",
    "js": "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js",
    "jwt": "eyJhbGciDiJIUzIlNiIsInR5cCI6IkpXVCJ9.eyJjb3V..."
  },
  "dynamic_payment_key": "11c73b07-cc98-4ee8-bb57-a662a7b5829e"
}

En el caso del método de inicialización vía Orden, se espera un string con el orderId generado en Koin.

  1. Una vez hecho esto, sin importar el método de inicialización, tendremos un objeto creado con los siguientes métodos:

  2. initialize(afterInitializedCallback, options?): El método afterInitializedCallback se va a ejecutar solo cuando se haya podido inicializar el sdk de Apple con éxito. Caso contrario se puede recibir el error utilizando onError. Opcionalmente podés pasar options: { language?: 'es' | 'en' | 'pt' } para forzar el idioma del botón.

  3. mount(buttonCssSelector): Los errores relacionados a esta función también se devuelven en el onError. Esta función recibe un parámetro, buttonCssSelector, el cual es selector CSS que apunte a un <div> dónde se va a colocar el botón de Apple Pay.

Apple Pay button
  1. onError({code, origin, message}): Manejo de errores de ApplePay o Koin. Siempre siguen el mismo formato.
  2. onSuccess({data}): Caso exitoso. Devuelve wallet_code, el koin_token (o datos equivalentes) e información de la tarjeta en un mismo objeto.
//onSuccess
{
    "wallet_code": "APPLE_PAY",
    "card": {
        "type": "CREDIT",
        "brand": {
            "name": "Visa",
            "code": "VI"
        },
        "instrument_token": "5e8fa408-7539-4402-85f3-43c6e9635177"
    }
}
  1. onEvent(code, payload): Eventos para comunicar el estado del proceso cuando sea posible y mantener al tanto al consumidor de nuestro script. El payload es opcional dependiendo del evento que se dispare.

  2. onFinish(data): Callback de la orden. Solo aplica con inicialización por order. En contexto wallet, el pago en vivo no llama a onFinish; el resultado va por onSuccess.

    data es { status, message }. status tiene dos formas:

    • Orden ya terminada al llamar a initialize: status es un string (Authorized, Collected, Cancelled, Failed o Voided) y message es Order already finished.
    • Pago en vivo, éxito o soft fail: status es el objeto { type, date? } y message es Order status updated. type usa los mismos valores. El estado se lee en data.status.type.
  3. destroy(): Elimina la instancia existente del SDK de Apple Pay si hay una.

Eventos

CodeDescriptionContexto
SCRIPT_LOADEDOcurre cuando el script de ApplePay es cargado.Wallet Order
USER_AGENT_DETECTEDOcurre al montar el botón, con datos de debug (navegador, dispositivo, versión iOS, etc.). Payload opcional.Wallet Order
BUTTON_LOADEDOcurre cuando el botón de ApplePay aparece en pantalla.Wallet Order
SESSION_STARTEDOcurre cuando el usuario hace clic en el botón de “Pagar con Apple Pay”.Wallet Order
SESSION_COMPLETEDOcurre cuando la sesión de Apple Pay fue iniciada correctamente.Wallet Order
SESSION_CANCELLEDOcurre cuando el usuario, en cualquier momento, da por finalizada la experiencia de pagar con Apple Pay.Wallet Order
SESSION_FAILEDOcurre cuando la sesión falla internamente en la etapa de inicialización.Wallet Order
MERCHANT_VALIDATION_STARTEDOcurre luego de que la sesión de Apple Pay fue iniciada correctamente.Wallet Order
MERCHANT_VALIDATION_COMPLETEDOcurre cuando la validación del merchant es correcta y la hoja de pago es presentada al usuario.Wallet Order
MERCHANT_VALIDATION_FAILEDOcurre cuando la validación del merchant falla y esto hace que la hoja de pago se cierre automáticamente.Wallet Order
PAYMENT_AUTHORIZATION_STARTEDOcurre luego de que el usuario haya indicado que quiere confirmar la compra y realizado con éxito las validaciones (biometría, etc)Wallet Order
PAYMENT_AUTHORIZATION_COMPLETEDOcurre cuando la confirmación de pago se realiza con éxito.Wallet Order
PAYMENT_AUTHORIZATION_FAILEDOcurre cuando la confirmación de pago falla internamente (puede ser por error de Apple o Koin)Wallet Order
UPDATE_ORDER_STATUS_FAILEDOcurre cuando la captura de la autorización falló.Order
UPDATE_ORDER_STATUS_COMPLETEDOcurre cuando la captura de la autorización se realizó con éxito.Order

Errores

CodeDescriptionOrigen
NOT_SUPPORTED_BROWSEROcurre cuando Apple Pay no está disponible en ese explorador.Apple
NOT_SUPPORTED_DEVICEOcurre cuando Apple Pay no es soportado en ese dispositivo.Apple
NOT_SUPPORTED_IOS_VERSIONOcurre cuando el dispositivo es iPhone/iPad con iOS anterior a 16.2.Apple
VALIDATE_MERCHANT_SESSION_ERROROcurre cuando Apple Pay detecta que la validación del merchant falló (cerrando la hoja de pago sin dar la posibilidad a pagar)Apple
INIT_MISSING_DYNAMIC_PAYMENT_KEYOcurre cuando la dynamic payment key no existe y es requeridaKoin
INIT_MISSING_ORDER_IDOcurre cuando el order id no existe y es requeridoKoin
INIT_MISSING_JSOcurre cuando la url del JS no existeKoin
INIT_MISSING_JWTOcurre cuando el JWT con los datos de la orden no existeKoin
PAYMENT_REQUEST_NOT_AVAILABLEOcurre cuando el JWT no contiene el objeto payment request para crear la sesión de Apple PayKoin
KOIN_TOKEN_ERROROcurre cuando no es posible obtener el koin_token luego de finalizada la experiencia apple pay y confirmado el pago por el usuarioKoin
CONFIGURATION_ERROROcurre cuando hay un error interno obteniendo y/o configurando la wallet para su posterior uso.Koin
PAYMENT_AUTHORIZED_ERROROcurre cuando hay un error interno al momento de obtener y procesar el payment data de Apple PayKoin
UPDATE_ORDER_STATUS_ERROROcurre cuando hay un error interno al momento de capturar el pago, sólo aplica para Orden.Koin

Estados de onFinish

Solo contexto order. type (o el string status, si la orden ya estaba terminada) es el estado de la orden en PascalCase.

statusDescription
AuthorizedEl pago fue autorizado.
CollectedEl pago fue cobrado.
CancelledEl pago fue cancelado.
FailedEl pago falló.
VoidedLa autorización fue anulada.

Si la orden ya estaba terminada al llamar a initialize, ese valor llega en data.status y message es Order already finished. Si el pago ocurre en vivo, llega en data.status.type y message es Order status updated.

FAQ

¿onFinish se dispara siempre con la orden en un estado final?

Sí, con inicialización por order. onFinish se dispara solo con un estado final. No se dispara con eventos intermedios, una revisión de antifraude, una autenticación 3DS ni un pago pendiente. En contexto wallet, el pago en vivo no llama a onFinish; el resultado va por onSuccess.

Si la orden ya estaba terminada al llamar a initialize, el status llega en data.status. Si el pago ocurre en vivo, llega en data.status.type.

¿Qué pasa si hay timeout, error de red, o el usuario cierra la pestaña?

Si el usuario cierra la pestaña, el SDK no lo informa por ningún evento. Timeout y error de red tampoco disparan onFinish. En esos casos el estado de la orden se toma de la notificación por webhook y del GET /v1/payment/orders/{id}. onError no es ese estado: aunque un fallo del SDK pueda llegar por ahí, no reemplaza a la notificación por webhook ni al GET.

Si la orden expira, en contexto order, onFinish se dispara con status Cancelled, leído en data.status o data.status.type según el caso de arriba.

¿El status de onFinish puede diferir del GET o del webhook?

No. El status de onFinish sale del mismo GET /v1/payment/orders/{id} y es un estado final. Consultar ese GET en ese momento devuelve la misma respuesta.

La fuente de verdad es siempre la notificación por webhook y ese GET. No recomendamos, bajo ningún término, reemplazar la notificación por webhook o el GET Status por el onFinish del SDK.

Cuando se dispara onFinish, ¿el estado ya está en la API?

Sí. Como el status sale de ese mismo GET, no hay demora. El estado ya está reflejado en la API.

Ejemplo

1 - Crear una instancia de la wallet

Para el caso de wallet, el objeto initData vendrá desde Payments. Tendrás que pasarlo como único parámetro sin hacerle ninguna modificación, sin embargo, para el caso de order, solo deberán especificar el orderId generado en Koin.

//ApplePay initialization methods
//Initialize APPLE_PAY by Wallet
const applepay = await window.KoinPayments.wallet(initData);

//Initialize APPLE_PAY by Order
const applepay = await window.KoinPayments.order(orderId);

2 - Agregar listeners

Sólo se puede usar una sola vez cada método, onSuccess, onError, onEvent y onFinish.
Podés ver los eventos disponibles y los errores posibles en la documentación de Koin Payments SDK.

//Listeners
applepay.onSuccess((data) => {
  console.log("Koin Token + Card info: ", data);
});

applepay.onError((error) => {
  console.error(
    "Error from callback: ",
    error.code,
    error.message,
    error.origin,
  );
});

applepay.onEvent((event) => {
  console.log("Event from callback: ", event);
  switch (event) {
    case "SCRIPT_LOADED":
      // Ocurre cuando el script de ApplePay es cargado.
      break;
    case "BUTTON_LOADED":
      // Ocurre cuando el botón de ApplePay aparece en pantalla.
      break;
    case "SESSION_STARTED":
      // Ocurre cuando el usuario hace clic en el botón de "Pagar con Apple Pay".
      break;
    case "SESSION_COMPLETED":
      // Ocurre cuando la sesión de Apple Pay fue iniciada correctamente.
      break;
    case "SESSION_CANCELLED":
      // Ocurre cuando el usuario, en cualquier momento, da por finalizada la experiencia de pagar con Apple Pay.
      break;
    case "SESSION_FAILED":
      // Ocurre cuando la sesión falla internamente en la etapa de inicialización.
      break;
    case "MERCHANT_VALIDATION_STARTED":
      // Ocurre luego de que la sesión de Apple Pay fue iniciada correctamente.
      break;
    case "MERCHANT_VALIDATION_COMPLETED":
      // Ocurre cuando la validación del merchant es correcta y la hoja de pago es presentada al usuario.
      break;
    case "MERCHANT_VALIDATION_FAILED":
      // Ocurre cuando la validación del merchant falla y esto hace que la hoja de pago se cierre automáticamente.
      break;
    case "PAYMENT_AUTHORIZATION_STARTED":
      // Ocurre luego de que el usuario haya indicado que quiere confirmar la compra y realizado con éxito las validaciones (biometría, etc)
      break;
    case "PAYMENT_AUTHORIZATION_COMPLETED":
      // Ocurre cuando la confirmación de pago se realiza con éxito.
      break;
    case "PAYMENT_AUTHORIZATION_FAILED":
      // Ocurre cuando la confirmación de pago falla internamente (puede ser por error de Apple o Koin)
      break;
    case "UPDATE_ORDER_STATUS_FAILED":
      // Ocurre cuando la captura de la autorización falló (solo aplica para order)
      break;
    case "UPDATE_ORDER_STATUS_COMPLETED":
      // Ocurre cuando la captura de la autorización se realizó con éxito (solo aplica para order)
      break;
  }
});

applepay.onFinish((data) => {
  // Orden ya terminada al inicializar: status es string y message es "Order already finished".
  // Pago en vivo (solo order): status es { type, date? } y message es "Order status updated".
  const status = typeof data.status === "string" ? data.status : data.status?.type;
  console.log("Finish from callback: ", status, data.message);
  switch (status) {
    case "Authorized":
    case "Collected":
      // El pago fue autorizado o cobrado
      break;
    case "Cancelled":
      // El pago fue cancelado
      break;
    case "Failed":
      // El pago falló
      break;
    case "Voided":
      // La autorización fue anulada
      break;
  }
});

3 - Iniciar flujo y montar el botón de Apple Pay

El método initialize se puede usar la cantidad de veces que se necesite.
Importante: el método mount se debe usar de la siguiente manera (y siempre dentro del callback del initialize):

//Initialize and mount methods
const applepay = await window.KoinPayments.wallet(initData);
//const applepay = await window.KoinPayments.order(orderId);

async function onClick() {
  applepay.initialize(() => {
    console.log("Initialized");
    // #btn-container es la div donde se montará el botón de Pagar con Apple Pay.
    applepay.mount("#btn-container");
  });
}

4 - Código completo

//Example
const applepay = await window.KoinPayments.wallet(initData);
//const applepay = await window.KoinPayments.order(orderId);

applepay.onSuccess((data) => {
  console.log("Koin Token + Card info: ", data);
});

applepay.onError((error) => {
  console.error(
    "Error from callback: ",
    error.code,
    error.message,
    error.origin,
  );
});

applepay.onEvent((event) => {
  console.log("Event from callback: ", event);
  switch (event) {
    case "SCRIPT_LOADED":
      // Ocurre cuando el script de ApplePay es cargado.
      break;
    case "BUTTON_LOADED":
      // Ocurre cuando el botón de ApplePay aparece en pantalla.
      break;
    case "SESSION_STARTED":
      // Ocurre cuando el usuario hace clic en el botón de "Pagar con Apple Pay".
      break;
    case "SESSION_COMPLETED":
      // Ocurre cuando la sesión de Apple Pay fue iniciada correctamente.
      break;
    case "SESSION_CANCELLED":
      // Ocurre cuando el usuario, en cualquier momento, da por finalizada la experiencia de pagar con Apple Pay.
      break;
    case "SESSION_FAILED":
      // Ocurre cuando la sesión falla internamente en la etapa de inicialización.
      break;
    case "MERCHANT_VALIDATION_STARTED":
      // Ocurre luego de que la sesión de Apple Pay fue iniciada correctamente.
      break;
    case "MERCHANT_VALIDATION_COMPLETED":
      // Ocurre cuando la validación del merchant es correcta y la hoja de pago es presentada al usuario.
      break;
    case "MERCHANT_VALIDATION_FAILED":
      // Ocurre cuando la validación del merchant falla y esto hace que la hoja de pago se cierre automáticamente.
      break;
    case "PAYMENT_AUTHORIZATION_STARTED":
      // Ocurre luego de que el usuario haya indicado que quiere confirmar la compra y realizado con éxito las validaciones (biometría, etc)
      break;
    case "PAYMENT_AUTHORIZATION_COMPLETED":
      // Ocurre cuando la confirmación de pago se realiza con éxito.
      break;
    case "PAYMENT_AUTHORIZATION_FAILED":
      // Ocurre cuando la confirmación de pago falla internamente (puede ser por error de Apple o Koin)
      break;
    case "UPDATE_ORDER_STATUS_FAILED":
      // Ocurre cuando la captura de la autorización falló (solo aplica para order)
      break;
    case "UPDATE_ORDER_STATUS_COMPLETED":
      // Ocurre cuando la captura de la autorización se realizó con éxito (solo aplica para order)
      break;
  }
});

applepay.onFinish((data) => {
  // Orden ya terminada al inicializar: status es string y message es "Order already finished".
  // Pago en vivo (solo order): status es { type, date? } y message es "Order status updated".
  const status = typeof data.status === "string" ? data.status : data.status?.type;
  console.log("Finish from callback: ", status, data.message);
  switch (status) {
    case "Authorized":
    case "Collected":
      // El pago fue autorizado o cobrado
      break;
    case "Cancelled":
      // El pago fue cancelado
      break;
    case "Failed":
      // El pago falló
      break;
    case "Voided":
      // La autorización fue anulada
      break;
  }
});

async function onClick() {
  applepay.initialize(() => {
    console.log("Initialized");
    // #btn-container es la div donde se montará el botón de Pagar con Apple Pay.
    applepay.mount("#btn-container");
  });
}