Alternative Payment Methods & Local Rails
Cards are a global default and a poor local one. In the Netherlands most consumers reach for iDEAL, in Germany a direct debit from a bank account is the norm for recurring services, in the United States ACH is how businesses pay anything substantial, and across Asia wallets dominate. Adding these rails is rarely a checkout-only change: they settle asynchronously, fail days later, carry mandates instead of tokens, and reverse under rules that have nothing to do with card networks. This page sits under Frontend Checkout UX & Dunning Recovery Flows and covers what a subscription system has to change internally before a non-card rail is safe to switch on.
The mistake that causes the most damage is treating a bank debit like a slow card. A card authorisation succeeds or fails in seconds, and a decline is a decline. A SEPA Direct Debit “succeeds” immediately in the sense that the instruction is accepted, then can fail five business days later with an insufficient-funds return — long after your system provisioned the service, sent the receipt, and recognised the revenue. Every downstream assumption that “payment succeeded” means “money arrived” has to be re-examined.
Prerequisites
Non-card rails put pressure on the parts of a billing system that assume synchronous outcomes. The list below is the minimum needed before the first mandate is collected.
Classifying Rails by Latency and Reversibility
Before writing any integration code, place each rail on two axes: how long until the money is confirmed, and how long the customer can pull it back. Those two numbers determine everything else — how long to hold provisioning, when to recognise revenue, and how aggressive dunning can be.
| Rail | Confirmation latency | Reversal window | Recurring support | Typical use |
|---|---|---|---|---|
| Card | Seconds | 120+ days (chargeback) | Native, token-based | Global default |
| SEPA Direct Debit | 2–5 business days | 8 weeks no-questions, 13 months unauthorised | Mandate-based | EU recurring |
| ACH debit | 3–5 business days | 60 days consumer, 2 days business | Mandate-based | US B2B |
| BACS Direct Debit | 3 business days | Indemnity claim, effectively open | Mandate-based | UK recurring |
| iDEAL / Bancontact | Seconds to minutes | None (push payment) | Only via mandate hand-off | EU one-off, first charge |
| Wallets (Apple/Google Pay) | Seconds | Underlying card rules | Inherits card token | Mobile checkout |
| Card-on-file after wallet | Seconds | 120+ days | Native | Post-wallet recurring |
Two structural observations fall out of this table. First, push-based methods like iDEAL are excellent for the first payment and useless for the second — the customer authorises one transfer, not a standing instruction. The standard pattern is to use iDEAL for the initial charge while simultaneously collecting a SEPA mandate for subsequent cycles, which is a two-object checkout that surprises teams expecting a single payment method to appear.
Second, bank debits have reversal windows measured in weeks, not days. An eight-week SEPA return right for consumer debits means a customer can reclaim a payment nearly two months after you provisioned service. That is not a fraud problem to be prevented; it is a business risk to be priced and monitored, and it is why annual prepayment on direct debit deserves a different risk posture than monthly.
Mandates Are Not Tokens
A card token is a reference to an instrument. A mandate is a legal authorisation with a lifecycle: it is accepted at a moment in time, it has a unique reference the debtor’s bank can see, it can be revoked by the customer at their bank without telling you, and it lapses if unused for a defined period — 36 months for SEPA. Storing it as “just another payment method row” loses the fields you are required to be able to produce.
CREATE TABLE payment_mandates (
mandate_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
customer_id UUID NOT NULL,
rail TEXT NOT NULL, -- 'sepa_debit' | 'ach_debit' | 'bacs_debit'
provider_mandate_ref TEXT NOT NULL, -- the reference printed on the debtor's statement
scheme_reference TEXT, -- creditor identifier where the scheme requires one
status TEXT NOT NULL, -- 'pending' | 'active' | 'revoked' | 'expired'
accepted_at TIMESTAMPTZ NOT NULL,
acceptance_ip INET,
acceptance_text TEXT NOT NULL, -- the exact wording the customer agreed to
last_used_at TIMESTAMPTZ,
revoked_at TIMESTAMPTZ,
UNIQUE (rail, provider_mandate_ref)
);
acceptance_text is the field teams omit and later regret. When a customer disputes a direct debit as unauthorised, the scheme asks what they agreed to and when. A row containing the verbatim mandate wording, the timestamp, and the originating address is the evidence; a boolean accepted: true is not. This is the same evidentiary discipline described in automating chargeback dispute evidence submission, applied earlier in the lifecycle.
last_used_at earns its place because dormancy kills mandates. A SEPA mandate unused for 36 months lapses, and an annual subscriber who pauses for a year is closer to that boundary than anyone expects. A monthly job that flags mandates approaching dormancy — and re-collects before they lapse — costs an hour to write and prevents a failed renewal that looks, from the customer’s side, like the product broke.
Asynchronous Payment State
The single largest code change is admitting that a payment can be neither succeeded nor failed for several days. Systems built card-first tend to have a two-state model with an implicit assumption that the answer arrives inside the request. Non-card rails need three states and a clock.
The policy question the diagram raises is when to provision. Waiting for settlement means a new SEPA customer waits five days for access, which no product tolerates. Provisioning on processing means accepting return risk on the first payment. The pragmatic answer used by most subscription products is to provision immediately but treat the first debit as a risk decision: cap the amount, or require a card for the first cycle and switch to direct debit afterwards for higher-value plans.
// Node handler for the asynchronous outcome. Note the three terminal branches.
export async function handlePaymentEvent(event: PspEvent): Promise<void> {
const { paymentIntentId, subscriptionId, status, failureCode } = normalize(event);
switch (status) {
case "processing":
// Money is in flight. Provision, but mark the invoice as awaiting settlement.
await invoices.markProcessing(paymentIntentId);
await entitlements.grantProvisional(subscriptionId);
break;
case "succeeded":
await invoices.markPaid(paymentIntentId);
await entitlements.confirm(subscriptionId);
break;
case "returned": // debit reversed days later
case "failed":
await invoices.markUnpaid(paymentIntentId, failureCode);
// Do NOT revoke instantly — enter the rail's grace window instead.
await dunning.startCycle(subscriptionId, { rail: event.rail });
break;
}
}
The grantProvisional / confirm split is worth the extra concept. It lets you report on how much of your active base is running on unsettled money, which is a real risk number for a business with a large direct-debit share, and it gives the fraud team a lever that does not require blocking the rail entirely.
Grace Windows and Dunning by Rail
Card dunning assumes fast feedback: a decline arrives in seconds, so retrying in three days is meaningful. Bank debit dunning must assume the opposite. A retry issued before the original return has been reported will simply be rejected, or worse, succeed and then also be returned — two failures charged two return fees.
| Rail | First retry after | Retry attempts | Total window | Notes |
|---|---|---|---|---|
| Card | 3 days | 3–4 | 2–4 weeks | Decline code should drive timing |
| SEPA Direct Debit | 5 business days | 2 | 4–6 weeks | Each failed debit may carry a bank fee |
| ACH debit | 5 business days | 2 | 4–6 weeks | Return codes R01/R09 retryable, R02/R07/R10 are not |
| BACS | 4 business days | 2 | 4 weeks | Re-presentation rules are scheme-specific |
The retryability of a return code is the operational detail that separates a working implementation from an expensive one. ACH return code R01 (insufficient funds) is worth retrying after a few days because the balance may recover. R02 (account closed) and R10 (customer advises unauthorised) are permanent — retrying is not merely futile, it can constitute a scheme violation and attract penalties. Encode the mapping in one place and let dunning consult it, the same way smart retry timing with card issuer decline codes does for cards.
ACH_RETRYABLE = {"R01", "R09"} # insufficient funds, uncollected funds
ACH_TERMINAL = {"R02", "R03", "R04", "R07", "R08", "R10", "R29"}
def next_action(return_code: str, attempt: int) -> str:
if return_code in ACH_TERMINAL:
return "request_new_method" # never re-present
if return_code in ACH_RETRYABLE and attempt < 2:
return "retry_after_5_business_days"
return "escalate_to_dunning_email"
Routing Customers to the Right Rails
Presenting every rail to every customer is the fastest way to reduce conversion. A German customer shown eight options picks slower than one shown two familiar ones; a US customer shown iDEAL learns nothing except that the checkout was not built for them. Routing is a small function of country, currency, amount, and whether the charge is recurring.
Keep the routing table in configuration rather than code so that enabling a rail in a new market is a data change reviewed by whoever owns payments, not a deploy. Order matters as much as membership: the first method shown gets a disproportionate share of selections, so put the locally dominant rail first rather than defaulting to card everywhere out of habit. That single ordering decision is often worth more conversion than the integration itself, and it interacts directly with presenting localized prices — a familiar price format next to an unfamiliar payment method still reads as foreign.
What Changes Downstream of Checkout
Teams scope these projects as checkout work and then discover the integration is the smallest part. Four downstream systems need deliberate changes, and each one has a failure mode that only appears in production.
Invoicing. A card charge and its invoice settle together, so many systems treat “invoice paid” and “payment succeeded” as the same event. With a bank debit they are days apart, and an invoice PDF generated at payment initiation will say “paid” for a payment that has not settled. Split the states: an invoice is open, then payment_pending, then paid, and only the last one produces a receipt. Customers in regulated markets frequently need a document that reflects actual settlement, and issuing one early creates a correction you have to chase later with a credit note.
Revenue recognition. Cash-basis reporting on unsettled debits overstates every period in which direct-debit volume is growing, because the returns land in the following period. The correction is mechanical — recognise on settlement, not initiation — but it must be decided before the rail is switched on, since restating a quarter is considerably more expensive than getting the trigger right the first time.
Support tooling. Agents need to see mandate status, the reason a debit was returned, and whether the return is retryable, in language that means something to a customer. “R10” and “MD01” are not answers. Map every scheme code to a plain-language explanation and a suggested next action, and put both in the support view; otherwise every bank-debit failure escalates to engineering.
Fraud and risk. Card fraud is instrument theft; bank-debit abuse is usually a first-party dispute — a customer claiming they never authorised a debit they did in fact authorise. The defence is the mandate evidence discussed above rather than a fraud score, which means the risk controls that work well for cards contribute almost nothing here. Monitor return rates by cohort instead, because scheme operators impose limits: sustained return rates above roughly 0.5% for unauthorised returns attract scrutiny and eventually the loss of the rail.
There is also a quieter operational change: reporting. Bank debits settle in batches with their own timing and their own fee structure, so payout reconciliation now has two shapes to normalise rather than one. Fold both into your own ledger with a common vocabulary as early as possible, following the approach in reconciling payouts against your ledger, rather than letting each rail’s quirks leak into month-end close.
Choosing Which Rails to Add First
Adding rails is not free — each one carries integration effort, a support burden, a reconciliation surface, and an ongoing failure mode. The sequencing question is which ones repay that cost, and the answer is almost always determined by where your customers already are rather than by which rail is technically most interesting.
Start with the data you already have. Group failed and abandoned checkouts by country, and compare that with your successful conversion mix. A market where traffic is high, checkout starts are high, and completion is low is a market where the payment method set is wrong — and the specific missing rail is usually the one that market considers default. That analysis costs an afternoon and is far more reliable than a general ranking of payment methods by global volume.
Weight the result by recurring suitability. A rail that cannot support the second payment adds conversion at acquisition and nothing at renewal, which means its value is capped at the first invoice unless you pair it with a mandate. For a subscription business the pairing matters more than the rail: iDEAL plus a SEPA mandate is a complete solution, while iDEAL alone converts a customer you then have to re-engage every month.
Finally, weight by operational readiness. A rail with a multi-day return window demands the asynchronous state machine, the widened grace window, and the support tooling described above. If those are not in place, adding the rail converts an integration project into an incident. The correct order is: build the asynchronous foundations once, then add rails, which is why this page treats the foundations as prerequisites rather than as part of each integration.
One more sequencing note: switch a rail on for new customers before offering it to existing ones. New customers exercise the happy path first and give you real settlement and return data on a small base. Migrating an existing card cohort onto direct debit is a larger change — it involves collecting mandates from people who already pay you successfully — and it is worth doing only once the rail’s failure behaviour is understood on your own traffic rather than from documentation.
Testing Strategy
Non-card rails are hard to test precisely because their interesting behaviour happens days later. Providers supply test account numbers that deterministically produce a return after settlement; wire those into an integration suite that fast-forwards through the provider’s test clock rather than waiting. Assert the full sequence: processing provisions, succeeded confirms, and a late returned moves the subscription into the rail-appropriate grace window without revoking data access.
Then test the ordering hazards. Deliver succeeded and returned out of order and assert the final state is returned, because event ordering across a multi-day window is not guaranteed. Deliver a duplicate processing and assert entitlements are not granted twice. Simulate a mandate revoked at the bank — where the first signal you receive is a debit failure with a mandate-invalid code — and assert the customer is asked for a new method rather than being retried into the same dead mandate.
Finally, snapshot the checkout method set for a handful of representative countries and assert both membership and order. A regression that quietly moves card above iDEAL for Dutch customers costs conversion and would otherwise be invisible until a monthly report moved.
Frequently Asked Questions
Can I use one payment intent for iDEAL and the following SEPA cycles? No. The iDEAL payment authorises a single transfer; the recurring debits need a mandate collected alongside it. Most providers expose this as a single checkout session that produces both a completed payment and a reusable mandate, but they remain two objects in your data model.
How long should I wait before treating a direct debit as settled? Use the scheme’s settlement window plus a margin — five business days for SEPA and ACH is the common choice — and let the provider’s succeeded event be authoritative rather than a timer. Timers are a fallback for missing events, not the primary signal.
Do bank debits reduce involuntary churn? Usually yes, substantially, because there is no expiry date and no issuer decline. They introduce a different failure mode — insufficient funds and revoked mandates — but the overall recovery picture is typically better than cards for recurring consumer and B2B billing in the markets where they are standard.
Should revenue be recognised on processing or succeeded? On settlement. Recognising unsettled bank debits inflates revenue by exactly the amount that will later be returned, and the correction lands in a subsequent period. Provision the service early if you choose; recognise the money late.
What about wallets — are they a separate rail? Operationally they are a presentation layer over a card in most cases, so the recurring behaviour follows card rules once the token is stored. Treat them as a conversion feature on checkout rather than a new settlement rail, and make sure the stored credential is usable for off-session charges before relying on it for renewals.
Related
- Frontend Checkout UX & Dunning Recovery Flows
- Payment Element Integration
- Multi-Currency Checkout & Localization
- Grace Period & Retry Logic
- Secure Card Vaulting & Tokenization
- Fraud Prevention & Dispute Management
- Adding SEPA Direct Debit to a Subscription Product
- Handling ACH Return Codes and Retry Eligibility
- Supporting iDEAL and Delayed-Notification Payment Methods