Skip to content

Commit cd94e23

Browse files
committed
feat(database): add prisma migrations, deploy scripts, and seed data
1 parent 9dd6977 commit cd94e23

10 files changed

Lines changed: 985 additions & 2 deletions

File tree

packages/database/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/stellar_pay"
2+
SHADOW_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/stellar_pay_shadow"
3+
TEST_DATABASE_URL="postgresql://postgres:postgres@localhost:5433/stellar_pay_test"
4+
TEST_SHADOW_DATABASE_URL="postgresql://postgres:postgres@localhost:5433/stellar_pay_test_shadow"
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
if [[ -z "${DATABASE_URL:-}" ]]; then
5+
echo "DATABASE_URL is required for migration deployment." >&2
6+
exit 1
7+
fi
8+
9+
pnpm exec prisma migrate deploy --schema ./migrations/schema.prisma
10+
pnpm exec prisma generate --schema ./migrations/schema.prisma
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
provider = "postgresql"
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
-- Create enums
2+
CREATE TYPE "MerchantStatus" AS ENUM ('ACTIVE', 'DISABLED');
3+
CREATE TYPE "UserRole" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');
4+
CREATE TYPE "PaymentIntentStatus" AS ENUM ('REQUIRES_PAYMENT_METHOD', 'PROCESSING', 'SUCCEEDED', 'CANCELED', 'FAILED');
5+
CREATE TYPE "EscrowStatus" AS ENUM ('PENDING', 'FUNDED', 'RELEASED', 'REFUNDED', 'FAILED');
6+
CREATE TYPE "SubscriptionInterval" AS ENUM ('DAY', 'WEEK', 'MONTH', 'YEAR');
7+
CREATE TYPE "SubscriptionStatus" AS ENUM ('TRIALING', 'ACTIVE', 'PAST_DUE', 'CANCELED', 'ENDED');
8+
CREATE TYPE "InvoiceStatus" AS ENUM ('PENDING', 'PAID', 'VOID', 'FAILED');
9+
CREATE TYPE "PayoutStatus" AS ENUM ('REQUESTED', 'PROCESSING', 'COMPLETED', 'FAILED', 'CANCELED');
10+
CREATE TYPE "WebhookStatus" AS ENUM ('RECEIVED', 'PROCESSED', 'FAILED');
11+
12+
-- Create tables
13+
CREATE TABLE "Merchant" (
14+
"id" TEXT NOT NULL,
15+
"name" TEXT NOT NULL,
16+
"legalName" TEXT,
17+
"email" TEXT,
18+
"status" "MerchantStatus" NOT NULL DEFAULT 'ACTIVE',
19+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
20+
"updatedAt" TIMESTAMP(3) NOT NULL,
21+
CONSTRAINT "Merchant_pkey" PRIMARY KEY ("id")
22+
);
23+
24+
CREATE TABLE "MerchantUser" (
25+
"id" TEXT NOT NULL,
26+
"merchantId" TEXT NOT NULL,
27+
"email" TEXT NOT NULL,
28+
"fullName" TEXT,
29+
"role" "UserRole" NOT NULL DEFAULT 'MEMBER',
30+
"isActive" BOOLEAN NOT NULL DEFAULT true,
31+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
32+
"updatedAt" TIMESTAMP(3) NOT NULL,
33+
CONSTRAINT "MerchantUser_pkey" PRIMARY KEY ("id")
34+
);
35+
36+
CREATE TABLE "Customer" (
37+
"id" TEXT NOT NULL,
38+
"merchantId" TEXT NOT NULL,
39+
"externalRef" TEXT,
40+
"email" TEXT,
41+
"name" TEXT,
42+
"metadata" JSONB,
43+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
44+
"updatedAt" TIMESTAMP(3) NOT NULL,
45+
CONSTRAINT "Customer_pkey" PRIMARY KEY ("id")
46+
);
47+
48+
CREATE TABLE "PaymentIntent" (
49+
"id" TEXT NOT NULL,
50+
"merchantId" TEXT NOT NULL,
51+
"customerId" TEXT,
52+
"amountMinor" INTEGER NOT NULL,
53+
"currency" VARCHAR(3) NOT NULL,
54+
"status" "PaymentIntentStatus" NOT NULL DEFAULT 'REQUIRES_PAYMENT_METHOD',
55+
"paymentMethod" TEXT,
56+
"description" TEXT,
57+
"clientSecret" TEXT NOT NULL DEFAULT gen_random_uuid()::text,
58+
"idempotencyKey" TEXT,
59+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
60+
"updatedAt" TIMESTAMP(3) NOT NULL,
61+
CONSTRAINT "PaymentIntent_pkey" PRIMARY KEY ("id")
62+
);
63+
64+
CREATE TABLE "EscrowTransaction" (
65+
"id" TEXT NOT NULL,
66+
"paymentIntentId" TEXT NOT NULL,
67+
"network" TEXT NOT NULL DEFAULT 'stellar',
68+
"escrowAddress" TEXT,
69+
"txHash" TEXT,
70+
"status" "EscrowStatus" NOT NULL DEFAULT 'PENDING',
71+
"fundedAt" TIMESTAMP(3),
72+
"releasedAt" TIMESTAMP(3),
73+
"refundedAt" TIMESTAMP(3),
74+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
75+
"updatedAt" TIMESTAMP(3) NOT NULL,
76+
CONSTRAINT "EscrowTransaction_pkey" PRIMARY KEY ("id")
77+
);
78+
79+
CREATE TABLE "Subscription" (
80+
"id" TEXT NOT NULL,
81+
"merchantId" TEXT NOT NULL,
82+
"customerId" TEXT NOT NULL,
83+
"planCode" TEXT NOT NULL,
84+
"amountMinor" INTEGER NOT NULL,
85+
"currency" VARCHAR(3) NOT NULL,
86+
"interval" "SubscriptionInterval" NOT NULL,
87+
"status" "SubscriptionStatus" NOT NULL DEFAULT 'TRIALING',
88+
"currentPeriodStart" TIMESTAMP(3) NOT NULL,
89+
"currentPeriodEnd" TIMESTAMP(3) NOT NULL,
90+
"cancelAtPeriodEnd" BOOLEAN NOT NULL DEFAULT false,
91+
"canceledAt" TIMESTAMP(3),
92+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
93+
"updatedAt" TIMESTAMP(3) NOT NULL,
94+
CONSTRAINT "Subscription_pkey" PRIMARY KEY ("id")
95+
);
96+
97+
CREATE TABLE "SubscriptionInvoice" (
98+
"id" TEXT NOT NULL,
99+
"subscriptionId" TEXT NOT NULL,
100+
"externalPaymentIntentId" TEXT,
101+
"amountMinor" INTEGER NOT NULL,
102+
"currency" VARCHAR(3) NOT NULL,
103+
"status" "InvoiceStatus" NOT NULL DEFAULT 'PENDING',
104+
"dueAt" TIMESTAMP(3) NOT NULL,
105+
"paidAt" TIMESTAMP(3),
106+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
107+
"updatedAt" TIMESTAMP(3) NOT NULL,
108+
CONSTRAINT "SubscriptionInvoice_pkey" PRIMARY KEY ("id")
109+
);
110+
111+
CREATE TABLE "TreasuryWallet" (
112+
"id" TEXT NOT NULL,
113+
"merchantId" TEXT NOT NULL,
114+
"network" TEXT NOT NULL DEFAULT 'stellar',
115+
"publicKey" TEXT NOT NULL,
116+
"encryptedSecret" TEXT NOT NULL,
117+
"isPrimary" BOOLEAN NOT NULL DEFAULT false,
118+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
119+
"updatedAt" TIMESTAMP(3) NOT NULL,
120+
CONSTRAINT "TreasuryWallet_pkey" PRIMARY KEY ("id")
121+
);
122+
123+
CREATE TABLE "Payout" (
124+
"id" TEXT NOT NULL,
125+
"merchantId" TEXT NOT NULL,
126+
"amountMinor" INTEGER NOT NULL,
127+
"currency" VARCHAR(3) NOT NULL,
128+
"destination" TEXT NOT NULL,
129+
"status" "PayoutStatus" NOT NULL DEFAULT 'REQUESTED',
130+
"requestedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
131+
"processedAt" TIMESTAMP(3),
132+
"txHash" TEXT,
133+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
134+
"updatedAt" TIMESTAMP(3) NOT NULL,
135+
CONSTRAINT "Payout_pkey" PRIMARY KEY ("id")
136+
);
137+
138+
CREATE TABLE "WebhookEvent" (
139+
"id" TEXT NOT NULL,
140+
"merchantId" TEXT,
141+
"provider" TEXT NOT NULL,
142+
"eventType" TEXT NOT NULL,
143+
"payload" JSONB NOT NULL,
144+
"status" "WebhookStatus" NOT NULL DEFAULT 'RECEIVED',
145+
"processedAt" TIMESTAMP(3),
146+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
147+
CONSTRAINT "WebhookEvent_pkey" PRIMARY KEY ("id")
148+
);
149+
150+
CREATE TABLE "AuditLog" (
151+
"id" BIGSERIAL NOT NULL,
152+
"actorType" TEXT NOT NULL,
153+
"actorId" TEXT,
154+
"action" TEXT NOT NULL,
155+
"entityType" TEXT NOT NULL,
156+
"entityId" TEXT,
157+
"metadata" JSONB,
158+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
159+
CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id")
160+
);
161+
162+
-- Indexes and unique constraints
163+
CREATE UNIQUE INDEX "Merchant_email_key" ON "Merchant"("email");
164+
CREATE UNIQUE INDEX "MerchantUser_email_key" ON "MerchantUser"("email");
165+
CREATE INDEX "MerchantUser_merchantId_idx" ON "MerchantUser"("merchantId");
166+
CREATE UNIQUE INDEX "Customer_merchantId_externalRef_key" ON "Customer"("merchantId", "externalRef");
167+
CREATE INDEX "Customer_merchantId_idx" ON "Customer"("merchantId");
168+
CREATE UNIQUE INDEX "PaymentIntent_clientSecret_key" ON "PaymentIntent"("clientSecret");
169+
CREATE UNIQUE INDEX "PaymentIntent_merchantId_idempotencyKey_key" ON "PaymentIntent"("merchantId", "idempotencyKey");
170+
CREATE INDEX "PaymentIntent_merchantId_idx" ON "PaymentIntent"("merchantId");
171+
CREATE INDEX "PaymentIntent_customerId_idx" ON "PaymentIntent"("customerId");
172+
CREATE UNIQUE INDEX "EscrowTransaction_paymentIntentId_key" ON "EscrowTransaction"("paymentIntentId");
173+
CREATE INDEX "Subscription_merchantId_idx" ON "Subscription"("merchantId");
174+
CREATE INDEX "Subscription_customerId_idx" ON "Subscription"("customerId");
175+
CREATE UNIQUE INDEX "SubscriptionInvoice_externalPaymentIntentId_key" ON "SubscriptionInvoice"("externalPaymentIntentId");
176+
CREATE INDEX "SubscriptionInvoice_subscriptionId_idx" ON "SubscriptionInvoice"("subscriptionId");
177+
CREATE UNIQUE INDEX "TreasuryWallet_publicKey_key" ON "TreasuryWallet"("publicKey");
178+
CREATE INDEX "TreasuryWallet_merchantId_idx" ON "TreasuryWallet"("merchantId");
179+
CREATE INDEX "Payout_merchantId_idx" ON "Payout"("merchantId");
180+
CREATE INDEX "WebhookEvent_merchantId_idx" ON "WebhookEvent"("merchantId");
181+
CREATE INDEX "AuditLog_entityType_entityId_idx" ON "AuditLog"("entityType", "entityId");
182+
183+
-- Foreign keys
184+
ALTER TABLE "MerchantUser" ADD CONSTRAINT "MerchantUser_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
185+
ALTER TABLE "Customer" ADD CONSTRAINT "Customer_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
186+
ALTER TABLE "PaymentIntent" ADD CONSTRAINT "PaymentIntent_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
187+
ALTER TABLE "PaymentIntent" ADD CONSTRAINT "PaymentIntent_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE SET NULL ON UPDATE CASCADE;
188+
ALTER TABLE "EscrowTransaction" ADD CONSTRAINT "EscrowTransaction_paymentIntentId_fkey" FOREIGN KEY ("paymentIntentId") REFERENCES "PaymentIntent"("id") ON DELETE CASCADE ON UPDATE CASCADE;
189+
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
190+
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
191+
ALTER TABLE "SubscriptionInvoice" ADD CONSTRAINT "SubscriptionInvoice_subscriptionId_fkey" FOREIGN KEY ("subscriptionId") REFERENCES "Subscription"("id") ON DELETE CASCADE ON UPDATE CASCADE;
192+
ALTER TABLE "SubscriptionInvoice" ADD CONSTRAINT "SubscriptionInvoice_externalPaymentIntentId_fkey" FOREIGN KEY ("externalPaymentIntentId") REFERENCES "PaymentIntent"("id") ON DELETE SET NULL ON UPDATE CASCADE;
193+
ALTER TABLE "TreasuryWallet" ADD CONSTRAINT "TreasuryWallet_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
194+
ALTER TABLE "Payout" ADD CONSTRAINT "Payout_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
195+
ALTER TABLE "WebhookEvent" ADD CONSTRAINT "WebhookEvent_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE SET NULL ON UPDATE CASCADE;

0 commit comments

Comments
 (0)