File size: 3,580 Bytes
fbf3514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d219561
fbf3514
 
d219561
fbf3514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d219561
fbf3514
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import { NextRequest, NextResponse } from "next/server";
import Razorpay from "razorpay";
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
);

let resend: any = null;
if (process.env.RESEND_API_KEY) {
  import("resend").then(({ Resend }) => { resend = new Resend(process.env.RESEND_API_KEY); });
}

export async function POST(req: NextRequest) {
  const rawBody = await req.text();
  const signature = req.headers.get("x-razorpay-signature") || "";
  const webhookSecret = process.env.RAZORPAY_WEBHOOK_SECRET!;

  // Verify signature
  const isValid = Razorpay.validateWebhookSignature(rawBody, signature, webhookSecret);
  if (!isValid) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
  }

  const event = JSON.parse(rawBody);
  const eventType: string = event.event;

  switch (eventType) {
    case "subscription.activated": {
      const sub = event.payload.subscription.entity;
      const plan = sub.notes?.plan || "pro";
      const userId = sub.notes?.user_id;

      if (userId) {
        const { data } = await supabase
          .from("profiles")
          .update({
            plan,
            razorpay_subscription_id: sub.id,
            updated_at: new Date().toISOString(),
          })
          .eq("id", userId)
          .select("email")
          .single();

        // Send welcome email
        if (data?.email && resend) {
          await resend.emails.send({
            from: "ClauseGuard <noreply@clauseguardweb.netlify.app>",
            to: [data.email],
            subject: `Welcome to ClauseGuard ${plan.charAt(0).toUpperCase() + plan.slice(1)}`,
            html: `<p>Your ${plan} subscription is active. You now have unlimited scans.</p><p><a href="https://clauseguardweb.netlify.app/dashboard-pages/dashboard">Go to dashboard</a></p>`,
          });
        }
      }
      break;
    }

    case "subscription.charged": {
      // Recurring payment succeeded — nothing to update, plan already active
      break;
    }

    case "subscription.cancelled":
    case "subscription.completed": {
      const sub = event.payload.subscription.entity;
      const userId = sub.notes?.user_id;

      if (userId) {
        await supabase
          .from("profiles")
          .update({
            plan: "free",
            razorpay_subscription_id: null,
            updated_at: new Date().toISOString(),
          })
          .eq("id", userId);
      }
      break;
    }

    case "subscription.halted": {
      const sub = event.payload.subscription.entity;
      const userId = sub.notes?.user_id;

      if (userId) {
        const { data } = await supabase
          .from("profiles")
          .select("email")
          .eq("id", userId)
          .single();

        if (data?.email && resend) {
          await resend.emails.send({
            from: "ClauseGuard <noreply@clauseguardweb.netlify.app>",
            to: [data.email],
            subject: "Payment failed — subscription paused",
            html: "<p>Your payment failed and your subscription has been paused. Please update your payment method to continue.</p>",
          });
        }
      }
      break;
    }

    case "payment.failed": {
      // Razorpay auto-retries for subscriptions — log for monitoring
      const payment = event.payload.payment.entity;
      console.warn("Payment failed:", payment.id, payment.error_description);
      break;
    }
  }

  return NextResponse.json({ status: "ok" });
}