$status], $extra); $body = implode("\n", array_map(fn ($v, $k) => "$k=$v", $pairs, array_keys($pairs))); $mock = new MockHandler([new Response(200, ['Content-Type' => 'text/plain;charset=UTF-8'], $body)]); return new Client(['handler' => HandlerStack::create($mock)]); } function makeService(Client $client): PayonePaymentService { return new PayonePaymentService( defaults: ['mid' => '1', 'aid' => '1', 'portalid' => '1', 'key' => 'abc', 'api_version' => '3.11'], client: $client, ); } // --- PayonePaymentResult DTO --- it('fromResponse baut ein vollständiges Ergebnis-DTO', function (): void { $raw = [ 'status' => 'APPROVED', 'txid' => '12345', 'userid' => '67890', 'redirecturl' => null, 'errormessage' => null, 'customermessage' => null, 'errorcode' => null, ]; $result = PayonePaymentResult::fromResponse($raw); expect($result->status)->toBe('APPROVED') ->and($result->txid)->toBe('12345') ->and($result->userid)->toEqual(67890) ->and($result->isApproved())->toBeTrue() ->and($result->isError())->toBeFalse() ->and($result->isRedirect())->toBeFalse() ->and($result->toArray())->toBe($raw); }); it('fromResponse setzt UNKNOWN bei fehlendem Status', function (): void { $result = PayonePaymentResult::fromResponse([]); expect($result->status)->toBe('UNKNOWN') ->and($result->txid)->toBe('') ->and($result->userid)->toEqual(0); }); it('isValid erkennt VALID-Status (creditcardcheck)', function (): void { $result = PayonePaymentResult::fromResponse(['status' => 'VALID', 'pseudocardpan' => 'PSEUDO1']); expect($result->isValid())->toBeTrue() ->and($result->isApproved())->toBeFalse(); }); // --- checkCreditCard --- it('checkCreditCard gibt PayonePaymentResult mit VALID zurück', function (): void { $client = makePayoneClient('VALID', ['pseudocardpan' => 'PSEUDO999']); $service = makeService($client); $result = $service->checkCreditCard([ 'cc_cardholder_first' => 'Max', 'cc_cardholder_last' => 'Mustermann', 'cc_cardpan' => '4111111111111111', 'cc_cardexpireyear' => '2026', 'cc_cardexpiremonth' => '12', 'cc_cardtype' => 'V', 'cc_cardcvc2' => '123', ], 'test'); expect($result)->toBeInstanceOf(PayonePaymentResult::class) ->and($result->status)->toBe('VALID') ->and($result->isValid())->toBeTrue() ->and($result->rawResponse['pseudocardpan'])->toBe('PSEUDO999'); }); it('checkCreditCard gibt ERROR-Ergebnis bei abgelehnter Karte zurück', function (): void { $client = makePayoneClient('ERROR', ['errorcode' => '863', 'errormessage' => 'Card declined']); $service = makeService($client); $result = $service->checkCreditCard([ 'cc_cardholder_first' => 'Max', 'cc_cardholder_last' => 'Mustermann', 'cc_cardpan' => '4111111111111111', 'cc_cardexpireyear' => '2025', 'cc_cardexpiremonth' => '01', 'cc_cardtype' => 'V', 'cc_cardcvc2' => '000', ], 'test'); expect($result->isError())->toBeTrue() ->and($result->errorCode)->toBe('863') ->and($result->errorMessage)->toBe('Card declined'); }); // --- chargeAbo --- it('chargeAbo erstellt ShoppingPayment und gibt APPROVED zurück', function (): void { $client = makePayoneClient('APPROVED', ['txid' => '77777', 'userid' => '12345']); $service = makeService($client); $shoppingUser = ShoppingUser::create([ 'auth_user_id' => 1, 'billing_firstname' => 'Max', 'billing_lastname' => 'Mustermann', 'billing_address' => 'Musterstraße 1', 'billing_zipcode' => '12345', 'billing_city' => 'Musterstadt', 'billing_country_id' => 1, 'shipping_country_id' => 1, 'billing_email' => 'max@example.com', 'same_as_billing' => true, 'mode' => 'test', 'wp_order_number' => 0, ]); $shoppingOrder = ShoppingOrder::create([ 'shopping_user_id' => $shoppingUser->id, 'auth_user_id' => 1, 'country_id' => 1, 'user_shop_id' => 1, 'mode' => 'test', 'is_abo' => 1, 'abo_interval' => 1, 'total_shipping' => '99.00', ]); $userAbo = UserAbo::create([ 'user_id' => 1, 'shopping_user_id' => $shoppingUser->id, 'clearingtype' => 'cc', 'wallettype' => '', 'payone_userid' => 99, 'carddata' => ['pseudocardpan' => 'PSEUDO123', 'cardexpiredate' => '2612'], 'abo_interval' => 1, 'status' => 1, ]); $result = $service->chargeAbo($userAbo, $shoppingOrder, $shoppingUser, 9900, 'EUR'); expect($result)->toBeInstanceOf(PayonePaymentResult::class) ->and($result->isApproved())->toBeTrue() ->and($result->txid)->toBe('77777') ->and($result->userid)->toEqual(12345) ->and($result->shoppingPayment)->toBeInstanceOf(ShoppingPayment::class) ->and($result->shoppingPayment->clearingtype)->toBe('cc') ->and($result->shoppingPayment->amount)->toEqual(9900) ->and($result->shoppingPayment->currency)->toBe('EUR') ->and($result->shoppingPayment->is_abo)->toBeTruthy() ->and($result->shoppingPayment->mode)->toBe('test'); expect(ShoppingPayment::count())->toEqual(1); }); it('chargeAbo gibt ERROR-Ergebnis zurück bei abgelehnter Abbuchung', function (): void { $client = makePayoneClient('ERROR', ['errorcode' => '307', 'errormessage' => 'Transaction declined']); $service = makeService($client); $shoppingUser = ShoppingUser::create([ 'auth_user_id' => 1, 'billing_firstname' => 'Anna', 'billing_lastname' => 'Test', 'billing_address' => 'Testweg 5', 'billing_zipcode' => '54321', 'billing_city' => 'Testort', 'billing_country_id' => 1, 'shipping_country_id' => 1, 'billing_email' => 'anna@test.com', 'same_as_billing' => true, 'mode' => 'test', 'wp_order_number' => 0, ]); $shoppingOrder = ShoppingOrder::create([ 'shopping_user_id' => $shoppingUser->id, 'auth_user_id' => 1, 'country_id' => 1, 'user_shop_id' => 1, 'mode' => 'live', 'is_abo' => 1, 'abo_interval' => 1, 'total_shipping' => '49.50', ]); $userAbo = UserAbo::create([ 'user_id' => 1, 'shopping_user_id' => $shoppingUser->id, 'clearingtype' => 'cc', 'wallettype' => '', 'payone_userid' => 42, 'carddata' => ['pseudocardpan' => 'PSEUDO456'], 'abo_interval' => 1, 'status' => 1, ]); $result = $service->chargeAbo($userAbo, $shoppingOrder, $shoppingUser, 4950, 'EUR'); expect($result->isError())->toBeTrue() ->and($result->errorCode)->toBe('307') ->and($result->shoppingPayment)->toBeInstanceOf(ShoppingPayment::class) ->and($result->shoppingPayment->mode)->toBe('live'); // ShoppingPayment wird auch bei ERROR erstellt (für Protokollierung) expect(ShoppingPayment::count())->toEqual(1); });