# Accept Apple Pay Building with an AI agent? Start at https://docs.payme.io/agents.md Apple Pay works on Apple devices only: | iOS devices | macOS devices | |---|---| | Safari on an iPhone or iPad with Apple Pay set up | Safari on a Mac with Touch ID, or on a Mac from 2012 or later paired with an iPhone or Apple Watch that has Apple Pay set up and Handoff turned on | You can offer it in two ways: - **On PayMe's Hosted Payment Page.** Nothing to build: enable the service, then create a sale with `sale_payment_method: "apple-pay"` or `multi`. - **On your own page**, with the browser SDK's `appleSale()`. You host PayMe's domain file, register your domains, and show the Apple Pay button yourself. The steps are below. Installments aren't available with Apple Pay: a sale with `sale_payment_method: "apple-pay"` and more than one installment fails with error `351`. On a `multi` page that lets the buyer choose the number of installments, a buyer who pays with Apple Pay pays in a single payment. ## Enable Apple Pay A seller installs Apple Pay from the dashboard app store (חנות אפליקציות), and it is active as soon as it is installed. A partner enables it for its seller with [vas-enable](/payments/apple-pay/post-vas-enable-apple-pay). Without the service, `generate-sale` with `apple-pay` fails with error `720`. ## On PayMe's Hosted Payment Page You don't need an Apple developer account or a domain file: PayMe's Apple merchant ID is used. The button appears only in Safari on an Apple device with a card in Wallet, so a test from Chrome or Windows shows no Apple Pay. Recurring payments with Apple Pay need a merchant token, which only the Hosted Payment Page requests, and PayMe-side configuration of the account. See [Recurring Payments](/payments/saved-cards-recurring). ## On your own page 1. [Host PayMe's domain file](#step-1-host-paymes-domain-file) on every domain that shows the button. 2. [Register the domains](#step-2-register-your-domains) with `vas-enable`. 3. [Get the testing account](#step-3-get-the-testing-account). 4. [Create the sale](#step-4-create-the-sale) on your server. 5. [Show the Apple Pay button](#step-5-show-the-apple-pay-button) with the browser SDK. 6. [Test your integration](#step-6-test-your-integration). `appleSale()` doesn't request merchant tokens, so it pays one-time sales only. For recurring Apple Pay, use the Hosted Payment Page. Registering domains with `vas-enable` takes a partner key. If you are a seller without a partner, contact PayMe Partners () to register your domains. ### Step 1: Host PayMe's domain file Download PayMe's file from the environment where you call `vas-enable`: | Environment | Download from | |---|---| | Sandbox | `https://sandbox.payme.io/.well-known/apple-developer-merchantid-domain-association` | | Production | `https://live.payme.io/.well-known/apple-developer-merchantid-domain-association` | The two files are different. Serve the sandbox file while you test, and replace it with the production file before you register the domains in production. Serve the file unchanged at `https:///.well-known/apple-developer-merchantid-domain-association`, with no file extension, on every domain and subdomain that shows the button. Register each of them, for example `example.com` and `shop.example.com`, separately. PayMe fetches the file from every domain during the `vas-enable` call and compares it byte for byte with its own file for that environment. Check that the file is: - served over HTTPS with status 200, with no redirect and no error page; - exactly the file you downloaded from PayMe, never one generated in your own Apple developer account, with nothing added (not even a line break at the end); - not blocked by a CDN or a firewall. If any domain fails, the whole call fails with error `703` and a message naming the site: "Certificate does not match, please reconfigure." or "Cannot locate certificate." Fix the file and send the call again: nothing is left pending. ### Step 2: Register your domains Call [vas-enable](/payments/apple-pay/post-vas-enable-apple-pay) with the domains that show the button in `vas_data.websites`, up to 99 per call. PayMe checks each domain's file and registers the domains with Apple in the same call. ```json { "payme_client_key": "payme_partner_key", "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "vas_payme_id": "VASLDEMO-VASLDEMO-VASLDEMO-1234567", "vas_data": { "websites": ["example.com", "shop.example.com"] } } ``` The response's `vas_data.websites` lists the approved domains, PayMe's own host included. To add a domain later, call `vas-enable` again with the new domain: domains already approved stay approved. (Partners can also use `vas-update`; contact PayMe support for its details.) See [vas-enable](/payments/apple-pay/post-vas-enable-apple-pay) for the response and errors. ### Step 3: Get the testing account PayMe provides a testing iCloud account, and the test cards are in [Apple Pay testing information](/payments/getting-started/test-cards-and-payment-methods#apple-pay-testing-information). Ask PayMe which environment to test Apple Pay in. Devices you can test on: iPhone 6 or later, iPad mini 3 or later, iPad Air 2, iPad Pro and Apple Watch. You can't test on a Mac. ### Step 4: Create the sale On your server, create the sale with [generate-sale](/payments/apple-pay/post-generate-sale-apple-pay) and `sale_payment_method: "apple-pay"`. It authenticates with `seller_payme_id`; never send this call from the browser. `POST https://sandbox.payme.io/api/generate-sale` (production: `https://live.payme.io/api/generate-sale`) ```json { "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "sale_price": 1000, "currency": "ILS", "product_name": "Order 1234", "sale_payment_method": "apple-pay" } ``` ```json { "status_code": 0, "sale_url": "https://sandbox.payme.io/sale/generate/SALE1655-3843870J-NTPYLJNW-SOWRLK9X", "payme_sale_id": "SALE1655-3843870J-NTPYLJNW-SOWRLK9X", "payme_sale_code": 1027305, "price": 1000, "transaction_id": "", "currency": "ILS", "sale_payment_method": "apple-pay" } ``` Pass `payme_sale_id` to your page: `appleSale()` takes it as `saleGuid`. ### Step 5: Show the Apple Pay button #### Load the scripts Load Apple's `apple-pay-sdk.js`, which provides the `` element, and PayMe's SDK, which provides the global `PayMe`: ```html ``` If your site sets a Content Security Policy, allow `script-src https://cdn.payme.io https://applepay.cdn-apple.com`, `frame-src https://hf.payme.io` and `connect-src https://sandbox.payme.io https://live.payme.io`. #### Add the button Follow Apple's [buttons and marks guidelines](https://developer.apple.com/design/human-interface-guidelines/apple-pay/overview/buttons-and-marks/). Give the button an `id` or a class: you pass its selector to the SDK. ```html ``` `buttonstyle`, `type` and `locale` are Apple's attributes: see [Apple's button documentation](https://developer.apple.com/documentation/apple_pay_on_the_web/applepaybuttonstyle). #### Check that Apple Pay is available Show the button only when both checks pass: | Check | Returns `true` when | |---|---| | `PayMe.isApplePayAvailable()` | The browser has Apple Pay (`window.ApplePaySession` exists) | | `PayMe.isSupportsApplePayVersion()` | The browser supports Apple Pay JS API version `PayMe.applePayApiVersion` (`4`) | | `instance.isMerchantSupportsApplePay(merchantId)` | Optional: Apple Pay is set up on the device (Apple's `canMakePayments()`) | ```javascript if (!(PayMe.isApplePayAvailable() && PayMe.isSupportsApplePayVersion())) { document.getElementById('pay-with-apple-pay').classList.add('hidden'); } ``` #### Start the sale ```javascript const publicKey = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; // the seller's public key const paymeSaleId = 'SALE1655-3843870J-NTPYLJNW-SOWRLK9X'; // from Step 4 const testMode = true; // false in production const instance = await PayMe.create(publicKey, { testMode }); let sale; try { sale = instance.appleSale({ merchantId: 'merchant.paymeproduction', saleGuid: paymeSaleId }); } catch (error) { // error.message is PayMe.ERR_MESSAGES.NOT_SUPPORTED_DEVICE, NO_APPLE_PAY or NOT_SUPPORTED_VERSION: // hide Apple Pay and offer another payment method. } if (sale) { sale.letsRock({ payButtonSelector: '#pay-with-apple-pay', buttonLocale: 'he', // optional: sets locale="he-IL" on the canceled() { // the buyer closed the Apple Pay sheet }, validated(err) { // PayMe finished Apple's merchant validation; err is set if it failed }, paymentCompleted(err, result) { if (err) return showError(err.message); // ":" for PayMe errors if (result.sale_status === 'completed') confirmOnServer(result.payme_sale_id); else showError(result.status_error_details); }, }).catch((err) => showError(err.message)); // config errors, VALIDATION_NO_NETWORKS, PayMe errors } ``` `appleSale()` options: | Option | Required | What it is | |---|---|---| | `saleGuid` | Yes | The `payme_sale_id` from Step 4 | | `merchantId` | Yes | Pass `merchant.paymeproduction`. The SDK uses it only for the device check; the identifier Apple validates comes from the seller's Apple Pay service. | | `debugFn` | No | A function that receives the SDK's debug messages | `letsRock(config)` takes the button's selector as a string, or this object: | Key | Default | What it does | |---|---|---| | `payButtonSelector` | none | A CSS selector for your Apple Pay button | | `buttonLocale` | none | Sets the `locale` of an ``: `en` becomes `en-US`, `he` becomes `he-IL`, and a value with a region, such as `fr-FR`, is used as is | | `canceled()` | does nothing | Called when the buyer closes the Apple Pay sheet | | `validated(err)` | does nothing | Called after PayMe completes Apple's merchant validation, before the buyer authorizes the payment. `err` is set if the validation failed. | | `paymentCompleted(err, result)` | does nothing | Called with the payment's result. `result` is the sale: `payme_sale_id`, `sale_status`, `payme_transaction_auth_number`, `buyer_card_mask`… | The callbacks must be plain functions or arrow functions, not `async` functions: an `async` callback makes `letsRock()` reject with a `validation:invalid-type-for-…-callback` error. Treat the order as paid only when `result.sale_status` is `completed`, and confirm it with the [sale callback](/payments/getting-started/callbacks) on your server before you fulfil. `letsRock()` resolves to a start function and attaches it to your button's click. Other methods on the sale: - `sale.setButtonLocale(locale)` changes the button's locale later. - `sale.destroy()` detaches the button and aborts an open Apple Pay session. Call it before you start a new sale on the same page. - `sale.manual()` returns `{ verify, preparePayment }`, the two steps `letsRock()` runs. You don't need it with `letsRock()`. #### Start the payment from your own control If your page has no Apple Pay button for the SDK to find, give `letsRock()` a selector that matches nothing, and call the start function it resolves to from your own click handler: ```javascript sale.letsRock('.no-apple-pay-button').then((startPayment) => { document.getElementById('my-custom-button').addEventListener('click', startPayment); }); ``` Only a user action can start an Apple Pay payment. Call `startPayment` synchronously inside a click or tap handler. A call from a timer, or from a `postMessage` that a timer sent, fails with Apple's error "Must create a new ApplePaySession from a user gesture handler" ([Apple's documentation](https://developer.apple.com/documentation/apple_pay_on_the_web/apple_pay_js_api/creating_an_apple_pay_session)). #### Inside another site's iframe Apple validates the top-level page's domain. When the SDK runs inside another site's iframe, pass that domain, which must be registered in Step 2: ```javascript const instance = await PayMe.create(publicKey, { testMode, domainName: 'shop.example.com' }); ``` ### Step 6: Test your integration 1. Put the test page below on your registered domain, for example `https://shop.example.com/apple-pay-test.html`. 2. Create a sale as in Step 4. 3. Open the page in Safari on a test device, fill in the seller's public key and the sale ID, and select **Start interaction**. 4. Pay with Apple Pay, and check that `paymentCompleted` and your sale callback both report the sale. ![Apple Pay test page](/marketplaces-and-platforms/_files/apple-pay-test-page.png) ```html Apple Pay test page






```

## Errors

The SDK reports errors as an `Error` whose `message` is one of the values in `PayMe.ERR_MESSAGES`, or a PayMe error.

| `PayMe.ERR_MESSAGES` key | `message` | When | Raised by |
|---|---|---|---|
| `NOT_SUPPORTED_DEVICE` | `no-supported:apple-pay-not-available-for-device` | The browser has no Apple Pay (`window.ApplePaySession` is missing) | `instance.appleSale()` throws |
| `NO_APPLE_PAY` | `no-supported:apple-pay-jsapi-not-available` | Apple Pay isn't set up on the device (`canMakePayments()` returned `false`) | `instance.appleSale()` throws |
| `NOT_SUPPORTED_VERSION` | `no-supported:apple-pay-version-not-supported` | The browser doesn't support Apple Pay JS API version 4. There is no workaround: offer another payment method. | `instance.appleSale()` throws |
| `VALIDATION_NO_NETWORKS` | `validation-error:no-supported-networks` | None of the seller's card brands is supported by Apple Pay. Contact PayMe support. | `letsRock()` rejects |
| `INVALID_CFG_PARAM` | `validation:unknown-configuration-parameter` | `letsRock()` got something other than a selector or a config object | `letsRock()` rejects |
| `INVALID_VALIDATED_CB`, `INVALID_CANCELLED_CB`, `INVALID_COMPLETED_CB` | `validation:invalid-type-for-validated-callback`, `validation:invalid-type-for-cancelled-callback`, `validation:invalid-type-for-payment-completed-callback` | That callback isn't a plain function (for example, it is `async`) | `letsRock()` rejects |

When PayMe rejects a call (the sale doesn't exist or isn't payable, the service isn't enabled, the merchant validation failed, the payment was declined), `letsRock()` rejects, or `validated(err)` or `paymentCompleted(err)` gets an `Error` whose message is `":"`.

The `message` values are codes, not text for buyers. Map them to your own messages:

```javascript
const messages = PayMe.ERR_MESSAGES;

const buyerMessages = {
  [messages.NOT_SUPPORTED_DEVICE]: 'This device does not support Apple Pay',
  [messages.NO_APPLE_PAY]: 'Apple Pay is not set up on this device',
  [messages.NOT_SUPPORTED_VERSION]: 'This device does not support the current Apple Pay version',
  [messages.VALIDATION_NO_NETWORKS]: 'Apple Pay is not available for this seller',
};

function showError(code) {
  alert(buyerMessages[code] ?? 'Apple Pay is not available. Please choose another payment method.');
}
```