What Pay by Bank Actually Is
Pay by Bank is not your grandmother's bank transfer. It's not the "pay by wire" option that takes 3 days and requires manually entering sort codes. Pay by Bank uses open banking APIs to initiate a payment directly from the customer's bank account to the merchant's account — in real time, with explicit consent, and with no card network in the middle.
The technical term is Payment Initiation Service (PIS), defined under PSD2 in Europe and enabled by the Open Banking Standard in the UK. The customer authenticates with their own bank, consents to a specific payment, and the funds move over real-time rails. No card numbers. No interchange fees. No chargebacks.
From an engineering perspective, this is a fundamentally different payment model. With cards, you're sending a payment request through a chain of intermediaries (gateway → acquirer → scheme → issuer). With Pay by Bank, you're initiating a credit push directly from the payer's bank. The money moves in one hop.
Key distinction: Card payments are "pull" — the merchant pulls funds from the customer's account via the card network. Pay by Bank is "push" — the customer pushes funds directly to the merchant. This eliminates the entire chargeback mechanism.
The Consent and Payment Initiation Flow
The Pay by Bank flow is redirect-based, similar to 3D Secure but with a critical difference: the customer authenticates directly with their bank, not with a card scheme. Here's how it works in production:
- Customer selects "Pay by Bank" at checkout and chooses their bank
- Your backend calls the open banking API to create a payment consent resource
- You redirect the customer to their bank's authorization page
- Customer authenticates (biometrics, PIN, whatever their bank uses)
- Customer explicitly consents to the specific payment amount and recipient
- Bank redirects back to your callback URL with an authorization code
- Your backend exchanges the code and submits the payment execution request
- Payment settles over real-time rails (Faster Payments, SEPA Instant, FedNow)
Authorization Flow Diagram
The consent object is the core primitive. It contains the payment amount, currency, creditor account details, and a reference. Once the customer authorizes it, you have a one-time permission to execute that exact payment. No storing card numbers, no PCI scope.
// Simplified consent creation (Open Banking UK style)
POST /domestic-payment-consents
{
"Data": {
"Initiation": {
"InstructionIdentification": "pay_abc123",
"EndToEndIdentification": "order_789",
"InstructedAmount": {
"Amount": "49.99",
"Currency": "GBP"
},
"CreditorAccount": {
"SchemeName": "UK.OBIE.SortCodeAccountNumber",
"Identification": "20000456789012",
"Name": "Merchant Ltd"
}
}
}
}
Cards vs Pay by Bank: The Engineering Trade-offs
Before you rip out your card integration, here's the honest comparison from running both in production:
| Dimension | Card Payments | Pay by Bank (A2A) |
|---|---|---|
| Transaction cost | 1.5–3.5% + fixed fee | 0.1–0.5% or flat fee |
| Settlement speed | T+1 to T+3 | Seconds to minutes |
| Chargebacks | Yes — fraud liability | None — push payment |
| Auth/conversion rate | 85–95% | 60–80% (improving) |
| Recurring payments | Easy (card-on-file) | Variable Recurring Payments (VRP) — newer |
| PCI compliance scope | SAQ-A minimum | Not applicable |
| Refund mechanism | Reverse the charge | Separate credit transfer required |
| Geographic coverage | Global | Regional (UK, EU, growing) |
The conversion rate gap is the elephant in the room. Redirecting customers to their bank app introduces friction. Some banks have clunky auth flows. Some customers don't trust it yet. But the economics are compelling enough that merchants with high average order values (utilities, rent, B2B) are adopting it aggressively.
Engineering Challenges in Production
Bank API Inconsistencies
This is the biggest pain point. Despite standards like Open Banking UK and the Berlin Group's NextGenPSD2, every bank implements things slightly differently. Response formats vary. Error codes are inconsistent. Some banks return payment status synchronously; others require polling. I've seen banks that return 200 OK with an error buried in the response body.
Your integration layer needs to normalize across banks. Build an adapter pattern with bank-specific implementations behind a common interface. Test against every bank's sandbox — and then test again in production, because sandboxes lie.
Webhook Reliability and Status Polling
Payment status updates come via webhooks from the open banking provider. But webhooks are unreliable by nature — they can be delayed, duplicated, or lost entirely. You need a dual strategy:
- Accept webhooks for real-time status updates (payment executed, failed, rejected)
- Run a background poller that checks pending payments every 30–60 seconds
- Implement idempotency on your webhook handler — you will receive duplicates
- Set a timeout: if a payment hasn't settled within 15 minutes, flag it for investigation
// Dual-track payment status resolution
func (s *PaymentService) ReconcilePayment(ctx context.Context, paymentID string) error {
payment, _ := s.repo.Get(ctx, paymentID)
if payment.Status == "SETTLED" || payment.Status == "FAILED" {
return nil // Terminal state, nothing to do
}
// Poll the open banking provider for current status
status, err := s.obClient.GetPaymentStatus(ctx, payment.ConsentID)
if err != nil {
return fmt.Errorf("status check failed: %w", err)
}
if status != payment.Status {
s.repo.UpdateStatus(ctx, paymentID, status)
s.events.Publish(ctx, PaymentStatusChanged{ID: paymentID, Status: status})
}
return nil
}
Handling the Redirect Dance
The redirect flow is fragile. Customers drop off at every step — they get confused by the bank redirect, their session times out, they close the browser mid-auth. You need to handle:
- Callback URLs with error parameters (customer declined, bank unavailable)
- Orphaned consents where the customer never completed auth
- Mobile deep-linking to bank apps (not all banks support app-to-app)
- Session recovery if the customer returns after a timeout
Real-Time Payment Rails
Pay by Bank rides on top of real-time payment infrastructure. The specific rail depends on geography:
- UK — Faster Payments Service (FPS): Settles in seconds, 24/7/365. £1M per-transaction limit. The most mature ecosystem for Pay by Bank.
- EU — SEPA Instant Credit Transfer (SCT Inst): Settles in under 10 seconds. €100,000 limit. Mandatory for all EU banks from 2025.
- US — FedNow: Launched 2023, still growing. Settles in seconds. $500,000 default limit. Adoption is bank-by-bank.
Settlement Speed Comparison
The implication for your system design: with real-time settlement, you can confirm payment finality almost immediately. No more "payment pending" states lingering for days. But you need infrastructure that can process settlement confirmations in real time — webhooks, event streams, and status updates that fire within seconds of the payment completing.
When to Offer Pay by Bank
Pay by Bank isn't a universal card replacement — at least not yet. Based on production data, here's where it makes sense today:
- High-value transactions: The flat/low-percentage fee structure means you save significantly on orders above £50–100. Utility bills, rent, insurance premiums.
- Recurring payments (with VRP): Variable Recurring Payments let customers authorize a mandate for ongoing payments without re-authenticating each time. Great for subscriptions with variable amounts.
- B2B payments: Businesses are comfortable with bank transfers. A2A removes the manual friction while keeping costs low.
- Markets with high card fees: In regions where interchange is unregulated or high, A2A is a compelling alternative.
Keep cards as the default for low-value, high-frequency purchases where conversion rate matters more than transaction cost. Offer Pay by Bank as a prominent alternative for the use cases above. Let the data guide your checkout optimization.
Implementation Approach
If you're adding Pay by Bank to an existing payment stack, here's the practical path:
- Start with a PIS provider: Don't connect to banks directly. Use providers like TrueLayer, Yapily, Token.io, or Plaid (for US). They normalize the bank APIs and handle the regulatory complexity.
- Model it as another payment method: In your payment orchestration layer, A2A should sit alongside cards, wallets, and other methods. Same state machine, different execution path.
- Build robust status tracking: Implement the webhook + polling dual strategy from day one. Don't rely solely on webhooks.
- Handle refunds separately: There's no "reverse" for a push payment. You need to initiate a separate credit transfer back to the customer. Build this into your refund service.
- Monitor bank availability: Banks have maintenance windows. Their APIs go down. Build a bank health dashboard and route customers to available banks.
Production tip: Store the bank's payment reference (end-to-end ID) alongside your internal payment ID. You'll need it for reconciliation, and some banks use different reference formats. Map them early.
References and Further Reading
- Open Banking UK — API Standards
- Berlin Group — NextGenPSD2 Framework
- Federal Reserve — FedNow Service
- European Payments Council — SEPA Instant
- TrueLayer — Payment Initiation API Docs
Disclaimer: This article reflects personal engineering experience and publicly available documentation. It does not constitute financial advice. Payment regulations, API specifications, and rail capabilities change frequently — always refer to official provider documentation and consult compliance teams before implementing payment systems in production. Specific figures (fees, settlement times, limits) are approximate and vary by provider, bank, and jurisdiction.