You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
3.8 KiB
125 lines
3.8 KiB
|
7 months ago
|
import { NextRequest, NextResponse } from "next/server";
|
||
|
|
import { stripe } from "@/lib/stripe";
|
||
|
|
import { getPool } from "@/lib/database";
|
||
|
|
import Stripe from "stripe";
|
||
|
|
|
||
|
|
export async function POST(request: NextRequest) {
|
||
|
|
const body = await request.text();
|
||
|
|
const signature = request.headers.get("stripe-signature");
|
||
|
|
|
||
|
|
if (!signature) {
|
||
|
|
return NextResponse.json({ error: "No signature" }, { status: 400 });
|
||
|
|
}
|
||
|
|
|
||
|
|
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
|
||
|
|
if (!webhookSecret) {
|
||
|
|
console.error("Missing STRIPE_WEBHOOK_SECRET");
|
||
|
|
return NextResponse.json(
|
||
|
|
{ error: "Webhook secret not configured" },
|
||
|
|
{ status: 500 }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
let event: Stripe.Event;
|
||
|
|
|
||
|
|
try {
|
||
|
|
event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
|
||
|
|
} catch (err: any) {
|
||
|
|
console.error(`Webhook signature verification failed: ${err.message}`);
|
||
|
|
return NextResponse.json(
|
||
|
|
{ error: `Webhook Error: ${err.message}` },
|
||
|
|
{ status: 400 }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
const pool = getPool();
|
||
|
|
const client = await pool.connect();
|
||
|
|
|
||
|
|
try {
|
||
|
|
// Handle different event types
|
||
|
|
switch (event.type) {
|
||
|
|
case "checkout.session.completed": {
|
||
|
|
const session = event.data.object as Stripe.Checkout.Session;
|
||
|
|
const userId = session.metadata?.userId;
|
||
|
|
const tier = session.metadata?.tier;
|
||
|
|
|
||
|
|
console.log("Webhook received - userId:", userId, "tier:", tier);
|
||
|
|
|
||
|
|
if (!userId || !tier) {
|
||
|
|
console.error("Missing user or tier in session metadata");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if subscription exists
|
||
|
|
const existing = await client.query(
|
||
|
|
`SELECT id FROM subscriptions WHERE user_id = $1`,
|
||
|
|
[userId]
|
||
|
|
);
|
||
|
|
|
||
|
|
// Normalize tier name
|
||
|
|
const tierName = tier.toUpperCase();
|
||
|
|
console.log("Processing tier:", tierName);
|
||
|
|
const storageLimit =
|
||
|
|
tierName === "BASIC"
|
||
|
|
? 5
|
||
|
|
: tierName === "STANDARD"
|
||
|
|
? 20
|
||
|
|
: tierName === "PREMIUM"
|
||
|
|
? 100
|
||
|
|
: 5;
|
||
|
|
|
||
|
|
if (existing.rows.length > 0) {
|
||
|
|
// Update existing subscription
|
||
|
|
await client.query(
|
||
|
|
`UPDATE subscriptions
|
||
|
|
SET status = 'ACTIVE', tier = $1, storage_limit_gb = $2, updated_at = CURRENT_TIMESTAMP
|
||
|
|
WHERE user_id = $3`,
|
||
|
|
[tierName, storageLimit, userId]
|
||
|
|
);
|
||
|
|
console.log(`Subscription updated for user ${userId}`);
|
||
|
|
} else {
|
||
|
|
// Create new subscription
|
||
|
|
await client.query(
|
||
|
|
`INSERT INTO subscriptions (user_id, tier, status, storage_limit_gb, current_usage_gb)
|
||
|
|
VALUES ($1, $2, 'ACTIVE', $3, 0)`,
|
||
|
|
[userId, tierName, storageLimit]
|
||
|
|
);
|
||
|
|
console.log(`Subscription created for user ${userId}`);
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
case "customer.subscription.deleted":
|
||
|
|
case "customer.subscription.updated": {
|
||
|
|
const subscription = event.data.object as Stripe.Subscription;
|
||
|
|
const customerId = subscription.customer as string;
|
||
|
|
|
||
|
|
// Find user by customer ID and update subscription status
|
||
|
|
const status = subscription.status === "active" ? "ACTIVE" : "INACTIVE";
|
||
|
|
await client.query(
|
||
|
|
`UPDATE subscriptions
|
||
|
|
SET status = $1, updated_at = CURRENT_TIMESTAMP
|
||
|
|
WHERE stripe_customer_id = $2`,
|
||
|
|
[status, customerId]
|
||
|
|
);
|
||
|
|
|
||
|
|
console.log(`Subscription ${event.type} for customer ${customerId}`);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
default:
|
||
|
|
console.log(`Unhandled event type: ${event.type}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
return NextResponse.json({ received: true });
|
||
|
|
} catch (error) {
|
||
|
|
console.error("Error processing webhook:", error);
|
||
|
|
return NextResponse.json(
|
||
|
|
{ error: "Webhook processing failed" },
|
||
|
|
{ status: 500 }
|
||
|
|
);
|
||
|
|
} finally {
|
||
|
|
client.release();
|
||
|
|
}
|
||
|
|
}
|