# @tagadapay/node-sdk

Official server-side Node.js SDK for **TagadaPay** — the PSP-agnostic payment
orchestration platform.

One client (`new Tagada(apiKey)`), three jobs:

- **CRM** — manage stores, products, orders, customers, subscriptions,
  promotions, funnels, and checkout pages from your server.
- **Payments & Processing** — process and route payments across PSPs, and
  onboard onto TagadaPay as your payment processor.
- **Partners** — provision and manage accounts on behalf of your sub-merchants.

📚 **Full documentation:** https://docs.tagada.io/developer-tools/node-sdk/quick-start

---

## Install

```bash
npm install @tagadapay/node-sdk
```

Requires Node.js 18+.

## Quick start

```ts
import Tagada from '@tagadapay/node-sdk';

const tagada = new Tagada('your-api-key');

// List your stores
const stores = await tagada.stores.list();

// Process a payment — TagadaPay routes it across your connected processors
const { payment } = await tagada.payments.process({
  amount: 4999,
  currency: 'USD',
  storeId: 'store_xxx',
  paymentInstrumentId: 'pi_xxx',
});
```

**Need an API key?** Run the interactive initializer (writes `.env`):

```bash
npx -p @tagadapay/node-sdk@latest tagada-init you@example.com
```

…or create one from **Dashboard → Settings → Access Tokens**. Full guide:
[Get an API key](https://docs.tagada.io/developer-tools/node-sdk/get-api-key).

---

## Authentication — keys & scopes

The SDK is one client; what you can call depends on the **key** you pass.

| Key | Format | Domain | Scope |
| --- | --- | --- | --- |
| **CRM Key** | `sk_crm_live_…` / `sk_crm_test_…` (legacy UUID accepted) | CRM | one merchant account (`acc_xxx`) |
| **Processing Key** | `tp_sk_live_…` / `tp_sk_test_…` | Processing | one TagadaPay account (`tpa_xxx`) — charging |
| **Partner Key** | `tp_sk_live_…` / `tp_sk_test_…` (partner-scoped) | both | provisioning on behalf of your merchants |

```ts
const tagada = new Tagada(process.env.TAGADA_API_KEY!);
```

---

## 1 · CRM

Manage everything around the sale: catalog, customers, orders, recurring
billing, marketing, checkout pages, and infrastructure. Authenticated with a
**CRM Key**, served on the CRM API.

| Resource | What it does |
| --- | --- |
| `tagada.stores` | Create and manage stores |
| `tagada.products` | Products with variants and multi-currency pricing |
| `tagada.orders` | Query orders |
| `tagada.customers` | Look up and manage customers |
| `tagada.subscriptions` | Recurring billing — create, cancel, rebill, migrate processor |
| `tagada.promotions` / `tagada.promotionCodes` | Discounts and discount codes |
| `tagada.offers` / `tagada.checkoutOffers` | Upsells, downsells, order bumps |
| `tagada.blockRules` | Fraud / block rules |
| `tagada.funnels` | Multi-step funnels (checkout → upsell → thank-you) |
| `tagada.shopify` | Manage the Shopify checkout script — bind the storefront to a funnel, remove it, audit the theme |
| `tagada.plugins` | Deploy SPAs to the edge CDN, A/B split testing, custom domains |
| `tagada.domains` | Add, verify, configure, and remove custom domains |
| `tagada.emailTemplates` | Transactional email templates |
| `tagada.shippingRates` | Delivery options surfaced at checkout |
| `tagada.taxExemptions` | Exempt specific customers from tax |
| `tagada.webhooks` | Register webhook endpoints (+ signature verification) |
| `tagada.events` | Event log, statistics, and audit trail |

```ts
const store = await tagada.stores.create({
  name: 'My Store',
  baseCurrency: 'USD',
  presentmentCurrencies: ['USD', 'EUR'],
  chargeCurrencies: ['USD'],
});

const product = await tagada.products.create({
  storeId: store.id,
  name: 'Premium Plan',
  active: true,
  variants: [{ name: 'Monthly', sku: 'premium-monthly', price: 2999, default: true, active: true }],
});
```

→ Full walkthrough (store → product → funnel → live checkout link):
[Node SDK Quick Start](https://docs.tagada.io/developer-tools/node-sdk/quick-start).

---

## 2 · Payments & Processing

Two things live here: **processing payments**, and **onboarding onto TagadaPay
as your processor** so you can go live.

### Process & route payments

| Resource | What it does |
| --- | --- |
| `tagada.payments` | Process, refund, and void payments |
| `tagada.checkout` | Server-side checkout sessions and payments |
| `tagada.paymentInstruments` | Vaulted (stored) payment methods |
| `tagada.paymentFlows` | PSP routing — cascade, weighted, failover |
| `tagada.processors` | Connect and manage PSP credentials (incl. Stripe Connect OAuth) |
| `tagada.threeds` | 3D Secure session management (server-to-server flow) |
| `tagada.paymentSetup` | Runtime payment configuration for a store |

TagadaPay is **PSP-agnostic**: connect multiple processors and let payment
flows route intelligently with automatic fallbacks.

```ts
const flow = await tagada.paymentFlows.create({
  data: {
    name: 'US Primary',
    strategy: 'cascade',          // 'simple' = single PSP, 'cascade' = fallback chain
    pickProcessorStrategy: 'weighted',
    processorConfigs: [
      { processorId: 'proc_stripe', weight: 60 },
      { processorId: 'proc_adyen', weight: 40 },
    ],
    fallbackProcessorConfigs: [{ processorId: 'proc_nmi', orderIndex: 0 }],
  },
});
```

### Onboard onto TagadaPay (become live)

To accept payments through TagadaPay, a merchant submits a KYB **application**;
once approved, our team provisions a TagadaPay account (`tpa_xxx`) you manage
and charge on.

> **Best practice — minting a key is a one-time setup step, not app code.**
> Applying and minting your processing key is a bootstrap action (think
> seeding script, or a click in the dashboard). Do it **once**, store the
> secret in your secrets manager, then have your application read it from the
> environment. Never call `enroll()` / `tpas.keys.create()` in a request path.

```ts
// ── setup.ts — run ONCE (one-off script / CLI / CI bootstrap) ──────────────
// Submit an application with your CRM key. `recommendations` lists KYB gaps;
// `documentWarnings` lists document/declared-data mismatches to fix for direct
// acquirer acceptance.
const app = await tagada.processing.applications.create({
  businessInfo: { businessName: 'Acme SAS', country: 'FR' },
  representative: { firstName: 'Jane', lastName: 'Smith', email: 'jane@acme.com' },
});
if (app.documentWarnings?.length) {
  for (const w of app.documentWarnings) console.warn(`[${w.code}] ${w.message}`);
}

// Optional: pre-check a single document right after uploading it, BEFORE submit.
const audit = await tagada.processing.applications.auditDocument({
  kind: 'passport',
  fileUrl: uploadedUrl,
  representative: { firstName: 'Jane', lastName: 'Smith', nationality: 'FR' },
});
// audit.issues: [] means it looks consistent with the declared data.

// Once approved, enroll ONCE to mint your processing key, then store the secret.
// enroll() is the programmatic equivalent of activating processing in the
// TagadaPay dashboard and copying your key. It is NOT idempotent — a second
// call mints another key, so run this once and persist the result.
const { key } = await tagada.processing.enroll({ mode: 'live' });
console.log('Store in your secret manager as TAGADA_PROCESSING_KEY:', key.secret);
```

```ts
// ── app.ts — your application, every request ───────────────────────────────
// Read the stored key from the environment. Never mint keys here.
const tp = new Tagada(process.env.TAGADA_PROCESSING_KEY!); // tp_sk_…

const { data: tpas } = await tp.processing.tpas.list();
await tp.payments.process({ /* … */ });
```

`tagada.processing.tpas.*` exposes `keys`, `requirements`, and `documents` to
fill KYB and mint per-account charging keys. → [Apply for TagadaPay Processing](https://docs.tagada.io/developer-tools/node-sdk/processing-applications).

---

## 3 · Partners namespace

If you run a platform that onboards and charges for **many** merchants, the
`partners.*` namespace provisions on their behalf. Requires a **Partner Key**.

> Same rule applies: provisioning and key minting belong in your **onboarding
> flow**, not your checkout path. Each minted secret is returned **once** —
> persist it in your vault keyed by merchant, then read it back when charging.

```ts
const partner = new Tagada(process.env.TAGADA_PARTNER_KEY!); // partner-scoped tp_sk_…

// 1. Create a CRM merchant (idempotent on externalRef). Pass `email` to
//    invite your client to the dashboard in the same call: they receive
//    an email branded as YOU (your name, logo, colors — not TagadaPay's),
//    set their own password, and land in the CRM as admin of their org.
const merchant = await partner.partners.crm.merchants.create({
  legalName: 'Acme SAS',
  externalRef: 'merchant_42',
  email: 'owner@acme.com',        // optional — omit for API-only merchants
});
// merchant.portalInvite → { status: 'invited', emailMode: 'branded', … }

// Created the merchant without an email? Invite them later (idempotent):
await partner.partners.crm.merchants.invite(merchant.id, { email: 'owner@acme.com' });

// Mint a CRM key to read/manage the merchant's data from your backend.
const crmKey = await partner.partners.crm.merchants.keys.create(merchant.id);

// 2. Provision a TagadaPay account (TPA) + mint a processing key.
const tpa = await partner.partners.processing.tpas.create({
  legalName: 'Acme SAS',          // required
  accountId: merchant.id,         // attach to the merchant above
  country: 'FR',
  currency: 'EUR',
  externalRef: 'merchant_42_eu',  // idempotency
});
const procKey = await partner.partners.processing.tpas.keys.create(tpa.id);

// 3. Submit KYB requirements & documents as they arrive.
await partner.partners.processing.tpas.requirements.update(tpa.id, 'legal_name', 'Acme SAS');
```

- `partners.crm.merchants.*` — provision merchants, invite them to the dashboard (`create({ email })` / `invite()`), mint CRM keys
- `partners.processing.tpas.*` — provision accounts, mint processing keys, KYB requirements/documents
- `partners.processing.acquirers.list()` — the acquirers you've signed (pin one via `tpas.create({ acquirer })`)

A complete runnable boilerplate (CLI provisioning + Express checkout) lives at
[github.com/TagadaPay/partners-examples](https://github.com/TagadaPay/partners-examples).
→ [Partners guide](https://docs.tagada.io/developer-tools/partners/introduction).

---

## Error handling

```ts
import Tagada, {
  TagadaAPIError,
  TagadaNotFoundError,
  TagadaValidationError,
  TagadaAuthenticationError,
  TagadaRateLimitError,
} from '@tagadapay/node-sdk';

try {
  await tagada.payments.retrieve('pay_nonexistent');
} catch (err) {
  if (err instanceof TagadaNotFoundError) {
    // 404
  } else if (err instanceof TagadaValidationError) {
    console.log(err.errors); // field-level errors
  } else if (err instanceof TagadaRateLimitError) {
    console.log(`Retry after ${err.retryAfter}s`);
  }
}
```

Every API error also carries the machine-readable `code` and human `message`
returned by the API — branch on `err.code`, log `err.message`:

```ts
try {
  await partner.partners.processing.tpas.create({ accountId, legalName, acquirer: 'adyen' });
} catch (err) {
  if (err instanceof TagadaAPIError) {
    err.code;    // e.g. 'acquirer_not_available', 'no_acquirer_assigned',
                 //      'mor_sub_merchant_not_enabled', 'partner_scope_required'
    err.message; // e.g. 'Acquirer "adyen" is not available to partner par_… Signed acquirers: (none).'
    err.statusCode; // 422
  }
}
```

Retries on `429`, `5xx`, and network errors are automatic (tune via `maxRetries`).
Pass an `idempotencyKey` to safely retry mutations:

```ts
await tagada.payments.process(params, { idempotencyKey: 'unique-key-123' });
```

## Configuration

```ts
const tagada = new Tagada({
  apiKey: 'your-api-key',
  // All optional — sensible production defaults are used when omitted.
  baseUrl: 'https://api.tagada.io/api/public/v1',
  timeout: 30_000,
  maxRetries: 2,
  apiVersion: '2025-01-01',
});
```

## TypeScript

Fully typed. Import any type you need:

```ts
import type { Payment, Customer, Subscription, Tpa, TagadaList } from '@tagadapay/node-sdk';
```

## Full guide

- [Node SDK Quick Start](https://docs.tagada.io/developer-tools/node-sdk/quick-start)
- [Partners guide](https://docs.tagada.io/developer-tools/partners/introduction)
- [API Reference](https://docs.tagada.io/api-reference/introduction)

## License

© Tagada Pay. All rights reserved.
