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

# Apple Pay & Google Pay

> Which processors accept wallets, the two integration paths, and what you must configure before the sheet will open.

# Apple Pay & Google Pay

Wallets sit between cards and APMs. The shopper confirms in a native sheet instead of typing a card
number, but what comes out the other side is a **network token that rides the card rails** — so
unlike an APM, a wallet payment can be stored and rebilled.

## Availability

| Processor               | Apple Pay | Google Pay | Notes                                                 |
| ----------------------- | :-------: | :--------: | ----------------------------------------------------- |
| **TagadaPay** (managed) |     ✓     |      ✓     | Nothing to configure beyond your domains              |
| Stripe                  |     ✓     |      ✓     | Also exposes Link and Klarna through Express Checkout |
| Checkout.com            |     ✓     |      ✓     |                                                       |
| NMI                     |     ✓     |      ✓     |                                                       |
| Mastercard MPGS         |     ✓     |      —     | Apple Pay only                                        |

Every other processor charges a wallet-sourced card as a plain card if it gets one, with no
cryptogram and no wallet indicator — which usually means worse authorisation rates and no liability
shift. Treat the table above as the real list.

## Two integration paths

<CardGroup cols={2}>
  <Card title="Native (vault tokenization)" icon="shield-halved">
    You open the sheet yourself. The encrypted wallet payload is decrypted inside TagadaPay's PCI
    vault and turned into a Tagada token you charge like any other instrument.
    **Works with every processor in the table.**
  </Card>

  <Card title="Through-processor (Express)" icon="bolt">
    The processor's own element renders the button and confirms the payment. Less code, but you
    inherit that processor's rules — and the resulting instrument may not be reusable.
  </Card>
</CardGroup>

<Note>
  Prefer the **native** path when you want one code path across processors, stored credentials for
  subscriptions, or control over the sheet. Prefer **Express** when you want the shortest possible
  integration on Stripe and do not need to rebill.
</Note>

## Native wallets with core-js

<Steps>
  <Step title="Check the device can pay">
    Both checks are async and must run before you render a button. Never render an Apple Pay button
    on a device that cannot use it.

    ```ts theme={null}
    import { isApplePayAvailable, isGooglePayAvailable } from '@tagadapay/core-js';

    const canApple = await isApplePayAvailable();
    const canGoogle = await isGooglePayAvailable();
    ```
  </Step>

  <Step title="Resolve the platform registration for your domain">
    Apple Pay runs through **TagadaPay's platform integration**: we hold the Apple certificates and
    register your checkout domains under them — you never manage a certificate yourself. Each
    registered domain gets its own registration id, so resolve it from the domain the shopper is
    actually on:

    ```ts theme={null}
    import { getStorePaymentSetup, resolveApplePayByok } from '@tagadapay/core-js';

    const setup = await getStorePaymentSetup({ storeId: 'store_xxx', apiKey: 'tp_pk_live_***' });
    const applePayMethod = setup.config.apple_pay;
    const registration = resolveApplePayByok(applePayMethod?.metadata, window.location.hostname);
    ```

    `registration` is `null` when the domain uses the store's default platform configuration —
    that is normal, not an error. When it is non-null, pass its `merchantRegistrationId` into the
    session in the next step.
  </Step>

  <Step title="Open the sheet from a user gesture">
    Apple Pay **must** be started synchronously inside a click handler. Any `await` before
    `startApplePaySession` and the browser will refuse to open the sheet.

    ```ts theme={null}
    import { startApplePaySession, applePayTokenToTagadaToken } from '@tagadapay/core-js';

    button.addEventListener('click', () => {
      startApplePaySession(
        {
          basisTheoryApiKey: 'key_***',
          countryCode: 'FR',
          storeName: 'My Store',
          merchantRegistrationId: registration?.merchantRegistrationId, // null → store default
        },
        { totalAmountMinor: 4999, currency: 'EUR' },
        {
          onSuccess: async (token, contacts) => {
            const tagadaToken = applePayTokenToTagadaToken(token);
            await chargeOnYourServer(tagadaToken, contacts);
          },
          onError: (msg) => console.error(msg),
        },
      );
    });
    ```
  </Step>

  <Step title="Charge server-side">
    The browser never charges. Send the token to your backend and create the payment there.

    ```ts theme={null}
    const { paymentInstrument } = await tagada.paymentInstruments.createFromToken({
      tagadaToken,
      storeId: 'store_xxx',
      customerData: { email: 'shopper@example.com' },
    });

    await tagada.payments.process({
      amount: 4999,
      currency: 'EUR',
      storeId: 'store_xxx',
      paymentInstrumentId: paymentInstrument.id,
    });
    ```
  </Step>
</Steps>

Google Pay is the same shape with `startGooglePaySession` and a `GooglePayServiceConfig`.

<Warning>
  `startApplePaySession` and `startGooglePaySession` take `(config, request, callbacks)` and return
  `void` — they are **not** promises. The token arrives in `callbacks.onSuccess`.
</Warning>

## Express Checkout (Stripe)

Stripe's Express Checkout Element renders Apple Pay, Google Pay, Link and Klarna as one row of
buttons. TagadaPay pins the intent to exactly the wallet the shopper pressed.

You discover it through `paymentSetup` — an `express_checkout:<processorId>` entry carries the
Stripe `publishableKey` and the per-method `methods` map — and confirm it with the headless SDK's
`tagada.payment.processStripeExpress({ checkoutSessionId, processorId, paymentMethod })`, which
returns the `clientSecret` for `stripe.confirmPayment()`.

Supported express methods are `apple_pay`, `google_pay`, `link` and `klarna_express`. Anything else
the Stripe account has switched on is deliberately refused rather than half-handled.

<Warning>
  An Express Checkout payment records a **tokenless** instrument. It settles the order it was
  created for, but there is no stored credential behind it, so it cannot be rebilled. Do not start a
  subscription from an Express button — use the native path.
</Warning>

## What you must configure

<AccordionGroup>
  <Accordion title="Apple Pay — register every checkout domain with us" icon="globe">
    Apple Pay runs through TagadaPay's **platform integration**: we hold the Apple certificates and
    register your domains under them. You never create an Apple Developer account, host a
    verification file, or manage a certificate.

    What you must do is tell us **every domain** that will show an Apple Pay button — checkout
    subdomains and any CDN alias you serve checkout from included. An unregistered domain fails at
    merchant validation, before the sheet ever opens, and the failure is silent to the shopper
    (the button simply does not work). Registration is per-domain: a store served from two
    hostnames needs both registered.

    At runtime, `resolveApplePayByok(applePayMethod.metadata, window.location.hostname)` returns
    the registration for the current domain; pass its `merchantRegistrationId` into
    `startApplePaySession` (or `tokenizeApplePay`). A `null` result means the domain uses the
    store's default platform configuration.
  </Accordion>

  <Accordion title="Google Pay — merchant id" icon="google">
    Google Pay needs your Google merchant id for production, and the button will render in test mode
    without it. The gateway side is handled by TagadaPay.
  </Accordion>

  <Accordion title="Both — HTTPS and a real user gesture" icon="lock">
    Wallets need HTTPS and a genuine click or tap to open the sheet. Apple Pay additionally refuses
    `localhost` — test it through an HTTPS tunnel. Google Pay tolerates `localhost` in test mode.
  </Accordion>
</AccordionGroup>

## Recurring and wallets

Unlike APMs, wallets **can** be rebilled. The wallet gives you a network token that is stored like
a card, so a subscription started with Apple Pay keeps billing after the sheet is long gone.

Two rules make the difference between a working rebill and a decline:

* **The cryptogram is single-use.** It authenticates the first, shopper-present charge only.
  Replaying it on a later merchant-initiated charge gets the charge declined.
* **Later charges are merchant-initiated.** They carry the stored credential and the network
  transaction id from the first payment instead. TagadaPay does this for you on the native path.

<Info>
  This is the practical reason to prefer native tokenization for anything with a subscription, a
  rebill or a post-purchase upsell.
</Info>

## Next

<CardGroup cols={3}>
  <Card title="Support matrix" icon="table" href="/developer-tools/payments/support-matrix">
    Every method and processor in one filterable view.
  </Card>

  <Card title="Card tokenization" icon="credit-card" href="/developer-tools/examples/card-tokenization">
    The same vault flow for plain cards.
  </Card>

  <Card title="From the SDKs" icon="code" href="/developer-tools/payments/sdk-usage">
    Wallet helpers in each package.
  </Card>
</CardGroup>
