# Apple Pay

Apple Pay lets users pay with a card already saved to their device — no card entry, no redirect. The payment sheet is native, authenticated by Face ID or Touch ID, and dismisses back into your app once the transaction is confirmed.

Identity creation, KYC, eligibility, and OTP must be completed before reaching the payment step. See the [Integration Guide](/products/native-api/docs/guides/foundations) if you haven't done this yet. This guide picks up from `paymentReady: true`.

## Choose your approach

| Approach | Best for |
|  --- | --- |
| [React Native SDK](#react-native-sdk) | React Native mobile apps |
| [Embedded Payment Button](#embedded-payment-button) | Web apps (Vanilla JS, React, Vue) |
| JS SDK | Web apps — coming soon |


## React Native SDK

### iOS platform setup

Before writing any code, complete the following steps in your Apple Developer account.

**You will need:**

- An active Apple Developer Program membership
- Access to [Apple's Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources)
- Xcode


**Coordinate with your Banxa integration contact before starting.** They will provide the Certificate Signing Request (CSR) file required in Step 2.

#### Step 1 — Create a merchant identifier

A merchant identifier uniquely identifies your app to Apple Pay. It lives under your Apple Developer account, not Banxa's.

1. Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources)
2. Click **Identifiers** in the sidebar, then **+**
3. Select **Merchant IDs**, then click **Continue**
4. Enter a description and an identifier — it must start with `merchant.` (e.g. `merchant.com.yourcompany.yourapp`)
5. Click **Continue**, review, then **Register**


If you already have a merchant identifier for your app, you can reuse it — you do not need to create a new one.

#### Step 2 — Create a payment processing certificate

The payment processing certificate encrypts payment tokens. It must be created using a CSR provided by Banxa — do not generate your own CSR for this step.

Once you have the CSR file from your Banxa integration contact:

1. Go to [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources)
2. Click **Identifiers**, filter by **Merchant IDs**, and select the one you created in Step 1
3. Under **Apple Pay Payment Processing Certificate**, click **Create Certificate**
4. Upload the `.certSigningRequest` file Banxa provided
5. Click **Continue**, then **Download** to save the `.cer` file
6. Send the `.cer` file back to your Banxa integration contact


Banxa will upload the certificate on their side. Wait for confirmation before proceeding. Do not move to Step 3 until you receive it.

Certificate expiry
Payment processing certificates expire every 25 months. When yours approaches expiry, repeat this step — Banxa will provide a fresh CSR.

#### Step 3 — Configure Xcode

1. Open your project in Xcode and select your app target
2. Go to the **Signing & Capabilities** tab
3. Click **+ Capability** and select **Apple Pay**
4. Under the Apple Pay capability, click **+** and add the merchant identifier from Step 1


The merchant identifier you add here must exactly match the one registered with Banxa. A mismatch is the most common cause of Apple Pay failures at runtime.

### Execute payment

After your backend confirms `paymentReady: true` from eligibility, call `createOrderAndShowPrimerCheckout` with `paymentMethod: 'applePay'`:


```typescript
import {
  isPrimerCheckoutWebViewResult,
} from '@banxa-official/react-native-sdk';

const result = await banxa.buy.createOrderAndShowPrimerCheckout(orderRequest, {
  paymentMethod: 'applePay',
  callbacks: {
    onCheckoutComplete: () => {
      // Apple Pay confirmed — transaction is in progress
    },
    onError: (error) => {
      // Apple Pay unavailable or failed — see error handling below
    },
    onDismiss: () => {
      // User dismissed the payment sheet without completing
    },
  },
});

if (isPrimerCheckoutWebViewResult(result)) {
  // Apple Pay unavailable for this session — SDK fell back to hosted WebView
  render(<CheckoutWebView {...result.webViewProps} />);
}
```

The `orderRequest` must include `externalCustomerId` — pass the same `identityReference` you used for eligibility.

### Error handling

`onError` is called when Apple Pay is unavailable for the current session. Common causes:

- User hasn't set up Apple Pay on their device
- No cards added to Wallet
- None of the user's cards match supported networks


Do not display an Apple Pay button until you've confirmed Apple Pay is available for the session.

Testing requires a real device
Apple Pay cannot be tested on the iOS simulator. The simulator will reach the payment sheet but fail at payment. Test on a real device with at least one card added to Wallet.

### Testing

Use the sandbox environment and Banxa test credentials during development:


```typescript
const banxa = new Banxa({
  environment: 'sandbox',
  // ...
});
```

Apple Pay testing requires a real iOS device with Apple Pay configured and at least one card added to Wallet.

### Troubleshooting

The error codes below are Primer errors surfaced through the SDK.

**Apple Pay button does not appear / `[unable-to-present-payment-method]`**

Apple Pay is not available for the current user or session. Common reasons:

- User has not set up Apple Pay on their device
- No cards added to Wallet
- None of the user's cards match supported networks
- Device doesn't support Apple Pay


The SDK filters Apple Pay from available payment methods in these cases. Only show an Apple Pay option when the SDK confirms it's available.

**`[unable-to-make-payments-on-provided-networks]`**

- Apple Pay capability not enabled in Xcode — check **Signing & Capabilities**
- No valid card in Wallet
- Card network not supported
- Device restrictions (e.g. parental controls) enabled


Try on a different real device with a different card.

**"Apple Pay Is Not Available in [App Name]" system error**

The merchant identifier in Xcode does not match the one associated with the payment processing certificate. Verify:

- The merchant identifier in your Xcode Apple Pay capability exactly matches the one registered with Banxa
- Your Banxa integration contact has confirmed the certificate is active


**`[payment-cancelled]` or payment sheet not presenting**

The merchant identifier is incorrect or does not match what is configured in Xcode. Verify both are identical.

**`[server-error] 404: No Matching ProcessingCertificate`**

Entitlements are correct, but the certificate uploaded to Banxa's system doesn't match your Xcode project. Contact your Banxa integration contact to verify the certificate.

### Going live

Ensure you are using production credentials and that your Banxa integration contact has confirmed the certificate is active in the production environment before releasing to the App Store.

## Embedded Payment Button

The Embedded Payment Button renders Apple Pay inside a web component — no SDK install required. It is a web integration for Vanilla JS, React, and Vue apps.

**Before you start:** Contact your Banxa integration contact to get provisioned. The button-only setting must be enabled on your environment, and your domain must be registered for Apple Pay (covered below). Neither can be self-served.

Environment-level setting
The button-only configuration applies to your entire environment. If it is enabled, the standard full checkout widget will no longer render for that environment. If you need both — for example, the button on one surface and full checkout on another — you will need a separate environment provisioned by Banxa.

### Apple Pay domain setup

Apple Pay on web requires your domain to be registered with Apple. Banxa handles the Apple Developer console steps — your part is to provide your domain and host a verification file.

**Step 1 — Share your domain with Banxa**

Send your integration contact the domain that will host the iframe, for example `app.yourcompany.com`. Banxa will register it in Apple's system.

**Step 2 — Host the domain association file**

Once Banxa has registered your domain, they will send you an `apple-developer-merchantid-domain-association` file. Host it at both paths — some configurations require the `.txt` extension:


```
https://app.yourcompany.com/.well-known/apple-developer-merchantid-domain-association
https://app.yourcompany.com/.well-known/apple-developer-merchantid-domain-association.txt
```

The file must be publicly accessible before Banxa can verify the domain.

**Step 3 — Confirmation**

Once the file is live, let your Banxa integration contact know. They will verify the domain and confirm when Apple Pay is active for your environment.

Do not re-download the file from Apple
If the domain association file is downloaded again from Apple's developer console, the file contents change and your hosted copy becomes invalid. If this happens, Banxa will send you an updated file to re-host.

### Install the web component


```bash
npm install @banxa-official/embedded-checkout-web-component
```

Import the package once to register the custom element:


```js
import '@banxa-official/embedded-checkout-web-component';
```

### Create an order

Once your backend confirms `paymentReady: true` from eligibility, create an order. This returns a `checkoutUrl` which you pass to the web component.


```http
POST /{partner}/v2/buy
Content-Type: application/json
x-api-key: YOUR_API_KEY

{
  "externalCustomerId": "user-123",
  "fiat": "AUD",
  "crypto": "BTC",
  "walletAddress": "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq",
  "fiatAmount": "500.00",
  "redirectUrl": "https://app.yourcompany.com/complete"
}
```

### Embed the component

Pass the `checkoutUrl` as the `source` attribute:


```html
<banxa-checkout source="https://checkout.banxa.com/..."></banxa-checkout>
```

The component stays hidden until the checkout page signals it is ready — at that point it resizes to fit the payment button and becomes visible.

**Lifecycle callbacks**

Assign functions to these properties on the element instance:

| Property | When called |
|  --- | --- |
| `onLoading` | Checkout is loading |
| `onReady` | Component is ready and visible |
| `onPaymentPending` | Payment initiated, awaiting confirmation |
| `onComplete` | Payment completed successfully |
| `onError` | An error occurred |


Each callback receives a `detail` object, not a DOM event.

### Framework examples


```html Vanilla JS
<script type="module">
  import '@banxa-official/embedded-checkout-web-component';
</script>

<banxa-checkout id="checkout" source="https://checkout.banxa.com/..."></banxa-checkout>

<script>
  const el = document.getElementById('checkout');
  el.onLoading = (detail) => console.log('loading', detail);
  el.onReady = (detail) => console.log('ready', detail);
  el.onPaymentPending = (detail) => console.log('payment pending', detail);
  el.onComplete = (detail) => console.log('complete', detail);
  el.onError = (detail) => console.log('error', detail);
</script>
```


```jsx React
import { useEffect, useRef } from 'react';
import '@banxa-official/embedded-checkout-web-component';

export function Checkout() {
  const checkoutRef = useRef(null);

  useEffect(() => {
    const el = checkoutRef.current;
    if (!el) return;

    el.onLoading = (detail) => console.log('loading', detail);
    el.onReady = (detail) => console.log('ready', detail);
    el.onPaymentPending = (detail) => console.log('payment pending', detail);
    el.onComplete = (detail) => console.log('complete', detail);
    el.onError = (detail) => console.log('error', detail);
  }, []);

  return <banxa-checkout ref={checkoutRef} source="https://checkout.banxa.com/..." />;
}
```


```vue Vue
<template>
  <banxa-checkout
    source="https://checkout.banxa.com/..."
    @loading="onLoading"
    @ready="onReady"
    @paymentPending="onPaymentPending"
    @complete="onComplete"
    @error="onError"
  />
</template>

<script setup>
import '@banxa-official/embedded-checkout-web-component';

const onLoading = (event) => console.log('loading', event.detail);
const onReady = (event) => console.log('ready', event.detail);
const onPaymentPending = (event) => console.log('payment pending', event.detail);
const onComplete = (event) => console.log('complete', event.detail);
const onError = (event) => console.log('error', event.detail);
</script>
```

### Going live

Before releasing to production:

- Confirm with your Banxa integration contact that your environment is provisioned for the Embedded Payment Button
- Confirm Apple Pay domain verification is complete for your production domain
- Switch to production credentials for order creation