const express = require('express'); const crypto = require('crypto'); const router = express.Router(); const dns = require('dns').promises; const db = require('../config/database'); const mailer = require('../config/mailer'); // ============================================ // E-Mail Validierung via DNS MX-Record // ============================================ async function verifyEmailDomain(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) return { valid: false, reason: 'Ungültiges E-Mail-Format' }; const domain = email.split('@')[1]; try { const records = await dns.resolveMx(domain); if (records && records.length > 0) return { valid: true }; return { valid: false, reason: 'Domain hat keine E-Mail-Server (MX-Records fehlen)' }; } catch (err) { return { valid: false, reason: 'E-Mail-Domain konnte nicht verifiziert werden' }; } } // ============================================ // IBAN Validierung (Modulo-97) // ============================================ const IBAN_LENGTHS = { AL:28,AD:24,AT:20,AZ:28,BH:22,BE:16,BA:20,BR:29,BG:22,CR:22,HR:21, CY:28,CZ:24,DK:18,DO:28,EE:20,FO:18,FI:18,FR:27,GE:22,DE:22,GI:23, GL:18,GT:28,HU:28,IS:26,IE:22,IL:23,IT:27,JO:30,KZ:20,KW:30,LV:21, LB:28,LI:21,LT:20,LU:20,MK:19,MT:31,MR:27,MU:30,MC:27,MD:24,ME:22, NL:18,NO:15,PK:24,PS:29,PL:28,PT:25,QA:29,RO:24,SM:27,SA:24,RS:22, SK:24,SI:19,ES:24,SE:24,CH:21,TN:24,TR:26,AE:23,GB:22,VG:24 }; function validateIBANServer(iban) { if (!iban) return { valid: true }; const clean = iban.replace(/\s/g, '').toUpperCase(); if (clean.length < 5) return { valid: false, error: 'IBAN zu kurz' }; const country = clean.substring(0, 2); if (!/^[A-Z]{2}$/.test(country)) return { valid: false, error: 'Ungültiger Ländercode' }; const expectedLen = IBAN_LENGTHS[country]; if (!expectedLen) return { valid: false, error: 'Ländercode nicht unterstützt' }; if (clean.length !== expectedLen) return { valid: false, error: country + '-IBAN muss ' + expectedLen + ' Zeichen haben' }; const rearranged = clean.substring(4) + clean.substring(0, 4); const numeric = rearranged.split('').map(c => { const code = c.charCodeAt(0); return code >= 65 ? (code - 55).toString() : c; }).join(''); let remainder = 0; for (let i = 0; i < numeric.length; i++) { remainder = (remainder * 10 + parseInt(numeric[i])) % 97; } if (remainder !== 1) return { valid: false, error: 'IBAN Prüfziffer ungültig' }; return { valid: true }; } // ============================================ // Bestätigungs-E-Mail Template // ============================================ function confirmationEmailHtml(member, confirmLink) { return `

Plusfit24

Mitgliedschaft bestätigen

Hallo ${member.first_name} ${member.last_name},

vielen Dank für deine Anmeldung bei PlusFit24! Um deine Mitgliedschaft zu aktivieren, bestätige bitte deine E-Mail-Adresse:

✅ Mitgliedschaft jetzt bestätigen

Deine Vertragsdaten:

📋 Tarif: ${member.tariff_name}

💰 Monatsbeitrag: ${Number(member.agreed_price).toFixed(2).replace('.', ',')} €

📦 Startpaket: ${Number(member.start_package_price).toFixed(2).replace('.', ',')} €

Dieser Link ist 24 Stunden gültig. Falls du diese Anmeldung nicht durchgeführt hast, ignoriere diese E-Mail.

PlusFit24 UG · Moosleiten 12 · 84089 Aiglsbach

`; } // ============================================ // POST /api/verify-email // ============================================ router.post('/verify-email', async (req, res) => { const { email } = req.body; if (!email) return res.json({ valid: false, reason: 'Keine E-Mail angegeben' }); const result = await verifyEmailDomain(email); res.json(result); }); // ============================================ // POST /api/submit-membership // ============================================ router.post('/submit-membership', async (req, res) => { try { const { tariff_id, salutation, title, first_name, last_name, birth_date, email, phone, street, address_addition, zip, city, bank_name, account_holder, iban, sepa_accepted, agb_accepted, datenschutz_accepted, data_correct, guardian_consent } = req.body; // Pflichtfelder if (!tariff_id || !first_name || !last_name || !birth_date || !email || !street || !zip || !city) { return res.json({ success: false, error: 'Bitte alle Pflichtfelder ausfüllen.' }); } if (!agb_accepted || !datenschutz_accepted || !data_correct) { return res.json({ success: false, error: 'Bitte alle Einverständniserklärungen bestätigen.' }); } // E-Mail validieren const emailCheck = await verifyEmailDomain(email); if (!emailCheck.valid) { return res.json({ success: false, error: 'E-Mail-Adresse ist nicht erreichbar: ' + emailCheck.reason }); } // IBAN prüfen if (iban && iban.trim()) { const ibanCheck = validateIBANServer(iban.trim()); if (!ibanCheck.valid) { return res.json({ success: false, error: 'IBAN ungültig: ' + ibanCheck.error }); } } // Alter berechnen const birthDateObj = new Date(birth_date); const today = new Date(); let age = today.getFullYear() - birthDateObj.getFullYear(); const mo = today.getMonth() - birthDateObj.getMonth(); if (mo < 0 || (mo === 0 && today.getDate() < birthDateObj.getDate())) age--; const is_minor = age < 18 ? 1 : 0; if (is_minor && !guardian_consent) { return res.json({ success: false, error: 'Bei Minderjährigen ist die Einverständniserklärung der Erziehungsberechtigten erforderlich.' }); } if (age < 14) { return res.json({ success: false, error: 'Das Mindestalter für eine Mitgliedschaft beträgt 14 Jahre.' }); } // Tarif laden const [tariffs] = await db.query('SELECT * FROM tariffs WHERE id = ? AND active = 1', [tariff_id]); if (tariffs.length === 0) { return res.json({ success: false, error: 'Ungültiger oder inaktiver Tarif.' }); } const tariff = tariffs[0]; // Berechnungen const startPackagePrice = parseFloat(tariff.start_package_price) || 35.00; const contractStart = new Date(today); contractStart.setDate(contractStart.getDate() + 15); const daysInMonth = new Date(contractStart.getFullYear(), contractStart.getMonth() + 1, 0).getDate(); const remainingDays = daysInMonth - contractStart.getDate() + 1; const partialMonth = Math.round((parseFloat(tariff.price_monthly) / daysInMonth) * remainingDays * 100) / 100; const firstPaymentAmt = Math.round((partialMonth + startPackagePrice) * 100) / 100; const contractEnd = new Date(contractStart); contractEnd.setMonth(contractEnd.getMonth() + tariff.duration_months); contractEnd.setDate(contractEnd.getDate() - 1); // Tokens generieren const access_token = crypto.randomBytes(16).toString('hex'); const confirmation_token = crypto.randomBytes(32).toString('hex'); // In DB speichern — Status: pending await db.query(` INSERT INTO memberships (tariff_id, salutation, title, first_name, last_name, birth_date, email, phone, street, address_addition, zip, city, bank_name, account_holder, iban, sepa_accepted, agb_accepted, datenschutz_accepted, data_correct, guardian_consent, is_minor, access_token, confirmation_token, agreed_price, agreed_duration, start_package_price, signup_date, contract_start, contract_end, effective_end, first_payment_date, first_payment_amt, status, reviewed) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) `, [ tariff_id, salutation, title || '', first_name, last_name, birth_date, email, phone || '', street, address_addition || '', zip, city, bank_name || '', account_holder || '', iban || '', sepa_accepted ? 1 : 0, agb_accepted ? 1 : 0, datenschutz_accepted ? 1 : 0, data_correct ? 1 : 0, guardian_consent ? 1 : 0, is_minor, access_token, confirmation_token, tariff.price_monthly, tariff.duration_months, startPackagePrice, today.toISOString().split('T')[0], contractStart.toISOString().split('T')[0], contractEnd.toISOString().split('T')[0], contractEnd.toISOString().split('T')[0], contractStart.toISOString().split('T')[0], firstPaymentAmt, 'pending', 0 ]); // Bestätigungs-E-Mail senden const baseUrl = process.env.APP_URL || 'https://plusfit24.software-joksch.com'; const confirmLink = `${baseUrl}/confirm/${confirmation_token}`; const memberData = { first_name, last_name, agreed_price: tariff.price_monthly, start_package_price: startPackagePrice, tariff_name: tariff.name }; try { await mailer.sendMail({ from: process.env.MAIL_FROM, to: email, subject: 'PlusFit24 – Bitte bestätige deine Mitgliedschaft', html: confirmationEmailHtml(memberData, confirmLink) }); } catch (mailErr) { console.error('E-Mail Fehler:', mailErr.message); // Trotzdem Erfolg zurückgeben — Admin kann manuell bestätigen } res.json({ success: true, pending: true }); } catch (err) { console.error('Submit error:', err); res.json({ success: false, error: 'Serverfehler. Bitte versuche es später erneut.' }); } }); module.exports = router;