> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gr4vy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Apple Pay on web without Embed

It is possible to use Apple Pay on the web without the SDK. To learn how to integrate
Apple Pay, following Apple's [documentation][documentation] is recommended.

The steps below highlight the basics and focus on the interaction with the API.
A full [code sample is available](https://github.com/gr4vy/sample-standalone-apple-pay) that shows this integration in action.

## About this integration

Similar to the standard Embed integration, there is minimal configuration to get set up to
process Apple Pay on the web. There is no need to sign up for an Apple Developer account
with this integration.

* Upload the **Domain Association File** to your website.
* Enable Apple Pay in your merchant dashboard.

Additionally, you then need to implement the following.

* Load the Apple Pay JS SDK.
* Render an Apple Pay button.
* Handle the click of the button.
* Verify the Apple Pay session.
* Catch the Apple Pay token and pass it to the API for processing.

## Enable Apple Pay

To enable Apple Pay, head over to your dashboard and then go to
**Connections** -> **Catalog** -> **Apple Pay**.

Next, complete, and submit the form to create a new Apple Pay service.

### Registering a domain

To process Apple Pay via the web, register all the domains where the buyer can
checkout. To do this, navigate to the Apple Pay service via the dashboard, **Connections** -> **Apple Pay** -> **Domains** -> **Add domain name**.

<Warning>
  **Domain Association File**

  Before submitting a domain name, please download the Domain Association File from
  the dashboard and upload it to every domain you wish to support.

  The file needs to be added to every domain in a predefined location.

  ```
  https://{domain}/.well-known/apple-developer-merchantid-domain-association
  ```

  This file is checked by Apple in the production environment
  to verify that permission has been granted to perform
  Apple Pay transactions on your dashboard.
</Warning>

## Integrate Apple Pay

### Load the Apple Pay JS SDK

Since iOS 18, users can shop with Apple Pay on devices other than Macs and non-Safari browsers. On submit, a popup displays asking users to scan a code with their iOS 18+ device and complete the payment there.

<img src="https://mintcdn.com/gr4vy/jCFeFdffXM43huI0/assets/images/apple-pay/third-party-browser-popup.png?fit=max&auto=format&n=jCFeFdffXM43huI0&q=85&s=4e04afbe7742d3b00140652f8d14d2ba" alt="Third-party browser popup" width="846" height="845" data-path="assets/images/apple-pay/third-party-browser-popup.png" />

To enable Apple Pay in browsers besides Safari, load the Apple Pay JS SDK into the app. It is safe to load this same library in Safari as well.

```html theme={"system"}
<head>
    <script crossorigin 
      src="https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js"> 
    </script>
</head>
```

#### Content Security Policy

Make sure to allow the Apple Pay JS SDK domain in your CSP if you're using one; otherwise, the script does not load and the Apple Pay option does not show in third-party browsers.

```
default-src 'self' applepay.cdn-apple.com ...; script-src 'self' applepay.cdn-apple.com ...
```

Please see the [Pay with Apple Pay in third-party browsers documentation](https://support.apple.com/en-jo/guide/safari/ibrw9b779bd3/mac) for more information.

### Render an Apple Pay button

An Apple Pay button can be displayed and customized [with Apple specific CSS](https://developer.apple.com/documentation/apple_pay_on_the_web/displaying_apple_pay_buttons_using_css).

```html theme={"system"}
<style>
  .button {
    display: inline-block;
    -webkit-appearance: -apple-pay-button;
    -apple-pay-button-type: pay;
    cursor: pointer;
  }
</style>

<button class="button"></button>
```

### Handle button click

When a button is clicked, start an [Apple Pay Session](https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api/creating_an_apple_pay_session).

```js theme={"system"}
  const onClick = async () => {
    const session = new ApplePaySession(3, {
      countryCode: 'AU',
      currencyCode: 'AUD',
      merchantCapabilities: ["supports3DS"],
      supportedNetworks: ["visa", "masterCard", "amex", "discover"],
      total: {
        label: "Demo (Card is not charged)",
        type: "final",
        amount: '12.99',
      },
    });

    ...

    session.begin();
  };
```

### Verify session

Next, this is the bit that's specific to the API. Make an API call to the Apple Pay session endpoint to verify the Apple Pay session for
your domain. This checks if accepting Apple Pay on your domain is allowed.

<Note>
  For this to work it's important to [register the domain](/guides/features/apple-pay/web#enabling-apple-pay)
  your domain for use with Apple Pay in the dashboard.
</Note>

```js theme={"system"}
...
session.onvalidatemerchant = async (event) => {
  const response = await fetch(`http://api.sandbox.example.gr4vy.app/digital-wallets/apple/session`, {
    method: "POST",
    headers: {
      "Authorization": `bearer [JWT]`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      validation_url: event.validationURL,
      domain_name: document.location.hostname,
    }),
  });
  const merchantSession = await response.json();
  session.completeMerchantValidation(merchantSession);
};

session.begin();
```

<Warning>
  When using a real card in a sandbox environment, the validation URL is rejected by the
  [`POST /digital-wallets/apple/session`](/reference/digital-wallets/get-apple-pay-session) endpoint. Please only use test cards in the sandbox environment.
</Warning>

### Create a transaction

Finally, once the Apple Pay transaction has been authorized by Apple, create a transaction with the API.

<Note>
  This API call could be made in your frontend code, or the token could be sent to your backend for processing.
</Note>

```js theme={"system"}
session.onpaymentauthorized = (event) => {
  await fetch(`http://api.sandbox.example.gr4vy.app/transactions`, {
    method: "POST",
    headers: {
      "Authorization": `bearer [JWT]`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      amount: 1299,
      currency: "AUD",
      country: "AU",
      payment_method: {
        method: "applepay",
        token: event.payment.token,
      },
    }),
  });
  

  session.completePayment({
    status: ApplePaySession.STATUS_SUCCESS,
  });
};

session.begin();
```

## Apple Pay inside an iframe

Apple Pay works when your checkout page sits inside an iframe, but it needs extra
configuration. Each merchant session is tied to a domain, and that domain has to be the
**top level** page, the one shown in the browser's address bar, rather than the frame the
payment is requested from.

Skip this section if your checkout page is the top level page.

<Warning>
  A mismatched domain is not rejected when the merchant session is created. The call to
  [`POST /digital-wallets/apple/session`](/reference/digital-wallets/get-apple-pay-session)
  succeeds, the Apple Pay sheet opens, the buyer authorizes the payment, and only then does
  it fail with a "Payment Not Completed" message. No payment is taken, but nothing looks
  wrong until the buyer has already authorized it.
</Warning>

### Allow payments in the iframe

Add `allow="payment"` to the iframe that contains your checkout page.

```html theme={"system"}
<iframe src="https://checkout.example.com/pay" allow="payment"></iframe>
```

Without it the browser blocks the payment request, so the Apple Pay button either does
not appear or the sheet cannot open. This is required on Safari 17 and iOS 17 and later.

Permission is delegated one level at a time, so where your checkout page sits more than
one iframe deep, **every** iframe in the chain needs the attribute. Missing it on a single
ancestor blocks the payment, even when the outer frames carry it.

<Warning>
  The permission is bound to the origin it was granted for, and `allow="payment"` grants it
  to the origin of the iframe `src`. If the framed page then navigates to a **different**
  origin, the permission stops applying, and Apple Pay disappears. A sign-in or SSO redirect
  that returns to another domain is the usual way this happens, and it is easy to miss
  because the frame still looks the same.

  The browser reports this as `SecurityError: Third-party iframes are not allowed to request
    payments unless explicitly allowed via Feature-Policy (payment)`.

  Keep the framed page on one origin for the whole checkout. Where it genuinely has to move
  between origins, name each one:

  ```html theme={"system"}
  <iframe
    src="https://checkout.example.com/pay"
    allow="payment https://checkout.example.com https://id.example.com"
  ></iframe>
  ```
</Warning>

### Register the top level domain

[Register the domain of the top level page](/guides/features/apple-pay/web#registering-a-domain)
for Apple Pay, and upload the domain association file to it, as you would for any other
checkout domain.

### Send the top level domain

The domain sent when the merchant session is created has to be that same top level
domain. This is the step most often missed, and registering the domain without sending
it fails in exactly the same way as sending it without registering.

When using Embed, set the [`topLevelDomain` option](/guides/payments/embed/options).

```js theme={"system"}
gr4vy.setup({
  // ...
  topLevelDomain: "shop.example.com",
});
```

When calling the API directly, send it as `domain_name` rather than deriving it from
`document.location`, which resolves to the frame's own domain.

```js theme={"system"}
body: JSON.stringify({
  validation_url: event.validationURL,
  domain_name: "shop.example.com",
}),
```

<Note>
  Every domain in the chain must be served over HTTPS with a valid certificate.
</Note>

## Common issues

<AccordionGroup>
  <Accordion title="Apple Pay option is not shown">
    There could be many reasons why this doesn't show.

    * Make sure you are using an [Apple Pay compatible browser and device](https://support.apple.com/en-gb/102896)
    * Make sure an Apple account is set up on the device and cards are added to it
    * Make sure the system is set up to handle Apple Pay
      * Check the Apple Pay connection is enabled
      * Check the Apple Pay connection is set up to handle transactions on the domain you're testing on
      * Check a card connector is enabled for the selected country/currency
      * Check that a Flow rule doesn't hide Apple Pay
  </Accordion>

  <Accordion title="Apple Pay payment failed">
    There could be a few reasons why this doesn't show but most likely
    this means the card was sent to a connector that either doesn't understand
    Apple Pay cards, or did not recognize the test card.
  </Accordion>

  <Accordion title="Error when adding test card to wallet">
    This seems to happen at times with the test cards provided by Apple. Try a
    different card, or try again later.
  </Accordion>

  <Accordion title="Apple Pay sheet opens and immediately closes">
    When using a real card in a sandbox environment, Apple Pay fails to initialize. This results
    in the Apple Pay UI quickly appearing and then disappearing.

    The reason for this happening is that the validation URL generated by Apple Pay is rejected by
    the [`POST /digital-wallets/apple/session`](/reference/digital-wallets/get-apple-pay-session) endpoint. Please only use [test cards](./testing) in the sandbox environment.
  </Accordion>

  <Accordion title="'Payment Not Completed' error message">
    If the Apple Pay dialog shows but closes automatically with a "Payment Not Completed"
    message, check whether your checkout page is inside an iframe. The merchant session is
    tied to a domain, and that domain has to be the top level page, so a nested checkout page
    needs the top level domain registered and sent when the session is created.

    See [Apple Pay inside an iframe](#apple-pay-inside-an-iframe) for the configuration this
    requires.
  </Accordion>
</AccordionGroup>

[documentation]: https://developer.apple.com/documentation/apple_pay_on_the_web/
