mivita/tests/Feature/AboPendingInitialPaymentTest.php
Kevin 2b2847add1 Abo: Doppelbestellung verhindern, Payment-Link loeschen & Pending-Status
Verhindert das Anlegen eines zweiten Abos (eigenes oder Kundenabo) wenn
bereits eines initiiert ist bzw. ein offener Abo-Payment-Link existiert
(redirectIfAboAlreadyInitiated). Payment-Links koennen jetzt geloescht
werden, abgesichert ueber canDeleteInstance + Shop-Zugehoerigkeit, mit
Erfolgs-/Fehler-Flash. Kundenabo-Uebersicht zeigt den Pending-Initial-
Payment-Status (wartend / in Bearbeitung / fehlgeschlagen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 13:37:30 +00:00

223 lines
7.5 KiB
PHP

<?php
use App\Models\Country;
use App\Models\Shipping;
use App\Models\ShippingCountry;
use App\Models\ShoppingInstance;
use App\Models\ShoppingOrder;
use App\Models\ShoppingPayment;
use App\Models\ShoppingUser;
use App\Models\UserAbo;
use App\Models\UserAboOrder;
use App\Models\UserShop;
use App\Services\AboHelper;
use App\Services\OrderPaymentService;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\URL;
use Tests\TestCase;
uses(TestCase::class, RefreshDatabase::class);
it('behandelt neue noch nicht bestaetigte Eigen-Abos als vorhandenes Abo', function (): void {
$fixture = createPendingInitialAboFixture();
expect(AboHelper::userHasAbo($fixture['user']))->toBeTrue()
->and($fixture['userAbo']->isPendingInitialPayment())->toBeTrue()
->and($fixture['userAbo']->hasInitialPaymentStatus())->toBeFalse()
->and($fixture['userAbo']->canDeletePendingInitialPayment())->toBeFalse();
});
it('behandelt neue Kunden-Abos anhand der E-Mail als vorhandenes Abo', function (): void {
$fixture = createPendingInitialAboFixture(['is_for' => 'ot']);
expect(AboHelper::hasAboByEmail($fixture['shoppingUser']->billing_email))->toBeTrue()
->and(AboHelper::memberHasAbo($fixture['shoppingUser']))->toBeTrue();
});
it('erlaubt das Loeschen eines neuen Abo-Eintrags erst bei fehlgeschlagener Erstzahlung', function (): void {
$fixture = createPendingInitialAboFixture(paymentOverrides: ['txaction' => 'failed']);
expect($fixture['userAbo']->fresh()->hasInitialPaymentStatus())->toBeTrue()
->and($fixture['userAbo']->fresh()->hasFailedInitialPayment())->toBeTrue()
->and($fixture['userAbo']->fresh()->canDeletePendingInitialPayment())->toBeTrue();
});
it('erlaubt Abo-Zahlungslink-Loeschung nur vor Zahlung oder nach Fehler', function (): void {
foreach ([0, 1, 5, 6] as $status) {
$instance = new ShoppingInstance(['status' => $status]);
expect(OrderPaymentService::canDeleteInstance($instance))->toBeTrue();
}
foreach ([2, 3, 4, 10] as $status) {
$instance = new ShoppingInstance(['status' => $status]);
expect(OrderPaymentService::canDeleteInstance($instance))->toBeFalse();
}
});
it('zeigt gesendete Abo-Zahlungslinks in der Kunden-Abo-Uebersicht', function (): void {
URL::resolveMissingNamedRoutesUsing(fn (string $name, array $parameters) => '/'.$name.'/'.implode('/', $parameters));
$fixture = createPendingInitialAboFixture();
$this->actingAs($fixture['user']);
$paymentLink = new ShoppingInstance([
'identifier' => 'paylink-'.uniqid(),
'user_shop_id' => $fixture['userShop']->id,
'payment' => 6,
'subdomain' => 'test.local',
'country_id' => $fixture['countryId'],
'amount' => 99.50,
'shopping_user_id' => $fixture['shoppingUser']->id,
'status' => 0,
'shopping_data' => [
'is_for' => 'abo-ot-customer',
'is_abo' => true,
'billing_email' => $fixture['shoppingUser']->billing_email,
],
]);
$paymentLink->created_at = now();
$html = view('user.abo.index', [
'user_abos' => collect(),
'abo_payment_links' => collect([$paymentLink]),
'view' => 'ot',
'isAdmin' => false,
])->render();
expect($html)
->toContain(__('abo.payment_link_sent_title'))
->toContain($paymentLink->identifier)
->toContain($fixture['shoppingUser']->billing_email)
->toContain(__('delete'));
});
it('blendet Abo-Zahlungslinks aus sobald daraus ein Abo existiert', function (): void {
$fixture = createPendingInitialAboFixture(['is_for' => 'ot']);
$paymentLink = new ShoppingInstance([
'user_shop_id' => $fixture['userShop']->id,
'payment' => 6,
'shopping_user_id' => $fixture['shoppingUser']->id,
'status' => 3,
'shopping_data' => [
'is_for' => 'abo-ot-customer',
'is_abo' => true,
'shopping_user_id' => $fixture['shoppingUser']->id,
'billing_email' => $fixture['shoppingUser']->billing_email,
],
]);
expect(OrderPaymentService::hasUserAboForPaymentLink($paymentLink, $fixture['user']))->toBeTrue();
});
/**
* @return array{user: User, userShop: UserShop, shoppingUser: ShoppingUser, userAbo: UserAbo, payment: ShoppingPayment, countryId: int}
*/
function createPendingInitialAboFixture(array $aboOverrides = [], array $paymentOverrides = []): array
{
$country = Country::create([
'code' => 'DE',
'phone' => '49',
'en' => 'Germany',
'de' => 'Deutschland',
'es' => 'Alemania',
'fr' => 'Allemagne',
'it' => 'Germania',
'ru' => 'Deutschland',
]);
$shipping = Shipping::create([
'name' => 'Standard',
'active' => true,
]);
$shippingCountry = ShippingCountry::create([
'shipping_id' => $shipping->id,
'country_id' => $country->id,
]);
$user = User::forceCreate([
'email' => 'pending-abo-'.uniqid('', true).'@example.com',
'password' => bcrypt('secret'),
'lang' => 'de',
]);
$userShop = UserShop::create([
'user_id' => $user->id,
'name' => 'TS'.substr(uniqid('', true), 0, 8),
'slug' => 'ts-'.uniqid(),
'active' => true,
]);
$shoppingUser = ShoppingUser::create([
'auth_user_id' => $user->id,
'member_id' => $user->id,
'billing_country_id' => $country->id,
'shipping_country_id' => $country->id,
'billing_email' => 'pending-customer-'.uniqid('', true).'@example.com',
'shipping_email' => $user->email,
'is_for' => $aboOverrides['is_for'] ?? 'me',
'is_from' => 'user_order',
]);
$shoppingOrder = ShoppingOrder::create([
'shopping_user_id' => $shoppingUser->id,
'auth_user_id' => $user->id,
'member_id' => $user->id,
'country_id' => $shippingCountry->id,
'user_shop_id' => $userShop->id,
'payment_for' => 2,
'is_abo' => true,
'abo_interval' => 5,
'paid' => false,
'mode' => 'test',
'total' => 100,
'subtotal' => 90,
]);
$payment = ShoppingPayment::create(array_merge([
'shopping_order_id' => $shoppingOrder->id,
'reference' => 'pending-ref-'.uniqid(),
'amount' => 10000,
'currency' => 'EUR',
'clearingtype' => 'cc',
'abo_interval' => 5,
'mode' => 'test',
], $paymentOverrides));
if (array_key_exists('txaction', $paymentOverrides)) {
$payment->txaction = $paymentOverrides['txaction'];
$payment->save();
}
$userAbo = UserAbo::create(array_merge([
'user_id' => $user->id,
'member_id' => $user->id,
'shopping_user_id' => $shoppingUser->id,
'email' => $shoppingUser->billing_email,
'is_for' => $aboOverrides['is_for'] ?? 'me',
'payone_userid' => 0,
'clearingtype' => 'cc',
'amount' => 10000,
'status' => 1,
'abo_interval' => 5,
'start_date' => now(),
'last_date' => now(),
'next_date' => now()->addMonth(),
], $aboOverrides));
UserAboOrder::create([
'user_abo_id' => $userAbo->id,
'shopping_order_id' => $shoppingOrder->id,
'status' => 1,
'paid' => false,
]);
return [
'user' => $user,
'userShop' => $userShop,
'shoppingUser' => $shoppingUser,
'userAbo' => $userAbo,
'payment' => $payment,
'countryId' => $shippingCountry->id,
];
}