/** * Main API Routes * General API endpoints */ import express from 'express' import { asyncHandler, NotFoundError, ValidationError } from '../middleware/errorHandler.mjs' import { validate, schemas, rules } from '../middleware/validate.mjs' import { respond } from '../utils/response.mjs' import { products, questions, submissions, orders } from '../services/database.mjs' import Stripe from 'stripe' import { config } from '../config/index.mjs' const router = express.Router() const stripe = new Stripe(config.stripe.secretKey) /** * GET /api/products * Get all active products */ router.get('/products', asyncHandler(async (req, res) => { const productList = await products.getActive() respond.success(res, productList) })) /** * GET /api/questions * Get questions for a product */ router.get('/questions', asyncHandler(async (req, res) => { const { productSlug } = req.query if (!productSlug) { throw new ValidationError('productSlug ist erforderlich', { productSlug: ['Pflichtfeld'] }) } const product = await products.getBySlug(productSlug) if (!product) { throw new NotFoundError('Produkt') } const questionList = await questions.getByProduct(product.$id) respond.success(res, questionList) })) /** * POST /api/submissions * Create a new submission */ router.post('/submissions', validate({ body: { productSlug: [rules.required('productSlug')], answers: [rules.required('answers'), rules.isObject('answers')], }, }), asyncHandler(async (req, res) => { const { productSlug, answers } = req.body const product = await products.getBySlug(productSlug) if (!product) { throw new NotFoundError('Produkt') } // Create submission const submission = await submissions.create({ productId: product.$id, status: 'draft', customerEmail: answers.email || answers.customer_email || null, customerName: answers.name || answers.customer_name || null, finalSummaryJson: JSON.stringify(answers), priceCents: product.priceCents, currency: product.currency, }) // Store answers separately await orders.create(submission.$id, { answers }) respond.created(res, { submissionId: submission.$id }) }) ) /** * POST /api/checkout * Create Stripe checkout session for one-time payment */ router.post('/checkout', validate({ body: { submissionId: [rules.required('submissionId')], }, }), asyncHandler(async (req, res) => { const { submissionId } = req.body // Get submission const submission = await submissions.create let submissionDoc try { const { db, Collections } = await import('../services/database.mjs') submissionDoc = await db.get(Collections.SUBMISSIONS, submissionId) } catch (error) { throw new NotFoundError('Submission') } // Create Stripe checkout session const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], line_items: [ { price_data: { currency: submissionDoc.currency || 'eur', product_data: { name: 'Email Sortierer Service', description: 'Personalisiertes E-Mail-Sortier-Setup', }, unit_amount: submissionDoc.priceCents || 4900, }, quantity: 1, }, ], mode: 'payment', success_url: `${config.frontendUrl}/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${config.frontendUrl}/cancel`, metadata: { submissionId, }, customer_email: submissionDoc.customerEmail || undefined, }) respond.success(res, { url: session.url, sessionId: session.id }) }) ) /** * GET /api/submission/:id * Get submission details */ router.get('/submission/:id', asyncHandler(async (req, res) => { const { id } = req.params const { db, Collections } = await import('../services/database.mjs') const submission = await db.get(Collections.SUBMISSIONS, id) // Don't expose sensitive data respond.success(res, { id: submission.$id, status: submission.status, createdAt: submission.$createdAt, }) })) /** * GET /api/config * Get public configuration */ router.get('/config', (req, res) => { respond.success(res, { features: { gmail: Boolean(config.google.clientId), outlook: Boolean(config.microsoft.clientId), ai: Boolean(config.mistral.apiKey), }, pricing: { basic: { price: 9, currency: 'EUR', accounts: 1 }, pro: { price: 19, currency: 'EUR', accounts: 3 }, business: { price: 49, currency: 'EUR', accounts: 10 }, }, }) }) export default router