Billwave
Get Started

Providers & accounts

How Billwave abstracts payment providers

Providers & accounts

Billwave is designed to work with multiple payment providers through a single interface. This page explains the abstraction layer and how provider differences are handled.

Supported providers

Different payment providers serve different markets and have different strengths. Paystack dominates in Nigeria and West Africa. Stripe is the global standard for online payments. Dodo Payments and Polar handle global billing flows. Billwave abstracts the differences so your application code stays the same regardless of which provider you choose.

Your app calls attach(), check(), and track(). Billwave figures out which provider to use, translates the request into the provider's API format, and normalizes the webhooks back into a common event model.

The adapter model

Each supported provider has an adapter — an implementation of a shared interface that handles:

  • Checkout sessions — creating a payment page for the customer
  • Subscriptions — creating, fetching, and canceling recurring billing
  • Webhook verification — validating that incoming events are genuine
  • Event normalization — translating provider-specific events into Billwave's internal format
  • Refunds — reversing charges (e.g. for card setup verification)
  • Products/plans — creating the provider-side representation of your plans

The adapter interface is designed so that core billing logic (plan switching, trial management, overage billing) works identically across providers. Provider-specific quirks are isolated inside the adapter.

Currently Supported providers

ProviderIDMarketsKey differences
PaystackpaystackNigeria, Ghana, South Africa, KenyaCard authorization codes for recurring charges; webhook verification via HMAC-SHA512
StripestripeGlobalIndustry-standard checkout flows; native subscription management with Stripe Billing; comprehensive webhook events
Dodo PaymentsdodopaymentsGlobal (USD, EUR, GBP)Product-centric checkout (requires product ID); on-demand subscriptions for card-on-file; Standard Webhooks spec
PolarpolarGlobalProduct-centric checkout (supports multiple products); native subscription lifecycle webhooks; Standard Webhooks signature model
BachsbachsAfrica-first (NGN, GHS, KES, USD)Cards, mobile money, bank transfer and stablecoins in one checkout; native trials and proration; no off-session charge API; subscriptions are USD-only today

Provider accounts

A provider account stores the credentials for a specific provider within your organization:

  • Secret key — the provider's API key (encrypted at rest)
  • Webhook secret — used to verify incoming webhook payloads
  • Environment — test or live

Each organization can configure one or more provider accounts per environment.

Sandbox accounts are managed by Billwave

In the sandbox you don't need any of the above. Billwave keeps a shared test account on each supported provider and your organization uses them automatically — they show up as Managed by Billwave in Settings → Providers. A sandbox account you add yourself for a provider overrides the managed one for that provider.

Live always uses your own accounts. See Environments.

How providers are selected

When a customer starts a checkout, Billwave picks a provider using this priority:

  1. Explicit provider parameter — if you pass provider: "dodopayments" to attach()
  2. Plan's provider — the provider selected when the plan was created
  3. Customer's existing provider — if they've paid before, use the same provider
  4. Fallback — the first configured provider account (in the sandbox this includes the managed accounts)

Billwave routes requests based on the plan's provider, explicit provider parameter, or customer history.

Provider-specific behavior

Some billing features work differently depending on the provider:

Trials:

  • Paystack — small verification charge (auto-refunded), Billwave manages trial billing
  • Stripe — native trial support via Stripe Billing subscription schedules
  • Dodo — native trial period, provider defers first charge automatically
  • Polar — native trial support via provider-side subscription lifecycle
  • Bachs — native trial period configured on the recurring product; Bachs saves the card at checkout and bills when the trial ends

Card on file:

  • Paystack — stores a reusable authorization code from the first charge
  • Stripe — stores customer payment methods; supports off-session payments with SetupIntents
  • Dodo — creates an on-demand subscription that can be charged with arbitrary amounts
  • Polar — stores provider-managed subscription IDs for recurring lifecycle events
  • Bachs — Bachs keeps the card for renewals but exposes no off-session charge API, so Billwave never auto-charges a Bachs customer. Overage and invoice payments fall back to a hosted checkout link (POST /billing/invoice/{id}/pay returns checkoutUrl)

Checkout:

  • Paystack — raw amount checkout, no pre-created product needed
  • Stripe — flexible checkout with PaymentIntents; supports one-time and subscription payments
  • Dodo — product-centric, requires a synced product ID for every checkout
  • Polar — product-centric checkout with support for single- or multi-product sessions
  • Bachs — product-centric for plans and credit packs; supports product-less raw-amount checkouts for invoices. Amounts are decimal strings on the wire, converted from Billwave minor units by the adapter

Plan upgrades:

  • Paystack — no native plan change API. Billwave uses a one-time prorated checkout when an immediate difference is due, then keeps recurring renewal aligned to the next full cycle. If the remaining difference is 0 or below Paystack's transaction minimum, Billwave switches immediately without charging the tiny remainder.
  • Stripe — native subscription updates. Billwave applies upgrades with immediate invoicing so proration is billed now instead of being left on a later invoice.
  • Dodo — native change-plan flow with provider-managed proration. If Dodo rejects the change, Billwave fails the upgrade instead of falling back to an invalid raw-amount checkout.
  • Polar — native subscription updates. Billwave uses the provider's immediate invoice mode for paid upgrades.
  • Bachs — native subscription updates with proration_behavior: invoice_now. Bachs requires the target product to bill at the same interval; if it rejects the change Billwave falls back to its non-native path. A plan interval change on Bachs creates a replacement product (billing cycles are immutable) and archives the old one.

These differences are handled internally. Your application code is the same regardless of provider.

Adding a new provider

Billwave's adapter interface is designed to be extended. At a high level:

  1. Implement ProviderAdapter in packages/adapters
  2. Register it in the provider registry
  3. Add credential fields to the dashboard
  4. Configure a provider account (and, for the sandbox, add Billwave's test credentials for it to the managed set)

Once registered, all billing flows (checkout, plan switching, webhooks, overage billing) work through the new adapter automatically.

On this page

Ask about billwave

Ready

Start a new chat below.

Powered by Cull