88 Badminton House is a premium sports retailer in KL. They needed an online store. What they actually needed was a system — product catalog, checkout, payment processing, admin dashboard, customer management, order tracking, and a walk-in stringing service tool that tracks client racket preferences. One developer. Full stack. Production.
This isn't a tutorial project. There's a deposit in my bank account. There's a client checking in every week. There's a launch date. The constraints are real, and real constraints produce better software than infinite timelines ever do.
The Brief
The client's existing business runs on WhatsApp messages and a physical store. Customers ask about product availability via WhatsApp. The owner checks stock manually. Orders are tracked in a notebook. Stringing jobs — restringing badminton rackets with specific string types and tensions — are managed by memory and sticky notes.
The website isn't the product. The system behind it is. The website is the interface that customers see. Behind it: real-time inventory that updates when orders come in, an admin panel where the owner manages everything from one screen, and a stringing service tracker that remembers every client's racket preferences so they don't have to repeat themselves every visit.
The Scope Nobody Warns You About
When a client says 'I need an online store,' they're imagining the storefront. Products on a page, a cart, a checkout button. That's maybe 30% of the actual work.
The other 70%: authentication system for admin access. Role-based permissions. Order state machine (pending → paid → ready → shipped → completed, with branches for cancellation and refunds). Email notifications at each state transition. Webhook handlers for payment confirmations. Rate limiting on public endpoints. CSRF protection. Input validation and sanitisation on every form. Admin dashboard with filtering, search, and pagination. Customer CRM with order history and lifetime value tracking. And the database schema that connects all of it — 12 tables with proper foreign keys, indexes, and constraints.
If you quote only for the storefront, you'll eat the other 70% at your own expense. I know because I've seen agencies do exactly that. I scoped every line item before sending the quote. Fixed price. Fixed scope. No surprises for either side.
Payment Integration in Malaysia
This is where being a Malaysian developer building for Malaysian businesses matters. You can't just plug in Stripe. Stripe doesn't support Malaysian ringgit for domestic transactions the way local payment gateways do. Malaysian customers expect FPX (direct bank transfer), Touch 'n Go, GrabPay, ShopeePay, Boost. These are the payment methods people actually use here.
I chose Revenue Monster — a Malaysian payment gateway that supports all of these methods through a single API. The integration wasn't trivial. OAuth2 authentication with RSA-signed requests. Webhook verification using HMAC-SHA256 signatures. Token refresh management with in-memory caching. Sandbox vs production environment switching.
There are no tutorials for this. Revenue Monster's documentation is decent but sparse. No Stack Overflow threads. No YouTube walkthroughs. No 'Revenue Monster + Next.js' starter template. I read the API docs, examined the authentication flow, built the integration from the specification. Claude Code helped implement the OAuth2 token management and webhook signature verification — but I had to understand the flow first and validate that the implementation matched the spec. The API docs were my source of truth, not Claude's training data.
The Admin Dashboard Nobody Sees
Customers see a beautiful storefront. The owner sees a dashboard that runs their business. Orders with status badges, filterable by state. Click into an order: customer details, line items, payment status, shipping information, stringing requests, timeline of every state change. Customer list with search, tags, order count, lifetime spend. Product management with variants, pricing, stock levels, images.
The admin authentication is custom — not NextAuth, not Supabase Auth. JWT tokens with bcrypt-hashed passwords, HTTP-only cookies with SameSite=Strict, 8-hour expiry, rate-limited login (5 attempts per 15 minutes), and constant-time password comparison to prevent timing attacks. I built this because I wanted full control over the auth flow and because the admin panel handles sensitive business data — customer PII, financial records, inventory. Off-the-shelf auth is fine for most cases. For an admin panel that manages real money, I want to understand every line.
The Stringing Service Problem
This is my favourite part of the build because it's the part no template handles. 88 Badminton House offers racket stringing as a walk-in service. A customer brings in their racket, chooses a string type, specifies tension (main and cross, separately), and picks it up later. Some customers come back monthly. They always want the same string at the same tension for the same racket.
I built a stringing client database. Each client has saved racket profiles — racket model, preferred string, preferred tensions, pre-stretch percentage. When they walk in, the owner pulls up their profile and creates a job with one click. Their preferences are pre-filled. The job enters a queue with status tracking (pending → strung → collected), priority levels (normal, urgent, rush), and links back to the customer profile.
This feature wasn't in the original brief. It came from understanding the business. I asked the client: 'What's the most repetitive part of your day?' The answer was remembering stringing preferences and manually tracking jobs on paper. So I built the solution. This is the difference between a developer who codes to spec and a developer who understands the business they're building for.
Security Is Not Optional
This system handles customer names, emails, phone numbers, shipping addresses, and payment references. Under Malaysia's Personal Data Protection Act (PDPA), I have legal obligations to protect this data. Security isn't a feature I add if there's time. It's the foundation I build on.
Content Security Policy headers that restrict which scripts, styles, and connections the browser allows. HTTP Strict Transport Security forcing HTTPS. X-Frame-Options denying iframe embedding. Rate limiting on every public-facing endpoint — checkout, contact form, newsletter signup, admin login. CSRF protection validating Origin and Referer headers. Input validation with strict schemas before any data touches the database. Audit logging on admin actions.
I also built a PDPA-compliant privacy policy that documents exactly what data is collected, why, how it's stored, who it's shared with, and how customers can exercise their rights. Not a template copied from the internet — a policy written specifically for this site's actual data flows, reviewed against the 2024 PDPA amendments.
Where AI Accelerated and Where I Stepped In
Claude Code accelerated the implementation dramatically. The admin dashboard — 15+ pages of CRUD interfaces, filters, search, status badges — would have taken weeks to build manually. With Claude, I described the data model and the UI patterns I wanted, reviewed the output, and iterated. Most of it was right on the first pass. The parts that weren't were always business logic edge cases: what happens when an order is partially refunded, how the stock count updates when a cancelled order is restored, what the stringing job status should be if the linked order is cancelled.
These edge cases are where I stepped in. Not because Claude can't handle conditional logic — it can. But because the correct behavior in these scenarios is a business decision, not a technical one. Should a cancelled order restore stock automatically? The client said yes. Should a stringing job be cancelled if the linked order is cancelled? The client said no — the racket is already in the shop. These decisions require understanding the business, not understanding code.
The Revenue Monster integration is where I did the most manual work. Claude had general knowledge of OAuth2 flows and webhook verification, but the specific implementation details — RSA signature generation, the exact header format Revenue Monster expects, the sandbox vs production URL switching — required reading the actual documentation and testing against the actual API. AI is trained on public knowledge. Niche Malaysian payment gateway integration details aren't well-represented in any training dataset.
What Shipping Looks Like
Shipping isn't deploying to Vercel and sending a link. Shipping is the client logging into their admin panel for the first time and understanding what they're looking at. It's the documentation that explains every feature. It's the 30-day support window where I fix anything that breaks in real-world usage. It's the handoff where the client owns the code, owns the data, and can hire any developer to maintain it if they want to.
One developer built this. Not a team. Not an agency. One person who understands the technology, understands the business, and has a tool that makes the execution faster without replacing the thinking. That's the model. That's what I'm proving is possible.
“A solo developer with the right tools and the right process can ship what used to require a team. The tools changed. The thinking didn't.”