April 19, 2026 9 min read

Engineering Account-to-Account Payments: The Pay by Bank Revolution

How open banking APIs are enabling direct bank-to-bank transfers as a checkout payment method — and what it takes to build this in production.

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 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:

  1. Customer selects "Pay by Bank" at checkout and chooses their bank
  2. Your backend calls the open banking API to create a payment consent resource
  3. You redirect the customer to their bank's authorization page
  4. Customer authenticates (biometrics, PIN, whatever their bank uses)
  5. Customer explicitly consents to the specific payment amount and recipient
  6. Bank redirects back to your callback URL with an authorization code
  7. Your backend exchanges the code and submits the payment execution request
  8. Payment settles over real-time rails (Faster Payments, SEPA Instant, FedNow)

Authorization Flow Diagram

Customer
Checkout
Merchant
Create consent
Open Banking API
PIS provider
Customer's Bank
Auth + consent
Consent Granted
Redirect back
Payment Executed
Funds settle

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:

// 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:

Real-Time Payment Rails

Pay by Bank rides on top of real-time payment infrastructure. The specific rail depends on geography:

Settlement Speed Comparison

Faster Payments (UK)
~2s
SEPA Instant (EU)
~10s
FedNow (US)
~20s
Card Settlement
T+1 to T+2
Traditional Bank Transfer
T+1 to T+3 (or more)
■ Real-time (seconds) ■ Next-day batch ■ Multi-day

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:

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:

  1. 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.
  2. 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.
  3. Build robust status tracking: Implement the webhook + polling dual strategy from day one. Don't rely solely on webhooks.
  4. 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.
  5. 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

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.