// controllers/payment.controller.js import Stripe from "stripe"; import { Payment } from "../models/payment.model.js"; // ✅ Load Stripe Secret Key from .env const stripe = new Stripe("sk_test_51SB8SnIFk8fh986GkYaNPVSfZzh6gcuXhq3tOa5hyE4U4vYIqrHwyGRu2OE1N5TNW39tJmfFOyYfsh4HcZOjlsj100xIeM46zU", { apiVersion: "2022-11-15", }); /** * 🔹 Option 1: PaymentIntent API (client uses clientSecret) */ export async function createPaymentIntent(req, res) { try { const { amount } = req.body; if (!amount) return res.status(400).json({ error: "amount is required" }); const paymentIntent = await stripe.paymentIntents.create({ amount: Math.round(amount * 100), // dollars → cents currency: "usd", automatic_payment_methods: { enabled: true }, }); await Payment.create({ amount: Math.round(amount * 100), stripePaymentIntentId: paymentIntent.id, status: "pending", }); res.json({ clientSecret: paymentIntent.client_secret }); } catch (err) { console.error("❌ Error creating PaymentIntent:", err); res.status(500).json({ error: "Internal Server Error" }); } } /** * 🔹 Option 2: Stripe Checkout Session (redirect flow) */ export async function createCheckoutSession(req, res) { try { const { email, amount, planId } = req.body; if (!email || !amount) { return res.status(400).json({ error: "email and amount are required" }); } const session = await stripe.checkout.sessions.create({ payment_method_types: ["card"], mode: "payment", customer_email: email, line_items: [ { price_data: { currency: "usd", product_data: { name: planId || "SEO Plan" }, unit_amount: Math.round(amount * 100), }, quantity: 1, }, ], success_url: "https://app.crawlerx.co/success", cancel_url: "https://app.crawlerx.co/cancel", }); // Save to DB using stripeSessionId instead of stripePaymentIntentId await Payment.create({ email, amount: Math.round(amount * 100), stripeSessionId: session.id, // ✅ use session id status: "pending", }); res.json({ sessionId: session.id }); } catch (err) { console.error("❌ Error creating checkout session:", err); res.status(500).json({ error: "Internal Server Error" }); } } /** * 🔹 Stripe Webhook * Stripe requires `express.raw({ type: "application/json" })` in route */ export async function handleWebhook(req, res) { const sig = req.headers["stripe-signature"]; let event; try { event = stripe.webhooks.constructEvent( req.rawBody, // Must be raw body sig, process.env.STRIPE_WEBHOOK_SECRET ); } catch (err) { console.error("❌ Webhook signature verification failed:", err.message); return res.status(400).send(`Webhook Error: ${err.message}`); } switch (event.type) { case "payment_intent.succeeded": { const paymentIntent = event.data.object; console.log("✅ PaymentIntent succeeded:", paymentIntent.id); await Payment.findOneAndUpdate( { stripePaymentIntentId: paymentIntent.id }, { status: "succeeded" } ); break; } case "checkout.session.completed": { const session = event.data.object; console.log("✅ Checkout session completed:", session.id); // Update DB record created earlier await Payment.findOneAndUpdate( { email: session.customer_email, status: "pending" }, { stripePaymentIntentId: session.payment_intent, status: "succeeded", } ); break; } default: console.log(`Unhandled event type ${event.type}`); } res.json({ received: true }); }