import 'dotenv/config'; import express from 'express'; import cors from 'cors'; import { initializeDatabase } from './config/database.js'; import checkoutRoutes from './routes/checkout.js'; import webhookRoutes from './routes/webhooks.js'; const app = express(); const PORT = process.env.PORT || 3001; // Initialize database console.log('🗄️ Initializing database...'); initializeDatabase(); // CORS configuration const corsOptions = { origin: (origin, callback) => { const allowedOrigins = [ process.env.FRONTEND_URL || 'http://localhost:5173', 'http://localhost:5173', // Always allow local development 'http://localhost:3800', // Docker frontend ].filter(Boolean); // Allow requests with no origin (mobile apps, Postman, etc.) if (!origin) { return callback(null, true); } if (allowedOrigins.includes(origin)) { callback(null, true); } else { console.warn(`🚫 CORS blocked request from origin: ${origin}`); callback(new Error('Not allowed by CORS')); } }, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], }; app.use(cors(corsOptions)); // IMPORTANT: Webhook routes must come BEFORE express.json() middleware // because Stripe webhooks require raw body for signature verification app.use('/api/webhooks', webhookRoutes); // JSON body parser for all other routes app.use(express.json()); // Health check endpoint app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // API Routes app.use('/api/checkout', checkoutRoutes); // Error handling middleware app.use((err, req, res, next) => { console.error('❌ Unhandled error:', err); res.status(500).json({ error: 'Internal server error', message: process.env.NODE_ENV === 'development' ? err.message : undefined, }); }); // 404 handler app.use((req, res) => { res.status(404).json({ error: 'Not found' }); }); // Start server app.listen(PORT, () => { console.log(''); console.log('🚀 Puffin App Server'); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log(`📡 Server running on port ${PORT}`); console.log(`🌐 Frontend URL: ${process.env.FRONTEND_URL}`); console.log(`🔑 Stripe configured: ${!!process.env.STRIPE_SECRET_KEY}`); console.log(`🌱 Wren API configured: ${!!process.env.WREN_API_TOKEN}`); console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); console.log(''); console.log('📝 Available endpoints:'); console.log(` GET http://localhost:${PORT}/health`); console.log(` POST http://localhost:${PORT}/api/checkout/create-session`); console.log(` GET http://localhost:${PORT}/api/checkout/session/:sessionId`); console.log(` POST http://localhost:${PORT}/api/webhooks/stripe`); console.log(''); console.log('🎣 Webhook events handled:'); console.log(' - checkout.session.completed'); console.log(' - checkout.session.async_payment_succeeded'); console.log(' - checkout.session.async_payment_failed'); console.log(' - checkout.session.expired'); console.log(''); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('👋 SIGTERM received, shutting down gracefully...'); process.exit(0); }); process.on('SIGINT', () => { console.log('👋 SIGINT received, shutting down gracefully...'); process.exit(0); });