← Blog
Architecture·July 24, 2026·2 min read

Payment-rail-agnostic billing: one interface, any provider

Why binding your billing logic to a single payment provider is a trap — and how one adapter interface lets you add Stripe, crypto, or in-app purchases without touching your core.

By INITE Billing

Most billing code rots the same way: it starts as a thin wrapper around one provider's SDK, and six months later that provider's concepts — its webhook shapes, its idempotency rules, its refund semantics — have leaked into every corner of the app. Swapping providers, or adding a second one, becomes a rewrite.

The adapter boundary

The fix is an old one: put a boundary between what a payment is and how it was taken. In INITE Billing every provider implements one interface:

export interface PaymentRailAdapter {
  rail(): string
  createPaymentIntent(input: CreateIntentInput): Promise<CreateIntentResult>
  getIntentStatus(providerIntentId: string): Promise<IntentStatusResult>
}

Stripe, native multi-chain crypto (ETH/SOL/TON/TRON), Apple IAP, Google Play, Lava, and ONE each satisfy it. The payment orchestrator — the state machine that drives orders, subscriptions, entitlements and credits — only ever sees a PaymentIntent. It does not know or care which rail paid.

What you get for free

Because the core is provider-blind, everything downstream is written once:

ConcernHandled by
Webhook verificationThe webhook processor, per adapter
Idempotent retriesThe orchestrator, on every state transition
Subscriptions & creditsThe core, independent of rail
Referrals & payoutsThe affiliate engine, independent of rail

Adding a rail is: implement the interface, register it, add a webhook endpoint if the provider pushes events. Nothing else moves.

The test that matters

If adding a payment provider forces you to touch subscription logic, the boundary is wrong. With an adapter interface, the diff for a new rail is a new file — not a migration across your codebase.

FAQ

What is a payment rail?

A payment rail is the channel that actually moves money — Stripe cards, an on-chain crypto transfer, Apple/Google in-app purchase, or a regional processor. INITE Billing supports six behind one adapter interface.

Do I have to change code to add a new provider?

No. A new provider implements the adapter interface and registers itself. Subscriptions, credits, entitlements, and referrals are untouched — that is the whole point of the boundary.

Related