Files
online-energieausweis/src/pages/api/rechnung/index.ts
Moritz Utcke e21a829cb6 Bugfixes
2025-04-20 17:21:46 -04:00

251 lines
6.7 KiB
TypeScript

import { z } from "zod";
import { Enums, prisma } from "#lib/server/prisma.js";
import { mollieClient } from "#lib/mollie.js";
import { PaymentMethod } from "@mollie/api-client";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import {
authorizationHeaders,
authorizationMiddleware,
} from "#lib/middleware/authorization.js";
import { UUidWithPrefix } from "#components/Ausweis/types.js";
import { getPrismaAusweisAdapter } from "#lib/server/ausweis.js";
import { PRICES, SERVICES, VALID_UUID_PREFIXES } from "#lib/constants.js";
import { Rechnung } from "#lib/client/prisma.js";
import { RechnungSchema } from "src/generated/zod/rechnung.js";
import { generatePrefixedId } from "#lib/db.js";
export const PUT = defineApiRoute({
meta: {
contentTypes: ["application/json"],
description:
"Erstellt eine neue Rechnung mit der ein Energieausweis bezahlt werden kann. Gibt eine Checkout URL zurück, welche der Nutzer folgen muss, um zum Kaufabschluss zu gelangen.",
summary: "Erstellt eine neue Rechnung zu einem Energieausweis Vorgang.",
tags: ["Rechnungen"],
},
input: z
.object({
ausweisart: z.nativeEnum(Enums.Ausweisart),
ausweis_id: UUidWithPrefix,
})
.merge(
RechnungSchema.omit({
benutzer_id: true,
bezahlt_am: true,
erstellt_am: true,
id: true,
status: true,
betrag: true,
storniert_am: true,
transaktions_referenz: true,
created_at: true,
updated_at: true
})
),
output: z.object({
checkout_url: z.string().optional(),
id: UUidWithPrefix,
}),
headers: authorizationHeaders,
middleware: authorizationMiddleware,
async fetch(input, ctx, user) {
// Wir erstellen eine Mollie Payment Referenz und eine neue Rechnung in unserer Datenbank, daraufhin geben
// wir eine Checkout URL zurück auf die der Nutzer weitergeleitet werden kann.
const { ausweis_id, ausweisart, bezahlmethode, services, partner_code } = input;
const adapter = getPrismaAusweisAdapter(ausweis_id);
if (!adapter) {
throw new APIError({
code: "BAD_REQUEST",
message: "Ungültige Ausweis UID"
})
}
const ausweis = await adapter.findUnique({
where: {
id: ausweis_id
},
include: {
rechnung: true
}
})
if (!ausweis) {
throw new APIError({
code: "NOT_FOUND",
message: "Ausweis nicht gefunden.",
});
}
if (ausweis.rechnung) {
throw new APIError({
code: "BAD_REQUEST",
message: "Eine Rechnung für diesen Ausweis existiert bereits.",
});
}
if (ausweis.benutzer_id !== user.id) {
throw new APIError({
code: "UNAUTHORIZED",
message: "Ausweis gehört nicht dem Nutzer.",
});
}
let betrag = PRICES[ausweisart][ausweis.ausweistyp]
const servicePriceList = SERVICES[ausweisart]
for (const service of input.services) {
betrag += servicePriceList[service]
}
const id = generatePrefixedId(9, VALID_UUID_PREFIXES.Rechnung);
// Wir erstellen eine neue Rechnung in unserer Datenbank.
let rechnung: Rechnung | null = null;
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
rechnung = await prisma.rechnung.create({
data: {
id,
benutzer_id: user.id,
betrag,
bezahlmethode: bezahlmethode,
status: Enums.Rechnungsstatus.open,
abweichende_versand_adresse: input.abweichende_versand_adresse,
email: input.email,
empfaenger: input.empfaenger,
ort: input.ort,
plz: input.plz,
strasse: input.strasse,
telefon: input.telefon,
versand_ort: input.versand_ort,
versand_empfaenger: input.versand_empfaenger,
versand_plz: input.versand_plz,
versand_strasse: input.versand_strasse,
versand_zusatzzeile: input.versand_zusatzzeile,
zusatzzeile: input.zusatzzeile,
verbrauchsausweis_wohnen: {
connect: {
id: ausweis_id
}
},
services,
partner_code
}
});
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
rechnung = await prisma.rechnung.create({
data: {
id,
benutzer_id: user.id,
betrag,
bezahlmethode: bezahlmethode,
status: Enums.Rechnungsstatus.open,
abweichende_versand_adresse: input.abweichende_versand_adresse,
email: input.email,
empfaenger: input.empfaenger,
ort: input.ort,
plz: input.plz,
strasse: input.strasse,
telefon: input.telefon,
versand_ort: input.versand_ort,
versand_empfaenger: input.versand_empfaenger,
versand_plz: input.versand_plz,
versand_strasse: input.versand_strasse,
versand_zusatzzeile: input.versand_zusatzzeile,
zusatzzeile: input.zusatzzeile,
verbrauchsausweis_gewerbe: {
connect: {
id: ausweis_id
}
},
services,
partner_code
}
});
} else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) {
rechnung = await prisma.rechnung.create({
data: {
id,
benutzer_id: user.id,
betrag,
bezahlmethode: bezahlmethode,
status: Enums.Rechnungsstatus.open,
abweichende_versand_adresse: input.abweichende_versand_adresse,
email: input.email,
empfaenger: input.empfaenger,
ort: input.ort,
plz: input.plz,
strasse: input.strasse,
telefon: input.telefon,
versand_ort: input.versand_ort,
versand_empfaenger: input.versand_empfaenger,
versand_plz: input.versand_plz,
versand_strasse: input.versand_strasse,
versand_zusatzzeile: input.versand_zusatzzeile,
zusatzzeile: input.zusatzzeile,
bedarfsausweis_wohnen: {
connect: {
id: ausweis_id
}
},
services,
partner_code
}
});
}
if (!rechnung) {
throw new APIError({
code: "INTERNAL_SERVER_ERROR",
message: "Rechnung konnte nicht erstellt werden.",
});
}
await adapter.update({
where: {
id: ausweis_id
},
data: {
bestellt: true
}
})
if (bezahlmethode === Enums.Bezahlmethoden.rechnung) {
return { id }
}
// Wir erstellen eine Mollie Payment Referenz.
const payment = await mollieClient.payments.create({
amount: {
value: betrag.toFixed(2),
currency: "EUR",
},
metadata: {
rechnung_id: rechnung.id,
},
method: input.bezahlmethode as PaymentMethod,
description: "Verbrauchsausweis Wohnen 2016",
redirectUrl: `https://online-energieausweis.org/payment/success?a=${ausweis.id}&r=${rechnung.id}`,
webhookUrl: `https://online-energieausweis.org/api/webhooks/mollie`,
cancelUrl: `https://online-energieausweis.org/kundendaten?a=${ausweis.id}&r=${rechnung.id}`
});
const checkoutUrl = payment.getCheckoutUrl();
if (!checkoutUrl) {
throw new APIError({
code: "INTERNAL_SERVER_ERROR",
message: "Checkout URL konnte nicht erstellt werden.",
});
}
return {
id,
checkout_url: checkoutUrl,
};
},
});