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

# Payment method support matrix

> Every card, wallet and alternative payment method TagadaPay can charge, and which processor each one runs on.

export const PaymentMatrix = () => {
  const PROCESSORS = [{
    id: 'tagadapay',
    label: 'TagadaPay'
  }, {
    id: 'stripe',
    label: 'Stripe'
  }, {
    id: 'airwallex',
    label: 'Airwallex'
  }, {
    id: 'checkout',
    label: 'Checkout.com'
  }, {
    id: 'nmi',
    label: 'NMI'
  }, {
    id: 'finix',
    label: 'Finix'
  }, {
    id: 'mastercard',
    label: 'Mastercard MPGS'
  }, {
    id: 'kesspay',
    label: 'KessPay'
  }, {
    id: 'ovri',
    label: 'Ovri'
  }, {
    id: 'ngenius',
    label: 'Network N-Genius'
  }, {
    id: 'ccavenue',
    label: 'CCAvenue'
  }, {
    id: 'authorizenet',
    label: 'Authorize.Net'
  }, {
    id: 'convesio',
    label: 'ConvesioPay'
  }, {
    id: 'paysafe',
    label: 'Paysafe'
  }, {
    id: 'tap',
    label: 'Tap Payments'
  }];
  const ALL_CARD = ['tagadapay', 'stripe', 'airwallex', 'checkout', 'nmi', 'finix', 'mastercard', 'kesspay', 'ovri', 'ngenius', 'ccavenue', 'authorizenet', 'convesio', 'paysafe', 'tap'];
  const ROWS = [{
    method: 'Cards',
    code: 'card',
    type: 'card',
    regions: 'Worldwide',
    recurring: true,
    on: ALL_CARD,
    note: 'Visa, Mastercard, Amex, Discover, JCB, Diners — per acquirer.'
  }, {
    method: 'Apple Pay',
    code: 'apple_pay',
    type: 'wallet',
    regions: 'Worldwide',
    recurring: true,
    on: ['tagadapay', 'stripe', 'checkout', 'nmi', 'mastercard']
  }, {
    method: 'Google Pay',
    code: 'google_pay',
    type: 'wallet',
    regions: 'Worldwide',
    recurring: true,
    on: ['tagadapay', 'stripe', 'checkout', 'nmi']
  }, {
    method: 'Link',
    code: 'link',
    type: 'wallet',
    regions: 'Worldwide',
    recurring: false,
    on: ['stripe'],
    note: 'Express Checkout Element only.'
  }, {
    method: 'iDEAL',
    code: 'ideal',
    type: 'apm',
    regions: 'NL · EUR',
    recurring: false,
    on: ['tagadapay', 'stripe', 'airwallex']
  }, {
    method: 'Bancontact',
    code: 'bancontact',
    type: 'apm',
    regions: 'BE · EUR',
    recurring: false,
    on: ['tagadapay', 'stripe', 'airwallex']
  }, {
    method: 'Klarna',
    code: 'klarna',
    type: 'apm',
    regions: 'EU · US · AU · CA',
    recurring: false,
    on: ['tagadapay', 'stripe', 'airwallex']
  }, {
    method: 'Afterpay / Clearpay',
    code: 'afterpay',
    type: 'apm',
    regions: 'AU · CA · NZ · US · GB',
    recurring: false,
    on: ['tagadapay', 'stripe', 'airwallex']
  }, {
    method: 'Affirm',
    code: 'affirm',
    type: 'apm',
    regions: 'US · CA · GB',
    recurring: false,
    on: ['tagadapay', 'stripe']
  }, {
    method: 'PayPal',
    code: 'paypal',
    type: 'apm',
    regions: 'Worldwide',
    recurring: false,
    on: ['tagadapay', 'airwallex']
  }, {
    method: 'TWINT',
    code: 'twint',
    type: 'apm',
    regions: 'CH · CHF',
    recurring: false,
    on: ['tagadapay', 'stripe', 'airwallex']
  }, {
    method: 'BLIK',
    code: 'blik',
    type: 'apm',
    regions: 'PL · PLN',
    recurring: false,
    on: ['tagadapay', 'stripe'],
    note: 'Needs a 6-digit one-time code from the shopper.'
  }, {
    method: 'EPS',
    code: 'eps',
    type: 'apm',
    regions: 'AT · EUR',
    recurring: false,
    on: ['tagadapay']
  }, {
    method: 'Przelewy24',
    code: 'p24',
    type: 'apm',
    regions: 'PL · PLN',
    recurring: false,
    on: ['tagadapay']
  }, {
    method: 'Multibanco',
    code: 'multibanco',
    type: 'apm',
    regions: 'PT · EUR',
    recurring: false,
    on: ['tagadapay'],
    note: 'Voucher — settles asynchronously.'
  }, {
    method: 'KNET',
    code: 'knet',
    type: 'apm',
    regions: 'KW · KWD',
    recurring: false,
    on: ['tagadapay']
  }, {
    method: 'Fawry',
    code: 'fawry',
    type: 'apm',
    regions: 'EG · EGP',
    recurring: false,
    on: ['tagadapay'],
    note: 'Cash-at-outlet voucher. Requires line items and a mobile number.'
  }, {
    method: 'Sofort',
    code: 'sofort',
    type: 'apm',
    regions: 'DE · AT · BE · NL · GB',
    recurring: false,
    on: ['tagadapay']
  }, {
    method: 'Skrill',
    code: 'skrill',
    type: 'apm',
    regions: 'EU · GB · US',
    recurring: false,
    on: ['tagadapay', 'airwallex']
  }];
  const TYPE_LABEL = {
    card: 'Card',
    wallet: 'Wallet',
    apm: 'APM'
  };
  const TABS = [{
    id: 'all',
    label: 'All'
  }, {
    id: 'card',
    label: 'Cards'
  }, {
    id: 'wallet',
    label: 'Wallets'
  }, {
    id: 'apm',
    label: 'APMs'
  }];
  const [type, setType] = useState('all');
  const [processor, setProcessor] = useState('all');
  const [query, setQuery] = useState('');
  const [dark, setDark] = useState(false);
  const [expanded, setExpanded] = useState({});
  useEffect(() => {
    const read = () => setDark(document.documentElement.classList.contains('dark'));
    read();
    const obs = new MutationObserver(read);
    obs.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => obs.disconnect();
  }, []);
  const TINT = {
    card: dark ? {
      bg: 'rgba(96,165,250,0.16)',
      fg: '#93c5fd'
    } : {
      bg: '#eff6ff',
      fg: '#1d4ed8'
    },
    wallet: dark ? {
      bg: 'rgba(192,132,252,0.16)',
      fg: '#d8b4fe'
    } : {
      bg: '#faf5ff',
      fg: '#7e22ce'
    },
    apm: dark ? {
      bg: 'rgba(52,211,153,0.16)',
      fg: '#6ee7b7'
    } : {
      bg: '#ecfdf5',
      fg: '#047857'
    }
  };
  const accent = dark ? '#34d399' : '#059669';
  const muted = dark ? '#9ca3af' : '#6b7280';
  const line = dark ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.08)';
  const controlStyle = {
    border: '1px solid ' + line,
    borderRadius: '6px',
    padding: '6px 10px',
    background: 'transparent',
    color: 'inherit',
    fontSize: '14px'
  };
  const badge = tint => ({
    background: tint.bg,
    color: tint.fg,
    padding: '2px 8px',
    borderRadius: '4px',
    fontSize: '12px',
    whiteSpace: 'nowrap',
    display: 'inline-block'
  });
  const labelFor = {};
  for (const p of PROCESSORS) labelFor[p.id] = p.label;
  const seen = {};
  for (const r of ROWS) for (const id of r.on) seen[id] = true;
  const usedProcessors = PROCESSORS.filter(p => seen[p.id]);
  const q = query.trim().toLowerCase();
  const visible = ROWS.filter(r => {
    if (type !== 'all' && r.type !== type) return false;
    if (processor !== 'all' && r.on.indexOf(processor) === -1) return false;
    if (!q) return true;
    const hay = [r.method, r.code, r.regions, r.note || ''].concat(r.on.map(id => labelFor[id] || id)).join(' ').toLowerCase();
    return hay.indexOf(q) !== -1;
  });
  return <div className="not-prose" style={{
    fontSize: '14px'
  }}>
      <div className="flex flex-wrap gap-2" style={{
    marginBottom: '10px'
  }}>
        {TABS.map(t => <button key={t.id} onClick={() => setType(t.id)} style={{
    padding: '4px 12px',
    borderRadius: '9999px',
    fontSize: '14px',
    cursor: 'pointer',
    border: '1px solid ' + (type === t.id ? accent : line),
    background: type === t.id ? accent : 'transparent',
    color: type === t.id ? dark ? '#052e21' : '#ffffff' : muted
  }}>
            {t.label}
          </button>)}
      </div>

      <div className="flex flex-wrap gap-2" style={{
    marginBottom: '10px'
  }}>
        <input type="text" value={query} placeholder="Search a method, code or region…" onChange={e => setQuery(e.target.value)} style={{
    ...controlStyle,
    flex: '1 1 220px'
  }} />
        <select value={processor} onChange={e => setProcessor(e.target.value)} style={controlStyle}>
          <option value="all">Any processor</option>
          {usedProcessors.map(p => <option key={p.id} value={p.id}>{p.label}</option>)}
        </select>
      </div>

      <div style={{
    color: muted,
    fontSize: '12px',
    marginBottom: '8px'
  }}>
        Showing {visible.length} of {ROWS.length} payment methods
      </div>

      <table style={{
    width: '100%',
    borderCollapse: 'collapse',
    tableLayout: 'fixed'
  }}>
          <colgroup>
            <col style={{
    width: '25%'
  }} />
            <col style={{
    width: '12%'
  }} />
            <col style={{
    width: '17%'
  }} />
            <col style={{
    width: '11%'
  }} />
            <col style={{
    width: '35%'
  }} />
          </colgroup>
          <thead>
            <tr style={{
    borderBottom: '1px solid ' + line
  }}>
              {['Method', 'Type', 'Coverage', 'Recurring', 'Available on'].map(h => <th key={h} style={{
    textAlign: 'left',
    padding: '8px 12px 8px 0',
    fontWeight: 600,
    minWidth: 0
  }}>{h}</th>)}
            </tr>
          </thead>
          <tbody>
            {visible.map(r => <tr key={r.code} style={{
    borderBottom: '1px solid ' + line,
    verticalAlign: 'top'
  }}>
                <td style={{
    padding: '8px 12px 8px 0',
    minWidth: 0
  }}>
                  <div style={{
    fontWeight: 500
  }}>{r.method}</div>
                  <code style={{
    fontSize: '12px',
    color: muted
  }}>{r.code}</code>
                  {r.note ? <div style={{
    fontSize: '12px',
    color: muted,
    marginTop: '4px'
  }}>{r.note}</div> : null}
                </td>
                <td style={{
    padding: '8px 12px 8px 0',
    minWidth: 0
  }}>
                  <span style={badge(TINT[r.type])}>{TYPE_LABEL[r.type]}</span>
                </td>
                <td style={{
    padding: '8px 12px 8px 0',
    color: muted,
    minWidth: 0
  }}>{r.regions}</td>
                <td style={{
    padding: '8px 12px 8px 0',
    minWidth: 0
  }}>{r.recurring ? 'Yes' : '—'}</td>
                <td style={{
    padding: '8px 0',
    minWidth: 0
  }}>
                  <div className="flex flex-wrap gap-1">
                    {(expanded[r.code] || r.on.length <= 8 ? r.on : r.on.slice(0, 6)).map(id => <span key={id} style={{
    padding: '2px 8px',
    borderRadius: '4px',
    fontSize: '12px',
    whiteSpace: 'nowrap',
    border: '1px solid ' + (id === 'tagadapay' ? accent : line),
    color: id === 'tagadapay' ? accent : muted,
    fontWeight: id === 'tagadapay' ? 600 : 400
  }}>
                        {labelFor[id] || id}
                      </span>)}
                    {r.on.length > 8 ? <button onClick={() => setExpanded({
    ...expanded,
    [r.code]: !expanded[r.code]
  })} style={{
    padding: '2px 8px',
    borderRadius: '4px',
    fontSize: '12px',
    whiteSpace: 'nowrap',
    cursor: 'pointer',
    border: '1px dashed ' + line,
    background: 'transparent',
    color: accent
  }}>
                        {expanded[r.code] ? 'Show less' : '+' + (r.on.length - 6) + ' more'}
                      </button> : null}
                  </div>
                </td>
              </tr>)}
          </tbody>
        </table>

      {visible.length === 0 ? <div style={{
    color: muted,
    padding: '24px 0',
    textAlign: 'center'
  }}>
          No payment method matches these filters.
        </div> : null}
    </div>;
};

# Payment method support matrix

Filter by method type, by processor, or search for a scheme by name or code.

<Info>
  A method listed here is **supported by the integration**. Whether it is available on *your*
  store also depends on your account, your currency and what your acquirer has enabled — resolve
  that at runtime with [`paymentSetup`](#discovering-what-a-store-can-actually-charge) rather than
  hard-coding this table.
</Info>

<PaymentMatrix />

## How to read this

TagadaPay charges money through two different arrangements, and the matrix mixes both:

<CardGroup cols={2}>
  <Card title="TagadaPay (managed)" icon="shield-check">
    We are your payment provider. You hold no processor credentials — onboarding, acquiring,
    settlement and payouts run through TagadaPay. This is the widest method catalogue.
  </Card>

  <Card title="Bring your own processor" icon="plug">
    You hold the merchant account and give us the credentials. The method catalogue is whatever
    that processor supports through our connector.
  </Card>
</CardGroup>

Every method in the matrix falls into one of three families, which behave very differently at
checkout:

| Family     | What the shopper does                                                                                | What your integration does                                                   |
| ---------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| **Card**   | Enters a card number (Visa, Mastercard, American Express, Discover, JCB, Diners Club — per acquirer) | Tokenize in the browser, then charge server-to-server                        |
| **Wallet** | Confirms in a native sheet (Touch ID, Face ID, screen lock)                                          | Open the sheet, receive a token, charge server-to-server                     |
| **APM**    | Is redirected to the provider, authorises, comes back                                                | Charge, receive a redirect URL, send the browser there, wait for the webhook |

<Warning>
  APMs are **redirect-based and asynchronous**. The charge call does not return a final status — it
  returns a `requireAction` with a redirect URL. The real outcome arrives on a webhook after the
  shopper returns. Never treat the charge response as a completed payment for an APM.
</Warning>

## Recurring and subscriptions

The `Recurring` column means the method can be charged again later without the shopper present
(a merchant-initiated transaction).

* **Cards, Apple Pay and Google Pay** are reusable. On the native path the wallet is stored as a
  network token and rebilled on the card rails, so a subscription started with Apple Pay keeps
  billing after the sheet is gone. Link is Express-only and single-use — hence its `—` in the
  Recurring column.
* **Most APMs are single-use.** The shopper authorises one specific amount on the provider's page.
  If your funnel has a subscription or a post-purchase upsell, an APM will not silently rebill —
  design for it.

## Discovering what a store can actually charge

The matrix tells you what the platform supports. It does not tell you what *your* store has
switched on. That answer is per-store and lives behind one call:

<CodeGroup>
  ```ts core-js (browser) theme={null}
  import { getStorePaymentSetup } from '@tagadapay/core-js';

  const setup = await getStorePaymentSetup({
    storeId: 'store_xxx',
    apiKey: 'tp_pk_live_***',
  });

  if (setup.config.apple_pay?.enabled) renderApplePayButton();
  if (setup.config.klarna?.enabled) renderKlarnaButton();
  ```

  ```ts node-sdk (server) theme={null}
  import { Tagada } from '@tagadapay/node-sdk';

  const tagada = new Tagada({ apiKey: process.env.TAGADA_API_KEY });
  const setup = await tagada.paymentSetup.get('store_xxx');

  const enabled = Object.values(setup.config).filter((m) => m.enabled);
  ```
</CodeGroup>

Keys in `config` are two-dimensional — `"<method>"` when there is one provider for that method,
and `"<method>:<provider>"` when the same method is routed through more than one. Read
[Alternative payment methods](/developer-tools/payments/alternative-payment-methods#discovery)
for the full shape.

## What this matrix does not tell you

<AccordionGroup>
  <Accordion title="Whether a method is commercially available to you" icon="handshake">
    Support in the integration is not the same as a signed agreement. Some methods need a separate
    contract with the scheme, or underwriting on your account. Talk to your account manager.
  </Accordion>

  <Accordion title="Per-country and per-currency limits" icon="globe">
    The Coverage column is indicative. Real availability is decided by your acquirer per market,
    and some methods have amount floors and ceilings. `paymentSetup` reflects what is actually
    switched on.
  </Accordion>

  <Accordion title="Refund behaviour per method" icon="rotate-left">
    Refunds go through the processor, not the method. Some APMs settle asynchronously (vouchers in
    particular) and cannot be refunded before they settle. Check the specific scheme's rules.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={3}>
  <Card title="Processors" icon="plug" href="/developer-tools/payments/processors">
    Every processor, and which operations each one supports.
  </Card>

  <Card title="Alternative payment methods" icon="building-columns" href="/developer-tools/payments/alternative-payment-methods">
    The redirect lifecycle, per-processor APM detail.
  </Card>

  <Card title="Apple Pay & Google Pay" icon="wallet" href="/developer-tools/payments/wallets">
    Native vault vs through-processor wallets.
  </Card>
</CardGroup>
