Digital goods / Gaming
Migrating Increddy to a self-hosted Medusa backend, without touching the frontend
A live digital-goods storefront migrated off Shopify onto a self-hosted Medusa v2 backend behind the same Next.js frontend, with license-key delivery rebuilt as a race-proof, idempotent reservation system.
Results
- legacy orders or customers migrated: a clean cutover, not a data migration
- 0legacy orders or customers migrated: a clean cutover, not a data migration
- monthly visitors the architecture is built to handle, via AWS ALB autoscaling
- 1M+monthly visitors the architecture is built to handle, via AWS ALB autoscaling
- independent teams, frontend and backend, kept in sync by shared contract docs
- 2independent teams, frontend and backend, kept in sync by shared contract docs
- reconciliation window closing any payment-to-key-delivery race condition
- 15 minreconciliation window closing any payment-to-key-delivery race condition
Increddy is a digital-goods storefront: game and software license keys and account logins, the same category as Driffle, Kinguin, or Instant Gaming. Nothing physical ships. A customer buys a key, the system reserves one instantly from a pool, and they reveal it in their order history after payment. Founder Neel Sakaria brought Kryeo in to replace the commerce backend under a live, global storefront.
The problem
Increddy ran on Shopify with a thin custom backend layer, a Directus CMS for content, and a third-party helpdesk for support. Shopify had gotten the storefront to market, but the commerce logic a license-key business actually needs did not fit cleanly into it: an engine for reserving exactly one key per order with no overselling accidents, a fraud and risk-scoring layer specific to digital goods (a category chargeback fraud targets hard), and a pricing model that treats "what currency do you see prices in" and "what currency will your card be charged in" as two different questions. All of that meant working against the platform instead of with it, on top of a revenue-share fee that scales with GMV forever.
What we built
A self-hosted Medusa v2 backend, built from scratch and populated from day one, running behind the exact same Next.js frontend Increddy already had. The existing Directus CMS and helpdesk integration were left in place deliberately: this was a backend swap, not a full rebuild.
- License-key engine. A custom module holding the key pool and the assignment logic: the core of the business, built as a first-class Medusa module rather than bolted onto an order field.
- Risk and fraud scoring. A custom module that can block checkout completion independently of Stripe's own fraud checks, tuned for the chargeback patterns specific to digital-goods resale.
- Currency architecture. Display currency (what a visitor sees) and charge currency (what their card is billed in) are modeled as two independent variables. Card payments default to USD for accounting and fraud simplicity, with an explicit "pay in your currency instead" option that unlocks local payment methods.
- Merchandising and content. Genre, platform, edition, and region taxonomy modules, activation guides served from a shared admin-editable library instead of duplicated per product, and old-URL redirects so a renamed product never 404s.
- Infrastructure. Postgres on AWS RDS, Redis for the event bus and workflow engine, the backend running on AWS EC2 behind an Application Load Balancer for horizontal scaling, and Cloudflare Workers at the edge, architected for a global audience rather than a single region.
The migration approach
Rather than designing the new backend from a specification document, the team read the live frontend code to determine exactly what API shape each feature needed, then built the backend to match it. Frontend and backend lived in two separate repositories, owned by separate people, kept in sync through a shared gap tracker mapping every frontend data need to a backend capability, plus matched task lists with explicit dependency IDs. Both teams verified every claim against running code rather than trusting either side's status notes, which caught several gaps a documentation-only process would have missed.
Because no Shopify data was being carried over, this was a clean cutover: the new Medusa backend went live with its own catalogue, and the frontend pointed at it directly for cart, auth, and checkout, with no proxy layer in the critical commerce path.
The hard problem: a key that can't sell twice or deliver zero
The core technical risk in a license-key business is the moment between "payment succeeded" and "a key is assigned." Get it wrong and you either sell the same key to two customers or take someone's money and hand them nothing.
The system reserves a key on the order.placed event as the primary path,
with a second, independent reservation trigger on payment.captured as a
safety net if the first one is missed or arrives out of order. Because both
triggers can fire for the same order, and because payment webhooks retry,
reservation had to be idempotent at the data layer. A background job then
sweeps every 15 minutes for any payment captured with no key assigned yet,
closing the gap for the cases that still fall through. Overselling is
allowed by design rather than blocked outright: a sold-out product can
still be purchased and drops into a manual-assignment queue for the admin,
which is a better failure mode for the business than refusing a sale
outright over an inventory count that might already be stale.
The result
The frontend never needed a rewrite: the storefront customers already knew kept working, with an entirely different backend underneath it. No legacy orders, customers, or catalogue data carried the usual migration risk, since none of it was migrated; the new backend was built and populated clean. The architecture is built to handle well over a million monthly visitors through AWS ALB autoscaling and Cloudflare Workers at the edge, and an end-to-end real payment has been verified through the live UI. The catalogue is live on increddy.com and growing.
For the specific engineering problems this surfaced, see the full migration story, how we made Increddy's key delivery idempotent under concurrent events, why display currency and charge currency are not the same decision, three things Shopify hides until you self-host, and how two teams on separate repos stayed in sync.
What we did
Tell us what you're building.