123 lines
4.6 KiB
TypeScript
123 lines
4.6 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import Stripe from "stripe";
|
|
import nodemailer from "nodemailer";
|
|
|
|
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
|
|
|
|
const mailer = nodemailer.createTransport({
|
|
host: "smtp.gmail.com",
|
|
port: 587,
|
|
secure: false,
|
|
auth: {
|
|
user: process.env.SMTP_USER, // nolankovacs@verimundsolutions.com Gmail address
|
|
pass: process.env.SMTP_PASS, // Gmail App Password
|
|
},
|
|
});
|
|
|
|
// Tier → expiry days (null = lifetime)
|
|
const TIER_EXPIRY: Record<string, number | null> = {
|
|
Weekly: 7,
|
|
Monthly: 30,
|
|
Annual: 365,
|
|
Lifetime: null,
|
|
};
|
|
|
|
async function generateLicense(tier: string, email: string): Promise<string> {
|
|
const res = await fetch(`${process.env.API_URL}/admin/generate-license`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"X-Admin-Key": process.env.ADMIN_KEY!,
|
|
},
|
|
body: JSON.stringify({
|
|
tier: tier.toLowerCase(),
|
|
email,
|
|
days_valid: TIER_EXPIRY[tier],
|
|
channel: "stable",
|
|
source: "stripe",
|
|
}),
|
|
});
|
|
|
|
if (!res.ok) throw new Error(`License generation failed: ${res.status}`);
|
|
const data = await res.json();
|
|
return data.license_key;
|
|
}
|
|
|
|
async function sendLicenseEmail(email: string, tier: string, licenseKey: string) {
|
|
const expiryText = TIER_EXPIRY[tier] === null
|
|
? "Your license never expires."
|
|
: `Your license is valid for ${TIER_EXPIRY[tier]} days from today.`;
|
|
|
|
await mailer.sendMail({
|
|
from: `"Verimund Solutions" <noreply@verimundsolutions.com>`,
|
|
to: email,
|
|
subject: `Your UIT ${tier} License Key`,
|
|
html: `
|
|
<div style="background:#0a0a0a;color:#ffffff;font-family:Arial,sans-serif;padding:40px;max-width:520px;margin:0 auto;">
|
|
<p style="color:#4ade80;font-size:11px;font-weight:600;letter-spacing:0.2em;text-transform:uppercase;margin-bottom:24px;">
|
|
Verimund Solutions
|
|
</p>
|
|
<h1 style="font-size:24px;font-weight:600;margin-bottom:8px;">Your license key is ready.</h1>
|
|
<p style="color:#888888;font-size:14px;margin-bottom:32px;">
|
|
Thank you for purchasing UIT ${tier}. ${expiryText}
|
|
</p>
|
|
|
|
<div style="background:#111111;border:1px solid #222222;border-radius:4px;padding:20px;margin-bottom:32px;">
|
|
<p style="color:#888888;font-size:11px;text-transform:uppercase;letter-spacing:0.15em;margin-bottom:8px;">License Key</p>
|
|
<p style="font-family:monospace;font-size:18px;font-weight:600;letter-spacing:0.05em;color:#ffffff;word-break:break-all;">
|
|
${licenseKey}
|
|
</p>
|
|
</div>
|
|
|
|
<p style="color:#888888;font-size:13px;line-height:1.7;margin-bottom:16px;">
|
|
To activate, open UIT and enter this key when prompted. The license will bind to your machine on first use.
|
|
</p>
|
|
<p style="color:#888888;font-size:13px;line-height:1.7;margin-bottom:32px;">
|
|
If you need to transfer your license to a new machine, contact us at
|
|
<a href="mailto:support@verimundsolutions.com" style="color:#ffffff;">support@verimundsolutions.com</a>.
|
|
</p>
|
|
|
|
<hr style="border:none;border-top:1px solid #222222;margin-bottom:24px;" />
|
|
<p style="color:#3a3a3a;font-size:11px;">
|
|
© ${new Date().getFullYear()} Verimund Solutions. This email was sent to ${email} because you purchased a UIT license.
|
|
</p>
|
|
</div>
|
|
`,
|
|
});
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
const body = await req.text();
|
|
const sig = req.headers.get("stripe-signature")!;
|
|
|
|
let event: Stripe.Event;
|
|
try {
|
|
event = stripe.webhooks.constructEvent(body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
|
|
} catch {
|
|
return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
|
|
}
|
|
|
|
if (event.type === "checkout.session.completed") {
|
|
const session = event.data.object as Stripe.Checkout.Session;
|
|
const email = session.customer_details?.email;
|
|
const tier = session.metadata?.tier;
|
|
|
|
if (!email || !tier) {
|
|
console.error("[WEBHOOK] Missing email or tier", { email, tier });
|
|
return NextResponse.json({ error: "Missing data" }, { status: 400 });
|
|
}
|
|
|
|
try {
|
|
const licenseKey = await generateLicense(tier, email);
|
|
await sendLicenseEmail(email, tier, licenseKey);
|
|
console.log(`[WEBHOOK] License issued: ${tier} → ${email}`);
|
|
} catch (err) {
|
|
console.error("[WEBHOOK] Failed to issue license:", err);
|
|
// Return 200 so Stripe doesn't retry — log the failure for manual follow-up
|
|
// TODO: add to a failed_deliveries table for retry
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({ received: true });
|
|
}
|