Stripe-Integration (S0–S3): Einmalzahlungen über Stripe Checkout + Notfall-Go-Live
Wegen PAYONE-Ausfall vorgezogen live geschaltet: alle Einmalzahlungen (Shop + Salescenter) laufen über Stripe Hosted Checkout, Abos sind bis Phase S4 eingefroren. - Provider-Abstraktion: PaymentProvider-Enum, PaymentGatewayInterface, PaymentProviderManager (Settings-Schalter, provider-treu je Bestand), PayoneGateway als unveränderter Adapter - StripePaymentService: Checkout Session (eine Gesamtposition, Metadata, success/cancel-Redirects), StripeClientWrapper (lazy, Dark Launch), StripeCustomerService, StripeTransactionRecorder (client_secret-Redaction) - Webhook /stripe/webhook: Signaturprüfung, Idempotenz über stripe_webhook_events (Crash-Retry-Reprocessing), txaction-Prioritäten, Betrags-Guard, lockForUpdate-Paid-Pfad, genau eine Bestätigungsmail - Zahlungsmethoden-Anzeige aus charge.succeeded (carddata inkl. Wallet, auftragsunabhängiges Backfill aus dem Event-Store) - Checkout: Wartungsmodus mit Admin-Bypass, Abo-Freeze-Hinweis, Stripe-Option (Online-Zahlung) statt Zahlartenwahl, Vorkasse ausblendbar - Abo-Freeze-Guards: Cron user:make_abo_order, Retry, Kartenupdate - Admin-Settings-Karte Zahlungen (Provider, Wartung, Freeze, Vorkasse) - Hotfix: 500 bei Vorkasse ohne Clearing-Daten in admin/sales/_detail und checkout-final (PAYONE-Ausfall) - Doku dev/2026-07-08-stripe/ (Plan S0–S8, Konzept, IST-Analyse) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
1108381988
commit
e1da90ff91
48 changed files with 4428 additions and 831 deletions
|
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Stripe-Einführung Phase S1 (dev/2026-07-08-stripe/):
|
||||
* Jede Zahlung / jedes Abo trägt seinen Zahlungsanbieter fest, damit der
|
||||
* Einstellungs-Schalter nur NEUE Vorgänge betrifft. Default 'payone'
|
||||
* backfillt zugleich alle Bestandszeilen. PAYONE-Spalten bleiben unverändert.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('user_abos', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('user_abos', 'payment_provider')) {
|
||||
$table->string('payment_provider', 20)->default('payone')->index()->after('payone_userid');
|
||||
}
|
||||
if (! Schema::hasColumn('user_abos', 'stripe_customer_id')) {
|
||||
$table->string('stripe_customer_id')->nullable()->after('carddata');
|
||||
}
|
||||
if (! Schema::hasColumn('user_abos', 'stripe_payment_method_id')) {
|
||||
$table->string('stripe_payment_method_id')->nullable()->after('stripe_customer_id');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('shopping_payments', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('shopping_payments', 'payment_provider')) {
|
||||
$table->string('payment_provider', 20)->default('payone')->index()->after('clearingtype');
|
||||
}
|
||||
if (! Schema::hasColumn('shopping_payments', 'external_reference')) {
|
||||
$table->string('external_reference')->nullable()->after('reference');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('payment_transactions', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('payment_transactions', 'provider')) {
|
||||
$table->string('provider', 20)->default('payone')->after('shopping_payment_id');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('users', 'stripe_customer_id')) {
|
||||
$table->string('stripe_customer_id')->nullable();
|
||||
}
|
||||
});
|
||||
|
||||
if (Schema::hasTable('customers')) {
|
||||
Schema::table('customers', function (Blueprint $table) {
|
||||
if (! Schema::hasColumn('customers', 'stripe_customer_id')) {
|
||||
$table->string('stripe_customer_id')->nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('user_abos', function (Blueprint $table) {
|
||||
foreach (['payment_provider', 'stripe_customer_id', 'stripe_payment_method_id'] as $column) {
|
||||
if (Schema::hasColumn('user_abos', $column)) {
|
||||
$table->dropColumn($column);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('shopping_payments', function (Blueprint $table) {
|
||||
foreach (['payment_provider', 'external_reference'] as $column) {
|
||||
if (Schema::hasColumn('shopping_payments', $column)) {
|
||||
$table->dropColumn($column);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('payment_transactions', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('payment_transactions', 'provider')) {
|
||||
$table->dropColumn('provider');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('users', 'stripe_customer_id')) {
|
||||
$table->dropColumn('stripe_customer_id');
|
||||
}
|
||||
});
|
||||
|
||||
if (Schema::hasTable('customers')) {
|
||||
Schema::table('customers', function (Blueprint $table) {
|
||||
if (Schema::hasColumn('customers', 'stripe_customer_id')) {
|
||||
$table->dropColumn('stripe_customer_id');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Stripe-Einführung Phase S2 (dev/2026-07-08-stripe/):
|
||||
* Idempotenz-Store + Audit-Log für Stripe-Webhook-Events. Die eindeutige
|
||||
* event_id verhindert Doppelverarbeitung bei Stripe-Retries (Muster:
|
||||
* Duplikat-Erkennung des PAYONE-Webhooks).
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (Schema::hasTable('stripe_webhook_events')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::create('stripe_webhook_events', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('event_id')->unique();
|
||||
$table->string('type')->index();
|
||||
$table->string('api_version')->nullable();
|
||||
$table->json('payload')->nullable();
|
||||
$table->timestamp('processed_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('stripe_webhook_events');
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue