The Matrix Hours
Shipped
Building a Budget-Constrained Commerce System That Behaves Like a Vertical SaaS
A case study in product thinking, systems design, security boundaries, and staged architecture
On the surface, it looks like a premium watch storefront. Underneath, it is really a constrained commerce operating system for a small business that needs to look trustworthy, sell credibly, and stay maintainable on almost no budget. That distinction matters, because the real problem was never "how do I make a nice e-commerce UI?" The real problem was:
How do I create a system that can capture demand, preserve trust, support manual payment behavior common in Bangladesh, and still leave a clean migration path toward a more rigorous order pipeline later?
That question shaped every architectural decision.
1. The Problem I Was Actually Solving
The business did not need a generic Western-style online store with Stripe, user accounts, recommendation engines, and operational overhead from day one. That would have been architectural theater.
What it needed was a system that solved five concrete problems:
- Product discovery had to feel premium enough to justify high-ticket watch purchases.
- Checkout had to match local buying behavior, where trust is often built through direct chat and manual payment confirmation.
- The owner needed a workflow that could start simple now and harden later without rewriting the entire frontend.
- The system had to run close to free at MVP stage.
- Security could not be postponed just because the budget was low.
This is where I stopped thinking of it as "a website" and started thinking of it as a vertical micro-SaaS for one business domain: assisted commerce.
The product is not yet multi-tenant, but the architecture is built with SaaS instincts:
- clear separation between catalog, checkout intent, order state, and admin operations
- explicit trust boundaries
- reversible infrastructure choices
- progressive hardening instead of all-at-once complexity
2. The Product Thesis
My product thesis was simple:
Do not automate the wrong layer first.
For a small business selling premium watches, the first bottleneck is not payment orchestration. It is trust, operational clarity, and product presentation. So I designed the system in phases:
Phase A: revenue-first
- Premium storefront
- Browser-side cart
- Google Sheets as a non-technical product CMS
- WhatsApp / Messenger assisted checkout
Phase B: operational hardening
- Supabase-backed orders
- Screenshot-based payment proof
- Admin-side verification workflow
- stock mutation only after verification
Phase C: controlled automation
- stronger API boundaries
- dashboard authentication
- more rigorous rate limiting
- event-driven notifications after admin confirmation
This staged model kept the current system shippable while keeping the future system structurally possible.
3. Why The Architecture Looks "In Between"
One of the most interesting parts of this repository is that it reflects a real architectural transition.
There is an older low-cost plan centered around Google Sheets plus WhatsApp checkout. There is also a newer, more serious target architecture centered around Supabase orders, private screenshot storage, row-level security, admin verification, and server-side enforcement.
That is not accidental drift. It is the exact shape of a product maturing under constraint.
| Layer | Current state | Target state |
|---|---|---|
| Catalog source | Google Sheets CSV with browser-side normalization and TTL cache | Supabase-backed product records with stricter server-side guarantees |
| Checkout | WhatsApp / Messenger intent handoff | Structured order creation with manual payment proof verification |
| Order truth | Human conversation thread | Database-backed order state machine |
| Admin operations | Manual owner workflow outside the app | Protected /dashboard with authenticated review and stock control |
| Security center of gravity | Client-side simplification plus middleware headers | Data invariants, RLS, validated uploads, guarded admin APIs |
In other words, this codebase is carrying two truths at once:
- the current revenue path is intentionally lightweight
- the desired operational path is intentionally stricter
Senior engineers will recognize this pattern immediately: the important design work is not choosing one forever. It is designing seams so the second architecture can replace the first without detonating the user-facing layer.
The seam I cared about most was the domain contract between:
- product source
- cart intent
- checkout intent
- order persistence
As long as those boundaries stay coherent, I can swap the backing implementation from:
Google Sheets CSV -> browser cache -> chat checkout
to:
Supabase tables -> validated API routes -> verification-first order lifecycle
without redesigning the entire storefront.
4. Core System Design
4.1 Frontend shell
The frontend is built with Next.js App Router and React 19. That choice was less about trend-chasing and more about preserving future optionality:
- route-level composition is clean
- metadata and SEO are built-in
- middleware support exists for future security enforcement
- the app can evolve from mostly static catalog pages into a more stateful order system without changing frameworks
The current UI layer is heavily componentized. Catalog browsing, product detail pages, cart behavior, overlay interactions, and page chrome are all isolated enough that business logic changes do not force a design rewrite.
4.2 Product data layer
Instead of forcing the owner into a database-backed CMS immediately, I used Google Sheets published as CSV. That sounds simple, but the interesting part is not the sheet. The interesting part is the anti-corruption layer around it.
src/lib/sheets.js does several important things:
- parses CSV into typed-ish product objects
- normalizes booleans, numbers, and tag arrays
- maintains a browser session cache with TTL
- supports stale-cache fallback if remote fetch fails
- allows forced refresh via query param
This means the sheet is not coupled directly to the UI. It is treated as an unstable external data source and normalized before it touches the rest of the app.
That is a small but very important systems decision.
4.3 Caching model
The browser cache is session-based, not permanent. That was deliberate.
For this kind of catalog, bad stale state is worse than slightly slower first load. I preferred:
- fast repeated views within a session
- graceful degradation if Sheets is unavailable
- no indefinite persistence across unrelated sessions
This is especially relevant for shared devices and price-sensitive inventory. Session-scoped caching reduces the risk of users returning days later with invalid assumptions about price or availability.
4.4 Cart state model
The cart is also stored in sessionStorage, then synchronized through React context.
Again, this is not just an implementation detail. It reflects product logic:
- this is guest checkout
- there are no customer accounts
- cart state should be ephemeral
- stale carts should decay naturally
That design avoids the complexity of identity, server-side cart reconciliation, and cross-device expectations long before the business actually benefits from those features.
4.5 Checkout as intent serialization
The current checkout model is not a transaction processor. It is an intent serializer.
The function in src/lib/checkout.js transforms cart state into a deterministic message payload for WhatsApp or Messenger. From a systems perspective, that is valuable because it preserves:
- product names
- quantities
- localized currency formatting
- human-readable summary
The output is not "payment completed." It is "buying intent preserved in a low-friction channel."
That is a very different, and much more honest, contract.
5. The Deeper Engineering Problem: Trust, Not Just Transactions
One of the major patterns I recognized early is that payment systems are really trust systems wearing a financial costume.
If I had integrated a payment gateway too early, I would have increased technical complexity without solving the business's actual trust model. For this market and budget tier, trust is often constructed through:
- recognizable brand presentation
- direct human communication
- visible payment proof
- seller-side manual verification
That pushed me toward a verification-first architecture rather than an auto-capture architecture.
The future Supabase design reflects that:
- customer submits order details
- customer uploads payment proof
- order remains
pending - owner verifies proof manually
- stock changes only after verification
That sequence is not a shortcut around payments. It is the domain model.
The technical insight here is that manual verification is not merely a temporary workaround. It is a domain-aligned state machine.
6. Security Model And Why I Treated It Seriously Early
Low budget is not an excuse for loose security. It just changes where security work has to be concentrated.
6.1 Customer-side simplification as a security strategy
I intentionally avoided customer accounts. This reduces:
- password storage risk
- account recovery complexity
- session fixation concerns
- attack surface around login and registration flows
That is a product simplification, but it is also a security reduction in system entropy.
6.2 Header-level protections already present
The middleware currently applies a baseline set of defensive headers:
X-Frame-Options: DENYX-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-origin- a restrictive
Content-Security-Policy
This matters because image-heavy storefronts often become loose with CSP in the name of convenience. I chose to allow only the image and connection sources the current architecture needs, primarily Cloudinary, Unsplash, and Google Docs.
6.3 Planned admin boundary
The future-sensitive part of the system is not public browsing. It is /dashboard.
That boundary should be protected with:
- single-password gate from
DASHBOARD_PASSWORD - server-side verification only
httpOnly,Secure,SameSite=Strictcookies- rate limiting on login attempts
- mandatory session checks on all dashboard APIs
This is intentionally narrow authentication. Not enterprise auth. Not user auth. Just a hardened operator boundary.
6.4 Database-side control model
The Supabase schema work already shows the security direction I wanted:
- RLS enabled on
products,orders, andorder_items - public read access only where catalog visibility requires it
- service-role-only access for sensitive reads and writes
- unique
transaction_idconstraint - optional
idempotency_keyfor request de-duplication - private screenshot bucket
The important pattern is that security is being pushed downward toward data invariants, not left only at the UI.
6.5 File upload threat model
Payment screenshots look harmless until you think like an attacker.
A screenshot upload pipeline must assume:
- MIME spoofing
- oversized uploads for denial-of-service
- SVG payloads as script delivery vectors
- object enumeration if storage is public
That is why the intended system rules include:
- private storage bucket
- server-side type validation
- magic-byte verification
- explicit size limits
- SVG rejection
These are small-business concerns only if you do not think adversarially. In practice, they are standard hygiene.
7. Pattern Recognition: The Part That Matters More Than Any Single Tool
The biggest advantage I had while building this was not a library choice. It was pattern recognition.
Pattern 1: Free-tier systems fail from hidden coupling, not from lack of power
Cheap systems usually break because everything becomes accidentally dependent on everything else:
- UI knows too much about storage format
- checkout assumes a future payment model
- inventory assumes real-time truth where none exists
- docs describe a world the code does not yet live in
So I kept drawing hard boundaries between display, data fetch, cart intent, and order lifecycle.
Pattern 2: Spreadsheets are excellent until you need invariants
Google Sheets is a fantastic non-technical CMS. It is terrible as a source of strong guarantees.
Sheets gives you:
- low operational cost
- client familiarity
- easy edits
Sheets does not give you:
- transactional integrity
- uniqueness guarantees
- concurrent update safety
- formal schema enforcement
That is why I treated Sheets as a launch mechanism, not the final source of truth.
Pattern 3: Documentation drift is an architectural smell
This repo carries traces of multiple architectural phases: WhatsApp-only checkout, spreadsheet-driven catalog, and future Supabase order verification.
That drift taught me something important: documentation is not secondary to architecture. It is part of architecture. If the docs and the code disagree long enough, engineers start building against fictional system boundaries.
Pattern 4: React state modeling gets exposed brutally in modern linting
Next.js 16 and React 19 are less forgiving of effect-driven state choreography than older stacks.
Running lint exposed a specific pattern in the current codebase:
- multiple places derive state synchronously inside effects
- some components still reflect earlier context shapes
- hydration and mount-guard logic is doing more work than it should
That is not just a lint nuisance. It reveals that the state graph wants simplification:
- derive more from inputs
- store less duplicated state
- reduce effect-driven synchronization
Modern React tooling is useful here because it surfaces architectural friction, not just style mistakes.
Pattern 5: The real migration unit is the interface, not the database
A lot of junior implementations think migration means "move to Postgres later."
That is incomplete.
The real migration unit is the contract that the UI consumes. If I keep the frontend dependent on a stable domain shape, I can replace:
- Google Sheets parsing
- Supabase queries
- API routes
- storage backends
without forcing a total UI rewrite.
That is one of the core SaaS patterns I intentionally designed toward.
8. Technical Hassles I Faced So Far
This project became interesting precisely because the problems were not surface-level.
8.1 Architecture drift during product evolution
The product started from a pure low-cost plan and then moved toward a more controlled order pipeline. That created a real engineering tension:
- keep the current system simple enough to ship
- but avoid writing it in a way that blocks the future system
This is where many projects accumulate rewrite debt. I tried to reduce that by isolating data access and preserving a domain-shaped checkout boundary.
8.2 State model inconsistencies
There are places in the current implementation where the context shape and consuming components are out of sync. This is a common symptom when a system evolves quickly across multiple architectural ideas.
It taught me to look for a recurring smell:
if components need mount guards, effect-based hydration, and duplicated derived state everywhere, the real issue is usually model shape, not rendering.
8.3 React 19 / Next 16 discipline changes
Linting exposed several setState-inside-useEffect patterns that older codebases often tolerate but newer React guidance pushes against.
This forced a more senior question:
which state is truly state, and which state is just a derived projection of existing inputs?
That question matters because effect-heavy code looks functional at first, then becomes fragile during hydration, route transitions, and future concurrency features.
8.4 Caching against an eventually-consistent spreadsheet source
Google Sheets feels immediate to the human editor, but published CSV delivery is not a strong-consistency channel. There is propagation delay, caching behavior outside my control, and browser-side cache behavior layered on top.
So the challenge was not just "fetch CSV." The challenge was designing a failure-tolerant read path that could:
- load fast
- survive temporary fetch failure
- avoid showing obviously broken UI
- avoid pretending the data is strongly consistent when it is not
8.5 Designing security without premium infrastructure
It is easy to design secure systems when you assume Redis, paid auth tooling, paid monitoring, dedicated queueing, and multiple backend services.
It is much harder when the rules are:
- no paid infrastructure
- no customer accounts
- no complex gateway integration yet
- still no compromise on trust and security posture
That forced more care around:
- row-level permissions
- idempotency
- duplicate transaction detection
- upload restrictions
- admin-only sensitive reads
8.6 Preserving aesthetic ambition without undermining performance
The brand needs premium motion, texture, and visual confidence. But premium interfaces are easy to overbuild.
The engineering challenge was balancing:
- strong visual identity
- image-heavy pages
- animation polish
- mobile performance
- low operational cost
This is where product design and systems design stop being separate disciplines.
9. How I Kept It Budget-Friendly Without Making It Disposable
The cheapest architecture is not the one with the fewest services. It is the one that minimizes irreversible decisions.
That is the principle I used.
What made it cost-efficient
- Google Sheets instead of a paid CMS
- Vercel-style deployment model instead of custom server management
- session-based cart instead of account-backed cart infrastructure
- WhatsApp/Messenger intent checkout instead of immediate payment gateway integration
- Cloud CDN usage for product imagery
- manual verification instead of premature automation
What made it not feel cheap
- strong visual presentation
- deliberate data normalization layer
- security headers and planned RLS posture
- clean separation between public catalog behavior and future sensitive operations
- migration path already considered during MVP decisions
This is the difference between a budget build and a fragile build. I was aiming for the first, not the second.
10. Future Development Space I Intentionally Left Open
I do not like shipping MVPs that can only scale by being thrown away.
So I left room for future development in the places that matter most.
10.1 Product source migration
The storefront should be able to move from Google Sheets to Supabase-backed products with minimal UI disruption because the data-fetch layer is already abstracted.
10.2 Verification-first order pipeline
The future order system can preserve the current business behavior while adding stronger guarantees:
- customer submits structured order
- payment screenshot stored privately
- transaction ID validated for uniqueness
- admin verifies manually
- stock decremented atomically after verification
This keeps the business process familiar while improving system correctness.
10.3 Dashboard hardening
The admin side can grow into:
- authenticated order review
- low-stock alerts
- audit-friendly order notes
- private asset viewing
- controlled status transitions
10.4 Notifications, but only after the model is trustworthy
I deliberately did not prioritize customer notifications early. Sending wrong automated updates is worse than sending none.
The correct order is:
- establish reliable order truth
- secure the admin verification flow
- then add outbound communication
That sequencing is boring in the best possible way.
10.5 Better state modeling
The current codebase has clear opportunities to reduce effect-heavy client logic and tighten context contracts. That work will improve maintainability more than adding surface features would.
11. What This Project Says About How I Build
This project represents how I think as both a developer and a system designer.
I do not start from the most impressive stack. I start from the actual business risk.
I look for:
- where the real trust boundary lives
- which complexity is structural versus performative
- what can stay manual for now
- what absolutely must be designed correctly even before scale
- how to make today's shortcut become tomorrow's migration seam instead of tomorrow's rewrite
The Matrix Hours taught me that strong systems work is often less about adding infrastructure and more about staging certainty.
I did not try to force a polished fiction of "full e-commerce." I designed a system that respects the market, the operator, the budget, and the likely evolution path.
That is the kind of product engineering I want to keep doing:
- honest about constraints
- careful with security
- deliberate with abstraction
- aesthetically ambitious
- operationally grounded
12. Closing Reflection
If someone only looks at the UI, they might see a watch store.
If a senior engineer looks more carefully, they will see the real project:
- a low-burn commerce architecture
- a staged migration plan
- a trust-centered checkout model
- a security posture designed to tighten over time
- and a codebase that is already teaching me where the next level of rigor needs to happen
That is why this project matters to me.
It is not just a storefront I built.
It is a case study in learning how to turn constraint into architecture.