-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathschema-d1.sql
More file actions
305 lines (283 loc) · 10 KB
/
schema-d1.sql
File metadata and controls
305 lines (283 loc) · 10 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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
-- ============================================================
-- MERCHANT DATABASE SCHEMA (D1 / SQLite)
-- Single-tenant: one database per store instance
-- Multi-tenancy handled at infrastructure layer (WFP/separate deployments)
-- ============================================================
-- API Keys
CREATE TABLE IF NOT EXISTS api_keys (
id TEXT PRIMARY KEY,
key_hash TEXT NOT NULL UNIQUE,
key_prefix TEXT NOT NULL,
role TEXT NOT NULL CHECK (role IN ('public', 'admin')),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Products
CREATE TABLE IF NOT EXISTS products (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT DEFAULT '',
image_url TEXT,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'draft')),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Variants (SKUs)
CREATE TABLE IF NOT EXISTS variants (
id TEXT PRIMARY KEY,
product_id TEXT NOT NULL REFERENCES products(id),
sku TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
price_cents INTEGER NOT NULL,
currency TEXT NOT NULL DEFAULT 'USD',
weight_g INTEGER NOT NULL,
dims_cm TEXT,
image_url TEXT,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'draft')),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Inventory
CREATE TABLE IF NOT EXISTS inventory (
id TEXT PRIMARY KEY,
sku TEXT NOT NULL UNIQUE,
on_hand INTEGER NOT NULL DEFAULT 0,
reserved INTEGER NOT NULL DEFAULT 0,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Inventory Logs
CREATE TABLE IF NOT EXISTS inventory_logs (
id TEXT PRIMARY KEY,
sku TEXT NOT NULL,
delta INTEGER NOT NULL,
reason TEXT NOT NULL CHECK (reason IN ('restock', 'correction', 'damaged', 'return', 'sale', 'release')),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Carts
CREATE TABLE IF NOT EXISTS carts (
id TEXT PRIMARY KEY,
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'checked_out', 'expired')),
customer_email TEXT NOT NULL,
currency TEXT NOT NULL DEFAULT 'USD',
stripe_checkout_session_id TEXT,
discount_code TEXT,
discount_id TEXT REFERENCES discounts(id),
discount_amount_cents INTEGER DEFAULT 0,
expires_at TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Cart Items
CREATE TABLE IF NOT EXISTS cart_items (
id TEXT PRIMARY KEY,
cart_id TEXT NOT NULL REFERENCES carts(id),
sku TEXT NOT NULL,
title TEXT NOT NULL,
qty INTEGER NOT NULL,
unit_price_cents INTEGER NOT NULL
);
-- Orders
CREATE TABLE IF NOT EXISTS orders (
id TEXT PRIMARY KEY,
customer_id TEXT REFERENCES customers(id),
number TEXT NOT NULL UNIQUE,
status TEXT NOT NULL DEFAULT 'paid' CHECK (status IN ('pending', 'paid', 'processing', 'shipped', 'delivered', 'refunded', 'canceled')),
customer_email TEXT NOT NULL,
shipping_name TEXT,
shipping_phone TEXT,
ship_to TEXT,
subtotal_cents INTEGER NOT NULL,
tax_cents INTEGER NOT NULL,
shipping_cents INTEGER NOT NULL DEFAULT 0,
total_cents INTEGER NOT NULL,
currency TEXT NOT NULL DEFAULT 'USD',
discount_code TEXT,
discount_id TEXT REFERENCES discounts(id),
discount_amount_cents INTEGER DEFAULT 0,
tracking_number TEXT,
tracking_url TEXT,
shipped_at TEXT,
stripe_checkout_session_id TEXT,
stripe_payment_intent_id TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Order Items
CREATE TABLE IF NOT EXISTS order_items (
id TEXT PRIMARY KEY,
order_id TEXT NOT NULL REFERENCES orders(id),
sku TEXT NOT NULL,
title TEXT NOT NULL,
qty INTEGER NOT NULL,
unit_price_cents INTEGER NOT NULL
);
-- Refunds
CREATE TABLE IF NOT EXISTS refunds (
id TEXT PRIMARY KEY,
order_id TEXT NOT NULL REFERENCES orders(id),
stripe_refund_id TEXT NOT NULL,
amount_cents INTEGER NOT NULL,
status TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Discounts
CREATE TABLE IF NOT EXISTS discounts (
id TEXT PRIMARY KEY,
code TEXT UNIQUE,
type TEXT NOT NULL CHECK (type IN ('percentage', 'fixed_amount')),
value INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive')),
min_purchase_cents INTEGER DEFAULT 0,
max_discount_cents INTEGER,
starts_at TEXT,
expires_at TEXT,
usage_limit INTEGER,
usage_limit_per_customer INTEGER DEFAULT 1,
usage_count INTEGER NOT NULL DEFAULT 0,
stripe_coupon_id TEXT,
stripe_promotion_code_id TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Discount Usage
CREATE TABLE IF NOT EXISTS discount_usage (
id TEXT PRIMARY KEY,
discount_id TEXT NOT NULL REFERENCES discounts(id),
order_id TEXT NOT NULL REFERENCES orders(id),
customer_email TEXT NOT NULL,
discount_amount_cents INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Customers
CREATE TABLE IF NOT EXISTS customers (
id TEXT PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
name TEXT,
phone TEXT,
password_hash TEXT,
email_verified_at TEXT,
auth_provider TEXT,
auth_provider_id TEXT,
accepts_marketing INTEGER DEFAULT 0,
locale TEXT DEFAULT 'en',
metadata TEXT,
order_count INTEGER DEFAULT 0,
total_spent_cents INTEGER DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
last_order_at TEXT
);
-- Customer Addresses
CREATE TABLE IF NOT EXISTS customer_addresses (
id TEXT PRIMARY KEY,
customer_id TEXT NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
label TEXT,
is_default INTEGER DEFAULT 0,
name TEXT,
company TEXT,
line1 TEXT NOT NULL,
line2 TEXT,
city TEXT NOT NULL,
state TEXT,
postal_code TEXT NOT NULL,
country TEXT NOT NULL DEFAULT 'US',
phone TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Events (webhook deduplication)
CREATE TABLE IF NOT EXISTS events (
id TEXT PRIMARY KEY,
stripe_event_id TEXT UNIQUE,
type TEXT NOT NULL,
payload TEXT NOT NULL,
processed_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Outbound Webhooks
CREATE TABLE IF NOT EXISTS webhooks (
id TEXT PRIMARY KEY,
url TEXT NOT NULL,
events TEXT NOT NULL,
secret TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Webhook Deliveries
CREATE TABLE IF NOT EXISTS webhook_deliveries (
id TEXT PRIMARY KEY,
webhook_id TEXT NOT NULL REFERENCES webhooks(id),
event_type TEXT NOT NULL,
payload TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'success', 'failed')),
attempts INTEGER NOT NULL DEFAULT 0,
last_attempt_at TEXT,
response_code INTEGER,
response_body TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- OAuth Clients (auto-registered by platforms)
CREATE TABLE IF NOT EXISTS oauth_clients (
id TEXT PRIMARY KEY,
client_id TEXT NOT NULL UNIQUE,
client_secret_hash TEXT,
name TEXT NOT NULL,
redirect_uris TEXT NOT NULL DEFAULT '[]',
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- OAuth Authorizations (pending auth requests)
CREATE TABLE IF NOT EXISTS oauth_authorizations (
id TEXT PRIMARY KEY,
client_id TEXT NOT NULL,
redirect_uri TEXT NOT NULL,
scope TEXT NOT NULL,
state TEXT,
code_challenge TEXT NOT NULL,
customer_email TEXT,
magic_token_hash TEXT,
magic_expires_at TEXT,
code_hash TEXT,
code_expires_at TEXT,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'authorized', 'used', 'expired')),
expires_at TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- OAuth Tokens
CREATE TABLE IF NOT EXISTS oauth_tokens (
id TEXT PRIMARY KEY,
client_id TEXT NOT NULL,
customer_id TEXT NOT NULL REFERENCES customers(id),
access_token_hash TEXT NOT NULL,
refresh_token_hash TEXT NOT NULL,
scope TEXT NOT NULL,
access_expires_at TEXT NOT NULL,
refresh_expires_at TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Config (key-value store for settings like Stripe keys)
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Indexes
CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash);
CREATE INDEX IF NOT EXISTS idx_products_status ON products(status);
CREATE INDEX IF NOT EXISTS idx_variants_sku ON variants(sku);
CREATE INDEX IF NOT EXISTS idx_variants_product ON variants(product_id);
CREATE INDEX IF NOT EXISTS idx_inventory_sku ON inventory(sku);
CREATE INDEX IF NOT EXISTS idx_carts_expires ON carts(expires_at);
CREATE INDEX IF NOT EXISTS idx_carts_status ON carts(status);
CREATE INDEX IF NOT EXISTS idx_orders_customer ON orders(customer_id);
CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
CREATE INDEX IF NOT EXISTS idx_orders_email ON orders(customer_email);
CREATE INDEX IF NOT EXISTS idx_discounts_code ON discounts(code);
CREATE INDEX IF NOT EXISTS idx_discounts_status ON discounts(status);
CREATE INDEX IF NOT EXISTS idx_discount_usage_order ON discount_usage(order_id);
CREATE INDEX IF NOT EXISTS idx_discount_usage_customer ON discount_usage(discount_id, customer_email);
CREATE UNIQUE INDEX IF NOT EXISTS idx_discount_usage_order_discount ON discount_usage(order_id, discount_id);
CREATE INDEX IF NOT EXISTS idx_customers_email ON customers(email);
CREATE INDEX IF NOT EXISTS idx_customer_addresses_customer ON customer_addresses(customer_id);
CREATE INDEX IF NOT EXISTS idx_webhooks_status ON webhooks(status);
CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_webhook ON webhook_deliveries(webhook_id);
CREATE INDEX IF NOT EXISTS idx_webhook_deliveries_status ON webhook_deliveries(status);
CREATE INDEX IF NOT EXISTS idx_oauth_clients_client_id ON oauth_clients(client_id);
CREATE INDEX IF NOT EXISTS idx_oauth_authorizations_client ON oauth_authorizations(client_id);
CREATE INDEX IF NOT EXISTS idx_oauth_tokens_access ON oauth_tokens(access_token_hash);
CREATE INDEX IF NOT EXISTS idx_oauth_tokens_refresh ON oauth_tokens(refresh_token_hash);
CREATE INDEX IF NOT EXISTS idx_oauth_tokens_customer ON oauth_tokens(customer_id);