Case study
Payments
Getting Customers Through TrueLayer's Consent Flow
A third of customers who chose pay by bank never finished paying, and most of them were lost inside their own bank's login. App-to-app redirects, a QR hand-off on desktop, a smarter bank picker and a return page that survives a lost session took completion from 64% to 82%.
Visit the live site- 4 min read
- Duration2 yrs 7 mos
- Last updated
The impact, in numbers
- Payment completion
- 64% → 82%
- Customers who chose pay by bank and got back to the merchant.
- Authorised once at the bank
- 81% → 91%
- App-to-app and the desktop QR hand-off, mostly.
- Data connections lapsing at 90 days
- ~33% → <10%
- After reconfirmation moved into one in-app prompt.
Context
I was one of the engineers on the team that owned TrueLayer's hosted consent journey: the pages between a merchant's "Pay by bank" button and the customer's bank. It covered bank selection, the consent screen, the redirect out to the bank and the page the customer came back to, built in #react and #typescript. The same journey also connected accounts for data access, which customers had to renew every 90 days.
The constraint
Each payment crossed three owners. The merchant owned the checkout, we owned selection and consent, and the bank owned login and authorisation. The biggest losses happened on the bank's side, where we couldn't change a single screen.
The consent screen was regulated, so content couldn't simply be cut: the Customer Experience Guidelines set what it must show. And money was moving. A return page that recorded a payment because a customer reached it, or a retry that sent a second consent, could charge someone twice.
What I did
- Merchant checkout to Bank picker + consent
- Bank picker + consent to Bank's app: Universal / app link
- Bank picker + consent to Bank web login: Fallback only
- Bank picker + consent to Desktop: QR code: On desktop
- Desktop: QR code to Bank's app: Scan
- Bank's app to Return page, no session: Redirect back
- Return page, no session to Payment status + webhook: Look up by payment ID
- 1
Measure across the bank boundary
We couldn't see inside the bank, so I tracked the redirect out and the return in with #mixpanel, joined them to the payment's final status, and broke it down per bank. That showed the bank's login, not our screens, losing the most.
- 2
Rebuild the bank picker
Ordered by our own payment volume instead of alphabetically, with search that knows aliases ("RBS") and a single "Continue with" button for the last bank used on that device.
- 3
Warn about bank outages before the redirect
Surfaced each bank's live status on the picker, so a customer whose bank was down heard it from us, not from a failed login.
- 4
Trim the consent screen to what's required
Payee, amount, reference and one "Continue" button, with the terms one tap away instead of a wall of text above the button.
- 5
App-to-app on mobile
Handed off through each bank's universal or app link, so customers approved in their banking app with Face ID instead of hunting for web credentials they had never used.
- 6
A way out of in-app browsers
Links opened from social apps run in a built-in browser where universal links often don't fire. I detected that case and showed an explicit "Open in your banking app" button.
- 7
QR hand-off on desktop
Instead of the bank's web login, desktop showed a QR code that opened the same consent on the customer's phone, while the desktop tab waited for the result.
- 8
A return page that needs no session
The bank's app often reopened the return URL in a different browser, without the merchant's cookie. The page now looks up an unguessable payment ID and shows only the status; the merchant's records come from the webhook, never from this page loading.
- 9
One-tap 90-day reconfirmation
Once the FCA's PS21/19 let providers reconfirm data access instead of sending customers back through their bank, I built an in-app prompt shown a week before expiry, listing what the customer would keep sharing.
- 10
Considered: our own bank login screen
Rejected. Collecting bank credentials on a third-party page is screen scraping, which open banking exists to replace, and it trains customers to type bank passwords into pages that aren't their bank's.
Outcome
Payment completion went from 64% to 82%, mostly at the bank step and the return.
On data access, about a third of connections used to lapse at each 90-day mark; with one in-app prompt, under one in ten did. Merchants noticed it first as fewer support tickets about "pay by bank not working", most of which had been customers lost on the way back.
Solving something similar?
I take on select engineering work like this.
What I'd do differently
Break the funnel down per bank from the first week. For months we read one blended completion rate, which hid that a handful of banks without app-to-app support were dragging it down; the per-bank view turned "improve conversion" into a short list of banks to chase.