mirror of
https://github.com/lukevella/rallly.git
synced 2025-08-01 23:48:53 +02:00
✨ Keep payment methods synchronized (#1569)
This commit is contained in:
parent
5e356afab6
commit
ca46b18f3a
20 changed files with 566 additions and 346 deletions
|
@ -10,6 +10,7 @@
|
|||
"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",
|
||||
"type-check": "tsc --pretty --noEmit",
|
||||
"lint": "eslint ./src"
|
||||
},
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
import { prisma } from "@rallly/database";
|
||||
|
||||
import { stripe } from "../lib/stripe";
|
||||
|
||||
(async function syncCancelAtPeriodEnd() {
|
||||
let processed = 0;
|
||||
let failed = 0;
|
||||
|
||||
const userSubscriptions = await prisma.subscription.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.info(`🚀 Syncing ${userSubscriptions.length} subscriptions...`);
|
||||
|
||||
for (const userSubscription of userSubscriptions) {
|
||||
try {
|
||||
const subscription = await stripe.subscriptions.retrieve(
|
||||
userSubscription.id,
|
||||
);
|
||||
|
||||
await prisma.subscription.update({
|
||||
where: {
|
||||
id: subscription.id,
|
||||
},
|
||||
data: {
|
||||
cancelAtPeriodEnd: subscription.cancel_at_period_end,
|
||||
},
|
||||
});
|
||||
|
||||
console.info(`✅ Subscription ${subscription.id} synced`);
|
||||
processed++;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`❌ Failed to sync subscription ${userSubscription.id}:`,
|
||||
error,
|
||||
);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.info(`📊 Sync complete: ${processed} processed, ${failed} failed`);
|
||||
})();
|
53
packages/billing/src/scripts/sync-payment-methods.ts
Normal file
53
packages/billing/src/scripts/sync-payment-methods.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import type { Prisma } from "@rallly/database";
|
||||
import { prisma } from "@rallly/database";
|
||||
|
||||
import { stripe } from "../lib/stripe";
|
||||
|
||||
(async function syncPaymentMethods() {
|
||||
let processed = 0;
|
||||
let failed = 0;
|
||||
|
||||
const users = await prisma.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
customerId: true,
|
||||
email: true,
|
||||
},
|
||||
where: {
|
||||
customerId: {
|
||||
not: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.info(`🚀 Syncing ${users.length} users...`);
|
||||
|
||||
for (const user of users) {
|
||||
if (!user.customerId) continue;
|
||||
try {
|
||||
const paymentMethods = await stripe.customers.listPaymentMethods(
|
||||
user.customerId,
|
||||
);
|
||||
|
||||
await prisma.paymentMethod.createMany({
|
||||
data: paymentMethods.data.map((paymentMethod) => ({
|
||||
id: paymentMethod.id,
|
||||
userId: user.id,
|
||||
type: paymentMethod.type,
|
||||
data: paymentMethod[paymentMethod.type] as Prisma.JsonObject,
|
||||
})),
|
||||
});
|
||||
|
||||
console.info(`✅ Payment methods synced for user ${user.email}`);
|
||||
processed++;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`❌ Failed to sync payment methods for user ${user.email}:`,
|
||||
error,
|
||||
);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.info(`📊 Sync complete: ${processed} processed, ${failed} failed`);
|
||||
})();
|
|
@ -0,0 +1,14 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE "payment_methods" (
|
||||
"id" TEXT NOT NULL,
|
||||
"user_id" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"data" JSONB NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "payment_methods_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "payment_methods" ADD CONSTRAINT "payment_methods_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@ -51,12 +51,13 @@ model User {
|
|||
customerId String? @map("customer_id")
|
||||
subscriptionId String? @unique @map("subscription_id")
|
||||
|
||||
comments Comment[]
|
||||
polls Poll[]
|
||||
watcher Watcher[]
|
||||
events Event[]
|
||||
accounts Account[]
|
||||
participants Participant[]
|
||||
comments Comment[]
|
||||
polls Poll[]
|
||||
watcher Watcher[]
|
||||
events Event[]
|
||||
accounts Account[]
|
||||
participants Participant[]
|
||||
paymentMethods PaymentMethod[]
|
||||
|
||||
subscription Subscription? @relation(fields: [subscriptionId], references: [id], onDelete: SetNull)
|
||||
|
||||
|
@ -82,6 +83,19 @@ enum SubscriptionInterval {
|
|||
@@map("subscription_interval")
|
||||
}
|
||||
|
||||
model PaymentMethod {
|
||||
id String @id
|
||||
userId String @map("user_id")
|
||||
type String
|
||||
data Json
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@map("payment_methods")
|
||||
}
|
||||
|
||||
model UserPaymentData {
|
||||
userId String @id @map("user_id")
|
||||
subscriptionId String @map("subscription_id")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue