import { Router, Response, NextFunction } from 'express'; import { body, validationResult } from 'express-validator'; import prisma from '../lib/prisma'; import { optionalAuth, authenticate, requireRole, AuthRequest } from '../middleware/auth'; const router = Router(); // POST /api/donations — public (guest) or authenticated // Stripe integration stub: stores intent, returns client_secret placeholder router.post('/', optionalAuth, [ body('amount').isFloat({ min: 1 }).withMessage('Amount must be at least $1'), body('campaign').optional().isIn(['land_preservation', 'volunteer_equipment', 'general']), ], async (req: AuthRequest, res: Response, next: NextFunction) => { const errors = validationResult(req); if (!errors.isEmpty()) { res.status(422).json({ errors: errors.array() }); return; } try { const { amount, campaign } = req.body; // STUB: In production, create a Stripe PaymentIntent here and return client_secret // const intent = await stripe.paymentIntents.create({ amount: Math.round(amount * 100), currency: 'usd' }); const stripe_payment_id = `pi_stub_${Date.now()}`; const donation = await prisma.donation.create({ data: { donor_id: req.user?.userId ?? null, amount: parseFloat(amount), campaign: campaign || 'general', stripe_payment_id, }, }); res.status(201).json({ donation, // stub: replace with real Stripe client_secret for frontend confirmation client_secret: `${stripe_payment_id}_secret`, message: 'Stripe integration pending — payment recorded as stub', }); } catch (err) { next(err); } } ); // GET /api/donations — admin only router.get('/', authenticate, requireRole('admin'), async (_req: AuthRequest, res: Response, next: NextFunction) => { try { const donations = await prisma.donation.findMany({ include: { donor: { select: { id: true, name: true, email: true } } }, orderBy: { created_at: 'desc' }, }); res.json(donations); } catch (err) { next(err); } }); export default router;