From 6f330de00a9be829f7c16c1337ce580a47a9723d Mon Sep 17 00:00:00 2001 From: Nuryagdy Mustapayev Date: Thu, 30 May 2024 11:28:53 +0200 Subject: [PATCH 1/2] GarantiPosRequestDataMapper fix txtype for order history --- .../GarantiPosRequestDataMapper.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/DataMapper/RequestDataMapper/GarantiPosRequestDataMapper.php b/src/DataMapper/RequestDataMapper/GarantiPosRequestDataMapper.php index 4d930394..b3dd223d 100644 --- a/src/DataMapper/RequestDataMapper/GarantiPosRequestDataMapper.php +++ b/src/DataMapper/RequestDataMapper/GarantiPosRequestDataMapper.php @@ -52,7 +52,7 @@ class GarantiPosRequestDataMapper extends AbstractRequestDataMapper PosInterface::TX_TYPE_PAY_POST_AUTH => 'postauth', PosInterface::TX_TYPE_CANCEL => 'void', PosInterface::TX_TYPE_REFUND => 'refund', - PosInterface::TX_TYPE_HISTORY => 'orderhistoryinq', + PosInterface::TX_TYPE_ORDER_HISTORY => 'orderhistoryinq', PosInterface::TX_TYPE_STATUS => 'orderinq', ]; @@ -79,10 +79,10 @@ public function create3DPaymentRequestData(AbstractPosAccount $posAccount, array 'Version' => self::API_VERSION, 'Terminal' => $this->getTerminalData($posAccount), 'Customer' => [ - 'IPAddress' => $responseData['customeripaddress'], + 'IPAddress' => $responseData['customeripaddress'], ], 'Order' => [ - 'OrderID' => $responseData['orderid'], + 'OrderID' => $responseData['orderid'], ], 'Transaction' => [ 'Type' => $responseData['txntype'], @@ -123,11 +123,11 @@ public function createNonSecurePaymentRequestData(AbstractPosAccount $posAccount 'Version' => self::API_VERSION, 'Terminal' => $this->getTerminalData($posAccount), 'Customer' => [ - 'IPAddress' => $order['ip'], + 'IPAddress' => $order['ip'], ], 'Card' => $this->getCardData($creditCard), 'Order' => [ - 'OrderID' => $order['id'], + 'OrderID' => $order['id'], ], 'Transaction' => [ 'Type' => $this->mapTxType($txType), @@ -162,7 +162,7 @@ public function createNonSecurePostAuthPaymentRequestData(AbstractPosAccount $po 'Version' => self::API_VERSION, 'Terminal' => $this->getTerminalData($posAccount), 'Customer' => [ - 'IPAddress' => $order['ip'], + 'IPAddress' => $order['ip'], ], 'Order' => [ 'OrderID' => $order['id'], @@ -194,7 +194,7 @@ public function createStatusRequestData(AbstractPosAccount $posAccount, array $o 'Version' => self::API_VERSION, 'Terminal' => $this->getTerminalData($posAccount), 'Customer' => [ - 'IPAddress' => $order['ip'], + 'IPAddress' => $order['ip'], ], 'Order' => [ 'OrderID' => $order['id'], @@ -228,7 +228,7 @@ public function createCancelRequestData(AbstractPosAccount $posAccount, array $o 'Version' => self::API_VERSION, 'Terminal' => $this->getTerminalData($posAccount, true), 'Customer' => [ - 'IPAddress' => $order['ip'], + 'IPAddress' => $order['ip'], ], 'Order' => [ 'OrderID' => $order['id'], @@ -263,7 +263,7 @@ public function createRefundRequestData(AbstractPosAccount $posAccount, array $o 'Version' => self::API_VERSION, 'Terminal' => $this->getTerminalData($posAccount, true), 'Customer' => [ - 'IPAddress' => $order['ip'], + 'IPAddress' => $order['ip'], ], 'Order' => [ 'OrderID' => $order['id'], @@ -298,13 +298,13 @@ public function createOrderHistoryRequestData(AbstractPosAccount $posAccount, ar 'Version' => self::API_VERSION, 'Terminal' => $this->getTerminalData($posAccount), 'Customer' => [ - 'IPAddress' => $order['ip'], + 'IPAddress' => $order['ip'], ], 'Order' => [ 'OrderID' => $order['id'], ], 'Transaction' => [ - 'Type' => $this->mapTxType(PosInterface::TX_TYPE_HISTORY), + 'Type' => $this->mapTxType(PosInterface::TX_TYPE_ORDER_HISTORY), 'InstallmentCnt' => $this->mapInstallment($order['installment']), 'Amount' => $this->formatAmount($order['amount']), //sabit olarak amount 100 gonderilecek 'CurrencyCode' => $this->mapCurrency($order['currency']), From e202b3e442291d0158e1b11eb9762f84d7257e84 Mon Sep 17 00:00:00 2001 From: Nuryagdy Mustapayev Date: Mon, 3 Jun 2024 15:34:43 +0200 Subject: [PATCH 2/2] GarantiPos - implement history request --- README.md | 2 +- docs/HISTORY-EXAMPLE.md | 12 +- examples/_common-codes/regular/history.php | 13 +- examples/garanti/regular/history.php | 3 + .../GarantiPosRequestDataMapper.php | 68 ++++- .../GarantiPosResponseDataMapper.php | 177 ++++++++++++- src/Gateways/GarantiPos.php | 11 +- tests/Functional/AkbankPosTest.php | 4 +- tests/Functional/GarantiPosTest.php | 24 ++ tests/Functional/PayForPosTest.php | 2 +- tests/Functional/PaymentTestTrait.php | 19 +- .../GarantiPosRequestDataMapperTest.php | 62 ++++- .../GarantiPosResponseDataMapperTest.php | 244 ++++++++++++++++++ tests/Unit/Gateways/GarantiPosTest.php | 49 +++- .../history/daily_range_history_expected.json | 155 +++++++++++ .../garanti/history/date_range_history.json | 225 ++++++++++++++++ 16 files changed, 1035 insertions(+), 35 deletions(-) create mode 100644 examples/garanti/regular/history.php create mode 100644 tests/Unit/test_data/garanti/history/daily_range_history_expected.json create mode 100644 tests/Unit/test_data/garanti/history/date_range_history.json diff --git a/README.md b/README.md index 23ad3e18..cd6e8592 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ sistemlerinin kullanılabilmesidir. | EST V3 POS

EstPos altyapının
daha güvenli
(sha512) hash
algoritmasıyla
uygulaması.
| -----"----- | -----"----- | -----"----- | | PayFlex MPI VPOS V4 | Ziraat
Vakıfbank VPOS 7/24
İşbank | NonSecure
3DSecure
Tekrarlanan Ödeme | İptal
İade
Durum sorgulama | | PayFlex
Common Payment V4
(Ortak Ödeme) | Ziraat
Vakıfbank
İşbank | NonSecure
3DPay
3DHost | İptal
İade | -| Garanti Virtual POS | Garanti | NonSecure
3DSecure
3DPay
3DHost
Tekrarlanan Ödeme | İptal
İade
Durum sorgulama
Sipariş Tarihçesini sorgulama | +| Garanti Virtual POS | Garanti | NonSecure
3DSecure
3DPay
3DHost
Tekrarlanan Ödeme | İptal
İade
Durum sorgulama
Sipariş Tarihçesini sorgulama
Geçmiş İşlemleri sorgulama | | PosNet | YapıKredi | NonSecure
3DSecure
| İptal
İade
Durum sorgulama | | 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 | diff --git a/docs/HISTORY-EXAMPLE.md b/docs/HISTORY-EXAMPLE.md index a3e5434f..5a1af137 100644 --- a/docs/HISTORY-EXAMPLE.md +++ b/docs/HISTORY-EXAMPLE.md @@ -44,7 +44,7 @@ try { require 'config.php'; -function createHistoryOrder(string $gatewayClass, array $extraData): array +function createHistoryOrder(string $gatewayClass, array $extraData, string $ip): array { $order = []; $txTime = new \DateTimeImmutable(); @@ -60,6 +60,14 @@ function createHistoryOrder(string $gatewayClass, array $extraData): array 'start_date' => $txTime->modify('-1 day'), 'end_date' => $txTime->modify('+1 day'), ]; + } elseif (\Mews\Pos\Gateways\GarantiPos::class === $gatewayClass) { + $order = [ + 'ip' => $ip, + 'page' => 1, //optional + // Başlangıç ve bitiş tarihleri arasında en fazla 30 gün olabilir + 'start_date' => $txTime, + 'end_date' => $txTime->modify('+1 day'), + ]; } elseif (\Mews\Pos\Gateways\AkbankPos::class === $gatewayClass) { $order = [ // Gün aralığı 1 günden fazla girilemez @@ -75,7 +83,7 @@ function createHistoryOrder(string $gatewayClass, array $extraData): array return $order; } -$order = createHistoryOrder(get_class($pos), []); +$order = createHistoryOrder(get_class($pos), [], '127.0.0.1'); $pos->history($order); $response = $pos->getResponse(); diff --git a/examples/_common-codes/regular/history.php b/examples/_common-codes/regular/history.php index 4cb080e1..efbee45c 100644 --- a/examples/_common-codes/regular/history.php +++ b/examples/_common-codes/regular/history.php @@ -8,7 +8,7 @@ require '../../_templates/_header.php'; -function createHistoryOrder(string $gatewayClass, array $extraData): array +function createHistoryOrder(string $gatewayClass, array $extraData, string $ip): array { $order = []; $txTime = new \DateTimeImmutable(); @@ -27,6 +27,15 @@ function createHistoryOrder(string $gatewayClass, array $extraData): array 'start_date' => $txTime->modify('-1 day'), 'end_date' => $txTime->modify('+1 day'), ]; + } elseif (\Mews\Pos\Gateways\GarantiPos::class === $gatewayClass) { + $order = [ + 'ip' => $ip, + 'currency' => \Mews\Pos\PosInterface::CURRENCY_USD, + 'page' => 1, //optional + // Başlangıç ve bitiş tarihleri arasında en fazla 30 gün olabilir + 'start_date' => $txTime, + 'end_date' => $txTime->modify('+1 day'), + ]; } elseif (\Mews\Pos\Gateways\AkbankPos::class === $gatewayClass) { $order = [ // Gün aralığı 1 günden fazla girilemez @@ -42,7 +51,7 @@ function createHistoryOrder(string $gatewayClass, array $extraData): array return $order; } -$order = createHistoryOrder(get_class($pos), []); +$order = createHistoryOrder(get_class($pos), [], $ip); dump($order); $transaction = \Mews\Pos\PosInterface::TX_TYPE_HISTORY; diff --git a/examples/garanti/regular/history.php b/examples/garanti/regular/history.php new file mode 100644 index 00000000..34a3742d --- /dev/null +++ b/examples/garanti/regular/history.php @@ -0,0 +1,3 @@ + 'void', PosInterface::TX_TYPE_REFUND => 'refund', PosInterface::TX_TYPE_ORDER_HISTORY => 'orderhistoryinq', + PosInterface::TX_TYPE_HISTORY => 'orderlistinq', PosInterface::TX_TYPE_STATUS => 'orderinq', ]; @@ -319,11 +324,45 @@ public function createOrderHistoryRequestData(AbstractPosAccount $posAccount, ar } /** + * @param GarantiPosAccount $posAccount * {@inheritDoc} */ public function createHistoryRequestData(AbstractPosAccount $posAccount, array $data = []): array { - throw new NotImplementedException(); + $order = $this->prepareHistoryOrder($data); + + $result = [ + 'Mode' => $this->getMode(), + 'Version' => self::API_VERSION, + 'Terminal' => $this->getTerminalData($posAccount), + 'Customer' => [ + 'IPAddress' => $order['ip'], + ], + 'Order' => [ + 'OrderID' => null, + 'GroupID' => null, + 'Description' => null, + // Başlangıç ve bitiş tarihleri arasında en fazla 30 gün olabilir + 'StartDate' => $this->formatRequestDateTime($order['start_date']), + 'EndDate' => $this->formatRequestDateTime($order['end_date']), + /** + * 500 adetten fazla işlem olması durumunda ListPageNum alanında diğer 500 lü grupların görüntülenmesi + * için sayfa numarası yazılır. + */ + 'ListPageNum' => $order['page'], + ], + 'Transaction' => [ + 'Type' => $this->mapTxType(PosInterface::TX_TYPE_HISTORY), + 'Amount' => $this->formatAmount(1), //sabit olarak amount 100 gonderilecek + 'CurrencyCode' => $this->mapCurrency(PosInterface::CURRENCY_TRY), + 'CardholderPresentCode' => '0', + 'MotoInd' => self::MOTO, + ], + ]; + + $result['Terminal']['HashData'] = $this->crypt->createHash($posAccount, $result); + + return $result; } @@ -462,6 +501,19 @@ protected function prepareOrderHistoryOrder(array $order): array return $this->prepareStatusOrder($order); } + /** + * @inheritDoc + */ + protected function prepareHistoryOrder(array $data): array + { + return [ + 'start_date' => $data['start_date'], + 'end_date' => $data['end_date'], + 'page' => $data['page'] ?? 1, + 'ip' => $data['ip'], + ]; + } + /** * @inheritDoc */ @@ -573,4 +625,14 @@ private function createRecurringData(array $recurringData): array 'StartDate' => isset($recurringData['startDate']) ? $recurringData['startDate']->format('Ymd') : '', ]; } + + /** + * @param \DateTimeInterface $dateTime + * + * @return string example 01/12/2010 11:11 + */ + private function formatRequestDateTime(\DateTimeInterface $dateTime): string + { + return $dateTime->format('d/m/Y H:i'); + } } diff --git a/src/DataMapper/ResponseDataMapper/GarantiPosResponseDataMapper.php b/src/DataMapper/ResponseDataMapper/GarantiPosResponseDataMapper.php index 46979b18..62a89917 100644 --- a/src/DataMapper/ResponseDataMapper/GarantiPosResponseDataMapper.php +++ b/src/DataMapper/ResponseDataMapper/GarantiPosResponseDataMapper.php @@ -253,7 +253,7 @@ public function mapStatusResponse(array $rawResponseData): array } /** @var array{Response: array} $transaction */ - $transaction = $rawResponseData['Transaction']; + $transaction = $rawResponseData['Transaction']; /** @var array $orderInqResult */ $orderInqResult = $rawResponseData['Order']['OrderInqResult']; $defaultResponse = $this->getDefaultStatusResponse($rawResponseData); @@ -333,7 +333,36 @@ public function mapOrderHistoryResponse(array $rawResponseData): array */ public function mapHistoryResponse(array $rawResponseData): array { - throw new NotImplementedException(); + $rawResponseData = $this->emptyStringsToNull($rawResponseData); + $procReturnCode = $this->getProcReturnCode($rawResponseData); + $status = self::TX_DECLINED; + if (self::PROCEDURE_SUCCESS_CODE === $procReturnCode) { + $status = self::TX_APPROVED; + } + + $mappedTransactions = []; + if (self::TX_APPROVED === $status) { + $rawTransactions = $rawResponseData['Order']['OrderListInqResult']['OrderTxnList']['OrderTxn']; + if (\count($rawTransactions) !== \count($rawTransactions, COUNT_RECURSIVE)) { + foreach ($rawTransactions as $transaction) { + $mappedTransaction = $this->mapSingleHistoryTransaction($transaction); + $mappedTransactions[] = $mappedTransaction; + } + } else { + $mappedTransactions[] = $this->mapSingleHistoryTransaction($rawTransactions); + } + } + + return [ + 'proc_return_code' => $procReturnCode, + 'error_code' => self::TX_DECLINED === $status ? $procReturnCode : null, + 'error_message' => self::TX_DECLINED === $status ? $rawResponseData['Transaction']['Response']['ErrorMsg'] : null, + 'status' => $status, + 'status_detail' => $this->getStatusDetail($procReturnCode), + 'trans_count' => \count($mappedTransactions), + 'transactions' => $mappedTransactions, + 'all' => $rawResponseData, + ]; } /** @@ -352,6 +381,23 @@ public function extractMdStatus(array $raw3DAuthResponseData): ?string return $raw3DAuthResponseData['mdstatus'] ?? null; } + /** + * @inheritDoc + */ + public function mapTxType($txType): ?string + { + $historyResponseTxTypes = [ + 'Satis' => PosInterface::TX_TYPE_PAY_AUTH, + 'On Otorizasyon' => PosInterface::TX_TYPE_PAY_PRE_AUTH, + 'On Otorizasyon Kapama' => PosInterface::TX_TYPE_PAY_POST_AUTH, + 'Iade' => PosInterface::TX_TYPE_REFUND, + 'Iptal' => PosInterface::TX_TYPE_CANCEL, + // ... Odul Sorgulama + ]; + + return $historyResponseTxTypes[$txType] ?? parent::mapTxType($txType); + } + /** * 100001 => 1000.01 * @param string $amount @@ -458,6 +504,40 @@ protected function getProcReturnCode(array $response): ?string return $response['Transaction']['Response']['Code'] ?? null; } + /** + * @inheritDoc + */ + protected function mapCurrency(string $currency): string + { + $historyResponseCurrencyMapping = [ + 'TL' => PosInterface::CURRENCY_TRY, + 'USD' => PosInterface::CURRENCY_USD, + 'EUR' => PosInterface::CURRENCY_EUR, + 'RUB' => PosInterface::CURRENCY_RUB, + 'JPY' => PosInterface::CURRENCY_JPY, + 'GBP' => PosInterface::CURRENCY_GBP, + ]; + + return $historyResponseCurrencyMapping[$currency] ?? parent::mapCurrency($currency); + } + + /** + * @inheritDoc + */ + protected function mapInstallment(?string $installment): int + { + if (null === $installment) { + return 0; + } + + // history response + if ('Pesin' === $installment || '1' === $installment) { + return 0; + } + + return parent::mapInstallment($installment); + } + /** * @param array $rawTx * @@ -494,4 +574,97 @@ private function mapSingleOrderHistoryTransaction(array $rawTx): array return $defaultResponse; } + + /** + * @param array $rawTx + * + * @return array + */ + private function mapSingleHistoryTransaction(array $rawTx): array + { + $procReturnCode = $rawTx['ResponseCode']; + $status = self::TX_DECLINED; + if (self::PROCEDURE_SUCCESS_CODE === $procReturnCode) { + $status = self::TX_APPROVED; + } + + $defaultResponse = $this->getDefaultOrderHistoryTxResponse(); + $defaultResponse['auth_code'] = $rawTx['AuthCode'] ?? null; + $defaultResponse['ref_ret_num'] = $rawTx['RetrefNum'] ?? null; + $defaultResponse['order_id'] = $rawTx['OrderID']; + $defaultResponse['batch_num'] = $rawTx['BatchNum']; + $defaultResponse['proc_return_code'] = $procReturnCode; + $defaultResponse['transaction_type'] = null !== $rawTx['TrxType'] ? $this->mapTxType($rawTx['TrxType']) : null; + $defaultResponse['order_status'] = null !== $rawTx['Status'] ? $this->mapHistoryOrderStatus($rawTx['Status'], $defaultResponse['transaction_type']) : null; + $defaultResponse['status'] = $status; + $defaultResponse['status_detail'] = $this->getStatusDetail($procReturnCode); + $defaultResponse['error_code'] = self::TX_APPROVED === $status ? null : $procReturnCode; + $defaultResponse['error_message'] = self::TX_APPROVED === $status ? null : $rawTx['SysErrMsg']; + + // 3D Secure => 3D + // 3D Pay => 3D + // NonSecure => '' + $defaultResponse['payment_model'] = '3D' === $rawTx['SafeType'] ? PosInterface::MODEL_3D_SECURE : PosInterface::MODEL_NON_SECURE; + $defaultResponse['transaction_time'] = null !== $rawTx['LastTrxDate'] ? new \DateTimeImmutable($rawTx['LastTrxDate']) : null; + if (self::TX_APPROVED === $status) { + $defaultResponse['masked_number'] = $rawTx['CardNumberMasked']; + $defaultResponse['installment_count'] = $this->mapInstallment($rawTx['InstallmentCnt']); + $defaultResponse['currency'] = null !== $rawTx['CurrencyCode'] ? $this->mapCurrency($rawTx['CurrencyCode']) : null; + $defaultResponse['first_amount'] = null !== $rawTx['AuthAmount'] ? $this->formatAmount($rawTx['AuthAmount']) : null; + if ($defaultResponse['order_status'] === PosInterface::PAYMENT_STATUS_PAYMENT_COMPLETED) { + $defaultResponse['capture_amount'] = $defaultResponse['first_amount']; + $defaultResponse['capture'] = $defaultResponse['first_amount'] > 0 ? $defaultResponse['capture_amount'] === $defaultResponse['first_amount'] : null; + $defaultResponse['capture_time'] = $defaultResponse['transaction_time']; + } + } + + return $defaultResponse; + } + + /** + * todo anlasilmayan durumlar: + * - "Status" => "Iptal", "TrxType" => "Satis" + * - "Status" => "Iptal", "TrxType" => "On Otorizasyon" + * - "Status" => "Iptal", "TrxType" => "Iptal" + * - "Status" => "Iptal", "TrxType" => "Iade" + * + * @param string $txStatus + * @param PosInterface::TX_TYPE_*|null $txType + * + * @return string|null + */ + private function mapHistoryOrderStatus(string $txStatus, ?string $txType): ?string + { + if (null === $txType) { + return null; + } + // txStatus possible values: + // Basarili + // Basarisiz + // Iptal + // Onaylandi + + if ('Basarili' === $txStatus || 'Onaylandi' === $txStatus) { + if (PosInterface::TX_TYPE_CANCEL === $txType) { + return PosInterface::PAYMENT_STATUS_CANCELED; + } + if (PosInterface::TX_TYPE_REFUND === $txType) { + // todo how can we decide if order is partially or fully refunded? + return PosInterface::PAYMENT_STATUS_FULLY_REFUNDED; + } + if (PosInterface::TX_TYPE_PAY_AUTH === $txType || PosInterface::TX_TYPE_PAY_POST_AUTH === $txType) { + return PosInterface::PAYMENT_STATUS_PAYMENT_COMPLETED; + } + if (PosInterface::TX_TYPE_PAY_PRE_AUTH === $txType) { + return PosInterface::PAYMENT_STATUS_PRE_AUTH_COMPLETED; + } + + return null; + } + if ('Iptal' === $txStatus) { + return null; + } + + return PosInterface::PAYMENT_STATUS_ERROR; + } } diff --git a/src/Gateways/GarantiPos.php b/src/Gateways/GarantiPos.php index 550ba002..b54fc28a 100644 --- a/src/Gateways/GarantiPos.php +++ b/src/Gateways/GarantiPos.php @@ -14,7 +14,6 @@ use Mews\Pos\Event\RequestDataPreparedEvent; use Mews\Pos\Exceptions\HashMismatchException; use Mews\Pos\Exceptions\UnsupportedPaymentModelException; -use Mews\Pos\Exceptions\UnsupportedTransactionTypeException; use Mews\Pos\PosInterface; use Symfony\Component\HttpFoundation\Request; @@ -48,7 +47,7 @@ class GarantiPos extends AbstractGateway PosInterface::TX_TYPE_CANCEL => true, PosInterface::TX_TYPE_REFUND => true, PosInterface::TX_TYPE_ORDER_HISTORY => true, - PosInterface::TX_TYPE_HISTORY => false, + PosInterface::TX_TYPE_HISTORY => true, ]; @@ -138,14 +137,6 @@ public function make3DHostPayment(Request $request, array $order, string $txType throw new UnsupportedPaymentModelException(); } - /** - * @inheritDoc - */ - public function history(array $data): PosInterface - { - throw new UnsupportedTransactionTypeException(); - } - /** * @inheritDoc * diff --git a/tests/Functional/AkbankPosTest.php b/tests/Functional/AkbankPosTest.php index 2fd8bbe7..667f75e3 100644 --- a/tests/Functional/AkbankPosTest.php +++ b/tests/Functional/AkbankPosTest.php @@ -146,7 +146,7 @@ function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThro public function testHistorySuccess(): void { - $historyOrder = $this->createHistoryOrder(\get_class($this->pos), []); + $historyOrder = $this->createHistoryOrder(\get_class($this->pos), [], '127.0.0.1'); $eventIsThrown = false; $this->eventDispatcher->addListener( @@ -389,7 +389,7 @@ function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThro */ public function testRecurringHistorySuccess(): void { - $historyOrder = $this->createHistoryOrder(\get_class($this->pos), []); + $historyOrder = $this->createHistoryOrder(\get_class($this->pos), [], '127.0.0.1'); $eventIsThrown = false; $this->eventDispatcher->addListener( diff --git a/tests/Functional/GarantiPosTest.php b/tests/Functional/GarantiPosTest.php index 65f6c24a..1c5a5e5c 100644 --- a/tests/Functional/GarantiPosTest.php +++ b/tests/Functional/GarantiPosTest.php @@ -260,4 +260,28 @@ function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThro $this->assertNotEmpty($response); $this->assertTrue($eventIsThrown); } + + /** + * @depends testStatusSuccess + */ + public function testHistorySuccess(): void + { + $historyOrder = $this->createHistoryOrder(\get_class($this->pos), [], '127.0.0.1'); + + $eventIsThrown = false; + $this->eventDispatcher->addListener( + RequestDataPreparedEvent::class, + function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThrown): void { + $eventIsThrown = true; + $this->assertSame(PosInterface::TX_TYPE_HISTORY, $requestDataPreparedEvent->getTxType()); + $this->assertCount(6, $requestDataPreparedEvent->getRequestData()); + }); + + $this->pos->history($historyOrder); + + $response = $this->pos->getResponse(); + $this->assertIsArray($response); + $this->assertTrue($eventIsThrown); + $this->assertNotEmpty($response['transactions']); + } } diff --git a/tests/Functional/PayForPosTest.php b/tests/Functional/PayForPosTest.php index e72d60e8..75f157e9 100644 --- a/tests/Functional/PayForPosTest.php +++ b/tests/Functional/PayForPosTest.php @@ -171,7 +171,7 @@ function (RequestDataPreparedEvent $requestDataPreparedEvent) use (&$eventIsThro public function testHistorySuccess(): void { - $historyOrder = $this->createHistoryOrder(\get_class($this->pos), []); + $historyOrder = $this->createHistoryOrder(\get_class($this->pos), [], '127.0.0.1'); $eventIsThrown = false; $this->eventDispatcher->addListener( diff --git a/tests/Functional/PaymentTestTrait.php b/tests/Functional/PaymentTestTrait.php index 60144077..9119da54 100644 --- a/tests/Functional/PaymentTestTrait.php +++ b/tests/Functional/PaymentTestTrait.php @@ -248,18 +248,17 @@ private function createOrderHistoryOrder(string $gatewayClass, array $lastRespon return $order; } - private function createHistoryOrder(string $gatewayClass, array $extraData): array + private function createHistoryOrder(string $gatewayClass, array $extraData, string $ip): array { + $txTime = new \DateTimeImmutable(); if (PayForPos::class === $gatewayClass) { return [ // odeme tarihi - 'transaction_date' => $extraData['transaction_date'] ?? new \DateTimeImmutable(), + 'transaction_date' => $extraData['transaction_date'] ?? $txTime, ]; } if (\Mews\Pos\Gateways\VakifKatilimPos::class === $gatewayClass) { - $txTime = new \DateTimeImmutable(); - return [ 'page' => 1, 'page_size' => 20, @@ -271,9 +270,17 @@ private function createHistoryOrder(string $gatewayClass, array $extraData): arr ]; } - if (\Mews\Pos\Gateways\AkbankPos::class === $gatewayClass) { - $txTime = new \DateTimeImmutable(); + if (\Mews\Pos\Gateways\GarantiPos::class === $gatewayClass) { + return [ + 'ip' => $ip, + 'page' => 1, + // Başlangıç ve bitiş tarihleri arasında en fazla 30 gün olabilir + 'start_date' => $txTime->modify('-1 day'), + 'end_date' => $txTime->modify('+1 day'), + ]; + } + if (\Mews\Pos\Gateways\AkbankPos::class === $gatewayClass) { return [ // Gün aralığı 1 günden fazla girilemez 'start_date' => $txTime->modify('-23 hour'), diff --git a/tests/Unit/DataMapper/RequestDataMapper/GarantiPosRequestDataMapperTest.php b/tests/Unit/DataMapper/RequestDataMapper/GarantiPosRequestDataMapperTest.php index 21cae2a2..7d5c2f11 100644 --- a/tests/Unit/DataMapper/RequestDataMapper/GarantiPosRequestDataMapperTest.php +++ b/tests/Unit/DataMapper/RequestDataMapper/GarantiPosRequestDataMapperTest.php @@ -190,6 +190,19 @@ public function testCreateOrderHistoryRequestData(array $order, array $expectedD $this->assertEquals($expectedData, $actual); } + /** + * @dataProvider historyRequestDataProvider + */ + public function testCreateHistoryRequestData(array $data, array $expectedData): void + { + $actualData = $this->requestDataMapper->createHistoryRequestData($this->account, $data); + + \ksort($expectedData); + \ksort($actualData); + + $this->assertSame($expectedData, $actualData); + } + /** * @dataProvider create3DPaymentRequestDataDataProvider */ @@ -491,8 +504,8 @@ public static function orderHistoryRequestDataProvider(): array return [ [ 'order' => [ - 'id' => 'order222', - 'ip' => '156.155.154.153', + 'id' => 'order222', + 'ip' => '156.155.154.153', 'installment' => 0, ], 'expected' => [ @@ -524,6 +537,51 @@ public static function orderHistoryRequestDataProvider(): array ]; } + public static function historyRequestDataProvider(): array + { + return [ + [ + 'data' => [ + 'start_date' => new \DateTime('2022-05-18 00:00:00'), + 'end_date' => new \DateTime('2022-05-18 23:59:59'), + 'ip' => '127.0.0.1', + ], + 'expected' => [ + 'Customer' => [ + 'IPAddress' => '127.0.0.1', + ], + + 'Mode' => 'TEST', + 'Order' => [ + 'OrderID' => null, + 'GroupID' => null, + 'Description' => null, + 'StartDate' => '18/05/2022 00:00', + 'EndDate' => '18/05/2022 23:59', + 'ListPageNum' => 1, + ], + + 'Terminal' => [ + 'ProvUserID' => 'PROVAUT', + 'UserID' => 'PROVAUT', + 'HashData' => '9B53A55199EBAD2F486089FD7310C4BC0C61A99FC37EF61F6BBAE67FA17E47641540B203E83C9F2E64DB64B971FE6FF604274316F6D010426D6AA91BE1D924E6', + 'ID' => '30691298', + 'MerchantID' => '7000679', + ], + + 'Transaction' => [ + 'Type' => 'orderlistinq', + 'Amount' => 100, + 'CurrencyCode' => '949', + 'CardholderPresentCode' => '0', + 'MotoInd' => 'N', + ], + 'Version' => '512', + ], + ], + ]; + } + public static function create3DPaymentRequestDataDataProvider(): \Generator { $account = AccountFactory::createGarantiPosAccount( diff --git a/tests/Unit/DataMapper/ResponseDataMapper/GarantiPosResponseDataMapperTest.php b/tests/Unit/DataMapper/ResponseDataMapper/GarantiPosResponseDataMapperTest.php index 5ceca6d8..d33877b4 100644 --- a/tests/Unit/DataMapper/ResponseDataMapper/GarantiPosResponseDataMapperTest.php +++ b/tests/Unit/DataMapper/ResponseDataMapper/GarantiPosResponseDataMapperTest.php @@ -7,6 +7,7 @@ use Mews\Pos\DataMapper\RequestDataMapper\GarantiPosRequestDataMapper; use Mews\Pos\DataMapper\ResponseDataMapper\GarantiPosResponseDataMapper; +use Mews\Pos\Exceptions\NotImplementedException; use Mews\Pos\Factory\CryptFactory; use Mews\Pos\Gateways\GarantiPos; use Mews\Pos\PosInterface; @@ -97,6 +98,12 @@ public function testMap3DPaymentData(array $order, string $txType, array $threeD $this->assertSame($expectedData, $actualData); } + public function testMap3DHostResponseData(): void + { + $this->expectException(NotImplementedException::class); + $this->responseDataMapper->map3DHostResponseData([], PosInterface::TX_TYPE_PAY_AUTH, []); + } + /** * @dataProvider threeDPayPaymentDataProvider */ @@ -164,6 +171,44 @@ public function testOrderMapHistoryResponse(array $responseData, array $expected $this->assertCount($actualData['trans_count'], $actualData['transactions']); + $this->assertArrayHasKey('all', $actualData); + $this->assertIsArray($actualData['all']); + $this->assertNotEmpty($actualData['all']); + unset($actualData['all']); + $this->assertSame($expectedData, $actualData); + } + + /** + * @dataProvider historyTestDataProvider + */ + public function testMapHistoryResponse(array $responseData, array $expectedData): void + { + $actualData = $this->responseDataMapper->mapHistoryResponse($responseData); + + if (count($actualData['transactions']) > 1 + && null !== $actualData['transactions'][0]['transaction_time'] + && null !== $actualData['transactions'][1]['transaction_time'] + ) { + $this->assertGreaterThan( + $actualData['transactions'][0]['transaction_time'], + $actualData['transactions'][1]['transaction_time'] + ); + } + + $this->assertCount($actualData['trans_count'], $actualData['transactions']); + + foreach (array_keys($actualData['transactions']) as $key) { + $this->assertEquals($expectedData['transactions'][$key]['transaction_time'], $actualData['transactions'][$key]['transaction_time'], 'tx: '.$key); + $this->assertEquals($expectedData['transactions'][$key]['capture_time'], $actualData['transactions'][$key]['capture_time'], 'tx: '.$key); + unset($actualData['transactions'][$key]['transaction_time'], $expectedData['transactions'][$key]['transaction_time']); + unset($actualData['transactions'][$key]['capture_time'], $expectedData['transactions'][$key]['capture_time']); + \ksort($actualData['transactions'][$key]); + \ksort($expectedData['transactions'][$key]); + } + + $this->assertArrayHasKey('all', $actualData); + $this->assertIsArray($actualData['all']); + $this->assertNotEmpty($actualData['all']); unset($actualData['all']); $this->assertSame($expectedData, $actualData); } @@ -1732,4 +1777,203 @@ public function orderHistoryTestDataProvider(): array ], ]; } + + public static function historyTestDataProvider(): \Generator + { + $dateRangeHistoryExpected = \json_decode(\file_get_contents(__DIR__.'/../../test_data/garanti/history/daily_range_history_expected.json'), true); + + foreach ($dateRangeHistoryExpected['transactions'] as &$item) { + if (null !== $item['transaction_time']) { + $item['transaction_time'] = new \DateTimeImmutable( + $item['transaction_time']['date'], + new \DateTimeZone($item['transaction_time']['timezone']) + ); + } + if (null !== $item['capture_time']) { + $item['capture_time'] = new \DateTimeImmutable( + $item['capture_time']['date'], + new \DateTimeZone($item['capture_time']['timezone']) + ); + } + } + + yield 'success_data_range_history' => [ + 'responseData' => \json_decode(\file_get_contents(__DIR__.'/../../test_data/garanti/history/date_range_history.json'), true), + //'responseData' => \json_decode(\file_get_contents(__DIR__.'/../../../../var/garanti-last-2-year-history.json'), true), + 'expectedData' => $dateRangeHistoryExpected, + ]; + yield 'success_single_transaction' => [ + 'responseData' => [ + 'Mode' => '', + 'Terminal' => [ + 'ProvUserID' => 'PROVAUT', + 'UserID' => 'PROVAUT', + 'ID' => '30691298', + 'MerchantID' => '7000679', + ], + 'Customer' => [ + 'IPAddress' => '172.26.0.1', + 'EmailAddress' => '', + ], + 'Order' => [ + 'OrderID' => '', + 'GroupID' => '', + 'OrderListInqResult' => [ + 'OrderTxnList' => [ + 'TotalTxnCount' => '1', + 'TotalPageCount' => '1', + 'ActPageNum' => '1', + 'OrderTxn' => [ + 'Id' => '1', + 'LastTrxDate' => '2024-06-03 16:06:29', + 'TrxType' => 'Satis', + 'OrderID' => '202406036C78', + 'Name' => '', + 'CardNumberMasked' => '42822090****8015', + 'ExpireDate' => '0830', + 'BankBin' => '42822090', + 'BatchNum' => '576200', + 'AuthCode' => '304919', + 'RetrefNum' => '415501677066', + 'OrigRetrefNum' => '', + 'InstallmentCnt' => 'Pesin', + 'Status' => 'Basarili', + 'AuthAmount' => '1001', + 'CurrencyCode' => 'TL', + 'RemainingBNSAmount' => '0', + 'UsedFBBAmount' => '0', + 'UsedChequeType' => '', + 'UsedChequeCount' => '0', + 'UsedChequeAmount' => '0', + 'SafeType' => '', + 'Comment1' => '', + 'Comment2' => '', + 'Comment3' => '', + 'UserId' => 'PROVAUT', + 'Settlement' => 'N', + 'EmailAddress' => '', + 'RecurringTotalPaymentNum' => '0', + 'RecurringLastPaymentNum' => '0', + 'RecurringTxnAmount' => '0', + 'ResponseCode' => '00', + 'SysErrMsg' => '', + ], + ], + ], + ], + 'Transaction' => [ + 'Response' => [ + 'Source' => 'GVPS', + 'Code' => '00', + 'ReasonCode' => '', + 'Message' => 'Approved', + 'ErrorMsg' => '', + 'SysErrMsg' => '', + ], + 'RetrefNum' => '', + 'AuthCode' => '', + 'BatchNum' => '', + 'SequenceNum' => '', + 'ProvDate' => '20240603 16:07:07', + 'CardNumberMasked' => '', + 'CardHolderName' => '', + 'CardType' => '', + 'HashData' => 'C1DD90277E3CE36D6226FF02E59D95999D78793CF8942860600BA2800A63CB991A518C1DECA1609C99DA8F8995CBB78A54E7D34F337A8BFF60D10B6DB47C8750', + 'HostMsgList' => '', + 'RewardInqResult' => [ + 'RewardList' => '', + 'ChequeList' => '', + ], + ], + ], + 'expectedData' => [ + 'proc_return_code' => '00', + 'error_code' => null, + 'error_message' => null, + 'status' => 'approved', + 'status_detail' => 'approved', + 'trans_count' => 1, + 'transactions' => [ + [ + 'auth_code' => '304919', + 'proc_return_code' => '00', + 'transaction_id' => null, + 'transaction_time' => new \DateTimeImmutable('2024-06-03 16:06:29'), + 'capture_time' => new \DateTimeImmutable('2024-06-03 16:06:29'), + 'error_message' => null, + 'ref_ret_num' => '415501677066', + 'order_status' => 'PAYMENT_COMPLETED', + 'transaction_type' => 'pay', + 'first_amount' => 10.01, + 'capture_amount' => 10.01, + 'status' => 'approved', + 'error_code' => null, + 'status_detail' => 'approved', + 'capture' => true, + 'currency' => 'TRY', + 'masked_number' => '42822090****8015', + 'order_id' => '202406036C78', + 'batch_num' => '576200', + 'payment_model' => 'regular', + 'installment_count' => 0, + ], + ], + ], + ]; + yield 'fail_invalid_fields' => [ + 'responseData' => [ + 'Mode' => '', + 'Terminal' => [ + 'ProvUserID' => 'PROVAUT', + 'UserID' => 'PROVAUT', + 'ID' => '30691298', + 'MerchantID' => '7000679', + ], + 'Customer' => [ + 'IPAddress' => '', + 'EmailAddress' => '', + ], + 'Order' => [ + 'OrderID' => '', + 'GroupID' => '', + 'OrderListInqResult' => [ + 'OrderTxnList' => '', + ], + ], + 'Transaction' => [ + 'Response' => [ + 'Source' => 'GVPS', + 'Code' => '92', + 'ReasonCode' => '0002', + 'Message' => 'Declined', + 'ErrorMsg' => 'Giriş yaptığınız işlem tipi için zorunlu alanları kontrol ediniz', + 'SysErrMsg' => 'CustomerIPAddress field must contain value because of the Mandatory Rule:null', + ], + 'RetrefNum' => '', + 'AuthCode' => '', + 'BatchNum' => '', + 'SequenceNum' => '', + 'ProvDate' => '20240530 12:53:46', + 'CardNumberMasked' => '', + 'CardHolderName' => '', + 'CardType' => '', + 'HashData' => '09852B466F45FE00769BE0E40F028FFF7B560CCA5871F8E562B910C2E9CEF9972A8C7F8655D67D1E31B24E81BF57F5B35F8446A94591256DCFEB92D551FEC858', + 'HostMsgList' => '', + 'RewardInqResult' => [ + 'RewardList' => '', + 'ChequeList' => '', + ], + ], + ], + 'expectedData' => [ + 'proc_return_code' => '92', + 'error_code' => '92', + 'error_message' => 'Giriş yaptığınız işlem tipi için zorunlu alanları kontrol ediniz', + 'status' => 'declined', + 'status_detail' => 'invalid_transaction', + 'trans_count' => 0, + 'transactions' => [], + ], + ]; + } } diff --git a/tests/Unit/Gateways/GarantiPosTest.php b/tests/Unit/Gateways/GarantiPosTest.php index c29803fa..a47b9671 100644 --- a/tests/Unit/Gateways/GarantiPosTest.php +++ b/tests/Unit/Gateways/GarantiPosTest.php @@ -13,7 +13,6 @@ use Mews\Pos\Entity\Card\CreditCardInterface; use Mews\Pos\Event\RequestDataPreparedEvent; use Mews\Pos\Exceptions\UnsupportedPaymentModelException; -use Mews\Pos\Exceptions\UnsupportedTransactionTypeException; use Mews\Pos\Factory\AccountFactory; use Mews\Pos\Factory\CreditCardFactory; use Mews\Pos\Gateways\GarantiPos; @@ -449,10 +448,38 @@ public function testRefundRequest(array $order, string $apiUrl): void $this->pos->refund($order); } - public function testHistoryRequest(): void + /** + * @dataProvider historyRequestDataProvider + */ + public function testHistoryRequest(array $order, string $apiUrl): void { - $this->expectException(UnsupportedTransactionTypeException::class); - $this->pos->history([]); + $account = $this->pos->getAccount(); + $txType = PosInterface::TX_TYPE_HISTORY; + $requestData = ['createHistoryRequestData']; + + $this->requestMapperMock->expects(self::once()) + ->method('createHistoryRequestData') + ->with($account, $order) + ->willReturn($requestData); + + $decodedResponse = ['decodedData']; + $this->configureClientResponse( + $txType, + $apiUrl, + $requestData, + 'request-body', + 'response-body', + $decodedResponse, + $order, + PosInterface::MODEL_NON_SECURE + ); + + $this->responseMapperMock->expects(self::once()) + ->method('mapHistoryResponse') + ->with($decodedResponse) + ->willReturn(['result']); + + $this->pos->history($order); } /** @@ -632,4 +659,18 @@ private function configureClientResponse( && $order === $dispatchedEvent->getOrder() && $paymentModel === $dispatchedEvent->getPaymentModel())); } + + public static function historyRequestDataProvider(): array + { + return [ + [ + 'order' => [ + 'ip' => '127.0.0.1', + 'start_date' => new \DateTimeImmutable(), + 'end_date' => new \DateTimeImmutable(), + ], + 'api_url' => 'https://sanalposprovtest.garantibbva.com.tr/VPServlet', + ], + ]; + } } diff --git a/tests/Unit/test_data/garanti/history/daily_range_history_expected.json b/tests/Unit/test_data/garanti/history/daily_range_history_expected.json new file mode 100644 index 00000000..aa52d2c7 --- /dev/null +++ b/tests/Unit/test_data/garanti/history/daily_range_history_expected.json @@ -0,0 +1,155 @@ +{ + "proc_return_code": "00", + "error_code": null, + "error_message": null, + "status": "approved", + "status_detail": "approved", + "trans_count": 5, + "transactions": [ + { + "auth_code": "304919", + "proc_return_code": "00", + "transaction_id": null, + "transaction_time": { + "date": "2024-06-03 14:04:19.000000", + "timezone_type": 3, + "timezone": "UTC" + }, + "capture_time": { + "date": "2024-06-03 14:04:19.000000", + "timezone_type": 3, + "timezone": "UTC" + }, + "error_message": null, + "ref_ret_num": "415501676495", + "order_status": "PAYMENT_COMPLETED", + "transaction_type": "pay", + "first_amount": 10.01, + "capture_amount": 10.01, + "status": "approved", + "error_code": null, + "status_detail": "approved", + "capture": true, + "currency": "TRY", + "masked_number": "42822090****8015", + "order_id": "202406033030", + "batch_num": "576200", + "payment_model": "regular", + "installment_count": 0 + }, + { + "auth_code": null, + "proc_return_code": "05", + "transaction_id": null, + "transaction_time": { + "date": "2024-06-03 14:04:29.000000", + "timezone_type": 3, + "timezone": "UTC" + }, + "capture_time": null, + "error_message": "RPC-05 condition was raised", + "ref_ret_num": "415501676496", + "order_status": "ERROR", + "transaction_type": "refund", + "first_amount": null, + "capture_amount": null, + "status": "declined", + "error_code": "05", + "status_detail": "reject", + "capture": null, + "currency": null, + "masked_number": null, + "order_id": "202406033030", + "batch_num": "576200", + "payment_model": "regular" + }, + { + "auth_code": "304919", + "proc_return_code": "00", + "transaction_id": null, + "transaction_time": { + "date": "2024-06-03 14:20:59.000000", + "timezone_type": 3, + "timezone": "UTC" + }, + "capture_time": { + "date": "2024-06-03 14:20:59.000000", + "timezone_type": 3, + "timezone": "UTC" + }, + "error_message": null, + "ref_ret_num": "415501676585", + "order_status": "PAYMENT_COMPLETED", + "transaction_type": "pay", + "first_amount": 4903.17, + "capture_amount": 4903.17, + "status": "approved", + "error_code": null, + "status_detail": "approved", + "capture": true, + "currency": "TRY", + "masked_number": "42822090****8015", + "order_id": "__lwyvqzf9_____65848", + "batch_num": "576200", + "payment_model": "3d", + "installment_count": 0 + }, + { + "auth_code": "304919", + "proc_return_code": "00", + "transaction_id": null, + "transaction_time": { + "date": "2024-06-03 14:50:07.000000", + "timezone_type": 3, + "timezone": "UTC" + }, + "capture_time": { + "date": "2024-06-03 14:50:07.000000", + "timezone_type": 3, + "timezone": "UTC" + }, + "error_message": null, + "ref_ret_num": "415501676711", + "order_status": "PAYMENT_COMPLETED", + "transaction_type": "pay", + "first_amount": 276.0, + "capture_amount": 276.0, + "status": "approved", + "error_code": null, + "status_detail": "approved", + "capture": true, + "currency": "TRY", + "masked_number": "42822090****8015", + "order_id": "00000093877E202406031450", + "batch_num": "576200", + "payment_model": "3d", + "installment_count": 0 + }, + { + "auth_code": null, + "proc_return_code": "14", + "transaction_id": null, + "transaction_time": { + "date": "2024-06-03 15:09:57.000000", + "timezone_type": 3, + "timezone": "UTC" + }, + "capture_time": null, + "error_message": "HATALI KART NUMARASI.", + "ref_ret_num": "415501676775", + "order_status": "ERROR", + "transaction_type": "pre", + "first_amount": null, + "capture_amount": null, + "status": "declined", + "error_code": "14", + "status_detail": null, + "capture": null, + "currency": null, + "masked_number": null, + "order_id": "202406038F95", + "batch_num": "576200", + "payment_model": "regular" + } + ] +} diff --git a/tests/Unit/test_data/garanti/history/date_range_history.json b/tests/Unit/test_data/garanti/history/date_range_history.json new file mode 100644 index 00000000..fbfce410 --- /dev/null +++ b/tests/Unit/test_data/garanti/history/date_range_history.json @@ -0,0 +1,225 @@ +{ + "Mode": "", + "Terminal": { + "ProvUserID": "PROVAUT", + "UserID": "PROVAUT", + "ID": "30691298", + "MerchantID": "7000679" + }, + "Customer": { + "IPAddress": "172.26.0.1", + "EmailAddress": "" + }, + "Order": { + "OrderID": "", + "GroupID": "", + "OrderListInqResult": { + "OrderTxnList": { + "TotalTxnCount": "5", + "TotalPageCount": "1", + "ActPageNum": "1", + "OrderTxn": [ + { + "Id": "1", + "LastTrxDate": "2024-06-03 14:04:19", + "TrxType": "Satis", + "OrderID": "202406033030", + "Name": "", + "CardNumberMasked": "42822090****8015", + "ExpireDate": "0830", + "BankBin": "42822090", + "BatchNum": "576200", + "AuthCode": "304919", + "RetrefNum": "415501676495", + "OrigRetrefNum": "", + "InstallmentCnt": "Pesin", + "Status": "Basarili", + "AuthAmount": "1001", + "CurrencyCode": "TL", + "RemainingBNSAmount": "0", + "UsedFBBAmount": "0", + "UsedChequeType": "", + "UsedChequeCount": "0", + "UsedChequeAmount": "0", + "SafeType": "", + "Comment1": "", + "Comment2": "", + "Comment3": "", + "UserId": "PROVAUT", + "Settlement": "N", + "EmailAddress": "", + "RecurringTotalPaymentNum": "0", + "RecurringLastPaymentNum": "0", + "RecurringTxnAmount": "0", + "ResponseCode": "00", + "SysErrMsg": "" + }, + { + "Id": "2", + "LastTrxDate": "2024-06-03 14:04:29", + "TrxType": "Iade", + "OrderID": "202406033030", + "Name": "", + "CardNumberMasked": "42822090****8015", + "ExpireDate": "0830", + "BankBin": "42822090", + "BatchNum": "576200", + "AuthCode": "", + "RetrefNum": "415501676496", + "OrigRetrefNum": "415501676495", + "InstallmentCnt": "Pesin", + "Status": "Basarisiz", + "AuthAmount": "801", + "CurrencyCode": "TL", + "RemainingBNSAmount": "0", + "UsedFBBAmount": "0", + "UsedChequeType": "", + "UsedChequeCount": "0", + "UsedChequeAmount": "0", + "SafeType": "", + "Comment1": "", + "Comment2": "", + "Comment3": "", + "UserId": "PROVRFN", + "Settlement": "E", + "EmailAddress": "", + "RecurringTotalPaymentNum": "0", + "RecurringLastPaymentNum": "0", + "RecurringTxnAmount": "0", + "ResponseCode": "05", + "SysErrMsg": "RPC-05 condition was raised" + }, + { + "Id": "3", + "LastTrxDate": "2024-06-03 14:20:59", + "TrxType": "Satis", + "OrderID": "__lwyvqzf9_____65848", + "Name": "", + "CardNumberMasked": "42822090****8015", + "ExpireDate": "0327", + "BankBin": "42822090", + "BatchNum": "576200", + "AuthCode": "304919", + "RetrefNum": "415501676585", + "OrigRetrefNum": "", + "InstallmentCnt": "Pesin", + "Status": "Basarili", + "AuthAmount": "490317", + "CurrencyCode": "TL", + "RemainingBNSAmount": "0", + "UsedFBBAmount": "0", + "UsedChequeType": "", + "UsedChequeCount": "0", + "UsedChequeAmount": "0", + "SafeType": "3D", + "Comment1": "", + "Comment2": "", + "Comment3": "", + "UserId": "GARANTI", + "Settlement": "N", + "EmailAddress": "yigit.ozerdem@etstur.com", + "RecurringTotalPaymentNum": "0", + "RecurringLastPaymentNum": "0", + "RecurringTxnAmount": "0", + "ResponseCode": "00", + "SysErrMsg": "" + }, + { + "Id": "4", + "LastTrxDate": "2024-06-03 14:50:07", + "TrxType": "Satis", + "OrderID": "00000093877E202406031450", + "Name": "", + "CardNumberMasked": "42822090****8015", + "ExpireDate": "0926", + "BankBin": "42822090", + "BatchNum": "576200", + "AuthCode": "304919", + "RetrefNum": "415501676711", + "OrigRetrefNum": "", + "InstallmentCnt": "Pesin", + "Status": "Basarili", + "AuthAmount": "27600", + "CurrencyCode": "TL", + "RemainingBNSAmount": "0", + "UsedFBBAmount": "0", + "UsedChequeType": "", + "UsedChequeCount": "0", + "UsedChequeAmount": "0", + "SafeType": "3D", + "Comment1": "", + "Comment2": "", + "Comment3": "", + "UserId": "KKB_USER", + "Settlement": "N", + "EmailAddress": "ozlemtopuz@kkb.com.tr", + "RecurringTotalPaymentNum": "0", + "RecurringLastPaymentNum": "0", + "RecurringTxnAmount": "0", + "ResponseCode": "00", + "SysErrMsg": "" + }, + { + "Id": "5", + "LastTrxDate": "2024-06-03 15:09:57", + "TrxType": "On Otorizasyon", + "OrderID": "202406038F95", + "Name": "", + "CardNumberMasked": "42822090****8015", + "ExpireDate": "0830", + "BankBin": "42822090", + "BatchNum": "576200", + "AuthCode": "", + "RetrefNum": "415501676775", + "OrigRetrefNum": "", + "InstallmentCnt": "Pesin", + "Status": "Basarisiz", + "AuthAmount": "1001", + "CurrencyCode": "TL", + "RemainingBNSAmount": "0", + "UsedFBBAmount": "0", + "UsedChequeType": "", + "UsedChequeCount": "0", + "UsedChequeAmount": "0", + "SafeType": "", + "Comment1": "", + "Comment2": "", + "Comment3": "", + "UserId": "PROVAUT", + "Settlement": "E", + "EmailAddress": "", + "RecurringTotalPaymentNum": "0", + "RecurringLastPaymentNum": "0", + "RecurringTxnAmount": "0", + "ResponseCode": "14", + "SysErrMsg": "HATALI KART NUMARASI." + } + ] + } + } + }, + "Transaction": { + "Response": { + "Source": "GVPS", + "Code": "00", + "ReasonCode": "", + "Message": "Approved", + "ErrorMsg": "", + "SysErrMsg": "" + }, + "RetrefNum": "", + "AuthCode": "", + "BatchNum": "", + "SequenceNum": "", + "ProvDate": "20240603 15:12:44", + "CardNumberMasked": "", + "CardHolderName": "", + "CardType": "", + "HashData": "8143B6233C873EC7C38AA560C98F34C646CB1F375B84C6760D6EDEFD914F396042C88625F5F5BA205C64C0C7D4C2ACD8F478EEE730AFA159930B5D479BEEC1F5", + "HostMsgList": "", + "RewardInqResult": { + "RewardList": "", + "ChequeList": "" + } + } +}