Apple Pay

iOS SDK – Headless Apple Pay

Headless Apple Pay lets you present the Apple Pay sheet and complete a payment from your own UI, without using the SDK's built-in payment screens. You design the button and checkout flow; the SDK handles the Apple Pay sheet and the payment itself, then reports the result back to you.

When to use it

  • You want a fully custom checkout screen (your own Apple Pay button, layout and copy).
  • You only need Apple Pay and don't want to present the SDK's payment UI.

If you're happy with the SDK's standard payment UI, you don't need headless mode.

Before you start

Prerequisites

xCode VersioniOS VersionSwift Version
Download Geidea iOS SDK Version1.5.1
13 and above
  • A session ID. Every headless payment runs against a payment session, passed to the SDK as sessionId.
  • An Apple Pay merchant ID. Passed to the SDK through ApplePayConfigurations(merchantId:). Your app must be set up for Apple Pay with that merchant ID (Apple Pay capability enabled in Xcode and the merchant ID registered in your Apple Developer account).

Step 1: Build a headless configuration

Create a HeadlessGDPaymentSDKConfiguration for the session you want to pay.

func makeHeadlessConfiguration(sessionId: String) -> HeadlessGDPaymentSDKConfiguration {
    HeadlessGDPaymentSDKConfiguration(
        sessionId: sessionId,
        applePayConfig: ApplePayConfigurations(merchantId: applePayMerchantId),
        language: selectedLanguage,
        environmentType: selectedEnvironmentType,
        region: selectedRegion
    )
}
ParameterDescription
sessionIdThe ID of the payment session this payment belongs to.
applePayConfigApple Pay settings. Create it with ApplePayConfigurations(merchantId:) using your Apple Pay merchant ID.
languageThe language the SDK should use.
environmentTypeThe SDK environment to run against (for example test or production).
regionThe region your merchant account belongs to.

Step 2: Configure the SDK for headless mode

Pass the configuration to configureSDKForHeadless(configuration:) on the shared SDK instance. This call can throw, so call it with try inside a do/catch and don't start a payment if it fails.

do {
    try GDPaymentSDK.sharedInstance().configureSDKForHeadless(configuration: configuration)
} catch {
    // Configuration failed: show an error and don't call payWithApplePay.
    return
}

Step 3: Present Apple Pay with payWithApplePay

Call payWithApplePay(amount:countryCode:currencyCode:completion:) from your own button's action. The SDK opens the Apple Pay sheet and calls your completion handler when the flow ends. Call it from the main thread (for example from a @MainActor function), since it presents UI.

ParameterTypeDescription
amountDoubleThe amount to charge.
countryCodeStringTwo-letter country code, for example "EG".
currencyCodeStringCurrency code of the payment, for example "EGP".
completionclosureCalled once with the payment result.

Step 4: Handle the result

The completion handler receives one of three results:

ResultMeaningWhat to do
.successThe payment completed.Show your confirmation screen.
.failure(let error)The payment could not be completed.Show an error and let the user try again or choose another method.
.cancelledThe user dismissed the Apple Pay sheet.Return to checkout quietly; this is not an error.

Full example

A complete flow triggered from your own Apple Pay button:

@MainActor
func payWithApplePay(sessionId: String) {
    guard let amount = Double(totalAmount) else {
        showError("Invalid amount")
        return
    }

    let configuration = HeadlessGDPaymentSDKConfiguration(
        sessionId: sessionId,
        applePayConfig: ApplePayConfigurations(merchantId: applePayMerchantId),
        language: selectedLanguage,
        environmentType: selectedEnvironmentType,
        region: selectedRegion
    )

    do {
        try GDPaymentSDK.sharedInstance().configureSDKForHeadless(configuration: configuration)
    } catch {
        showError("Couldn't start Apple Pay: \(error.localizedDescription)")
        return
    }

    GDPaymentSDK.sharedInstance().payWithApplePay(
        amount: amount,
        countryCode: "EG",
        currencyCode: "EGP"
    ) { [weak self] result in
        guard let self else { return }
        switch result {
        case .success:
            self.showPaymentConfirmation()
        case .failure(let error):
            self.showError("Payment failed: \(error.localizedDescription)")
        case .cancelled:
            break // The user closed the Apple Pay sheet; stay on checkout.
        }
    }
}

showError and showPaymentConfirmation stand in for your own UI code.

Tips

  • Build the configuration and call configureSDKForHeadless before each payment, using the session ID for that payment.
  • Validate the amount before calling payWithApplePay rather than force-unwrapping it.
  • Treat .cancelled separately from .failure so users who simply close the sheet don't see an error.

Flutter SDK – Headless Apple Pay

Add headless Apple Pay functionality on Apple Pay button directly without the need to open SDK checkout:

final paymentSDK = GDPaymentSDK.sharedInstance();

// This function should be applied before calling any headless function.
final bool configured = await paymentSDK.configureHeadlessPayment(
  configuration: GDPaymentSDKConfiguration(
    sessionId: sessionId,
    applePayConfiguration: GDApplePayConfiguration(
      merchantId: "merchant.com.company.example",
      merchantName: "Merchant Name",
      merchantCapability: ApplePayMerchantCapability.threeDSecure, // Optional
      paymentNetworks: [
        ApplePayPaymentNetwork.visa,
        ApplePayPaymentNetwork.masterCard,
        ApplePayPaymentNetwork.mada,
      ], // Optional
    ),
  ),
);

if (configured) {
  final PaymentResponse response = await paymentSDK.startHeadlessApplePay(
    configuration: HeadlessApplePayConfig(
      amount: 5.20,  // Two decimal double
      countryCode: "SA", // Apple Pay Country Code
      currencyCode: "EGP", // Apple Pay Currency Code
    ),
  );

  switch (response.status) {
      case PaymentStatus.success:
        emit(
          PaymentAuthenticated(
            orderId: response.result?.orderId,
            sessionId: sessionId,
          ),
        );

      case PaymentStatus.failure:
        // You can use response.error on failure 

      case PaymentStatus.canceled:
        // Triggered when Apple Pay sheet closes
    }
}

Tips

  • Build the configuration and call configureSDKForHeadless before each payment, using the session ID for that payment.
  • Make sure the amount goes to HeadlessApplePayConfig is exaclty two decimal double.

React Native SDK – Headless Apple Pay

Open Apple Pay directly from your own button without opening the SDK checkout:

import { payWithApplePay } from '@geidea/payment-sdk-react-native';

async function handleApplePay(sessionId) {
  try {
    const response = await payWithApplePay({
      sessionId,
      applePayConfig: {
        merchantId: 'merchant.com.company.example',
        merchantName: 'Merchant Name',
        merchantCapabilities: ['3DS'], // Optional
        supportedNetworks: ['visa', 'masterCard', 'mada'], // Optional
      },
      environment: 'sandbox',
      region: 'ksa',
      language: 'en',
    });

    switch (response.status) {
      case 'completed':
        // Payment completed.
        // Order ID: response.result?.orderId
        break;

      case 'canceled':
        // Apple Pay canceled.
        break;
    }
  } catch (error) {
    // Handle payment failure.
    console.error(error);
  }
}

Tips

  • Available on iOS only. Enable Apple Pay in Xcode with the same merchant ID.
  • The SDK retrieves the amount, country, and currency from the session.
  • Use the environment and region matching your session.
  • No separate headless configuration call is required.

Did this page help you?