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
|
|
@ -1,12 +1,47 @@
|
|||
@php($stripeChange = app(\App\Services\AboPaymentMethodService::class)->usesStripeChange())
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="font-weight-bold">{{ __('abo.update_card_title') }}</h5>
|
||||
<h5 class="font-weight-bold">{{ $stripeChange ? __('abo.payment_method_change_title') : __('abo.update_card_title') }}</h5>
|
||||
@if(app(\App\Services\Payment\PaymentProviderManager::class)->aboPaymentsFrozen())
|
||||
<div class="alert alert-warning mb-0">
|
||||
<strong>{{ __('payment.abo_payments_frozen_title') }}</strong><br>
|
||||
{{ __('payment.abo_payments_frozen') }}
|
||||
</div>
|
||||
<a href="{{ $backUrl ?? route('portal.my_subscriptions') }}" class="btn btn-default mt-3">{{ __('abo.back') }}</a>
|
||||
@elseif($stripeChange)
|
||||
{{-- Stripe-Setup-Flow (Plan S5): freie Zahlartenwahl auf der Hosted-Seite, keine Abbuchung --}}
|
||||
<p class="text-muted">{{ __('abo.payment_method_change_intro') }}</p>
|
||||
|
||||
@if (isset($errors) && $errors->any())
|
||||
<div class="alert alert-danger p-2">
|
||||
<ul class="mb-0">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (Session::has('alert-error'))
|
||||
<div class="alert alert-danger p-2">{{ Session::get('alert-error') }}</div>
|
||||
@endif
|
||||
|
||||
<div class="alert alert-info p-2">
|
||||
<i class="ion ion-md-card mr-2" style="font-size: 1.2rem; line-height: 1rem;"></i>
|
||||
<span>{{ __('abo.current_payment_method') }}: <strong>{{ $user_abo->maskedCard() ?: $user_abo->getPaymentType() }}</strong></span>
|
||||
</div>
|
||||
|
||||
<p class="fs-13 mb-1">{{ __('payment.stripe_online_methods') }}</p>
|
||||
<p class="fs-12 text-muted">{{ __('abo.payment_method_change_no_charge') }}</p>
|
||||
|
||||
<form method="POST" action="{{ $stripeFormAction ?? route('portal_abos_payment_method_stripe', [$user_abo->id]) }}">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-primary mt-2">
|
||||
{{ __('abo.payment_method_change_button') }}
|
||||
</button>
|
||||
<a href="{{ $backUrl ?? route('portal.my_subscriptions') }}" class="btn btn-default mt-2">{{ __('abo.back') }}</a>
|
||||
<img class="float-right mt-2" width="90" src="{{ asset('images/payments-assets/stripe.png') }}" alt="Stripe">
|
||||
</form>
|
||||
@else
|
||||
<p class="text-muted">{{ __('abo.card_update_intro') }}</p>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
@extends('portal.layouts.public')
|
||||
|
||||
@section('content')
|
||||
@php($stripeChange = app(\App\Services\AboPaymentMethodService::class)->usesStripeChange())
|
||||
<div class="text-center" style="max-width: 640px; margin: 0 auto;">
|
||||
@if ($success)
|
||||
<i class="ion ion-md-checkmark-circle text-success" style="font-size: 3rem;"></i>
|
||||
<h4 class="font-weight-bold mt-3">{{ __('abo.card_update_done_success_title') }}</h4>
|
||||
<p class="text-muted">{{ __('abo.card_update_done_success_copy') }}</p>
|
||||
<h4 class="font-weight-bold mt-3">{{ $stripeChange ? __('abo.payment_method_change_done_title') : __('abo.card_update_done_success_title') }}</h4>
|
||||
<p class="text-muted">{{ $stripeChange ? __('abo.payment_method_change_success') : __('abo.card_update_done_success_copy') }}</p>
|
||||
@else
|
||||
<i class="ion ion-md-close-circle text-danger" style="font-size: 3rem;"></i>
|
||||
<h4 class="font-weight-bold mt-3">{{ __('abo.card_update_done_failed_title') }}</h4>
|
||||
<p class="text-muted">{{ __('abo.card_update_done_failed_copy') }}</p>
|
||||
<p class="text-muted">{{ $stripeChange ? __('abo.payment_method_change_done_failed_copy') : __('abo.card_update_done_failed_copy') }}</p>
|
||||
@endif
|
||||
|
||||
<p class="text-muted mt-3">{{ __('abo.card_update_portal_hint') }}</p>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
@section('content')
|
||||
<div style="max-width: 640px; margin: 0 auto;">
|
||||
<h4 class="font-weight-bold mb-3">{{ __('abo.update_card_title') }}</h4>
|
||||
<h4 class="font-weight-bold mb-3">{{ app(\App\Services\AboPaymentMethodService::class)->usesStripeChange() ? __('abo.payment_method_change_title') : __('abo.update_card_title') }}</h4>
|
||||
|
||||
@include('portal.abo._payment_method_form', [
|
||||
'user_abo' => $user_abo,
|
||||
'formAction' => route('abo_card_update_store', [$token]),
|
||||
'stripeFormAction' => route('abo_card_update_stripe', [$token]),
|
||||
'backUrl' => \App\Services\Util::getMyMivitaPortalUrl(),
|
||||
])
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
<h4 class="font-weight-bold py-2 mb-2">
|
||||
{{ __('navigation.my_abo') }}
|
||||
<span class="text-muted">{{ '#' . $user_abo->payone_userid }}</span>
|
||||
{{-- Stripe-Abos haben keine payone_userid — dann die Abo-Nummer zeigen --}}
|
||||
<span class="text-muted">{{ '#' . ($user_abo->payone_userid ?: $user_abo->id) }}</span>
|
||||
</h4>
|
||||
|
||||
@if (Session::has('alert-success'))
|
||||
|
|
@ -133,16 +134,17 @@
|
|||
@include('admin.abo._executions')
|
||||
</div>
|
||||
|
||||
@if ($user_abo->isCreditCard())
|
||||
@php($canStartPaymentChange = app(\App\Services\AboPaymentMethodService::class)->canStartChange($user_abo))
|
||||
@if ($user_abo->isCreditCard() || $canStartPaymentChange)
|
||||
<div class="card mt-3 mb-3">
|
||||
<div class="card-body">
|
||||
<h6 class="font-weight-bold">{{ __('abo.payment_method') }}</h6>
|
||||
<p class="mb-2">
|
||||
{{ __('payment.credit_card') }}@if ($user_abo->maskedCard())
|
||||
{{ $user_abo->getPaymentType() }}@if ($user_abo->maskedCard())
|
||||
: {{ $user_abo->maskedCard() }}
|
||||
@endif
|
||||
</p>
|
||||
@if ($user_abo->canUpdatePaymentMethod())
|
||||
@if ($canStartPaymentChange)
|
||||
<a href="{{ route('portal_abos_payment_method_edit', [$user_abo->id]) }}"
|
||||
class="btn btn-sm btn-primary">
|
||||
{{ __('abo.update_payment_method') }}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
@section('content')
|
||||
|
||||
<h4 class="font-weight-bold py-2 mb-2">
|
||||
{{ __('abo.update_card_title') }}
|
||||
<span class="text-muted">{{ '#' . $user_abo->payone_userid }}</span>
|
||||
{{ app(\App\Services\AboPaymentMethodService::class)->usesStripeChange() ? __('abo.payment_method_change_title') : __('abo.update_card_title') }}
|
||||
<span class="text-muted">{{ '#' . ($user_abo->payone_userid ?: $user_abo->id) }}</span>
|
||||
</h4>
|
||||
|
||||
@include('portal.abo._payment_method_form', ['user_abo' => $user_abo])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue