diff --git a/Controller/Callback.php b/Controller/Callback.php index 035df08..9fa69bb 100644 --- a/Controller/Callback.php +++ b/Controller/Callback.php @@ -4,6 +4,7 @@ use PicPay\Checkout\Helper\Data as HelperData; use PicPay\Checkout\Helper\Order as HelperOrder; +use PicPay\Checkout\Helper\Tds as HelperTds; use PicPay\Checkout\Model\CallbackFactory; use PicPay\Checkout\Model\ResourceModel\Callback as CallbackResourceModel; use Magento\Framework\App\Action\Action; @@ -33,6 +34,11 @@ abstract class Callback extends Action implements \Magento\Framework\App\CsrfAwa */ protected $helperOrder; + /** + * @var HelperTds + */ + protected $helperTds; + /** * @var CallbackFactory */ @@ -70,6 +76,7 @@ public function __construct( ResultFactory $resultFactory, HelperData $helperData, HelperOrder $helperOrder, + HelperTds $helperTds, CallbackFactory $callbackFactory, CallbackResourceModel $callbackResourceModel, ManagerInterface $eventManager, @@ -78,6 +85,7 @@ public function __construct( $this->resultFactory = $resultFactory; $this->helperData = $helperData; $this->helperOrder = $helperOrder; + $this->helperTds = $helperTds; $this->callbackFactory = $callbackFactory; $this->callbackResourceModel = $callbackResourceModel; $this->eventManager = $eventManager; diff --git a/Controller/Callback/Payments.php b/Controller/Callback/Payments.php index dc42f80..b24dbe3 100644 --- a/Controller/Callback/Payments.php +++ b/Controller/Callback/Payments.php @@ -11,14 +11,11 @@ use Magento\Framework\App\RequestInterface; use Magento\Framework\Controller\ResultFactory; use PicPay\Checkout\Controller\Callback; -use PicPay\Checkout\Gateway\Http\Client\Api; -use PicPay\Checkout\Helper\Order; use Laminas\Http\Response; -use PicPay\Checkout\Helper\Order as HelperOrder; -use Magento\Sales\Model\Order as SalesOrder; class Payments extends Callback { + public const DEFAULT_STATUS_CODE = 500; /** * @var string */ @@ -43,31 +40,29 @@ public function execute() $this->helperData->log(__('Webhook %1', __CLASS__), self::LOG_NAME); $result = $this->resultFactory->create(ResultFactory::TYPE_RAW); - $statusCode = 500; + $statusCode = self::DEFAULT_STATUS_CODE; $orderIncrementId = ''; try { - $content = $this->getContent($this->getRequest()); - $this->logParams($content); + $webhookData = $this->getContent($this->getRequest()); + $this->logParams($webhookData); $method = 'picpay-payments'; + $content = isset($webhookData['type']) ? $webhookData['data'] : $webhookData; + $chargeId = $content['chargeId'] ?? $content['merchantChargeId']; - $content = isset($content['type']) ? $content['data'] : $content; - - if (isset($content['status'])) { - $chargeId = $content['merchantChargeId']; - if (isset($content['status'])) { - $picpayStatus = $content['status']; - $order = $this->helperOrder->loadOrderByMerchantChargeId($chargeId); - if ($order->getId()) { - $orderIncrementId = $order->getIncrementId(); - $method = $order->getPayment()->getMethod(); - $amount = $content['amount'] ? $content['amount'] / 100 : $order->getGrandTotal(); - $refundedAmount = $content['refundedAmount'] ? $content['refundedAmount'] / 100 : 0; - - $this->helperOrder->updateOrder($order, $picpayStatus, $content, $amount, $method, true, $refundedAmount); - $statusCode = Response::STATUS_CODE_200; - } - } + $statusCode = $this->processTds($webhookData, $chargeId, $content, $statusCode); + if ( + isset($content['status']) + && $statusCode === self::DEFAULT_STATUS_CODE + && $order = $this->helperOrder->loadOrderByMerchantChargeId($chargeId) + ) { + $status = $content['status']; + $orderIncrementId = $order->getIncrementId(); + $method = $order->getPayment()->getMethod(); + $amount = $content['amount'] ? $content['amount'] / 100 : $order->getGrandTotal(); + $refundedAmount = $content['refundedAmount'] ? $content['refundedAmount'] / 100 : 0; + $this->helperOrder->updateOrder($order, $status, $content, $amount, $method, true, $refundedAmount); + $statusCode = Response::STATUS_CODE_200; } /** @var \PicPay\Checkout\Model\Callback $callBack */ @@ -78,11 +73,22 @@ public function execute() $callBack->setPayload($this->helperData->jsonEncode($content)); $this->callbackResourceModel->save($callBack); } catch (\Exception $e) { - $statusCode = 500; + $statusCode = self::DEFAULT_STATUS_CODE; $this->helperData->getLogger()->error($e->getMessage()); } $result->setHttpResponseCode($statusCode); return $result; } + + public function processTds(array $webhookData, string $chargeId, array $content, int $statusCode): int + { + $quote = $this->helperTds->loadQuoteByChargeId($chargeId); + if (isset($webhookData['type']) && $webhookData['type'] == 'THREE_DS_CHALLENGE' && $quote->getId()) { + $quote = $this->helperTds->loadQuoteByChargeId($chargeId); + $this->helperTds->updateQuote($quote, $content); + $statusCode = Response::STATUS_CODE_200; + } + return $statusCode; + } } diff --git a/Controller/Tds/Challenge.php b/Controller/Tds/Challenge.php new file mode 100644 index 0000000..5e2f7f3 --- /dev/null +++ b/Controller/Tds/Challenge.php @@ -0,0 +1,89 @@ +checkoutSession = $checkoutSession; + $this->json = $json; + $this->resultJsonFactory = $resultJsonFactory; + $this->quoteRepository = $quoteRepository; + + parent::__construct($context); + } + + public function execute() + { + try { + $result = $this->resultJsonFactory->create(); + + $quoteId = $this->checkoutSession->getQuoteId(); + $quote = $this->quoteRepository->get($quoteId); + + if ($quote->getPicpayChargeId()) { + $tdsChallengeStatus = $quote->getPicpayChallengeStatus(); + return $result->setData([ + 'challenge_hash' => hash_hmac('sha256', $quote->getPicpayChargeId(), $quote->getId()), + 'challenge_customer_email' => $quote->getCustomerEmail(), + 'challenge_customer_name' => $quote->getCustomerFirstname() . ' ' . $quote->getCustomerLastname(), + 'challenge_customer_address' => $quote->getBillingAddress()->getStreet(), + 'challenge_status' => $tdsChallengeStatus, + 'charge_id' => $quote->getPicpayChargeId() + ]); + } + } catch (\Exception $e) { + return $result->setData(['error' => true, 'message' => $e->getMessage()]); + } + + return $result->setData(['error' => true, 'message' => __('No orders found for this user.')]); + } + + public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException + { + $result = $this->resultFactory->create(ResultFactory::TYPE_RAW); + $result->setHttpResponseCode(403); + return new InvalidRequestException( + $result + ); + } + + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } +} diff --git a/Controller/Tds/Enrollment.php b/Controller/Tds/Enrollment.php new file mode 100644 index 0000000..cc70aa7 --- /dev/null +++ b/Controller/Tds/Enrollment.php @@ -0,0 +1,102 @@ +resultJsonFactory = $resultJsonFactory; + $this->json = $json; + $this->tds = $tds; + + parent::__construct($context); + } + + public function execute() + { + $result = $this->resultJsonFactory->create(); + + try { + $content = $this->getRequest()->getContent(); + $bodyParams = ($content) ? $this->json->unserialize($content) : []; + $response = $this->tds->runTdsRequest($bodyParams); + + if ($response['response']['chargeId']) { + $result->setJsonData($this->json->serialize($response['response']['transactions'][0])); + } + + $responseCode = 200; + } catch (\Exception $e) { + $responseCode = 500; + $this->messageManager->addErrorMessage($e->getMessage()); + } + + $result->setHttpResponseCode($responseCode); + return $result; + } + + /** + * @param RequestInterface $request + * @return InvalidRequestException|null + */ + public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException + { + $result = $this->resultFactory->create(ResultFactory::TYPE_RAW); + $result->setHttpResponseCode(403); + return new InvalidRequestException( + $result + ); + } + + /** + * @param RequestInterface $request + * @return bool|null + */ + public function validateForCsrf(RequestInterface $request): ?bool + { + return true; + } +} diff --git a/Gateway/Http/Client.php b/Gateway/Http/Client.php index e0a0d18..ab7eced 100644 --- a/Gateway/Http/Client.php +++ b/Gateway/Http/Client.php @@ -79,7 +79,7 @@ protected function getDefaultHeaders(): array { return [ 'Content-Type' => 'application/json', - 'caller-origin' => 'M2-v' . $this->helper->getModuleVersion() + 'caller-origin' => 'Magento' ]; } diff --git a/Gateway/Http/Client/Api.php b/Gateway/Http/Client/Api.php index 2d9e3b4..bb9d940 100644 --- a/Gateway/Http/Client/Api.php +++ b/Gateway/Http/Client/Api.php @@ -24,6 +24,7 @@ use PicPay\Checkout\Gateway\Http\Client\Api\Refund; use PicPay\Checkout\Gateway\Http\Client\Api\Capture; use PicPay\Checkout\Gateway\Http\Client\Api\Token; +use PicPay\Checkout\Gateway\Http\Client\Api\Tds; use PicPay\Checkout\Gateway\Http\ClientInterface; use PicPay\Checkout\Helper\Data; @@ -74,6 +75,11 @@ class Api */ private $token; + /** + * @var Tds + */ + private $tds; + /** * @var string */ @@ -88,7 +94,8 @@ public function __construct( Refund $refund, Capture $capture, Card $card, - Query $query + Query $query, + Tds $tds ) { $this->helper = $helper; $this->token = $token; @@ -99,6 +106,7 @@ public function __construct( $this->capture = $capture; $this->card = $card; $this->query = $query; + $this->tds = $tds; } /** @@ -186,6 +194,14 @@ public function card(): ClientInterface return $this->getClient($this->card); } + /** + * @throws \Exception + */ + public function tds(): ClientInterface + { + return $this->getClient($this->tds); + } + /** * @throws \Exception */ diff --git a/Gateway/Http/Client/Api/Tds.php b/Gateway/Http/Client/Api/Tds.php new file mode 100644 index 0000000..879c4b1 --- /dev/null +++ b/Gateway/Http/Client/Api/Tds.php @@ -0,0 +1,48 @@ +getEndpointPath('payments/tds_setup'); + $method = Request::METHOD_POST; + return $this->makeRequest($path, $method, 'payments', $data); + } + + public function enrollment(array $data): array + { + $path = $this->getEndpointPath('payments/tds_enrollment'); + $method = Request::METHOD_POST; + return $this->makeRequest($path, $method, 'payments', $data); + } + + public function challengeStatus($chargeId): array + { + $path = $this->getEndpointPath('payments/tds_challenge_status'); + $method = Request::METHOD_GET; + return $this->makeRequest($path, $method); + } + + public function authorization($data): array + { + $path = $this->getEndpointPath('payments/tds_authorization'); + $method = Request::METHOD_POST; + return $this->makeRequest($path, $method, 'payments', $data); + } +} diff --git a/Gateway/Http/Client/Transaction.php b/Gateway/Http/Client/Transaction.php index 3d74c2e..8f13873 100644 --- a/Gateway/Http/Client/Transaction.php +++ b/Gateway/Http/Client/Transaction.php @@ -71,7 +71,7 @@ public function placeRequest(TransferInterface $transferObject) break; default: - $transaction = $this->api->create()->execute($requestBody, $config['store_id']); + $transaction = $this->executeCardTransaction($config, $requestBody); } $this->api->logResponse($transaction, self::LOG_NAME); @@ -83,4 +83,21 @@ public function placeRequest(TransferInterface $transferObject) return ['status' => $status, 'status_code' => $statusCode, 'transaction' => $transaction['response']]; } + + /** + * @param $config + * @param $requestBody + * @return array + * @throws \Exception + */ + protected function executeCardTransaction($config, $requestBody): array + { + if ($config['use_tds']) { + $transaction = $this->api->tds()->authorization($requestBody); + $transaction['response'] = $transaction['response']['charge'] ?? $transaction['response']; + return $transaction; + } + + return $this->api->create()->execute($requestBody, $config['store_id']); + } } diff --git a/Gateway/Request/CaptureRequest.php b/Gateway/Request/CaptureRequest.php index 0f75bce..0cec75c 100644 --- a/Gateway/Request/CaptureRequest.php +++ b/Gateway/Request/CaptureRequest.php @@ -45,7 +45,7 @@ public function build(array $buildSubject) ]; $clientConfig = [ - 'order_id' => $payment->getAdditionalInformation('merchantChargeId'), + 'order_id' => $payment->getAdditionalInformation('merchantChargeId') ?? $order->getPicpayMerchantId(), 'status' => $payment->getAdditionalInformation('status'), 'store_id' => $order->getStoreId() ]; diff --git a/Gateway/Request/CreditCard/TransactionRequest.php b/Gateway/Request/CreditCard/TransactionRequest.php index 6e15339..74cff28 100644 --- a/Gateway/Request/CreditCard/TransactionRequest.php +++ b/Gateway/Request/CreditCard/TransactionRequest.php @@ -2,17 +2,24 @@ namespace PicPay\Checkout\Gateway\Request\CreditCard; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Customer\Model\Session as CustomerSession; +use Magento\Framework\Event\ManagerInterface; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Payment\Gateway\ConfigInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; +use PicPay\Checkout\Gateway\Http\Client\Api; use PicPay\Checkout\Gateway\Request\PaymentsRequest; use Magento\Payment\Gateway\Data\PaymentDataObjectInterface; use Magento\Payment\Gateway\Request\BuilderInterface; +use PicPay\Checkout\Helper\Data; use PicPay\Checkout\Model\Ui\CreditCard\ConfigProvider; class TransactionRequest extends PaymentsRequest implements BuilderInterface { - /** * Builds ENV request * @@ -32,10 +39,18 @@ public function build(array $buildSubject) $payment = $buildSubject['payment']->getPayment(); $order = $payment->getOrder(); - $this->validateCard($order, $payment); - $request = $this->getTransactions($order, $buildSubject['amount']); + if ($payment->getAdditionalInformation('use_tds_authorization')) { + $request = $this->authorizationRequest->getRequest($order); + } else { + $this->validateCard($order, $payment); + $request = $this->getTransactions($order, $buildSubject['amount']); + } - return ['request' => $request, 'client_config' => ['store_id' => $order->getStoreId()]]; + $clientConfig = [ + 'store_id' => $order->getStoreId(), + 'use_tds' => $payment->getAdditionalInformation('use_tds_authorization') + ]; + return ['request' => $request, 'client_config' => $clientConfig]; } /** diff --git a/Gateway/Request/PaymentsRequest.php b/Gateway/Request/PaymentsRequest.php index 678ef27..a9ca6fb 100644 --- a/Gateway/Request/PaymentsRequest.php +++ b/Gateway/Request/PaymentsRequest.php @@ -21,6 +21,7 @@ namespace PicPay\Checkout\Gateway\Request; use PicPay\Checkout\Gateway\Http\Client\Api; +use PicPay\Checkout\Gateway\Request\Tds\AuthorizationRequest; use PicPay\Checkout\Helper\Data; use Magento\Customer\Model\Session as CustomerSession; use Magento\Framework\Event\ManagerInterface; @@ -57,6 +58,10 @@ class PaymentsRequest */ protected $date; + /** + * @var AuthorizationRequest + */ + protected $authorizationRequest; /** * @var DateTime @@ -92,7 +97,8 @@ public function __construct( CustomerSession $customerSession, CategoryRepositoryInterface $categoryRepository, ProductRepositoryInterface $productRepository, - Api $api + Api $api, + AuthorizationRequest $authorizationRequest ) { $this->eventManager = $eventManager; $this->helper = $helper; @@ -103,6 +109,7 @@ public function __construct( $this->categoryRepository = $categoryRepository; $this->productRepository = $productRepository; $this->api = $api; + $this->authorizationRequest = $authorizationRequest; } protected function getTransactions(Order $order, float $amount): array diff --git a/Gateway/Request/RefundRequest.php b/Gateway/Request/RefundRequest.php index 95cc03b..9793234 100644 --- a/Gateway/Request/RefundRequest.php +++ b/Gateway/Request/RefundRequest.php @@ -71,7 +71,7 @@ public function build(array $buildSubject) ]; $clientConfig = [ - 'order_id' => $payment->getAdditionalInformation('merchantChargeId'), + 'order_id' => $payment->getAdditionalInformation('merchantChargeId') ?? $order->getPicpayMerchantId(), 'store_id' => $order->getStoreId() ]; diff --git a/Gateway/Request/Tds/AuthorizationRequest.php b/Gateway/Request/Tds/AuthorizationRequest.php new file mode 100644 index 0000000..4c64ada --- /dev/null +++ b/Gateway/Request/Tds/AuthorizationRequest.php @@ -0,0 +1,61 @@ +helper = $helper; + } + + /** + * @param Order $order + * @return array + */ + public function getRequest(Order $order): array + { + return [ + 'chargeId' => $order->getPicpayChargeId(), + 'capture' => !$this->helper->isLateCapture(), + 'transactions' => $this->getAuthTransactionInfo($order) + ]; + } + + /** + * @param Order $order + * @return array + */ + protected function getAuthTransactionInfo(Order $order): array + { + $installments = (int) $order->getPayment()->getAdditionalInformation('cc_installments') ?: 1; + + $transactionInfo = [ + 'installmentNumber' => $installments, + 'installmentType' => $installments > 1 ? 'MERCHANT' : 'NONE', + 'card' => $this->getCardData($order, $order->getPayment()), + ]; + return [$transactionInfo]; + } + + + protected function getCardData(Order $order, Payment $payment): array + { + return [ + 'cvv' => $payment->getCcCid(), + 'cardholderAuthenticationId' => $order->getPicpayCardholderAuthId() + ]; + } +} diff --git a/Gateway/Request/Tds/EnrollmentRequest.php b/Gateway/Request/Tds/EnrollmentRequest.php new file mode 100644 index 0000000..e4a71f3 --- /dev/null +++ b/Gateway/Request/Tds/EnrollmentRequest.php @@ -0,0 +1,109 @@ +getEnrollmentTransactions($quote, $buildSubject, $payment->getAdditionalData()); + return ['request' => $request, 'client_config' => ['store_id' => $quote->getStoreId()]]; + } + + /** + * @param Quote $quote + * @param $paymentData + * @param $additionalData + * @return array + */ + protected function getEnrollmentTransactions(Quote $quote, $paymentData, $additionalData): array + { + return [ + 'chargeId' => $paymentData['chargeId'], + 'customer' => $this->getTdsCustomerData($quote), + 'browser' => $this->getBrowserData($additionalData['browser_data']), + 'transactions' => $this->getTdsTransactionInfo($quote, $paymentData['amount']) + ]; + } + + /** + * @param Quote $quote + * @return array + */ + protected function getTdsCustomerData(Quote $quote): array + { + $payment = $quote->getPayment(); + $taxvat = (string) $payment->getAdditionalInformation('picpay_customer_taxvat'); + + $address = $quote->getBillingAddress(); + $fullName = $this->getCustomerFullName($quote); + $phoneNumber = $this->helper->formatPhoneNumber($address->getTelephone()); + + $customerData = [ + 'name' => $fullName, + 'email' => $quote->getCustomerEmail(), + 'documentType' => $this->getDocumentType($taxvat), + 'document' => $taxvat, + 'phone' => $phoneNumber, + ]; + + if ($quote->getCustomerDob()) { + $customerData['birth_date'] = $this->helper->formatDate($quote->getCustomerDob()); + } + + return $customerData; + } + + /** + * @param $data + * @return array + */ + protected function getBrowserData($data) + { + $browserData = [ + 'httpAcceptBrowserValue' => $_SERVER['HTTP_ACCEPT'] , + 'httpAcceptContent' => $_SERVER['HTTP_ACCEPT'] , + ]; + return array_merge($browserData, $data); + } + + /** + * @param Quote $quote + * @param float $orderAmount + * @return array + */ + protected function getTdsTransactionInfo(Quote $quote, float $orderAmount): array + { + $cardData = $this->getCardData($quote); + unset($cardData['cardType']); + + $cardData['number'] = $cardData['cardNumber']; + unset($cardData['cardNumber']); + + $transactionInfo = [ + 'amount' => $orderAmount * 100, + 'card' => $cardData + ]; + return [$transactionInfo]; + } +} diff --git a/Gateway/Request/Tds/SetupRequest.php b/Gateway/Request/Tds/SetupRequest.php new file mode 100644 index 0000000..ea6c43d --- /dev/null +++ b/Gateway/Request/Tds/SetupRequest.php @@ -0,0 +1,59 @@ +getSetupTransactions($quote, $buildSubject['amount']); + return ['request' => $request, 'client_config' => ['store_id' => $quote->getStoreId()]]; + } + + protected function getSetupTransactions(Quote $quote, float $amount): array + { + return [ + 'paymentSource' => 'GATEWAY', + 'transactions' => $this->getTdsTransactionInfo($quote, $amount) + ]; + } + + /** + * @param Quote $quote + * @param float $orderAmount + * @return array[] + * @throws \Exception + */ + protected function getTdsTransactionInfo(Quote $quote, float $orderAmount): array + { + $cardData = $this->getCardData($quote); + + unset($cardData['cardType']); + $transactionInfo = [ + 'amount' => $orderAmount * 100, + 'paymentType' => 'CREDIT', + 'card' => $cardData + ]; + return [$transactionInfo]; + } +} diff --git a/Gateway/Request/Tds/TdsRequest.php b/Gateway/Request/Tds/TdsRequest.php new file mode 100644 index 0000000..85d5c15 --- /dev/null +++ b/Gateway/Request/Tds/TdsRequest.php @@ -0,0 +1,85 @@ +getPayment(); + $taxvat = (string) $payment->getAdditionalInformation('picpay_customer_taxvat'); + + return [ + 'cardType' => 'CREDIT', + 'cardNumber' => $payment->getCcNumber(), + 'cvv' => $payment->getCcCid(), + 'brand' => $this->getCardType($payment->getCcType()), + 'cardholderName' => $payment->getCcOwner(), + 'cardholderDocument' => $this->helper->digits($taxvat), + 'expirationMonth' => (int) $payment->getCcExpMonth(), + 'expirationYear' => (int) $payment->getCcExpYear(), + 'billingAddress' => $this->getQuoteBillingAddress($quote) + ]; + } + + /** + * @param Quote $quote + * @return array + */ + protected function getQuoteBillingAddress(Quote $quote): array + { + $billingAddress = $quote->getBillingAddress(); + $number = $billingAddress->getStreetLine($this->getStreetField('number')) ?: 0; + $complement = $billingAddress->getStreetLine($this->getStreetField('complement')); + $address = [ + 'street' => $billingAddress->getStreetLine($this->getStreetField('street')), + 'number' => $number, + 'neighborhood' => $billingAddress->getStreetLine($this->getStreetField('district')), + 'city' => $billingAddress->getCity(), + 'state' => $billingAddress->getRegionCode(), + 'country' => $billingAddress->getCountryId(), + 'zipCode' => $this->helper->clearNumber($billingAddress->getPostcode()), + ]; + + if ($complement) { + $address['complement'] = $complement; + } + + return $address; + } + + /** + * @param Quote $quote + * @return string + */ + protected function getCustomerFullName(Quote $quote): string + { + $firstName = $quote->getCustomerFirstname(); + $lastName = $quote->getCustomerLastname(); + return $firstName . ' ' . $lastName; + } + + /** + * @param $type + * @return string + */ + protected function getCardType($type) + { + $types = [ + 'MC' => 'MASTERCARD', + 'VI' => 'VISA', + 'ELO' => 'ELO', + ]; + + return $types[$type] ?? ''; + } +} diff --git a/Gateway/Response/CreditCard/TransactionHandler.php b/Gateway/Response/CreditCard/TransactionHandler.php index 49b6373..ea64de7 100644 --- a/Gateway/Response/CreditCard/TransactionHandler.php +++ b/Gateway/Response/CreditCard/TransactionHandler.php @@ -73,11 +73,15 @@ public function handle(array $handlingSubject, array $response) $payment = $this->helperOrder->updateDefaultAdditionalInfo($payment, $transaction); $payment = $this->helperOrder->updatePaymentAdditionalInfo($payment, $transaction['transactions'], 'credit'); - if ($transaction['chargeStatus'] == HelperOrder::STATUS_PRE_AUTHORIZED) { + if ( + $transaction['chargeStatus'] == HelperOrder::STATUS_PRE_AUTHORIZED + || $transaction['chargeStatus'] == HelperOrder::STATUS_CHARGE_PRE_AUTHORIZED + ) { $payment->getOrder()->setState('new'); $payment->setSkipOrderProcessing(true); } - $payment->getOrder()->setData('picpay_charge_id', $transaction['merchantChargeId']); + $merchantChargeId = $transaction['merchantChargeId'] ?? $payment->getOrder()->getPicpayMerchantId(); + $payment->getOrder()->setData('picpay_charge_id', $merchantChargeId); } } diff --git a/Gateway/Response/PaymentsHandler.php b/Gateway/Response/PaymentsHandler.php index 5e21339..ed7c941 100644 --- a/Gateway/Response/PaymentsHandler.php +++ b/Gateway/Response/PaymentsHandler.php @@ -44,7 +44,10 @@ public function validateResponse(array $handlingSubject, array $response): array } $transaction = $response['transaction']; - if (!isset($transaction['merchantChargeId']) || !isset($transaction['chargeStatus'])) { + if ( + (!isset($transaction['merchantChargeId']) && !isset($transaction['id'])) + || !isset($transaction['chargeStatus']) + ) { throw new LocalizedException(__('There was an error processing your request.')); } return array($handlingSubject['payment'], $transaction); diff --git a/Helper/Data.php b/Helper/Data.php index 9b27971..655c6b3 100644 --- a/Helper/Data.php +++ b/Helper/Data.php @@ -14,6 +14,7 @@ namespace PicPay\Checkout\Helper; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\HTTP\Header; @@ -122,6 +123,9 @@ class Data extends \Magento\Payment\Helper\Data /** @var SessionManager */ protected $sessionManager; + /** @var ResourceConnection */ + protected $resourceConnection; + public function __construct( Context $context, LayoutFactory $layoutFactory, @@ -141,6 +145,7 @@ public function __construct( OrderInterface $order, Header $httpHeader, SessionManager $sessionManager, + ResourceConnection $resourceConnection, ComponentRegistrar $componentRegistrar, DateTime $dateTime, DirectoryData $helperDirectory, @@ -168,6 +173,7 @@ public function __construct( $this->order = $order; $this->httpHeader = $httpHeader; $this->sessionManager = $sessionManager; + $this->resourceConnection = $resourceConnection; $this->componentRegistrar = $componentRegistrar; $this->dateTime = $dateTime; $this->helperDirectory = $helperDirectory; @@ -311,6 +317,7 @@ public function saveRequest( $request = $this->serializeAndMask($request); $response = $this->serializeAndMask($response); + $connection = $this->resourceConnection->getConnection(); $requestModel = $this->requestFactory->create(); $requestModel->setRequest($request); $requestModel->setResponse($response); @@ -318,6 +325,7 @@ public function saveRequest( $requestModel->setStatusCode($statusCode); $this->requestRepository->save($requestModel); + $connection->commit(); } catch (\Exception $e) { $this->log($e->getMessage()); } diff --git a/Helper/Order.php b/Helper/Order.php index ddf2101..9736b7e 100644 --- a/Helper/Order.php +++ b/Helper/Order.php @@ -9,17 +9,11 @@ namespace PicPay\Checkout\Helper; -use BaconQrCode\Renderer\ImageRenderer as QrCodeImageRenderer; -use BaconQrCode\Renderer\Image\ImagickImageBackEnd as QrCodeImagickImageBackEnd; -use BaconQrCode\Renderer\RendererStyle\RendererStyle as QrCodeRendererStyle; -use BaconQrCode\Writer as QrCodeWritter; use Magento\Framework\Exception\LocalizedException; use PicPay\Checkout\Helper\Data as HelperData; use PicPay\Checkout\Gateway\Http\Client; use PicPay\Checkout\Gateway\Http\Client\Api; -use PicPay\Checkout\Model\Ui\CreditCard\ConfigProvider as CcConfigProvider; use Magento\Framework\App\Config\Initial; -use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\App\Helper\Context; use Magento\Framework\Filesystem; use Magento\Framework\Stdlib\DateTime\DateTime; @@ -45,6 +39,8 @@ class Order extends \Magento\Payment\Helper\Data public const STATUS_PRE_AUTHORIZED = 'PRE_AUTHORIZED'; + public const STATUS_CHARGE_PRE_AUTHORIZED = 'PreAuthorized'; + public const STATUS_PARTIAL = 'PARTIAL'; public const STATUS_ERROR = 'ERROR'; @@ -65,7 +61,7 @@ class Order extends \Magento\Payment\Helper\Data protected $orderFactory; /** - * @var OrderFactory + * @var OrderRepository */ protected $orderRepository; @@ -116,7 +112,6 @@ class Order extends \Magento\Payment\Helper\Data protected $dateTime; /** - * Order constructor. * @param Context $context * @param LayoutFactory $layoutFactory * @param Factory $paymentMethodFactory @@ -428,13 +423,17 @@ public function getStatusState(string $status): string return ''; } - public function loadOrderByMerchantChargeId(string $chargeId): SalesOrder + /** + * @param string $chargeId + * @return SalesOrder|false + */ + public function loadOrderByMerchantChargeId(string $chargeId): SalesOrder|false { $order = $this->orderFactory->create(); if ($chargeId) { $order->loadByAttribute('picpay_charge_id', $chargeId); } - return $order; + return $order->getId() ? $order : false; } /** diff --git a/Helper/Tds.php b/Helper/Tds.php new file mode 100644 index 0000000..0a9519c --- /dev/null +++ b/Helper/Tds.php @@ -0,0 +1,86 @@ +quoteRepository = $quoteRepository; + $this->quoteCollectionFactory = $quoteCollectionFactory; + $this->resourcePayment = $resourcePayment; + parent::__construct($context); + } + + + /** + * @param string $chargeId + * @return \Magento\Framework\DataObject + */ + public function loadQuoteByChargeId(string $chargeId) + { + $collection = $this->quoteCollectionFactory->create(); + $collection->addFieldToFilter('picpay_charge_id', $chargeId); + return $collection->getFirstItem(); + } + + /** + * @param $quote + * @param $content + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function updateQuote($quote, $content) + { + $quote->setPicpayChallengeStatus($content['status']); + $quote->setPicpayMerchantId($content['merchantChargeId']); + $this->quoteRepository->save($quote); + } + +} diff --git a/Model/CheckoutTds.php b/Model/CheckoutTds.php new file mode 100644 index 0000000..31a3e61 --- /dev/null +++ b/Model/CheckoutTds.php @@ -0,0 +1,196 @@ +checkoutSession = $checkoutSession; + $this->helperData = $helperData; + $this->enrollmentRequest = $enrollmentRequest; + $this->setupRequest = $setupRequest; + $this->eventManager = $eventManager; + $this->api = $api; + $this->quoteRepository = $quoteRepository; + } + + /** + * @param $data + * @return mixed|void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function runTdsRequest($data) + { + $enrollment = []; + $paymentData = $this->getPaymentData($data); + $setup = $this->createSetup($paymentData); + + if ($setup['response']['chargeId']) { + $enrollment = $this->runEnrollment($setup['response']['chargeId'], $paymentData); + $this->processEnrollment($enrollment, $setup['response']['transactions'][0]['cardholderAuthenticationId']); + } + + return $enrollment; + } + + /** + * @param $params + * @return \Magento\Framework\DataObject + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + protected function getPaymentData($params) + { + $paymentData = new \Magento\Framework\DataObject(); + $paymentData->setData($params); + + $this->eventManager->dispatch( + 'payment_method_assign_data_picpay_checkout_cc', + [ + AbstractDataAssignObserver::METHOD_CODE => 'picpay_checkout_cc', + AbstractDataAssignObserver::MODEL_CODE => $this->checkoutSession->getQuote()->getPayment(), + AbstractDataAssignObserver::DATA_CODE => $paymentData + ] + ); + return $paymentData; + } + + /** + * @param $paymentData + * @return mixed + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function createSetup($paymentData) + { + $quote = $this->checkoutSession->getQuote(); + + $transaction = $this->setupRequest->build([ + 'payment' => $paymentData, + 'quote' => $quote, + 'amount' => $quote->getGrandTotal() + ]); + + $result = $this->api->tds()->setup($transaction['request']); + + if ($result['status'] == 200) { + $this->checkoutSession->setPicPayTdsChargeId($result['response']['chargeId']); + $this->checkoutSession->setPicPayTdsSetupTransaction($result['response']['transactions']); + return $result; + } + + throw new \Exception('Error trying to create 3DS setup on PicPay'); + } + + /** + * @param $chargeId + * @param $paymentData + * @return mixed + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function runEnrollment($chargeId, $paymentData) + { + $quote = $this->checkoutSession->getQuote(); + $transaction = $this->enrollmentRequest->build([ + 'payment' => $paymentData, + 'quote' => $quote, + 'amount' => $quote->getGrandTotal(), + 'chargeId' => $chargeId + ]); + + $result = $this->api->tds()->enrollment($transaction['request']); + + if ($result['status'] == 200) { + $this->checkoutSession->setPicPayTdsChargeStatus($result['response']['chargeStatus']); + $this->checkoutSession->setPicPayTdsEnrollmentTransaction($result['response']['transactions']); + return $result; + } + + throw new \Exception('Error trying to create 3DS setup on PicPay'); + } + + /** + * @param mixed $enrollment + * @param $cardholderAuthenticationId + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function processEnrollment($enrollment, $cardholderAuthenticationId): void + { + if (isset($enrollment['response']['chargeId']) && isset($enrollment['response']['transactions'][0])) { + $transaction = $enrollment['response']['transactions'][0]; + $this->checkoutSession->setPicPayTdsChallengeStatus($transaction['cardholderAuthenticationStatus']); + + $quote = $this->checkoutSession->getQuote(); + $quote->setPicpayChargeId($enrollment['response']['chargeId']); + $quote->setPicpayChallengeStatus($transaction['cardholderAuthenticationStatus']); + $quote->setPicpayMerchantId($cardholderAuthenticationId); + $quote->setPicpayCardholderAuthId($cardholderAuthenticationId); + $this->quoteRepository->save($quote); + } + } +} diff --git a/Model/Ui/CreditCard/ConfigProvider.php b/Model/Ui/CreditCard/ConfigProvider.php index ca464fe..a8d20d4 100644 --- a/Model/Ui/CreditCard/ConfigProvider.php +++ b/Model/Ui/CreditCard/ConfigProvider.php @@ -86,7 +86,9 @@ public function getConfig() 'customer_taxvat' => $customerTaxvat, 'sandbox' => (int) $this->helper->getGeneralConfig('use_sandbox'), 'icons' => $this->getPaymentIcons(), - 'availableTypes' => $this->getCcAvailableTypes($methodCode) + 'availableTypes' => $this->getCcAvailableTypes($methodCode), + 'use_tds' => (int) $this->canUseTds($grandTotal), + 'place_not_authenticated_order' => (int) $this->helper->getConfig('place_not_authenticated_tds'), ], 'ccform' => [ 'grandTotal' => [$methodCode => $grandTotal], @@ -104,6 +106,14 @@ public function getConfig() ]; } + public function canUseTds($amount) + { + $isActive = $this->helper->getConfig('tds_active'); + $minAmount = $this->helper->getConfig('min_tds_order_total'); + + return $isActive && $minAmount <= $amount; + } + /** * Get icons for available payment methods * diff --git a/Model/Ui/Pix/ConfigProvider.php b/Model/Ui/Pix/ConfigProvider.php index 1802155..081b0e6 100644 --- a/Model/Ui/Pix/ConfigProvider.php +++ b/Model/Ui/Pix/ConfigProvider.php @@ -73,12 +73,16 @@ public function __construct( */ public function getConfig() { + $customer = $this->customerSession->getCustomer(); + $customerTaxvat = ($customer && $customer->getTaxvat()) ? $customer->getTaxvat() : ''; + return [ 'payment' => [ self::CODE => [ 'grand_total' => $this->checkoutSession->getQuote()->getGrandTotal(), 'sandbox' => (int) $this->helper->getGeneralConfig('use_sandbox'), - 'checkout_instructions' => $this->helper->getConfig('checkout_instructions', self::CODE) + 'checkout_instructions' => $this->helper->getConfig('checkout_instructions', self::CODE), + 'customer_taxvat' => $customerTaxvat, ] ] ]; diff --git a/Observer/CreditCardAssignObserver.php b/Observer/CreditCardAssignObserver.php index 1e2bd47..25ccae4 100644 --- a/Observer/CreditCardAssignObserver.php +++ b/Observer/CreditCardAssignObserver.php @@ -88,7 +88,12 @@ public function execute(Observer $observer) 'cc_exp_year' => $ccExpYear ]); - $extraInfo = ['installments' => $installments, 'cc_installments' => $installments, 'cc_bin' => $ccBin]; + $extraInfo = [ + 'installments' => $installments, + 'cc_installments' => $installments, + 'cc_bin' => $ccBin, + 'use_tds_authorization' => $additionalData['use_tds_authorization'] ?? 0, + ]; foreach ($extraInfo as $key => $value) { $paymentInfo->setAdditionalInformation($key, $value); } diff --git a/composer.json b/composer.json index 8b6697e..28d4978 100755 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "picpay/ecommerce-integration-magento2", - "version": "1.2.2", + "version": "1.3.2", "description": "N/A", "type": "magento2-module", "autoload": { diff --git a/etc/adminhtml/system/credit_card.xml b/etc/adminhtml/system/credit_card.xml index 3c277ff..e0c3dc7 100644 --- a/etc/adminhtml/system/credit_card.xml +++ b/etc/adminhtml/system/credit_card.xml @@ -217,5 +217,34 @@ payment/picpay_checkout_cc/max_order_total + + + Magento\Config\Block\System\Config\Form\Fieldset + + + Magento\Config\Model\Config\Source\Yesno + payment/picpay_checkout_cc/tds_active + + + + Magento\Config\Model\Config\Source\Yesno + payment/picpay_checkout_cc/place_not_authenticated_tds + + 1 + + + + validate-number validate-zero-or-greater + + + payment/picpay_checkout_cc/min_tds_order_total + + 1 + + + + 1 + + diff --git a/etc/config.xml b/etc/config.xml index ef82277..51aa017 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -40,6 +40,10 @@ api/v1/charge/{order_id}/refund api/v1/charge/{order_id}/refund api/v1/cards/verification + api/v1/charge/3ds/setup + api/v1/charge/3ds/enrollment + api/v1/charge/3ds/:chargeId/status + api/v1/charge/3ds/authorization diff --git a/etc/csp_whitelist.xml b/etc/csp_whitelist.xml new file mode 100644 index 0000000..187e535 --- /dev/null +++ b/etc/csp_whitelist.xml @@ -0,0 +1,90 @@ + + + + + + *.picpay.com + *.pagar.me + *.netsgroup.com + *.sibs.pt + *.seglan.com + *.secureacs.com + *.rsa3dsauth.com + *.apata.io + *.cardinalcommerce.com + *.santander.com.br + *.bradesco.com.br + *.bradesco + *.stone.com.br + *.nubank.com.br + *.itau.com.br + *.bb.com.br + *.caixa.gov.br + *.inter.co + *.bancointer.com.br + *.c6bank.com.br + *.bancobmg.com.br + *.safra.com.br + *.sicoob.com.br + *.banrisul.com.br + *.banrisul.b.br + *.banorte.com + *.xpi.com.br + *.btgpactual.com + *.btgpactualdigital.com + *.mercadopago.com.br + *.mercadopago.com + *.amedigital.com + *.neon.tech + *.neon.com.br + *.wise.com + *.revolut.com + *.sandbox.3dsecure.io + *.google.com + + + + + *.pagar.me + *.netsgroup.com + *.sibs.pt + *.seglan.com + *.secureacs.com + *.rsa3dsauth.com + *.apata.io + *.cardinalcommerce.com + *.santander.com.br + *.bradesco.com.br + *.bradesco + *.stone.com.br + *.nubank.com.br + *.itau.com.br + *.bb.com.br + *.caixa.gov.br + *.inter.co + *.bancointer.com.br + *.c6bank.com.br + *.bancobmg.com.br + *.safra.com.br + *.sicoob.com.br + *.banrisul.com.br + *.banrisul.b.br + *.banorte.com + *.xpi.com.br + *.btgpactual.com + *.btgpactualdigital.com + *.mercadopago.com.br + *.mercadopago.com + *.picpay.com + *.amedigital.com + *.neon.tech + *.neon.com.br + *.wise.com + *.revolut.com + *.sandbox.3dsecure.io + *.google.com + + + + diff --git a/etc/db_schema.xml b/etc/db_schema.xml index 4f3bd9f..00795e4 100644 --- a/etc/db_schema.xml +++ b/etc/db_schema.xml @@ -71,6 +71,8 @@ + +
@@ -86,6 +88,10 @@ + + + +
diff --git a/etc/di.xml b/etc/di.xml index f6e8016..52f35d6 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -230,6 +230,13 @@ + + + + PicPayCreditCardConfig + + + PicPayPixConfig diff --git a/etc/events.xml b/etc/events.xml index 099fe03..70ec813 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -19,6 +19,14 @@ + + + + + + + + diff --git a/etc/fieldset.xml b/etc/fieldset.xml index fdf99eb..29c43e7 100644 --- a/etc/fieldset.xml +++ b/etc/fieldset.xml @@ -21,6 +21,18 @@ + + + + + + + + + + + +
diff --git a/i18n/en_US.csv b/i18n/en_US.csv index 47d254f..04c1207 100644 --- a/i18n/en_US.csv +++ b/i18n/en_US.csv @@ -135,3 +135,6 @@ Response,Response "Success Page Instructions","Success Page Instructions" "Instructions to be shown in success page, it can be HTML","Instructions to be shown in success page, it can be HTML" "Payment received.","Payment received." +"We were unable to authenticate your transaction, please try again.", "We were unable to authenticate your transaction, please try again." +"Place order if authentication fails", "Place order if authentication fails" +"Order minimum amount to use 3DS", "Order minimum amount to use 3DS" diff --git a/i18n/pt_BR.csv b/i18n/pt_BR.csv index c006b6c..47168f1 100644 --- a/i18n/pt_BR.csv +++ b/i18n/pt_BR.csv @@ -131,3 +131,6 @@ Response,Resposta "Success Page Instructions","Instruções da Página de Sucesso" "Instructions to be shown in success page, it can be HTML","Instruções para ser mostrada na página de sucesso, pode ser HTML" "Payment received.", "Pagamento recebido." +"We were unable to authenticate your transaction, please try again.", "Não foi possível autenticar sua transação. Por favor, tente novamente." +"Place order if authentication fails", "Finalizar pedido mesmo que a autenticação falhe" +"Order minimum amount to use 3DS", "Valor mínimo do pedido para usar 3DS" diff --git a/view/base/web/js/view/info/pix.js b/view/base/web/js/view/info/pix.js index 59b6e97..96c53c0 100644 --- a/view/base/web/js/view/info/pix.js +++ b/view/base/web/js/view/info/pix.js @@ -62,7 +62,7 @@ define([ checkPaymentStatus: function () { let source = new EventSource( - urlBuilder.build('picpay_checkout/payment/status?' + 'expiration_time=' + this.expiration_time()) + urlBuilder.build('picpay_checkout/payment/status?' + 'order_id=' + this.expiration_time()) ); let self = this; source.onmessage = function(event) { diff --git a/view/frontend/templates/payment/info/cc.phtml b/view/frontend/templates/payment/info/cc.phtml index 5ed5e70..0d0a6cf 100644 --- a/view/frontend/templates/payment/info/cc.phtml +++ b/view/frontend/templates/payment/info/cc.phtml @@ -27,7 +27,7 @@ */ $specificInfo = $block->getSpecificInformation(); $title = $block->escapeHtml($block->getMethod()->getTitle()); -?>zz +?>
diff --git a/view/frontend/web/css/checkout.less b/view/frontend/web/css/checkout.less index d1aa74a..351926c 100644 --- a/view/frontend/web/css/checkout.less +++ b/view/frontend/web/css/checkout.less @@ -23,7 +23,7 @@ margin-right: auto; } .pix-barcode-container { - text-align: center; + justify-self: center; margin: 10px 0; .pix-img-code { diff --git a/view/frontend/web/js/credit-card/tds.js b/view/frontend/web/js/credit-card/tds.js new file mode 100644 index 0000000..8fee69d --- /dev/null +++ b/view/frontend/web/js/credit-card/tds.js @@ -0,0 +1,187 @@ +/** + * PicPay + * + * NOTICE OF LICENSE + * + * This source file is subject to the PicPay license that is + * available through the world-wide-web at this URL: + * + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade this extension to newer + * version in the future. + * + * @category PicPay + * @package PicPay_Checkout + * @copyright Copyright (c) PicPay + * + */ + +define([ + 'underscore', + 'ko', + 'jquery', + 'mage/translate', + 'mage/url', + 'Magento_Ui/js/modal/modal', + 'Magento_Checkout/js/model/full-screen-loader', + 'Magento_Customer/js/customer-data', + 'Magento_Ui/js/model/messageList' + ], function ( + _, + ko, + $, + $t, + urlBuilder, + modal, + fullScreenLoader, + customerData, + messageList +) { + 'use strict'; + + return class Tds { + runTds(cardData, placeOrderCallback) { + let self = this; + cardData.additional_data.browser_data = this.getBrowserData(); + + $.ajax({ + url: urlBuilder.build('picpay_checkout/tds/enrollment'), + global: true, + data: JSON.stringify(cardData), + contentType: 'application/json', + type: 'POST', + async: true + }).done(function (data) { + if (data['cardholderAuthenticationStatus'] == 'Challenged' && data['accessToken']) { + self.setTdsIframe(data); + $('#picpay-tds-step-up-form').submit(); + $('#picpay-tds-modal').modal('openModal'); + fullScreenLoader.stopLoader(); + self.checkChallengeStatus(placeOrderCallback); + return; + } + + if (data['cardholderAuthenticationStatus'] == 'Approved') { + self.placeOrder(placeOrderCallback, true); + return; + } + + if (data['cardholderAuthenticationStatus'] == 'Rejected' && self.canPlaceNotAuthorizedOrder()) { + self.placeOrder(placeOrderCallback, false); + return; + } + + $('#picpay-tds-modal').modal('closeModal'); + self.displayErrorMessage($t('We were unable to authenticate your transaction, please try again.')); + fullScreenLoader.stopLoader(); + }); + } + + initTdsModal() { + let modalOptions = { + type: 'popup', + responsive: true, + innerScroll: false, + modalClass: 'picpay-tds-modal', + buttons: [] + }; + + modal(modalOptions, $('#picpay-tds-modal')); + } + + setTdsIframe(data) { + let iframe = $('#picpay-tds-step-up-iframe'); + let modal = $('.picpay-tds-modal .modal-inner-wrap'); + + iframe.css('height', data['heightChallenge']); + modal.css('height', 'fit-content'); + + iframe.css('width', data['widthChallenge']); + modal.css('width', 'fit-content'); + + let form = $('#picpay-tds-step-up-form'); + form.attr('action', data['stepUpUrl']); + + let input = $('#picpay-tds-access-code'); + input.val(data['accessToken']); + } + + getBrowserData() { + return { + httpBrowserJavaEnabled: navigator.javaEnabled(), + httpBrowserJavaScriptEnabled: true, + httpBrowserColorDepth: screen.colorDepth, + httpBrowserScreenHeight: screen.height, + httpBrowserScreenWidth: screen.width, + httpBrowserTimeDifference: new Date().getTimezoneOffset(), + httpBrowserLanguage: navigator.language, + userAgentBrowserValue: navigator.userAgent + } + } + + checkChallengeStatus(placeOrderCallback) { + let self = this; + let challengeInterval = setInterval(function() { + $.ajax({ + url: urlBuilder.build('picpay_checkout/tds/challenge'), + type: 'POST', + contentType: 'application/json', + data: JSON.stringify({}), + dataType: 'json', + async: true, + success: function (response) { + if (response.error) { + messageList.addSuccessMessage({'message': $t(response.error)}); + return; + } + + if (response.challenge_status == 'Approved') { + clearInterval(challengeInterval); + self.placeOrder(placeOrderCallback, true); + return; + } + + if (response.challenge_status == 'Rejected' && self.canPlaceNotAuthorizedOrder()) { + clearInterval(challengeInterval); + self.placeOrder(placeOrderCallback, false); + return; + } + + if (response.challenge_status == 'Rejected') { + $('#picpay-tds-modal').modal('closeModal'); + self.displayErrorMessage($t('We were unable to authenticate your transaction, please try again.')); + clearInterval(challengeInterval); + } + }, + error: function () { + clearInterval(challengeInterval); + $('#picpay-tds-modal').modal('closeModal'); + console.error('An error occurred while checking the order status.'); + } + }); + }, 1500); + } + + placeOrder(placeOrderCallback, withTds) { + if (typeof placeOrderCallback === 'function') { + placeOrderCallback(withTds); + } + $('#picpay-tds-modal').modal('closeModal'); + } + + canPlaceNotAuthorizedOrder() { + return window.checkoutConfig.payment['picpay_checkout_cc'].place_not_authenticated_order; + } + + displayErrorMessage(message) { + messageList.addErrorMessage({'message': $t(message)}); + customerData.set('messages', { + messages: [ + { text: message, type: 'error' } + ] + }); + } + } +}); diff --git a/view/frontend/web/js/view/payment/method-renderer/cc.js b/view/frontend/web/js/view/payment/method-renderer/cc.js index cc242b6..4f4a13a 100644 --- a/view/frontend/web/js/view/payment/method-renderer/cc.js +++ b/view/frontend/web/js/view/payment/method-renderer/cc.js @@ -27,6 +27,9 @@ define([ 'Magento_SalesRule/js/action/cancel-coupon', 'Magento_Customer/js/model/customer', 'Magento_Payment/js/view/payment/cc-form', + 'mage/url', + 'PicPay_Checkout/js/credit-card/tds', + 'Magento_Checkout/js/model/full-screen-loader', 'PicPay_Checkout/js/model/credit-card-validation/credit-card-number-validator', 'Magento_Payment/js/model/credit-card-validation/credit-card-data', 'picpay-cc-form', @@ -44,6 +47,9 @@ define([ cancelCouponCodeAction, customer, Component, + urlBuilder, + Tds, + FullScreenLoader, cardNumberValidator, creditCardData, creditCardForm @@ -61,9 +67,11 @@ define([ showCardData: ko.observable(true), installments: ko.observableArray([]), hasInstallments: ko.observable(false), + useTdsAuthorization: ko.observable(false), installmentsUrl: '', showInstallmentsWarning: ko.observable(true), - debounceTimer: null + debounceTimer: null, + tds: '' }, /** @inheritdoc */ @@ -129,6 +137,14 @@ define([ return this; }, + initialize: function () { + this._super(); + + this.tds = new Tds(); + + return this; + }, + loadCard: function () { let ccName = document.getElementById(this.getCode() + '_cc_owner'); let ccNumber = document.getElementById(this.getCode() + '_cc_number'); @@ -150,7 +166,6 @@ define([ * @returns {Object} */ getData: function () { - let ccExpMonth = ''; let ccExpYear = ''; let ccExpDate = this.creditCardExpDate(); @@ -170,7 +185,8 @@ define([ 'cc_exp_year': ccExpYear.length === 4 ? ccExpYear : '20' + ccExpYear, 'cc_number': this.picpayCreditCardNumber(), 'cc_owner': this.creditCardOwner(), - 'installments': this.creditCardInstallments() + 'installments': this.creditCardInstallments(), + 'use_tds_authorization': this.useTdsAuthorization() } }; }, @@ -253,6 +269,48 @@ define([ }); }, 500); } + }, + + isTdsActive: function () { + return window.checkoutConfig.payment[this.getCode()].use_tds; + }, + + canUseTds: function () { + let allowedCardTypes = ['VI', 'MC', 'ELO']; + return window.checkoutConfig.payment[this.getCode()].use_tds && + allowedCardTypes.includes(this.creditCardType()); + }, + + renderTdsModal: function () { + this.tds.initTdsModal(); + }, + + canPlaceNotAuthorizedOrder: function() { + return window.checkoutConfig.payment[this.getCode()].place_not_authenticated_order; + }, + + placeOrderContinue: function(data, event, _super) { + _super(data, event); + }, + + placeOrder: function (data, event) { + var _super = this._super.bind(this); + FullScreenLoader.startLoader(); + if (event) { + event.preventDefault(); + } + + if (this.canUseTds()) { + this.useTdsAuthorization(true); + this.tds.runTds(this.getData(), (placeOrderWithTds) => { + this.useTdsAuthorization(placeOrderWithTds); + this.placeOrderContinue(data, event, _super); + }); + return; + } + + this.useTdsAuthorization(false); + return this._super(data, event); } }); }); diff --git a/view/frontend/web/js/view/payment/method-renderer/pix.js b/view/frontend/web/js/view/payment/method-renderer/pix.js index 7218c4b..f6cacbb 100644 --- a/view/frontend/web/js/view/payment/method-renderer/pix.js +++ b/view/frontend/web/js/view/payment/method-renderer/pix.js @@ -19,27 +19,43 @@ */ define( [ + 'jquery', + 'ko', 'Magento_Checkout/js/view/payment/default', - 'mage/url' + 'Magento_Customer/js/model/customer' ], - function (Component, url) { + function ($, ko, Component, customer) { 'use strict'; return Component.extend({ defaults: { - template: 'PicPay_Checkout/payment/form/pix' + template: 'PicPay_Checkout/payment/form/pix', }, + taxvat: ko.observable(), + getCode: function() { return 'picpay_checkout_pix'; }, + validate: function () { + const $form = $('#' + 'form_' + this.getCode()); + return ($form.validation() && $form.validation('isValid')); + }, + getData: function() { return { - 'method': this.item.method + 'method': this.item.method, + 'additional_data': { + 'taxvat': this.taxvat() + } }; }, + isLoggedIn: function () { + return customer.isLoggedIn(); + }, + hasInstructions: function () { return (window.checkoutConfig.payment.picpay_checkout_pix.checkout_instructions.length > 0); }, diff --git a/view/frontend/web/js/view/payment/method-renderer/wallet.js b/view/frontend/web/js/view/payment/method-renderer/wallet.js index ac38813..4a4a152 100644 --- a/view/frontend/web/js/view/payment/method-renderer/wallet.js +++ b/view/frontend/web/js/view/payment/method-renderer/wallet.js @@ -19,10 +19,12 @@ */ define( [ + 'jquery', + 'ko', 'Magento_Checkout/js/view/payment/default', - 'mage/url' + 'Magento_Customer/js/model/customer' ], - function (Component, url) { + function ($, ko, Component, customer) { 'use strict'; return Component.extend({ @@ -30,16 +32,25 @@ define( template: 'PicPay_Checkout/payment/form/wallet' }, + taxvat: ko.observable(), + getCode: function() { return 'picpay_checkout_wallet'; }, getData: function() { return { - 'method': this.item.method + 'method': this.item.method, + 'additional_data': { + 'taxvat': this.taxvat() + } }; }, + isLoggedIn: function () { + return customer.isLoggedIn(); + }, + hasInstructions: function () { return (window.checkoutConfig.payment.picpay_checkout_wallet.checkout_instructions.length > 0); }, diff --git a/view/frontend/web/template/payment/form/cc.html b/view/frontend/web/template/payment/form/cc.html index 14cae2f..9ed1324 100644 --- a/view/frontend/web/template/payment/form/cc.html +++ b/view/frontend/web/template/payment/form/cc.html @@ -222,6 +222,15 @@ + + +
+ +
+ +
+
+ diff --git a/view/frontend/web/template/payment/form/pix.html b/view/frontend/web/template/payment/form/pix.html index 4ecda2a..6955187 100644 --- a/view/frontend/web/template/payment/form/pix.html +++ b/view/frontend/web/template/payment/form/pix.html @@ -32,31 +32,57 @@
-
-
-
- -
- -
-
+
+
+
+
+ +
+ +
+
- - - + +
+ +
+ +
+
+ -
- + -
-
- - - -
-
+
+ + + +
+ +
+ + + +
+
+
diff --git a/view/frontend/web/template/payment/form/wallet.html b/view/frontend/web/template/payment/form/wallet.html index 16d2205..217f6ef 100644 --- a/view/frontend/web/template/payment/form/wallet.html +++ b/view/frontend/web/template/payment/form/wallet.html @@ -41,6 +41,30 @@
+ +
+ +
+ +
+
+ +