Copy of Apple Pay via Widget

This guide walks you through integrating Apple Pay using the DEUNA Web‑SDK (@deuna/web-sdk v1.6). It covers the two integration paths the SDK exposes:

Payment WidgetPayment Vault
What it does**Handles the full checkout: UI, payment processing, and confirmationTokenizes the card only — returns a card_id for you to use
Who processes the purchaseDEUNA (internally)You (via the Purchase API from your backend)
Apple Pay buttonRendered inside DEUNA's iframeRendered by you, in your own UI
SDK methodinitPaymentWidgetinitElements({ types: ['APPLE_PAY'] })
Use whenYou want a drop-in checkout experienceYou need control over the payment flow or want to store the card for later
Full integration referencePayment Widget — Web SDKPayment Vault - Web SDK

1. Prerequisites

Before you start, make sure the following are in place:

RequirementNotes
DEUNA accountActive merchant account in the DEUNA Dashboard.
publicApiKeyPublic API key issued by DEUNA. Required for DeunaSDK.initialize.
orderTokenGenerated on your backend via the DEUNA Orders API. Required to start a payment.
userToken (optional)Required only when you want to tokenize the card against a known DEUNA user.
HTTPS domainApple Pay requires the page to be served over HTTPS. localhost is allowed only for development.
Browser supportSafari required for the native Apple Pay sheet. Other browsers fall back to QR-code flow (iOS 18+).
Web‑SDK package@deuna/web-sdk@^1.6.0

Loading the SDK from the CDN

Add the SDK script to your HTML (ideally in <head> so it's ready before checkout renders):

<script src="https://cdn.deuna.io/web-sdk/v1.6/index.js"></script>

If you need to wait for the script to load before using it (e.g. when injecting it dynamically), listen for the `load` event:

```ts
const script = document.createElement("script");
script.src = "https://cdn.deuna.io/web-sdk/v1.6/index.js";
script.onload = () => {
  // window.DeunaSDK is now available
};
document.head.appendChild(script);

Domain verification (required for both paths)

Every domain that renders an Apple Pay button — whether via the Payment Widget or the Vault — must be registered with Apple through DEUNA. This applies to production domains and to any staging or preview environments.

Step‑by‑step

  1. Obtain the domain association file from the DEUNA Dashboard (or from Apple Pay if using your own merchant ID). The file is typically named apple-developer-merchantid-domain-association (no extension) or .txt.
  2. Host the file unchanged at:
    https://<your-domain>/.well-known/apple-developer-merchantid-domain-association
    Make sure:
    • The response is served over HTTPS.
    • Content-Type is text/plain; charset=utf-8.
    • No redirect, no auth wall, no 404. Response must be 200 OK.
  3. Verify the domain in the DEUNA Dashboard (or Apple Pay Developer portal). Apple will fetch the file from your server; if it matches, the domain is registered.
  4. Repeat per domain. Every hostname that loads the widget (e.g. checkout.mystore.com, staging.mystore.com) must serve the file and be registered separately.

Reference implementation (Next.js - Example)

This repository already implements the verification endpoint. Two things are wired up:

next.config.js — rewrites both the canonical and .txt paths to an API route:

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 — returns the file contents as 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);
}

The file contents are stored in an environment variable (NEXT_PUBLIC_APPLE_VALIDATION_CONTENT_FILE) so they can be rotated per environment without a code change.

How to verify it works

curl -i https://<your-domain>/.well-known/apple-developer-merchantid-domain-association

Expect:

  • HTTP 200 OK
  • Content-Type: text/plain; charset=utf-8
  • A body starting with 7B22... (the Apple blob)

2. Initialize the Web‑SDK and the Payment Widget with Apple Pay

Use this path when you want DEUNA's full Payment Widget UI (iframe) to render Apple Pay alongside other payment methods. The widget handles the Apple Pay session internally — you only pass the order and callbacks.

// DeunaSDK is the global injected by https://cdn.deuna.io/web-sdk/v1.6/index.js
const { DeunaSDK } = window;

// 1) Initialize the SDK once (e.g. at app bootstrap).
await DeunaSDK.initialize({
  env: "staging",           // "production" | "staging" | "develop" | "sandbox"
  publicApiKey: "<YOUR_PUBLIC_API_KEY>",
});

// 2) Open the Payment Widget with an order. Apple Pay is enabled automatically
//    when the merchant has it configured in the DEUNA Dashboard.
await DeunaSDK.initPaymentWidget({
  mode: "modal",            // or "embedded" with a `target`
  orderToken: "<ORDER_TOKEN>",
  userToken: "<USER_TOKEN>",  // optional
  language: "es",
  callbacks: {
    onSuccess: (payload) => {
      console.log("Payment success", payload);
      DeunaSDK.close();
    },
    onError:            (err)      => console.error("Error", err),
    onClosed:           (action)   => console.log("Closed by", action),
    onPaymentProcessing:()         => console.log("Processing..."),
    onEventDispatch:    (type, d)  => console.log("Event", type, d),
  },
});

Notes:

  • The widget renders the Apple Pay button when the device supports it and the merchant has Apple Pay enabled.
  • All Apple Pay configuration (displayName, supportedNetworks, merchantCapabilities, merchantIdentifier, credentials) is resolved by the widget via the DEUNA SSR endpoint. No extra params needed on the client.
  • Merchant validation is delegated to DEUNA's backend: POST {apiBase}/wallet/credentials/{credentialId}/payment-session.


4. Call Apple Pay (Payment Vault)

const { DeunaSDK } = window; // from https://cdn.deuna.io/web-sdk/v1.6/index.js

await DeunaSDK.initialize({
  env: "staging",
  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()

await DeunaSDK.initElements({
  types: [{ name: "APPLE_PAY" }],
  callbacks: {
    onSuccess: (payload) => console.log("Apple Pay success", payload),
    onError:   (err)     => console.error("Apple Pay error", err),
    onClosed:  (action)  => console.log("Sheet closed:", action),
  },
});

5 Gotchas

GotchaFix
Button does not renderCheck getWalletsAvailable() result, browser support, and HTTPS.
Payment sheet opens then freezesMerchant validation failed — check domain verification file and DEUNA credentials.
session.begin() silently rejectedYou lost the user gesture. Do not await long‑running work between click and session.begin(). Use the orderToken + userInfo SSR path to pre‑resolve transactionInfo.
Works in Safari, not ChromeExpected for the in‑device flow. SDK falls back to a QR‑code flow on non‑Safari browsers (iOS 18+).

Quick reference

APIPurpose
DeunaSDK.initialize({ env, publicApiKey })One‑time SDK bootstrap.
DeunaSDK.initPaymentWidget({ orderToken, callbacks, ... })Open DEUNA's full Payment Widget (Apple Pay included).
DeunaSDK.getWalletsAvailable()Check which wallets are usable + warm up providers.
DeunaSDK.initElements({ types, orderToken?, callbacks })Initialize Apple Pay.
.well-known/apple-developer-merchantid-domain-associationThe file you must host for Apple Pay domain verification.