Stripe-Integration (S4, S5, S7.6): Abos, Zahlungsart ändern, Zahlarten-Kommunikation
Phase S4 – Abos über Stripe (E2E komplett im Testmodus): - Abo-/Recovery-Checkout: customer_creation=always + setup_future_usage=off_session - StripePaymentService::chargeAbo (off-session PaymentIntent, Idempotency-Key), StripeGateway, Provider-Dispatch im Abbuchungs-Cron (PAYONE-Pfad unverändert) - Webhook: Zahlungsmittel-Capture vor Paid-Pfad, 503 payment_not_yet_visible bei Cron-Transaktions-Race; Command stripe:process-pending-events (Cron-Ende + 10-min Scheduler) verarbeitet liegengebliebene Events nach - createNewAbo/advanceAboAfterRecoveryPayment Stripe-aware inkl. Race-Absicherung; Recovery-Orders nutzen advanceAboAfterRecoveryPayment (latenter PAYONE-Duplikat-Bug) - Migrationen: Stripe-Abo-Spalten auf shopping_payments; clearingtype varchar(3)→(20) inkl. Reparatur der zu 'str' gekürzten Live-Zeilen Phase S5 – Zahlungsart ändern (Scope: jede Zahlart, nicht nur Kreditkarte): - Stripe Checkout mode=setup ohne Abbuchung; PAYONE-Abos ziehen beim Wechsel auf Stripe um (Auslaufmodell §7.2) - AboPaymentMethodService: beginStripeChange/finalize/applyStripePaymentMethod (idempotent, detacht alte PM, card_changes-Log); Webhook setup_intent.succeeded als Sicherheitsnetz; Einstiege Portal/CRM/Berater-Kunden-Link flow-abhängig - Views/Mails flow-abhängig („Zahlungsweise" statt „Kreditkarte") Phase S7.6 – Zahlarten-Kommunikation: - Einheitliche Zahlarten-Logos (SVG) + wiederverwendbares Partial payment_logos; Checkout/Footer zeigen Kern-Zahlarten + „···"-Icon, /zahlungsarten komplett neu mit allen aktiven Stripe-Zahlarten (gegen Account-Konfiguration verifiziert) und Vorkasse (PAYONE); Sofort/SEPA entfernt Tests: StripeAboTest (15), StripePaymentMethodChangeTest (10), PaymentMethodsCommunicationTest (28), PaymentProviderManagerTest angepasst. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6f21845607
commit
ce4750fd1a
68 changed files with 2856 additions and 236 deletions
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Stripe-Einführung Phase S4 (dev/2026-07-08-stripe/):
|
||||
* Abo-fähige Stripe-Zahlungen. Der Webhook schreibt Customer-/PaymentMethod-ID
|
||||
* an die Zahlung (Transport zum Abo in createNewAbo/advanceAboAfterRecoveryPayment);
|
||||
* user_abo_id markiert wiederkehrende Cron-Abbuchungen, damit der Paid-Pfad
|
||||
* daraus kein neues Abo anlegt.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('shopping_payments', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('shopping_payments', 'stripe_customer_id')) {
|
||||
$table->string('stripe_customer_id')->nullable()->after('external_reference');
|
||||
}
|
||||
if (! Schema::hasColumn('shopping_payments', 'stripe_payment_method_id')) {
|
||||
$table->string('stripe_payment_method_id')->nullable()->after('stripe_customer_id');
|
||||
}
|
||||
if (! Schema::hasColumn('shopping_payments', 'user_abo_id')) {
|
||||
$table->unsignedBigInteger('user_abo_id')->nullable()->index()->after('stripe_payment_method_id');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('shopping_payments', function (Blueprint $table) {
|
||||
foreach (['stripe_customer_id', 'stripe_payment_method_id', 'user_abo_id'] as $column) {
|
||||
if (Schema::hasColumn('shopping_payments', $column)) {
|
||||
$table->dropColumn($column);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Stripe-Einführung Phase S4 (dev/2026-07-08-stripe/):
|
||||
* clearingtype war varchar(3) (PAYONE-Kürzel cc/wlt/elv/vor/fnc/sb) — MySQL hat
|
||||
* 'stripe' still zu 'str' abgeschnitten (SQLite in den Tests erzwingt keine Länge,
|
||||
* daher unentdeckt). Spalten verbreitern und die abgeschnittenen Bestandswerte
|
||||
* reparieren. Anzeige-/Logikpfade lesen payment_provider, nicht clearingtype —
|
||||
* der Fix ist Datenhygiene.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('shopping_payments', function (Blueprint $table) {
|
||||
$table->string('clearingtype', 20)->nullable(false)->change();
|
||||
});
|
||||
|
||||
Schema::table('user_abos', function (Blueprint $table) {
|
||||
$table->string('clearingtype', 20)->nullable(false)->change();
|
||||
});
|
||||
|
||||
DB::table('shopping_payments')
|
||||
->where('payment_provider', 'stripe')
|
||||
->where('clearingtype', 'str')
|
||||
->update(['clearingtype' => 'stripe']);
|
||||
|
||||
DB::table('user_abos')
|
||||
->where('payment_provider', 'stripe')
|
||||
->where('clearingtype', 'str')
|
||||
->update(['clearingtype' => 'stripe']);
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
// Rückweg bewusst ohne Verkürzung auf varchar(3): das würde Daten
|
||||
// abschneiden. Nur die Werte zurückdrehen.
|
||||
DB::table('shopping_payments')
|
||||
->where('payment_provider', 'stripe')
|
||||
->where('clearingtype', 'stripe')
|
||||
->update(['clearingtype' => 'str']);
|
||||
|
||||
DB::table('user_abos')
|
||||
->where('payment_provider', 'stripe')
|
||||
->where('clearingtype', 'stripe')
|
||||
->update(['clearingtype' => 'str']);
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue