import express from 'express'; import cors from 'cors'; import Stripe from 'stripe'; import dotenv from 'dotenv'; dotenv.config(); const app = express(); const port = process.env.PORT || 3001; // Initialize Stripe const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); // Middleware app.use(cors({ origin: process.env.FRONTEND_URL || 'http://localhost:5173', })); app.use(express.json()); // Health check app.get('/api/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // Create Stripe Checkout Session app.post('/api/checkout', async (req, res) => { try { const { orderData } = req.body; // Validate required fields if (!orderData?.contact?.email || !orderData?.contact?.name) { return res.status(400).json({ error: 'Name and email are required' }); } // Create Stripe Checkout Session const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], mode: 'payment', customer_email: orderData.contact.email, line_items: [ { price_data: { currency: 'eur', product_data: { name: 'Website in 48h', description: `${orderData.websiteType} | ${orderData.style} | ${orderData.theme === 'dark' ? 'Dunkel' : 'Hell'}`, images: ['https://webklar.de/og-image.png'], // Optional }, unit_amount: 19900, // 199€ in cents }, quantity: 1, }, ], metadata: { customerName: orderData.contact.name, customerCompany: orderData.contact.company || '', customerPhone: orderData.contact.phone || '', websiteType: orderData.websiteType, style: orderData.style, theme: orderData.theme, colorPrimary: orderData.colors?.primary || '#0A400C', colorSecondary: orderData.colors?.secondary || '#819067', colorAccent: orderData.colors?.accent || '#B1AB86', customReferenceUrl: orderData.customInput?.referenceUrl || '', customDescription: orderData.customInput?.description || '', }, success_url: `${process.env.FRONTEND_URL || 'http://localhost:5173'}/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${process.env.FRONTEND_URL || 'http://localhost:5173'}/konfigurator`, }); res.json({ sessionId: session.id, url: session.url }); } catch (error) { console.error('Stripe error:', error); res.status(500).json({ error: 'Failed to create checkout session', details: error.message }); } }); // Verify session (for success page) app.get('/api/session/:sessionId', async (req, res) => { try { const session = await stripe.checkout.sessions.retrieve(req.params.sessionId); res.json({ id: session.id, status: session.payment_status, customerEmail: session.customer_email, amountTotal: session.amount_total, metadata: session.metadata, }); } catch (error) { console.error('Session verification error:', error); res.status(404).json({ error: 'Session not found' }); } }); // Stripe Webhook (for production) app.post('/api/webhook', express.raw({ type: 'application/json' }), async (req, res) => { const sig = req.headers['stripe-signature']; const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; if (!webhookSecret) { console.warn('Webhook secret not configured'); return res.status(400).send('Webhook secret not configured'); } try { const event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret); switch (event.type) { case 'checkout.session.completed': const session = event.data.object; console.log('āœ… Payment successful:', session.id); // TODO: Save order to database, send confirmation email, etc. break; case 'payment_intent.payment_failed': console.log('āŒ Payment failed:', event.data.object.id); break; default: console.log(`Unhandled event type: ${event.type}`); } res.json({ received: true }); } catch (error) { console.error('Webhook error:', error.message); res.status(400).send(`Webhook Error: ${error.message}`); } }); app.listen(port, () => { console.log(`\nšŸš€ Server running on http://localhost:${port}`); console.log(`\nšŸ“‹ Endpoints:`); console.log(` GET /api/health`); console.log(` POST /api/checkout`); console.log(` GET /api/session/:sessionId`); console.log(` POST /api/webhook\n`); });