import { NextResponse } from "next/server" import { verifyPaypalWebhook } from "@/lib/paypal/webhook" import { decodeCustomId, getSubscription } from "@/lib/paypal/checkout" import { fulfillSubscription, fulfillLifetime, markPaypalSubscriptionInactive } from "@/lib/paypal/fulfill" // Inbound PayPal webhook. Signature is verified via PayPal's API using // PAYPAL_WEBHOOK_ID; unverified events are rejected. export async function POST(request: Request) { const body = await request.text() const valid = await verifyPaypalWebhook(request.headers, body) if (!valid) return NextResponse.json({ error: "invalid signature" }, { status: 400 }) let event: { event_type?: string; resource?: Record } try { event = JSON.parse(body) } catch { return NextResponse.json({ ok: true }) } const type = event.event_type ?? "" const resource = (event.resource ?? {}) as Record try { switch (type) { case "BILLING.SUBSCRIPTION.ACTIVATED": case "BILLING.SUBSCRIPTION.UPDATED": { const decoded = decodeCustomId(resource.custom_id) if (decoded && resource.id) { await fulfillSubscription( decoded.userId, decoded.plan, resource.id, resource.billing_info?.next_billing_time, "active" ) } break } case "PAYMENT.SALE.COMPLETED": { // A recurring payment cleared — refresh status + next billing date. const subId = resource.billing_agreement_id as string | undefined if (subId) { const sub = await getSubscription(subId) const decoded = decodeCustomId(sub?.custom_id) if (sub && decoded) { await fulfillSubscription( decoded.userId, decoded.plan, subId, sub.billing_info?.next_billing_time, "active" ) } } break } case "BILLING.SUBSCRIPTION.CANCELLED": case "BILLING.SUBSCRIPTION.EXPIRED": { if (resource.id) { await markPaypalSubscriptionInactive( resource.id, type.endsWith("CANCELLED") ? "canceled" : "expired", true ) } break } case "BILLING.SUBSCRIPTION.SUSPENDED": { if (resource.id) await markPaypalSubscriptionInactive(resource.id, "suspended", false) break } case "PAYMENT.CAPTURE.COMPLETED": { // Lifetime order capture (backup to the return handler). const decoded = decodeCustomId(resource.custom_id) if (decoded && decoded.plan === "lifetime") await fulfillLifetime(decoded.userId) break } } } catch { // Never loop forever on a handler bug — PayPal retries non-2xx. } return NextResponse.json({ received: true }) }