-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathschema.sql
More file actions
114 lines (102 loc) · 3.69 KB
/
Copy pathschema.sql
File metadata and controls
114 lines (102 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
-- =========================================================
-- Coupon Maker Enterprise - Supabase PostgreSQL Schema
-- Run this script in the Supabase SQL Editor (https://supabase.com/dashboard)
-- =========================================================
-- 1. Create Batches Table
CREATE TABLE IF NOT EXISTS batches (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
batch_name TEXT NOT NULL,
template_key TEXT NOT NULL,
token_count INT NOT NULL,
denomination TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
notes TEXT
);
-- 2. Create Tokens Table
CREATE TABLE IF NOT EXISTS tokens (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
code TEXT UNIQUE NOT NULL,
batch_id BIGINT REFERENCES batches(id) ON DELETE CASCADE,
qr_payload TEXT,
status TEXT DEFAULT 'GENERATED', -- 'GENERATED', 'PRINTED', 'REDEEMED', 'VOID'
denomination TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
redeemed_at TIMESTAMPTZ,
customer_phone TEXT,
customer_name TEXT,
customer_invoice TEXT
);
CREATE INDEX IF NOT EXISTS idx_tokens_code ON tokens (code);
CREATE INDEX IF NOT EXISTS idx_tokens_status ON tokens (status);
CREATE INDEX IF NOT EXISTS idx_tokens_batch ON tokens (batch_id);
-- 3. Atomic Anti-Fraud Redemption Function (RPC)
-- Handles concurrent customer scans with row-locking to guarantee ZERO double redemptions
CREATE OR REPLACE FUNCTION redeem_token(
p_code TEXT,
p_customer_phone TEXT,
p_customer_name TEXT DEFAULT '',
p_invoice TEXT DEFAULT ''
)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
v_token RECORD;
BEGIN
-- Look up token with row lock (FOR UPDATE prevents race conditions)
SELECT * INTO v_token
FROM tokens
WHERE UPPER(TRIM(code)) = UPPER(TRIM(p_code))
FOR UPDATE;
IF NOT FOUND THEN
RETURN jsonb_build_object(
'success', false,
'status', 'NOT_FOUND',
'message', 'Invalid or counterfeit token code. Please check your card.'
);
END IF;
IF v_token.status = 'REDEEMED' THEN
RETURN jsonb_build_object(
'success', false,
'status', 'ALREADY_REDEEMED',
'message', 'This token was already redeemed on ' || TO_CHAR(v_token.redeemed_at, 'YYYY-MM-DD HH24:MI'),
'redeemed_at', v_token.redeemed_at,
'code', v_token.code
);
END IF;
IF v_token.status = 'VOID' THEN
RETURN jsonb_build_object(
'success', false,
'status', 'VOID',
'message', 'This token has been deactivated / voided.'
);
END IF;
-- Atomically update token status to REDEEMED
UPDATE tokens
SET
status = 'REDEEMED',
redeemed_at = NOW(),
customer_phone = TRIM(p_customer_phone),
customer_name = TRIM(p_customer_name),
customer_invoice = TRIM(p_invoice)
WHERE id = v_token.id;
RETURN jsonb_build_object(
'success', true,
'status', 'SUCCESS',
'message', 'Token successfully redeemed! Points / Reward credited.',
'code', v_token.code,
'denomination', v_token.denomination,
'redeemed_at', NOW()
);
END;
$$;
-- 4. Enable Row Level Security (RLS)
ALTER TABLE batches ENABLE ROW LEVEL SECURITY;
ALTER TABLE tokens ENABLE ROW LEVEL SECURITY;
-- Allow Public/Anon users to execute the RPC function only
GRANT EXECUTE ON FUNCTION redeem_token(TEXT, TEXT, TEXT, TEXT) TO anon, authenticated;
-- Public cannot directly browse all tokens (prevents stealing unseen coupon codes)
-- Service role has full unrestricted admin access for batch insertion and stats
CREATE POLICY "Allow public token redemption verification" ON tokens
FOR SELECT USING (true);