From df47a703c54ea3c7a64d3468a3169dd30607d948 Mon Sep 17 00:00:00 2001 From: Nuryagdy Mustapayev Date: Wed, 24 Apr 2024 08:48:38 +0200 Subject: [PATCH 1/4] kuveytpos - working on test coverage --- docs/PRE-AUTH-POST-EXAMPLE.md | 2 +- docs/THREED-PAYMENT-EXAMPLE.md | 2 +- src/Crypt/KuveytPosCrypt.php | 3 +- .../KuveytPosRequestDataMapper.php | 4 +- .../KuveytPosResponseDataMapper.php | 4 +- src/Gateways/KuveytPos.php | 4 - src/Gateways/VakifKatilimPos.php | 5 -- tests/Unit/Crypt/KuveytPosCryptTest.php | 13 +++- .../KuveytPosRequestDataMapperTest.php | 38 ++++++++++ .../KuveytPosResponseDataMapperTest.php | 54 +++++++++++++- tests/Unit/Gateways/KuveytPosTest.php | 73 +++++++++++-------- tests/Unit/Gateways/VakifKatilimTest.php | 7 -- 12 files changed, 153 insertions(+), 56 deletions(-) diff --git a/docs/PRE-AUTH-POST-EXAMPLE.md b/docs/PRE-AUTH-POST-EXAMPLE.md index 236f6596..4704daf5 100644 --- a/docs/PRE-AUTH-POST-EXAMPLE.md +++ b/docs/PRE-AUTH-POST-EXAMPLE.md @@ -115,7 +115,7 @@ try { } ``` ```html - +
$value) : ?> diff --git a/docs/THREED-PAYMENT-EXAMPLE.md b/docs/THREED-PAYMENT-EXAMPLE.md index e80bf2fe..888f9e38 100644 --- a/docs/THREED-PAYMENT-EXAMPLE.md +++ b/docs/THREED-PAYMENT-EXAMPLE.md @@ -217,7 +217,7 @@ try { } ``` ```html - + $value) : ?> diff --git a/src/Crypt/KuveytPosCrypt.php b/src/Crypt/KuveytPosCrypt.php index 9e05e74f..a2ec1f12 100644 --- a/src/Crypt/KuveytPosCrypt.php +++ b/src/Crypt/KuveytPosCrypt.php @@ -6,6 +6,7 @@ namespace Mews\Pos\Crypt; use Mews\Pos\Entity\Account\AbstractPosAccount; +use Mews\Pos\Exceptions\NotImplementedException; class KuveytPosCrypt extends AbstractCrypt { @@ -40,7 +41,7 @@ public function create3DHash(AbstractPosAccount $posAccount, array $requestData) */ public function check3DHash(AbstractPosAccount $posAccount, array $data): bool { - return true; + throw new NotImplementedException(); } /** diff --git a/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php b/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php index fc37055e..a0709a1c 100644 --- a/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php +++ b/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php @@ -392,7 +392,7 @@ protected function mapInstallment(int $installment): string */ protected function preparePaymentOrder(array $order): array { - return array_merge($order, [ + return \array_merge($order, [ 'installment' => $order['installment'] ?? 0, 'currency' => $order['currency'] ?? PosInterface::CURRENCY_TRY, ]); @@ -403,7 +403,7 @@ protected function preparePaymentOrder(array $order): array */ protected function prepareStatusOrder(array $order): array { - return array_merge($order, [ + return \array_merge($order, [ 'id' => $order['id'], 'currency' => $order['currency'] ?? PosInterface::CURRENCY_TRY, 'start_date' => $order['start_date'] ?? date_create('-360 day'), diff --git a/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php b/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php index e8e7ec87..d71877d5 100644 --- a/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php +++ b/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php @@ -123,7 +123,7 @@ public function map3DPaymentData(array $raw3DAuthResponseData, ?array $rawPaymen */ public function map3DPayResponseData(array $raw3DAuthResponseData, string $txType, array $order): array { - return $this->map3DPaymentData($raw3DAuthResponseData, $raw3DAuthResponseData, $txType, $order); + throw new NotImplementedException(); } /** @@ -131,7 +131,7 @@ public function map3DPayResponseData(array $raw3DAuthResponseData, string $txTyp */ public function map3DHostResponseData(array $raw3DAuthResponseData, string $txType, array $order): array { - return $this->map3DPayResponseData($raw3DAuthResponseData, $txType, $order); + throw new NotImplementedException(); } /** diff --git a/src/Gateways/KuveytPos.php b/src/Gateways/KuveytPos.php index 1b8920cc..f3411828 100644 --- a/src/Gateways/KuveytPos.php +++ b/src/Gateways/KuveytPos.php @@ -143,10 +143,6 @@ public function make3DPayment(Request $request, array $order, string $txType, Cr return $this; } - if (!$this->requestDataMapper->getCrypt()->check3DHash($this->account, $gatewayResponse)) { - throw new HashMismatchException(); - } - $this->logger->debug('finishing payment'); $requestData = $this->requestDataMapper->create3DPaymentRequestData($this->account, $order, $txType, $gatewayResponse); diff --git a/src/Gateways/VakifKatilimPos.php b/src/Gateways/VakifKatilimPos.php index f9a8db3e..7af9b902 100644 --- a/src/Gateways/VakifKatilimPos.php +++ b/src/Gateways/VakifKatilimPos.php @@ -14,7 +14,6 @@ use Mews\Pos\Entity\Account\KuveytPosAccount; use Mews\Pos\Entity\Card\CreditCardInterface; use Mews\Pos\Event\RequestDataPreparedEvent; -use Mews\Pos\Exceptions\HashMismatchException; use Mews\Pos\Exceptions\UnsupportedPaymentModelException; use Mews\Pos\Exceptions\UnsupportedTransactionTypeException; use Mews\Pos\PosInterface; @@ -122,10 +121,6 @@ public function make3DPayment(Request $request, array $order, string $txType, Cr return $this; } - if (!$this->requestDataMapper->getCrypt()->check3DHash($this->account, $gatewayResponse)) { - throw new HashMismatchException(); - } - $this->logger->debug('finishing payment'); $requestData = $this->requestDataMapper->create3DPaymentRequestData($this->account, $order, $txType, $gatewayResponse); diff --git a/tests/Unit/Crypt/KuveytPosCryptTest.php b/tests/Unit/Crypt/KuveytPosCryptTest.php index 3aaafff9..23eb048e 100644 --- a/tests/Unit/Crypt/KuveytPosCryptTest.php +++ b/tests/Unit/Crypt/KuveytPosCryptTest.php @@ -6,7 +6,9 @@ namespace Mews\Pos\Tests\Unit\Crypt; use Mews\Pos\Crypt\KuveytPosCrypt; +use Mews\Pos\Entity\Account\AbstractPosAccount; use Mews\Pos\Entity\Account\KuveytPosAccount; +use Mews\Pos\Exceptions\NotImplementedException; use Mews\Pos\Factory\AccountFactory; use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; @@ -54,7 +56,9 @@ public function testCreate3DHash(array $requestData, string $expected): void public function testCheck3DHash(): void { - $this->assertTrue($this->crypt->check3DHash($this->account, [])); + $this->expectException(NotImplementedException::class); + + $this->crypt->check3DHash($this->account, []); } /** @@ -67,6 +71,13 @@ public function testCreateHash(array $requestData, string $expected): void $this->assertSame($expected, $actual); } + public function testCreateHashException(): void + { + $account = $this->createMock(AbstractPosAccount::class); + $this->expectException(\LogicException::class); + $this->crypt->createHash($account, []); + } + public static function hashCreateDataProvider(): array { return [ diff --git a/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php b/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php index 55daafd8..87b8a24f 100644 --- a/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php +++ b/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php @@ -175,6 +175,44 @@ public function testCreate3DPaymentRequestData(KuveytPosAccount $kuveytPosAccoun $this->assertEquals($expectedData, $actual); } + public function testGet3DFormData(): void + { + $expected = [ + 'gateway' => 'https://bank-gateway.com', + 'method' => 'POST', + 'inputs' => [ + 'abc' => '123', + ], + ]; + + $actual = $this->requestDataMapper->create3DFormData( + $this->account, + ['abc' => '123'], + PosInterface::MODEL_3D_SECURE, + PosInterface::TX_TYPE_PAY_AUTH, + 'https://bank-gateway.com', + ); + + $this->assertSame($expected, $actual); + } + + public function testCreateNonSecurePostAuthPaymentRequestData(): void + { + $this->expectException(\Mews\Pos\Exceptions\NotImplementedException::class); + $this->requestDataMapper->createNonSecurePostAuthPaymentRequestData($this->account, []); + } + + public function testCreateOrderHistoryRequestData(): void + { + $this->expectException(\Mews\Pos\Exceptions\NotImplementedException::class); + $this->requestDataMapper->createOrderHistoryRequestData($this->account, []); + } + + public function testCreateHistoryRequestData(): void + { + $this->expectException(\Mews\Pos\Exceptions\NotImplementedException::class); + $this->requestDataMapper->createHistoryRequestData($this->account, []); + } public static function createCancelRequestDataProvider(): iterable { diff --git a/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php b/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php index bddda6f7..c76115a6 100644 --- a/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php +++ b/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php @@ -7,6 +7,7 @@ use Mews\Pos\DataMapper\RequestDataMapper\KuveytPosRequestDataMapper; use Mews\Pos\DataMapper\ResponseDataMapper\KuveytPosResponseDataMapper; +use Mews\Pos\Exceptions\NotImplementedException; use Mews\Pos\Factory\CryptFactory; use Mews\Pos\Gateways\KuveytPos; use Mews\Pos\PosInterface; @@ -16,6 +17,7 @@ /** * @covers \Mews\Pos\DataMapper\ResponseDataMapper\KuveytPosResponseDataMapper + * @covers \Mews\Pos\DataMapper\ResponseDataMapper\AbstractResponseDataMapper */ class KuveytPosResponseDataMapperTest extends TestCase { @@ -85,6 +87,11 @@ public function testMapPaymentResponse(string $txType, array $responseData, arra } unset($actualData['transaction_time'], $expectedData['transaction_time']); + if ([] !== $responseData) { + $this->assertArrayHasKey('all', $actualData); + $this->assertIsArray($actualData['all']); + $this->assertNotEmpty($actualData['all']); + } unset($actualData['all']); \ksort($expectedData); \ksort($actualData); @@ -164,6 +171,30 @@ public function testMap3DPaymentData(array $order, string $txType, array $threeD $this->assertEquals($expectedData, $actualData); } + public function testMap3DPayResponseData(): void + { + $this->expectException(NotImplementedException::class); + $this->responseDataMapper->map3DPayResponseData([], PosInterface::TX_TYPE_PAY_AUTH, []); + } + + public function testMap3DHostResponseData(): void + { + $this->expectException(NotImplementedException::class); + $this->responseDataMapper->map3DHostResponseData([], PosInterface::TX_TYPE_PAY_AUTH, []); + } + + public function testMapHistoryResponse(): void + { + $this->expectException(NotImplementedException::class); + $this->responseDataMapper->mapHistoryResponse([]); + } + + public function testMapOrderHistoryResponse(): void + { + $this->expectException(NotImplementedException::class); + $this->responseDataMapper->mapOrderHistoryResponse([]); + } + public static function paymentTestDataProvider(): iterable { yield 'fail1' => [ @@ -198,7 +229,28 @@ public static function paymentTestDataProvider(): iterable 'installment_count' => null, ], ]; - + yield 'empty' => [ + 'txType' => PosInterface::TX_TYPE_PAY_AUTH, + 'responseData' => [], + 'expectedData' => [ + 'order_id' => null, + 'transaction_id' => null, + 'transaction_type' => 'pay', + 'transaction_time' => null, + 'currency' => null, + 'amount' => null, + 'payment_model' => 'regular', + 'auth_code' => null, + 'ref_ret_num' => null, + 'batch_num' => null, + 'proc_return_code' => null, + 'status' => 'declined', + 'status_detail' => null, + 'error_code' => null, + 'error_message' => null, + 'installment_count' => null, + ], + ]; yield 'success1' => [ 'txType' => PosInterface::TX_TYPE_PAY_AUTH, 'responseData' => [ diff --git a/tests/Unit/Gateways/KuveytPosTest.php b/tests/Unit/Gateways/KuveytPosTest.php index e5935e0f..e092c6d1 100644 --- a/tests/Unit/Gateways/KuveytPosTest.php +++ b/tests/Unit/Gateways/KuveytPosTest.php @@ -173,26 +173,14 @@ public function testGetCommon3DFormDataSuccessResponse(): void $txType = PosInterface::TX_TYPE_PAY_AUTH; $paymentModel = PosInterface::MODEL_3D_SECURE; $card = $this->card; - - $this->serializerMock->expects(self::once()) - ->method('encode') - ->with(['form-data'], $txType) - ->willReturn('encoded-request-data'); - - $this->serializerMock->expects(self::once()) - ->method('decode') - ->with($response, $txType) - ->willReturn(['form_inputs' => ['form-inputs'], 'gateway' => 'form-action-url']); - $this->prepareClient( - $this->httpClientMock, - $response, + $requestData = ['form-data']; + $this->configureClientResponse( + $txType, 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelPayGate', - [ - 'body' => 'encoded-request-data', - 'headers' => [ - 'Content-Type' => 'text/xml; charset=UTF-8', - ], - ], + $requestData, + 'encoded-request-data', + $response, + ['form_inputs' => ['form-inputs'], 'gateway' => 'form-action-url'], ); $this->eventDispatcherMock->expects(self::once()) @@ -207,7 +195,7 @@ public function testGetCommon3DFormDataSuccessResponse(): void $txType, $card ) - ->willReturn(['form-data']); + ->willReturn($requestData); $this->requestMapperMock->expects(self::once()) ->method('create3DFormData') @@ -239,12 +227,8 @@ public function testMake3DPayment( bool $isSuccess ): void { - if ($is3DSuccess) { - $this->cryptMock->expects(self::once()) - ->method('check3DHash') - ->with($this->account, $decodedRequest) - ->willReturn(true); - } + $this->cryptMock->expects(self::never()) + ->method('check3DHash'); $this->responseMapperMock->expects(self::once()) ->method('extractMdStatus') @@ -423,13 +407,40 @@ public static function makeRegularPaymentDataProvider(): array 'txType' => PosInterface::TX_TYPE_PAY_AUTH, 'api_url' => 'https://boa.vakifkatilim.com.tr/VirtualPOS.Gateway/Home/Non3DPayGate', ], + ]; + } + + private function configureClientResponse( + string $txType, + string $apiUrl, + array $requestData, + string $encodedRequestData, + string $responseContent, + array $decodedResponse, + ?int $statusCode = null + ): void + { + $this->serializerMock->expects(self::once()) + ->method('encode') + ->with($requestData, $txType) + ->willReturn($encodedRequestData); + + $this->serializerMock->expects(self::once()) + ->method('decode') + ->with($responseContent, $txType) + ->willReturn($decodedResponse); + + $this->prepareClient( + $this->httpClientMock, + $responseContent, + $apiUrl, [ - 'order' => [ - 'id' => '2020110828BC', + 'headers' => [ + 'Content-Type' => 'text/xml; charset=UTF-8', ], - 'txType' => PosInterface::TX_TYPE_PAY_PRE_AUTH, - 'api_url' => 'https://boa.vakifkatilim.com.tr/VirtualPOS.Gateway/Home/PreAuthorizaten', + 'body' => $encodedRequestData, ], - ]; + $statusCode + ); } } diff --git a/tests/Unit/Gateways/VakifKatilimTest.php b/tests/Unit/Gateways/VakifKatilimTest.php index c76c7253..26e9a873 100644 --- a/tests/Unit/Gateways/VakifKatilimTest.php +++ b/tests/Unit/Gateways/VakifKatilimTest.php @@ -268,13 +268,6 @@ public function testMake3DPayment( bool $isSuccess ): void { - if ($is3DSuccess) { - $this->cryptMock->expects(self::once()) - ->method('check3DHash') - ->with($this->account, $request->request->all()) - ->willReturn(true); - } - $this->responseMapperMock->expects(self::once()) ->method('extractMdStatus') ->with($request->request->all()) From 69680564137457572197568866a1cd3cfdbb9bca Mon Sep 17 00:00:00 2001 From: Nuryagdy Mustapayev Date: Wed, 24 Apr 2024 08:49:41 +0200 Subject: [PATCH 2/4] kuveytpos - working on test coverage --- src/Gateways/KuveytPos.php | 1 - tests/Unit/Crypt/KuveytPosCryptTest.php | 7 +++++++ .../RequestDataMapper/KuveytPosRequestDataMapperTest.php | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Gateways/KuveytPos.php b/src/Gateways/KuveytPos.php index f3411828..95a71162 100644 --- a/src/Gateways/KuveytPos.php +++ b/src/Gateways/KuveytPos.php @@ -16,7 +16,6 @@ use Mews\Pos\Entity\Account\KuveytPosAccount; use Mews\Pos\Entity\Card\CreditCardInterface; use Mews\Pos\Event\RequestDataPreparedEvent; -use Mews\Pos\Exceptions\HashMismatchException; use Mews\Pos\Exceptions\UnsupportedPaymentModelException; use Mews\Pos\Exceptions\UnsupportedTransactionTypeException; use Mews\Pos\PosInterface; diff --git a/tests/Unit/Crypt/KuveytPosCryptTest.php b/tests/Unit/Crypt/KuveytPosCryptTest.php index 23eb048e..636a2801 100644 --- a/tests/Unit/Crypt/KuveytPosCryptTest.php +++ b/tests/Unit/Crypt/KuveytPosCryptTest.php @@ -54,6 +54,13 @@ public function testCreate3DHash(array $requestData, string $expected): void $this->assertSame($expected, $actual); } + public function testCreate3DHashException(): void + { + $account = $this->createMock(AbstractPosAccount::class); + $this->expectException(\LogicException::class); + $this->crypt->create3DHash($account, []); + } + public function testCheck3DHash(): void { $this->expectException(NotImplementedException::class); diff --git a/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php b/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php index 87b8a24f..92e6a402 100644 --- a/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php +++ b/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php @@ -21,6 +21,7 @@ /** * @covers \Mews\Pos\DataMapper\RequestDataMapper\KuveytPosRequestDataMapper + * @covers \Mews\Pos\DataMapper\RequestDataMapper\AbstractRequestDataMapper */ class KuveytPosRequestDataMapperTest extends TestCase { From 8a14553493795152a6e8df6fd5ab24a577508e9e Mon Sep 17 00:00:00 2001 From: Nuryagdy Mustapayev Date: Wed, 24 Apr 2024 16:42:50 +0200 Subject: [PATCH 3/4] Kuveyt Pos added support for non secure payment --- README.md | 2 +- config/pos_production.php | 2 +- config/pos_test.php | 2 +- examples/kuveytpos/regular/_config.php | 6 +- .../KuveytPosRequestDataMapper.php | 27 ++- .../KuveytPosResponseDataMapper.php | 11 +- src/Gateways/KuveytPos.php | 72 +++++-- tests/Functional/KuveytPosTest.php | 116 +++++++++++ .../KuveytPosRequestDataMapperTest.php | 181 ++++++++++++++--- .../KuveytPosResponseDataMapperTest.php | 188 +++++++++++++++++- tests/Unit/Gateways/KuveytPosTest.php | 104 +++++++++- 11 files changed, 652 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index aadf6c39..9416edfc 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ sistemlerinin kullanılabilmesidir. | PosNetV1
(JSON API) | Albaraka Türk | NonSecure
3DSecure | İptal
İade
Durum sorgulama | | PayFor | Finansbank
Enpara | NonSecure
3DSecure
3DPay
3DHost | İptal
İade
Durum sorgulama
Sipariş Tarihçesini sorgulama
Geçmiş İşlemleri sorgulama | | InterPOS | Deniz bank | NonSecure
3DSecure
3DPay
3DHost | İptal
İade
Durum sorgulama | -| Kuveyt POS TDV2.0.0 | Kuveyt Türk | 3DSecure | İptal
İade
Durum sorgulama
(SOAP API) | +| Kuveyt POS TDV2.0.0 | Kuveyt Türk | NonSecure
3DSecure | İptal
İade
Durum sorgulama
(SOAP API) | | VakifKatilimPos
(test edilmesi gerekiyor) | Vakıf Katılım | NonSecure
3DSecure
3DHost | İptal
İade
Durum sorgulama
Sipariş Tarihçesini sorgulama
Geçmiş İşlemleri sorgulama | ### Ana başlıklar diff --git a/config/pos_production.php b/config/pos_production.php index a46ebf52..76ff7c0d 100644 --- a/config/pos_production.php +++ b/config/pos_production.php @@ -161,7 +161,7 @@ 'name' => 'kuveyt-pos', 'class' => Mews\Pos\Gateways\KuveytPos::class, 'gateway_endpoints' => [ - 'payment_api' => 'https://sanalpos.kuveytturk.com.tr/ServiceGateWay/Home/ThreeDModelProvisionGate', + 'payment_api' => 'https://sanalpos.kuveytturk.com.tr/ServiceGateWay/Home', 'gateway_3d' => 'https://sanalpos.kuveytturk.com.tr/ServiceGateWay/Home/ThreeDModelPayGate', 'query_api' => 'https://boa.kuveytturk.com.tr/BOA.Integration.WCFService/BOA.Integration.VirtualPos/VirtualPosService.svc?wsdl', ], diff --git a/config/pos_test.php b/config/pos_test.php index f0e7b0fa..285837fc 100644 --- a/config/pos_test.php +++ b/config/pos_test.php @@ -109,7 +109,7 @@ 'name' => 'kuveyt-pos', 'class' => Mews\Pos\Gateways\KuveytPos::class, 'gateway_endpoints' => [ - 'payment_api' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelProvisionGate', + 'payment_api' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home', 'gateway_3d' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelPayGate', 'query_api' => 'https://boatest.kuveytturk.com.tr/BOA.Integration.WCFService/BOA.Integration.VirtualPos/VirtualPosService.svc?wsdl', ], diff --git a/examples/kuveytpos/regular/_config.php b/examples/kuveytpos/regular/_config.php index e5a17540..62ed2287 100644 --- a/examples/kuveytpos/regular/_config.php +++ b/examples/kuveytpos/regular/_config.php @@ -9,13 +9,13 @@ $account = \Mews\Pos\Factory\AccountFactory::createKuveytPosAccount( 'kuveytpos', '496', - 'apiuser1', + 'apitest', '400235', - 'Api1232', + 'api123', PosInterface::MODEL_3D_SECURE ); $pos = getGateway($account, $eventDispatcher); $templateTitle = 'Regular Payment'; -$paymentModel = PosInterface::MODEL_3D_SECURE; +$paymentModel = PosInterface::MODEL_NON_SECURE; diff --git a/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php b/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php index a0709a1c..a03f23a0 100644 --- a/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php +++ b/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php @@ -94,7 +94,7 @@ public function create3DPaymentRequestData(AbstractPosAccount $posAccount, array 'TransactionType' => $this->mapTxType($txType), 'InstallmentCount' => $responseData['VPosMessage']['InstallmentCount'], 'Amount' => $responseData['VPosMessage']['Amount'], - 'DisplayAmount' => $this->formatAmount($responseData['VPosMessage']['Amount']), + 'DisplayAmount' => $responseData['VPosMessage']['Amount'], 'CurrencyCode' => $responseData['VPosMessage']['CurrencyCode'], 'MerchantOrderId' => $responseData['VPosMessage']['MerchantOrderId'], 'TransactionSecurity' => $responseData['VPosMessage']['TransactionSecurity'], @@ -161,11 +161,34 @@ public function createNonSecurePostAuthPaymentRequestData(AbstractPosAccount $po } /** + * @param KuveytPosAccount $posAccount + * * {@inheritDoc} */ public function createNonSecurePaymentRequestData(AbstractPosAccount $posAccount, array $order, string $txType, CreditCardInterface $creditCard): array { - throw new NotImplementedException(); + $order = $this->preparePaymentOrder($order); + + $requestData = $this->getRequestAccountData($posAccount) + [ + 'APIVersion' => self::API_VERSION, + 'HashData' => '', + 'TransactionType' => $this->mapTxType($txType), + 'TransactionSecurity' => '1', + 'MerchantOrderId' => (string) $order['id'], + 'Amount' => $this->formatAmount($order['amount']), + 'DisplayAmount' => $this->formatAmount($order['amount']), + 'CurrencyCode' => $this->mapCurrency($order['currency']), + 'InstallmentCount' => $this->mapInstallment($order['installment']), + 'CardHolderName' => $creditCard->getHolderName(), + 'CardNumber' => $creditCard->getNumber(), + 'CardExpireDateYear' => $creditCard->getExpireYear(self::CREDIT_CARD_EXP_YEAR_FORMAT), + 'CardExpireDateMonth' => $creditCard->getExpireMonth(self::CREDIT_CARD_EXP_MONTH_FORMAT), + 'CardCVV2' => $creditCard->getCvv(), + ]; + + $requestData['HashData'] = $this->crypt->createHash($posAccount, $requestData); + + return $requestData; } /** diff --git a/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php b/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php index d71877d5..82f983af 100644 --- a/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php +++ b/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php @@ -176,11 +176,18 @@ public function mapStatusResponse(array $rawResponseData): array $defaultResponse['transaction_id'] = $orderContract['Stan']; $defaultResponse['currency'] = $this->mapCurrency($orderContract['FEC']); $defaultResponse['first_amount'] = (float) $orderContract['FirstAmount']; - $defaultResponse['capture_amount'] = null !== $orderContract['FirstAmount'] ? (float) $orderContract['FirstAmount'] : null; - $defaultResponse['capture'] = $defaultResponse['first_amount'] > 0 && $defaultResponse['first_amount'] === $defaultResponse['capture_amount']; $defaultResponse['masked_number'] = $orderContract['CardNumber']; $defaultResponse['transaction_time'] = new \DateTimeImmutable($orderContract['OrderDate']); $defaultResponse['installment_count'] = $this->mapInstallment($orderContract['InstallmentCount']); + if (PosInterface::PAYMENT_STATUS_PAYMENT_COMPLETED === $defaultResponse['order_status']) { + $defaultResponse['capture_amount'] = null !== $orderContract['FirstAmount'] ? (float) $orderContract['FirstAmount'] : null; + $defaultResponse['capture'] = $defaultResponse['first_amount'] > 0 && $defaultResponse['first_amount'] === $defaultResponse['capture_amount']; + if ($defaultResponse['capture']) { + $defaultResponse['capture_time'] = new \DateTimeImmutable($orderContract['UpdateSystemDate']); + } + } elseif (PosInterface::PAYMENT_STATUS_CANCELED === $defaultResponse['order_status']) { + $defaultResponse['cancel_time'] = new \DateTimeImmutable($orderContract['UpdateSystemDate']); + } } return $defaultResponse; diff --git a/src/Gateways/KuveytPos.php b/src/Gateways/KuveytPos.php index 95a71162..08a93f59 100644 --- a/src/Gateways/KuveytPos.php +++ b/src/Gateways/KuveytPos.php @@ -45,6 +45,7 @@ class KuveytPos extends AbstractGateway /** @inheritdoc */ protected static array $supportedTransactions = [ PosInterface::TX_TYPE_PAY_AUTH => [ + PosInterface::MODEL_NON_SECURE, PosInterface::MODEL_3D_SECURE, ], PosInterface::TX_TYPE_PAY_PRE_AUTH => false, @@ -62,6 +63,29 @@ public function getAccount(): AbstractPosAccount return $this->account; } + /** + * @inheritDoc + */ + public function getApiURL(string $txType = null, string $paymentModel = null, ?string $orderTxType = null): string + { + if (\in_array( + $txType, + [ + PosInterface::TX_TYPE_REFUND, + PosInterface::TX_TYPE_STATUS, + PosInterface::TX_TYPE_CANCEL, + ], + true + )) { + return $this->getQueryAPIUrl(); + } + if (null !== $txType && null !== $paymentModel) { + return parent::getApiURL().'/'.$this->getRequestURIByTransactionType($txType, $paymentModel); + } + + return parent::getApiURL(); + } + /** * @inheritDoc */ @@ -107,14 +131,6 @@ public function get3DFormData(array $order, string $paymentModel, string $txType return $this->getCommon3DFormData($this->account, $order, $paymentModel, $txType, $gatewayUrl, $creditCard); } - /** - * @inheritDoc - */ - public function makeRegularPayment(array $order, CreditCardInterface $creditCard, string $txType): PosInterface - { - throw new UnsupportedPaymentModelException(); - } - /** * @inheritDoc */ @@ -163,7 +179,7 @@ public function make3DPayment(Request $request, array $order, string $txType, Cr $contents, $txType, PosInterface::MODEL_3D_SECURE, - $this->getApiURL() + $this->getApiURL($txType, PosInterface::MODEL_3D_SECURE) ); $this->response = $this->responseDataMapper->map3DPaymentData($gatewayResponse, $bankResponse, $txType, $order); @@ -185,7 +201,7 @@ protected function send($contents, string $txType, string $paymentModel, string throw new InvalidArgumentException(\sprintf('Invalid data type provided for %s transaction!', $txType)); } - return $this->data = $this->sendSoapRequest($contents, $txType); + return $this->data = $this->sendSoapRequest($contents, $txType, $url); } $this->logger->debug('sending request', ['url' => $url]); @@ -204,17 +220,17 @@ protected function send($contents, string $txType, string $paymentModel, string /** * @phpstan-param PosInterface::TX_TYPE_STATUS|PosInterface::TX_TYPE_REFUND|PosInterface::TX_TYPE_CANCEL $txType * - * @param array $contents - * @param string $txType + * @param array $contents + * @param string $txType + * @param string $url * * @return array * * @throws SoapFault * @throws Throwable */ - protected function sendSoapRequest(array $contents, string $txType): array + private function sendSoapRequest(array $contents, string $txType, string $url): array { - $url = $this->getQueryAPIUrl(); $this->logger->debug('sending soap request', [ 'txType' => $txType, 'url' => $url, @@ -308,4 +324,32 @@ private function getCommon3DFormData(KuveytPosAccount $kuveytPosAccount, array $ return $this->requestDataMapper->create3DFormData($this->account, $decodedResponse['form_inputs'], $paymentModel, $txType, $decodedResponse['gateway'], $creditCard); } + + /** + * @phpstan-param PosInterface::TX_TYPE_* $txType + * @phpstan-param PosInterface::MODEL_* $paymentModel + * + * @return string + * + * @throws UnsupportedTransactionTypeException + */ + private function getRequestURIByTransactionType(string $txType, string $paymentModel): string + { + $arr = [ + PosInterface::TX_TYPE_PAY_AUTH => [ + PosInterface::MODEL_NON_SECURE => 'Non3DPayGate', + PosInterface::MODEL_3D_SECURE => 'ThreeDModelProvisionGate', + ], + ]; + + if (!isset($arr[$txType])) { + throw new UnsupportedTransactionTypeException(); + } + + if (!isset($arr[$txType][$paymentModel])) { + throw new UnsupportedTransactionTypeException(); + } + + return $arr[$txType][$paymentModel]; + } } diff --git a/tests/Functional/KuveytPosTest.php b/tests/Functional/KuveytPosTest.php index 5d66b21d..215b70cf 100644 --- a/tests/Functional/KuveytPosTest.php +++ b/tests/Functional/KuveytPosTest.php @@ -138,4 +138,120 @@ function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThro $this->assertNotEmpty($formData); $this->assertTrue($eventIsThrown); } + + public function testNonSecurePaymentSuccess(): array + { + $order = $this->createPaymentOrder(); + + $this->eventDispatcher->addListener( + RequestDataPreparedEvent::class, + function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThrown): void { + $eventIsThrown = true; + $this->assertSame(PosInterface::TX_TYPE_PAY_AUTH, $requestDataPreparedEvent->getTxType()); + $this->assertCount(17, $requestDataPreparedEvent->getRequestData()); + }); + + $this->pos->payment( + PosInterface::MODEL_NON_SECURE, + $order, + PosInterface::TX_TYPE_PAY_AUTH, + $this->card + ); + + $response = $this->pos->getResponse(); + + $this->assertTrue($this->pos->isSuccess()); + + $this->assertIsArray($response); + $this->assertNotEmpty($response); + $this->assertTrue($eventIsThrown); + + return $this->pos->getResponse(); + } + + /** + * @depends testRefundFail + */ + public function testCancelSuccess(array $lastResponse): array + { + $statusOrder = $this->createCancelOrder(\get_class($this->pos), $lastResponse); + + $eventIsThrown = false; + $this->eventDispatcher->addListener( + RequestDataPreparedEvent::class, + function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThrown): void { + $eventIsThrown = true; + $this->assertSame(PosInterface::TX_TYPE_CANCEL, $requestDataPreparedEvent->getTxType()); + $this->assertCount(15, $requestDataPreparedEvent->getRequestData()); + }); + + $this->pos->cancel($statusOrder); + + $this->assertTrue($this->pos->isSuccess()); + $response = $this->pos->getResponse(); + $this->assertIsArray($response); + $this->assertNotEmpty($response); + $this->assertTrue($eventIsThrown); + + return $lastResponse; + } + + /** + * @depends testNonSecurePaymentSuccess + */ + public function testStatusSuccess(array $lastResponse): array + { + $statusOrder = $this->createStatusOrder(\get_class($this->pos), $lastResponse); + + $eventIsThrown = false; + $this->eventDispatcher->addListener( + RequestDataPreparedEvent::class, + function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThrown): void { + $eventIsThrown = true; + $this->assertSame(PosInterface::TX_TYPE_STATUS, $requestDataPreparedEvent->getTxType()); + $this->assertCount(15, $requestDataPreparedEvent->getRequestData()); + }); + + $this->pos->status($statusOrder); + + $this->assertTrue($this->pos->isSuccess()); + $response = $this->pos->getResponse(); + $this->assertIsArray($response); + $this->assertNotEmpty($response); + $this->assertTrue($eventIsThrown); + + return $lastResponse; + } + + /** + * @depends testNonSecurePaymentSuccess + */ + public function testRefundFail(array $lastResponse): array + { + $refundOrder = $this->createRefundOrder(\get_class($this->pos), $lastResponse); + $refundOrder['amount'] = 1.0; + + $eventIsThrown = false; + $this->eventDispatcher->addListener( + RequestDataPreparedEvent::class, + function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThrown): void { + $eventIsThrown = true; + $this->assertSame(PosInterface::TX_TYPE_REFUND, $requestDataPreparedEvent->getTxType()); + $this->assertCount(15, $requestDataPreparedEvent->getRequestData()); + }); + + $this->pos->refund($refundOrder); + + $this->assertFalse($this->pos->isSuccess()); + $response = $this->pos->getResponse(); + $this->assertIsArray($response); + $this->assertNotEmpty($response); + $this->assertTrue($eventIsThrown); + $this->assertSame( + 'İade işlemi, satışla aynı gün içerisinde yapılamaz. İptal işlemi yapabilirsiniz.', + $response['error_message'] + ); + + return $lastResponse; + } } diff --git a/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php b/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php index 92e6a402..62f23375 100644 --- a/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php +++ b/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php @@ -6,18 +6,17 @@ namespace Mews\Pos\Tests\Unit\DataMapper\RequestDataMapper; use Generator; +use Mews\Pos\Crypt\CryptInterface; use Mews\Pos\DataMapper\RequestDataMapper\KuveytPosRequestDataMapper; use Mews\Pos\Entity\Account\KuveytPosAccount; use Mews\Pos\Entity\Card\CreditCardInterface; use Mews\Pos\Exceptions\UnsupportedTransactionTypeException; use Mews\Pos\Factory\AccountFactory; use Mews\Pos\Factory\CreditCardFactory; -use Mews\Pos\Factory\CryptFactory; -use Mews\Pos\Gateways\KuveytPos; use Mews\Pos\PosInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\EventDispatcher\EventDispatcherInterface; -use Psr\Log\NullLogger; /** * @covers \Mews\Pos\DataMapper\RequestDataMapper\KuveytPosRequestDataMapper @@ -31,6 +30,9 @@ class KuveytPosRequestDataMapperTest extends TestCase private KuveytPosRequestDataMapper $requestDataMapper; + /** @var CryptInterface|MockObject */ + private CryptInterface $crypt; + protected function setUp(): void { parent::setUp(); @@ -54,8 +56,8 @@ protected function setUp(): void CreditCardInterface::CARD_TYPE_VISA ); - $crypt = CryptFactory::createGatewayCrypt(KuveytPos::class, new NullLogger()); - $this->requestDataMapper = new KuveytPosRequestDataMapper($dispatcher, $crypt); + $this->crypt = $this->createMock(CryptInterface::class); + $this->requestDataMapper = new KuveytPosRequestDataMapper($dispatcher, $this->crypt); } /** @@ -129,6 +131,10 @@ public function testCreate3DEnrollmentCheckRequestData(array $order, string $txT $account = $this->account; $card = $this->card; + $this->crypt->expects(self::once()) + ->method('create3DHash') + ->willReturn('request-3d-hash'); + $actualData = $this->requestDataMapper->create3DEnrollmentCheckRequestData( $account, $order, @@ -136,7 +142,11 @@ public function testCreate3DEnrollmentCheckRequestData(array $order, string $txT $txType, $card ); - $this->assertEquals($expectedData, $actualData); + + ksort($expectedData); + ksort($actualData); + + $this->assertSame($expectedData, $actualData); } /** @@ -144,7 +154,26 @@ public function testCreate3DEnrollmentCheckRequestData(array $order, string $txT */ public function testCreateCancelRequestData(array $order, array $expected): void { + $this->crypt->expects(self::once()) + ->method('createHash') + ->willReturn('request-hash'); + $actual = $this->requestDataMapper->createCancelRequestData($this->account, $order); + + foreach ($actual as &$item) { + if (is_array($item)) { + ksort($item); + } + } + foreach ($expected as &$item) { + if (is_array($item)) { + ksort($item); + } + } + + \ksort($actual); + \ksort($expected); + $this->assertEquals($expected, $actual); } @@ -153,8 +182,27 @@ public function testCreateCancelRequestData(array $order, array $expected): void */ public function testCreateRefundRequestData(array $order, array $expected): void { + $this->crypt->expects(self::once()) + ->method('createHash') + ->willReturn('request-hash'); + $actual = $this->requestDataMapper->createRefundRequestData($this->account, $order); - $this->assertEquals($expected, $actual); + + foreach ($actual as &$item) { + if (is_array($item)) { + ksort($item); + } + } + foreach ($expected as &$item) { + if (is_array($item)) { + ksort($item); + } + } + + \ksort($actual); + \ksort($expected); + + $this->assertSame($expected, $actual); } /** @@ -162,18 +210,71 @@ public function testCreateRefundRequestData(array $order, array $expected): void */ public function testCreateStatusRequestData(array $order, array $expected): void { + $this->crypt->expects(self::once()) + ->method('createHash') + ->willReturn('request-hash'); + $actual = $this->requestDataMapper->createStatusRequestData($this->account, $order); - $this->assertEquals($expected, $actual); + + foreach ($actual as &$item) { + if (is_array($item)) { + ksort($item); + } + } + foreach ($expected as &$item) { + if (is_array($item)) { + ksort($item); + } + } + + \ksort($actual); + \ksort($expected); + + $this->assertSame($expected, $actual); } /** * @dataProvider create3DPaymentRequestDataDataProvider */ - public function testCreate3DPaymentRequestData(KuveytPosAccount $kuveytPosAccount, array $order, string $txType, array $responseData, array $expectedData): void + public function testCreate3DPaymentRequestData(array $order, string $txType, array $responseData, array $expectedData): void { - $actual = $this->requestDataMapper->create3DPaymentRequestData($kuveytPosAccount, $order, $txType, $responseData); + $this->crypt->expects(self::once()) + ->method('createHash') + ->willReturn('request-hash'); + + $actual = $this->requestDataMapper->create3DPaymentRequestData( + $this->account, + $order, + $txType, + $responseData + ); + + \ksort($actual); + \ksort($expectedData); + + $this->assertSame($expectedData, $actual); + } + + /** + * @dataProvider nonSecurePaymentRequestDataProvider + */ + public function testCreateNonSecurePaymentRequestData(array $order, string $txType, array $expectedData): void + { + $this->crypt->expects(self::once()) + ->method('createHash') + ->willReturn('request-hash'); + + $actualData = $this->requestDataMapper->createNonSecurePaymentRequestData( + $this->account, + $order, + $txType, + $this->card + ); - $this->assertEquals($expectedData, $actual); + ksort($expectedData); + ksort($actualData); + + $this->assertSame($expectedData, $actualData); } public function testGet3DFormData(): void @@ -245,7 +346,7 @@ public static function createCancelRequestDataProvider(): iterable 'VPosMessage' => [ 'APIVersion' => KuveytPosRequestDataMapper::API_VERSION, 'InstallmentMaturityCommisionFlag' => 0, - 'HashData' => 'Om26dd7XpVGq0KyTJBM3TUH4fSU=', + 'HashData' => 'request-hash', 'MerchantId' => '80', 'SubMerchantId' => 0, 'CustomerId' => '400235', @@ -300,7 +401,7 @@ public static function createRefundRequestDataProvider(): Generator 'VPosMessage' => [ 'APIVersion' => KuveytPosRequestDataMapper::API_VERSION, 'InstallmentMaturityCommisionFlag' => 0, - 'HashData' => 'Om26dd7XpVGq0KyTJBM3TUH4fSU=', + 'HashData' => 'request-hash', 'MerchantId' => '80', 'SubMerchantId' => 0, 'CustomerId' => '400235', @@ -351,7 +452,7 @@ public static function createStatusRequestDataProvider(): iterable 'VPosMessage' => [ 'APIVersion' => KuveytPosRequestDataMapper::API_VERSION, 'InstallmentMaturityCommisionFlag' => 0, - 'HashData' => 'RwQ5Sfc6D4Ovy7jvQgf5jGA/rOk=', + 'HashData' => 'request-hash', 'MerchantId' => '80', 'SubMerchantId' => 0, 'CustomerId' => '400235', @@ -381,28 +482,17 @@ public static function createStatusRequestDataProvider(): iterable public static function create3DPaymentRequestDataDataProvider(): array { - $account = AccountFactory::createKuveytPosAccount( - 'kuveytpos', - '80', - 'apiuser', - '400235', - 'Api123' - ); - $order = [ 'id' => '2020110828BC', 'amount' => 1, 'installment' => '0', 'currency' => PosInterface::CURRENCY_TRY, - 'success_url' => 'http://localhost/finansbank-payfor/3d/response.php', - 'fail_url' => 'http://localhost/finansbank-payfor/3d/response.php', 'ip' => '127.0.0.1', 'lang' => PosInterface::LANG_TR, ]; return [ [ - 'account' => $account, 'order' => $order, 'txType' => PosInterface::TX_TYPE_PAY_AUTH, 'responseData' => [ @@ -434,7 +524,7 @@ public static function create3DPaymentRequestDataDataProvider(): array ], 'expected' => [ 'APIVersion' => KuveytPosRequestDataMapper::API_VERSION, - 'HashData' => '9nMtjMKzb7y/hOC4RiDZXkR8uqE=', + 'HashData' => 'request-hash', 'MerchantId' => '80', 'CustomerId' => '400235', 'UserName' => 'apiuser', @@ -448,7 +538,7 @@ public static function create3DPaymentRequestDataDataProvider(): array 'TransactionType' => 'Sale', 'InstallmentCount' => '0', 'Amount' => '100', - 'DisplayAmount' => 10000, + 'DisplayAmount' => '100', 'CurrencyCode' => '0949', 'MerchantOrderId' => '2020110828BC', 'TransactionSecurity' => '3', @@ -476,7 +566,7 @@ public static function create3DEnrollmentCheckRequestDataDataProvider(): array 'MerchantId' => '80', 'UserName' => 'apiuser', 'CustomerId' => '400235', - 'HashData' => 'aEW1KcKuzz2e+oeU36kyEnC65/4=', + 'HashData' => 'request-3d-hash', 'TransactionType' => 'Sale', 'TransactionSecurity' => '3', 'InstallmentCount' => '0', @@ -499,4 +589,39 @@ public static function create3DEnrollmentCheckRequestDataDataProvider(): array ], ]; } + + public static function nonSecurePaymentRequestDataProvider(): array + { + return [ + 'pay_no_installment' => [ + 'order' => [ + 'id' => '2020110828BC', + 'amount' => 1.10, + 'ip' => '127.0.0.1', + 'installment' => 0, + 'currency' => PosInterface::CURRENCY_TRY, + ], + 'txType' => PosInterface::TX_TYPE_PAY_AUTH, + 'expected' => [ + 'APIVersion' => 'TDV2.0.0', + 'Amount' => 110, + 'CardCVV2' => '123', + 'CardExpireDateMonth' => '01', + 'CardExpireDateYear' => '25', + 'CardHolderName' => 'John Doe', + 'CardNumber' => '4155650100416111', + 'CurrencyCode' => '0949', + 'CustomerId' => '400235', + 'DisplayAmount' => 110, + 'HashData' => 'request-hash', + 'InstallmentCount' => '0', + 'MerchantId' => '80', + 'MerchantOrderId' => '2020110828BC', + 'TransactionSecurity' => '1', + 'TransactionType' => 'Sale', + 'UserName' => 'apiuser', + ], + ], + ]; + } } diff --git a/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php b/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php index c76115a6..426eec8b 100644 --- a/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php +++ b/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php @@ -143,7 +143,11 @@ public function testMapStatusResponse(array $responseData, array $expectedData): unset($actualData['refund_time'], $expectedData['refund_time']); unset($actualData['cancel_time'], $expectedData['cancel_time']); + $this->assertArrayHasKey('all', $actualData); + $this->assertIsArray($actualData['all']); + $this->assertNotEmpty($actualData['all']); unset($actualData['all']); + \ksort($expectedData); \ksort($actualData); $this->assertSame($expectedData, $actualData); @@ -940,7 +944,7 @@ public static function statusTestDataProvider(): iterable 'capture' => true, 'remote_order_id' => '114293600', 'currency' => PosInterface::CURRENCY_TRY, - 'capture_time' => null, + 'capture_time' => new \DateTimeImmutable('2023-07-08T23:45:35.283'), 'transaction_time' => new \DateTimeImmutable('2023-07-08T23:45:15.797'), 'cancel_time' => null, 'refund_amount' => null, @@ -989,6 +993,188 @@ public static function statusTestDataProvider(): iterable 'cancel_time' => null, ], ]; + yield 'tdv2_success_tx_pay' => [ + 'responseData' => [ + 'GetMerchantOrderDetailResult' => [ + 'Results' => [], + 'Success' => true, + 'Value' => [ + 'OrderContract' => [ + 'IsSelected' => false, + 'IsSelectable' => true, + 'OrderId' => 155768281, + 'MerchantOrderId' => '20240424C7A5', + 'MerchantId' => 496, + 'CardHolderName' => 'John Doe', + 'CardType' => 'MasterCard', + 'CardNumber' => '518896******2544', + 'OrderDate' => '2024-04-24T16:03:42.07', + 'OrderStatus' => 1, + 'LastOrderStatus' => 1, + 'OrderType' => 1, + 'TransactionStatus' => 1, + 'FirstAmount' => '10.01', + 'CancelAmount' => '0.00', + 'DrawbackAmount' => '0.00', + 'ClosedAmount' => '0.00', + 'FEC' => '0949', + 'VPSEntryMode' => 'ECOM', + 'InstallmentCount' => 0, + 'TransactionSecurity' => 3, + 'ResponseCode' => '00', + 'ResponseExplain' => 'İşlem gerçekleştirildi.', + 'EndOfDayStatus' => 1, + 'TransactionSide' => 'Auto', + 'CardHolderIPAddress' => '', + 'MerchantIPAddress' => '45.130.202.59', + 'MerchantUserName' => 'apitest', + 'ProvNumber' => '050990', + 'BatchId' => 545, + 'CardExpireDate' => '2506', + 'PosTerminalId' => 'VP008759', + 'Explain' => '', + 'Explain2' => '', + 'Explain3' => '', + 'RRN' => '411516539768', + 'Stan' => '539768', + 'UserName' => 'vposuser', + 'HostName' => 'STD8BOATEST1', + 'SystemDate' => '2024-04-24T16:03:42.077', + 'UpdateUserName' => 'vposuser', + 'UpdateHostName' => 'STD8BOATEST2', + 'UpdateSystemDate' => '2024-04-24T16:04:12.373', + 'EndOfDayDate' => null, + 'HostIP' => '172.20.8.84', + 'FECAmount' => '0', + 'IdentityTaxNumber' => '', + 'QueryId' => '0', + 'DebtId' => '0', + 'DebtorName' => '', + 'Period' => '', + 'SurchargeAmount' => '0', + 'SGKDebtAmount' => '0', + 'DeferringCount' => null, + ], + ], + ], + ], + 'expectedData' => [ + 'auth_code' => '050990', + 'capture' => true, + 'capture_amount' => 10.01, + 'currency' => 'TRY', + 'error_code' => null, + 'error_message' => null, + 'first_amount' => 10.01, + 'installment_count' => 0, + 'masked_number' => '518896******2544', + 'order_id' => '20240424C7A5', + 'order_status' => 'PAYMENT_COMPLETED', + 'proc_return_code' => '00', + 'ref_ret_num' => '411516539768', + 'refund_amount' => null, + 'remote_order_id' => '155768281', + 'status' => 'approved', + 'status_detail' => null, + 'transaction_id' => '539768', + 'transaction_type' => null, + 'transaction_time' => new \DateTimeImmutable('2024-04-24T16:03:42.07'), + 'capture_time' => new \DateTimeImmutable('2024-04-24T16:04:12.373'), + 'refund_time' => null, + 'cancel_time' => null, + ], + ]; + yield 'tdv2_success_tx_pay_then_cancel' => [ + 'responseData' => [ + 'GetMerchantOrderDetailResult' => [ + 'Results' => [], + 'Success' => true, + 'Value' => [ + 'OrderContract' => [ + 'IsSelected' => false, + 'IsSelectable' => true, + 'OrderId' => 155768281, + 'MerchantOrderId' => '20240424C7A5', + 'MerchantId' => 496, + 'CardHolderName' => 'John Doe', + 'CardType' => 'MasterCard', + 'CardNumber' => '518896******2544', + 'OrderDate' => '2024-04-24T16:03:42.07', + 'OrderStatus' => 1, + 'LastOrderStatus' => 6, + 'OrderType' => 1, + 'TransactionStatus' => 1, + 'FirstAmount' => '10.01', + 'CancelAmount' => '10.01', + 'DrawbackAmount' => '0.00', + 'ClosedAmount' => '0.00', + 'FEC' => '0949', + 'VPSEntryMode' => 'ECOM', + 'InstallmentCount' => 0, + 'TransactionSecurity' => 3, + 'ResponseCode' => '00', + 'ResponseExplain' => 'İşlem gerçekleştirildi.', + 'EndOfDayStatus' => 1, + 'TransactionSide' => 'Auto', + 'CardHolderIPAddress' => '', + 'MerchantIPAddress' => '45.130.202.59', + 'MerchantUserName' => 'apitest', + 'ProvNumber' => '050990', + 'BatchId' => 545, + 'CardExpireDate' => '2506', + 'PosTerminalId' => 'VP008759', + 'Explain' => '', + 'Explain2' => '', + 'Explain3' => '', + 'RRN' => '411516539768', + 'Stan' => '539768', + 'UserName' => 'vposuser', + 'HostName' => 'STD8BOATEST1', + 'SystemDate' => '2024-04-24T16:03:42.077', + 'UpdateUserName' => 'webgate', + 'UpdateHostName' => 'STD8BOATEST1', + 'UpdateSystemDate' => '2024-04-24T16:09:27.067', + 'EndOfDayDate' => null, + 'HostIP' => '172.20.8.84', + 'FECAmount' => '0', + 'IdentityTaxNumber' => '', + 'QueryId' => '0', + 'DebtId' => '0', + 'DebtorName' => '', + 'Period' => '', + 'SurchargeAmount' => '0', + 'SGKDebtAmount' => '0', + 'DeferringCount' => null, + ], + ], + ], + ], + 'expectedData' => [ + 'auth_code' => '050990', + 'capture' => null, + 'capture_amount' => null, + 'currency' => 'TRY', + 'error_code' => null, + 'error_message' => null, + 'first_amount' => 10.01, + 'installment_count' => 0, + 'masked_number' => '518896******2544', + 'order_id' => '20240424C7A5', + 'order_status' => 'CANCELED', + 'proc_return_code' => '00', + 'ref_ret_num' => '411516539768', + 'refund_amount' => null, + 'remote_order_id' => '155768281', + 'status' => 'approved', + 'status_detail' => null, + 'transaction_id' => '539768', + 'transaction_type' => null, + 'transaction_time' => new \DateTimeImmutable('2024-04-24T16:03:42.07'), + 'capture_time' => null, + 'refund_time' => null, + 'cancel_time' => new \DateTimeImmutable('2024-04-24T16:09:27.067'), + ], + ]; } public static function cancelTestDataProvider(): iterable diff --git a/tests/Unit/Gateways/KuveytPosTest.php b/tests/Unit/Gateways/KuveytPosTest.php index e092c6d1..d8a0c837 100644 --- a/tests/Unit/Gateways/KuveytPosTest.php +++ b/tests/Unit/Gateways/KuveytPosTest.php @@ -79,7 +79,7 @@ protected function setUp(): void 'name' => 'kuveyt-pos', 'class' => KuveytPos::class, 'gateway_endpoints' => [ - 'payment_api' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelProvisionGate', + 'payment_api' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home', 'gateway_3d' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelPayGate', 'query_api' => 'https://boatest.kuveytturk.com.tr/BOA.Integration.WCFService/BOA.Integration.VirtualPos/VirtualPosService.svc?wsdl', ], @@ -164,6 +164,26 @@ public function testSetTestMode(): void $this->assertTrue($this->pos->isTestMode()); } + /** + * @dataProvider getApiUrlDataProvider + */ + public function testGetApiURL(?string $txType, ?string $paymentModel, string $expected): void + { + $actual = $this->pos->getApiURL($txType, $paymentModel); + + $this->assertSame($expected, $actual); + } + + /** + * @dataProvider getApiUrlExceptionDataProvider + */ + public function testGetApiURLException(string $txType, string $paymentModel, string $exceptionClass): void + { + $this->expectException($exceptionClass); + + $this->pos->getApiURL($txType, $paymentModel); + } + /** * @return void */ @@ -253,7 +273,7 @@ public function testMake3DPayment( $this->prepareClient( $this->httpClientMock, 'response-body', - $this->config['gateway_endpoints']['payment_api'], + 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelProvisionGate', [ 'body' => 'request-body', 'headers' => [ @@ -308,10 +328,35 @@ public function testMake3DPayment( $this->assertSame($isSuccess, $this->pos->isSuccess()); } - public function testMakeRegularPayment(): void + /** + * @dataProvider makeRegularPaymentDataProvider + */ + public function testMakeRegularPayment(array $order, string $txType, string $apiUrl): void { - $this->expectException(UnsupportedPaymentModelException::class); - $this->pos->makeRegularPayment([], $this->card, PosInterface::TX_TYPE_PAY_AUTH); + $account = $this->pos->getAccount(); + $card = $this->card; + $this->requestMapperMock->expects(self::once()) + ->method('createNonSecurePaymentRequestData') + ->with($account, $order, $txType, $card) + ->willReturn(['createNonSecurePaymentRequestData']); + + $paymentResponse = ['paymentResponse']; + + $this->configureClientResponse( + $txType, + $apiUrl, + ['createNonSecurePaymentRequestData'], + 'request-body', + 'response-body', + $paymentResponse + ); + + $this->responseMapperMock->expects(self::once()) + ->method('mapPaymentResponse') + ->with($paymentResponse, $txType, $order) + ->willReturn(['result']); + + $this->pos->makeRegularPayment($order, $card, $txType); } public function testMakeRegularPostAuthPayment(): void @@ -405,7 +450,7 @@ public static function makeRegularPaymentDataProvider(): array 'id' => '2020110828BC', ], 'txType' => PosInterface::TX_TYPE_PAY_AUTH, - 'api_url' => 'https://boa.vakifkatilim.com.tr/VirtualPOS.Gateway/Home/Non3DPayGate', + 'api_url' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/Non3DPayGate', ], ]; } @@ -443,4 +488,51 @@ private function configureClientResponse( $statusCode ); } + + public static function getApiUrlDataProvider(): array + { + return [ + [ + 'txType' => PosInterface::TX_TYPE_PAY_AUTH, + 'paymentModel' => PosInterface::MODEL_3D_SECURE, + 'expected' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/ThreeDModelProvisionGate', + ], + [ + 'txType' => PosInterface::TX_TYPE_PAY_AUTH, + 'paymentModel' => PosInterface::MODEL_NON_SECURE, + 'expected' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home/Non3DPayGate', + ], + [ + 'txType' => PosInterface::TX_TYPE_REFUND, + 'paymentModel' => PosInterface::MODEL_NON_SECURE, + 'expected' => 'https://boatest.kuveytturk.com.tr/BOA.Integration.WCFService/BOA.Integration.VirtualPos/VirtualPosService.svc?wsdl', + ], + [ + 'txType' => PosInterface::TX_TYPE_CANCEL, + 'paymentModel' => PosInterface::MODEL_NON_SECURE, + 'expected' => 'https://boatest.kuveytturk.com.tr/BOA.Integration.WCFService/BOA.Integration.VirtualPos/VirtualPosService.svc?wsdl', + ], + [ + 'txType' => PosInterface::TX_TYPE_STATUS, + 'paymentModel' => PosInterface::MODEL_NON_SECURE, + 'expected' => 'https://boatest.kuveytturk.com.tr/BOA.Integration.WCFService/BOA.Integration.VirtualPos/VirtualPosService.svc?wsdl', + ], + [ + 'txType' => null, + 'paymentModel' => null, + 'expected' => 'https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home', + ], + ]; + } + + public static function getApiUrlExceptionDataProvider(): array + { + return [ + [ + 'txType' => PosInterface::TX_TYPE_PAY_AUTH, + 'paymentModel' => PosInterface::MODEL_3D_PAY, + 'exception_class' => UnsupportedTransactionTypeException::class, + ], + ]; + } } From 2e5a2886ff7d7b11ff971c12b1b6ee4506af85ca Mon Sep 17 00:00:00 2001 From: Nuryagdy Mustapayev Date: Fri, 26 Apr 2024 10:13:08 +0200 Subject: [PATCH 4/4] kuveyt pos improve status response mapping --- .../KuveytPosResponseDataMapper.php | 5 +- .../KuveytPosResponseDataMapperTest.php | 91 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php b/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php index 82f983af..fe48cd7f 100644 --- a/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php +++ b/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php @@ -29,7 +29,8 @@ class KuveytPosResponseDataMapper extends AbstractResponseDataMapper */ protected array $orderStatusMappings = [ 1 => PosInterface::PAYMENT_STATUS_PAYMENT_COMPLETED, - 5 => PosInterface::PAYMENT_STATUS_FULLY_REFUNDED, + 4 => PosInterface::PAYMENT_STATUS_FULLY_REFUNDED, + 5 => PosInterface::PAYMENT_STATUS_PARTIALLY_REFUNDED, 6 => PosInterface::PAYMENT_STATUS_CANCELED, ]; @@ -187,6 +188,8 @@ public function mapStatusResponse(array $rawResponseData): array } } elseif (PosInterface::PAYMENT_STATUS_CANCELED === $defaultResponse['order_status']) { $defaultResponse['cancel_time'] = new \DateTimeImmutable($orderContract['UpdateSystemDate']); + } elseif (PosInterface::PAYMENT_STATUS_FULLY_REFUNDED === $defaultResponse['order_status']) { + $defaultResponse['refund_time'] = new \DateTimeImmutable($orderContract['UpdateSystemDate']); } } diff --git a/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php b/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php index 426eec8b..4883abb9 100644 --- a/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php +++ b/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php @@ -1175,6 +1175,97 @@ public static function statusTestDataProvider(): iterable 'cancel_time' => new \DateTimeImmutable('2024-04-24T16:09:27.067'), ], ]; + yield 'tdv2_success_tx_pay_then_refund' => [ + 'responseData' => [ + 'GetMerchantOrderDetailResult' => [ + 'Results' => [], + 'Success' => true, + 'Value' => [ + 'OrderContract' => [ + 'IsSelected' => false, + 'IsSelectable' => true, + 'OrderId' => 155768298, + 'MerchantOrderId' => '202404240DEE', + 'MerchantId' => 496, + 'CardHolderName' => 'John Doe', + 'CardType' => 'MasterCard', + 'CardNumber' => '518896******2544', + 'OrderDate' => '2024-04-24T16:33:44.01', + 'OrderStatus' => 1, + 'LastOrderStatus' => 4, + 'OrderType' => 1, + 'TransactionStatus' => 1, + 'FirstAmount' => '10.01', + 'CancelAmount' => '0.00', + 'DrawbackAmount' => '10.01', + 'ClosedAmount' => '0.00', + 'FEC' => '0949', + 'VPSEntryMode' => 'ECOM', + 'InstallmentCount' => 0, + 'TransactionSecurity' => 1, + 'ResponseCode' => '00', + 'ResponseExplain' => 'İşlem gerçekleştirildi.', + 'EndOfDayStatus' => 2, + 'TransactionSide' => 'Auto', + 'CardHolderIPAddress' => '', + 'MerchantIPAddress' => '45.130.202.55', + 'MerchantUserName' => 'apitest', + 'ProvNumber' => '051004', + 'BatchId' => 545, + 'CardExpireDate' => '2506', + 'PosTerminalId' => 'VP008759', + 'Explain' => '', + 'Explain2' => '', + 'Explain3' => '', + 'RRN' => '411516539788', + 'Stan' => '539788', + 'UserName' => 'vposuser', + 'HostName' => 'STD8BOATEST2', + 'SystemDate' => '2024-04-24T16:33:44.02', + 'UpdateUserName' => 'webgate', + 'UpdateHostName' => 'STD8BOATEST2', + 'UpdateSystemDate' => '2024-04-26T10:59:49.443', + 'EndOfDayDate' => '2024-04-24T17:08:46.15', + 'HostIP' => '172.20.8.85', + 'FECAmount' => '0', + 'IdentityTaxNumber' => '', + 'QueryId' => '0', + 'DebtId' => '0', + 'DebtorName' => '', + 'Period' => '', + 'SurchargeAmount' => '0', + 'SGKDebtAmount' => '0', + 'DeferringCount' => null, + ], + ], + ], + ], + 'expectedData' => [ + 'auth_code' => '051004', + 'capture' => null, + 'capture_amount' => null, + 'currency' => 'TRY', + 'error_code' => null, + 'error_message' => null, + 'first_amount' => 10.01, + 'installment_count' => 0, + 'masked_number' => '518896******2544', + 'order_id' => '202404240DEE', + 'order_status' => 'FULLY_REFUNDED', + 'proc_return_code' => '00', + 'ref_ret_num' => '411516539788', + 'refund_amount' => null, + 'remote_order_id' => '155768298', + 'status' => 'approved', + 'status_detail' => null, + 'transaction_id' => '539788', + 'transaction_type' => null, + 'transaction_time' => new \DateTimeImmutable('2024-04-24T16:33:44.01'), + 'capture_time' => null, + 'refund_time' => new \DateTimeImmutable('2024-04-26T10:59:49.443'), + 'cancel_time' => null, + ], + ]; } public static function cancelTestDataProvider(): iterable