Design an Embeddable Pay by Bank Widget
PlaidSoftware Engineer · OnsiteLast updated
Design a secure, embeddable Pay by Bank frontend widget and supporting backend infrastructure that enables direct bank checkout via an isolated iframe and asynchronous payment processing.
Scenario
You are designing a merchant-facing Pay by Bank embeddable widget for a financial technology platform. Merchants embed a lightweight JavaScript SDK on their checkout pages. When a user chooses to pay with their bank account, the SDK renders an isolated iframe modal where the user searches for their financial institution, authenticates, and authorizes a bank transfer (such as ACH, RTP, or FedNow) without exposing their bank credentials or account details to the merchant.
Requirements
Functional
- Session Initialization: Merchant backend creates a single-use, short-lived session token (e.g.,
link_token) bound to the checkout amount, currency, and merchant identity. - Secure Widget Lifecycle: The client-side SDK instantiates an iframe pointing to a hosted auth domain, establishes a secure two-way communication channel, and surfaces events (e.g.,
onSuccess,onExit,onError). - Payment Authorization & Token Exchange: The widget securely captures user consent, completes bank OAuth or credential handoffs, and issues a short-lived authorization code to exchange on the merchant backend for a reusable or single-use payment token.
- Asynchronous Payment State: Support payment rails where final settlement takes minutes (RTP/FedNow) to multiple business days (ACH), notifying merchants via webhooks.
Non-Functional & Security
- Strict Secret Boundaries: Bank credentials, account numbers, and internal auth tokens must never leak to the merchant's DOM or network log.
- Browser Security: Mitigate clickjacking, cross-site scripting (XSS), and origin spoofing across parent-to-iframe communication.
- Resilience & Idempotency: Safe retry behavior for network dropouts, interrupted OAuth redirects, and duplicate clicks.
- Availability & Latency: 99.99% uptime; iframe cold load time under 500ms.
Scale Assumptions
- Peak traffic: 10,000 widget load requests per second; 500 completed authorizations per second.
- Over 5,000 integrated merchants and 100 million total linked bank accounts.
What to Cover
- End-to-End Flow & APIs: Trace requests from merchant backend, merchant client DOM, iframe widget, your platform backend, and third-party bank APIs.
- Security & Browser Isolation: Explain
window.postMessagehandshake, origin validation, Content Security Policy (CSP), iframe sandboxing attributes, and OAuth redirect strategies. - Data Model & State Machine: Define the payment session and transaction lifecycle states, including failure and dispute handling.
- Reliability & Observability: Mechanism for tracking funnel drop-offs, telemetry without PII leakage, and webhook delivery guarantees.
Hints
Hint 1
Ensure the postMessage protocol mandates that both the parent SDK and the child iframe strictly verify `event.origin` before reading payloads or sending sensitive responses.
Hint 2
Decouple client-side completion events from backend settlement by returning an exchangeable public token rather than an authorized payment object.
Hint 3
Consider how bank OAuth redirects interact with iframes, specifically why top-level browser redirects or popup windows are often required due to frame-busting headers.
Follow-up questions
- How do you handle banks that set `X-Frame-Options: DENY` or refuse to load their login pages inside an iframe during OAuth flows?
- If a user closes the browser tab after completing bank MFA but before the iframe sends `onSuccess` to the merchant page, how does the merchant know the payment was authorized?
- How would you prevent a compromised merchant website from tampering with the payment amount or intercepting the postMessage channel?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.