I Talked to AI for 2 Weeks. It Built India's Fabric Marketplace. I Just Asked the Right Questions.
- 4 days ago
- 9 min read
Written by Shivam Dube - AI, Technology Analyst.
01 · Why did FabricBazaar Marketplace?
I built FabricBazaar — India's fabric Marketplace for sarees, kurtas, bed sheets, and handloom textiles. No CS degree. No team. No startup funding. Just a clear idea, Claude as my AI pair programmer, and a willingness to iterate fast.
India's textile industry is one of the largest in the world — and almost none of it is accessible online in a trustworthy, organised way. Weavers from Varanasi, block-print makers from Jaipur, silk sellers from Kanchipuram — all selling on WhatsApp or Instagram, buried on undiscoverable general marketplaces.
▪ WHAT I WANTED TO BUILD in India's Fabric Marketplace?
Focused exclusively on Indian fabrics — no noise, no distractions
Verified sellers publicly with a trust badge system
All Indian payment methods — UPI, cards, wallets, net banking, Cash on Delivery
Real-time order tracking with OTP-confirmed delivery
Worked for every state in India — not just four
Real seller analytics so they could actually grow their business
02 · The Stack
I didn't choose the stack analytically. I described the product and Claude recommended it. Flask over Django — lighter, faster to prototype, every route is a single function you can read in one pass.
Tool | Technology | Why |
Backend | Flask 3.1 (Python) | Lightweight, readable, every route is a function. The AI writes cleaner Flask than Django — less magic to debug. |
Database | SQLAlchemy + PostgreSQL | SQLite locally, PostgreSQL in production. Should have been PostgreSQL from day one — concurrent writes can corrupt SQLite. |
Payments | Razorpay SDK | UPI, Cards, Wallets, Net Banking — all Indian payment rails in one SDK. The only real choice for India. |
Hosting | Connect GitHub, add env vars, deploy. Free PostgreSQL. Auto-deploys on every git push. | |
AI Engine | Claude (Anthropic) | Wrote every line of code, designed architecture, ran security audit, wrote 161 tests, found the ₹1 bug. |
Security | Flask-WTF + Bcrypt + Limiter | CSRF on every form. Bcrypt password hashing. Rate limiting on auth and cart routes. |
No Flask-SQLAlchemy magic methods. No shortcuts. Every abstraction built explicitly so I could understand and debug it completely. This paid off enormously when tracking down the stock race condition.
03 · How It Got Built?
The best prompt is a problem statement, not a technical instruction. 'I want sellers to upload product photos' produces better code than 'write a Flask route that accepts multipart/form-data.' |
— learned after hundreds of Claude conversations |
Phase | What Got Built |
Phase 1 | Core Architecture · One prompt, entire skeleton Described the marketplace in a single paragraph. Got back complete folder structure, all data models, all 15 blueprints. Four user roles with separate dashboards and access decorators in one session. |
Phase 2 | Shop + Cart + Search · Feature by feature Product listings with multi-field search. Filter panel: category, price range, seller. Sort: newest, price, featured, popular. Paginated 12/page. Guest cart with session storage that merges into DB on login. |
Phase 3 | Razorpay Checkout · The most important phase Full checkout with all 36 Indian states, 6-digit PIN validation. Razorpay order creation. HMAC-SHA256 server-side signature verification. CoD fraud prevention: blocked above ₹5,000 and if 2+ pending CoD orders. THIS IS WHERE THE ₹1 BUG WAS HIDING. |
Phase 4 | Tracking + Delivery + OTP · Logistics layer Auto-assign delivery partner by state — all 36 states and UTs mapped (was originally only 4). OTP-based delivery confirmation using secrets.randbelow(). Real-time tracking timeline with 9 status states. |
Phase 5 | Seller Dashboard + Analytics · The seller experience Company profile with GST/PAN, logo upload (MIME-verified). Product management with all toggles. Analytics: revenue by day, orders by status, top 10 products. Seller data isolation — sellers see only their own orders. |
Phase 6 | Security Audit + Deep Testing · Where everything changed Uploaded full codebase. Asked: what is broken, insecure, and would embarrass in production? Found 14 issues. Then 161 test cases across 8 categories. Found race conditions, insecure OTPs, monkey-patched columns, missing templates, and the ₹1 bug. |
04 · Bugs, hmm ...
These weren't edge cases. Some would have caused immediate financial damage or permanently destroyed buyer trust if they had reached real customers.
▪ BUG #1 — THE ₹1 PAYMENT BUG
🐛 CRITICAL Every Razorpay payment charged exactly ₹1 regardless of cart total |
Razorpay takes amount in paise — 100 paise equals ₹1. The checkout code had amount: 100 hardcoded. A buyer purchasing a ₹4,000 Kanjivaram silk saree would be charged ₹1. Would have surfaced on the very first real transaction. |
● ● ● routes/checkout.py — the one-line fix |
- amount_paise = 100 # ₹1 always — completely broken |
+ amount_paise = int(float(order.total) * 100) # correct paise conversion |
✅ Fixed One line + HMAC-SHA256 server-side verification added |
Also added hmac.compare_digest() constant-time signature check so payment callbacks cannot be faked. Any mismatch now logs the client IP as a potential fraud attempt. Found only because of a security audit. |
▪ BUG #2 — LAST-ITEM RACE CONDITION
🐛 HIGH Two buyers could simultaneously purchase the last item in stock |
Without a database row lock, two concurrent checkouts could both read stock = 1, both decrement it, both succeed — leaving stock at -1. Classic concurrency problem invisible in single-user development. |
● ● ● routes/checkout.py — adding the DB row lock |
- p = Product.query.get(product.id) |
+ p = Product.query.with_for_update().filter_by(id=product.id).first() |
# with_for_update() → DB row lock until transaction commits |
▪ BUG #3 — HARDCODED CREDENTIALS IN SOURCE CODE
● ● ● config.py — before and after |
# ❌ Original — DO NOT DO THIS |
- RAZORPAY_KEY_ID = "rzp_test_SPBHWeZf5cvbyY" |
- RAZORPAY_KEY_SECRET = "Qt6KtjlDp8i6IhZ6BhAT9D41" |
- SECRET_KEY = "fabricbazaar-secret-key-2024" |
# ✅ Fixed — all secrets from environment variables |
+ RAZORPAY_KEY_ID = os.environ.get('RAZORPAY_KEY_ID', '') |
+ RAZORPAY_KEY_SECRET = os.environ.get('RAZORPAY_KEY_SECRET', '') |
+ SECRET_KEY = os.environ.get('SECRET_KEY') or secrets.token_hex(32) |
✅ Fixed .env file + .gitignore + production warnings |
Added .env.example template, .gitignore excluding .env and *.db, and a ProductionConfig.init_app() that logs a loud warning if Razorpay keys or SQLite are detected at production startup. |
▪ BUG #4 — DELIVERY ONLY COVERED 4 STATES
🐛 BUSINESS LOGIC Only Maharashtra, Karnataka, Chhattisgarh, and Goa had delivery routing |
Every other buyer in India had their order routed to a single fallback rider. That is 32 states and UTs with no proper delivery coverage for a marketplace claiming to serve India nationally. |
✅ Fixed Expanded STATE_PARTNER_EMAIL to all 28 states + 8 Union Territories |
One Python dictionary mapping every Indian state and UT to a dedicated delivery partner email. Expanding from 4 to 36 required adding 32 dictionary entries and updating the seed file. |
▪ BUG #5 — PREDICTABLE DELIVERY OTPS
🐛 SECURITY OTPs generated with Python's non-cryptographic random module |
random.randint(100000, 999999) is seeded from system state and is not cryptographically unpredictable. A sophisticated attacker could potentially calculate upcoming OTP values. |
● ● ● models/delivery.py |
- import random |
- return str(random.randint(100000, 999999)) |
+ import secrets |
+ return str(secrets.randbelow(900000) + 100000) # cryptographically secure |
▪ BUG #6 — MONKEY-PATCHED DATABASE COLUMNS
🐛 MIGRATIONS seller_reply columns defined outside class — invisible to Alembic |
The seller_reply and seller_replied_at columns were added as module-level statements after class definitions. Alembic scans class bodies to detect schema changes — it could not see these. Every fresh deployment would silently be missing those columns. |
✅ Fixed Columns moved inside class definitions — also fixed loyalty double-award and missing templates |
Also fixed in the same session: loyalty points double-award guard (idempotency check), duplicate email registration crash, two missing templates (wishlist.html and admin/settings.html), and missing rate limits on cart endpoints. |
▪ BUG #7 — RENDER FREE TIER HAS NO SHELL
● ● ● start.sh — auto-seed on first boot |
#!/bin/bash |
# Check if DB is empty → seed automatically → start gunicorn |
python -c " |
from app import create_app, autoseed_if_empty |
app = create_app('production') |
autoseed_if_empty(app) # skips if User.query.count() > 0 |
" |
gunicorn "app:create_app('production')" --workers 2 --bind 0.0.0.0:$PORT |
✅ Fixed start.sh seeds automatically if database is empty — no shell, no upgrade needed |
On every server boot: check User.query.count(). If zero — fresh deployment — run seed.py. If non-zero — data exists — skip. Safe to restart as many times as needed. Works on the free tier with no upgrade required. |
05 · Security Audit Findings
The audit was a single conversation where the full codebase was uploaded and the question asked was: what is broken, insecure, and would embarrass in production?
Finding | Severity | Status |
Razorpay credentials hardcoded in config.py | HIGH | Fixed |
Flask SECRET_KEY hardcoded as plain string | HIGH | Fixed |
Razorpay amount hardcoded to 100 paise (₹1) | HIGH | Fixed |
No rate limiting on login or register routes | HIGH | Fixed |
No HMAC verification on Razorpay callback | HIGH | Fixed |
Stock decrement has no DB row lock | MEDIUM | Fixed |
Delivery OTP uses random (not secrets) | MEDIUM | Fixed |
File upload checks extension only — no MIME type verification | MEDIUM | Fixed |
No security HTTP headers on responses | MEDIUM | Fixed |
SESSION_COOKIE_SECURE not set in production | MEDIUM | Fixed |
Login error reveals whether email exists | LOW | Fixed |
No rate limiting on cart add endpoint | LOW | Fixed |
Razorpay webhook not implemented | MEDIUM | Roadmap P1 |
Admin panel has no 2FA | MEDIUM | Roadmap P2 |
06 · Numbers & SWOT
Metric | Value | Metric | Value |
Lines of Python | 5,959 | HTML Templates | 90+ |
Route Blueprints | 15 | Data Models | 10 |
Test Cases | 161 | Tests Passing | 160 (99%) |
Security Issues Found | 14 | Issues Fixed | 12/14 |
States Covered | 36 (all India) | Cost to Build | ₹0 |
SWOT Analysis
STRENGTHS
| WEAKNESSES
|
OPPORTUNITIES
| THREATS
|
07 · Lessons — What Vibe Coding Taught Me?
1 | Add .env to .gitignore before writing line one Not before pushing to GitHub. Before writing anything. Credentials in source code is not a beginner mistake — it is catastrophic. The original config had live Razorpay keys in plain text. One public repo push and it is over. |
2 | PostgreSQL from day one, not SQLite SQLite is a single file. Perfect for local development. The moment two users touch your app simultaneously it can corrupt data. Set up Render PostgreSQL on day one — it is free, takes five minutes, and you never think about it again. |
3 | Describe the product, not the code 'I want sellers to upload product photos' produces better code than 'write a Flask route that accepts multipart/form-data.' The AI knows the implementation. You know the product. Stay in your lane. |
4 | Ask for audits more than features The most valuable sessions were not building features — they were uploading the full codebase and asking what is broken, insecure, and would embarrass in production. That is how the Rs. 1 payment bug was caught. |
5 | Test every user journey manually before calling it done Automated tests catch code errors. Manual walkthroughs catch product errors — like the missing wishlist template that caused a silent 500 error only when a logged-in buyer visited their wishlist for the first time on a fresh deployment. |
6 | Know your hosting limits before deploying Render free tier: no shell, 15-minute spin-down, 90-day PostgreSQL expiry. None are dealbreakers, but all need workarounds planned in advance. The shell problem was solved with the auto-seed startup script. |
“ |
Vibe coding is real software development. It just inverts where the thinking happens. You think about the product, users, and business logic. The AI thinks about syntax, patterns, and implementation. That is a genuinely good division of labour if you stay in your lane. |
— FabricBazaar Team · Raipur, Chhattisgarh |
08 · Roadmap
Pri | Feature | Description |
P1 | Razorpay Webhook | Server-side payment event verification — more reliable than client-side callback alone, protects against network failures during payment |
P1 | Email Notifications | Order confirmations, shipping updates, OTP delivery via SendGrid — currently everything is shown on-screen only |
P1 | ONDC Integration | India's Open Network for Digital Commerce — national buyer reach with zero marketing spend |
P2 | B2B Wholesale Portal | MOQ pricing, bulk order workflows, GST invoice generation for garment manufacturers — the fabric industry's real volume |
P2 | WhatsApp Business API | Order notifications, seller chat, and re-marketing via WhatsApp — where India's buyers actually live |
P3 | Android App + UPI Deep Links | Native mobile for Tier-2/3 city buyers who shop exclusively on phones — better conversion than responsive web |
P3 | AI Fabric Matching | Photo or occasion-based recommendations — 'find me something for a winter wedding in Rajasthan' |
Try It Live Now ...
FabricBazaar is live at marketplace-fk8b.onrender.com.
Browse, log in as any role, test the full checkout flow.
Role | Password | Access | |
Admin | Admin@1234 | Full platform control | |
Seller | Seller@1234 | Seller dashboard | |
Customer | Customer@1 | Buyer experience |





Comments