> ## 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.

# Alternative payment methods

> Apple Pay, Google Pay, Klarna, iDEAL, Bancontact — including the native vs through-processor distinction that determines which browser SDK to load.

# Alternative payment methods

APMs split into two operational categories from a code standpoint:

| Category          | Examples                                                              | Browser work                      | Server work                                                 |
| ----------------- | --------------------------------------------------------------------- | --------------------------------- | ----------------------------------------------------------- |
| **Wallet APMs**   | Apple Pay, Google Pay                                                 | YES — open native sheet, tokenize | Charge call (server-to-server)                              |
| **Redirect APMs** | Klarna, Afterpay, iDEAL, Bancontact, TWINT, BLIK, Affirm, EPS, PayPal | NONE — just a button click        | Charge call returns a redirect URL, browser navigates to it |

Within wallet APMs there’s a further split: **native** (you tokenize directly via TagadaPay’s vault) vs **through-processor** (Stripe Payment Request, Airwallex Wallet, etc.). This page explains all three paths.

***

## Supported methods

Beyond cards, TagadaPay supports the following methods. Availability per store
depends on the connected processor and the merchant's enabled config — always
confirm with [`paymentSetup.get(storeId)`](/developer-tools/partners/payment-setup).

**Cards & bank debits**

| Method                                                 | Notes                                                           |
| ------------------------------------------------------ | --------------------------------------------------------------- |
| Cards (Visa, Mastercard, Amex, Discover, JCB, Diners…) | All gateways                                                    |
| Apple Pay / Google Pay                                 | Native vault tokenization or through-processor (Stripe Express) |
| SEPA Direct Debit                                      | EUR bank debit                                                  |
| ACH (US bank account)                                  | USD bank debit                                                  |

**Alternative payment methods**

| Method              |           Stripe          | Airwallex | Managed |
| ------------------- | :-----------------------: | :-------: | :-----: |
| Klarna              |             ✓             |     ✓     |    ✓    |
| Afterpay / Clearpay |             ✓             |     ✓     |    ✓    |
| PayPal              |             —             |     ✓     |    ✓    |
| iDEAL               |             ✓             |     ✓     |    ✓    |
| Bancontact          |             ✓             |     ✓     |    ✓    |
| TWINT               |             ✓             |     ✓     |    ✓    |
| BLIK                |             ✓             |     —     |    ✓    |
| Affirm              |             ✓             |     —     |    ✓    |
| EPS                 |             —             |     —     |    ✓    |
| Skrill              |             —             |     ✓     |    —    |
| Crypto              | Dedicated crypto provider |           |         |

<Info>
  **Managed** = methods we run on your behalf when TagadaPay is your acquirer —
  no processor credentials of your own required.
</Info>

***

## Mental model: method × provider

Every APM in the system has two coordinates:

| Coordinate   | Question it answers              | Examples                                                                     |
| ------------ | -------------------------------- | ---------------------------------------------------------------------------- |
| **method**   | What does the customer see?      | `apple_pay`, `google_pay`, `klarna`, `ideal`, `bancontact`                   |
| **provider** | Which infrastructure handles it? | `apple_pay` (native), `google_pay` (native), `stripe`, `airwallex`, `tagada` |

The **same method can ship through different providers**, with different code paths. Example: a merchant might offer Apple Pay through native tokenization in one country, and through Stripe Payment Request in another.

You discover this via [`paymentSetup.get(storeId)`](/developer-tools/partners/payment-setup):

```jsonc theme={null}
{
  "apple_pay": {
    "enabled": true,
    "method": "apple_pay",
    "provider": "apple_pay",      // ← native path
    "metadata": { "country": "FR" }
  },
  "apple_pay:stripe": {
    "enabled": true,
    "method": "apple_pay",
    "provider": "stripe",          // ← through-processor path
    "publishableKey": "pk_live_xxx"
  },
  "klarna:stripe": {
    "enabled": true,
    "method": "klarna",
    "provider": "stripe",
    "type": "apm",
    "processorId": "proc_stripe_eu"
  }
}
```

Your client code reads the `provider` field to decide which browser SDK (if any) to load.

***

## Wallet APMs — native path

This is the simplest, most efficient path. The customer’s wallet talks **directly** to TagadaPay’s vault. No third-party processor SDK loads in the browser.

### Apple Pay (native)

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

if (!isApplePayAvailable()) return;  // device doesn't support Apple Pay

startApplePaySession(
  {
    basisTheoryApiKey: paymentSetup.apple_pay.metadata.basisTheoryApiKey,
    countryCode: 'FR',
    storeName: 'Acme SAS',
  },
  {
    currency: 'EUR',
    totalAmountMinor: 4999,
    lineItems: [
      { label: 'Subtotal', amount: '49.99' },
    ],
  },
  {
    onSuccess: async (tagadaToken, contacts) => {
      // tagadaToken is the same shape as a card token —
      // pass it to your server, then go through the standard charge flow.
      await fetch('/api/charge', {
        method: 'POST',
        body: JSON.stringify({
          tagadaToken,
          paymentMethod: 'apple_pay',
          customerData: {
            email: contacts.shippingContact?.emailAddress,
            firstName: contacts.shippingContact?.givenName,
            lastName: contacts.shippingContact?.familyName,
          },
        }),
      });
    },
    onError: (err) => console.error(err),
    onCancel: () => console.log('user cancelled'),
  },
);
```

Server side it’s **identical** to a card charge — just pass `paymentMethod: 'apple_pay'`:

```ts theme={null}
const { paymentInstrument } = await charging.paymentInstruments.createFromToken({
  tagadaToken, storeId, customerData,
});
const { payment } = await charging.payments.process({
  paymentInstrumentId: paymentInstrument.id,
  storeId,
  amount: 4999,
  currency: 'EUR',
  paymentMethod: 'apple_pay',
  mode: 'purchase',
});
```

### Google Pay (native)

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

if (!isGooglePayAvailable()) return;

startGooglePaySession(
  {
    basisTheoryApiKey: paymentSetup.google_pay.metadata.basisTheoryApiKey,
    merchantId: paymentSetup.google_pay.metadata.merchantId,
    merchantName: 'Acme SAS',
    countryCode: 'FR',
    sandboxed: false,  // true for test mode
  },
  { currency: 'EUR', totalAmountMinor: 4999 },
  {
    onSuccess: async (tagadaToken, payerInfo) => {
      // Same as Apple Pay — pass tagadaToken to your server with paymentMethod: 'google_pay'
    },
  },
);
```

<Tip>
  **Both wallet primitives are session-free.** They never call TagadaPay’s backend during the sheet/tokenize phase — only Apple/Google + BasisTheory. The first TagadaPay call happens on your server when you create the payment instrument. This is why partners can use them without a `checkoutSession`.
</Tip>

***

## Wallet APMs — through-processor (Stripe Express)

When `paymentSetup` exposes `apple_pay:stripe` or `google_pay:stripe`, the wallet runs through Stripe’s **Payment Request API**. You load Stripe.js, Stripe handles the wallet UI and tokenization, and you pass the resulting Stripe PaymentMethod to TagadaPay for the charge.

```ts theme={null}
import { StripeExpressCheckout } from '@tagadapay/core-js';
//                ^^^^^^^^^^^^^^^^^^^^
// Loads Stripe.js, opens the appropriate sheet (Apple Pay or Google Pay), and returns a Stripe PaymentMethod.

const checkout = new StripeExpressCheckout({
  publishableKey: paymentSetup['express_checkout:proc_stripe_eu'].publishableKey,
  amount: 4999,
  currency: 'EUR',
  country: 'FR',
});

const result = await checkout.show({
  requestPayerName: true,
  requestPayerEmail: true,
});

if (result.status === 'success') {
  // result.paymentMethodId is a Stripe PaymentMethod (pm_xxx)
  // Pass it to your server.
  await fetch('/api/charge-stripe-express', {
    method: 'POST',
    body: JSON.stringify({
      stripePaymentMethodId: result.paymentMethodId,
      processorId: paymentSetup['express_checkout:proc_stripe_eu'].processorId,
    }),
  });
}
```

Server-side, the charge call references the Stripe PaymentMethod via the processor:

```ts theme={null}
const { payment } = await charging.payments.process({
  storeId,
  customerId: customer.id,
  processorId: 'proc_stripe_eu',
  amount: 4999,
  currency: 'EUR',
  paymentMethod: 'apple_pay',  // logical method name
  externalPaymentMethodId: stripePaymentMethodId,  // Stripe pm_xxx
  mode: 'purchase',
});
```

### When does each path apply?

| `paymentSetup[...]` shape                     | Browser code                                              |
| --------------------------------------------- | --------------------------------------------------------- |
| `apple_pay` with `provider: 'apple_pay'`      | `startApplePaySession()` (native)                         |
| `apple_pay:stripe` with `provider: 'stripe'`  | `StripeExpressCheckout.show()`                            |
| `google_pay` with `provider: 'google_pay'`    | `startGooglePaySession()` (native)                        |
| `google_pay:stripe` with `provider: 'stripe'` | `StripeExpressCheckout.show()`                            |
| `express_checkout:proc_xxx`                   | `StripeExpressCheckout.show()` (multiple methods bundled) |

A merchant’s `paymentSetup` may include several of these at once. Render the buttons accordingly:

```ts theme={null}
const setup = await tagada.paymentSetup.get(storeId);

if (setup.apple_pay?.enabled && setup.apple_pay.provider === 'apple_pay') {
  renderNativeApplePayButton();
} else if (setup['apple_pay:stripe']?.enabled) {
  renderStripeExpressButton();
}
```

***

## Redirect APMs

For Klarna, Afterpay, iDEAL, Bancontact, TWINT, BLIK, Affirm, EPS, PayPal: the browser does **nothing** beyond clicking. All processor differences are absorbed server-side.

### Initiate from your server

```ts theme={null}
const { payment } = await charging.payments.process({
  storeId,
  customerId: customer.id,                              // not paymentInstrumentId
  processorId: setup['klarna:stripe'].processorId,      // proc_stripe_eu
  paymentMethod: 'klarna',                               // universal method name
  amount: 4999,
  currency: 'EUR',
  mode: 'purchase',
  returnUrl: 'https://your-platform.com/checkout/return?order=order_42',
  metadata: { yourOrderId: 'order_42' },
});

// payment.status === 'requires_action'
// payment.requireAction === 'redirect'
// payment.requireActionData.metadata.redirect.redirectUrl === 'https://...'
```

### Hand the redirect URL to the browser

```ts theme={null}
// Your /api/charge response:
return Response.json({
  paymentId: payment.id,
  redirectUrl: payment.requireActionData.metadata.redirect.redirectUrl,
});
```

```ts theme={null}
// Your client:
const { redirectUrl } = await fetch('/api/charge', { method: 'POST', body: ... }).then(r => r.json());
window.location.href = redirectUrl;
```

The customer completes the APM flow on the processor’s page (Klarna, iDEAL, etc.), then is sent to your `returnUrl`. The final payment status comes through a webhook (`payment.succeeded` / `payment.failed`).

### Same method, different processors

The exact same client code initiates Klarna whether the merchant’s `paymentSetup` routes through Stripe, Airwallex, or our native APM processor:

```ts theme={null}
// All three of these work identically on the client side
processorId: 'proc_stripe_eu'    // Klarna via Stripe
processorId: 'proc_airwallex_eu' // Klarna via Airwallex
processorId: 'proc_tagada_apm'   // Klarna via Tagada native
```

The processor decision is data-driven (read from `paymentSetup`), not code-driven. Your code just passes through the `processorId`.

***

## Discovering what each merchant has enabled

Use `paymentSetup.get(storeId)` to fetch the full method × provider config for a merchant. See [Payment setup config](/developer-tools/partners/payment-setup) for the full shape.

```ts theme={null}
const setup = await tagada.paymentSetup.get('store_xxx');

const enabledMethods = Object.entries(setup)
  .filter(([_, entry]) => entry.enabled)
  .map(([key, entry]) => ({
    key,                       // e.g. 'apple_pay:stripe'
    method: entry.method,      // 'apple_pay'
    provider: entry.provider,  // 'stripe'
    isExpress: entry.express,
    processorId: entry.processorId,
  }));

console.log(enabledMethods);
// → [
//     { key: 'card', method: 'card', provider: 'tagada', ... },
//     { key: 'apple_pay', method: 'apple_pay', provider: 'apple_pay', isExpress: true, ... },
//     { key: 'klarna:stripe', method: 'klarna', provider: 'stripe', ... },
//   ]
```

***

## Recurring with APMs

| Method     | Recurring support                                                                                            |
| ---------- | ------------------------------------------------------------------------------------------------------------ |
| Card       | ✅ MIT supported, see [payments page](/developer-tools/partners/payments#merchant-initiated-transactions-mit) |
| Apple Pay  | ✅ Token saved as a normal payment instrument; subsequent charges via MIT                                     |
| Google Pay | ✅ Same as Apple Pay                                                                                          |
| Klarna     | ⚠️ Requires Klarna’s subscription product, supported via Stripe processor only                               |
| iDEAL      | ❌ One-shot only                                                                                              |
| Bancontact | ❌ One-shot only                                                                                              |
| PayPal     | ✅ Vaulted via PayPal Reference Transactions, supported via Tagada native processor                           |

Pass `recurring: true` when calling `payments.process` for any APM that supports it — we’ll set the right processor flags so the issuer/wallet records the consent.

***

## Quick decision table

```
Customer clicks "Apple Pay" button
        │
        ▼
What does paymentSetup.apple_pay.provider say?
        │
        ├── 'apple_pay' ──────► startApplePaySession (core-js)
        │                       Browser → BasisTheory → tagadaToken
        │                       Server  → payments.process({ paymentInstrumentId })
        │
        └── 'stripe'    ──────► StripeExpressCheckout (core-js)
                                Browser → Stripe.js → Stripe PaymentMethod
                                Server  → payments.process({ externalPaymentMethodId })

Customer clicks "Klarna" button
        │
        ▼
Server: payments.process({ paymentMethod: 'klarna', customerId, processorId, returnUrl })
        │
        ▼
Server returns { redirectUrl }
        │
        ▼
Browser: window.location = redirectUrl
        │
        ▼
Klarna page → customer authorizes → returns to your returnUrl
        │
        ▼
Webhook: payment.succeeded
```
