Billing Phase 2: Stripe Checkout, Kundenportal und Webhook-Verarbeitung
app/stripe.php: minimaler REST-Client fuer die Stripe-API auf Basis von PHP-Streams statt eines vendorten SDKs - diese PHP-Installation hat keine curl-Extension, Streams sind zudem portabler und brauchen kein composer.json. - Fuer jeden bezahlten Tarif per API ein Stripe-Produkt mit monatlichem Preis angelegt, referenziert ueber einen stabilen lookup_key statt hartcodierter Price-ID; Erstellung ist idempotent. - abo-upgrade.php: erstellt eine Stripe-Checkout-Session fuer den gewaehlten Tarif, tenant_id/plan_code als Metadaten auf Session UND Subscription (damit spaetere Subscription-Events zuordenbar bleiben). - abo-portal.php: oeffnet das Stripe Customer Portal fuer bestehende Kunden (Zahlungsmittel/Kuendigung, ohne eigene UI dafuer). - stripe-webhook.php: verifiziert die Stripe-Signature per HMAC-SHA256 mit Zeitstempel-Toleranz, verarbeitet checkout.session.completed, customer.subscription.updated/.deleted, invoice.paid/.payment_failed und haelt tenant_billing aktuell. Bewusst kein CSRF-/Login-Check (Stripe ruft unauthentifiziert auf), stattdessen ausschliesslich Signaturpruefung als Echtheitsnachweis. - mandant-einstellungen.php: echter "Jetzt upgraden"-Button (Stripe Checkout) sowie "Zahlungsmethode verwalten/Abo kuendigen" (Customer Portal), sobald ein Stripe-Kunde existiert. Live getestet (Stripe-Testmodus, kein echtes Geld): voller Checkout-Flow bis zum echten Redirect auf checkout.stripe.com, Webhook-Verarbeitung durch selbst erzeugte, korrekt signierte Test-Events (da diese Dev-Umgebung keine oeffentlich erreichbare URL fuer echte Stripe- Zustellung hat) inklusive Ablehnung falscher Signaturen, Customer Portal mit echtem per API angelegtem Test-Kunden. Alle Regressionstests weiterhin gruen (36 Seiten HTTP-Smoke, Golden Master, M8-Isolation/ Rollenmatrix). Noch offen: echten Webhook-Endpunkt auf die Produktions-Domain eintragen, sobald diese feststeht; Wechsel auf Live-Keys; Dolibarr-Sync (Phase 3). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,7 @@ if ($canManageSettings) {
|
||||
$auditLog = app_fetch_audit_log($pdo, (int)$user['tenant_id'], 50);
|
||||
$billingCheck = billing_check_tenant($pdo, (int)$user['tenant_id']);
|
||||
$billingPlans = billing_plans();
|
||||
$billing = billing_fetch_or_init($pdo, (int)$user['tenant_id']);
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
@@ -125,17 +126,38 @@ include 'nav.php';
|
||||
Aktueller Tarif: <b><?php echo saas_html($billingPlans[$billingCheck['current_plan']]['label'] ?? $billingCheck['current_plan']); ?></b><br>
|
||||
Aktive Teilnehmer: <?php echo (int)$billingCheck['active_participants']; ?>
|
||||
</p>
|
||||
<?php if (isset($_GET['upgrade']) && $_GET['upgrade'] === 'success'): ?>
|
||||
<div class="hint-box success"><p>Danke! Die Zahlung wird verarbeitet, der Tarif ist gleich aktuell.</p></div>
|
||||
<?php elseif (isset($_GET['upgrade']) && $_GET['upgrade'] === 'cancelled'): ?>
|
||||
<div class="hint-box error"><p>Der Bezahlvorgang wurde abgebrochen, es wurde nichts geändert.</p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($billingCheck['needs_upgrade']): ?>
|
||||
<div class="hint-box error">
|
||||
<p>Mit <?php echo (int)$billingCheck['active_participants']; ?> aktiven Teilnehmern benötigt ihr den
|
||||
Tarif „<?php echo saas_html($billingPlans[$billingCheck['required_plan']]['label'] ?? $billingCheck['required_plan']); ?>".
|
||||
Die automatische Abrechnung ist noch in Vorbereitung – bitte <a href="mailto:info@ctb-it.de">meldet euch bei uns</a>,
|
||||
wir stellen den passenden Tarif manuell um. Details zu den Stufen stehen unter <a href="preise.php">Preise</a>.</p>
|
||||
Details zu den Stufen stehen unter <a href="preise.php">Preise</a>.</p>
|
||||
<?php if (($billingPlans[$billingCheck['required_plan']]['stripe_lookup_key'] ?? null) !== null): ?>
|
||||
<form method="post" action="abo-upgrade.php">
|
||||
<?php echo app_csrf_field(); ?>
|
||||
<input type="hidden" name="plan_code" value="<?php echo saas_html($billingCheck['required_plan']); ?>">
|
||||
<button type="submit">Jetzt auf „<?php echo saas_html($billingPlans[$billingCheck['required_plan']]['label']); ?>" upgraden</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<p>Für diese Teilnehmerzahl <a href="mailto:info@ctb-it.de">meldet euch bitte bei uns</a>.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="hint-box success"><p>Euer aktueller Tarif deckt eure Teilnehmerzahl ab.</p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (($billing['stripe_customer_id'] ?? null) !== null): ?>
|
||||
<form method="post" action="abo-portal.php">
|
||||
<?php echo app_csrf_field(); ?>
|
||||
<button type="submit">Zahlungsmethode verwalten / Abo kündigen</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<h3>Protokoll</h3>
|
||||
<p>Die letzten Admin-Aktionen für diesen Mandanten.</p>
|
||||
<table>
|
||||
|
||||
Reference in New Issue
Block a user