Skip to content

Commit af2180f

Browse files
committed
feat: add subscriptions/payments migration + fix login form HTML structure
1 parent 4ce578a commit af2180f

2 files changed

Lines changed: 114 additions & 13 deletions

File tree

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
-- Migration 020: Create subscriptions and payment_transactions tables
2+
-- Required by billing endpoints: /billing/subscription, /billing/verify-payment,
3+
-- /billing/cancel, /billing/transactions, /billing/paddle-webhook
4+
-- Also adds subscription_start / subscription_end to companies for trial-guard.js
5+
6+
-- ═══════════════════════════════════════════════════════════════════
7+
-- 1. Subscriptions table — stores active/cancelled subscription state
8+
-- ═══════════════════════════════════════════════════════════════════
9+
10+
CREATE TABLE IF NOT EXISTS subscriptions (
11+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
12+
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
13+
plan TEXT NOT NULL DEFAULT 'trial', -- trial, starter, professional, enterprise
14+
status TEXT NOT NULL DEFAULT 'active', -- active, paused, cancelled, expired
15+
gateway TEXT, -- cashfree, paddle, manual
16+
gateway_subscription_id TEXT, -- external subscription ID
17+
gateway_customer_id TEXT, -- external customer ID
18+
currency TEXT DEFAULT 'INR',
19+
billing_cycle TEXT DEFAULT 'monthly', -- monthly, annual
20+
current_period_start TIMESTAMPTZ,
21+
current_period_end TIMESTAMPTZ,
22+
cancelled_at TIMESTAMPTZ,
23+
created_at TIMESTAMPTZ DEFAULT now(),
24+
updated_at TIMESTAMPTZ DEFAULT now(),
25+
tenant_id TEXT
26+
);
27+
28+
-- Index for fast lookup by company
29+
CREATE INDEX IF NOT EXISTS idx_subscriptions_company_id ON subscriptions(company_id);
30+
CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status);
31+
CREATE INDEX IF NOT EXISTS idx_subscriptions_tenant_id ON subscriptions(tenant_id);
32+
33+
-- ═══════════════════════════════════════════════════════════════════
34+
-- 2. Payment transactions — audit log for all payment events
35+
-- ═══════════════════════════════════════════════════════════════════
36+
37+
CREATE TABLE IF NOT EXISTS payment_transactions (
38+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
39+
company_id UUID NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
40+
subscription_id UUID REFERENCES subscriptions(id),
41+
gateway TEXT NOT NULL, -- cashfree, paddle
42+
gateway_order_id TEXT, -- external order/transaction ID
43+
amount NUMERIC(12, 2) NOT NULL DEFAULT 0,
44+
currency TEXT DEFAULT 'INR',
45+
status TEXT NOT NULL DEFAULT 'pending', -- pending, paid, failed, refunded
46+
plan TEXT,
47+
billing_cycle TEXT,
48+
metadata JSONB DEFAULT '{}', -- raw gateway response
49+
created_at TIMESTAMPTZ DEFAULT now(),
50+
updated_at TIMESTAMPTZ DEFAULT now(),
51+
tenant_id TEXT
52+
);
53+
54+
CREATE INDEX IF NOT EXISTS idx_payment_transactions_company_id ON payment_transactions(company_id);
55+
CREATE INDEX IF NOT EXISTS idx_payment_transactions_status ON payment_transactions(status);
56+
CREATE INDEX IF NOT EXISTS idx_payment_transactions_created_at ON payment_transactions(created_at DESC);
57+
58+
-- ═══════════════════════════════════════════════════════════════════
59+
-- 3. Add subscription columns to companies (for trial-guard.js)
60+
-- ═══════════════════════════════════════════════════════════════════
61+
62+
ALTER TABLE companies ADD COLUMN IF NOT EXISTS subscription_plan TEXT DEFAULT 'trial';
63+
ALTER TABLE companies ADD COLUMN IF NOT EXISTS subscription_start TIMESTAMPTZ DEFAULT now();
64+
ALTER TABLE companies ADD COLUMN IF NOT EXISTS subscription_end TIMESTAMPTZ;
65+
ALTER TABLE companies ADD COLUMN IF NOT EXISTS is_active BOOLEAN DEFAULT true;
66+
67+
-- ═══════════════════════════════════════════════════════════════════
68+
-- 4. RLS policies — tenant isolation
69+
-- ═══════════════════════════════════════════════════════════════════
70+
71+
ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;
72+
ALTER TABLE payment_transactions ENABLE ROW LEVEL SECURITY;
73+
74+
-- Subscriptions: service role full access, authenticated users read own
75+
CREATE POLICY "subscriptions_service_all" ON subscriptions
76+
FOR ALL USING (true) WITH CHECK (true);
77+
78+
CREATE POLICY "subscriptions_read_own" ON subscriptions
79+
FOR SELECT USING (
80+
company_id IN (
81+
SELECT company_id FROM users WHERE auth_id = auth.uid()
82+
)
83+
);
84+
85+
-- Transactions: service role full access, authenticated users read own
86+
CREATE POLICY "payment_transactions_service_all" ON payment_transactions
87+
FOR ALL USING (true) WITH CHECK (true);
88+
89+
CREATE POLICY "payment_transactions_read_own" ON payment_transactions
90+
FOR SELECT USING (
91+
company_id IN (
92+
SELECT company_id FROM users WHERE auth_id = auth.uid()
93+
)
94+
);
95+
96+
-- ═══════════════════════════════════════════════════════════════════
97+
-- 5. Backfill: set subscription_start for existing companies
98+
-- ═══════════════════════════════════════════════════════════════════
99+
100+
UPDATE companies
101+
SET subscription_start = created_at,
102+
subscription_end = created_at + INTERVAL '2 days'
103+
WHERE subscription_start IS NULL AND subscription_plan = 'trial';

platform/login.html

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -588,19 +588,17 @@ <h2>Sign In</h2>
588588

589589

590590

591-
<div style="text-align:center;color:#9ca3af;font-size:13px;margin-bottom:12px;"><button type="button"
592-
onclick="signInWithGoogle()"
593-
style="width:100%;padding:11px;margin-bottom:12px;border:1px solid #dadce0;border-radius:8px;background:#fff;color:#3c4043;font-size:14px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:10px;font-family:inherit;">
594-
<img src="https://www.gstatic.com/firebasejs/ui/2.0.0/images/auth/google.svg" width="20">
595-
Continue with Google
596-
</button>
597-
<div style="text-align:center;color:#9ca3af;font-size:13px;margin-bottom:12px;">— or continue with
598-
email —</div>
599-
<button type="submit" class="submit-btn" id="submitBtn">
600-
<span class="spinner" id="spinner"></span>
601-
<i class="fas fa-sign-in-alt" id="btnIcon"></i>
602-
<span id="btnText">Sign In</span>
603-
</button>
591+
<button type="button" onclick="signInWithGoogle()"
592+
style="width:100%;padding:11px;margin-bottom:12px;border:1px solid #dadce0;border-radius:8px;background:#fff;color:#3c4043;font-size:14px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:10px;font-family:inherit;">
593+
<img src="https://www.gstatic.com/firebasejs/ui/2.0.0/images/auth/google.svg" width="20">
594+
Continue with Google
595+
</button>
596+
<div style="text-align:center;color:#9ca3af;font-size:13px;margin-bottom:12px;">— or continue with email —</div>
597+
<button type="submit" class="submit-btn" id="submitBtn">
598+
<span class="spinner" id="spinner"></span>
599+
<i class="fas fa-sign-in-alt" id="btnIcon"></i>
600+
<span id="btnText">Sign In</span>
601+
</button>
604602
</form>
605603

606604
<div class="auth-links">

0 commit comments

Comments
 (0)