---
title: "Apple Pay"
description: "Turn on Apple Pay, register your site and pay as a card with a token of the buyer's data."
url: https://docs.fluveo.com/payments/apple-pay/
---

> Index of every page: https://docs.fluveo.com/llms.txt. Integrating Fluveo? Read https://docs.fluveo.com/agents/integrate.md first.

> Too long? This page with code in one language only: https://docs.fluveo.com/payments/apple-pay.curl.md (cURL), https://docs.fluveo.com/payments/apple-pay.node.md (Node.js), https://docs.fluveo.com/payments/apple-pay.python.md (Python), https://docs.fluveo.com/payments/apple-pay.php.md (PHP).

# Apple Pay

Turn on Apple Pay, register your site and pay as a card with a token of the buyer's data.

An Apple Pay payment is a card payment. The buyer approves the payment on their device, your app or page sends Apple's payment data to Fluveo, and Fluveo turns it into a single-use card token. From there you pay, capture, refund and read events exactly as for any [card](https://docs.fluveo.com/payments/cards.md). Every store uses the same calls.

The flow has four calls:

1. Check that Apple Pay is on in your payment method configuration.
2. Register each website where the Apple Pay button shows, as a payment method domain.
3. Send the Apple Pay payment data to `POST /v1/tokens` and get a `tok_` back.
4. Pay with the token: make a PaymentMethod from it, or pass it straight to the PaymentIntent.

## Turn Apple Pay on

Your store has one payment method configuration. List it to find its id and see the switches. A new store has Apple Pay on and Google Pay off. Card stays on while either wallet is on.

```bash
curl https://api.fluveo.com/v1/payment_method_configurations \
  -H "Authorization: Bearer sk_test_..."
```

To turn Apple Pay on, or off, update that configuration with `apple_pay[display_preference][preference]` set to `on` or `off`.

```bash
curl https://api.fluveo.com/v1/payment_method_configurations/pmc_... \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: pmc-apple-pay-on" \
  -d "apple_pay[display_preference][preference]=on"
```

The answer is the configuration. Apple Pay on reads like this:

*apple_pay on the configuration*

```json
{
  "apple_pay": {
    "available": true,
    "display_preference": {
      "overridable": null,
      "preference": "on",
      "value": "on"
    }
  }
}
```

Turned off, `available` is `false` and both `preference` and `value` are `off`. The switch says whether a checkout may show the Apple Pay button. A payment you make through the API with a token is not refused when the switch is off. Every parameter is on [the configuration update](https://docs.fluveo.com/api/wallets.md#post-v1-payment-method-configurations-configuration).

## Register your website

A wallet button shows only on a website your store has registered. Create one payment method domain per host name, with the bare host: no scheme, port or path.

```bash
curl https://api.fluveo.com/v1/payment_method_domains \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: pmd-shop-example" \
  -d domain_name=shop.example.com
```

The domain shows a `status` for each wallet. With a test key, `apple_pay` and `google_pay` are `active` on a new domain:

*200 response (trimmed)*

```json
{
  "id": "pmd_1Qx7bIcDeFgHiJkLmNoPqRsT",
  "object": "payment_method_domain",
  "domain_name": "shop.example.com",
  "enabled": true,
  "livemode": false,
  "apple_pay": { "status": "active" },
  "google_pay": { "status": "active" }
}
```

When a wallet reads `inactive`, its `status_details.error_message` says why. Fix what it names, then ask again with `POST /v1/payment_method_domains/{id}/validate`, which returns the domain with each wallet's new status. `POST /v1/payment_method_domains/{id}` with `enabled=false` turns a domain off and `enabled=true` turns it back on. A domain's status does not turn a wallet on: the configuration's switch does. A store keeps at most 100 domains in each mode. See [the domain calls](https://docs.fluveo.com/api/wallets.md#post-v1-payment-method-domains).

## Create a token

When the buyer approves the payment, Apple Pay gives your app or page the payment data (`PKPaymentToken`'s `paymentData`) and a few facts about the card. Send them to `POST /v1/tokens`:

**Body parameters**

- `pk_token` (string, required): Apple's payment data as the device returned it, as JSON. Fluveo reads it once to make the token and never stores it.
- `pk_token_instrument_name` (string, optional): The card's name as Apple Pay shows it, such as `Visa 1881`.
- `pk_token_payment_network` (string, optional): The card network as Apple Pay names it, such as `Visa` or `MasterCard`.
- `pk_token_transaction_id` (string, optional): Apple Pay's transaction identifier. Each one makes one token. See [Test it](https://docs.fluveo.com/payments/apple-pay.md#test-it) for its test form.

```bash
curl https://api.fluveo.com/v1/tokens \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-3001-token" \
  --data-urlencode 'pk_token={"version":"EC_v1","data":"...","signature":"...","header":{...}}' \
  --data-urlencode "pk_token_instrument_name=Visa 1881" \
  --data-urlencode pk_token_payment_network=Visa \
  --data-urlencode pk_token_transaction_id=2f1c...e9
```

The answer is a token whose `card` names Apple Pay. `used` is `false` until a PaymentMethod takes it. `GET /v1/tokens/{id}` reads it again.

*200 response (trimmed)*

```json
{
  "id": "tok_1Qx7cJdEfGhIjKlMnOpQrStU",
  "object": "token",
  "type": "card",
  "used": false,
  "livemode": false,
  "card": {
    "brand": "Visa",
    "last4": "4242",
    "dynamic_last4": "4242",
    "tokenization_method": "apple_pay",
    "wallet": {
      "apple_pay": { "type": "apple_pay" },
      "type": "apple_pay"
    }
  }
}
```

The create takes only these four parameters. A card number in any of them is refused with a `400` whose `param` names that parameter, and no token is made. Every parameter is on [the token create](https://docs.fluveo.com/api/wallets.md#post-v1-tokens).

## Pay with the token

Pick one of two ways. Both make a card PaymentMethod from the token.

Make the PaymentMethod first with `card[token]`, then pay with it like any card:

```bash
curl https://api.fluveo.com/v1/payment_methods \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-3001-pm" \
  -d type=card \
  -d "card[token]=tok_1Qx7cJdEfGhIjKlMnOpQrStU"

curl https://api.fluveo.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-3001-pay" \
  -d amount=1000 \
  -d currency=usd \
  -d "payment_method_types[]=card" \
  -d payment_method=pm_1Qx7dKeFgHiJkLmNoPqRsTuV \
  -d confirm=true
```

Or pass the token to the PaymentIntent in `payment_method_data[card][token]`, and Fluveo makes the PaymentMethod for you:

```bash
curl https://api.fluveo.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-3002-pay" \
  -d amount=1000 \
  -d currency=usd \
  -d "payment_method_types[]=card" \
  -d "payment_method_data[type]=card" \
  -d "payment_method_data[card][token]=tok_1Qx7eLfGhIjKlMnOpQrStUvW" \
  -d confirm=true
```

`1000` is $10.00, in the smallest unit of the currency. Send nothing else of a card beside the token: `card[number]` next to `card[token]` is a `400` `parameter_unknown` on `card[number]`. The payment then moves as any card payment does. `capture_method=manual` holds it for a later capture, and you fulfil on `payment_intent.succeeded`. See [Cards](https://docs.fluveo.com/payments/cards.md#payment-flow).

## What comes back

The card shows its wallet in `card.wallet` on the PaymentMethod, and in `payment_method_details.card.wallet` on the Charge and in the `charge.succeeded` event:

*payment_method_details.card.wallet on the Charge*

```json
{
  "apple_pay": { "type": "apple_pay" },
  "dynamic_last4": "4242",
  "type": "apple_pay"
}
```

`last4` is the last four digits of the buyer's card. `dynamic_last4` is the last four of the number the device used in its place. A card paid without a wallet shows `wallet` as `null`.

## A token pays once

A token makes one PaymentMethod. Sending it a second time, in `card[token]` or `payment_method_data[card][token]`, is a `400` with `code` `token_already_used` on `param` `token`, and the token reads `used: true`.

The PaymentMethod made from it also pays once. Confirming another PaymentIntent with it is a `400` with no `code` and no `param`, whose message starts with "The provided PaymentMethod was previously used with a PaymentIntent without Customer attachment" and ends with "It may not be used again. To use a PaymentMethod multiple times, you must attach it to a Customer first." For the buyer's next purchase, ask Apple Pay for new payment data and make a new token.

## Refunds

Refund an Apple Pay payment as you refund any card payment: `POST /v1/refunds` with the `payment_intent` and, for part of it, an `amount`. See [Refund a payment](https://docs.fluveo.com/payments/refunds.md).

## Test it

With a test key, you need no device. Put a test form in `pk_token_transaction_id` and Fluveo makes the token of a test card, whatever `pk_token` holds:

```text
ApplePayStubs~<test card number>~<amount>~<currency>~<anything>
```

```bash
curl https://api.fluveo.com/v1/tokens \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: test-token-order-abc" \
  --data-urlencode 'pk_token={"version":"EC_v1","data":"","signature":"","header":{}}' \
  --data-urlencode "pk_token_instrument_name=Simulated Instrument" \
  --data-urlencode pk_token_payment_network=Visa \
  --data-urlencode "pk_token_transaction_id=ApplePayStubs~4242424242424242~1000~USD~order-abc"
```

The token's card is the test card, and its `dynamic_last4` is the card number's last four, `4242` here. Each transaction id makes one token, so give each test token a new last part. Write that part in letters: a run of digits there that forms a valid card number is refused. The test form works only with a test key.

## Errors

Every refusal below is a `400` with `type` `invalid_request_error`.

| When | `code` | `param` | `message` |
| --- | --- | --- | --- |
| `pk_token` is not Apple Pay payment data Fluveo can read | none | none | "The Apple Pay payment data isn't formatted correctly. Create new payment data using a current Fluveo mobile SDK: https://docs.fluveo.com/apple-pay" |
| A second token from the same `pk_token_transaction_id` | none | none | "This transaction approval has already been used to create a token." |
| `pk_token_payment_network` sent empty | `parameter_invalid_empty` | `pk_token_payment_network` | "You passed an empty string for 'pk_token_payment_network'. We assume empty values are an attempt to unset a parameter; however 'pk_token_payment_network' cannot be unset. You should remove 'pk_token_payment_network' from your request or supply a non-empty value." |
| A token sent a second time | `token_already_used` | `token` | "The token tok_... has already been used. Each token can only be used once to create a source." |
| `card[token]` or `payment_method_data[card][token]` names no token of your store | none | `token` | "Invalid token id: tok_..." |
| `GET /v1/tokens/{id}` of a token your store does not have | `resource_missing` | `token` | "No such token: 'tok_...'" |
| A PaymentMethod made from a token, used again | none | none | "The provided PaymentMethod was previously used with a PaymentIntent without Customer attachment, ..." (see [A token pays once](https://docs.fluveo.com/payments/apple-pay.md#pay-once)) |
| Card turned off while Apple Pay or Google Pay is on | none | none | "Invalid configuration. Apple Pay and Google Pay require Card to be enabled." |
| A 101st domain in one mode | none | none | "You have reached the maximum of 100 payment method domains." |

Make a new token from new Apple Pay data after any of the token errors. See [Errors](https://docs.fluveo.com/errors.md) for the error object.

## Google Pay

Google Pay has its own switch, `google_pay[display_preference][preference]` on the same configuration, off on a new store. Each domain shows its own `google_pay` status. Tokens read only Apple Pay data: `POST /v1/tokens` takes `pk_token` and nothing that carries Google Pay's.

> **For AI agents:**
>
> Apple Pay is a card: POST /v1/tokens with pk_token (Apple's paymentData JSON) and the optional pk_token_instrument_name, pk_token_payment_network and pk_token_transaction_id, then pay with card[token] on POST /v1/payment_methods (type=card) or payment_method_data[card][token] on the PaymentIntent. A token and the PaymentMethod made from it pay once; never retry a payment with a used token, ask for new Apple Pay data. In tests, set pk_token_transaction_id=ApplePayStubs~4242424242424242~1000~USD~<letters, new each time>. Never log or store pk_token.

## Next

- [Cards](https://docs.fluveo.com/payments/cards.md), for how a card payment moves, 3D Secure and declines.
- [Refund a payment](https://docs.fluveo.com/payments/refunds.md)
- [Testing](https://docs.fluveo.com/start/testing.md)
- [The wallets reference](https://docs.fluveo.com/api/wallets.md) lists every parameter of the calls on this page.
