E-Commerce · 2024
Orbit Store
A template storefront loading in 3.8s was leaking mobile revenue. We rebuilt it headless: static product pages, edge-cached catalogue, a two-step Stripe checkout and an admin panel the team runs without a developer.
Client
Orbit Store
Industry
D2C E-Commerce
Timeline
3 weeks
My role
Full-Stack Developer & UI Designer
The problem
A slow, template-based store with a 3.8s load time and a checkout that lost customers on mobile.
The solution
A headless Next.js storefront with Stripe checkout, wishlists, and an admin panel the team can run without a developer.
+41%
Conversion rate
0.9s
Load time
+2.3x
Mobile revenue
What I built
Two-step checkout
Address + payment on one screen, Apple/Google Pay first on mobile.
Edge catalogue
Products rendered statically and revalidated on webhook, so pages are instant.
Wishlists
Guest wishlists in local storage, merged into the account on sign-in.
Admin panel
Inventory, orders, discount codes and content blocks — no CMS licence.
Architecture
- Next.js App Router with ISR product pages on Vercel edge
- Prisma + PostgreSQL for orders, inventory and customers
- Stripe Payment Intents with webhook-driven order state machine
- Cloudinary for responsive AVIF/WebP product imagery
Hard problems
Cart abandonment on mobile
Cut the checkout from 4 screens to 1 and added wallet buttons; mobile completion rose 2.3x.
Stale stock counts
Inventory reserved at payment-intent creation and released by a 15-minute expiry job.
Code from the build
A few real excerpts from the repository.
export async function POST(req: Request) {
const cart = CartSchema.parse(await req.json());
const items = await priceServerSide(cart); // never trust client prices
const intent = await stripe.paymentIntents.create({
amount: items.reduce((s, i) => s + i.unitAmount * i.qty, 0),
currency: "usd",
automatic_payment_methods: { enabled: true },
metadata: { cartId: cart.id },
});
await reserveStock(items, { expiresInMinutes: 15 });
return Response.json({ clientSecret: intent.client_secret });
}switch (event.type) {
case "payment_intent.succeeded":
await db.order.update({
where: { intentId: event.data.object.id },
data: { status: "PAID", paidAt: new Date() },
});
await sendReceipt(event.data.object);
break;
case "payment_intent.payment_failed":
await releaseStock(event.data.object.metadata.cartId);
break;
}"Conversions jumped 41% in three weeks and we finally stopped paying an agency to change a banner."
How it was built
- Discovery call, scope and fixed timeline agreed up front
- Custom design system built in Figma before development
- Typed, reviewed codebase with CI checks on every commit
- Deployed with monitoring, backups and post-launch support