12,000+ Businesses. Here's What I Got Wrong Building Their E-Commerce Stack.
Quick summary
After serving 12,000+ businesses across Asia and Europe, these are the mistakes that cost the most: wrong payment integration, missing multi-currency, and checkout flows that silently killed conversions.
Why Most E-Commerce Projects Fail
Building an online store sounds simple until you actually do it. Payment processing, inventory management, multi-currency support, shipping calculations, tax compliance — the complexity compounds fast.
I've built e-commerce platforms that serve 12,000+ businesses across multiple countries. Here are the hard-won lessons that guides and tutorials won't teach you.
Architecture Decisions That Matter
Monolith First, Always
Every e-commerce platform I've built started as a monolith. Not because microservices are bad, but because:
- You ship faster with a monolith
- Debugging is simpler
- Deployment is one step
- You don't know your bottlenecks yet
Split into services only when you have a specific scaling problem. "We might need to scale" is not a specific problem.
Database Design Is Everything
E-commerce has inherently relational data. Products belong to categories. Orders contain line items. Users have addresses. Carts reference products. This is where PostgreSQL shines.
Key tables I always start with:
users— authentication and profileproducts— with JSONB for flexible attributescategories— hierarchical with parent referencesorders— with status trackingorder_items— line items with price snapshot (never reference current product price)addresses— separate table, linked to users and orders
Critical rule: Always store the price at the time of purchase in the order item. Product prices change. An order placed at the old price should reflect the old price forever.
Cart Strategy
Two approaches, both valid:
- Database carts — Persistent, survive browser closures, work across devices. More server load.
- Local storage carts — Fast, no server calls, but lost if the user clears their browser.
I typically use a hybrid: local storage for anonymous users, database cart for logged-in users, with a merge when they log in.
Multi-Currency: Harder Than You Think
Supporting multiple currencies isn't just about multiplying prices by exchange rates.
What you actually need:
- Real-time exchange rates — Use an API like Open Exchange Rates or Fixer.io. Cache rates for 1 hour.
- Price rounding rules — Different currencies have different rounding conventions. Japanese Yen has no decimal places. Kuwaiti Dinar has 3.
- Display formatting —
$1,234.56vs1.234,56 €vs¥1,234. UseIntl.NumberFormat. - Settlement currency — You might display prices in EUR but settle in USD. Your payment processor handles conversion, but you need to track both.
The approach that worked:
Store all prices in the smallest currency unit (cents) in a base currency (USD). Convert for display only. This avoids floating-point issues and keeps your accounting clean.
Checkout Optimization: Where Revenue Lives
A 40% reduction in checkout abandonment means 40% more revenue. Here's what moves the needle:
1. Guest Checkout Is Non-Negotiable
Forcing account creation before purchase kills conversions. Let people buy first, create an account after (with their order email pre-filled).
2. Minimize Form Fields
Every field you add increases abandonment. The minimum for checkout:
- Shipping address
- Payment info
Name can come from the payment method. Phone number can be optional. Don't ask for anything you don't strictly need.
3. Show Progress
A simple 3-step indicator (Shipping → Payment → Review) reduces anxiety and tells users how much longer the process takes.
4. Address Autocomplete
Use Google Places API or a similar service. Typing a full address is painful on mobile. Autocomplete reduces errors and speeds up the process significantly.
5. Multiple Payment Methods
In Europe, bank transfers and local payment methods are common. In India, UPI is dominant. In the USA, credit cards and PayPal lead. Support what your users actually use, not just Stripe's default card form.
Payment Integration: The Careful Parts
Webhook-First Architecture
Never rely on the client to confirm payment. Your flow should be:
- Client initiates payment
- Redirect to payment provider
- Payment provider sends a webhook to your server
- Your server updates the order status
- Client polls or receives confirmation
The webhook is the source of truth. Client-side confirmations can be faked, interrupted, or lost.
Idempotency
Payment webhooks can be delivered more than once. Your handler must be idempotent — processing the same webhook twice should not charge the customer twice or create duplicate orders.
Use a unique idempotency_key (usually the payment intent ID) and check if you've already processed it before taking action.
Refund Handling
Build refund logic from day one, even if you think you won't need it. You will. The data model should track:
- Original payment reference
- Refund amount (partial refunds are common)
- Reason
- Status
- Who initiated it
Inventory Management
Prevent Overselling
When 100 people are buying the last item simultaneously, you need to prevent overselling. Two approaches:
- Pessimistic locking — Lock the inventory row when someone adds to cart. Simple but can cause contention.
- Optimistic locking — Check inventory at checkout time. If it changed since the user added it to cart, show an error. Better performance, slightly worse UX.
I prefer optimistic locking with a reservation system: when someone starts checkout, reserve the item for 15 minutes. If they don't complete, release it.
Search and Filtering
For small catalogs (under 10,000 products), PostgreSQL full-text search works great. For larger catalogs, use a dedicated search engine like Meilisearch or Elasticsearch.
Key features users expect:
- Faceted search — Filter by size, color, price range, category simultaneously
- Typo tolerance — "bleu shirt" should find "blue shirt"
- Instant results — Search-as-you-type with debouncing
Performance at Scale
Caching Strategy
- Product pages — Cache at the CDN level with a 5-minute TTL. Revalidate on product update.
- Cart — Never cache. Always fresh.
- Category pages — Cache for 1 hour. Invalidate when products in the category change.
- Search results — Cache popular queries for 15 minutes.
Image Optimization
Product images are the heaviest assets. Use:
- WebP format with JPEG fallback
- Multiple sizes (thumbnail, card, full) generated at upload time
- Lazy loading for below-the-fold images
- CDN delivery (Cloudinary, Imgix, or Next.js built-in optimizer)
Conclusion
E-commerce development is a lesson in handling complexity without letting it overwhelm you. Start simple, handle money carefully, optimize checkout relentlessly, and always build for the user who's buying on a phone with one hand while holding a coffee in the other.
The platforms that succeed aren't the most feature-rich — they're the ones where buying something feels effortless.
Written by
Abhishek Gautam
Full Stack Developer & Software Engineer based in Delhi, India. Building web applications and SaaS products with React, Next.js, Node.js, and TypeScript. 8+ projects deployed across 7+ countries.
Free Weekly Briefing
The AI & Dev Briefing
One honest email a week — what actually matters in AI and software engineering. No noise, no sponsored content. Read by developers across 30+ countries.
No spam. Unsubscribe anytime.
You might also like
Shopify vs WooCommerce vs Custom Development in 2026: Which Should You Choose?
Shopify, WooCommerce, or custom-built? A developer's honest comparison of all three e-commerce options in 2026 — real costs, limitations, when each makes sense, and which businesses get it wrong.
10 min read
The Full Stack Developer's Real Tech Stack in 2026 — After 8 Production Projects
After 8 major projects deployed across 7 countries: the technologies that kept showing up in production. Not a "learn everything" list — a focused breakdown of what actually matters at scale.
10 min read