BlueSoft
RAG over financial documentsAI agents & MCPLLM featuresML for fraud & risk
Case studies
BlogAbout
Start a conversation
BlueSoft

AI engineering, built around fintech.

Phone ·+44 7868 694422·+92 318 6327097
WhatsApp ·+44 7868 694422·+92 318 6327097
[email protected]

SERVICES

RAG over financial docsAI agents & MCPLLM featuresML for fraud & risk

COMPANY

AboutCase studiesTradevoBlogContact

CONNECT

LinkedInUpworkTradevo
© 2026 BlueSoft Limited. All rights reserved.
PrivacyTerms
CASE STUDIESMAGIC

Magic — Payments Without Cards

A production payments platform for e-commerce merchants. Bank-to-bank transfers in place of card processing, with customer rewards funded by the interchange savings.

SHIPPED
Production
DURATION
End-to-end
SERVICES
Payments · Fintech
STACK
Django · PostgreSQL · Plaid · Dwolla · TypeScript SDK
01 · THE PROBLEM

What Magic needed, and why the bank-to-bank route is harder than it looks.

Card interchange is a tax on every e-commerce transaction. For a merchant doing $10M/year, Visa and Mastercard fees alone consume $200K–$300K in margin — money that goes to networks and issuers, not the business.

The alternative — ACH and open-banking transfers — has existed for years. But adoption is blocked by three real engineering problems, not marketing ones: bank-account-based payments feel slow and unfamiliar at checkout; settlement is asynchronous and reversible in ways most teams underestimate; and integration cost is high — merchants will not rebuild their checkout for a new processor.

Our engagement: build the full platform end-to-end. Backend, infrastructure, merchant-facing SDK, and the embedded checkout experience.

“The interesting work isn't in shipping bank-to-bank payments. It's in shipping them in a way that drops into a merchant's existing checkout the way Stripe does — or it loses before it starts.”
— Engineering lead, Magic
02 · CONSTRAINTS

What made this hard.

Drop-in integration: no merchant backend changes
Stripe set the bar a decade ago — single npm install, a few lines of JSX, no backend changes. Anything heavier and merchants walk away even when the interchange savings are obvious.
Trust boundary: bank credentials never touch merchant domain
PCI and compliance scope require that bank authentication, account linking, and amount confirmation all happen on a Magic-controlled domain. The merchant's frontend receives only a signed result via postMessage.
Settlement timing diverges from API responses
A Dwolla 'successful' response means instructions accepted, not money moved. Settlement takes 1–4 business days and a transfer that looks complete on Tuesday can return failed on Friday — insufficient funds, account closure, ACH return codes.
Idempotency required end-to-end, not just at HTTP layer
SDK retries, webhook redeliveries, and manual replays all need to collapse to the same transaction. The difference between a payment platform you can operate and one that quietly double-charges customers when AWS hiccups.
Audit trail per state-changing operation
Every state-changing operation logs actor, source, and full request context. Required for compliance reviews, invaluable for debugging settlement disputes weeks after the fact.
03 · WHAT WE BUILT

The architecture.

A drop-in payment method for e-commerce merchants. Customers click Pay with Magic on the merchant's store, authenticate their bank in a Magic-hosted flow, and funds move bank-to-bank. Merchants pay a fraction of card processing fees and can route the savings into customer rewards — tokens, cashback, store credit — to incentivize the switch.

The platform is four moving parts: a thin TypeScript SDK on the merchant page, a hosted checkout on a Magic-controlled domain, a Django backend organized around an event-sourced ledger, and external rails (Plaid for bank linking, Dwolla for fund movement) talked to through a webhook + event orchestrator that treats retries as expected, not exceptional.

ARCHITECTURE
Simplified · the production system has additional caching, observability, and dead-letter queues
MERCHANT SDK
npm · one component
→
HOSTED CHECKOUT
popup + iframe fallback
→
MAGIC BACKEND
Django
→
EVENT LEDGER
append-only
→
SIGNED RESULT
postMessage
EXTERNAL RAILS · ACH & OPEN BANKING
PLAID · bank link · auth
→
DWOLLA · ACH · transfer
→
WEBHOOK ORCHESTRATOR · retries + DLQ

Integration surface: a deliberately thin SDK

One npm install. One component. The SDK opens a Magic-hosted page in a controlled child window (popup with iframe fallback for blocked contexts) and returns a signed result via postMessage. Nothing else. Every additional method on a public SDK is a future support burden.

This isolation matters for three reasons: PCI and compliance scope (merchant never touches bank credentials), upgrade path (we can change the entire checkout flow on our side without merchants redeploying), and trust model (customer enters credentials on a domain that visibly belongs to a payment provider, not a random storefront).

Transaction engine: state machine, not row

Every transfer moves through initiated → submitted → pending → settled → finalized, with explicit paths to failed, returned, and disputed. State transitions are append-only events on a ledger table, never destructive updates on a transactions table.

The ledger is the source of truth, not the bank's API response. Dwolla's transfer endpoint is the trigger; reconciliation against Dwolla webhooks, periodic transfer status pulls, and bank return files determines the actual state. Current state is computed, not stored as ground truth.

Idempotency at the engine, not the edge

Every transfer carries a deterministic idempotency key derived from (merchant_id, customer_id, order_id, amount, nonce). Retries — from the SDK, from webhook redelivery, from manual replays — collapse to the same transaction at insert time. The key is enforced at the engine layer, not bolted onto HTTP handlers.

Rewards as a separate-but-linked ledger

The payment ledger and the rewards ledger are independent systems. Every reward grant references the originating transaction by ID but settles on its own state machine, with its own idempotency model. When a transaction reverses, the reward is automatically clawed back through the same event mechanism — not through application logic that someone has to remember to write.

The bookkeeping cost of this separation is higher. The cost of not doing it shows up six months in, when a merchant disputes a settlement total and there's no clean way to prove the numbers.

04 · WHAT IT LOOKS LIKE

What it looks like in production.

A few views from the production pipeline. Some details have been redacted for client confidentiality.

PRODUCT VIEW
Bank selection inside Magic's hosted flow. Authentication runs on Magic's domain through Plaid Link, so bank credentials never touch the merchant's page.
PRODUCT VIEW
The drop-in checkout, rendered inside a merchant store. One SDK component adds Pay by bank alongside the existing card option — no backend changes on the merchant's side.
05 · TRADEOFFS

What we considered, and why we didn't ship it.

Every architectural decision had 2-3 alternatives we seriously considered. Here are the approaches we rejected and why:

REJECTED

Deduct rewards from the transfer amount

Naive version: subtract the reward at transfer time. Creates accounting problems within a quarter — refunds and reversals require unwinding two intents in a single ledger row, and merchant statements stop reconciling to bank statements.

REJECTED

Single ledger for payments and rewards

Cheaper to build. But settlement timing diverges from reward timing, and disputes become impossible to debug because the same row carries both concerns. Six months in, you can't prove a merchant's settlement total.

REJECTED

Clever code in the critical path

The transfer engine, the idempotency check, and the ledger writer are deliberately the most boring code in the system. Cleverness lives at the edges — SDK, integration surface, developer tooling. Not where money moves.

REJECTED

Trust the bank's API response

Dwolla's success response means instructions accepted, not money moved. Treating it as ground truth produces a system that silently disagrees with reality every time a transfer returns 1–4 days later.

06 · OUTCOME

What shipped and what changed.

The platform launched into production handling live merchant transactions across the full flow: bank linking, authorized transfers, reconciliation, rewards distribution, and refunds. The SDK shipped on npm and integrated with merchant storefronts in under an afternoon per integration.

The technical decisions documented above held through scale. The event-sourced reconciliation model, in particular, paid back the upfront cost the first time a settlement question came in from a merchant — and every time after. The question "what actually happened to this $4,127 transfer last Thursday?" became a query, not an investigation.

The separation of payments and rewards ledgers showed its value the first time a merchant disputed a settlement total. With independent state machines and a shared event mechanism for reversals, the system produced a complete, defensible timeline for both ledgers without any reconciliation logic written by hand.

$200K–300K
INTERCHANGE SAVED (PER $10M GMV)
1–4 days
REAL SETTLEMENT WINDOW HANDLED
< 1 afternoon
MERCHANT SDK INTEGRATION TIME
“The boring code in the critical path is the reason we can answer settlement questions in seconds instead of days. Cleverness at the edges, discipline at the core.”
— Founder, BlueSoft (lead engineer)
07 · RETROSPECTIVE

What we'd do differently next time.

Looking back: the highest-leverage decision was treating the ledger as an append-only event log from day one, not retrofitting it later. Every payments platform eventually arrives at event sourcing for the money path. The teams that start with row-mutation accounting spend a year unwinding it before they can ship reconciliation features.

The second-highest was keeping the SDK intentionally thin. Every additional method on a public SDK becomes a future support burden — and merchant integrations are where the platform's reputation lives or dies. A small surface area is easier to support, easier to upgrade, and easier to reason about during compliance reviews.

If we started today, the only change we'd make is investing earlier in dead-letter queue tooling for unprocessable webhooks. We built it in week 6; doing it in week 1 would have saved roughly a week of incident triage on the early integrations.

Event-sourced ledger from day one
Every payments platform arrives at event sourcing eventually. Starting there saves a year of unwinding row-mutation accounting later.
CASE.01
Magic
SHIPPED
Production
DURATION
End-to-end
SERVICES
Payments · Fintech
STACK
Django · PostgreSQL · Plaid · Dwolla · TypeScript SDK
KEY FACT
$200K–300K
interchange saved on $10M GMV
READY TO TALK?

Have something like this you need to ship?

First call is 30 minutes. You describe what you're trying to ship and what's in the way. We ask technical questions and figure out together whether we're the right team for it.

Book a 30-min call →Or email instead
RESPONSE
Within 1 business day
FORMAT
30 minutes · No deck
FIT
Figured out together
OUTCOME
Yes, no, or a referral