mivita/tests/Feature/AboPortalPaymentMethodTest.php
Kevin 671022a28d Abo: Kreditkarte aktualisieren (CRM, Portal, Berater-Link) + Admin-Abo-Löschung
- Karten-Update ohne Abbuchung via PAYONE creditcardcheck/pseudocardpan
  (AboPaymentMethodService, CardUpdateResult/CreditCardCheckData-DTOs)
- Eingabe nur durch Abo-Inhaber: CRM- und Portal-Formular + zeitlich
  begrenzter Berater→Kunde-Link (AboCardUpdateLink + Mail)
- Änderungshistorie in user_abo_card_changes, Anzeige im Admin-Abo-Detail
- Admin: Abo löschen (destroy) inkl. Test
- Portal-Fix: myOrderCreate wirft 404 statt TypeError bei ungültiger ID
- VIP-Sichtbarkeitstests für Einmalartikel-Fenster ergänzt
- Enthält bereits die Freeze-Guards für den Stripe-Notbetrieb (Folge-Commit)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 15:56:19 +00:00

127 lines
4.3 KiB
PHP

<?php
use App\Http\Controllers\Portal\AboController;
use App\Models\Customer;
use App\Models\ShoppingUser;
use App\Models\UserAbo;
use App\Services\AboPaymentMethodService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\URL;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class);
function stubMissingRoutes(): void
{
URL::resolveMissingNamedRoutesUsing(
fn (string $name, array $parameters) => '/'.$name.'/'.implode('/', $parameters)
);
}
function makePortalAboFixture(array $cardOverride = []): array
{
$shoppingUser = ShoppingUser::create([
'auth_user_id' => 1,
'billing_firstname' => 'Karl',
'billing_lastname' => 'Kunde',
'billing_address' => 'Kundenweg 2',
'billing_zipcode' => '11111',
'billing_city' => 'Kundenstadt',
'billing_country_id' => 1,
'shipping_country_id' => 1,
'billing_email' => 'kunde@example.com',
'same_as_billing' => true,
'mode' => 'test',
'wp_order_number' => 0,
]);
$abo = UserAbo::create([
'user_id' => 1,
'shopping_user_id' => $shoppingUser->id,
'email' => 'kunde@example.com',
'clearingtype' => 'cc',
'wallettype' => '',
'payone_userid' => 111,
'carddata' => array_merge([
'pseudocardpan' => 'OLD_PSEUDO',
'truncatedcardpan' => '411111XXXXXX0000',
'cardexpiredate' => '2501',
], $cardOverride),
'abo_interval' => 5,
'status' => 2,
'card_update_pending_at' => now(),
]);
$customer = Customer::create([
'name' => 'Karl Kunde',
'email' => 'kunde@example.com',
'shopping_user_id' => $shoppingUser->id,
]);
return compact('shoppingUser', 'abo', 'customer');
}
it('formats the masked card from token data only', function () {
$abo = new UserAbo([
'clearingtype' => 'cc',
'carddata' => ['truncatedcardpan' => '411111XXXXXX1234', 'cardexpiredate' => '3012'],
]);
expect($abo->maskedCard())->toBe('411111XXXXXX1234 (12/30)');
$noToken = new UserAbo(['clearingtype' => 'cc', 'carddata' => ['pseudocardpan' => 'X']]);
expect($noToken->maskedCard())->toBeNull();
});
it('renders the portal credit-card form with current card and cc fields', function () {
stubMissingRoutes();
$abo = makePortalAboFixture()['abo'];
$html = view('portal.abo._payment_method_form', ['user_abo' => $abo])->render();
expect($html)
->toContain('name="cc_cardpan"')
->toContain('name="cc_cardcvc2"')
->toContain('name="cc_cardexpiremonth"')
->toContain('411111XXXXXX0000 (01/25)')
->toContain(__('abo.update_payment_method'))
->toContain(__('abo.card_security_notice'));
});
it('applies the stashed card on a successful 3-D Secure return', function () {
stubMissingRoutes();
$fixture = makePortalAboFixture();
$this->actingAs($fixture['customer'], 'customers');
session(['abo_card_update.'.$fixture['abo']->id => [
'carddata' => ['pseudocardpan' => 'NEW_PSEUDO', 'truncatedcardpan' => '555555XXXXXX9999', 'cardexpiredate' => '3011'],
'userid' => 777,
]]);
(new AboController)->paymentMethodReturn(
$fixture['abo']->id, 'success', app(AboPaymentMethodService::class)
);
$abo = $fixture['abo']->fresh();
expect($abo->carddata['pseudocardpan'])->toBe('NEW_PSEUDO')
->and($abo->payone_userid)->toEqual(777)
->and($abo->card_update_pending_at)->toBeNull()
->and($abo->card_changes()->count())->toBe(1)
->and($abo->card_changes()->first()->source)->toBe('portal')
->and($abo->card_changes()->first()->masked_card)->toBe('555555XXXXXX9999 (11/30)');
});
it('clears the pending trace and keeps the card on a cancelled 3-D Secure return', function () {
stubMissingRoutes();
$fixture = makePortalAboFixture();
$this->actingAs($fixture['customer'], 'customers');
(new AboController)->paymentMethodReturn(
$fixture['abo']->id, 'cancel', app(AboPaymentMethodService::class)
);
$abo = $fixture['abo']->fresh();
expect($abo->carddata['pseudocardpan'])->toBe('OLD_PSEUDO')
->and($abo->payone_userid)->toEqual(111)
->and($abo->card_update_pending_at)->toBeNull();
});