Payment Platform
A payment platform is the infrastructure that lets a company move money on behalf of itself or its users. It sits above payment processors and below product surfaces, orchestrating ledgers, payouts, KYC, fraud, and reporting into one system.
What a payment platform is
A payment platform is the layer that lets a business — a fintech, a marketplace, a SaaS — move money. It sits above raw payment processors (Stripe, Adyen, Marqeta, Modern Treasury) and below product surfaces (the checkout, the wallet, the payouts dashboard). Its job is to orchestrate the moving parts: charging cards, holding balances, releasing payouts, recording entries on a double-entry ledger, running fraud and AML checks, and producing reporting that finance can trust.
A pure payment processor handles one card-network round-trip per request. A payment platform manages the whole lifecycle: hold, settle, payout, refund, dispute, reconcile.
Payment platform versus payment processor
Easy distinction:
- Processor: a vendor that talks to card networks or bank rails on your behalf. Stripe, Adyen, Worldpay, Adyen for Platforms, Marqeta.
- Platform: the system you build (or buy) that uses processors as backends, carries an internal ledger, owns the customer record, and exposes a unified API to your product surfaces.
A marketplace running on Stripe Connect is a payment platform that uses Stripe as a processor. A neobank running on Marqeta + Modern Treasury + Plaid is a payment platform that orchestrates all three. The platform layer is where the company’s product, compliance, and operational logic lives.
What goes into a production payment platform
A typical production payment platform has these pieces:
- Ledger service. Append-only, double-entry, idempotent on writes. The source of truth for balances.
- Orchestration layer. Routes payment requests to the right processor based on geography, currency, BIN, or risk score. Handles retries, fallbacks, and processor failover.
- Payouts engine. Aggregates settlements, runs payout schedules, handles holds for chargebacks and disputes.
- KYC and KYB. Onboarding flows for individuals (KYC) and businesses (KYB).
- Fraud and AML. AML screening, transaction monitoring, sanctions checks.
- Reporting and reconciliation. Daily processor settlement comparison against the internal ledger.
- Webhook fan-out. Outbound events to product code, finance, and partner integrations.
Each of these is a service worth getting right on its own. The studio’s fintech development practice ships them as a coherent platform rather than a stack of disconnected vendor integrations.
Architectural patterns
Two patterns dominate:
- Single-processor with platform abstractions. Start with Stripe (or Adyen, or Marqeta) and a thin orchestration layer that has the option to swap processors per route. Faster to ship.
- Multi-processor from day one. Required when geography, regulatory diversity, or BIN-routing economics demand it. Heavier upfront, more flexibility.
Most fintechs start with the first pattern and graduate to the second around Series B. The most expensive mistake is to start without any platform abstraction at all — once Stripe SDK calls are scattered across the codebase, swapping is a multi-quarter project.
Code-shape example
The orchestration boundary that makes multi-processor routing possible:
interface PaymentRequest {
idempotencyKey: string;
amountMinor: number;
currency: string;
customerId: string;
description: string;
countryCode: string;
metadata: Record<string, string>;
}
interface PaymentResult {
id: string;
processor: "stripe" | "adyen" | "marqeta";
processorRef: string;
status: "succeeded" | "failed" | "requires_action";
amountMinor: number;
currency: string;
}
// Adapter per processor, common interface.
interface ProcessorAdapter {
charge(req: PaymentRequest): Promise<PaymentResult>;
refund(processorRef: string, amountMinor: number): Promise<PaymentResult>;
}
// Routing rules: pick a processor based on country, currency, BIN.
function pickProcessor(req: PaymentRequest): ProcessorAdapter {
if (req.countryCode === "US") return adapters.stripe;
if (["EU", "GB", "DE", "FR"].includes(req.countryCode)) return adapters.adyen;
return adapters.stripe;
}
export async function charge(req: PaymentRequest): Promise<PaymentResult> {
const processor = pickProcessor(req);
const result = await processor.charge(req);
// Always write to the internal ledger, regardless of processor.
await ledger.post({
idempotencyKey: req.idempotencyKey,
description: req.description,
occurredAt: new Date().toISOString(),
entries: [
{
account: `processor-clearing-${result.processor}`,
amountMinor: -result.amountMinor,
currency: result.currency,
},
{
account: `customer-${req.customerId}`,
amountMinor: +result.amountMinor,
currency: result.currency,
},
],
});
return result;
}
The ledger is the source of truth across processors. Swapping a processor or adding a new one becomes adapter work, not a rewrite of every call site.
How we implement it at Dashhold
Patterns shipped on every payment-platform engagement:
- One ledger, many processors. The orchestration layer is ours; processors are adapters.
- Processor failover, not just retries. When Stripe has an outage, the platform routes to Adyen (or whichever fallback is configured). Idempotency keys prevent double-charges across processors.
- Reconciliation as a scheduled job. Daily job pulls processor settlement files, compares against ledger totals per
processor-clearing-*account, and opens a ticket on mismatch. Audit-ready. - Webhook handlers as idempotent functions. Each processor’s webhook delivery is at-least-once. The handler keys on the processor’s event id, runs the ledger write inside an idempotency check.
- Per-jurisdiction policy modules. Geography, currency, and product-specific rules live in modules. Adding India, Brazil, or the EU is a new module + a routing rule, not a global refactor.
Common pitfalls
- No abstraction over the processor. Stripe SDK calls scattered across the codebase. Swapping or adding a processor becomes a multi-quarter project.
- Skipping the internal ledger. Letting the processor be the source of truth feels expedient; reconciling 18 months later is a multi-week project. Always write to your own ledger.
- Webhook handlers without idempotency. Processor retries cause double-writes. Every handler is keyed on the event id.
- Reconciliation as a quarterly thing. Daily reconciliation catches drift in 24 hours. Quarterly catches it after the customer notices.
- Fraud rules baked into processor adapters. Fraud should run at the platform layer, not inside processor code, because rules apply across processors.
- Hard-coded payout schedules. Per-tenant payout schedules are nearly always demanded eventually. Build a schedule abstraction from day one.
Where payment platforms sit
Payment platforms are the load-bearing infrastructure of every fintech and marketplace. They are also where compliance, reliability, and revenue all converge — every minute of downtime, every failed reconciliation, every regulator inquiry routes through this layer. The studio’s fintech development team builds payment platforms as the central engagement, with KYC, ledger, and payouts as the load-bearing components.
See also
- Double-entry ledger — the data model under the platform
- Idempotency key — the retry-safety primitive
- KYC onboarding — the gateway customers pass through
- Stripe vs Adyen for Platforms — comparison guide for the load-bearing processor choice