♻️ Make user required in subscription model (#1585)

This commit is contained in:
Luke Vella 2025-02-27 15:23:01 +00:00 committed by GitHub
parent 01758f81ae
commit aebea5a41c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 87 additions and 92 deletions

View file

@ -24,26 +24,60 @@ export async function onCustomerSubscriptionCreated(event: Stripe.Event) {
throw new Error("Missing user ID");
}
// Create and update user
await prisma.user.update({
where: {
id: res.data.userId,
},
data: {
subscription: {
create: {
id: subscription.id,
active: isActive,
priceId,
currency,
interval,
amount,
status: subscription.status,
createdAt: toDate(subscription.created),
periodStart: toDate(subscription.current_period_start),
periodEnd: toDate(subscription.current_period_end),
const userId = res.data.userId;
// Check if user already has a subscription
const existingUser = await prisma.user.findUnique({
where: { id: userId },
include: { subscription: true },
});
if (!existingUser) {
throw new Error(`User with ID ${userId} not found`);
}
// If user already has a subscription, update it or replace it
if (existingUser.subscription) {
// Update the existing subscription with new data
await prisma.subscription.update({
where: { id: existingUser.subscription.id },
data: {
id: subscription.id,
active: isActive,
priceId,
currency,
interval,
amount,
status: subscription.status,
createdAt: toDate(subscription.created),
periodStart: toDate(subscription.current_period_start),
periodEnd: toDate(subscription.current_period_end),
cancelAtPeriodEnd: subscription.cancel_at_period_end,
},
});
} else {
// Create a new subscription for the user
await prisma.user.update({
where: {
id: userId,
},
data: {
subscription: {
create: {
id: subscription.id,
active: isActive,
priceId,
currency,
interval,
amount,
status: subscription.status,
createdAt: toDate(subscription.created),
periodStart: toDate(subscription.current_period_start),
periodEnd: toDate(subscription.current_period_end),
cancelAtPeriodEnd: subscription.cancel_at_period_end,
},
},
},
},
});
});
}
}

View file

@ -7,7 +7,6 @@
"./*": "./src/*.ts"
},
"scripts": {
"normalize-subscription-metadata": "dotenv -e ../../.env -- tsx ./src/scripts/normalize-metadata.ts",
"checkout-expiry": "dotenv -e ../../.env -- tsx ./src/scripts/checkout-expiry.ts",
"subscription-data-sync": "dotenv -e ../../.env -- tsx ./src/scripts/subscription-data-sync.ts",
"sync-payment-methods": "dotenv -e ../../.env -- tsx ./src/scripts/sync-payment-methods.ts",

View file

@ -1,67 +0,0 @@
/* eslint-disable no-console */
/**
* This script will go through all subscriptions and add the userId to the metadata.
*/
import { prisma } from "@rallly/database";
import { stripe } from "../lib/stripe";
async function getSubscriptionsWithMissingMetadata(
starting_after?: string,
): Promise<string[]> {
const res: string[] = [];
const subscriptions = await stripe.subscriptions.list({
limit: 100,
starting_after,
});
subscriptions.data.forEach((subscription) => {
if (!subscription.metadata.userId) {
res.push(subscription.id);
}
});
if (subscriptions.has_more) {
return [
...res,
...(await getSubscriptionsWithMissingMetadata(
subscriptions.data[subscriptions.data.length - 1].id,
)),
];
} else {
return res;
}
}
async function normalizeSubscriptionMetadata() {
const subscriptions = await getSubscriptionsWithMissingMetadata();
console.log(
`Found ${subscriptions.length} subscriptions with missing metadata`,
);
for (const subscriptionId of subscriptions) {
const user = await prisma.user.findFirst({
select: {
id: true,
},
where: {
subscriptionId: subscriptionId,
},
});
if (!user) {
console.log("User not found for subscription", subscriptionId);
continue;
}
await stripe.subscriptions.update(subscriptionId, {
metadata: {
userId: user.id,
},
});
console.log("Updated subscription", subscriptionId);
}
}
normalizeSubscriptionMetadata();

View file

@ -0,0 +1,30 @@
-- DropForeignKey
ALTER TABLE "users" DROP CONSTRAINT "users_subscription_id_fkey";
-- DropIndex
DROP INDEX "users_subscription_id_key";
-- AlterTable
ALTER TABLE "subscriptions" ADD COLUMN "user_id" TEXT;
-- Populate user_id in subscriptions table using data from users table
UPDATE "subscriptions" s
SET "user_id" = u.id
FROM "users" u
WHERE u."subscription_id" = s.id;
-- Delete orphaned subscriptions (subscriptions without a corresponding user)
DELETE FROM "subscriptions"
WHERE "user_id" IS NULL;
-- Make user_id NOT NULL after populating data
ALTER TABLE "subscriptions" ALTER COLUMN "user_id" SET NOT NULL;
-- AlterTable
ALTER TABLE "users" DROP COLUMN "subscription_id";
-- CreateIndex
CREATE UNIQUE INDEX "subscriptions_user_id_key" ON "subscriptions"("user_id");
-- AddForeignKey
ALTER TABLE "subscriptions" ADD CONSTRAINT "subscriptions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View file

@ -49,7 +49,6 @@ model User {
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime? @updatedAt @map("updated_at")
customerId String? @map("customer_id")
subscriptionId String? @unique @map("subscription_id")
comments Comment[]
polls Poll[]
@ -58,8 +57,7 @@ model User {
accounts Account[]
participants Participant[]
paymentMethods PaymentMethod[]
subscription Subscription? @relation(fields: [subscriptionId], references: [id], onDelete: SetNull)
subscription Subscription? @relation("UserToSubscription")
@@map("users")
}
@ -111,8 +109,9 @@ model Subscription {
periodStart DateTime @map("period_start")
periodEnd DateTime @map("period_end")
cancelAtPeriodEnd Boolean @default(false) @map("cancel_at_period_end")
userId String @unique @map("user_id")
user User?
user User @relation("UserToSubscription", fields: [userId], references: [id], onDelete: Cascade)
@@map("subscriptions")
}