From 90e37a18d838fc25cb685985a13b372e61938211 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Tue, 13 Jun 2023 10:47:04 +0200 Subject: [PATCH 01/11] Introduction of ErrorResponse --- src/Client/ValueObject/ErrorResponse.php | 165 ++++++++++++++++++ .../Client/ValueObject/ErrorResponseTest.php | 116 ++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 src/Client/ValueObject/ErrorResponse.php create mode 100644 tests/Unit/Client/ValueObject/ErrorResponseTest.php diff --git a/src/Client/ValueObject/ErrorResponse.php b/src/Client/ValueObject/ErrorResponse.php new file mode 100644 index 00000000..d9580305 --- /dev/null +++ b/src/Client/ValueObject/ErrorResponse.php @@ -0,0 +1,165 @@ +statusCode; + } + + public function getResponseHeader(): ResponseHeader + { + return $this->responseHeader; + } + + public function getName(): string + { + return $this->name; + } + + public function getMessage(): string + { + return $this->message; + } + + public function getDetail(): array + { + return $this->detail; + } + + public function getBehavior(): string + { + return $this->behavior; + } + + public function getFailedOperation(): string + { + return $this->failedOperation; + } + + public function getTransactionId(): ?string + { + return $this->transactionId; + } + + public function getOrderId(): ?string + { + return $this->orderId; + } + + public function getPayerMessage(): ?string + { + return $this->payerMessage; + } + + public function getProcessorName(): ?string + { + return $this->processorName; + } + + public function getProcessorResult(): ?string + { + return $this->processorResult; + } + + public function getProcessorMessage(): ?string + { + return $this->processorMessage; + } + + public function toArray(): array + { + return [ + 'StatusCode' => $this->getStatusCode(), + 'ResponseHeader' => $this->getResponseHeader()->toArray(), + 'Name' => $this->getName(), + 'Message' => $this->getMessage(), + 'Behavior' => $this->getBehavior(), + 'TransactionId' => $this->getTransactionId(), + 'OrderId' => $this->getOrderId(), + 'FailedOperation' => $this->getFailedOperation(), + 'PayerMessage' => $this->getPayerMessage(), + 'ProcessorName' => $this->getProcessorName(), + 'ProcessorResult' => $this->getProcessorResult(), + 'ProcessorMessage' => $this->getProcessorMessage(), + ]; + } +} diff --git a/tests/Unit/Client/ValueObject/ErrorResponseTest.php b/tests/Unit/Client/ValueObject/ErrorResponseTest.php new file mode 100644 index 00000000..31691051 --- /dev/null +++ b/tests/Unit/Client/ValueObject/ErrorResponseTest.php @@ -0,0 +1,116 @@ + 402, + 'ResponseHeader' => [ + 'SpecVersion' => '1.33', + 'RequestId' => 'b27de121-ffa0-4f1d-b7aa-b48109a88486', + ], + 'Behavior' => 'DO_NOT_RETRY', + 'ErrorName' => '3DS_AUTHENTICATION_FAILED', + 'ErrorMessage' => '3D-Secure authentication failed', + 'TransactionId' => '723n4MAjMdhjSAhAKEUdA8jtl9jb', + 'PayerMessage' => 'Card holder information -> Failed', + 'OrderId' => '000000042', + ]); + + $this->assertEquals(402, $response->getStatusCode()); + $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals('DO_NOT_RETRY', $response->getBehavior()); + $this->assertEquals('3DS_AUTHENTICATION_FAILED', $response->getName()); + $this->assertEquals('3D-Secure authentication failed', $response->getMessage()); + $this->assertEquals('723n4MAjMdhjSAhAKEUdA8jtl9jb', $response->getTransactionId()); + $this->assertEquals('Card holder information -> Failed', $response->getPayerMessage()); + $this->assertEquals('000000042', $response->getOrderId()); + $this->assertEquals('Assert', $response->getFailedOperation()); + } + + /** @test */ + public function it_creates_error_response_vo_for_capture_from_array(): void + { + $response = ErrorResponse::forCapture([ + 'StatusCode' => 402, + 'ResponseHeader' => [ + 'SpecVersion' => '1.33', + 'RequestId' => 'b27de121-ffa0-4f1d-b7aa-b48109a88486', + ], + "Behavior" => "DO_NOT_RETRY", + "ErrorName" => "TRANSACTION_ALREADY_CAPTURED", + "ErrorMessage" => "Transaction already captured", + ]); + + $this->assertEquals(402, $response->getStatusCode()); + $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals('DO_NOT_RETRY', $response->getBehavior()); + $this->assertEquals('TRANSACTION_ALREADY_CAPTURED', $response->getName()); + $this->assertEquals('Transaction already captured', $response->getMessage()); + $this->assertEquals('Capture', $response->getFailedOperation()); + } + + /** @test */ + public function it_creates_error_response_vo_for_authorize_from_array(): void + { + $response = ErrorResponse::forAuthorize([ + 'StatusCode' => 402, + 'ResponseHeader' => [ + 'SpecVersion' => '1.33', + 'RequestId' => 'b27de121-ffa0-4f1d-b7aa-b48109a88486', + ], + "Behavior" => "DO_NOT_RETRY", + "ErrorName" => "VALIDATION_FAILED", + "ErrorMessage" => "Request validation failed", + "ErrorDetail" => [ + "TerminalId: The field TerminalId is invalid.", + ], + ]); + + $this->assertEquals(402, $response->getStatusCode()); + $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals('DO_NOT_RETRY', $response->getBehavior()); + $this->assertEquals('VALIDATION_FAILED', $response->getName()); + $this->assertEquals('Request validation failed', $response->getMessage()); + $this->assertEquals(["TerminalId: The field TerminalId is invalid."], $response->getDetail()); + $this->assertEquals('Authorize', $response->getFailedOperation()); + } + + /** @test */ + public function it_creates_error_response_vo_for_refund_from_array(): void + { + $response = ErrorResponse::forRefund([ + 'StatusCode' => 402, + 'ResponseHeader' => [ + 'SpecVersion' => '1.33', + 'RequestId' => 'b27de121-ffa0-4f1d-b7aa-b48109a88486', + ], + "Behavior" => "DO_NOT_RETRY", + "ErrorName" => "TRANSACTION_NOT_FOUND", + "ErrorMessage" => "Transaction not found", + ]); + + $this->assertEquals(402, $response->getStatusCode()); + $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals('DO_NOT_RETRY', $response->getBehavior()); + $this->assertEquals('TRANSACTION_NOT_FOUND', $response->getName()); + $this->assertEquals('Transaction not found', $response->getMessage()); + $this->assertEquals('Refund', $response->getFailedOperation()); + } + + private function assertResponseHeader(ResponseHeader $responseHeader): void + { + $this->assertEquals('1.33', $responseHeader->getSpecVersion()); + $this->assertEquals('b27de121-ffa0-4f1d-b7aa-b48109a88486', $responseHeader->getRequestId()); + } +} From 66eeef8a68d48402dd2ee8736907ba24ec9ad0c5 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Tue, 13 Jun 2023 12:00:03 +0200 Subject: [PATCH 02/11] Fix existing VO tests --- src/Client/ValueObject/AssertResponse.php | 2 +- src/Client/ValueObject/AuthorizeResponse.php | 2 +- src/Client/ValueObject/CaptureResponse.php | 2 +- src/Client/ValueObject/RefundResponse.php | 2 +- tests/Unit/Client/ValueObject/AssertResponseTest.php | 2 ++ tests/Unit/Client/ValueObject/AuthorizeResponseTest.php | 5 ++++- tests/Unit/Client/ValueObject/CaptureResponseTest.php | 3 +++ tests/Unit/Client/ValueObject/RefundResponseTest.php | 3 +++ 8 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Client/ValueObject/AssertResponse.php b/src/Client/ValueObject/AssertResponse.php index ecb7753a..1ef63abd 100644 --- a/src/Client/ValueObject/AssertResponse.php +++ b/src/Client/ValueObject/AssertResponse.php @@ -80,7 +80,7 @@ public function toArray(): array public static function fromArray(array $data): self { return new self( - $data['StatusCode'], + $data['StatusCode'] ?? 400, ResponseHeader::fromArray($data['ResponseHeader']), isset($data['Transaction']) ? Transaction::fromArray($data['Transaction']) : null, isset($data['PaymentMeans']) ? PaymentMeans::fromArray($data['PaymentMeans']) : null, diff --git a/src/Client/ValueObject/AuthorizeResponse.php b/src/Client/ValueObject/AuthorizeResponse.php index 612c4756..c35bfeb7 100644 --- a/src/Client/ValueObject/AuthorizeResponse.php +++ b/src/Client/ValueObject/AuthorizeResponse.php @@ -70,7 +70,7 @@ public function toArray(): array public static function fromArray(array $data): self { return new self( - $data['StatusCode'], + $data['StatusCode'] ?? 400, ResponseHeader::fromArray($data['ResponseHeader']), $data['Token'] ?? null, $data['Expiration'] ?? null, diff --git a/src/Client/ValueObject/CaptureResponse.php b/src/Client/ValueObject/CaptureResponse.php index f5fb33a3..01eeab9f 100644 --- a/src/Client/ValueObject/CaptureResponse.php +++ b/src/Client/ValueObject/CaptureResponse.php @@ -69,7 +69,7 @@ public function toArray(): array public static function fromArray(array $data): self { return new self( - $data['StatusCode'], + $data['StatusCode'] ?? 400, ResponseHeader::fromArray($data['ResponseHeader']), $data['CaptureId'] ?? null, $data['Status'] ?? null, diff --git a/src/Client/ValueObject/RefundResponse.php b/src/Client/ValueObject/RefundResponse.php index 27a9a9ae..88bbaff7 100644 --- a/src/Client/ValueObject/RefundResponse.php +++ b/src/Client/ValueObject/RefundResponse.php @@ -64,7 +64,7 @@ public function toArray(): array public static function fromArray(array $data): self { return new self( - $data['StatusCode'], + $data['StatusCode'] ?? 400, ResponseHeader::fromArray($data['ResponseHeader']), isset($data['Transaction']) ? Transaction::fromArray($data['Transaction']) : null, isset($data['PaymentMeans']) ? PaymentMeans::fromArray($data['PaymentMeans']) : null, diff --git a/tests/Unit/Client/ValueObject/AssertResponseTest.php b/tests/Unit/Client/ValueObject/AssertResponseTest.php index bf3a7e6e..6e7622bc 100644 --- a/tests/Unit/Client/ValueObject/AssertResponseTest.php +++ b/tests/Unit/Client/ValueObject/AssertResponseTest.php @@ -62,6 +62,7 @@ public function it_creates_assert_response_vo_from_array(): void ], ]); + $this->assertEquals(200, $response->getStatusCode()); $this->assertResponseHeader($response->getResponseHeader()); $transaction = $response->getTransaction(); @@ -174,6 +175,7 @@ public function it_creates_assert_response_vo_with_an_error_from_array(): void ]); $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals(402, $response->getStatusCode()); $this->assertEquals('DO_NOT_RETRY', $response->getError()->getBehavior()); $this->assertEquals('3DS_AUTHENTICATION_FAILED', $response->getError()->getName()); $this->assertEquals('3D-Secure authentication failed', $response->getError()->getMessage()); diff --git a/tests/Unit/Client/ValueObject/AuthorizeResponseTest.php b/tests/Unit/Client/ValueObject/AuthorizeResponseTest.php index c7ae50fc..82c5be09 100644 --- a/tests/Unit/Client/ValueObject/AuthorizeResponseTest.php +++ b/tests/Unit/Client/ValueObject/AuthorizeResponseTest.php @@ -25,11 +25,13 @@ public function it_creates_authorize_response_vo_from_array(): void ]); $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('sk6jU1jJ7KqO1hgC', $response->getToken()); $this->assertEquals('2025-01-30T12:45:22.258+01:00', $response->getExpiration()); $this->assertEquals('https://www.saferpay.com/api/redirect', $response->getRedirectUrl()); } + /** @test */ public function it_creates_authorize_response_vo_with_an_error_from_array(): void { $response = AuthorizeResponse::fromArray([ @@ -46,10 +48,11 @@ public function it_creates_authorize_response_vo_with_an_error_from_array(): voi ]); $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals(400, $response->getStatusCode()); $this->assertEquals('DO_NOT_RETRY', $response->getError()->getBehavior()); $this->assertEquals('VALIDATION_FAILED', $response->getError()->getName()); $this->assertEquals('Request validation failed', $response->getError()->getMessage()); - $this->assertEquals(['TerminalId: The field TerminalId is invalid.'], $response->getError()->getDetails()); + $this->assertEquals(['TerminalId: The field TerminalId is invalid.'], $response->getError()->getDetail()); } private function assertResponseHeader(ResponseHeader $responseHeader): void diff --git a/tests/Unit/Client/ValueObject/CaptureResponseTest.php b/tests/Unit/Client/ValueObject/CaptureResponseTest.php index 3f282d57..1c2c372e 100644 --- a/tests/Unit/Client/ValueObject/CaptureResponseTest.php +++ b/tests/Unit/Client/ValueObject/CaptureResponseTest.php @@ -25,11 +25,13 @@ public function it_creates_capture_response_vo_from_array(): void ]); $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('723n4MAjMdhjSAhAKEUdA8jtl9jb', $response->getCaptureId()); $this->assertEquals('CAPTURED', $response->getStatus()); $this->assertEquals('2015-01-30T12:45:22.258+01:00', $response->getDate()); } + /** @test */ public function it_creates_capture_response_vo_with_an_error_from_array(): void { $response = CaptureResponse::fromArray([ @@ -43,6 +45,7 @@ public function it_creates_capture_response_vo_with_an_error_from_array(): void ]); $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals(400, $response->getStatusCode()); $this->assertEquals('DO_NOT_RETRY', $response->getError()->getBehavior()); $this->assertEquals('TRANSACTION_ALREADY_CAPTURED', $response->getError()->getName()); $this->assertEquals('Transaction already captured', $response->getError()->getMessage()); diff --git a/tests/Unit/Client/ValueObject/RefundResponseTest.php b/tests/Unit/Client/ValueObject/RefundResponseTest.php index 2ff1c62c..ecfac831 100644 --- a/tests/Unit/Client/ValueObject/RefundResponseTest.php +++ b/tests/Unit/Client/ValueObject/RefundResponseTest.php @@ -54,11 +54,13 @@ public function it_creates_refund_response_vo_from_array(): void ], ]); + $this->assertEquals(200, $response->getStatusCode()); $this->assertResponseHeader($response->getResponseHeader()); $this->assertTransaction($response->getTransaction()); $this->assertPaymentMeans($response->getPaymentMeans()); } + /** @test */ public function it_creates_refund_response_vo_with_an_error_from_array(): void { $response = RefundResponse::fromArray([ @@ -72,6 +74,7 @@ public function it_creates_refund_response_vo_with_an_error_from_array(): void ]); $this->assertResponseHeader($response->getResponseHeader()); + $this->assertEquals(400, $response->getStatusCode()); $this->assertEquals('DO_NOT_RETRY', $response->getError()->getBehavior()); $this->assertEquals('TRANSACTION_NOT_FOUND', $response->getError()->getName()); $this->assertEquals('Transaction not found', $response->getError()->getMessage()); From 7470a4ac80b19415e567e0ecad55deff004b9e01 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 07:29:18 +0200 Subject: [PATCH 03/11] Use ErrorResponse in Assert action --- spec/Client/SaferpayClientSpec.php | 83 +++++++++++++++++++ .../PaymentEventDispatcherSpec.php | 21 ++--- spec/Payum/Action/AssertActionSpec.php | 25 +++--- src/Client/SaferpayClient.php | 19 +++-- src/Client/SaferpayClientInterface.php | 5 +- src/Client/ValueObject/ErrorResponse.php | 2 +- .../PaymentEventDispatcher.php | 3 +- .../PaymentEventDispatcherInterface.php | 3 +- src/Payum/Action/AssertAction.php | 7 +- 9 files changed, 128 insertions(+), 40 deletions(-) diff --git a/spec/Client/SaferpayClientSpec.php b/spec/Client/SaferpayClientSpec.php index 5fde65bc..11342fb0 100644 --- a/spec/Client/SaferpayClientSpec.php +++ b/spec/Client/SaferpayClientSpec.php @@ -9,6 +9,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AssertResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payment\EventDispatcher\PaymentEventDispatcherInterface; use CommerceWeavers\SyliusSaferpayPlugin\Resolver\SaferpayApiBaseUrlResolverInterface; @@ -227,6 +228,70 @@ function it_performs_assert_request( $this->assert($payment)->shouldBeAnInstanceOf(AssertResponse::class); } + function it_performs_failed_assert_request( + HttpClientInterface $client, + SaferpayClientBodyFactoryInterface $saferpayClientBodyFactory, + SaferpayApiBaseUrlResolverInterface $saferpayApiBaseUrlResolver, + PaymentEventDispatcherInterface $paymentEventDispatcher, + PaymentInterface $payment, + PaymentMethodInterface $paymentMethod, + GatewayConfigInterface $gatewayConfig, + ResponseInterface $response, + ): void { + $payload = [ + 'RequestHeader' => [ + 'SpecVersion' => '1.33', + 'CustomerId' => 'CUSTOMER-ID', + 'RequestId' => 'b27de121-ffa0-4f1d-b7aa-b48109a88486', + 'RetryIndicator' => 0, + ], + 'Token' => 'TOKEN', + ]; + $saferpayClientBodyFactory->createForAssert($payment)->willReturn($payload); + $saferpayApiBaseUrlResolver->resolve($gatewayConfig)->willReturn('https://test.saferpay.com/api/'); + + $payment->getId()->willReturn(1); + $payment->getDetails()->willReturn(['saferpay_token' => 'TOKEN']); + $payment->getMethod()->willReturn($paymentMethod); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $gatewayConfig->getConfig()->willReturn([ + 'username' => 'USERNAME', + 'password' => 'PASSWORD', + 'sandbox' => true, + ]); + + $client + ->request( + 'POST', + 'https://test.saferpay.com/api/Payment/v1/PaymentPage/Assert', + [ + 'headers' => [ + 'Authorization' => 'Basic ' . base64_encode('USERNAME:PASSWORD'), + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + ], + 'body' => json_encode($payload), + ] + ) + ->shouldBeCalled() + ->willReturn($response); + + $response->getStatusCode()->willReturn(400); + $response->getContent(false)->willReturn($this->getExampleAssertFailedResponse()); + + $paymentEventDispatcher + ->dispatchAssertionFailedEvent( + $payment, + 'Payment/v1/PaymentPage/Assert', + $payload, + ErrorResponse::forAssert(array_merge(['StatusCode' => 400], json_decode($this->getExampleAssertFailedResponse(), true))) + ) + ->shouldBeCalled() + ; + + $this->assert($payment)->shouldBeAnInstanceOf(ErrorResponse::class); + } + function it_performs_capture_request( HttpClientInterface $client, SaferpayClientBodyFactoryInterface $saferpayClientBodyFactory, @@ -537,6 +602,24 @@ private function getExampleAssertResponse(): string RESPONSE; } + private function getExampleAssertFailedResponse(): string + { + return <<getRequestUrl() === 'Payment/v1/PaymentPage/Assert' && $event->getRequestBody() === $payload && $response['StatusCode'] === 402 - && $response['Error']['Name'] === '3DS_AUTHENTICATION_FAILED' - && $response['Error']['Message'] === '3D-Secure authentication failed' - && $response['Error']['Behavior'] === 'DO_NOT_RETRY' - && $response['Error']['TransactionId'] === 'Q3hd5IbzlnKpvAICv2QdA72QlA1b' - && $response['Error']['OrderId'] === '000000042' - && $response['Error']['PayerMessage'] === 'Card holder information -> Failed' - && $response['Error']['ProcessorName'] === null - && $response['Error']['ProcessorResult'] === null - && $response['Error']['ProcessorMessage'] === null + && $response['Name'] === '3DS_AUTHENTICATION_FAILED' + && $response['Message'] === '3D-Secure authentication failed' + && $response['Behavior'] === 'DO_NOT_RETRY' + && $response['TransactionId'] === 'Q3hd5IbzlnKpvAICv2QdA72QlA1b' + && $response['OrderId'] === '000000042' + && $response['PayerMessage'] === 'Card holder information -> Failed' + && $response['ProcessorName'] === null + && $response['ProcessorResult'] === null + && $response['ProcessorMessage'] === null ; })) ->shouldBeCalled() @@ -130,7 +131,7 @@ function it_dispatches_payment_assertion_failed_event( $payment, 'Payment/v1/PaymentPage/Assert', $payload, - AssertResponse::fromArray($response) + ErrorResponse::forAssert($response) ); } diff --git a/spec/Payum/Action/AssertActionSpec.php b/spec/Payum/Action/AssertActionSpec.php index df03c08c..9a6f0b3f 100644 --- a/spec/Payum/Action/AssertActionSpec.php +++ b/spec/Payum/Action/AssertActionSpec.php @@ -8,6 +8,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AssertResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Error; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Transaction; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Action\ErrorName; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Action\StatusAction; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Request\Assert; @@ -83,16 +84,14 @@ function it_asserts_the_successfull_payment( function it_asserts_the_cancelled_payment( SaferpayClientInterface $saferpayClient, SyliusPaymentInterface $payment, - AssertResponse $assertResponse, - Error $error, + ErrorResponse $errorResponse, ): void { $payment->getDetails()->willReturn([]); - $saferpayClient->assert($payment)->willReturn($assertResponse); - $assertResponse->getStatusCode()->willReturn(402); - $assertResponse->getError()->willReturn($error); - $error->getName()->willReturn(ErrorName::TRANSACTION_ABORTED); - $error->getTransactionId()->willReturn('b27de121-ffa0-4f1d-b7aa-b48109a88486'); + $saferpayClient->assert($payment)->willReturn($errorResponse); + $errorResponse->getStatusCode()->willReturn(402); + $errorResponse->getName()->willReturn(ErrorName::TRANSACTION_ABORTED); + $errorResponse->getTransactionId()->willReturn('b27de121-ffa0-4f1d-b7aa-b48109a88486'); $payment ->setDetails([ @@ -108,16 +107,14 @@ function it_asserts_the_cancelled_payment( function it_asserts_the_failed_payment( SaferpayClientInterface $saferpayClient, SyliusPaymentInterface $payment, - AssertResponse $assertResponse, - Error $error, + ErrorResponse $errorResponse, ): void { $payment->getDetails()->willReturn([]); - $saferpayClient->assert($payment)->willReturn($assertResponse); - $assertResponse->getStatusCode()->willReturn(402); - $assertResponse->getError()->willReturn($error); - $error->getName()->willReturn(ErrorName::TRANSACTION_DECLINED); - $error->getTransactionId()->willReturn('b27de121-ffa0-4f1d-b7aa-b48109a88486'); + $saferpayClient->assert($payment)->willReturn($errorResponse); + $errorResponse->getStatusCode()->willReturn(402); + $errorResponse->getName()->willReturn(ErrorName::TRANSACTION_DECLINED); + $errorResponse->getTransactionId()->willReturn('b27de121-ffa0-4f1d-b7aa-b48109a88486'); $payment ->setDetails([ diff --git a/src/Client/SaferpayClient.php b/src/Client/SaferpayClient.php index 6fca89c0..9a8e9dbf 100644 --- a/src/Client/SaferpayClient.php +++ b/src/Client/SaferpayClient.php @@ -7,6 +7,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AssertResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payment\EventDispatcher\PaymentEventDispatcherInterface; use CommerceWeavers\SyliusSaferpayPlugin\Resolver\SaferpayApiBaseUrlResolverInterface; @@ -23,10 +24,10 @@ final class SaferpayClient implements SaferpayClientInterface { - private const PAYMENT_INITIALIZE_URL = 'Payment/v1/PaymentPage/Initialize'; - private const PAYMENT_ASSERT_URL = 'Payment/v1/PaymentPage/Assert'; + private const PAYMENT_INITIALIZE_URL = 'Payment/v1/PaymentPage/Initialize'; + private const TRANSACTION_CAPTURE_URL = 'Payment/v1/Transaction/Capture'; private const TRANSACTION_REFUND_URL = 'Payment/v1/Transaction/Refund'; @@ -70,7 +71,7 @@ public function authorize(PaymentInterface $payment, TokenInterface $token): Aut return $response; } - public function assert(PaymentInterface $payment): AssertResponse + public function assert(PaymentInterface $payment): AssertResponse|ErrorResponse { $payload = $this->saferpayClientBodyFactory->createForAssert($payment); $result = $this->request( @@ -80,9 +81,9 @@ public function assert(PaymentInterface $payment): AssertResponse $this->provideGatewayConfig($payment), ); - $response = AssertResponse::fromArray($result); + if (200 === $result['StatusCode']) { + $response = AssertResponse::fromArray($result); - if ($response->isSuccessful()) { $this->paymentEventDispatcher->dispatchAssertionSucceededEvent( $payment, self::PAYMENT_ASSERT_URL, @@ -90,6 +91,8 @@ public function assert(PaymentInterface $payment): AssertResponse $response, ); } else { + $response = ErrorResponse::forAssert($result); + $this->paymentEventDispatcher->dispatchAssertionFailedEvent( $payment, self::PAYMENT_ASSERT_URL, @@ -142,9 +145,9 @@ public function refund(PaymentInterface $payment): RefundResponse $this->provideGatewayConfig($payment), ); - $response = RefundResponse::fromArray($result); + if (200 === $result['StatusCode']) { + $response = RefundResponse::fromArray($result); - if ($response->isSuccessful()) { $this->paymentEventDispatcher->dispatchRefundSucceededEvent( $payment, self::TRANSACTION_REFUND_URL, @@ -152,6 +155,8 @@ public function refund(PaymentInterface $payment): RefundResponse $response, ); } else { + $response = ErrorResponse::forRefund($result); + $this->paymentEventDispatcher->dispatchRefundFailedEvent( $payment, self::TRANSACTION_REFUND_URL, diff --git a/src/Client/SaferpayClientInterface.php b/src/Client/SaferpayClientInterface.php index 6a32bfac..799090d6 100644 --- a/src/Client/SaferpayClientInterface.php +++ b/src/Client/SaferpayClientInterface.php @@ -7,6 +7,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AssertResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; use Payum\Core\Model\GatewayConfigInterface; use Payum\Core\Security\TokenInterface; @@ -16,11 +17,11 @@ interface SaferpayClientInterface { public function authorize(PaymentInterface $payment, TokenInterface $token): AuthorizeResponse; - public function assert(PaymentInterface $payment): AssertResponse; + public function assert(PaymentInterface $payment): AssertResponse|ErrorResponse; public function capture(PaymentInterface $payment): CaptureResponse; - public function refund(PaymentInterface $payment): RefundResponse; + public function refund(PaymentInterface $payment): RefundResponse|ErrorResponse; public function getTerminal(GatewayConfigInterface $gatewayConfig): array; } diff --git a/src/Client/ValueObject/ErrorResponse.php b/src/Client/ValueObject/ErrorResponse.php index d9580305..b07c7c08 100644 --- a/src/Client/ValueObject/ErrorResponse.php +++ b/src/Client/ValueObject/ErrorResponse.php @@ -6,7 +6,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; -final class ErrorResponse +class ErrorResponse { private function __construct( private int $statusCode, diff --git a/src/Payment/EventDispatcher/PaymentEventDispatcher.php b/src/Payment/EventDispatcher/PaymentEventDispatcher.php index b6f66b83..d8223f2b 100644 --- a/src/Payment/EventDispatcher/PaymentEventDispatcher.php +++ b/src/Payment/EventDispatcher/PaymentEventDispatcher.php @@ -7,6 +7,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AssertResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAssertionFailed; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAssertionSucceeded; @@ -65,7 +66,7 @@ public function dispatchAssertionFailedEvent( PaymentInterface $payment, string $url, array $request, - AssertResponse $response, + ErrorResponse $response, ): void { /** @var int $paymentId */ $paymentId = $payment->getId(); diff --git a/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php b/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php index 8becf135..01f10157 100644 --- a/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php +++ b/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php @@ -7,6 +7,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AssertResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; use Sylius\Component\Core\Model\PaymentInterface; @@ -37,7 +38,7 @@ public function dispatchAssertionFailedEvent( PaymentInterface $payment, string $url, array $request, - AssertResponse $response, + ErrorResponse $response, ): void; public function dispatchCaptureSucceededEvent( diff --git a/src/Payum/Action/AssertAction.php b/src/Payum/Action/AssertAction.php index ec1b2ab1..281b536d 100644 --- a/src/Payum/Action/AssertAction.php +++ b/src/Payum/Action/AssertAction.php @@ -7,6 +7,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\SaferpayClientInterface; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AssertResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Error; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Request\Assert; use Payum\Core\Action\ActionInterface; use Payum\Core\Exception\RequestNotSupportedException; @@ -31,9 +32,7 @@ public function execute($request): void $response = $this->saferpayClient->assert($payment); if ($response->getStatusCode() !== Response::HTTP_OK) { - $error = $response->getError(); - WebmozartAssert::notNull($error); - $this->handleFailedResponse($payment, $error); + $this->handleFailedResponse($payment, $response); return; } @@ -46,7 +45,7 @@ public function supports($request): bool return ($request instanceof Assert) && ($request->getModel() instanceof PaymentInterface); } - private function handleFailedResponse(PaymentInterface $payment, Error $response): void + private function handleFailedResponse(PaymentInterface $payment, ErrorResponse $response): void { $paymentDetails = $payment->getDetails(); $paymentDetails['transaction_id'] = $response->getTransactionId(); From 2ca04011350364b53f43e79cd935f3e258946a20 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 08:10:05 +0200 Subject: [PATCH 04/11] Use ErrorResponse in refund action --- spec/Client/SaferpayClientSpec.php | 97 +++++++++++++++++-- .../PaymentEventDispatcherSpec.php | 20 ++-- spec/Payum/Action/RefundActionSpec.php | 6 +- src/Client/SaferpayClient.php | 2 +- .../PaymentEventDispatcher.php | 2 +- .../PaymentEventDispatcherInterface.php | 2 +- src/Payum/Action/RefundAction.php | 3 +- 7 files changed, 109 insertions(+), 23 deletions(-) diff --git a/spec/Client/SaferpayClientSpec.php b/spec/Client/SaferpayClientSpec.php index 11342fb0..3f9d0dde 100644 --- a/spec/Client/SaferpayClientSpec.php +++ b/spec/Client/SaferpayClientSpec.php @@ -62,12 +62,6 @@ function it_performs_authorize_request( 'password' => 'PASSWORD', 'sandbox' => true, ]); - $payment->getOrder()->willReturn($order); - $payment->getAmount()->willReturn(10000); - $payment->getCurrencyCode()->willReturn('CHF'); - $order->getNumber()->willReturn('000000001'); - - $token->getAfterUrl()->willReturn('https://example.com/after'); $client ->request( @@ -477,6 +471,79 @@ function it_performs_refund_request( $this->refund($payment)->shouldBeAnInstanceOf(RefundResponse::class); } + function it_handles_failed_refund_request( + HttpClientInterface $client, + SaferpayClientBodyFactoryInterface $saferpayClientBodyFactory, + SaferpayApiBaseUrlResolverInterface $saferpayApiBaseUrlResolver, + PaymentEventDispatcherInterface $paymentEventDispatcher, + PaymentInterface $payment, + PaymentMethodInterface $paymentMethod, + GatewayConfigInterface $gatewayConfig, + ResponseInterface $response, + ): void { + $payload = [ + 'RequestHeader' => [ + 'SpecVersion' => '1.33', + 'CustomerId' => 'CUSTOMER-ID', + 'RequestId' => 'b27de121-ffa0-4f1d-b7aa-b48109a88486', + 'RetryIndicator' => 0, + ], + 'Refund' => [ + 'Amount' => [ + 'Value' => 10000, + 'CurrencyCode' => 'CHF', + ], + ], + 'CaptureReference' => [ + 'CaptureId' => '0d7OYrAInYCWSASdzSh3bbr4jrSb_c', + ], + ]; + + $saferpayClientBodyFactory->createForRefund($payment)->willReturn($payload); + $saferpayApiBaseUrlResolver->resolve($gatewayConfig)->willReturn('https://test.saferpay.com/api/'); + + $payment->getId()->willReturn(1); + $payment->getDetails()->willReturn(['capture_id' => '0d7OYrAInYCWSASdzSh3bbr4jrSb_c']); + $payment->getMethod()->willReturn($paymentMethod); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $gatewayConfig->getConfig()->willReturn([ + 'username' => 'USERNAME', + 'password' => 'PASSWORD', + 'sandbox' => true, + ]); + + $client + ->request( + 'POST', + 'https://test.saferpay.com/api/Payment/v1/Transaction/Refund', + [ + 'headers' => [ + 'Authorization' => 'Basic ' . base64_encode('USERNAME:PASSWORD'), + 'Content-Type' => 'application/json', + 'Accept' => 'application/json', + ], + 'body' => json_encode($payload), + ] + ) + ->shouldBeCalled() + ->willReturn($response); + + $response->getStatusCode()->willReturn(400); + $response->getContent(false)->willReturn($this->getExampleRefundFailedResponse()); + + $paymentEventDispatcher + ->dispatchRefundFailedEvent( + $payment, + 'Payment/v1/Transaction/Refund', + $payload, + ErrorResponse::forRefund(array_merge(['StatusCode' => 400], json_decode($this->getExampleRefundFailedResponse(), true))) + ) + ->shouldBeCalled() + ; + + $this->refund($payment)->shouldBeAnInstanceOf(ErrorResponse::class); + } + function it_performs_get_terminal_request( HttpClientInterface $client, SaferpayClientBodyFactoryInterface $saferpayClientBodyFactory, @@ -678,6 +745,24 @@ private function getExampleRefundResponse(): string RESPONSE; } + private function getExampleRefundFailedResponse(): string + { + return <<getRequestUrl() === 'Payment/v1/Transaction/Refund' && $event->getRequestBody() === $payload && $response['StatusCode'] === 402 - && $response['Error']['Name'] === 'TRANSACTION_NOT_FOUND' - && $response['Error']['Message'] === 'Transaction not found' - && $response['Error']['Behavior'] === 'DO_NOT_RETRY' - && $response['Error']['TransactionId'] === null - && $response['Error']['OrderId'] === null - && $response['Error']['PayerMessage'] === null - && $response['Error']['ProcessorName'] === null - && $response['Error']['ProcessorResult'] === null - && $response['Error']['ProcessorMessage'] === null + && $response['Name'] === 'TRANSACTION_NOT_FOUND' + && $response['Message'] === 'Transaction not found' + && $response['Behavior'] === 'DO_NOT_RETRY' + && $response['TransactionId'] === null + && $response['OrderId'] === null + && $response['PayerMessage'] === null + && $response['ProcessorName'] === null + && $response['ProcessorResult'] === null + && $response['ProcessorMessage'] === null ; })) ->shouldBeCalled() @@ -232,7 +232,7 @@ function it_dispatches_payment_refund_failed_event( $payment, 'Payment/v1/Transaction/Refund', $payload, - RefundResponse::fromArray($response) + ErrorResponse::forRefund($response) ); } diff --git a/spec/Payum/Action/RefundActionSpec.php b/spec/Payum/Action/RefundActionSpec.php index 508f53b1..87219f5a 100644 --- a/spec/Payum/Action/RefundActionSpec.php +++ b/spec/Payum/Action/RefundActionSpec.php @@ -7,6 +7,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\SaferpayClientInterface; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Transaction; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Action\StatusAction; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Exception\PaymentRefundFailedException; @@ -148,13 +149,12 @@ function it_throws_the_payment_refund_failed_exception_if_there_is_different_sta SyliusPaymentInterface $payment, RefundInterface $request, TokenInterface $token, - RefundResponse $refundResponse, + ErrorResponse $errorResponse, ): void { $request->getModel()->willReturn($payment); $request->getToken()->willReturn($token); - $saferpayClient->refund($payment)->willReturn($refundResponse); - $refundResponse->isSuccessful()->willReturn(false); + $saferpayClient->refund($payment)->willReturn($errorResponse); $this ->shouldThrow(PaymentRefundFailedException::class) diff --git a/src/Client/SaferpayClient.php b/src/Client/SaferpayClient.php index 9a8e9dbf..b4534161 100644 --- a/src/Client/SaferpayClient.php +++ b/src/Client/SaferpayClient.php @@ -135,7 +135,7 @@ public function capture(PaymentInterface $payment): CaptureResponse return $response; } - public function refund(PaymentInterface $payment): RefundResponse + public function refund(PaymentInterface $payment): RefundResponse|ErrorResponse { $payload = $this->saferpayClientBodyFactory->createForRefund($payment); $result = $this->request( diff --git a/src/Payment/EventDispatcher/PaymentEventDispatcher.php b/src/Payment/EventDispatcher/PaymentEventDispatcher.php index d8223f2b..1857294d 100644 --- a/src/Payment/EventDispatcher/PaymentEventDispatcher.php +++ b/src/Payment/EventDispatcher/PaymentEventDispatcher.php @@ -114,7 +114,7 @@ public function dispatchRefundFailedEvent( PaymentInterface $payment, string $url, array $request, - RefundResponse $response, + ErrorResponse $response, ): void { /** @var int $paymentId */ $paymentId = $payment->getId(); diff --git a/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php b/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php index 01f10157..bca2a2e2 100644 --- a/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php +++ b/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php @@ -66,6 +66,6 @@ public function dispatchRefundFailedEvent( PaymentInterface $payment, string $url, array $request, - RefundResponse $response, + ErrorResponse $response, ): void; } diff --git a/src/Payum/Action/RefundAction.php b/src/Payum/Action/RefundAction.php index 452853e9..3a6c3a50 100644 --- a/src/Payum/Action/RefundAction.php +++ b/src/Payum/Action/RefundAction.php @@ -6,6 +6,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\SaferpayClientInterface; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Exception\PaymentRefundFailedException; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Request\RefundInterface; @@ -29,7 +30,7 @@ public function execute($request): void $payment = $request->getModel(); $response = $this->saferpayClient->refund($payment); - if (!$response->isSuccessful()) { + if ($response instanceof ErrorResponse) { throw new PaymentRefundFailedException(); } From e5792dbed64ef85b4d7c3267841b854b08b2c921 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 09:06:52 +0200 Subject: [PATCH 05/11] Introduce common ResponseInterface --- .../PaymentEventDispatcherSpec.php | 1 - src/Client/SaferpayClient.php | 5 ++-- src/Client/SaferpayClientInterface.php | 8 ++--- src/Client/ValueObject/AssertResponse.php | 13 ++------- src/Client/ValueObject/AuthorizeResponse.php | 2 +- src/Client/ValueObject/CaptureResponse.php | 2 +- src/Client/ValueObject/ErrorResponse.php | 9 ++++-- src/Client/ValueObject/RefundResponse.php | 2 +- src/Client/ValueObject/ResponseInterface.php | 18 ++++++++++++ src/Payum/Action/AssertAction.php | 5 ++-- src/Payum/Action/RefundAction.php | 1 + .../Client/ValueObject/AssertResponseTest.php | 29 ++----------------- 12 files changed, 42 insertions(+), 53 deletions(-) create mode 100644 src/Client/ValueObject/ResponseInterface.php diff --git a/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php b/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php index 41f7e2d7..c3a68392 100644 --- a/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php +++ b/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php @@ -341,7 +341,6 @@ private function getExampleAssertResponse(): array ], 'InPsd2Scope' => null, ], - 'Error' => null, ]; } diff --git a/src/Client/SaferpayClient.php b/src/Client/SaferpayClient.php index b4534161..931e230b 100644 --- a/src/Client/SaferpayClient.php +++ b/src/Client/SaferpayClient.php @@ -9,6 +9,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ResponseInterface; use CommerceWeavers\SyliusSaferpayPlugin\Payment\EventDispatcher\PaymentEventDispatcherInterface; use CommerceWeavers\SyliusSaferpayPlugin\Resolver\SaferpayApiBaseUrlResolverInterface; use Payum\Core\Model\GatewayConfigInterface; @@ -71,7 +72,7 @@ public function authorize(PaymentInterface $payment, TokenInterface $token): Aut return $response; } - public function assert(PaymentInterface $payment): AssertResponse|ErrorResponse + public function assert(PaymentInterface $payment): ResponseInterface { $payload = $this->saferpayClientBodyFactory->createForAssert($payment); $result = $this->request( @@ -135,7 +136,7 @@ public function capture(PaymentInterface $payment): CaptureResponse return $response; } - public function refund(PaymentInterface $payment): RefundResponse|ErrorResponse + public function refund(PaymentInterface $payment): ResponseInterface { $payload = $this->saferpayClientBodyFactory->createForRefund($payment); $result = $this->request( diff --git a/src/Client/SaferpayClientInterface.php b/src/Client/SaferpayClientInterface.php index 799090d6..fd350dbb 100644 --- a/src/Client/SaferpayClientInterface.php +++ b/src/Client/SaferpayClientInterface.php @@ -4,11 +4,9 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Client; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AssertResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ResponseInterface; use Payum\Core\Model\GatewayConfigInterface; use Payum\Core\Security\TokenInterface; use Sylius\Component\Core\Model\PaymentInterface; @@ -17,11 +15,11 @@ interface SaferpayClientInterface { public function authorize(PaymentInterface $payment, TokenInterface $token): AuthorizeResponse; - public function assert(PaymentInterface $payment): AssertResponse|ErrorResponse; + public function assert(PaymentInterface $payment): ResponseInterface; public function capture(PaymentInterface $payment): CaptureResponse; - public function refund(PaymentInterface $payment): RefundResponse|ErrorResponse; + public function refund(PaymentInterface $payment): ResponseInterface; public function getTerminal(GatewayConfigInterface $gatewayConfig): array; } diff --git a/src/Client/ValueObject/AssertResponse.php b/src/Client/ValueObject/AssertResponse.php index 1ef63abd..713b35f1 100644 --- a/src/Client/ValueObject/AssertResponse.php +++ b/src/Client/ValueObject/AssertResponse.php @@ -4,14 +4,13 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Error; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Liability; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Payer; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\PaymentMeans; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Transaction; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; -class AssertResponse +class AssertResponse implements ResponseInterface { private function __construct( private int $statusCode, @@ -20,7 +19,6 @@ private function __construct( private ?PaymentMeans $paymentMeans, private ?Payer $payer, private ?Liability $liability, - private ?Error $error, ) { } @@ -54,14 +52,9 @@ public function getLiability(): ?Liability return $this->liability; } - public function getError(): ?Error - { - return $this->error; - } - public function isSuccessful(): bool { - return $this->statusCode >= 200 && $this->statusCode < 300 && null === $this->error; + return $this->statusCode >= 200 && $this->statusCode < 300; } public function toArray(): array @@ -73,7 +66,6 @@ public function toArray(): array 'PaymentMeans' => $this->getPaymentMeans()?->toArray(), 'Payer' => $this->getPayer()?->toArray(), 'Liability' => $this->getLiability()?->toArray(), - 'Error' => $this->getError()?->toArray(), ]; } @@ -86,7 +78,6 @@ public static function fromArray(array $data): self isset($data['PaymentMeans']) ? PaymentMeans::fromArray($data['PaymentMeans']) : null, isset($data['Payer']) ? Payer::fromArray($data['Payer']) : null, isset($data['Liability']) ? Liability::fromArray($data['Liability']) : null, - isset($data['ErrorName']) ? Error::fromArray($data) : null, ); } } diff --git a/src/Client/ValueObject/AuthorizeResponse.php b/src/Client/ValueObject/AuthorizeResponse.php index c35bfeb7..20619688 100644 --- a/src/Client/ValueObject/AuthorizeResponse.php +++ b/src/Client/ValueObject/AuthorizeResponse.php @@ -7,7 +7,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Error; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; -class AuthorizeResponse +class AuthorizeResponse implements ResponseInterface { private function __construct( private int $statusCode, diff --git a/src/Client/ValueObject/CaptureResponse.php b/src/Client/ValueObject/CaptureResponse.php index 01eeab9f..e3ca39fc 100644 --- a/src/Client/ValueObject/CaptureResponse.php +++ b/src/Client/ValueObject/CaptureResponse.php @@ -7,7 +7,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Error; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; -class CaptureResponse +class CaptureResponse implements ResponseInterface { private function __construct( private int $statusCode, diff --git a/src/Client/ValueObject/ErrorResponse.php b/src/Client/ValueObject/ErrorResponse.php index b07c7c08..d5ebcf77 100644 --- a/src/Client/ValueObject/ErrorResponse.php +++ b/src/Client/ValueObject/ErrorResponse.php @@ -6,7 +6,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; -class ErrorResponse +class ErrorResponse implements ResponseInterface { private function __construct( private int $statusCode, @@ -37,7 +37,7 @@ public static function forAssert(array $data): self 'Assert', $data['TransactionId'], $data['OrderId'], - $data['PayerMessage'], + $data['PayerMessage'] ?? null, ); } @@ -162,4 +162,9 @@ public function toArray(): array 'ProcessorMessage' => $this->getProcessorMessage(), ]; } + + public function isSuccessful(): bool + { + return false; + } } diff --git a/src/Client/ValueObject/RefundResponse.php b/src/Client/ValueObject/RefundResponse.php index 88bbaff7..f8333c1d 100644 --- a/src/Client/ValueObject/RefundResponse.php +++ b/src/Client/ValueObject/RefundResponse.php @@ -9,7 +9,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Transaction; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; -class RefundResponse +class RefundResponse implements ResponseInterface { private function __construct( private int $statusCode, diff --git a/src/Client/ValueObject/ResponseInterface.php b/src/Client/ValueObject/ResponseInterface.php new file mode 100644 index 00000000..74306af5 --- /dev/null +++ b/src/Client/ValueObject/ResponseInterface.php @@ -0,0 +1,18 @@ +getModel(); + /** @var AssertResponse|ErrorResponse $response */ $response = $this->saferpayClient->assert($payment); - if ($response->getStatusCode() !== Response::HTTP_OK) { + if ($response instanceof ErrorResponse) { $this->handleFailedResponse($payment, $response); return; diff --git a/src/Payum/Action/RefundAction.php b/src/Payum/Action/RefundAction.php index 3a6c3a50..de4e6b4b 100644 --- a/src/Payum/Action/RefundAction.php +++ b/src/Payum/Action/RefundAction.php @@ -29,6 +29,7 @@ public function execute($request): void /** @var PaymentInterface $payment */ $payment = $request->getModel(); + /** @var RefundResponse|ErrorResponse $response */ $response = $this->saferpayClient->refund($payment); if ($response instanceof ErrorResponse) { throw new PaymentRefundFailedException(); diff --git a/tests/Unit/Client/ValueObject/AssertResponseTest.php b/tests/Unit/Client/ValueObject/AssertResponseTest.php index 6e7622bc..b876e00b 100644 --- a/tests/Unit/Client/ValueObject/AssertResponseTest.php +++ b/tests/Unit/Client/ValueObject/AssertResponseTest.php @@ -87,6 +87,8 @@ public function it_creates_assert_response_vo_from_array(): void $this->assertTrue($liability->getThreeDs()->getAuthenticated()); $this->assertTrue($liability->getThreeDs()->getLiabilityShift()); $this->assertEquals('ARkvCgk5Y1t/BDFFXkUPGX9DUgs=', $liability->getThreeDs()->getXid()); + + $this->assertTrue($response->isSuccessful()); } /** @test */ @@ -155,33 +157,8 @@ public function it_creates_assert_paypal_response_vo_from_array(): void $this->assertFalse($liability->getLiabilityShift()); $this->assertEquals('MERCHANT', $liability->getLiableEntity()); $this->assertEquals('UNKNOWN', $liability->getInPsd2Scope()); - } - - /** @test */ - public function it_creates_assert_response_vo_with_an_error_from_array(): void - { - $response = AssertResponse::fromArray([ - 'StatusCode' => 402, - 'ResponseHeader' => [ - 'SpecVersion' => '1.33', - 'RequestId' => 'b27de121-ffa0-4f1d-b7aa-b48109a88486', - ], - 'Behavior' => 'DO_NOT_RETRY', - 'ErrorName' => '3DS_AUTHENTICATION_FAILED', - 'ErrorMessage' => '3D-Secure authentication failed', - 'TransactionId' => '723n4MAjMdhjSAhAKEUdA8jtl9jb', - 'PayerMessage' => 'Card holder information -> Failed', - 'OrderId' => '000000042', - ]); - $this->assertResponseHeader($response->getResponseHeader()); - $this->assertEquals(402, $response->getStatusCode()); - $this->assertEquals('DO_NOT_RETRY', $response->getError()->getBehavior()); - $this->assertEquals('3DS_AUTHENTICATION_FAILED', $response->getError()->getName()); - $this->assertEquals('3D-Secure authentication failed', $response->getError()->getMessage()); - $this->assertEquals('723n4MAjMdhjSAhAKEUdA8jtl9jb', $response->getError()->getTransactionId()); - $this->assertEquals('Card holder information -> Failed', $response->getError()->getPayerMessage()); - $this->assertEquals('000000042', $response->getError()->getOrderId()); + $this->assertTrue($response->isSuccessful()); } private function assertResponseHeader(ResponseHeader $responseHeader): void From 6dc557e820ce3db263a21a76f48e6b660c7f16b8 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 10:27:03 +0200 Subject: [PATCH 06/11] Use ErrorResponse in Authorize --- spec/Client/SaferpayClientSpec.php | 10 +--- .../PaymentEventDispatcherSpec.php | 48 +++++++++++++++++++ spec/Payum/Action/AuthorizeActionSpec.php | 7 +-- src/Client/SaferpayClient.php | 9 ++-- src/Client/SaferpayClientInterface.php | 2 +- src/Client/ValueObject/AuthorizeResponse.php | 9 ---- src/Client/ValueObject/ErrorResponse.php | 5 +- .../PaymentEventDispatcher.php | 2 +- .../PaymentEventDispatcherInterface.php | 2 +- src/Payum/Action/AuthorizeAction.php | 3 +- .../ValueObject/AuthorizeResponseTest.php | 24 ---------- 11 files changed, 69 insertions(+), 52 deletions(-) diff --git a/spec/Client/SaferpayClientSpec.php b/spec/Client/SaferpayClientSpec.php index 3f9d0dde..235fc12a 100644 --- a/spec/Client/SaferpayClientSpec.php +++ b/spec/Client/SaferpayClientSpec.php @@ -46,7 +46,6 @@ function it_performs_authorize_request( PaymentInterface $payment, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig, - OrderInterface $order, TokenInterface $token, ResponseInterface $response, ): void { @@ -103,7 +102,6 @@ function it_dispatches_a_failed_event_once_the_authorization_fails( PaymentInterface $payment, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig, - OrderInterface $order, TokenInterface $token, ResponseInterface $response, ): void { @@ -119,10 +117,6 @@ function it_dispatches_a_failed_event_once_the_authorization_fails( 'password' => 'PASSWORD', 'sandbox' => true, ]); - $payment->getOrder()->willReturn($order); - $payment->getAmount()->willReturn(10000); - $payment->getCurrencyCode()->willReturn('CHF'); - $order->getNumber()->willReturn('000000001'); $token->getAfterUrl()->willReturn('https://example.com/after'); @@ -150,12 +144,12 @@ function it_dispatches_a_failed_event_once_the_authorization_fails( $payment, 'Payment/v1/PaymentPage/Initialize', $payload, - AuthorizeResponse::fromArray(array_merge(['StatusCode' => 402], json_decode($this->getExampleAuthorizeErrorResponse(), true))) + ErrorResponse::forAuthorize(array_merge(['StatusCode' => 402], json_decode($this->getExampleAuthorizeErrorResponse(), true))) ) ->shouldBeCalled() ; - $this->authorize($payment, $token)->shouldBeAnInstanceOf(AuthorizeResponse::class); + $this->authorize($payment, $token)->shouldBeAnInstanceOf(ErrorResponse::class); } function it_performs_assert_request( diff --git a/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php b/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php index c3a68392..28704483 100644 --- a/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php +++ b/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php @@ -10,6 +10,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAssertionFailed; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAssertionSucceeded; +use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAuthorizationFailed; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAuthorizationSucceeded; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentCaptureSucceeded; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; @@ -64,6 +65,36 @@ function it_dispatches_payment_authorization_succeeded_event( ); } + function it_dispatches_payment_authorization_failed_event( + MessageBusInterface $eventBus, + PaymentInterface $payment, + ): void { + $payload = $this->getExampleAuthorizePayload(); + $response = $this->getExampleAuthorizeErrorResponse(); + + $payment->getId()->willReturn(1); + + $eventBus + ->dispatch(Argument::that(function (PaymentAuthorizationFailed $event) use ($payload, $response) { + return + $event->getPaymentId() === 1 + && $event->getRequestUrl() === 'Payment/v1/PaymentPage/Initialize' + && $event->getRequestBody() === $payload + && $event->getResponseData()['Name'] === 'CANNOT_AUTHORIZED' + ; + })) + ->shouldBeCalled() + ->willReturn(new Envelope(new \stdClass())) + ; + + $this->dispatchAuthorizationFailedEvent( + $payment, + 'Payment/v1/PaymentPage/Initialize', + $payload, + ErrorResponse::forAuthorize($response), + ); + } + function it_dispatches_payment_assertion_succeeded_event( MessageBusInterface $eventBus, PaymentInterface $payment, @@ -276,6 +307,23 @@ private function getExampleAuthorizeResponse(): array ]; } + private function getExampleAuthorizeErrorResponse(): array + { + return [ + 'StatusCode' => 402, + 'ResponseHeader' => [ + 'SpecVersion' => '1.33', + 'RequestId' => 'abc123', + ], + 'Behavior' => 'DO_NOT_RETRY', + 'ErrorName' => 'CANNOT_AUTHORIZED', + 'ErrorMessage' => 'Cannot authorized payment', + 'TransactionId' => 'Q3hd5IbzlnKpvAICv2QdA72QlA1b', + 'PayerMessage' => 'Card authorization -> Failed', + 'OrderId' => '000000042', + ]; + } + public function getExampleAssertPayload(): array { return [ diff --git a/spec/Payum/Action/AuthorizeActionSpec.php b/spec/Payum/Action/AuthorizeActionSpec.php index dcf8c369..61fba2a9 100644 --- a/spec/Payum/Action/AuthorizeActionSpec.php +++ b/spec/Payum/Action/AuthorizeActionSpec.php @@ -6,6 +6,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\SaferpayClientInterface; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Action\StatusAction; use Payum\Core\Exception\RequestNotSupportedException; @@ -118,15 +119,15 @@ function it_marks_the_payment_as_failed_if_there_is_different_status_code_than_o SyliusPaymentInterface $payment, Authorize $request, TokenInterface $token, - AuthorizeResponse $authorizeResponse, + ErrorResponse $errorResponse, ): void { $request->getModel()->willReturn($payment); $request->getToken()->willReturn($token); $payment->getState()->willReturn(SyliusPaymentInterface::STATE_NEW); - $saferpayClient->authorize($payment, $token)->willReturn($authorizeResponse); - $authorizeResponse->getStatusCode()->willReturn(402); + $saferpayClient->authorize($payment, $token)->willReturn($errorResponse); + $errorResponse->getStatusCode()->willReturn(402); $payment->setDetails(['status' => StatusAction::STATUS_FAILED])->shouldBeCalled(); diff --git a/src/Client/SaferpayClient.php b/src/Client/SaferpayClient.php index 931e230b..81ccab77 100644 --- a/src/Client/SaferpayClient.php +++ b/src/Client/SaferpayClient.php @@ -41,7 +41,7 @@ public function __construct( ) { } - public function authorize(PaymentInterface $payment, TokenInterface $token): AuthorizeResponse + public function authorize(PaymentInterface $payment, TokenInterface $token): ResponseInterface { $payload = $this->saferpayClientBodyFactory->createForAuthorize($payment, $token); $result = $this->request( @@ -51,9 +51,10 @@ public function authorize(PaymentInterface $payment, TokenInterface $token): Aut $this->provideGatewayConfig($payment), ); - $response = AuthorizeResponse::fromArray($result); - if ($response->isSuccessful()) { + if (200 === $result['StatusCode']) { + $response = AuthorizeResponse::fromArray($result); + $this->paymentEventDispatcher->dispatchAuthorizationSucceededEvent( $payment, self::PAYMENT_INITIALIZE_URL, @@ -61,6 +62,8 @@ public function authorize(PaymentInterface $payment, TokenInterface $token): Aut $response, ); } else { + $response = ErrorResponse::forAuthorize($result); + $this->paymentEventDispatcher->dispatchAuthorizationFailedEvent( $payment, self::PAYMENT_INITIALIZE_URL, diff --git a/src/Client/SaferpayClientInterface.php b/src/Client/SaferpayClientInterface.php index fd350dbb..52e16ea9 100644 --- a/src/Client/SaferpayClientInterface.php +++ b/src/Client/SaferpayClientInterface.php @@ -13,7 +13,7 @@ interface SaferpayClientInterface { - public function authorize(PaymentInterface $payment, TokenInterface $token): AuthorizeResponse; + public function authorize(PaymentInterface $payment, TokenInterface $token): ResponseInterface; public function assert(PaymentInterface $payment): ResponseInterface; diff --git a/src/Client/ValueObject/AuthorizeResponse.php b/src/Client/ValueObject/AuthorizeResponse.php index 20619688..ddf5be86 100644 --- a/src/Client/ValueObject/AuthorizeResponse.php +++ b/src/Client/ValueObject/AuthorizeResponse.php @@ -15,7 +15,6 @@ private function __construct( private ?string $token, private ?string $expiration, private ?string $redirectUrl, - private ?Error $error, ) { } @@ -44,11 +43,6 @@ public function getRedirectUrl(): ?string return $this->redirectUrl; } - public function getError(): ?Error - { - return $this->error; - } - public function isSuccessful(): bool { return 200 <= $this->statusCode && $this->statusCode <= 299; @@ -62,8 +56,6 @@ public function toArray(): array 'Token' => $this->getToken(), 'Expiration' => $this->getExpiration(), 'RedirectUrl' => $this->getRedirectUrl(), - 'ErrorName' => $this->getError()?->getName(), - 'ErrorMessage' => $this->getError()?->getMessage(), ]; } @@ -75,7 +67,6 @@ public static function fromArray(array $data): self $data['Token'] ?? null, $data['Expiration'] ?? null, $data['RedirectUrl'] ?? null, - isset($data['ErrorName']) ? Error::fromArray($data) : null, ); } } diff --git a/src/Client/ValueObject/ErrorResponse.php b/src/Client/ValueObject/ErrorResponse.php index d5ebcf77..3c38c5c6 100644 --- a/src/Client/ValueObject/ErrorResponse.php +++ b/src/Client/ValueObject/ErrorResponse.php @@ -61,9 +61,12 @@ public static function forAuthorize(array $data): self ResponseHeader::fromArray($data['ResponseHeader']), $data['ErrorName'], $data['ErrorMessage'], - $data['ErrorDetail'], + $data['ErrorDetail'] ?? [], $data['Behavior'], 'Authorize', + $data['TransactionId'] ?? null, + $data['OrderId'] ?? null, + $data['PayerMessage'] ?? null, ); } diff --git a/src/Payment/EventDispatcher/PaymentEventDispatcher.php b/src/Payment/EventDispatcher/PaymentEventDispatcher.php index 1857294d..230afa5a 100644 --- a/src/Payment/EventDispatcher/PaymentEventDispatcher.php +++ b/src/Payment/EventDispatcher/PaymentEventDispatcher.php @@ -42,7 +42,7 @@ public function dispatchAuthorizationFailedEvent( PaymentInterface $payment, string $url, array $request, - AuthorizeResponse $response, + ErrorResponse $response, ): void { /** @var int $paymentId */ $paymentId = $payment->getId(); diff --git a/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php b/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php index bca2a2e2..96d8adda 100644 --- a/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php +++ b/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php @@ -24,7 +24,7 @@ public function dispatchAuthorizationFailedEvent( PaymentInterface $payment, string $url, array $request, - AuthorizeResponse $response, + ErrorResponse $response, ): void; public function dispatchAssertionSucceededEvent( diff --git a/src/Payum/Action/AuthorizeAction.php b/src/Payum/Action/AuthorizeAction.php index 81699a81..d6b58ba7 100644 --- a/src/Payum/Action/AuthorizeAction.php +++ b/src/Payum/Action/AuthorizeAction.php @@ -5,6 +5,7 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Payum\Action; use CommerceWeavers\SyliusSaferpayPlugin\Client\SaferpayClientInterface; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use Payum\Core\Action\ActionInterface; use Payum\Core\Exception\RequestNotSupportedException; use Payum\Core\Request\Authorize; @@ -35,7 +36,7 @@ public function execute(mixed $request): void $response = $this->saferpayClient->authorize($payment, $token); - if ($response->getStatusCode() !== Response::HTTP_OK) { + if ($response instanceof ErrorResponse) { $payment->setDetails([ 'status' => StatusAction::STATUS_FAILED, ]); diff --git a/tests/Unit/Client/ValueObject/AuthorizeResponseTest.php b/tests/Unit/Client/ValueObject/AuthorizeResponseTest.php index 82c5be09..843c7e56 100644 --- a/tests/Unit/Client/ValueObject/AuthorizeResponseTest.php +++ b/tests/Unit/Client/ValueObject/AuthorizeResponseTest.php @@ -31,30 +31,6 @@ public function it_creates_authorize_response_vo_from_array(): void $this->assertEquals('https://www.saferpay.com/api/redirect', $response->getRedirectUrl()); } - /** @test */ - public function it_creates_authorize_response_vo_with_an_error_from_array(): void - { - $response = AuthorizeResponse::fromArray([ - "ResponseHeader" => [ - "SpecVersion" => "1.33", - "RequestId" => "b27de121-ffa0-4f1d-b7aa-b48109a88486", - ], - "Behavior" => "DO_NOT_RETRY", - "ErrorName" => "VALIDATION_FAILED", - "ErrorMessage" => "Request validation failed", - "ErrorDetail" => [ - "TerminalId: The field TerminalId is invalid.", - ], - ]); - - $this->assertResponseHeader($response->getResponseHeader()); - $this->assertEquals(400, $response->getStatusCode()); - $this->assertEquals('DO_NOT_RETRY', $response->getError()->getBehavior()); - $this->assertEquals('VALIDATION_FAILED', $response->getError()->getName()); - $this->assertEquals('Request validation failed', $response->getError()->getMessage()); - $this->assertEquals(['TerminalId: The field TerminalId is invalid.'], $response->getError()->getDetail()); - } - private function assertResponseHeader(ResponseHeader $responseHeader): void { $this->assertEquals('1.33', $responseHeader->getSpecVersion()); From 1d5748ee263dff7014a05190ddce9813d0dc953c Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 10:45:47 +0200 Subject: [PATCH 07/11] Use ErrorResponse in capture --- spec/Client/SaferpayClientSpec.php | 4 +- .../PaymentEventDispatcherSpec.php | 53 +++++++++++++++++-- spec/Payum/Action/CaptureActionSpec.php | 12 ++--- src/Client/SaferpayClient.php | 9 ++-- src/Client/SaferpayClientInterface.php | 4 +- src/Client/ValueObject/AuthorizeResponse.php | 1 - src/Client/ValueObject/CaptureResponse.php | 9 ---- src/Client/ValueObject/RefundResponse.php | 9 ---- .../PaymentEventDispatcher.php | 2 +- .../PaymentEventDispatcherInterface.php | 2 +- src/Payum/Action/AuthorizeAction.php | 1 - src/Payum/Action/CaptureAction.php | 11 ++++ 12 files changed, 76 insertions(+), 41 deletions(-) diff --git a/spec/Client/SaferpayClientSpec.php b/spec/Client/SaferpayClientSpec.php index 235fc12a..9b7242e5 100644 --- a/spec/Client/SaferpayClientSpec.php +++ b/spec/Client/SaferpayClientSpec.php @@ -384,12 +384,12 @@ function it_dispatches_a_failed_event_once_the_capture_fails( $payment, 'Payment/v1/Transaction/Capture', $payload, - CaptureResponse::fromArray(array_merge(['StatusCode' => 402], json_decode($this->getExampleCaptureErrorResponse(), true))) + ErrorResponse::forCapture(array_merge(['StatusCode' => 402], json_decode($this->getExampleCaptureErrorResponse(), true))) ) ->shouldBeCalled() ; - $this->capture($payment)->shouldBeAnInstanceOf(CaptureResponse::class); + $this->capture($payment)->shouldBeAnInstanceOf(ErrorResponse::class); } function it_performs_refund_request( diff --git a/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php b/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php index 28704483..eff29da2 100644 --- a/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php +++ b/spec/Payment/EventDispatcher/PaymentEventDispatcherSpec.php @@ -12,6 +12,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAssertionSucceeded; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAuthorizationFailed; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentAuthorizationSucceeded; +use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentCaptureFailed; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentCaptureSucceeded; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\RefundResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payment\Event\PaymentRefundFailed; @@ -196,6 +197,37 @@ function it_dispatches_payment_capture_succeeded_event( ); } + function it_dispatches_payment_capture_failed_event( + MessageBusInterface $eventBus, + PaymentInterface $payment, + ): void { + $payload = $this->getExampleCapturePayload(); + $response = $this->getExampleCaptureErrorResponse(); + + $payment->getId()->willReturn(1); + + $eventBus + ->dispatch(Argument::that(function (PaymentCaptureFailed $event) use ($payload, $response) { + return + $event->getPaymentId() === 1 + && $event->getRequestUrl() === 'Payment/v1/Transaction/Capture' + && $event->getRequestBody() === $payload + && $event->getResponseData()['Name'] === 'CAPTURE_FAILED' + && $event->getResponseData()['FailedOperation'] === 'Capture' + ; + })) + ->shouldBeCalled() + ->willReturn(new Envelope(new \stdClass())) + ; + + $this->dispatchCaptureFailedEvent( + $payment, + 'Payment/v1/Transaction/Capture', + $payload, + ErrorResponse::forCapture($response) + ); + } + function it_dispatches_payment_refund_succeeded_event( MessageBusInterface $eventBus, PaymentInterface $payment, @@ -302,8 +334,6 @@ private function getExampleAuthorizeResponse(): array 'Token' => '234uhfh78234hlasdfh8234e1234', 'Expiration' => '2015-01-30T12:45:22.258+01:00', 'RedirectUrl' => 'https://www.saferpay.com/vt2/api/...', - 'ErrorName' => null, - 'ErrorMessage' => null, ]; } @@ -435,7 +465,23 @@ private function getExampleCaptureResponse(): array 'CaptureId' => '723n4MAjMdhjSAhAKEUdA8jtl9jb', 'Status' => 'CAPTURED', 'Date' => '2015-01-30T12:45:22.258+01:00', - 'Error' => null, + ]; + } + + private function getExampleCaptureErrorResponse(): array + { + return [ + 'StatusCode' => 402, + 'ResponseHeader' => [ + 'SpecVersion' => '1.33', + 'RequestId' => 'abc123', + ], + 'Behavior' => 'DO_NOT_RETRY', + 'ErrorName' => 'CAPTURE_FAILED', + 'ErrorMessage' => 'Payment capture failed', + 'TransactionId' => 'Q3hd5IbzlnKpvAICv2QdA72QlA1b', + 'PayerMessage' => 'Payment -> Failed', + 'OrderId' => '000000042', ]; } @@ -503,7 +549,6 @@ private function getExampleRefundResponse(): array 'BankAccount' => null, 'PayPal' => null, ], - 'Error' => null, ]; } diff --git a/spec/Payum/Action/CaptureActionSpec.php b/spec/Payum/Action/CaptureActionSpec.php index d37d4ba2..67404ceb 100644 --- a/spec/Payum/Action/CaptureActionSpec.php +++ b/spec/Payum/Action/CaptureActionSpec.php @@ -6,6 +6,7 @@ use CommerceWeavers\SyliusSaferpayPlugin\Client\SaferpayClientInterface; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Action\StatusAction; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Status\StatusCheckerInterface; use Payum\Core\Exception\RequestNotSupportedException; @@ -92,18 +93,17 @@ function it_marks_the_payment_as_failed_if_there_is_different_status_code_than_o SaferpayClientInterface $saferpayClient, SyliusPaymentInterface $payment, StatusCheckerInterface $statusChecker, - CaptureResponse $captureResponse, + ErrorResponse $errorResponse, ): void { $statusChecker->isCaptured($payment)->willReturn(false); - $saferpayClient->capture($payment)->willReturn($captureResponse); - $captureResponse->getStatus()->willReturn(StatusAction::STATUS_CAPTURED); - $captureResponse->getCaptureId()->willReturn('0d7OYrAInYCWSASdzSh3bbr4jrSb_c'); - $captureResponse->getStatusCode()->willReturn(402); + $saferpayClient->capture($payment)->willReturn($errorResponse); + $errorResponse->getTransactionId()->willReturn('TRANSACTION_ID'); + $errorResponse->getStatusCode()->willReturn(402); $payment->getDetails()->willReturn([]); $payment - ->setDetails(['status' => StatusAction::STATUS_FAILED, 'capture_id' => '0d7OYrAInYCWSASdzSh3bbr4jrSb_c']) + ->setDetails(['status' => StatusAction::STATUS_FAILED, 'transaction_id' => 'TRANSACTION_ID']) ->shouldBeCalled() ; diff --git a/src/Client/SaferpayClient.php b/src/Client/SaferpayClient.php index 81ccab77..7574fe08 100644 --- a/src/Client/SaferpayClient.php +++ b/src/Client/SaferpayClient.php @@ -51,7 +51,6 @@ public function authorize(PaymentInterface $payment, TokenInterface $token): Res $this->provideGatewayConfig($payment), ); - if (200 === $result['StatusCode']) { $response = AuthorizeResponse::fromArray($result); @@ -108,7 +107,7 @@ public function assert(PaymentInterface $payment): ResponseInterface return $response; } - public function capture(PaymentInterface $payment): CaptureResponse + public function capture(PaymentInterface $payment): ResponseInterface { $payload = $this->saferpayClientBodyFactory->createForCapture($payment); $result = $this->request( @@ -118,9 +117,9 @@ public function capture(PaymentInterface $payment): CaptureResponse $this->provideGatewayConfig($payment), ); - $response = CaptureResponse::fromArray($result); + if (200 === $result['StatusCode']) { + $response = CaptureResponse::fromArray($result); - if ($response->isSuccessful()) { $this->paymentEventDispatcher->dispatchCaptureSucceededEvent( $payment, self::TRANSACTION_CAPTURE_URL, @@ -128,6 +127,8 @@ public function capture(PaymentInterface $payment): CaptureResponse $response, ); } else { + $response = ErrorResponse::forCapture($result); + $this->paymentEventDispatcher->dispatchCaptureFailedEvent( $payment, self::TRANSACTION_CAPTURE_URL, diff --git a/src/Client/SaferpayClientInterface.php b/src/Client/SaferpayClientInterface.php index 52e16ea9..694db054 100644 --- a/src/Client/SaferpayClientInterface.php +++ b/src/Client/SaferpayClientInterface.php @@ -4,8 +4,6 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Client; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ResponseInterface; use Payum\Core\Model\GatewayConfigInterface; use Payum\Core\Security\TokenInterface; @@ -17,7 +15,7 @@ public function authorize(PaymentInterface $payment, TokenInterface $token): Res public function assert(PaymentInterface $payment): ResponseInterface; - public function capture(PaymentInterface $payment): CaptureResponse; + public function capture(PaymentInterface $payment): ResponseInterface; public function refund(PaymentInterface $payment): ResponseInterface; diff --git a/src/Client/ValueObject/AuthorizeResponse.php b/src/Client/ValueObject/AuthorizeResponse.php index ddf5be86..3be0116b 100644 --- a/src/Client/ValueObject/AuthorizeResponse.php +++ b/src/Client/ValueObject/AuthorizeResponse.php @@ -4,7 +4,6 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Error; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; class AuthorizeResponse implements ResponseInterface diff --git a/src/Client/ValueObject/CaptureResponse.php b/src/Client/ValueObject/CaptureResponse.php index e3ca39fc..e62ffef6 100644 --- a/src/Client/ValueObject/CaptureResponse.php +++ b/src/Client/ValueObject/CaptureResponse.php @@ -4,7 +4,6 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Error; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; class CaptureResponse implements ResponseInterface @@ -15,7 +14,6 @@ private function __construct( private ?string $captureId, private ?string $status, private ?string $date, - private ?Error $error, ) { } @@ -44,11 +42,6 @@ public function getDate(): ?string return $this->date; } - public function getError(): ?Error - { - return $this->error; - } - public function isSuccessful(): bool { return 200 <= $this->statusCode && $this->statusCode <= 299; @@ -62,7 +55,6 @@ public function toArray(): array 'CaptureId' => $this->getCaptureId(), 'Status' => $this->getStatus(), 'Date' => $this->getDate(), - 'Error' => $this->getError(), ]; } @@ -74,7 +66,6 @@ public static function fromArray(array $data): self $data['CaptureId'] ?? null, $data['Status'] ?? null, $data['Date'] ?? null, - isset($data['ErrorName']) ? Error::fromArray($data) : null, ); } } diff --git a/src/Client/ValueObject/RefundResponse.php b/src/Client/ValueObject/RefundResponse.php index f8333c1d..5460a10b 100644 --- a/src/Client/ValueObject/RefundResponse.php +++ b/src/Client/ValueObject/RefundResponse.php @@ -4,7 +4,6 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject; -use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Error; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\PaymentMeans; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Body\Transaction; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\Header\ResponseHeader; @@ -16,7 +15,6 @@ private function __construct( private ResponseHeader $responseHeader, private ?Transaction $transaction, private ?PaymentMeans $paymentMeans, - private ?Error $error, ) { } @@ -40,11 +38,6 @@ public function getPaymentMeans(): ?PaymentMeans return $this->paymentMeans; } - public function getError(): ?Error - { - return $this->error; - } - public function isSuccessful(): bool { return 200 <= $this->statusCode && $this->statusCode <= 299; @@ -57,7 +50,6 @@ public function toArray(): array 'ResponseHeader' => $this->getResponseHeader()->toArray(), 'Transaction' => $this->getTransaction()?->toArray(), 'PaymentMeans' => $this->getPaymentMeans()?->toArray(), - 'Error' => $this->getError()?->toArray(), ]; } @@ -68,7 +60,6 @@ public static function fromArray(array $data): self ResponseHeader::fromArray($data['ResponseHeader']), isset($data['Transaction']) ? Transaction::fromArray($data['Transaction']) : null, isset($data['PaymentMeans']) ? PaymentMeans::fromArray($data['PaymentMeans']) : null, - isset($data['ErrorName']) ? Error::fromArray($data) : null, ); } } diff --git a/src/Payment/EventDispatcher/PaymentEventDispatcher.php b/src/Payment/EventDispatcher/PaymentEventDispatcher.php index 230afa5a..654b1261 100644 --- a/src/Payment/EventDispatcher/PaymentEventDispatcher.php +++ b/src/Payment/EventDispatcher/PaymentEventDispatcher.php @@ -90,7 +90,7 @@ public function dispatchCaptureFailedEvent( PaymentInterface $payment, string $url, array $request, - CaptureResponse $response, + ErrorResponse $response, ): void { /** @var int $paymentId */ $paymentId = $payment->getId(); diff --git a/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php b/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php index 96d8adda..751d096b 100644 --- a/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php +++ b/src/Payment/EventDispatcher/PaymentEventDispatcherInterface.php @@ -52,7 +52,7 @@ public function dispatchCaptureFailedEvent( PaymentInterface $payment, string $url, array $request, - CaptureResponse $response, + ErrorResponse $response, ): void; public function dispatchRefundSucceededEvent( diff --git a/src/Payum/Action/AuthorizeAction.php b/src/Payum/Action/AuthorizeAction.php index d6b58ba7..abd033dc 100644 --- a/src/Payum/Action/AuthorizeAction.php +++ b/src/Payum/Action/AuthorizeAction.php @@ -10,7 +10,6 @@ use Payum\Core\Exception\RequestNotSupportedException; use Payum\Core\Request\Authorize; use Sylius\Component\Core\Model\PaymentInterface; -use Symfony\Component\HttpFoundation\Response; use Webmozart\Assert\Assert; final class AuthorizeAction implements ActionInterface diff --git a/src/Payum/Action/CaptureAction.php b/src/Payum/Action/CaptureAction.php index f0511d02..023138f0 100644 --- a/src/Payum/Action/CaptureAction.php +++ b/src/Payum/Action/CaptureAction.php @@ -5,6 +5,8 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Payum\Action; use CommerceWeavers\SyliusSaferpayPlugin\Client\SaferpayClientInterface; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\CaptureResponse; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use CommerceWeavers\SyliusSaferpayPlugin\Payum\Status\StatusCheckerInterface; use Payum\Core\Action\ActionInterface; use Payum\Core\Exception\RequestNotSupportedException; @@ -32,7 +34,16 @@ public function execute($request): void return; } + /** @var CaptureResponse $response */ $response = $this->saferpayClient->capture($payment); + if ($response instanceof ErrorResponse) { + $payment->setDetails([ + 'status' => StatusAction::STATUS_FAILED, + 'transaction_id' => $response->getTransactionId(), + ]); + + return; + } $paymentDetails = $payment->getDetails(); $isSuccessfulResponse = $response->getStatusCode() === Response::HTTP_OK; From bda12d3a2f01d80a2a75c77a57f88133a2a2235d Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 10:46:06 +0200 Subject: [PATCH 08/11] Remove unneeded Error VO --- spec/Payum/Action/RefundActionSpec.php | 5 +- src/Client/ValueObject/Body/Error.php | 104 ------------------ src/Payum/Action/AuthorizeAction.php | 2 + src/Payum/Action/CaptureAction.php | 2 +- src/Payum/Action/RefundAction.php | 3 +- .../ValueObject/CaptureResponseTest.php | 20 ---- .../Client/ValueObject/RefundResponseTest.php | 20 ---- 7 files changed, 7 insertions(+), 149 deletions(-) delete mode 100644 src/Client/ValueObject/Body/Error.php diff --git a/spec/Payum/Action/RefundActionSpec.php b/spec/Payum/Action/RefundActionSpec.php index 87219f5a..017cb9e4 100644 --- a/spec/Payum/Action/RefundActionSpec.php +++ b/spec/Payum/Action/RefundActionSpec.php @@ -169,7 +169,7 @@ function it_throws_the_payment_refund_failed_exception_if_there_is_different_sta TokenInterface $token, RefundResponse $refundResponse, Transaction $transaction, - CaptureResponse $captureResponse, + ErrorResponse $errorResponse, ): void { $request->getModel()->willReturn($payment); $request->getToken()->willReturn($token); @@ -189,8 +189,7 @@ function it_throws_the_payment_refund_failed_exception_if_there_is_different_sta ->shouldBeCalled() ; - $saferpayClient->capture($payment)->willReturn($captureResponse); - $captureResponse->isSuccessful()->willReturn(false); + $saferpayClient->capture($payment)->willReturn($errorResponse); $this ->shouldThrow(PaymentRefundFailedException::class) diff --git a/src/Client/ValueObject/Body/Error.php b/src/Client/ValueObject/Body/Error.php deleted file mode 100644 index 683f3e2b..00000000 --- a/src/Client/ValueObject/Body/Error.php +++ /dev/null @@ -1,104 +0,0 @@ -name; - } - - public function getMessage(): string - { - return $this->message; - } - - public function getBehavior(): string - { - return $this->behavior; - } - - public function getDetail(): array - { - return $this->detail; - } - - public function getTransactionId(): ?string - { - return $this->transactionId; - } - - public function getOrderId(): ?string - { - return $this->orderId; - } - - public function getPayerMessage(): ?string - { - return $this->payerMessage; - } - - public function getProcessorName(): ?string - { - return $this->processorName; - } - - public function getProcessorResult(): ?string - { - return $this->processorResult; - } - - public function getProcessorMessage(): ?string - { - return $this->processorMessage; - } - - public function toArray(): array - { - return [ - 'Name' => $this->getName(), - 'Message' => $this->getMessage(), - 'Behavior' => $this->getBehavior(), - 'Detail' => $this->getDetail(), - 'TransactionId' => $this->getTransactionId(), - 'OrderId' => $this->getOrderId(), - 'PayerMessage' => $this->getPayerMessage(), - 'ProcessorName' => $this->getProcessorName(), - 'ProcessorResult' => $this->getProcessorResult(), - 'ProcessorMessage' => $this->getProcessorMessage(), - ]; - } - - public static function fromArray(array $data): self - { - return new self( - $data['ErrorName'], - $data['ErrorMessage'], - $data['Behavior'], - $data['ErrorDetail'] ?? [], - $data['TransactionId'] ?? null, - $data['OrderId'] ?? null, - $data['PayerMessage'] ?? null, - $data['ProcessorName'] ?? null, - $data['ProcessorResult'] ?? null, - $data['ProcessorMessage'] ?? null, - ); - } -} diff --git a/src/Payum/Action/AuthorizeAction.php b/src/Payum/Action/AuthorizeAction.php index abd033dc..2b9fe855 100644 --- a/src/Payum/Action/AuthorizeAction.php +++ b/src/Payum/Action/AuthorizeAction.php @@ -5,6 +5,7 @@ namespace CommerceWeavers\SyliusSaferpayPlugin\Payum\Action; use CommerceWeavers\SyliusSaferpayPlugin\Client\SaferpayClientInterface; +use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\AuthorizeResponse; use CommerceWeavers\SyliusSaferpayPlugin\Client\ValueObject\ErrorResponse; use Payum\Core\Action\ActionInterface; use Payum\Core\Exception\RequestNotSupportedException; @@ -33,6 +34,7 @@ public function execute(mixed $request): void return; } + /** @var AuthorizeResponse|ErrorResponse $response */ $response = $this->saferpayClient->authorize($payment, $token); if ($response instanceof ErrorResponse) { diff --git a/src/Payum/Action/CaptureAction.php b/src/Payum/Action/CaptureAction.php index 023138f0..87a553ba 100644 --- a/src/Payum/Action/CaptureAction.php +++ b/src/Payum/Action/CaptureAction.php @@ -34,7 +34,7 @@ public function execute($request): void return; } - /** @var CaptureResponse $response */ + /** @var CaptureResponse|ErrorResponse $response */ $response = $this->saferpayClient->capture($payment); if ($response instanceof ErrorResponse) { $payment->setDetails([ diff --git a/src/Payum/Action/RefundAction.php b/src/Payum/Action/RefundAction.php index de4e6b4b..742ecdcb 100644 --- a/src/Payum/Action/RefundAction.php +++ b/src/Payum/Action/RefundAction.php @@ -43,8 +43,9 @@ public function execute($request): void return; } + /** @var CaptureResponse|ErrorResponse $response */ $response = $this->saferpayClient->capture($payment); - if (!$response->isSuccessful()) { + if ($response instanceof ErrorResponse) { throw new PaymentRefundFailedException(); } diff --git a/tests/Unit/Client/ValueObject/CaptureResponseTest.php b/tests/Unit/Client/ValueObject/CaptureResponseTest.php index 1c2c372e..be20d54a 100644 --- a/tests/Unit/Client/ValueObject/CaptureResponseTest.php +++ b/tests/Unit/Client/ValueObject/CaptureResponseTest.php @@ -31,26 +31,6 @@ public function it_creates_capture_response_vo_from_array(): void $this->assertEquals('2015-01-30T12:45:22.258+01:00', $response->getDate()); } - /** @test */ - public function it_creates_capture_response_vo_with_an_error_from_array(): void - { - $response = CaptureResponse::fromArray([ - "ResponseHeader" => [ - "SpecVersion" => "1.33", - "RequestId" => "b27de121-ffa0-4f1d-b7aa-b48109a88486", - ], - "Behavior" => "DO_NOT_RETRY", - "ErrorName" => "TRANSACTION_ALREADY_CAPTURED", - "ErrorMessage" => "Transaction already captured", - ]); - - $this->assertResponseHeader($response->getResponseHeader()); - $this->assertEquals(400, $response->getStatusCode()); - $this->assertEquals('DO_NOT_RETRY', $response->getError()->getBehavior()); - $this->assertEquals('TRANSACTION_ALREADY_CAPTURED', $response->getError()->getName()); - $this->assertEquals('Transaction already captured', $response->getError()->getMessage()); - } - private function assertResponseHeader(ResponseHeader $responseHeader): void { $this->assertEquals('1.33', $responseHeader->getSpecVersion()); diff --git a/tests/Unit/Client/ValueObject/RefundResponseTest.php b/tests/Unit/Client/ValueObject/RefundResponseTest.php index ecfac831..c902b221 100644 --- a/tests/Unit/Client/ValueObject/RefundResponseTest.php +++ b/tests/Unit/Client/ValueObject/RefundResponseTest.php @@ -60,26 +60,6 @@ public function it_creates_refund_response_vo_from_array(): void $this->assertPaymentMeans($response->getPaymentMeans()); } - /** @test */ - public function it_creates_refund_response_vo_with_an_error_from_array(): void - { - $response = RefundResponse::fromArray([ - "ResponseHeader" => [ - "SpecVersion" => "1.33", - "RequestId" => "b27de121-ffa0-4f1d-b7aa-b48109a88486", - ], - "Behavior" => "DO_NOT_RETRY", - "ErrorName" => "TRANSACTION_NOT_FOUND", - "ErrorMessage" => "Transaction not found", - ]); - - $this->assertResponseHeader($response->getResponseHeader()); - $this->assertEquals(400, $response->getStatusCode()); - $this->assertEquals('DO_NOT_RETRY', $response->getError()->getBehavior()); - $this->assertEquals('TRANSACTION_NOT_FOUND', $response->getError()->getName()); - $this->assertEquals('Transaction not found', $response->getError()->getMessage()); - } - private function assertResponseHeader(ResponseHeader $responseHeader): void { $this->assertEquals('1.33', $responseHeader->getSpecVersion()); From 28195eaadc14c4e554c5e9f2b1cb5cf723a3f2a0 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 13:51:54 +0200 Subject: [PATCH 09/11] Make Transaction and PaymentMeans required --- src/Client/ValueObject/AssertResponse.php | 16 ++++++++-------- src/Client/ValueObject/RefundResponse.php | 16 ++++++++-------- src/Payum/Action/AssertAction.php | 2 -- src/Payum/Action/RefundAction.php | 3 --- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/Client/ValueObject/AssertResponse.php b/src/Client/ValueObject/AssertResponse.php index 713b35f1..1c981e58 100644 --- a/src/Client/ValueObject/AssertResponse.php +++ b/src/Client/ValueObject/AssertResponse.php @@ -15,8 +15,8 @@ class AssertResponse implements ResponseInterface private function __construct( private int $statusCode, private ResponseHeader $responseHeader, - private ?Transaction $transaction, - private ?PaymentMeans $paymentMeans, + private Transaction $transaction, + private PaymentMeans $paymentMeans, private ?Payer $payer, private ?Liability $liability, ) { @@ -32,12 +32,12 @@ public function getResponseHeader(): ResponseHeader return $this->responseHeader; } - public function getTransaction(): ?Transaction + public function getTransaction(): Transaction { return $this->transaction; } - public function getPaymentMeans(): ?PaymentMeans + public function getPaymentMeans(): PaymentMeans { return $this->paymentMeans; } @@ -62,8 +62,8 @@ public function toArray(): array return [ 'StatusCode' => $this->getStatusCode(), 'ResponseHeader' => $this->getResponseHeader()->toArray(), - 'Transaction' => $this->getTransaction()?->toArray(), - 'PaymentMeans' => $this->getPaymentMeans()?->toArray(), + 'Transaction' => $this->getTransaction()->toArray(), + 'PaymentMeans' => $this->getPaymentMeans()->toArray(), 'Payer' => $this->getPayer()?->toArray(), 'Liability' => $this->getLiability()?->toArray(), ]; @@ -74,8 +74,8 @@ public static function fromArray(array $data): self return new self( $data['StatusCode'] ?? 400, ResponseHeader::fromArray($data['ResponseHeader']), - isset($data['Transaction']) ? Transaction::fromArray($data['Transaction']) : null, - isset($data['PaymentMeans']) ? PaymentMeans::fromArray($data['PaymentMeans']) : null, + Transaction::fromArray($data['Transaction']), + PaymentMeans::fromArray($data['PaymentMeans']), isset($data['Payer']) ? Payer::fromArray($data['Payer']) : null, isset($data['Liability']) ? Liability::fromArray($data['Liability']) : null, ); diff --git a/src/Client/ValueObject/RefundResponse.php b/src/Client/ValueObject/RefundResponse.php index 5460a10b..3cb9404e 100644 --- a/src/Client/ValueObject/RefundResponse.php +++ b/src/Client/ValueObject/RefundResponse.php @@ -13,8 +13,8 @@ class RefundResponse implements ResponseInterface private function __construct( private int $statusCode, private ResponseHeader $responseHeader, - private ?Transaction $transaction, - private ?PaymentMeans $paymentMeans, + private Transaction $transaction, + private PaymentMeans $paymentMeans, ) { } @@ -28,12 +28,12 @@ public function getResponseHeader(): ResponseHeader return $this->responseHeader; } - public function getTransaction(): ?Transaction + public function getTransaction(): Transaction { return $this->transaction; } - public function getPaymentMeans(): ?PaymentMeans + public function getPaymentMeans(): PaymentMeans { return $this->paymentMeans; } @@ -48,8 +48,8 @@ public function toArray(): array return [ 'StatusCode' => $this->getStatusCode(), 'ResponseHeader' => $this->getResponseHeader()->toArray(), - 'Transaction' => $this->getTransaction()?->toArray(), - 'PaymentMeans' => $this->getPaymentMeans()?->toArray(), + 'Transaction' => $this->getTransaction()->toArray(), + 'PaymentMeans' => $this->getPaymentMeans()->toArray(), ]; } @@ -58,8 +58,8 @@ public static function fromArray(array $data): self return new self( $data['StatusCode'] ?? 400, ResponseHeader::fromArray($data['ResponseHeader']), - isset($data['Transaction']) ? Transaction::fromArray($data['Transaction']) : null, - isset($data['PaymentMeans']) ? PaymentMeans::fromArray($data['PaymentMeans']) : null, + Transaction::fromArray($data['Transaction']), + PaymentMeans::fromArray($data['PaymentMeans']), ); } } diff --git a/src/Payum/Action/AssertAction.php b/src/Payum/Action/AssertAction.php index f9f7990a..5ea8a794 100644 --- a/src/Payum/Action/AssertAction.php +++ b/src/Payum/Action/AssertAction.php @@ -11,7 +11,6 @@ use Payum\Core\Action\ActionInterface; use Payum\Core\Exception\RequestNotSupportedException; use Sylius\Component\Core\Model\PaymentInterface; -use Webmozart\Assert\Assert as WebmozartAssert; final class AssertAction implements ActionInterface { @@ -66,7 +65,6 @@ private function handleSuccessfulResponse(PaymentInterface $payment, AssertRespo $paymentDetails = $payment->getDetails(); $transaction = $response->getTransaction(); - WebmozartAssert::notNull($transaction); $paymentDetails['status'] = $transaction->getStatus(); $paymentDetails['transaction_id'] = $transaction->getId(); diff --git a/src/Payum/Action/RefundAction.php b/src/Payum/Action/RefundAction.php index 742ecdcb..89b98f0d 100644 --- a/src/Payum/Action/RefundAction.php +++ b/src/Payum/Action/RefundAction.php @@ -13,7 +13,6 @@ use Payum\Core\Action\ActionInterface; use Payum\Core\Exception\RequestNotSupportedException; use Sylius\Component\Core\Model\PaymentInterface; -use Webmozart\Assert\Assert; final class RefundAction implements ActionInterface { @@ -38,7 +37,6 @@ public function execute($request): void $this->handleRefundResponse($payment, $response); $transaction = $response->getTransaction(); - Assert::notNull($transaction); if ($transaction->getStatus() === StatusAction::STATUS_CAPTURED) { return; } @@ -60,7 +58,6 @@ public function supports($request): bool private function handleRefundResponse(PaymentInterface $payment, RefundResponse $response): void { $transaction = $response->getTransaction(); - Assert::notNull($transaction); $paymentDetails = $payment->getDetails(); if ($transaction->getStatus() === StatusAction::STATUS_CAPTURED) { From 41b7455ba739a37583c7ad23eb01e02ebb98cc96 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 13:53:55 +0200 Subject: [PATCH 10/11] Unhardcode default response code --- src/Client/ValueObject/AssertResponse.php | 2 +- src/Client/ValueObject/AuthorizeResponse.php | 2 +- src/Client/ValueObject/CaptureResponse.php | 2 +- src/Client/ValueObject/RefundResponse.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Client/ValueObject/AssertResponse.php b/src/Client/ValueObject/AssertResponse.php index 1c981e58..d23346e9 100644 --- a/src/Client/ValueObject/AssertResponse.php +++ b/src/Client/ValueObject/AssertResponse.php @@ -72,7 +72,7 @@ public function toArray(): array public static function fromArray(array $data): self { return new self( - $data['StatusCode'] ?? 400, + $data['StatusCode'], ResponseHeader::fromArray($data['ResponseHeader']), Transaction::fromArray($data['Transaction']), PaymentMeans::fromArray($data['PaymentMeans']), diff --git a/src/Client/ValueObject/AuthorizeResponse.php b/src/Client/ValueObject/AuthorizeResponse.php index 3be0116b..b2622e83 100644 --- a/src/Client/ValueObject/AuthorizeResponse.php +++ b/src/Client/ValueObject/AuthorizeResponse.php @@ -61,7 +61,7 @@ public function toArray(): array public static function fromArray(array $data): self { return new self( - $data['StatusCode'] ?? 400, + $data['StatusCode'], ResponseHeader::fromArray($data['ResponseHeader']), $data['Token'] ?? null, $data['Expiration'] ?? null, diff --git a/src/Client/ValueObject/CaptureResponse.php b/src/Client/ValueObject/CaptureResponse.php index e62ffef6..27b95b93 100644 --- a/src/Client/ValueObject/CaptureResponse.php +++ b/src/Client/ValueObject/CaptureResponse.php @@ -61,7 +61,7 @@ public function toArray(): array public static function fromArray(array $data): self { return new self( - $data['StatusCode'] ?? 400, + $data['StatusCode'], ResponseHeader::fromArray($data['ResponseHeader']), $data['CaptureId'] ?? null, $data['Status'] ?? null, diff --git a/src/Client/ValueObject/RefundResponse.php b/src/Client/ValueObject/RefundResponse.php index 3cb9404e..28491685 100644 --- a/src/Client/ValueObject/RefundResponse.php +++ b/src/Client/ValueObject/RefundResponse.php @@ -56,7 +56,7 @@ public function toArray(): array public static function fromArray(array $data): self { return new self( - $data['StatusCode'] ?? 400, + $data['StatusCode'], ResponseHeader::fromArray($data['ResponseHeader']), Transaction::fromArray($data['Transaction']), PaymentMeans::fromArray($data['PaymentMeans']), From 7e64d8b241d1793c78554d79a3fee0cb21148da3 Mon Sep 17 00:00:00 2001 From: Mateusz Zalewski Date: Wed, 14 Jun 2023 14:00:55 +0200 Subject: [PATCH 11/11] Extract errors constructors into single private method --- src/Client/ValueObject/ErrorResponse.php | 65 +++++++++--------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/src/Client/ValueObject/ErrorResponse.php b/src/Client/ValueObject/ErrorResponse.php index 3c38c5c6..79b77b32 100644 --- a/src/Client/ValueObject/ErrorResponse.php +++ b/src/Client/ValueObject/ErrorResponse.php @@ -27,60 +27,22 @@ private function __construct( public static function forAssert(array $data): self { - return new self( - $data['StatusCode'], - ResponseHeader::fromArray($data['ResponseHeader']), - $data['ErrorName'], - $data['ErrorMessage'], - [], - $data['Behavior'], - 'Assert', - $data['TransactionId'], - $data['OrderId'], - $data['PayerMessage'] ?? null, - ); + return self::createForOperation($data, 'Assert'); } public static function forCapture(array $data): self { - return new self( - $data['StatusCode'], - ResponseHeader::fromArray($data['ResponseHeader']), - $data['ErrorName'], - $data['ErrorMessage'], - [], - $data['Behavior'], - 'Capture', - ); + return self::createForOperation($data, 'Capture'); } public static function forAuthorize(array $data): self { - return new self( - $data['StatusCode'], - ResponseHeader::fromArray($data['ResponseHeader']), - $data['ErrorName'], - $data['ErrorMessage'], - $data['ErrorDetail'] ?? [], - $data['Behavior'], - 'Authorize', - $data['TransactionId'] ?? null, - $data['OrderId'] ?? null, - $data['PayerMessage'] ?? null, - ); + return self::createForOperation($data, 'Authorize'); } public static function forRefund(array $data): self { - return new self( - $data['StatusCode'], - ResponseHeader::fromArray($data['ResponseHeader']), - $data['ErrorName'], - $data['ErrorMessage'], - [], - $data['Behavior'], - 'Refund', - ); + return self::createForOperation($data, 'Refund'); } public function getStatusCode(): int @@ -170,4 +132,23 @@ public function isSuccessful(): bool { return false; } + + private static function createForOperation(array $data, string $operation): self + { + return new self( + $data['StatusCode'], + ResponseHeader::fromArray($data['ResponseHeader']), + $data['ErrorName'], + $data['ErrorMessage'], + $data['ErrorDetail'] ?? [], + $data['Behavior'], + $operation, + $data['TransactionId'] ?? null, + $data['OrderId'] ?? null, + $data['PayerMessage'] ?? null, + $data['ProcessorName'] ?? null, + $data['ProcessorResult'] ?? null, + $data['ProcessorMessage'] ?? null, + ); + } }