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
|
|
@ -64,14 +64,15 @@ class UserMakeOrder
|
|||
return $ret;
|
||||
}
|
||||
|
||||
private $stripeShoppingPayment;
|
||||
|
||||
public function makePayment($testmode = false)
|
||||
{
|
||||
Log::info('Starte Zahlungsvorgang für UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
$provider = app(PaymentProviderManager::class)->providerFor($this->userAbo);
|
||||
if ($provider !== PaymentProvider::Payone) {
|
||||
Log::error('Abo-Abbuchung für Provider "'.$provider->value.'" ist noch nicht implementiert. UserAbo ID: '.$this->userAbo->id);
|
||||
throw new \RuntimeException('Abo-Abbuchung für Provider "'.$provider->value.'" ist noch nicht implementiert (UserAbo '.$this->userAbo->id.').');
|
||||
if ($provider === PaymentProvider::Stripe) {
|
||||
return $this->makeStripePayment();
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -96,6 +97,39 @@ class UserMakeOrder
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stripe-Abo (Plan S4.4): off-session Abbuchung über das provider-neutrale
|
||||
* Gateway. Rückgabeformat entspricht der PAYONE-Response, damit der
|
||||
* aufrufende Cron/Retry unverändert verzweigen kann (APPROVED/ERROR/PENDING).
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function makeStripePayment(): array
|
||||
{
|
||||
$gateway = app(PaymentProviderManager::class)->gateway(PaymentProvider::Stripe);
|
||||
$amountCents = (int) round($this->shopping_order->total_shipping * 100);
|
||||
|
||||
$result = $gateway->chargeAbo(
|
||||
$this->userAbo,
|
||||
$this->shopping_order,
|
||||
$this->shopping_user,
|
||||
$amountCents,
|
||||
'EUR',
|
||||
);
|
||||
|
||||
$this->stripeShoppingPayment = $result->shoppingPayment;
|
||||
|
||||
Log::info('Stripe-Zahlungsvorgang abgeschlossen für UserAbo ID: '.$this->userAbo->id.', Status: '.$result->status);
|
||||
|
||||
return [
|
||||
'status' => $result->status,
|
||||
'txid' => $result->transactionId,
|
||||
'errormessage' => $result->errorMessage,
|
||||
'customermessage' => $result->customerMessage,
|
||||
'provider' => $result->provider->value,
|
||||
];
|
||||
}
|
||||
|
||||
private function recordPaymentTransaction(mixed $response): void
|
||||
{
|
||||
$shoppingPayment = $this->getShoppingPayment();
|
||||
|
|
@ -120,6 +154,10 @@ class UserMakeOrder
|
|||
{
|
||||
Log::info('Rufe Zahlungsinformationen ab für UserAbo ID: '.$this->userAbo->id);
|
||||
|
||||
if ($this->stripeShoppingPayment) {
|
||||
return $this->stripeShoppingPayment;
|
||||
}
|
||||
|
||||
if ($this->pay) {
|
||||
$payment = $this->pay->getShoppingPayment();
|
||||
Log::info('Zahlungsinformationen abgerufen: '.($payment ? 'erfolgreich' : 'nicht verfügbar'));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue