Skip to content

Commit

Permalink
Kuveyt Pos added support for non secure payment
Browse files Browse the repository at this point in the history
  • Loading branch information
nuryagdym committed Apr 24, 2024
1 parent 6968056 commit 8a14553
Show file tree
Hide file tree
Showing 11 changed files with 652 additions and 59 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ sistemlerinin kullanılabilmesidir.
| PosNetV1<br/>(JSON API) | Albaraka Türk | NonSecure<br/>3DSecure | İptal<br/>İade<br/>Durum sorgulama |
| PayFor | Finansbank<br/>Enpara | NonSecure<br/>3DSecure<br/>3DPay<br/>3DHost | İptal<br/>İade<br/>Durum sorgulama<br/>Sipariş Tarihçesini sorgulama<br/>Geçmiş İşlemleri sorgulama |
| InterPOS | Deniz bank | NonSecure<br/>3DSecure<br/>3DPay<br/>3DHost | İptal<br/>İade<br/>Durum sorgulama |
| Kuveyt POS TDV2.0.0 | Kuveyt Türk | 3DSecure | İptal<br/>İade<br/>Durum sorgulama<br/>(SOAP API) |
| Kuveyt POS TDV2.0.0 | Kuveyt Türk | NonSecure<br/>3DSecure | İptal<br/>İade<br/>Durum sorgulama<br/>(SOAP API) |
| VakifKatilimPos<br/>(test edilmesi gerekiyor) | Vakıf Katılım | NonSecure<br/>3DSecure<br/>3DHost | İptal<br/>İade<br/>Durum sorgulama<br/>Sipariş Tarihçesini sorgulama<br/>Geçmiş İşlemleri sorgulama |

### Ana başlıklar
Expand Down
2 changes: 1 addition & 1 deletion config/pos_production.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
],
Expand Down
2 changes: 1 addition & 1 deletion config/pos_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
],
Expand Down
6 changes: 3 additions & 3 deletions examples/kuveytpos/regular/_config.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
27 changes: 25 additions & 2 deletions src/DataMapper/RequestDataMapper/KuveytPosRequestDataMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down Expand Up @@ -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;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
72 changes: 58 additions & 14 deletions src/Gateways/KuveytPos.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
*/
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);
Expand All @@ -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]);
Expand All @@ -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<string, mixed> $contents
* @param string $txType
* @param array<string, mixed> $contents
* @param string $txType
* @param string $url
*
* @return array<string, mixed>
*
* @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,
Expand Down Expand Up @@ -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];
}
}
116 changes: 116 additions & 0 deletions tests/Functional/KuveytPosTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Loading

0 comments on commit 8a14553

Please sign in to comment.