Multi-PSP Routing & Vault
Time : ~10 minutes | Difficulty : Intermediate
The Problem
You’re locked into one payment processor. If it declines, you lose the sale:
Customer → Stripe → decline → lost sale
With TagadaPay, you connect all your processors and route intelligently:
Customer → TagadaPay → Stripe (40% traffic)
→ Checkout.com (30% traffic)
→ Airwallex (30% traffic)
→ NMI (fallback if all fail)
Card data is vaulted once . Transactions flow to the best processor automatically. If one declines, the next one picks up — zero extra code on your side .
TagadaPay sits above every PSP. It’s not a Stripe plugin or a Checkout.com add-on. You connect any combination of Stripe, NMI, Checkout.com, Airwallex, Adyen, and more — then define routing rules. Each processor is just a connection with API keys.
How It Works
┌─────────────────────────────────────────────────────────────┐
│ Your Checkout │
│ (Plugin SDK, headless, or any frontend) │
│ ↓ │
│ Card tokenized │
│ (PCI-compliant vault, one time) │
│ ↓ │
│ ┌──────── Payment Flow ─────────┐ │
│ │ strategy: cascade │ │
│ │ 40% → Stripe │ │
│ │ 30% → Checkout.com │ ← sticky: reuse │
│ │ 30% → Airwallex │ last successful │
│ │ fallback #1 → NMI │ processor for │
│ │ fallback #2 → Stripe (retry) │ returning │
│ └───────────────────────────────┘ customers │
│ ↓ │
│ Transaction OK │
└─────────────────────────────────────────────────────────────┘
Three concepts to understand:
Concept What it is Processor A PSP connection (Stripe, NMI, Checkout.com, Airwallex, etc.) with its own API keys Payment Flow A routing rule: which processors to use, traffic weights, fallback order Vault Card data is tokenized once and reusable across all processors — you never see raw card numbers
Step 1: Connect Your Processors
Add each processor in the TagadaPay dashboard → Settings → Processors. Each one just needs its API keys:
Processor What you provide Stripe Secret key + Publishable key NMI API key (or username/password) Checkout.com Secret key + Public key + Processing Channel ID Airwallex Client ID + API Key
After connecting, list them via the SDK to get their IDs:
import Tagada from '@tagadapay/node-sdk' ;
const tagada = new Tagada ( 'your-api-key' );
const { processors } = await tagada . processors . list ();
console . log ( processors );
// [
// { id: 'proc_s1', name: 'Stripe', type: 'stripe', enabled: true },
// { id: 'proc_nmi', name: 'NMI', type: 'nmi', enabled: true },
// { id: 'proc_cko', name: 'Checkout.com', type: 'checkout', enabled: true },
// { id: 'proc_aw', name: 'Airwallex', type: 'airwallex', enabled: true },
// ]
You can connect unlimited processor accounts — even multiple Stripe accounts (US, EU, etc.). TagadaPay is fully gateway-agnostic.
Step 2: Create a Payment Flow
A payment flow defines how transactions are routed across your 4 processors:
const flow = await tagada . paymentFlows . create ({
data: {
name: 'Global 4-PSP Cascade' ,
strategy: 'cascade' ,
fallbackMode: true ,
maxFallbackRetries: 2 ,
threeDsEnabled: true ,
stickyProcessorEnabled: true ,
pickProcessorStrategy: 'weighted' ,
processorConfigs: [
{ processorId: 'proc_s1' , weight: 40 }, // Stripe — 40%
{ processorId: 'proc_cko' , weight: 30 }, // Checkout.com — 30%
{ processorId: 'proc_aw' , weight: 30 }, // Airwallex — 30%
],
fallbackProcessorConfigs: [
{ processorId: 'proc_nmi' , orderIndex: 0 }, // NMI — 1st fallback
{ processorId: 'proc_s1' , orderIndex: 1 }, // Stripe — 2nd fallback (retry)
],
},
});
console . log ( flow . id ); // 'flow_xyz'
Here’s what each field does:
Field What it does strategy: 'cascade'Use multiple processors (vs 'simple' for one) fallbackMode: trueIf the primary processor declines, try the next one maxFallbackRetries: 2Try up to 2 fallback processors before giving up pickProcessorStrategy: 'weighted'Distribute traffic by weight (40/30/30 split) stickyProcessorEnabled: trueReturning customers reuse their last successful processor threeDsEnabled: trueEnable 3D Secure when required by the card issuer processorConfigsPrimary processors with traffic weights fallbackProcessorConfigsBackup processors tried in orderIndex order on decline
Yes, a processor can appear in both lists. Stripe is the primary at 40% and the 2nd fallback. If Checkout.com or Airwallex declines, NMI tries first, then Stripe retries — sometimes a retry on the same PSP with a fresh attempt succeeds.
Step 3: Vault a Card (Tokenize Once)
Card data is tokenized client-side via @tagadapay/core-js. The raw card number never reaches your server.
// Client-side (browser)
import { useCardTokenization } from '@tagadapay/core-js/react' ;
const { tokenizeCard } = useCardTokenization ({ environment: 'production' });
const { tagadaToken } = await tokenizeCard ({
cardNumber: '4242424242424242' ,
expiryDate: '12/28' ,
cvc: '123' ,
cardholderName: 'Jane Doe' ,
});
// Send tagadaToken to your server →
Then, server-side , create a reusable payment instrument:
// Server-side (Node.js)
const { paymentInstrument , customer } = await tagada . paymentInstruments . createFromToken ({
tagadaToken: tagadaToken , // base64 string from client
storeId: 'store_xxx' ,
customerData: {
email: 'jane@example.com' ,
firstName: 'Jane' ,
lastName: 'Doe' ,
},
});
console . log ( paymentInstrument . id ); // 'pi_abc123'
// This instrument is now vaulted — reusable across ALL processors
One vault, all processors. The payment instrument is PSP-agnostic. TagadaPay translates it to the right format for whichever processor the flow selects — Stripe, NMI, Checkout.com, Airwallex, or any other. You tokenize once, charge anywhere.
Step 4: Charge — TagadaPay Routes Automatically
const { payment } = await tagada . payments . process ({
amount: 4999 , // $49.99 in cents
currency: 'USD' ,
storeId: 'store_xxx' ,
paymentInstrumentId: paymentInstrument . id ,
paymentFlowId: flow . id , // uses the 4-PSP cascade flow
initiatedBy: 'customer' ,
mode: 'purchase' ,
});
console . log ( payment . status ); // 'captured'
console . log ( payment . processorId ); // 'proc_cko' (Checkout.com won the route)
What happened under the hood:
TagadaPay’s weighted algorithm picked Checkout.com (30% weight, this time)
If Checkout.com declined → automatically retried on NMI (fallback #1)
If NMI also declined → retried on Stripe (fallback #2)
The winning processor is recorded in payment.processorId
You wrote zero retry logic. Zero PSP-specific code. One API call for 4 processors.
Routing Strategies Explained
Weighted Distribution
Split traffic by percentage. Great for load balancing or gradually migrating to a new processor.
processorConfigs : [
{ processorId: 'proc_s1' , weight: 40 }, // Stripe — 40%
{ processorId: 'proc_cko' , weight: 30 }, // Checkout.com — 30%
{ processorId: 'proc_aw' , weight: 30 }, // Airwallex — 30%
]
Lowest Capacity
Sends traffic to whichever processor has processed the fewest transactions today. Auto-balances across all 4 PSPs.
pickProcessorStrategy : 'lowestCapacity' ,
processorConfigs : [
{ processorId: 'proc_s1' }, // Stripe
{ processorId: 'proc_cko' }, // Checkout.com
{ processorId: 'proc_aw' }, // Airwallex
{ processorId: 'proc_nmi' }, // NMI
]
Automatic (Round Robin)
Equal distribution — each processor gets roughly the same number of transactions.
pickProcessorStrategy : 'automatic' ,
Sticky Processor
When enabled, a returning customer is always routed to the processor that last succeeded for them. Reduces declines on stored-credential transactions.
stickyProcessorEnabled : true ,
Multiple Flows for Different Scenarios
You can create different flows for different situations. Example: a standard flow and a high-value flow using different processor mixes:
// Standard flow: spread across Stripe + Checkout.com, NMI as fallback
const standardFlow = await tagada . paymentFlows . create ({
data: {
name: 'Standard' ,
strategy: 'cascade' ,
fallbackMode: true ,
maxFallbackRetries: 1 ,
threeDsEnabled: true ,
stickyProcessorEnabled: true ,
pickProcessorStrategy: 'weighted' ,
processorConfigs: [
{ processorId: 'proc_s1' , weight: 50 }, // Stripe
{ processorId: 'proc_cko' , weight: 50 }, // Checkout.com
],
fallbackProcessorConfigs: [
{ processorId: 'proc_nmi' , orderIndex: 0 },
],
},
});
// High-value flow: Airwallex primary (best rates for large amounts),
// all others as fallbacks
const highValueFlow = await tagada . paymentFlows . create ({
data: {
name: 'High Value (>$500)' ,
strategy: 'cascade' ,
fallbackMode: true ,
maxFallbackRetries: 3 ,
threeDsEnabled: true ,
stickyProcessorEnabled: false ,
pickProcessorStrategy: 'weighted' ,
processorConfigs: [
{ processorId: 'proc_aw' , weight: 100 }, // Airwallex — all traffic
],
fallbackProcessorConfigs: [
{ processorId: 'proc_s1' , orderIndex: 0 }, // Stripe — 1st fallback
{ processorId: 'proc_cko' , orderIndex: 1 }, // Checkout.com — 2nd
{ processorId: 'proc_nmi' , orderIndex: 2 }, // NMI — last resort
],
},
});
// Route based on order amount
const flowId = orderTotal > 50000
? highValueFlow . id
: standardFlow . id ;
await tagada . payments . process ({
amount: orderTotal ,
currency: 'USD' ,
storeId: 'store_xxx' ,
paymentInstrumentId: 'pi_abc123' ,
paymentFlowId: flowId ,
initiatedBy: 'customer' ,
mode: 'purchase' ,
});
You can also override per funnel step — see the Step Config Guide .
Recurring Charges (MIT)
For subscriptions and server-initiated charges, use initiatedBy: 'merchant'. The vaulted instrument works the same way — TagadaPay handles the stored-credential handshake with each PSP.
await tagada . payments . process ({
amount: 2999 ,
currency: 'USD' ,
storeId: 'store_xxx' ,
paymentInstrumentId: 'pi_abc123' ,
paymentFlowId: flow . id ,
initiatedBy: 'merchant' ,
reasonType: 'recurring' ,
mode: 'purchase' ,
});
No customer present, no 3DS challenge, same routing intelligence.
Comparison: Single PSP vs TagadaPay
Single PSP (e.g. Stripe alone) TagadaPay (Stripe + NMI + Checkout.com + Airwallex) Processors 1 Unlimited, any combination Add a new PSP New integration, new code Dashboard → add API keys → done Fallback on decline You build it Automatic cascade Traffic splitting You build it weight: 40 / weight: 30 / weight: 30Sticky routing You build it stickyProcessorEnabled: trueCard vault PSP-specific tokens PSP-agnostic, works across all processors 3DS Per-PSP implementation Unified, automatic Lines of code Hundreds (per PSP) ~20 (for all PSPs combined)
Full Example: 4 PSPs, End to End
import Tagada from '@tagadapay/node-sdk' ;
const tagada = new Tagada ( 'your-api-key' );
// 1. List all connected processors
const { processors } = await tagada . processors . list ();
const stripe = processors . find ( p => p . type === 'stripe' ) ! ;
const nmi = processors . find ( p => p . type === 'nmi' ) ! ;
const checkout = processors . find ( p => p . type === 'checkout' ) ! ;
const airwallex = processors . find ( p => p . type === 'airwallex' ) ! ;
// 2. Create a 4-PSP cascade flow
const flow = await tagada . paymentFlows . create ({
data: {
name: '4-PSP Global Cascade' ,
strategy: 'cascade' ,
fallbackMode: true ,
maxFallbackRetries: 2 ,
threeDsEnabled: true ,
stickyProcessorEnabled: true ,
pickProcessorStrategy: 'weighted' ,
processorConfigs: [
{ processorId: stripe . id , weight: 40 }, // Stripe — 40%
{ processorId: checkout . id , weight: 30 }, // Checkout.com — 30%
{ processorId: airwallex . id , weight: 30 }, // Airwallex — 30%
],
fallbackProcessorConfigs: [
{ processorId: nmi . id , orderIndex: 0 }, // NMI — 1st fallback
{ processorId: stripe . id , orderIndex: 1 }, // Stripe — 2nd fallback
],
},
});
// 3. Vault a card (after client-side tokenization via @tagadapay/core-js)
const { paymentInstrument } = await tagada . paymentInstruments . createFromToken ({
tagadaToken: 'eyJ0eXBlIjoiY2FyZC...' , // base64 from client
storeId: 'store_xxx' ,
customerData: { email: 'jane@example.com' , firstName: 'Jane' },
});
// 4. Charge — TagadaPay picks the best processor from the flow
const { payment } = await tagada . payments . process ({
amount: 4999 ,
currency: 'USD' ,
storeId: 'store_xxx' ,
paymentInstrumentId: paymentInstrument . id ,
paymentFlowId: flow . id ,
initiatedBy: 'customer' ,
mode: 'purchase' ,
});
console . log ( `Payment ${ payment . status } via ${ payment . processorId } ` );
// → "Payment captured via proc_cko" (Checkout.com won this round)
Next Steps
Subscriptions & Rebilling Trials, auto-retry, manual rebill, processor migration for recurring billing
Node SDK Quick Start Full SDK setup with stores, products, and funnels
Headless Payments (Frontend) Build your own checkout UI with client-side card tokenization and 3DS
Scripts & Pixels per Step Add tracking pixels and custom scripts to funnel steps