Files
Wabenhain/src/i18n/sponsorship/abo.ts
T
Nick SzaunigandClaude Opus 5.5 515fca1c16
Docker Image bauen und veroeffentlichen / typecheck (push) Successful in 2m57s
Docker Image bauen und veroeffentlichen / lint (push) Successful in 3m7s
Docker Image bauen und veroeffentlichen / build-and-push (push) Canceled after 0s
Docker Image bauen und veroeffentlichen / test (push) Canceled after 1m22s
fix: Befunde aus Sicherheits- und Korrektheitsreview der Bienenpatenschaft
- Oeffentliche Kuendigung: exakte Identifikation, Limits je Vertrag und
  Adresse, Bestaetigung nur an die Kontoadresse mit Ruecknahme-Link (HMAC)
- Vorschau: Body-Grenze beim Lesen, Parallelitaets- und Minutenlimit, Cache
- Rabatte gelten nicht fuer Patenschaftspositionen
- Abo aktiv erst nach bestaetigter Zahlung, offene Abschluesse zaehlen
  zum Kontingent und werden nach 24 h aufgeraeumt, Sitzung 58 Minuten
- Konto-Loeschung nur ohne laufendes Abo, Urkunden-Dateien werden entfernt
- Abo-Lieferungen mit Zahlungszeile (DHL-Marke moeglich)
- Shop zeigt freien Bestand nach Abo-Reservierung
- Storno/volle Erstattung widerruft die Patenschaft (Status WIDERRUFEN)
- Umsatzsteuer der Patenschaft per VAT_RATE_PERCENT_PATENSCHAFT (19 %)
- Digitale Bestellungen archivieren sich, Kalendertage in Europe/Berlin,
  Monatsenden begrenzt, Datenauskunft um Patenschaften ergaenzt

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-16 15:09:32 +02:00

348 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Locale } from "@/i18n/config";
import type { SubscriptionStatus } from "@/generated/prisma/client";
/**
* Texte des Abos: Dankeseite nach dem Abschluss, Abo-Seite im Konto mit
* Kuendigungsknopf (§ 312k BGB) und die oeffentliche Seite
* /vertraege-kuendigen. Eigenes Modul wie die uebrigen Patenschaftstexte,
* damit parallele Teile sich nicht in die Quere kommen. Ansprache: du.
*
* Die Beschriftungen "Verträge hier kündigen" und "Jetzt kündigen" sind vom
* Gesetz vorgegeben (§ 312k Abs. 2 BGB) und duerfen nicht umformuliert werden.
*/
export interface AboStrings {
footerLink: string;
status: Record<SubscriptionStatus, string>;
thanks: {
metaTitle: string;
activeTitle: string;
activeText: string;
processingTitle: string;
processingText: string;
notCompletedTitle: string;
notCompletedText: string;
toSubscription: string;
toAccount: string;
backToOffer: string;
};
detail: {
metaTitle: string;
back: string;
title: (offer: string) => string;
beeLine: (bee: string) => string;
contractNumber: string;
statusLabel: string;
monthlyPrice: string;
nextCharge: string;
endsOn: string;
paidMonths: string;
progressTitle: string;
progress: (paid: number, required: number) => string;
progressHarvest: (paid: number, required: number) => string;
sepaHoldNote: string;
deliveriesTitle: string;
deliveriesEmpty: string;
deliveryPrepared: (date: string, jars: number) => string;
deliveryDeferred: (date: string) => string;
delinquentNote: string;
paymentTitle: string;
paymentText: string;
paymentButton: string;
paymentUnavailable: string;
cancelTitle: string;
cancelText: string;
cancelButton: string;
cancelledNote: (date: string) => string;
endedNote: (date: string) => string;
certificateLink: string;
cancelDone: (date: string) => string;
cancelFailed: string;
};
confirm: {
metaTitle: string;
title: string;
intro: string;
kindLabel: string;
kindValue: string;
contractLabel: string;
effectiveLabel: string;
effectiveValue: (date: string) => string;
confirmationLabel: string;
confirmationValue: (email: string) => string;
submit: string;
back: string;
};
publicPage: {
metaTitle: string;
title: string;
intro: string;
accountHint: string;
nameLabel: string;
emailLabel: string;
emailHint: string;
referenceLabel: string;
referenceHint: string;
messageLabel: string;
requiredNote: string;
startButton: string;
reviewHeading: string;
reviewIntro: string;
confirmButton: string;
editButton: string;
submitting: string;
successHeading: string;
receivedAtLabel: string;
/** Fuer passende und unpassende Angaben gleich (kein Hinweis, ob eine Mail rausging). */
successMailNote: string;
errorMessage: string;
rateLimited: string;
invalid: string;
};
undoPage: {
metaTitle: string;
title: string;
intro: (end: string) => string;
button: string;
successTitle: string;
successText: string;
invalidTitle: string;
invalidText: string;
notCancelledText: string;
expiredText: string;
errorText: string;
toAccount: string;
};
}
const de: AboStrings = {
footerLink: "Verträge hier kündigen",
status: {
AUSSTEHEND: "Abschluss offen",
AKTIV: "Aktiv",
IM_VERZUG: "Zahlung offen",
GEKUENDIGT: "Gekündigt",
BEENDET: "Beendet",
},
thanks: {
metaTitle: "Abo abgeschlossen",
activeTitle: "Danke für dein Abo!",
activeText: "Dein Abo ist aktiv – die Urkunde erscheint nach der ersten Zahlung in deinem Konto.",
processingTitle: "Deine Zahlung wird geprüft",
processingText:
"Dein Abo ist abgeschlossen. Bei Lastschrift dauert die Bestätigung der Bank einige Tage – die Urkunde erscheint nach der ersten Zahlung in deinem Konto.",
notCompletedTitle: "Das Abo wurde nicht abgeschlossen",
notCompletedText: "Es wurde nichts abgebucht. Du kannst es jederzeit erneut versuchen.",
toSubscription: "Zum Abo",
toAccount: "Zu meiner Patenschaft",
backToOffer: "Zurück zur Patenschaft",
},
detail: {
metaTitle: "Mein Abo",
back: "Zurück zur Patenschaft",
title: (offer) => `Abo „${offer}“`,
beeLine: (bee) => `Patenbiene ${bee}`,
contractNumber: "Vertragsnummer",
statusLabel: "Status",
monthlyPrice: "Monatlicher Betrag",
nextCharge: "Nächste Abbuchung",
endsOn: "Endet am",
paidMonths: "Bezahlte Monate",
progressTitle: "Bis zur nächsten Honiglieferung",
progress: (paid, required) => `${paid} von ${required} Monaten bezahlt`,
progressHarvest: (paid, required) =>
`${paid} von ${required} Monaten bezahlt – geliefert wird im nächsten Erntefenster`,
sepaHoldNote:
"Bei Lastschrift zählt die erste Zahlung erst nach acht Wochen für die Lieferung, weil die Bank sie so lange zurückholen kann.",
deliveriesTitle: "Honiglieferungen",
deliveriesEmpty: "Noch keine Lieferung – der Honig kommt, sobald genug Monate bezahlt sind.",
deliveryPrepared: (date, jars) => `${date}: ${jars} Gläser, in Vorbereitung oder unterwegs`,
deliveryDeferred: (date) => `${date}: zurückgestellt – wir melden uns bei dir`,
delinquentNote:
"Eine Zahlung ist offen. Solange sie nicht ausgeglichen ist, ruhen die Honiglieferungen.",
paymentTitle: "Zahlungsart",
paymentText: "Du kannst deine Zahlungsart sicher bei unserem Zahlungsdienstleister Stripe ändern.",
paymentButton: "Zahlungsart ändern",
paymentUnavailable: "Das Ändern der Zahlungsart ist gerade nicht möglich. Bitte versuche es später erneut.",
cancelTitle: "Kündigen",
cancelText: "Das Abo ist monatlich kündbar und endet zum Ende des bezahlten Monats.",
cancelButton: "Verträge hier kündigen",
cancelledNote: (date) => `Gekündigt – das Abo endet am ${date}. Danach wird nichts mehr abgebucht.`,
endedNote: (date) => `Das Abo ist am ${date} beendet. Urkunde, Medaille und Downloads bleiben in deinem Konto.`,
certificateLink: "Zur Urkunde",
cancelDone: (date) => `Deine Kündigung ist eingegangen. Das Abo endet am ${date}. Eine Bestätigung ist per Mail unterwegs.`,
cancelFailed: "Die Kündigung konnte gerade nicht verarbeitet werden. Bitte versuche es erneut oder nutze die Seite „Verträge hier kündigen“.",
},
confirm: {
metaTitle: "Kündigung bestätigen",
title: "Kündigung bestätigen",
intro: "Bitte prüfe deine Angaben. Mit dem Klick auf „Jetzt kündigen“ ist die Kündigung erklärt.",
kindLabel: "Art der Kündigung",
kindValue: "Ordentliche Kündigung zum nächstmöglichen Zeitpunkt",
contractLabel: "Vertrag",
effectiveLabel: "Wirksam zum",
effectiveValue: (date) => `${date} (Ende des bezahlten Monats)`,
confirmationLabel: "Bestätigung an",
confirmationValue: (email) => email,
submit: "Jetzt kündigen",
back: "Abbrechen",
},
publicPage: {
metaTitle: "Verträge hier kündigen",
title: "Verträge hier kündigen",
intro:
"Hier kündigst du dein Abo der Bienenpatenschaft ohne Anmeldung. Die Kündigung wirkt zum Ende des bezahlten Monats. Du bekommst sofort eine Eingangsbestätigung per Mail.",
accountHint: "Mit Kundenkonto geht es auch direkt dort: Konto → Patenschaft → Abo.",
nameLabel: "Vor- und Nachname *",
emailLabel: "E-Mail-Adresse *",
emailHint: "Die Adresse deines Kundenkontos – dorthin geht die Bestätigung.",
referenceLabel: "Vertragsnummer *",
referenceHint: "Die Patenschaftsnummer von der Urkunde (WH-P-…) oder die Vertragsnummer aus deinem Konto.",
messageLabel: "Nachricht (optional)",
requiredNote: "* Pflichtangaben",
startButton: "Verträge hier kündigen",
reviewHeading: "Kündigung bestätigen",
reviewIntro: "Bitte prüfe deine Angaben. Mit dem Klick auf „Jetzt kündigen“ ist die Kündigung erklärt.",
confirmButton: "Jetzt kündigen",
editButton: "Angaben ändern",
submitting: "Wird gesendet …",
successHeading: "Deine Kündigung ist eingegangen",
receivedAtLabel: "Eingegangen am",
successMailNote:
"Die Eingangsbestätigung schicken wir an die E-Mail-Adresse deines Kundenkontos. Bitte bewahre diese Seite als Nachweis auf (Ausdruck oder Bildschirmfoto).",
errorMessage: "Deine Kündigung konnte nicht gesendet werden. Bitte versuche es erneut oder schreib uns eine Mail.",
rateLimited: "Zu viele Anfragen. Bitte versuche es später erneut oder schreib uns eine Mail.",
invalid: "Bitte überprüfe deine Angaben.",
},
undoPage: {
metaTitle: "Kündigung zurücknehmen",
title: "Kündigung zurücknehmen",
intro: (end) => `Dein Abo ist zum ${end} gekündigt. Wenn du die Kündigung zurücknimmst, läuft es einfach weiter wie bisher.`,
button: "Kündigung zurücknehmen",
successTitle: "Dein Abo läuft weiter",
successText: "Die Kündigung ist zurückgenommen. Es ändert sich nichts an deinem Abo.",
invalidTitle: "Dieser Link gilt nicht (mehr)",
invalidText: "Der Link ist abgelaufen, unvollständig oder die Kündigung wurde schon zurückgenommen. Im Kundenkonto siehst du den aktuellen Stand.",
notCancelledText: "Für dieses Abo ist keine Kündigung (mehr) vorgemerkt.",
expiredText: "Der Kündigungstermin ist schon erreicht; zurücknehmen lässt sich die Kündigung nicht mehr.",
errorText: "Das hat gerade nicht geklappt. Bitte versuche es gleich noch einmal oder schreib uns eine Mail.",
toAccount: "Zum Kundenkonto",
},
};
const en: AboStrings = {
footerLink: "Cancel contracts here",
status: {
AUSSTEHEND: "Sign-up pending",
AKTIV: "Active",
IM_VERZUG: "Payment outstanding",
GEKUENDIGT: "Cancelled",
BEENDET: "Ended",
},
thanks: {
metaTitle: "Subscription confirmed",
activeTitle: "Thank you for subscribing!",
activeText: "Your subscription is active – the certificate appears in your account after the first payment.",
processingTitle: "Your payment is being confirmed",
processingText:
"Your subscription is set up. With direct debit, the bank takes a few days to confirm – the certificate appears in your account after the first payment.",
notCompletedTitle: "The subscription was not completed",
notCompletedText: "Nothing has been charged. You can try again at any time.",
toSubscription: "View subscription",
toAccount: "My sponsorship",
backToOffer: "Back to bee sponsorship",
},
detail: {
metaTitle: "My subscription",
back: "Back to sponsorship",
title: (offer) => `Subscription “${offer}”`,
beeLine: (bee) => `Sponsored bee ${bee}`,
contractNumber: "Contract number",
statusLabel: "Status",
monthlyPrice: "Monthly amount",
nextCharge: "Next charge",
endsOn: "Ends on",
paidMonths: "Paid months",
progressTitle: "Until the next honey delivery",
progress: (paid, required) => `${paid} of ${required} months paid`,
progressHarvest: (paid, required) => `${paid} of ${required} months paid – delivered in the next harvest window`,
sepaHoldNote:
"With direct debit, the first payment only counts towards a delivery after eight weeks, because the bank can reverse it until then.",
deliveriesTitle: "Honey deliveries",
deliveriesEmpty: "No delivery yet – the honey comes as soon as enough months are paid.",
deliveryPrepared: (date, jars) => `${date}: ${jars} jars, being prepared or on the way`,
deliveryDeferred: (date) => `${date}: on hold – we will get in touch`,
delinquentNote: "A payment is outstanding. Honey deliveries are paused until it is settled.",
paymentTitle: "Payment method",
paymentText: "You can change your payment method securely with our payment provider Stripe.",
paymentButton: "Change payment method",
paymentUnavailable: "Changing the payment method is not possible right now. Please try again later.",
cancelTitle: "Cancel",
cancelText: "The subscription can be cancelled monthly and ends at the end of the paid month.",
cancelButton: "Cancel contracts here",
cancelledNote: (date) => `Cancelled – the subscription ends on ${date}. Nothing will be charged afterwards.`,
endedNote: (date) => `The subscription ended on ${date}. Your certificate, medal and downloads remain in your account.`,
certificateLink: "View certificate",
cancelDone: (date) => `We have received your cancellation. The subscription ends on ${date}. A confirmation is on its way by email.`,
cancelFailed: "The cancellation could not be processed right now. Please try again or use the “Cancel contracts here” page.",
},
confirm: {
metaTitle: "Confirm cancellation",
title: "Confirm cancellation",
intro: "Please check your details. Clicking “Cancel now” submits your cancellation.",
kindLabel: "Type of cancellation",
kindValue: "Ordinary cancellation at the earliest possible date",
contractLabel: "Contract",
effectiveLabel: "Effective on",
effectiveValue: (date) => `${date} (end of the paid month)`,
confirmationLabel: "Confirmation to",
confirmationValue: (email) => email,
submit: "Cancel now",
back: "Back",
},
publicPage: {
metaTitle: "Cancel contracts here",
title: "Cancel contracts here",
intro:
"Cancel your bee sponsorship subscription here without logging in. The cancellation takes effect at the end of the paid month. You receive a confirmation of receipt by email right away.",
accountHint: "With a customer account you can also do this directly there: Account → Sponsorship → Subscription.",
nameLabel: "First and last name *",
emailLabel: "Email address *",
emailHint: "The address of your customer account – the confirmation goes there.",
referenceLabel: "Contract number *",
referenceHint: "The sponsorship number from the certificate (WH-P-…) or the contract number from your account.",
messageLabel: "Message (optional)",
requiredNote: "* Required",
startButton: "Cancel contracts here",
reviewHeading: "Confirm cancellation",
reviewIntro: "Please check your details. Clicking “Cancel now” submits your cancellation.",
confirmButton: "Cancel now",
editButton: "Edit details",
submitting: "Sending …",
successHeading: "We have received your cancellation",
receivedAtLabel: "Received on",
successMailNote:
"We send the confirmation of receipt to the email address of your customer account. Please keep this page as proof (print or screenshot).",
errorMessage: "Your cancellation could not be sent. Please try again or email us.",
rateLimited: "Too many requests. Please try again later or email us.",
invalid: "Please check your details.",
},
undoPage: {
metaTitle: "Withdraw cancellation",
title: "Withdraw cancellation",
intro: (end) => `Your subscription is cancelled as of ${end}. If you withdraw the cancellation, it simply continues as before.`,
button: "Withdraw cancellation",
successTitle: "Your subscription continues",
successText: "The cancellation has been withdrawn. Nothing changes about your subscription.",
invalidTitle: "This link is not (or no longer) valid",
invalidText: "The link has expired, is incomplete or the cancellation has already been withdrawn. Your customer account shows the current status.",
notCancelledText: "No cancellation is (any longer) scheduled for this subscription.",
expiredText: "The cancellation date has already been reached; the cancellation can no longer be withdrawn.",
errorText: "That did not work just now. Please try again shortly or email us.",
toAccount: "To your account",
},
};
export function getAboStrings(locale: Locale): AboStrings {
return locale === "en" ? en : de;
}