251 lines
6.7 KiB
TypeScript
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,
|
|
};
|
|
},
|
|
});
|
|
|
|
|