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

# SaaS Billing with TagadaPay

> Bill subscriptions from your own app — processor-agnostic, powered by payment flows

# SaaS Billing with TagadaPay

**Time**: \~20 minutes | **Difficulty**: Intermediate

Bill subscriptions from **your own SaaS app** — not a storefront, not a funnel. You keep your UI; TagadaPay handles vaulting, routing, and recurring charges across **any processor or TPA**.

<Info>
  **This guide is for SaaS builders** migrating from Stripe Billing or building subscription billing from scratch. For e-commerce storefronts, see [Build a Store with AI](/developer-tools/headless-sdk/build-store-with-ai).
</Info>

***

## Why TagadaPay for SaaS (vs Stripe)

| Stripe                                 | TagadaPay                                                                  |
| -------------------------------------- | -------------------------------------------------------------------------- |
| Locked to Stripe as processor          | **Processor-agnostic** — Stripe, Adyen, Checkout.com, NMI, your own TPA    |
| One `PaymentMethod` per Stripe account | **One vault** — card tokenized once, routed to any processor               |
| Switching processor = re-collect cards | **Payment flows** — switch TPAs/processors without touching customer cards |
| `stripe.subscriptions.create`          | `tagada.subscriptions.create` — same mental model                          |

The killer feature: **payment flows**. Connect as many TPAs and processors as you want. If one TPA is banned, add a fallback in the flow — cards stay vaulted, rebills keep working.

```
Your SaaS checkout
       ↓
  Card tokenized (core-js) — once
       ↓
  ┌──── Payment Flow ────┐
  │  sandbox  (dev)      │
  │  → tagadapay-router  │  ← live TPA (tpa_xxx)
  │  → Stripe (optional) │
  │  → Checkout.com      │
  └──────────────────────┘
       ↓
  Subscription rebills — automatic, same vault
```

***

## Architecture: frontend vs backend

<Tabs>
  <Tab title="Frontend (browser)">
    **Package:** `@tagadapay/core-js`

    * Tokenize the card → `tagadaToken`
    * Handle 3DS if the charge asks for it (`requireAction: 'redirect'` → send the customer to the processor's hosted page; standalone 3DS sessions are only for gateway-style processors like NMI — see [Quick Start](/developer-tools/saas-billing/quick-start#handle-3ds-usually-nothing-to-do))
    * **Never** put your CRM API key in the browser

    ```ts theme={null}
    import { useCardTokenization } from '@tagadapay/core-js/react';

    const { tokenizeCard } = useCardTokenization({ environment: 'production' });
    const { tagadaToken } = await tokenizeCard({
      cardNumber: '4242424242424242',
      expiryDate: '12/30',
      cvc: '123',
      cardholderName: 'Alex Founder',
    });

    // Send tagadaToken to YOUR backend — not to TagadaPay directly
    await fetch('/api/payment-instruments', {
      method: 'POST',
      body: JSON.stringify({ tagadaToken, storeId, customerData }),
    });
    ```
  </Tab>

  <Tab title="Backend (server)">
    **Package:** `@tagadapay/node-sdk`

    * Create customer
    * Vault card (`createFromToken`)
    * Charge through payment flow (`payments.process`)
    * Create subscription (`subscriptions.create`)
    * Verify webhooks (`webhooks.constructEvent`)

    ```ts theme={null}
    import Tagada from '@tagadapay/node-sdk';
    const tagada = new Tagada(process.env.TAGADA_API_KEY!);

    // Vault
    const { paymentInstrument } = await tagada.paymentInstruments.createFromToken({
      tagadaToken, storeId, customerData,
    });

    // Charge (routes through your payment flow automatically)
    const { payment } = await tagada.payments.process({
      amount: 1900, currency: 'EUR', storeId,
      paymentInstrumentId: paymentInstrument.id,
      customerId, initiatedBy: 'customer',
      paymentFlowId: process.env.TAGADA_PAYMENT_FLOW_ID,
    });

    // Subscribe
    const { subscription } = await tagada.subscriptions.create({
      customerId, priceId, storeId, currency: 'EUR',
      defaultPaymentInstrumentId: paymentInstrument.id,
      paymentId: payment.id,
    });
    ```
  </Tab>
</Tabs>

***

## Working example

Everything in this guide was tested against production. Clone and run:

```bash theme={null}
git clone https://github.com/TagadaPay/examples.git
cd examples/mini-saas-billing   # or examples/mini-saas-billing in the monorepo
cp env.example .env             # add TAGADA_API_KEY + TAGADA_STORE_ID
npm install
npm run seed                    # product + cascade payment flow
npm run verify                  # automated end-to-end test
npm run dev                     # frontend :5173 + backend :3001
```

Open [http://localhost:5173](http://localhost:5173), click **Subscribe**, and watch the flow log.

***

## Next steps

<CardGroup cols={2}>
  <Card title="Quick Start" icon="rocket" href="/developer-tools/saas-billing/quick-start">
    Step-by-step: product → checkout → subscription in 15 minutes
  </Card>

  <Card title="Payment Flows" icon="route" href="/developer-tools/saas-billing/payment-flows">
    Multi-TPA routing, cascade, failover — the Stripe killer
  </Card>

  <Card title="Migrate from Stripe" icon="arrow-right-arrow-left" href="/developer-tools/saas-billing/migrate-from-stripe">
    Object mapping and cutover playbook
  </Card>

  <Card title="Subscriptions API" icon="repeat" href="/developer-tools/node-sdk/subscriptions">
    Rebilling, trials, cancel, processor migration
  </Card>
</CardGroup>
