feat: Bienenpatenschaft mit Urkunde, Abo, Medaillen und 3D-Konto
Setzt Teilprojekte 1-5 der Spec 2026-10-16 um: - Urkunden-Engine (lib/certificate): ein Layout fuer PDF, Vorschau und 3D-Textur, Vorlage classic@1, eingebettete OFL-Schriften, Testmatrix - Einmal-Patenschaft ueber Traegerprodukte (Kategorie PATENSCHAFT), Paket wird im Checkout in Patenschaft (Regelsatz) und Honig aufgeteilt, Kontopflicht, rein digitale Bestellungen (FulfillmentMethod DIGITAL) - Ausstellung nach Zahlung idempotent, Wiederholung im App-Prozess (instrumentation + background-jobs), Ablage in CERTIFICATE_DIR - Verkaufsseite /patenschaft mit Konfigurator und Live-Vorschau, Warenkorb mit Schluessel je Position - Kundenkonto: 3D-Ansicht von Urkunde und Medaille, Downloads, ZIP, Uebertragung per befristetem Einmal-Link, Treuestufen bis VIP - Admin: Angebote, Patenschaften, Druck, Korrektur mit Revisionen, Abos, Erntefenster, Deckung, SEPA-Frist, Treuestufen - Abo ueber Stripe Billing: Lieferung erst nach bezahlten Monaten, SEPA-Neukunden-Frist und Rueckbuchungsschutz, Honigreservierung, Kuendigungsbutton, Monatsrechnungen, Stripe-Einrichtungsskript Startangebote werden inaktiv angelegt; Livebetrieb erst nach Freigabe. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
dcb578f062
commit
f227aeb8f9
269 files changed
+28054
-141
No files matched your search
@@ -0,0 +1,60 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Container, LinkButton, SectionHeading } from "@/components/ui";
|
||||
import { noIndexPrivate } from "@/lib/seo";
|
||||
import { getCurrentCustomer } from "@/lib/auth";
|
||||
import { resolveSubscriptionReturn } from "@/lib/sponsorship-subscription/checkout-return";
|
||||
import { getLocale } from "@/i18n/locale";
|
||||
import { getAboStrings } from "@/i18n/sponsorship/abo";
|
||||
|
||||
// Fragt bei jedem Aufruf den aktuellen Stand bei Stripe ab — nie aus einem Cache.
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
return { title: getAboStrings(await getLocale()).thanks.metaTitle, robots: noIndexPrivate };
|
||||
}
|
||||
|
||||
type Props = { searchParams: Promise<Record<string, string | string[] | undefined>> };
|
||||
|
||||
/**
|
||||
* Rueckkehrseite nach dem Abo-Checkout (return_url in checkout.ts). Ueber
|
||||
* "abgeschlossen" entscheidet die serverseitige Nachfrage bei Stripe, nicht
|
||||
* die URL; angelegt wird hier nichts — Abo, Zahlung und Urkunde entstehen
|
||||
* ausschliesslich ueber den Webhook.
|
||||
*/
|
||||
export default async function AboThanksPage({ searchParams }: Props) {
|
||||
const t = getAboStrings(await getLocale()).thanks;
|
||||
const { session_id: sessionId } = await searchParams;
|
||||
const customer = await getCurrentCustomer();
|
||||
const result =
|
||||
typeof sessionId === "string" && customer ? await resolveSubscriptionReturn(sessionId, customer.id) : { kind: "notCompleted" as const };
|
||||
|
||||
const view =
|
||||
result.kind === "active"
|
||||
? { title: t.activeTitle, text: t.activeText }
|
||||
: result.kind === "processing"
|
||||
? { title: t.processingTitle, text: t.processingText }
|
||||
: { title: t.notCompletedTitle, text: t.notCompletedText };
|
||||
|
||||
return (
|
||||
<div className="py-24">
|
||||
<Container className="max-w-xl text-center">
|
||||
<SectionHeading title={view.title} align="center" level="h1" className="mx-auto" />
|
||||
<p role="status" className="mt-6 text-ink/70">
|
||||
{view.text}
|
||||
</p>
|
||||
<div className="mt-10 flex flex-wrap items-center justify-center gap-4">
|
||||
{result.kind === "notCompleted" ? (
|
||||
<LinkButton href="/patenschaft">{t.backToOffer}</LinkButton>
|
||||
) : (
|
||||
<>
|
||||
<LinkButton href={`/konto/patenschaft/abo/${result.subscriptionId}`}>{t.toSubscription}</LinkButton>
|
||||
<LinkButton href="/konto/patenschaft" variant="secondary">
|
||||
{t.toAccount}
|
||||
</LinkButton>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user