aethex-forge/api/nexus/payments/create-intent.ts
2025-11-16 06:57:56 +00:00

127 lines
3.8 KiB
TypeScript

import type { VercelRequest, VercelResponse } from "@vercel/node";
import Stripe from "stripe";
import { getAdminClient } from "../../_supabase.js";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", {
apiVersion: "2024-11-20",
});
export default async function handler(req: VercelRequest, res: VercelResponse) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
const admin = getAdminClient();
// Only authenticated requests
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ error: "Unauthorized" });
}
const token = authHeader.replace("Bearer ", "");
const {
data: { user },
error: authError,
} = await admin.auth.getUser(token);
if (authError || !user) {
return res.status(401).json({ error: "Invalid token" });
}
try {
const { opportunityId, creatorId, amount, contractType } = req.body;
if (!opportunityId || !creatorId || !amount || !contractType) {
return res.status(400).json({
error:
"Missing required fields: opportunityId, creatorId, amount, contractType",
});
}
// Verify the user is the client who posted the opportunity
const { data: opportunity, error: oppError } = await admin
.from("nexus_opportunities")
.select("posted_by, title")
.eq("id", opportunityId)
.single();
if (oppError || !opportunity || opportunity.posted_by !== user.id) {
return res.status(403).json({
error: "Not authorized to post contracts for this opportunity",
});
}
// Verify the creator has applied
const { data: application, error: appError } = await admin
.from("nexus_applications")
.select("id, status")
.eq("opportunity_id", opportunityId)
.eq("creator_id", creatorId)
.single();
if (appError || !application || application.status !== "accepted") {
return res
.status(400)
.json({ error: "Creator application not accepted" });
}
// Calculate commission (20% to AeThex)
const commissionPercent = 20;
const commissionAmount = Math.round((amount * commissionPercent) / 100);
const creatorPayout = Math.round(amount - commissionAmount);
// Create Stripe payment intent
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100), // Convert to cents
currency: "usd",
metadata: {
opportunityId,
creatorId,
clientId: user.id,
contractType,
},
description: `NEXUS Contract: ${opportunity.title}`,
});
// Create contract record
const { data: contract, error: contractError } = await admin
.from("nexus_contracts")
.insert({
opportunity_id: opportunityId,
creator_id: creatorId,
client_id: user.id,
title: opportunity.title,
contract_type: contractType,
total_amount: amount,
aethex_commission_percent: commissionPercent,
aethex_commission_amount: commissionAmount,
creator_payout_amount: creatorPayout,
status: "pending",
})
.select()
.single();
if (contractError) {
return res.status(500).json({ error: contractError.message });
}
// Store payment intent ID in contract
await admin
.from("nexus_contracts")
.update({ stripe_payment_intent_id: paymentIntent.id })
.eq("id", contract.id);
return res.status(200).json({
clientSecret: paymentIntent.client_secret,
contractId: contract.id,
paymentIntentId: paymentIntent.id,
amount,
commissionAmount,
creatorPayout,
});
} catch (error: any) {
console.error("Payment intent error:", error);
return res.status(500).json({ error: error?.message || "Server error" });
}
}