diff --git a/config/pos_production.php b/config/pos_production.php index be3e6d7b..213d1e8b 100644 --- a/config/pos_production.php +++ b/config/pos_production.php @@ -152,8 +152,8 @@ 'name' => 'kuveyt-pos', 'class' => Mews\Pos\Gateways\KuveytPos::class, 'gateway_endpoints' => [ - 'payment_api' => 'https://boa.kuveytturk.com.tr/sanalposservice/Home/ThreeDModelProvisionGate', - 'gateway_3d' => 'https://boa.kuveytturk.com.tr/sanalposservice/Home/ThreeDModelPayGate', + 'payment_api' => 'https://sanalpos.kuveytturk.com.tr/ServiceGateWay/Home/ThreeDModelProvisionGate', + '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/docs/THREED-PAYMENT-EXAMPLE.md b/docs/THREED-PAYMENT-EXAMPLE.md index 83be25be..c5ca8790 100644 --- a/docs/THREED-PAYMENT-EXAMPLE.md +++ b/docs/THREED-PAYMENT-EXAMPLE.md @@ -172,6 +172,39 @@ try { } }); + // KuveytVos TDV2.0.0 icin ozel biri durum + $eventDispatcher->addListener( + RequestDataPreparedEvent::class, + function (RequestDataPreparedEvent $requestDataPreparedEvent) use ($pos): void { + if (get_class($pos) !== \Mews\Pos\Gateways\KuveytPos::class) { + return; + } + // KuveytPos TDV2.0.0 icin zorunlu eklenmesi gereken ekstra alanlar: + $additionalRequestDataForKuveyt = [ + 'DeviceData' => [ + //2 karakter olmalıdır. 01-Mobil, 02-Web Browser için kullanılmalıdır. + 'DeviceChannel' => '02', + ], + 'CardHolderData' => [ + 'BillAddrCity' => 'İstanbul', + // ISO 3166-1 sayısal üç haneli ülke kodu standardı kullanılmalıdır. + 'BillAddrCountry' => '792', + 'BillAddrLine1' => 'XXX Mahallesi XXX Caddesi No 55 Daire 1', + 'BillAddrPostCode' => '34000', + // ISO 3166-2'de tanımlı olan il/eyalet kodu olmalıdır. + 'BillAddrState' => '40', + 'Email' => 'xxxxx@gmail.com', + 'MobilePhone' => [ + 'Cc' => '90', + 'Subscriber' => '1234567899', + ], + ], + ]; + $requestData = $requestDataPreparedEvent->getRequestData(); + $requestData = array_merge_recursive($requestData, $additionalRequestDataForKuveyt); + $requestDataPreparedEvent->setRequestData($requestData); + }); + $formData = $pos->get3DFormData( $order, $paymentModel, diff --git a/examples/_common-codes/3d/form.php b/examples/_common-codes/3d/form.php index 250f7841..c5799c8d 100644 --- a/examples/_common-codes/3d/form.php +++ b/examples/_common-codes/3d/form.php @@ -56,6 +56,71 @@ */ }); + // KuveytVos TDV2.0.0 icin ozel biri durum + $eventDispatcher->addListener( + RequestDataPreparedEvent::class, + function (RequestDataPreparedEvent $requestDataPreparedEvent) use ($pos): void { + if (get_class($pos) !== \Mews\Pos\Gateways\KuveytPos::class) { + return; + } + // KuveytPos TDV2.0.0 icin zorunlu eklenmesi gereken ekstra alanlar: + $additionalRequestDataForKuveyt = [ + 'DeviceData' => [ + /** + * DeviceChannel : DeviceData alanı içerisinde gönderilmesi beklenen işlemin yapıldığı cihaz bilgisi. + * 2 karakter olmalıdır. 01-Mobil, 02-Web Browser için kullanılmalıdır. + */ + 'DeviceChannel' => '02', + ], + 'CardHolderData' => [ + /** + * BillAddrCity: Kullanılan kart ile ilişkili kart hamilinin fatura adres şehri. + * Maksimum 50 karakter uzunluğunda olmalıdır. + */ + 'BillAddrCity' => 'İstanbul', + /** + * BillAddrCountry Kullanılan kart ile ilişkili kart hamilinin fatura adresindeki ülke kodu. + * Maksimum 3 karakter uzunluğunda olmalıdır. + * ISO 3166-1 sayısal üç haneli ülke kodu standardı kullanılmalıdır. + */ + 'BillAddrCountry' => '792', + /** + * BillAddrLine1: Kullanılan kart ile ilişkili kart hamilinin teslimat adresinde yer alan sokak vb. bilgileri içeren açık adresi. + * Maksimum 150 karakter uzunluğunda olmalıdır. + */ + 'BillAddrLine1' => 'XXX Mahallesi XXX Caddesi No 55 Daire 1', + /** + * BillAddrPostCode: Kullanılan kart ile ilişkili kart hamilinin fatura adresindeki posta kodu. + */ + 'BillAddrPostCode' => '34000', + /** + * BillAddrState: CardHolderData alanı içerisinde gönderilmesi beklenen ödemede kullanılan kart ile ilişkili kart hamilinin fatura adresindeki il veya eyalet bilgisi kodu. + * ISO 3166-2'de tanımlı olan il/eyalet kodu olmalıdır. + */ + 'BillAddrState' => '40', + /** + * Email: Kullanılan kart ile ilişkili kart hamilinin iş yerinde oluşturduğu hesapta kullandığı email adresi. + * Maksimum 254 karakter uzunluğunda olmalıdır. + */ + 'Email' => 'xxxxx@gmail.com', + 'MobilePhone' => [ + /** + * Cc: Kullanılan kart ile ilişkili kart hamilinin cep telefonuna ait ülke kodu. 1-3 karakter uzunluğunda olmalıdır. + */ + 'Cc' => '90', + /** + * Subscriber: Kullanılan kart ile ilişkili kart hamilinin cep telefonuna ait abone numarası. + * Maksimum 15 karakter uzunluğunda olmalıdır. + */ + 'Subscriber' => '1234567899', + ], + ], + ]; + $requestData = $requestDataPreparedEvent->getRequestData(); + $requestData = array_merge_recursive($requestData, $additionalRequestDataForKuveyt); + $requestDataPreparedEvent->setRequestData($requestData); + }); + /** * Bu Event'i dinleyerek 3D formun hash verisi hesaplanmadan önce formun input array içireğini güncelleyebilirsiniz. */ diff --git a/examples/kuveytpos/3d/_config.php b/examples/kuveytpos/3d/_config.php index 792e5d00..f1be35f3 100644 --- a/examples/kuveytpos/3d/_config.php +++ b/examples/kuveytpos/3d/_config.php @@ -9,9 +9,9 @@ $account = \Mews\Pos\Factory\AccountFactory::createKuveytPosAccount( 'kuveytpos', '496', - 'apiuser1', + 'apitest', '400235', - 'Api1232', + 'api123', PosInterface::MODEL_3D_SECURE ); diff --git a/examples/kuveytpos/_payment_config.php b/examples/kuveytpos/_payment_config.php index d73e017d..202cd9e8 100644 --- a/examples/kuveytpos/_payment_config.php +++ b/examples/kuveytpos/_payment_config.php @@ -9,11 +9,12 @@ $testCards = [ 'visa1' => [ - 'number' => '4155650100416111', + //Kart Doğrulama Şifresi: 123456 + 'number' => '5188961939192544', 'year' => '25', - 'month' => '1', - 'cvv' => '123', + 'month' => '06', + 'cvv' => '929', 'name' => 'John Doe', - 'type' => CreditCardInterface::CARD_TYPE_VISA, + 'type' => CreditCardInterface::CARD_TYPE_MASTERCARD, ], ]; diff --git a/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php b/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php index c1275144..fc37055e 100644 --- a/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php +++ b/src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php @@ -19,7 +19,7 @@ class KuveytPosRequestDataMapper extends AbstractRequestDataMapper { /** @var string */ - public const API_VERSION = '1.0.0'; + public const API_VERSION = 'TDV2.0.0'; /** @var string */ public const CREDIT_CARD_EXP_YEAR_FORMAT = 'y'; @@ -39,10 +39,10 @@ class KuveytPosRequestDataMapper extends AbstractRequestDataMapper * {@inheritDoc} */ protected array $txTypeMappings = [ - PosInterface::TX_TYPE_PAY_AUTH => 'Sale', - PosInterface::TX_TYPE_CANCEL => 'SaleReversal', - PosInterface::TX_TYPE_STATUS => 'GetMerchantOrderDetail', - PosInterface::TX_TYPE_REFUND => 'PartialDrawback', // Also there is a "Drawback" + PosInterface::TX_TYPE_PAY_AUTH => 'Sale', + PosInterface::TX_TYPE_CANCEL => 'SaleReversal', + PosInterface::TX_TYPE_STATUS => 'GetMerchantOrderDetail', + PosInterface::TX_TYPE_REFUND => 'Drawback', // Also there is a "PartialDrawback" ]; /** @@ -133,6 +133,9 @@ public function create3DEnrollmentCheckRequestData(KuveytPosAccount $kuveytPosAc 'MerchantOrderId' => $order['id'], 'OkUrl' => $order['success_url'], 'FailUrl' => $order['fail_url'], + 'DeviceData' => [ + 'ClientIP' => $order['ip'], + ], ]; if ($creditCard instanceof CreditCardInterface) { diff --git a/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php b/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php index df42cb15..8082c004 100644 --- a/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php +++ b/src/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapper.php @@ -146,6 +146,12 @@ public function mapStatusResponse(array $rawResponseData): array $defaultResponse = $this->getDefaultStatusResponse($rawResponseData); if (!isset($data['OrderContract'])) { + if (isset($rawResponseData['GetMerchantOrderDetailResult']['Results']['Result'])) { + $rawResult = $rawResponseData['GetMerchantOrderDetailResult']['Results']['Result']; + $defaultResponse['error_code'] = $rawResult['ErrorCode']; + $defaultResponse['error_message'] = $rawResult['ErrorMessage']; + } + return $defaultResponse; } @@ -198,8 +204,9 @@ public function mapRefundResponse(array $rawResponseData): array 'all' => $rawResponseData, ]; + $drawbackResult = $rawResponseData['PartialDrawbackResult'] ?? $rawResponseData['DrawBackResult']; + $value = $drawbackResult['Value']; - $value = $rawResponseData['PartialDrawbackResult']['Value']; $procReturnCode = $this->getProcReturnCode($value); if (null === $procReturnCode) { @@ -210,11 +217,16 @@ public function mapRefundResponse(array $rawResponseData): array $status = self::TX_APPROVED; } - $responseResults = $rawResponseData['PartialDrawbackResult']['Results']; + $responseResults = $drawbackResult['Results']; if (self::TX_APPROVED !== $status && isset($responseResults['Result']) && [] !== $responseResults['Result']) { - $responseResult = $responseResults['Result'][0]; - $result['error_code'] = $responseResult['ErrorCode']; - $result['error_message'] = $responseResult['ErrorMessage']; + if (isset($responseResults['Result'][0])) { + $responseResult = $responseResults['Result'][0]; + } else { + $responseResult = $responseResults['Result']; + } + $result['proc_return_code'] = $procReturnCode; + $result['error_code'] = $responseResult['ErrorCode'] ?? $procReturnCode; + $result['error_message'] = $responseResult['ErrorMessage']; return $result; } @@ -269,9 +281,14 @@ public function mapCancelResponse(array $rawResponseData): array $responseResults = $rawResponseData['SaleReversalResult']['Results']; if (self::TX_APPROVED !== $status && isset($responseResults['Result']) && [] !== $responseResults['Result']) { - $responseResult = $responseResults['Result'][0]; - $result['error_code'] = $responseResult['ErrorCode']; - $result['error_message'] = $responseResult['ErrorMessage']; + if (isset($responseResults['Result'][0])) { + $responseResult = $responseResults['Result'][0]; + } else { + $responseResult = $responseResults['Result']; + } + $result['proc_return_code'] = $procReturnCode; + $result['error_code'] = $responseResult['ErrorCode'] ?? $procReturnCode; + $result['error_message'] = $responseResult['ErrorMessage']; return $result; } diff --git a/src/Gateways/KuveytPos.php b/src/Gateways/KuveytPos.php index 7d3a3e6f..5fdfecbd 100644 --- a/src/Gateways/KuveytPos.php +++ b/src/Gateways/KuveytPos.php @@ -257,7 +257,13 @@ protected function sendSoapRequest(array $contents, string $txType, string $url throw new RuntimeException('Bankaya istek başarısız!'); } - return $this->serializer->decode($result, $txType); + $encodedResult = \json_encode($result); + + if (false === $encodedResult) { + return []; + } + + return $this->serializer->decode($encodedResult, $txType); } /** diff --git a/tests/Functional/KuveytPosTest.php b/tests/Functional/KuveytPosTest.php new file mode 100644 index 00000000..5d66b21d --- /dev/null +++ b/tests/Functional/KuveytPosTest.php @@ -0,0 +1,141 @@ +eventDispatcher = new EventDispatcher(); + + $this->pos = PosFactory::createPosGateway($account, $config, $this->eventDispatcher); + + $this->pos->setTestMode(true); + + $this->card = CreditCardFactory::createForGateway( + $this->pos, + '5188961939192544', + '25', + '06', + '929', + 'John Doe', + CreditCardInterface::CARD_TYPE_MASTERCARD + ); + } + + /** + * NOT: sadece Turkiye IPsiyle istek gonderince cevap alabiliyoruz. + * @return void + */ + public function testCreate3DFormData(): void + { + $order = $this->createPaymentOrder(); + + $eventIsThrown = false; + $this->eventDispatcher->addListener( + RequestDataPreparedEvent::class, + function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThrown): void { + $eventIsThrown = true; + $additionalRequestDataForKuveyt = [ + 'DeviceData' => [ + /** + * DeviceChannel : DeviceData alanı içerisinde gönderilmesi beklenen işlemin yapıldığı cihaz bilgisi. + * 2 karakter olmalıdır. 01-Mobil, 02-Web Browser için kullanılmalıdır. + */ + 'DeviceChannel' => '02', + ], + 'CardHolderData' => [ + /** + * BillAddrCity: Kullanılan kart ile ilişkili kart hamilinin fatura adres şehri. + * Maksimum 50 karakter uzunluğunda olmalıdır. + */ + 'BillAddrCity' => 'İstanbul', + /** + * BillAddrCountry Kullanılan kart ile ilişkili kart hamilinin fatura adresindeki ülke kodu. + * Maksimum 3 karakter uzunluğunda olmalıdır. + * ISO 3166-1 sayısal üç haneli ülke kodu standardı kullanılmalıdır. + */ + 'BillAddrCountry' => '792', + /** + * BillAddrLine1: Kullanılan kart ile ilişkili kart hamilinin teslimat adresinde yer alan sokak vb. bilgileri içeren açık adresi. + * Maksimum 150 karakter uzunluğunda olmalıdır. + */ + 'BillAddrLine1' => 'XXX Mahallesi XXX Caddesi No 55 Daire 1', + /** + * BillAddrPostCode: Kullanılan kart ile ilişkili kart hamilinin fatura adresindeki posta kodu. + */ + 'BillAddrPostCode' => '34000', + /** + * BillAddrState: CardHolderData alanı içerisinde gönderilmesi beklenen ödemede kullanılan kart ile ilişkili kart hamilinin fatura adresindeki il veya eyalet bilgisi kodu. + * ISO 3166-2'de tanımlı olan il/eyalet kodu olmalıdır. + */ + 'BillAddrState' => '40', + /** + * Email: Kullanılan kart ile ilişkili kart hamilinin iş yerinde oluşturduğu hesapta kullandığı email adresi. + * Maksimum 254 karakter uzunluğunda olmalıdır. + */ + 'Email' => 'xxxxx@gmail.com', + 'MobilePhone' => [ + /** + * Cc: Kullanılan kart ile ilişkili kart hamilinin cep telefonuna ait ülke kodu. 1-3 karakter uzunluğunda olmalıdır. + */ + 'Cc' => '90', + /** + * Subscriber: Kullanılan kart ile ilişkili kart hamilinin cep telefonuna ait abone numarası. + * Maksimum 15 karakter uzunluğunda olmalıdır. + */ + 'Subscriber' => '1234567899', + ], + ], + ]; + $requestData = $requestDataPreparedEvent->getRequestData(); + $requestData = array_merge($requestData, $additionalRequestDataForKuveyt); + $requestDataPreparedEvent->setRequestData($requestData); + $this->assertSame(PosInterface::TX_TYPE_PAY_AUTH, $requestDataPreparedEvent->getTxType()); + }); + + $formData = $this->pos->get3DFormData( + $order, + PosInterface::MODEL_3D_SECURE, + PosInterface::TX_TYPE_PAY_AUTH, + $this->card, + ); + + $this->assertIsArray($formData); + $this->assertNotEmpty($formData); + $this->assertTrue($eventIsThrown); + } +} diff --git a/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php b/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php index 7460b45d..55daafd8 100644 --- a/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php +++ b/tests/Unit/DataMapper/RequestDataMapper/KuveytPosRequestDataMapperTest.php @@ -9,13 +9,10 @@ use Mews\Pos\DataMapper\RequestDataMapper\KuveytPosRequestDataMapper; use Mews\Pos\Entity\Account\KuveytPosAccount; use Mews\Pos\Entity\Card\CreditCardInterface; -use Mews\Pos\Exceptions\BankClassNullException; -use Mews\Pos\Exceptions\BankNotFoundException; use Mews\Pos\Exceptions\UnsupportedTransactionTypeException; use Mews\Pos\Factory\AccountFactory; use Mews\Pos\Factory\CreditCardFactory; use Mews\Pos\Factory\CryptFactory; -use Mews\Pos\Factory\PosFactory; use Mews\Pos\Gateways\KuveytPos; use Mews\Pos\PosInterface; use PHPUnit\Framework\TestCase; @@ -27,24 +24,16 @@ */ class KuveytPosRequestDataMapperTest extends TestCase { - public KuveytPosAccount $account; + private KuveytPosAccount $account; private CreditCardInterface $card; private KuveytPosRequestDataMapper $requestDataMapper; - /** - * @return void - * - * @throws BankClassNullException - * @throws BankNotFoundException - */ protected function setUp(): void { parent::setUp(); - $config = require __DIR__.'/../../../../config/pos_test.php'; - $this->account = AccountFactory::createKuveytPosAccount( 'kuveytpos', '80', @@ -54,10 +43,8 @@ protected function setUp(): void ); $dispatcher = $this->createMock(EventDispatcherInterface::class); - $pos = PosFactory::createPosGateway($this->account, $config, $dispatcher); - $this->card = CreditCardFactory::createForGateway( - $pos, + $this->card = CreditCardFactory::create( '4155650100416111', 25, 1, @@ -281,7 +268,7 @@ public static function createRefundRequestDataProvider(): Generator 'UserName' => 'apiuser', 'CardType' => 'Visa', 'BatchID' => 0, - 'TransactionType' => 'PartialDrawback', + 'TransactionType' => 'Drawback', 'InstallmentCount' => 0, 'Amount' => 101, 'DisplayAmount' => 0, @@ -466,6 +453,9 @@ public static function create3DEnrollmentCheckRequestDataDataProvider(): array 'CardExpireDateYear' => '25', 'CardExpireDateMonth' => '01', 'CardCVV2' => '123', + 'DeviceData' => [ + 'ClientIP' => '127.0.0.1', + ], ], ], ]; diff --git a/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php b/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php index baf6c543..b63dda22 100644 --- a/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php +++ b/tests/Unit/DataMapper/ResponseDataMapper/KuveytPosResponseDataMapperTest.php @@ -97,7 +97,12 @@ public function testMapPaymentResponse(string $txType, array $responseData, arra public function testMapRefundResponse(array $responseData, array $expectedData): void { $actualData = $this->responseDataMapper->mapRefundResponse($responseData); + $this->assertArrayHasKey('all', $actualData); + $this->assertIsArray($actualData['all']); + $this->assertNotEmpty($actualData['all']); unset($actualData['all']); + ksort($actualData); + ksort($expectedData); $this->assertSame($expectedData, $actualData); } @@ -107,7 +112,12 @@ public function testMapRefundResponse(array $responseData, array $expectedData): public function testMapCancelResponse(array $responseData, array $expectedData): void { $actualData = $this->responseDataMapper->mapCancelResponse($responseData); + $this->assertArrayHasKey('all', $actualData); + $this->assertIsArray($actualData['all']); + $this->assertNotEmpty($actualData['all']); unset($actualData['all']); + ksort($actualData); + ksort($expectedData); $this->assertSame($expectedData, $actualData); } @@ -418,7 +428,7 @@ public static function threeDPaymentDataProvider(): array 'installment_count' => null, ], ], - '3d_auth_fail' => [ + '3d_auth_fail' => [ 'order' => [], 'txType' => PosInterface::TX_TYPE_PAY_AUTH, 'threeDResponseData' => [ @@ -457,7 +467,7 @@ public static function threeDPaymentDataProvider(): array 'installment_count' => null, ], ], - 'success1' => [ + 'success1' => [ 'order' => [], 'txType' => PosInterface::TX_TYPE_PAY_AUTH, 'threeDResponseData' => [ @@ -556,6 +566,200 @@ public static function threeDPaymentDataProvider(): array 'installment_count' => 0, ], ], + 'success_tdv2' => [ + 'order' => [], + 'txType' => PosInterface::TX_TYPE_PAY_AUTH, + 'threeDResponseData' => [ + 'VPosMessage' => [ + 'OrderId' => '155767806', + 'OkUrl' => 'http://localhost/kuveytpos/3d/response.php', + 'FailUrl' => 'http://localhost/kuveytpos/3d/response.php', + 'MerchantId' => '496', + 'SubMerchantId' => '0', + 'CustomerId' => '400235', + 'UserName' => '', + 'HashPassword' => '', + 'CardNumber' => '51889619****2544', + 'IsTemporaryCard' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'BatchID' => '545', + 'InstallmentCount' => '0', + 'Amount' => '101', + 'CancelAmount' => '0', + 'MerchantOrderId' => '2024042111A0', + 'OrderStatus' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'RetryCount' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'FECAmount' => '0', + 'CurrencyCode' => '949', + 'QeryId' => '0', + 'DebtId' => '0', + 'SurchargeAmount' => '0', + 'SGKDebtAmount' => '0', + 'TransactionSecurity' => '3', + 'DeferringCount' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'InstallmentMaturityCommisionFlag' => '0', + 'PaymentId' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'OrderPOSTransactionId' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'TranDate' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'TransactionUserId' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'DeviceData' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'CardHolderData' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + ], + 'IsEnrolled' => 'true', + 'IsVirtual' => 'false', + 'ResponseCode' => '00', + 'ResponseMessage' => 'Kart doğrulandı.', + 'OrderId' => '155767806', + 'TransactionTime' => '0001-01-01T00:00:00', + 'MerchantOrderId' => '2024042111A0', + 'HashData' => 'dlO20ZLqs4W5FfgY0VmUSBUpwoM=', + 'MD' => 'KdipxuB/+AncDNdIKzh5uRh4flnYqSp3drh/2yzaOQraHWjGF+NOkvSjYzbQtRQn', + 'ReferenceId' => '1d2a9d4cc853468090d915943341ad89', + 'MerchantId' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'BusinessKey' => '202404219999000000009059964', + '@xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + '@xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', + ], + 'paymentData' => [ + 'VPosMessage' => [ + 'OrderId' => '155767806', + 'OkUrl' => 'http://localhost/kuveytpos/3d/response.php', + 'FailUrl' => 'http://localhost/kuveytpos/3d/response.php', + 'MerchantId' => '496', + 'SubMerchantId' => '0', + 'CustomerId' => '400235', + 'UserName' => '', + 'HashPassword' => '', + 'CardNumber' => '51889619****2544', + 'IsTemporaryCard' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'BatchID' => '545', + 'InstallmentCount' => '0', + 'Amount' => '101', + 'CancelAmount' => '0', + 'MerchantOrderId' => '2024042111A0', + 'OrderStatus' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'RetryCount' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'FECAmount' => '0', + 'CurrencyCode' => '949', + 'QeryId' => '0', + 'DebtId' => '0', + 'SurchargeAmount' => '0', + 'SGKDebtAmount' => '0', + 'TransactionSecurity' => '3', + 'DeferringCount' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'InstallmentMaturityCommisionFlag' => '0', + 'PaymentId' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'OrderPOSTransactionId' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'TranDate' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'TransactionUserId' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'DeviceData' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'CardHolderData' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + ], + 'IsEnrolled' => 'true', + 'IsVirtual' => 'false', + 'ProvisionNumber' => '050560', + 'RRN' => '411219539222', + 'Stan' => '539222', + 'ResponseCode' => '00', + 'ResponseMessage' => 'OTORİZASYON VERİLDİ', + 'OrderId' => '155767806', + 'TransactionTime' => '2024-04-21T19:01:28.95', + 'MerchantOrderId' => '2024042111A0', + 'HashData' => 'N7zs7LHEy3lx6N2nb0FUG2UcnUw=', + 'MerchantId' => [ + '@xsi:nil' => 'true', + '#' => '', + ], + 'BusinessKey' => '202404219999000000009042173', + '@xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + '@xmlns:xsd' => 'http://www.w3.org/2001/XMLSchema', + ], + 'expectedData' => [ + 'transaction_security' => 'MPI fallback', + 'md_status' => null, + 'tx_status' => null, + 'md_error_message' => null, + 'transaction_id' => '539222', + 'transaction_type' => 'pay', + 'transaction_time' => new \DateTimeImmutable(), + 'auth_code' => '050560', + 'ref_ret_num' => '411219539222', + 'error_message' => null, + 'remote_order_id' => '155767806', + 'order_id' => '2024042111A0', + 'proc_return_code' => '00', + 'status' => 'approved', + 'status_detail' => 'approved', + 'amount' => 1.01, + 'currency' => 'TRY', + 'error_code' => null, + 'masked_number' => '51889619****2544', + 'payment_model' => '3d', + 'installment_count' => 0, + ], + ], ]; } @@ -685,6 +889,47 @@ public static function statusTestDataProvider(): iterable 'installment_count' => 0, ], ]; + yield 'tdv2_fail_hash_error' => [ + 'responseData' => [ + 'GetMerchantOrderDetailResult' => [ + 'Results' => [ + 'Result' => [ + 'ErrorMessage' => 'Şifrelenen veriler (Hashdata) uyuşmamaktadır.', + 'ErrorCode' => 'HashDataError', + 'IsFriendly' => true, + 'Severity' => 'BusinessError', + ], + ], + 'Success' => false, + 'Value' => [ + ], + ], + ], + 'expectedData' => [ + 'auth_code' => null, + 'capture' => null, + 'capture_amount' => null, + 'currency' => null, + 'error_code' => 'HashDataError', + 'error_message' => 'Şifrelenen veriler (Hashdata) uyuşmamaktadır.', + 'first_amount' => null, + 'installment_count' => null, + 'masked_number' => null, + 'order_id' => null, + 'order_status' => null, + 'proc_return_code' => null, + 'ref_ret_num' => null, + 'refund_amount' => null, + 'status' => 'declined', + 'status_detail' => null, + 'transaction_id' => null, + 'transaction_type' => null, + 'transaction_time' => null, + 'capture_time' => null, + 'refund_time' => null, + 'cancel_time' => null, + ], + ]; } public static function cancelTestDataProvider(): iterable @@ -753,7 +998,7 @@ public static function cancelTestDataProvider(): iterable 'status_detail' => null, ], ]; - yield 'fail2' => [ + yield 'fail_already_cancelled' => [ 'responseData' => [ 'SaleReversalResult' => [ 'Results' => [ @@ -787,7 +1032,43 @@ public static function cancelTestDataProvider(): iterable 'expectedData' => [ 'order_id' => null, 'auth_code' => null, - 'proc_return_code' => null, + 'proc_return_code' => 'DbLayerError', + 'transaction_id' => null, + 'currency' => null, + 'error_message' => 'İşlem daha önce iptal edilmiştir.', + 'ref_ret_num' => null, + 'status' => 'declined', + 'error_code' => '21', + 'status_detail' => null, + ], + ]; + yield 'tdv2_fail_already_cancelled' => [ + 'responseData' => [ + 'SaleReversalResult' => [ + 'Results' => [ + 'Result' => [ + 'ErrorMessage' => 'İşlem daha önce iptal edilmiştir.', + 'ErrorCode' => '21', + 'IsFriendly' => true, + 'Severity' => 'BusinessError', + ], + ], + 'Success' => false, + 'Value' => [ + 'IsEnrolled' => false, + 'IsVirtual' => false, + 'ResponseCode' => 'DbLayerError', + 'OrderId' => 0, + 'TransactionTime' => '0001-01-01T00:00:00', + 'MerchantId' => null, + 'BusinessKey' => '0', + ], + ], + ], + 'expectedData' => [ + 'order_id' => null, + 'auth_code' => null, + 'proc_return_code' => 'DbLayerError', 'transaction_id' => null, 'currency' => null, 'error_message' => 'İşlem daha önce iptal edilmiştir.', @@ -865,6 +1146,41 @@ public static function refundTestDataProvider(): iterable 'status_detail' => null, ], ]; + yield 'tdv2_fail_partial_refund_not_allowed' => [ + 'responseData' => [ + 'PartialDrawbackResult' => [ + 'Results' => [ + 'Result' => [ + 'ErrorMessage' => 'Kısmi iade işlemi, satışla aynı gün içerisindeyse tutarın tamamı için yapılamaz. Tutarın tamamı için iptal işlemi yapabilirsiniz.', + 'IsFriendly' => true, + 'Severity' => 'BusinessError', + ], + ], + 'Success' => false, + 'Value' => [ + 'IsEnrolled' => false, + 'IsVirtual' => false, + 'ResponseCode' => 'DbLayerError', + 'OrderId' => 0, + 'TransactionTime' => '0001-01-01T00:00:00', + 'MerchantId' => null, + 'BusinessKey' => '0', + ], + ], + ], + 'expectedData' => [ + 'order_id' => null, + 'auth_code' => null, + 'proc_return_code' => 'DbLayerError', + 'transaction_id' => null, + 'currency' => null, + 'ref_ret_num' => null, + 'status' => 'declined', + 'error_code' => 'DbLayerError', + 'error_message' => 'Kısmi iade işlemi, satışla aynı gün içerisindeyse tutarın tamamı için yapılamaz. Tutarın tamamı için iptal işlemi yapabilirsiniz.', + 'status_detail' => null, + ], + ]; yield 'success1' => [ 'responseData' => [ @@ -902,5 +1218,80 @@ public static function refundTestDataProvider(): iterable 'remote_order_id' => '114293626', ], ]; + + yield 'tdv2_success_full_refund' => [ + 'responseData' => [ + 'DrawBackResult' => [ + 'Results' => [], + 'Success' => true, + 'Value' => [ + 'IsEnrolled' => false, + 'IsVirtual' => false, + 'ProvisionNumber' => '050823', + 'RRN' => '411415539590', + 'Stan' => '539590', + 'ResponseCode' => '00', + 'ResponseMessage' => 'OTORİZASYON VERİLDİ', + 'OrderId' => 155767855, + 'TransactionTime' => '2024-04-23T15:19:15.7471578', + 'MerchantOrderId' => '202404229EAC', + 'CurrencyCode' => '0949', + 'MerchantId' => null, + 'BusinessKey' => '202404239999000000013631520', + ], + ], + ], + 'expectedData' => [ + 'auth_code' => '050823', + 'currency' => 'TRY', + 'error_code' => null, + 'error_message' => null, + 'order_id' => '202404229EAC', + 'proc_return_code' => '00', + 'ref_ret_num' => '411415539590', + 'remote_order_id' => '155767855', + 'status' => 'approved', + 'status_detail' => null, + 'transaction_id' => '539590', + ], + ]; + yield 'tdv2_success1' => [ + 'responseData' => [ + 'PartialDrawbackResult' => [ + 'Results' => [ + + ], + 'Success' => true, + 'Value' => [ + 'IsEnrolled' => false, + 'IsVirtual' => false, + 'ProvisionNumber' => '050569', + 'RRN' => '411220539231', + 'Stan' => '539231', + 'ResponseCode' => '00', + 'ResponseMessage' => 'OTORİZASYON VERİLDİ', + 'OrderId' => 155767811, + 'TransactionTime' => '2024-04-21T20:09:21.3829986', + 'MerchantOrderId' => '202404218A62', + 'CurrencyCode' => '0949', + 'MerchantId' => null, + 'BusinessKey' => '202404219999000000009542260', + ], + ], + ], + 'expectedData' => [ + 'auth_code' => '050569', + 'currency' => 'TRY', + 'error_code' => null, + 'error_message' => null, + 'order_id' => '202404218A62', + 'proc_return_code' => '00', + 'ref_ret_num' => '411220539231', + 'remote_order_id' => '155767811', + 'status' => 'approved', + 'status_detail' => null, + 'transaction_id' => '539231', + ], + ]; } } diff --git a/tests/Unit/Serializer/KuveytPosSerializerTest.php b/tests/Unit/Serializer/KuveytPosSerializerTest.php index b3dff1e0..039a7e6b 100644 --- a/tests/Unit/Serializer/KuveytPosSerializerTest.php +++ b/tests/Unit/Serializer/KuveytPosSerializerTest.php @@ -77,6 +77,16 @@ public function testDecodeJson(string $input, string $txType, array $expected): $this->assertSame($expected, $actual); } + /** + * @dataProvider decodeXmlDataProvider + */ + public function testDecodeXML(string $input, string $txType, array $expected): void + { + $actual = $this->serializer->decode($input, $txType); + + $this->assertSame($expected, $actual); + } + /** * @dataProvider decodeExceptionDataProvider */ @@ -200,16 +210,6 @@ public static function decodeHtmlDataProvider(): array ]; } - /** - * @dataProvider decodeXmlDataProvider - */ - public function testDecodeXML(string $input, string $txType, array $expected): void - { - $actual = $this->serializer->decode($input, $txType); - - $this->assertSame($expected, $actual); - } - public static function decodeXmlDataProvider(): iterable { yield [