DEV Community

Cover image for Push vs. Pull — A Developer's Field Guide to Payment Rails
harry
harry

Posted on • Edited on

Push vs. Pull — A Developer's Field Guide to Payment Rails

If you've only ever integrated card payments, there's a whole category of payment rails you're probably underusing: bank-to-bank transfers. Cards are a push model, the customer authorizes each charge in the moment. Direct Debit and open banking payments work completely differently, and picking the wrong one for your use case creates real engineering headaches down the line.

The rails, briefly

Bacs (UK) and SEPA Direct Debit (EU) are pull-based, and Bacs specifically is the rail most of the UK startup ecosystem defaults to for subscription billing. Once a customer signs a mandate, you can initiate collections on your own schedule without them re-authorizing each time.

ACH in the US works similarly but with its own quirks around same-day windows and return codes that differ meaningfully from Bacs' processing cycle.

Open banking payment initiation (PIS), running over rails like Faster Payments or SEPA Instant, flips the model. It's push-based like cards, but instead of card networks, the customer authorizes a transfer directly from their bank in real time. Settlement is near-instant, but you can't use it for unattended recurring billing the way you can Direct Debit, unless you're using variable recurring payments (VRP), which is still inconsistently supported across banks.

Why this matters for your architecture

If you're building recurring billing, the mandate lifecycle is the part people underestimate. A mandate can be active, pending, cancelled, or expired, and your system needs to handle a customer revoking it directly with their bank, not just through your app. That means webhook-driven mandate status updates aren't optional, they're core to not double-charging or silently failing to collect.

Settlement timing also changes your data model. With cards, you generally know success or failure within seconds. With Direct Debit, a payment can appear "submitted" for days before it either clears or bounces, and it can still be reversed afterward via indemnity claims. If your system treats "submitted" as "paid," you will eventually ship a bug that lets someone use a service they never actually paid for.

Picking the right rail for the job

*A rough mental model that holds up in practice:
*

Predictable recurring billing where the amount is known in advance → Direct Debit (Bacs/SEPA/ACH)
One-off payments where speed matters and instant confirmation is required → open banking PIS / Pay by Bank
Variable, irregular recurring charges → VRP where supported, otherwise a hybrid of card-on-file plus fallback

Most serious payment platforms (Stripe, GoCardless, and others) now support multiple rails behind one API, which is generally the right call unless you have a very narrow use case, since it lets you route by cost, speed, and reliability rather than betting everything on one scheme.
It's a pattern worth watching if you follow UK tech news, since more of the payment infrastructure being built out of London is converging on exactly this multi-rail approach rather than picking one scheme and locking in.

Top comments (2)

Collapse
 
mihirkanzariya profile image
Mihir kanzariya

The mandate case that hurts more is the one where the status never changes at all. Under the Bacs Direct Debit Guarantee a payer can ask their bank to reverse a collection they say was wrong, and that reaches you as an indemnity claim rather than any mandate state change, with no meaningful time limit on how far back they can go. So the mandate keeps reading active while collections behind it get pulled back.

Practical version: mandate state and collection outcome are two separate state machines, and it is easy to write code that quietly treats "mandate is active" as "collections are succeeding". Tie entitlements to the collection outcome instead, or a customer keeps access while the money keeps going back. This is the UK Direct Debit case specifically, SEPA and ACH have their own return and dispute rules that don't map onto it.

Collapse
 
harry_williams profile image
harry

This is a really important distinction and honestly should've been in the original piece. You're right that indemnity claims are the sneaky case, mandate state says "active," but the money's already been clawed back, and nothing in the mandate lifecycle reflects that.

The "two separate state machines" framing is exactly right, and I like tying entitlement to collection outcome rather than mandate status. Much safer default than assuming active mandate implies successful collection.

Good callout too on this being UK-specific. Worth anyone building multi-rail systems remembering SEPA and ACH don't inherit this behavior, they have their own dispute windows and return codes, so a system built purely off the Bacs indemnity model will get it wrong for the other rails.