update subscription algorithm

This commit is contained in:
root
2026-05-25 03:12:19 -04:00
parent 95376d3223
commit c9cbe479aa
42 changed files with 3098 additions and 307 deletions
@@ -0,0 +1,21 @@
CREATE TABLE "pricing_configs" (
"id" TEXT NOT NULL,
"plan" TEXT NOT NULL,
"billingPeriod" TEXT NOT NULL,
"amount" INTEGER NOT NULL,
"updatedAt" TIMESTAMP(3) NOT NULL,
"updatedBy" TEXT,
CONSTRAINT "pricing_configs_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "pricing_configs_plan_billingPeriod_key" ON "pricing_configs"("plan", "billingPeriod");
-- Seed default prices (amounts in centimes MAD)
INSERT INTO "pricing_configs" ("id", "plan", "billingPeriod", "amount", "updatedAt") VALUES
('prc_starter_monthly', 'STARTER', 'MONTHLY', 2990, NOW()),
('prc_starter_annual', 'STARTER', 'ANNUAL', 29900, NOW()),
('prc_growth_monthly', 'GROWTH', 'MONTHLY', 3990, NOW()),
('prc_growth_annual', 'GROWTH', 'ANNUAL', 39900, NOW()),
('prc_pro_monthly', 'PRO', 'MONTHLY', 99900, NOW()),
('prc_pro_annual', 'PRO', 'ANNUAL', 999000, NOW());
@@ -0,0 +1,66 @@
-- Add new SubscriptionStatus enum values
ALTER TYPE "SubscriptionStatus" ADD VALUE IF NOT EXISTS 'PAYMENT_PENDING';
ALTER TYPE "SubscriptionStatus" ADD VALUE IF NOT EXISTS 'SUSPENDED';
ALTER TYPE "SubscriptionStatus" ADD VALUE IF NOT EXISTS 'EXPIRED';
ALTER TYPE "SubscriptionStatus" ADD VALUE IF NOT EXISTS 'PAUSED';
-- Add new InvoiceStatus enum value
ALTER TYPE "InvoiceStatus" ADD VALUE IF NOT EXISTS 'VOIDED';
-- Extend subscriptions table (camelCase to match Prisma default)
ALTER TABLE "subscriptions"
ADD COLUMN IF NOT EXISTS "trialUsed" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS "paymentPendingSince" TIMESTAMP(3),
ADD COLUMN IF NOT EXISTS "paymentDueAt" TIMESTAMP(3),
ADD COLUMN IF NOT EXISTS "pastDueSince" TIMESTAMP(3),
ADD COLUMN IF NOT EXISTS "suspendedAt" TIMESTAMP(3),
ADD COLUMN IF NOT EXISTS "endedAt" TIMESTAMP(3),
ADD COLUMN IF NOT EXISTS "retryCount" INTEGER NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS "maxRetryCount" INTEGER NOT NULL DEFAULT 5;
-- Extend subscription_invoices table
ALTER TABLE "subscription_invoices"
ADD COLUMN IF NOT EXISTS "providerInvoiceId" TEXT,
ADD COLUMN IF NOT EXISTS "dueAt" TIMESTAMP(3),
ADD COLUMN IF NOT EXISTS "failedAt" TIMESTAMP(3),
ADD COLUMN IF NOT EXISTS "voidedAt" TIMESTAMP(3);
-- Create subscription_events table
CREATE TABLE IF NOT EXISTS "subscription_events" (
"id" TEXT NOT NULL,
"subscriptionId" TEXT NOT NULL,
"companyId" TEXT NOT NULL,
"eventType" TEXT NOT NULL,
"source" TEXT NOT NULL DEFAULT 'system',
"payload" JSONB NOT NULL DEFAULT '{}',
"occurredAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "subscription_events_pkey" PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "subscription_events_subscriptionId_idx" ON "subscription_events"("subscriptionId");
CREATE INDEX IF NOT EXISTS "subscription_events_companyId_idx" ON "subscription_events"("companyId");
ALTER TABLE "subscription_events"
ADD CONSTRAINT "subscription_events_subscriptionId_fkey"
FOREIGN KEY ("subscriptionId") REFERENCES "subscriptions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- Create payment_attempts table
CREATE TABLE IF NOT EXISTS "payment_attempts" (
"id" TEXT NOT NULL,
"invoiceId" TEXT NOT NULL,
"subscriptionId" TEXT NOT NULL,
"providerPaymentId" TEXT,
"status" TEXT NOT NULL,
"failureCode" TEXT,
"failureMessage" TEXT,
"attemptedAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "payment_attempts_pkey" PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "payment_attempts_invoiceId_idx" ON "payment_attempts"("invoiceId");
ALTER TABLE "payment_attempts"
ADD CONSTRAINT "payment_attempts_invoiceId_fkey"
FOREIGN KEY ("invoiceId") REFERENCES "subscription_invoices"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+85 -21
View File
@@ -35,8 +35,12 @@ enum BillingPeriod {
enum SubscriptionStatus {
TRIALING
ACTIVE
PAYMENT_PENDING
PAST_DUE
SUSPENDED
CANCELLED
EXPIRED
PAUSED
UNPAID
}
@@ -45,6 +49,7 @@ enum InvoiceStatus {
PAID
FAILED
REFUNDED
VOIDED
}
enum PaymentProvider {
@@ -357,20 +362,29 @@ model Company {
}
model Subscription {
id String @id @default(cuid())
companyId String @unique
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
plan Plan @default(STARTER)
billingPeriod BillingPeriod @default(MONTHLY)
status SubscriptionStatus @default(TRIALING)
currency String @default("MAD")
trialStartAt DateTime?
trialEndAt DateTime?
currentPeriodStart DateTime?
currentPeriodEnd DateTime?
cancelledAt DateTime?
cancelAtPeriodEnd Boolean @default(false)
invoices SubscriptionInvoice[]
id String @id @default(cuid())
companyId String @unique
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
plan Plan @default(STARTER)
billingPeriod BillingPeriod @default(MONTHLY)
status SubscriptionStatus @default(TRIALING)
currency String @default("MAD")
trialStartAt DateTime?
trialEndAt DateTime?
trialUsed Boolean @default(false)
currentPeriodStart DateTime?
currentPeriodEnd DateTime?
paymentPendingSince DateTime?
paymentDueAt DateTime?
pastDueSince DateTime?
suspendedAt DateTime?
endedAt DateTime?
cancelledAt DateTime?
cancelAtPeriodEnd Boolean @default(false)
retryCount Int @default(0)
maxRetryCount Int @default(5)
invoices SubscriptionInvoice[]
events SubscriptionEvent[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -378,19 +392,40 @@ model Subscription {
@@map("subscriptions")
}
model SubscriptionEvent {
id String @id @default(cuid())
subscriptionId String
subscription Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
companyId String
eventType String
source String @default("system")
payload Json @default("{}")
occurredAt DateTime
createdAt DateTime @default(now())
@@index([subscriptionId])
@@index([companyId])
@@map("subscription_events")
}
model SubscriptionInvoice {
id String @id @default(cuid())
id String @id @default(cuid())
companyId String
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
subscriptionId String
subscription Subscription @relation(fields: [subscriptionId], references: [id])
subscription Subscription @relation(fields: [subscriptionId], references: [id])
providerInvoiceId String?
amount Int
currency String @default("MAD")
currency String @default("MAD")
status InvoiceStatus
amanpayTransactionId String? @unique
paypalCaptureId String? @unique
paymentProvider PaymentProvider @default(AMANPAY)
amanpayTransactionId String? @unique
paypalCaptureId String? @unique
paymentProvider PaymentProvider @default(AMANPAY)
dueAt DateTime?
paidAt DateTime?
failedAt DateTime?
voidedAt DateTime?
attempts PaymentAttempt[]
createdAt DateTime @default(now())
@@ -398,6 +433,22 @@ model SubscriptionInvoice {
@@map("subscription_invoices")
}
model PaymentAttempt {
id String @id @default(cuid())
invoiceId String
invoice SubscriptionInvoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
subscriptionId String
providerPaymentId String?
status String
failureCode String?
failureMessage String?
attemptedAt DateTime
createdAt DateTime @default(now())
@@index([invoiceId])
@@map("payment_attempts")
}
model BrandSettings {
id String @id @default(cuid())
companyId String @unique
@@ -1115,3 +1166,16 @@ model AuditLog {
@@index([action])
@@map("audit_logs")
}
model PricingConfig {
id String @id @default(cuid())
plan String
billingPeriod String
amount Int
updatedAt DateTime @updatedAt
updatedBy String?
@@unique([plan, billingPeriod])
@@map("pricing_configs")
}