← Design system

Architecture plan

निष्ठा

Where this is going, and on what

Draft — Sept 2026 Pre-deploy
Schema + webhook logic — built, 20/20 tests Square sandbox token — expired (401) Hosting — not chosen Auth — not built Nothing deployed

Three decisions

What has to be settled before anything ships

Everything built so far — the schema, the reference-id link, the webhook handler and its earn/redeem/refund logic — runs entirely locally. None of it has anywhere to live yet, no one can log into it, and it doesn't know how to talk to the site people will actually land on. These three calls are what unblock deployment, in the order they were raised.

Hosting

Unblocks webhooks + OAuth

Render, on a paid always-on tier — not the free one.

Why not free-tier: a sleeping dyno delays webhook delivery, and a delayed stamp reads to a customer as a broken product, not a slow one. node:sqlite also needs a persistent disk, which rules out serverless hosts like Vercel or Netlify outright — their filesystems are wiped between invocations.

SQLite-with-a-volume, or Postgres now

Open — your call

Leaning Postgres now rather than at the Phase 2 gate.

Why move it up: server/db.js is deliberately the only file that touches the database, and there's no real data yet to migrate — this is the cheapest this move will ever be. Doing it now also sidesteps the whole "does this host handle a persistent SQLite file correctly" question for good, rather than deferring a known Phase 2 task under pilot-launch time pressure.

Authentication

Two audiences, two answers

Cafes sign in with Square OAuth — no separate password system. Customers get no login at all, by design.

Why one flow, not two: Square OAuth is already Phase 2 work (a cafe connects its own account instead of a pasted token) — using it as the dashboard login too closes the "no auth at all" gap and that OAuth item in a single build, instead of standing up a parallel email/password system a non-technical owner will eventually need reset. Customer auth would break the product's actual pitch: the card code is the only credential, by design.

Frontend integration

One deploy, not two

Keep it single-origin — the same Express app already serves public/* and /api/*.

Why not split it out: a separate frontend host means CORS, a second deploy pipeline, and a second place webhooks or auth could quietly drift out of sync. If the three-column hero becomes the real marketing site, that's a file merge into public/ — not new infrastructure.

System shape

One app, one origin, one database

Square is the only external system of record for identity and payment. Everything niṣṭhā owns — the stamp ledger, the reward state, the webhook audit trail — lives in one place.

Browser

Customer / cafe

Signup, wallet card, dashboard — static pages, no build step

Till hardware

Square checkout

Staff scan the card's barcode inside Square's own POS

https, same origin

Render — always on

Express app (server/index.js)

public/* static · /api/* product routes · /api/webhooks/square — signature-verified, 200-then-process

reads / writes webhook push, API calls

This app owns it

Postgres

cafes · customers · transactions · webhook_events — the append-only ledger

Square owns it

Customers, Payments, Orders

Identity and money never live in niṣṭhā's database

Data model

What moves, and what doesn't

The four original tables plus the two added for the webhook flow carry over to Postgres unchanged in shape — this is a storage-engine swap, not a redesign.

Already shipped, tested against sqlite
TableCarries
cafesLoyalty rule, brand, square_loyalty_discount_id — the id that tells a real redemption apart from a comped drink
customerscard_code — the same string as Square's referenceId — plus live stamp/reward counts
transactionsAppend-only ledger: earn / redeem / reverse, linked to the Square payment that caused it
webhook_eventsEvery inbound event, by id — dedupe, audit trail, and retry queue in one table

Two more arrive with the auth decision above, not the database swap: a cafes.square_oauth_token (encrypted at rest) replacing the pasted access token, and a session store for the dashboard login itself.

Rollout

Mapped onto the four phases already agreed

01

Prove it works against real Square

  • Schema, reference id, webhook endpoint, earn/redeem/refund logic — built and tested
  • Barcode formats on the card — not started, no blockers
  • Deploy to Render with real HTTPS — blocked on the hosting call above
  • Run the full loop against sandbox, including a voided sale and a refund — blocked on a fresh sandbox token

Gate: a stamp lands on a phone because a real Square transaction completed — not a button click.

02

Safe to put in front of a cafe

  • Square OAuth as the dashboard login — closes the auth gap and the pasted-token gap together
  • Postgres with backups — proposed to happen now, at Phase 1, not deferred to here
  • Privacy policy, consent capture, deletion process
  • Error tracking and uptime monitoring

Gate: you'd be comfortable if a stranger's phone number were in the database tonight.

03

One pilot cafe

  • Real till, real scanner, real queue — the architecture above gets its first live test
  • Watch for staff skipping the scan during a rush — the named risk most likely to actually happen

Gate: a month of real use where regulars trust their stamp count.

04

Make it sellable

  • Pricing, informed by the pilot rather than guessed
  • Internal admin view over the webhook_events table — expired tokens, failed deliveries, missing punches
  • Real Apple/Google Wallet passes; gift cards on the same card and scan

Immediate next steps

In order

  1. Confirm the Postgres-now call. Everything below assumes it; if SQLite-with-a-volume is preferred instead, the hosting step changes, not the rest.you
  2. Pull a fresh Square sandbox access token from the Developer Console — the current one is 401ing.you
  3. Stand up Render — web service on an always-on tier, plus Postgres if confirmed — and point SQUARE_WEBHOOK_NOTIFICATION_URL at it.build
  4. Run the sandbox verification loop from the test plan: signup → till scan → stamp → refund reversal, against the real deployed endpoint.build
  5. Build Square OAuth as the cafe dashboard login, retiring the pasted-token field.build