Server-to-server payments
Once you have a Processing Key (tp_sk_…) + a storeId, the entire payment lifecycle is yours. No checkout session, no funnel, no UI assumptions on our side.
The four steps
Step 1 — Tokenize the card (browser)
Use@tagadapay/core-js. The card data goes directly to BasisTheory’s vault — never to your server, never to ours:
Step 2 — Create a payment instrument (server)
Convert the single-usetagadaToken into a reusable paymentInstrument:
customer is reused if you provide a matching email — one customer can hold multiple instruments.
Address field names. The platform stores and validates addresses as
address1, address2, postal, city, state, country (ISO 3166-1 alpha-2), plus firstName / lastName. The SDK still accepts the legacy line1 / line2 / postalCode aliases and maps them for you, but prefer the canonical names.Addresses are optional — including for digital products. Neither
billingAddress nor shippingAddress is required to charge: selling digital goods with no shipping address works out of the box. If you do provide an address, it is only forwarded to the acquirer when it carries a valid ISO 3166-1 alpha-2 country — an incomplete address (e.g. missing country) is safely omitted from the acquirer call instead of failing the payment. Providing a complete billingAddress is still recommended: it improves issuer approval rates (AVS) and 3DS frictionless outcomes.Step 3 — 3DS (usually nothing to do)
Most integrations skip this step. Whether 3-D Secure runs, and how, is decided by the PSP/acquirer behind your TPA — and most of them handle it themselves. There are two ways it can happen:
Option 1 — PSP/acquirer-hosted 3DS (the default)
Modern PSPs (Adyen, Stripe, …) own the 3DS challenge. You don’t callthreeds.createSession. You just charge (Step 4) with a returnUrl; if the issuer requires SCA, payments.process comes back with requireAction: 'redirect' and a redirectUrl pointing at the processor’s own hosted 3DS page. You send the customer there, they authenticate, and they return to your returnUrl. Nothing to collect, nothing to pass through — the processor reconciles the authentication internally.
This is the path the rest of this guide uses. If your TPA is on a hosted-3DS PSP, ignore the code below and go straight to Step 4.
Option 2 — Tagada standalone 3DS (the exception)
Some gateway-style processors (e.g. NMI) don’t host 3DS, but they do accept pre-authenticated 3DS values (CAVV, ECI, DS transaction id…) on the charge. For those — and only when the acquirer is compatible and takes those values as input — Tagada can run the 3DS flow for you, collect the values, and pass them raw to the acquirer. You opt in explicitly:When does 3DS trigger? When the card flow has
threeDsEnabled: true (set per-merchant via Payment Flows) or the issuer requests SCA. How it runs is up to the processor: hosted PSPs return requireAction: 'redirect' (Option 1); gateways that need raw values use the standalone session (Option 2). When in doubt, skip this step and just handle the requireAction that the charge returns.Step 4 — Charge
Handling the response
Multi-TPA routing (cascade & weighted)
payments.process doesn’t hit a processor directly — it runs the payment
flow attached to the storeId. A single merchant can own several TPAs (e.g. an
Adyen sub-merchant and a Stripe Merchant-of-Record account) and route across
them with no code change on your side: same process call, different
storeId.
- Cascade (failover) — the flow has a primary processor and an ordered
fallback list (
fallbackMode: true). If the primary declines (non-terminal), Tagada automatically retries the same charge on the next TPA, with a fresh payment method per attempt. Your code just sees the finalcaptured. - Weighted — multiple processors with weights; each charge is routed to one TPA by weight.
A processing key restricted to one TPA resolves to that TPA’s merchant
account, so it can charge any store of that merchant — including a store
whose flow spans several of the merchant’s TPAs. You don’t need a separate key
per processor.
partners-examples/02-merchant-checkout.
Auth + capture (split-flow)
Authorize now, capture later (hotels, car rentals, pre-orders, marketplaces):payments.cancel(auth.id) to release the authorization without capturing.
Refunds
Full refund:Merchant-initiated transactions (MIT)
For subscription renewals, retry logic, or any charge initiated without the customer present:Listing and retrieving payments
Webhooks
To learn about asynchronous events (3DS callbacks, settlement, disputes), set up webhooks at the TPA level:Errors you should handle
The SDK throws typed errors:
