Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/database/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/stellar_pay"
SHADOW_DATABASE_URL="postgresql://postgres:postgres@localhost:5432/stellar_pay_shadow"
TEST_DATABASE_URL="postgresql://postgres:postgres@localhost:5433/stellar_pay_test"
TEST_SHADOW_DATABASE_URL="postgresql://postgres:postgres@localhost:5433/stellar_pay_test_shadow"
10 changes: 10 additions & 0 deletions packages/database/migrations/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ -z "${DATABASE_URL:-}" ]]; then
echo "DATABASE_URL is required for migration deployment." >&2
exit 1
fi

pnpm exec prisma migrate deploy --schema ./migrations/schema.prisma
pnpm exec prisma generate --schema ./migrations/schema.prisma
1 change: 1 addition & 0 deletions packages/database/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
provider = "postgresql"
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
-- Create enums
CREATE TYPE "MerchantStatus" AS ENUM ('ACTIVE', 'DISABLED');
CREATE TYPE "UserRole" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');
CREATE TYPE "PaymentIntentStatus" AS ENUM ('REQUIRES_PAYMENT_METHOD', 'PROCESSING', 'SUCCEEDED', 'CANCELED', 'FAILED');
CREATE TYPE "EscrowStatus" AS ENUM ('PENDING', 'FUNDED', 'RELEASED', 'REFUNDED', 'FAILED');
CREATE TYPE "SubscriptionInterval" AS ENUM ('DAY', 'WEEK', 'MONTH', 'YEAR');
CREATE TYPE "SubscriptionStatus" AS ENUM ('TRIALING', 'ACTIVE', 'PAST_DUE', 'CANCELED', 'ENDED');
CREATE TYPE "InvoiceStatus" AS ENUM ('PENDING', 'PAID', 'VOID', 'FAILED');
CREATE TYPE "PayoutStatus" AS ENUM ('REQUESTED', 'PROCESSING', 'COMPLETED', 'FAILED', 'CANCELED');
CREATE TYPE "WebhookStatus" AS ENUM ('RECEIVED', 'PROCESSED', 'FAILED');

-- Create tables
CREATE TABLE "Merchant" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"legalName" TEXT,
"email" TEXT,
"status" "MerchantStatus" NOT NULL DEFAULT 'ACTIVE',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Merchant_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "MerchantUser" (
"id" TEXT NOT NULL,
"merchantId" TEXT NOT NULL,
"email" TEXT NOT NULL,
"fullName" TEXT,
"role" "UserRole" NOT NULL DEFAULT 'MEMBER',
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "MerchantUser_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "Customer" (
"id" TEXT NOT NULL,
"merchantId" TEXT NOT NULL,
"externalRef" TEXT,
"email" TEXT,
"name" TEXT,
"metadata" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Customer_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "PaymentIntent" (
"id" TEXT NOT NULL,
"merchantId" TEXT NOT NULL,
"customerId" TEXT,
"amountMinor" INTEGER NOT NULL,
"currency" VARCHAR(3) NOT NULL,
"status" "PaymentIntentStatus" NOT NULL DEFAULT 'REQUIRES_PAYMENT_METHOD',
"paymentMethod" TEXT,
"description" TEXT,
"clientSecret" TEXT NOT NULL DEFAULT gen_random_uuid()::text,
"idempotencyKey" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "PaymentIntent_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "EscrowTransaction" (
"id" TEXT NOT NULL,
"paymentIntentId" TEXT NOT NULL,
"network" TEXT NOT NULL DEFAULT 'stellar',
"escrowAddress" TEXT,
"txHash" TEXT,
"status" "EscrowStatus" NOT NULL DEFAULT 'PENDING',
"fundedAt" TIMESTAMP(3),
"releasedAt" TIMESTAMP(3),
"refundedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "EscrowTransaction_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "Subscription" (
"id" TEXT NOT NULL,
"merchantId" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"planCode" TEXT NOT NULL,
"amountMinor" INTEGER NOT NULL,
"currency" VARCHAR(3) NOT NULL,
"interval" "SubscriptionInterval" NOT NULL,
"status" "SubscriptionStatus" NOT NULL DEFAULT 'TRIALING',
"currentPeriodStart" TIMESTAMP(3) NOT NULL,
"currentPeriodEnd" TIMESTAMP(3) NOT NULL,
"cancelAtPeriodEnd" BOOLEAN NOT NULL DEFAULT false,
"canceledAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Subscription_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "SubscriptionInvoice" (
"id" TEXT NOT NULL,
"subscriptionId" TEXT NOT NULL,
"externalPaymentIntentId" TEXT,
"amountMinor" INTEGER NOT NULL,
"currency" VARCHAR(3) NOT NULL,
"status" "InvoiceStatus" NOT NULL DEFAULT 'PENDING',
"dueAt" TIMESTAMP(3) NOT NULL,
"paidAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "SubscriptionInvoice_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "TreasuryWallet" (
"id" TEXT NOT NULL,
"merchantId" TEXT NOT NULL,
"network" TEXT NOT NULL DEFAULT 'stellar',
"publicKey" TEXT NOT NULL,
"encryptedSecret" TEXT NOT NULL,
"isPrimary" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "TreasuryWallet_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "Payout" (
"id" TEXT NOT NULL,
"merchantId" TEXT NOT NULL,
"amountMinor" INTEGER NOT NULL,
"currency" VARCHAR(3) NOT NULL,
"destination" TEXT NOT NULL,
"status" "PayoutStatus" NOT NULL DEFAULT 'REQUESTED',
"requestedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"processedAt" TIMESTAMP(3),
"txHash" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Payout_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "WebhookEvent" (
"id" TEXT NOT NULL,
"merchantId" TEXT,
"provider" TEXT NOT NULL,
"eventType" TEXT NOT NULL,
"payload" JSONB NOT NULL,
"status" "WebhookStatus" NOT NULL DEFAULT 'RECEIVED',
"processedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "WebhookEvent_pkey" PRIMARY KEY ("id")
);

CREATE TABLE "AuditLog" (
"id" BIGSERIAL NOT NULL,
"actorType" TEXT NOT NULL,
"actorId" TEXT,
"action" TEXT NOT NULL,
"entityType" TEXT NOT NULL,
"entityId" TEXT,
"metadata" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id")
);

-- Indexes and unique constraints
CREATE UNIQUE INDEX "Merchant_email_key" ON "Merchant"("email");
CREATE UNIQUE INDEX "MerchantUser_email_key" ON "MerchantUser"("email");
CREATE INDEX "MerchantUser_merchantId_idx" ON "MerchantUser"("merchantId");
CREATE UNIQUE INDEX "Customer_merchantId_externalRef_key" ON "Customer"("merchantId", "externalRef");
CREATE INDEX "Customer_merchantId_idx" ON "Customer"("merchantId");
CREATE UNIQUE INDEX "PaymentIntent_clientSecret_key" ON "PaymentIntent"("clientSecret");
CREATE UNIQUE INDEX "PaymentIntent_merchantId_idempotencyKey_key" ON "PaymentIntent"("merchantId", "idempotencyKey");
CREATE INDEX "PaymentIntent_merchantId_idx" ON "PaymentIntent"("merchantId");
CREATE INDEX "PaymentIntent_customerId_idx" ON "PaymentIntent"("customerId");
CREATE UNIQUE INDEX "EscrowTransaction_paymentIntentId_key" ON "EscrowTransaction"("paymentIntentId");
CREATE INDEX "Subscription_merchantId_idx" ON "Subscription"("merchantId");
CREATE INDEX "Subscription_customerId_idx" ON "Subscription"("customerId");
CREATE UNIQUE INDEX "SubscriptionInvoice_externalPaymentIntentId_key" ON "SubscriptionInvoice"("externalPaymentIntentId");
CREATE INDEX "SubscriptionInvoice_subscriptionId_idx" ON "SubscriptionInvoice"("subscriptionId");
CREATE UNIQUE INDEX "TreasuryWallet_publicKey_key" ON "TreasuryWallet"("publicKey");
CREATE INDEX "TreasuryWallet_merchantId_idx" ON "TreasuryWallet"("merchantId");
CREATE INDEX "Payout_merchantId_idx" ON "Payout"("merchantId");
CREATE INDEX "WebhookEvent_merchantId_idx" ON "WebhookEvent"("merchantId");
CREATE INDEX "AuditLog_entityType_entityId_idx" ON "AuditLog"("entityType", "entityId");

-- Foreign keys
ALTER TABLE "MerchantUser" ADD CONSTRAINT "MerchantUser_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Customer" ADD CONSTRAINT "Customer_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "PaymentIntent" ADD CONSTRAINT "PaymentIntent_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "PaymentIntent" ADD CONSTRAINT "PaymentIntent_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "EscrowTransaction" ADD CONSTRAINT "EscrowTransaction_paymentIntentId_fkey" FOREIGN KEY ("paymentIntentId") REFERENCES "PaymentIntent"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "SubscriptionInvoice" ADD CONSTRAINT "SubscriptionInvoice_subscriptionId_fkey" FOREIGN KEY ("subscriptionId") REFERENCES "Subscription"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "SubscriptionInvoice" ADD CONSTRAINT "SubscriptionInvoice_externalPaymentIntentId_fkey" FOREIGN KEY ("externalPaymentIntentId") REFERENCES "PaymentIntent"("id") ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "TreasuryWallet" ADD CONSTRAINT "TreasuryWallet_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Payout" ADD CONSTRAINT "Payout_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "WebhookEvent" ADD CONSTRAINT "WebhookEvent_merchantId_fkey" FOREIGN KEY ("merchantId") REFERENCES "Merchant"("id") ON DELETE SET NULL ON UPDATE CASCADE;
Loading