Skip to content

R-02 PRE-BRIEF NOTES — THE TILL

Stripe + Checkout + Entitlement

Locked 18 March 2026 — write brief when R-01 closes


Architecture Decisions (Locked)

Stripe is the ledger. We surface it, we don't duplicate it. - All financial data lives in Stripe - We query Stripe API to display in L1/L2/L3/L4 panels - Never store card details, invoice PDFs, or replicate Stripe data - Stripe Customer Portal handles self-service billing for L3/L4

Legal entity on all invoices: United Central Colleges of Australia Pty Ltd ABN 59 168 872 535 Trading as: RTOpacks

Currency: AUD GST: Enable Stripe Tax — auto-calculates and remits 10% GST for Australian buyers. Zero code.

Stripe account structure: - Org: RTO Packs (Stripe Connect) - Trading account: RTOPacks.com.au (live keys, AUD, settles to Pty Ltd bank) - Parent entity: United Central Colleges of Australia Pty Ltd


The Dual Auth Gate (Locked)

You can browse anonymously. You cannot buy anonymously.

Before checkout: 1. Enter email + mobile 2. Verify both (same dual auth flow as R-01 — Auth0 email OTP + Twilio Verify SMS) 3. Account created or existing account recognised 4. Proceed to Stripe checkout as identified user

Session: 30-day verified session cookie. Verify once, buy multiple times within 30 days without re-verifying.

If already L3 tenant: Skip gate, go straight to checkout.

Every successful purchase: - Creates L4 account if one doesn't exist (JIT provisioning) - Sends confirmation to email + SMS - Entitlement record created in engine-db - Lands in L4 dashboard


Finance Tab Per Layer (Locked)

L1 — Platform view (Tim) Total revenue across all tenants. MRR, total processed, by tenant. Stripe Connect org level. Engine P&L.

L2 — UCCA staff Read-only. Total dollars through gateway. No tenant granularity. Health check, not accounting.

L3 — RTO admin Their purchases, invoices, billing history. Stripe Customer Portal link — self-service. We surface it, Stripe hosts it.

L4 — Learner / direct buyer Their purchase history. What they bought, when, download/access link. Stripe Customer Portal link for receipt download.


Verification Gate Copy (Locked)

Headline: "Your content is built to your specification. We verify who you are so it stays yours."

Context: - Appears on the dual auth gate screen before checkout - Does NOT say "security" or "verification" or "identity" - Positions verification as IP protection, not a barrier - No legal language, no alarm bells - Communicates that content is customised = higher perceived value

Supporting copy (draft): "The training context, learner descriptors, and organisational details you provide are your IP. We take that seriously."


Purchases Table — engine-db (Locked)

CREATE TABLE IF NOT EXISTS purchases (
  id                      TEXT PRIMARY KEY,
  tenant_id               TEXT REFERENCES tenants(id), -- NULL for guest (shouldn't happen post-R-02)
  user_id                 TEXT REFERENCES users(id),
  stripe_customer_id      TEXT NOT NULL,
  stripe_payment_intent_id TEXT UNIQUE NOT NULL,
  stripe_invoice_id       TEXT,
  product_id              TEXT NOT NULL,
  price_id                TEXT NOT NULL,
  amount_aud              INTEGER NOT NULL,            -- in cents
  gst_aud                 INTEGER NOT NULL DEFAULT 0, -- in cents
  status                  TEXT NOT NULL CHECK (status IN ('pending','paid','refunded','disputed')),
  entitlement_json        TEXT NOT NULL DEFAULT '{}', -- what they get
  delivery_status         TEXT NOT NULL DEFAULT 'pending', -- stub for now
  created_at              INTEGER NOT NULL DEFAULT (unixepoch()),
  paid_at                 INTEGER,
  refunded_at             INTEGER
);

CREATE INDEX IF NOT EXISTS idx_purchases_tenant    ON purchases(tenant_id);
CREATE INDEX IF NOT EXISTS idx_purchases_user      ON purchases(user_id);
CREATE INDEX IF NOT EXISTS idx_purchases_stripe_pi ON purchases(stripe_payment_intent_id);
CREATE INDEX IF NOT EXISTS idx_purchases_status    ON purchases(status);

Migration from ops-db (Locked)

Current state: - Stripe webhook writes to ops-db (old architecture) - Checkout route on ops-v2 (ops.ucca.online) - Keys on ucca-ops Worker secrets

R-02 migration: - Webhook stays at ops.ucca.online/api/stripe/webhook but writes to engine-db purchases table - Checkout flow wired to L4 user session from R-01 dual auth gate - Stripe Tax enabled on RTOPacks.com.au account - ops-db Stripe tables deprecated (not deleted, just no longer written to)


Blockers — None

R-02 is fully scoped. Write brief when Alex confirms R-01 closed.


R-02 PRE-BRIEF · UCCA Inc · 18 March 2026 "You can browse anonymously. You cannot buy anonymously."