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

# Partner Quick Start

> Provision a sub-merchant and charge a card in 4 steps. ~10 minutes to a successful payment.

# Partner Quick Start

**Time**: \~10 minutes · **Difficulty**: Beginner · **You will get**: a successful test payment

***

## What you’re going to build

```
1. Onboard a sub-merchant     →  tpa_xxx + auto-created store
2. Mint a Processing Key      →  tp_sk_xxx restricted to that tpa
3. Tokenize a card client-side→  tagadaToken (PCI-safe)
4. Charge                     →  payment_xxx ✓
```

By the end you’ll have a `payment_xxx` row visible in the TagadaPay dashboard and the test funds reflected on the settlement balance account (for MoR sub-merchants, that's the **partner's** shared balance — see [Payouts, balances & fees](/developer-tools/partners/payouts)).

***

## Prerequisites

| Requirement                          | How to get it                                                                                                                          |
| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| **Partner account**                  | Talk to your account manager — partners are activated manually                                                                         |
| **Partner API key (live)**           | Minted in the **Partner Hub** by TagadaPay — required for the [merchant automation API](/developer-tools/partners/merchant-automation) |
| **Node.js 18+** + **modern browser** | `node --version`, any evergreen browser                                                                                                |

<Warning>
  **Test mode first.** Append `?mode=test` or use the test partner key (`tp_sk_test_xxx`) for the entire flow below. Once you’ve charged a test card successfully, switch to live keys.
</Warning>

***

## 1. Install the two SDKs

```bash theme={null}
npm install @tagadapay/node-sdk @tagadapay/core-js
```

That’s the entire dependency footprint for partner S2S.

***

## 2. Provision a sub-merchant (server-side)

<Warning>
  **Payfac partners only.** `tpas.create()` returns `403 payfac_required` if your partner book has no payfac white-label. CRM-only partners: create the merchant with `partners.crm.merchants.create`, then submit [applications](/developer-tools/node-sdk/processing-applications) or use [embed onboarding](/developer-tools/partners/embed-onboarding) — see [Accounts](/developer-tools/partners/accounts).
</Warning>

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

const partner = new Tagada(process.env.TAGADA_PARTNER_KEY!);
//                          ↑ tp_sk_live_xxx (partner-scoped)

// One round-trip creates: a merchant (acc_xxx) + a TPA + a convenience store
const tpa = await partner.partners.processing.tpas.create({
  legalName: 'Acme SAS',       // required — the KYB legal entity
  country: 'FR',
  currency: 'EUR',
  externalRef: 'merchant_42',  // your own merchant id — used for idempotency
  // accountId: 'acc_xxx',    // pass this to attach to an EXISTING merchant
});

console.log(tpa.id);         // tpa_xxx — the sub-merchant TPA
console.log(tpa.accountId);  // acc_xxx — owning CRM merchant

// Discover the auto-created store via the CRM API
const stores = await partner.stores.list({ accountId: tpa.accountId });
const storeId = stores.data[0].id; // store_xxx
```

<Note>
  **Idempotent.** Calling `create()` again with the same `externalRef` returns the existing TPA — safe to retry on network errors.
</Note>

See [Processing provisioning](/developer-tools/partners/accounts) for KYB requirements and merchant portal policy. Need the CRM control plane too? See [CRM provisioning](/developer-tools/partners/merchants).

***

## 3. Mint a Processing Key for that TPA (server-side)

```ts theme={null}
const key = await partner.partners.processing.tpas.keys.create(tpa.id);
// label: "Your Partner — merchant_42" (from externalRef)

console.log(key.secret);  // tp_sk_live_xxx — RETURNED ONCE, store securely
console.log(key.label);   // human-readable merchant id
console.log(key.id);      // ak_xxx — the key id (visible later for revocation)
```

<Warning>
  The `secret` is only ever returned once. Store it in your secret manager immediately. If you lose it, you must revoke and mint a new Processing Key.
</Warning>

The Processing Key is **scoped to one TPA**. Even if it leaks, an attacker can only act on `merchant_42`'s processing — never on your other merchants.

***

## 4. Tokenize the card (client-side, in the browser)

```html theme={null}
<!-- Your merchant&rsquo;s checkout page -->
<form id="pay">
  <input id="card" placeholder="4242 4242 4242 4242" />
  <input id="exp"  placeholder="12/30" />
  <input id="cvc"  placeholder="123" />
  <button type="submit">Pay €49.99</button>
</form>

<script type="module">
  import { Tokenizer } from 'https://esm.sh/@tagadapay/core-js@2';

  const tokenizer = new Tokenizer({ environment: 'production' });

  document.getElementById('pay').addEventListener('submit', async (e) => {
    e.preventDefault();
    const tagadaToken = await tokenizer.tokenizeCard({
      cardNumber: card.value,
      expiryDate: exp.value,
      cvc: cvc.value,
    });

    // Send the token (and ONLY the token) to your server
    await fetch('/api/charge', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ tagadaToken, amount: 4999, currency: 'EUR' }),
    });
  });
</script>
```

<Tip>
  **PCI scope-out.** The raw card number, expiry, and CVC never touch your server. The `tagadaToken` is a single-use vault reference — safe to log, safe to store. See [`@tagadapay/core-js`](https://www.npmjs.com/package/@tagadapay/core-js).
</Tip>

***

## 5. Charge (server-side, with the Processing Key)

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

// Your /api/charge handler
export async function POST(req: Request) {
  const { tagadaToken, amount, currency } = await req.json();

  // Re-create a Tagada client with the PROCESSING KEY for this merchant
  const charging = new Tagada(process.env.MERCHANT_42_PROCESSING_KEY!);

  // Create a reusable payment instrument from the single-use token
  const { paymentInstrument, customer } = await charging.paymentInstruments.createFromToken({
    tagadaToken,
    storeId: storeId,      // the storeId you discovered from the CRM stores API
    customerData: {
      email: 'jane@example.com',
      firstName: 'Jane',
      lastName: 'Doe',
    },
  });

  // Charge it
  const { payment } = await charging.payments.process({
    paymentInstrumentId: paymentInstrument.id,
    storeId: storeId,
    amount,           // 4999 = €49.99 in minor units
    currency,         // 'EUR'
    paymentMethod: 'card',
    mode: 'purchase', // 'auth' for authorize-only, capture later
  });

  return Response.json({ paymentId: payment.id, status: payment.status });
}
```

That’s the complete loop. Tokenize browser-side, charge server-side, no checkout session anywhere.

***

## What you just built

```
Browser                          Your server                      TagadaPay
───────                          ───────────                      ─────────
                                                                   ┌─ partner key
                             partners.processing.tpas.create ────►│ provisions TPA + merchant
                                                                   ├─ stores tpa_xxx
                        partners.processing.tpas.keys.create ────►│ mints tp_sk_xxx
                                                                   │
tokenizeCard(card) ──► api.basistheory.com  ─────────────────────► │ vault stores PAN
       │                                                           │ returns tagadaToken
       │ tagadaToken                                                │
       ▼                                                            │
 POST /api/charge ──► createFromToken(tagadaToken)  ──► proc key ──►│ creates pi_xxx
                          │                                         │
                          ▼                                         │
                      payments.process(pi)            ──► proc key ─►│ routes to processor
                                                                    │ returns payment_xxx
```

***

## Common next steps

<Card title="Add Apple Pay / Google Pay" icon="wallet" href="/developer-tools/partners/apms">
  The wallet APMs use the same primitives in `core-js`, plus a small native sheet step. Native vs Stripe routing is explained on the APM page.
</Card>

<Card title="Add Klarna / iDEAL / Bancontact" icon="link" href="/developer-tools/partners/apms#redirect-apms">
  Redirect APMs need no client tokenization — you call `payments.process` with `paymentMethod: 'klarna'` and a `returnUrl`, then redirect the customer to the URL we hand back.
</Card>

<Card title="Add 3DS where required" icon="shield-check" href="/developer-tools/partners/payments#3ds">
  For European cards or high-risk transactions, run a 3DS challenge between tokenization and charge.
</Card>

<Card title="Discover what each merchant has enabled" icon="sliders" href="/developer-tools/partners/payment-setup">
  Use `paymentSetup.get(storeId)` to fetch the `method × provider` config and render the right buttons per merchant. Get `storeId` from `stores.list({ accountId })`.
</Card>
