Apple Pay via Widget

This guide walks you through integrating Apple Pay using the DEUNA SDKs

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

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.
Compatibility requirementsRefer to the official Apple Pay on the Web and Apple Pay implementation documentation. Apple Pay requires Safari on iOS 10+ or macOS 10.12+; on non-Safari browsers (Chrome, Edge, Firefox), a QR code flow is available for users with an iPhone running iOS 18 or later.

1.1. 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. Integrations

Make sure to follow the Getting Started guide for our SDKs depending on your specific integration:


2.1. Payment Widget

📘

Use this when: you want DEUNA to handle the full payment experience. Apple Pay appears inside DEUNA's iframe alongside other payment methods. You do not call the Purchase API.

Apple Pay particularities

  • No extra configuration needed in code. Apple Pay is enabled or disabled from the DEUNA Dashboard. If the merchant has it active and the user's device supports it, the button appears automatically.
  • userToken is optional — only needed when you want to associate the payment with a known DEUNA user account.
  • The standard widget callbacks (onSuccess, onError, onClosed, onPaymentProcessing) work the same way as for other payment methods.

2.2. Payment Vault

📘

Use this when: you want to render your own Apple Pay button and control the purchase flow. The Vault tokenizes the card and returns a card_id. You call the Purchase API from your backend.

Web SDK

Let the Web SDK resolve Apple Pay credentials from the DEUNA backend using your publicApiKey and (optionally) an orderToken.

For more info check the getWalletsAvailable documentation.

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();

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.card_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.');
}

iOS SDK

**Apple Pay Wallet Prerequisites (iOS) **

To use Apple Pay Wallet with the SDK on iOS, make sure all of the following are configured correctly.

  1. App and Team setup

    • Your app must be signed with an Apple Developer Team that has Apple Pay enabled.
    • The target PRODUCT_BUNDLE_IDENTIFIER must match the App ID configured in Apple Developer.
    • The app must be installed on a real iOS device (Apple Pay does not fully work in simulator).
  2. Merchant ID setup

    • Create or use a Merchant ID in Apple Developer (example: merchant.test.deuna.dev.pay).
    • Assign that Merchant ID to the app’s App ID under Identifiers > App ID > Apple Pay.
    • In Xcode, enable Signing & Capabilities > Apple Pay and select the same Merchant ID.
  3. Required certificates
    For the Merchant ID used by the SDK, you must have:

    • Apple Pay Payment Processing Certificate (required for in-app Apple Pay token processing).
    📘

    **Important: **

    • When creating the Payment Processing Certificate, upload a valid CSR file (.csr).
    • Some Apple flows require an ECC P-256 CSR for Apple Pay processing
  4. Provisioning profiles

    • Regenerate provisioning profiles after changing Apple Pay capabilities or Merchant ID assignments.
    • Reinstall the app after profile/capability changes (Clean Build Folder + delete app from device + reinstall).
  5. SDK/backend consistency requirements

    • The Merchant ID returned by backend credentials (external_merchant_id) must exactly match the Merchant ID enabled in the app entitlement.

    • The SDK wallet flow should include user context (userInfo) when required by your backend to return userToken and userId.

    • If userToken/userId are missing, tokenization can fail even if Apple Pay UI opens.


4. 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 — the native sheet requires Safari. Other browsers fall back to QR-code flow (iOS 18+).

Quick reference

APIPurpose
DeunaSDK.getWalletsAvailable()Check which wallets are available on the device.
DeunaSDK.initElements({ types, orderToken?, callbacks })Initialize Apple Pay. Render your own Apple Pay button
DeunaSDK.initPaymentWidget({ orderToken, callbacks, ... })Open DEUNA's full Payment Widget (Apple Pay included).
.well-known/apple-developer-merchantid-domain-associationDomain verification file you must host on every domain