add backend/src/routes/bankingWebhook.ts

This commit is contained in:
Giuseppe Lombardo 2026-08-12 16:28:50 +00:00
parent 5c9a5e4f08
commit 17abbf2a33

View File

@ -0,0 +1,69 @@
import { Router, Request, Response } from 'express';
import express from 'express';
import { PrismaClient } from '@prisma/client';
import { verifyWebhookSignature } from '../modules/banking/signature';
import { PaymentService } from '../modules/banking/paymentService';
import { IncomingBankWebhookPayload } from '../modules/banking/types';
const prisma = new PrismaClient();
const paymentService = new PaymentService(prisma);
/**
* Router NUR für den Banking-Webhook. Trägt seinen eigenen
* `express.raw()`-Body-Parser direkt an der Route, damit er unabhängig
* davon funktioniert, wie der globale JSON-Parser in app.ts registriert ist
* (kein Risiko, dass der Stream doppelt gelesen wird).
*/
export const bankingWebhookRawRouter = Router();
const BANKING_WEBHOOK_SECRET = process.env.BANKING_WEBHOOK_SECRET!;
const WG_ACCOUNT_IBAN = process.env.WG_ACCOUNT_IBAN!; // Schutz vor Fremdkonto-Events
bankingWebhookRawRouter.post(
'/webhooks/banking/transactions',
express.raw({ type: 'application/json' }),
async (req: Request, res: Response) => {
const rawBody: string = req.body instanceof Buffer ? req.body.toString('utf8') : '';
const signature = req.header('X-Signature');
const isValid = verifyWebhookSignature(rawBody, signature, BANKING_WEBHOOK_SECRET);
if (!isValid) {
return res.status(401).json({ error: 'invalid_signature' });
}
let payload: IncomingBankWebhookPayload;
try {
payload = JSON.parse(rawBody);
} catch {
return res.status(400).json({ error: 'invalid_json' });
}
if (payload.accountIban !== WG_ACCOUNT_IBAN) {
// Kein Fehler, aber irrelevantes Konto -> ignorieren (200, damit Provider nicht retriggert)
return res.status(200).json({ status: 'ignored_account' });
}
if (payload.amount <= 0) {
// Nur Zahlungseingänge relevant, keine Abbuchungen
return res.status(200).json({ status: 'ignored_outgoing' });
}
try {
const result = await paymentService.processIncomingTransaction(payload);
return res.status(200).json(result);
} catch (err) {
console.error('[bankingWebhook] Verarbeitung fehlgeschlagen:', err);
// 500, damit der Provider (bei entsprechender Retry-Policy) erneut zustellt
return res.status(500).json({ error: 'processing_failed' });
}
},
);
/** Router für die normalen (JSON-basierten) Banking-/Payment-Endpunkte. */
export const paymentsRouter = Router();
/** Cockpit-Endpoint: liefert die Ampel-Übersicht für alle aktiven Verträge. */
paymentsRouter.get('/payments/traffic-lights', async (_req: Request, res: Response) => {
const lights = await paymentService.getRentTrafficLights();
res.status(200).json(lights);
});