Main_Website-Oblistudios/payments.js

78 lines
2.9 KiB
JavaScript

// payments.js
import express from "express";
import Stripe from "stripe";
import { pool } from "./db.js";
import { sendDiscordOrder } from "./discord.js";
const router = express.Router();
const stripe = new Stripe(process.env.STRIPE_SECRET);
router.post("/api/create-checkout", async (req, res) => {
const user = req.session.user;
if (!user) return res.sendStatus(401);
const { items } = req.body;
// (Optional) enforce again that requested server qty <= currently reserved
const line_items = items.map(i => ({
quantity: i.qty,
price_data: {
currency: "usd",
product_data: { name: i.name },
unit_amount: Math.round(i.price * 100)
}
}));
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items,
success_url: process.env.SUCCESS_URL,
cancel_url: process.env.CANCEL_URL,
metadata: {
discord_user_id: user.id,
discord_username: user.username,
items: JSON.stringify(items)
}
});
res.json({ url: session.url });
});
// Webhook: payment succeeded / refunded
router.post("/api/stripe/webhook", express.raw({ type: "application/json" }), async (req, res) => {
const sig = req.headers["stripe-signature"];
let evt;
try {
evt = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (e) {
return res.status(400).send(`Webhook Error: ${e.message}`);
}
if (evt.type === "checkout.session.completed") {
const s = evt.data.object;
const items = JSON.parse(s.metadata.items);
const total = items.reduce((a, i) => a + i.price * i.qty, 0);
await pool.query(
"INSERT INTO orders(id, discord_user_id, discord_username, line_items, total_cents, stripe_payment_intent, status) VALUES (gen_random_uuid(), $1,$2,$3,$4,$5,'paid')",
[s.metadata.discord_user_id, s.metadata.discord_username, JSON.stringify(items), Math.round(total * 100), s.payment_intent]
);
await sendDiscordOrder({ user: s.metadata, items, total });
}
if (evt.type === "charge.refunded") {
const pi = evt.data.object.payment_intent;
// Find the order and mark refunded
const { rows } = await pool.query("UPDATE orders SET status='refunded' WHERE stripe_payment_intent=$1 RETURNING line_items", [pi]);
if (rows.length) {
const items = rows[0].line_items;
const returned = items.filter(i => serverIds.has(i.id)).reduce((a, i) => a + i.qty, 0);
if (returned > 0) {
await pool.query("UPDATE inventory_state SET value_int = value_int + $1 WHERE key='server_slots_remaining'", [returned]);
}
await sendDiscordOrder({ refund: true, items });
}
}
res.json({ received: true });
});
export default router;