diff --git a/Block/Adminhtml/Form/Field/RedirectUrl.php b/Block/Adminhtml/Form/Field/RedirectUrl.php new file mode 100755 index 0000000..945f441 --- /dev/null +++ b/Block/Adminhtml/Form/Field/RedirectUrl.php @@ -0,0 +1,51 @@ +_storeManager->getStores(); + $valueReturn = ''; + $urlArray = []; + + foreach ($stores as $store) { + $baseUrl = $store->getBaseUrl(UrlInterface::URL_TYPE_WEB, true); + if ($baseUrl) { + $value = $baseUrl . 'tendopay/standard/success/'; + $urlArray[] = "
" . $this->escapeHtml($value) . "
"; + } + } + + $urlArray = array_unique($urlArray); + foreach ($urlArray as $uniqueUrl) { + $valueReturn .= "
" . $uniqueUrl . "
"; + } + + return '' . $valueReturn . ''; + } + + /** + * Render element value + * + * @param AbstractElement $element + * @return string + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + protected function _renderInheritCheckbox(AbstractElement $element) + { + return ''; + } +} diff --git a/Block/Adminhtml/Payment/Info.php b/Block/Adminhtml/Payment/Info.php new file mode 100755 index 0000000..aa71c42 --- /dev/null +++ b/Block/Adminhtml/Payment/Info.php @@ -0,0 +1,103 @@ +tendopayHelper = $tendopayHelper; + } + + /** + * @param null $transport + * @return \Magento\Framework\DataObject|null + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function _prepareSpecificInformation($transport = null) + { + if (null !== $this->_paymentSpecificInformation) { + return $this->_paymentSpecificInformation; + } + + $transport = parent::_prepareSpecificInformation($transport); + + $helper = $this->tendopayHelper; + + if (!$this->getIsSecureMode()) { + $info = $this->getInfo(); + $order = $info->getOrder(); + $txnId = $info->getLastTransId(); + if (!$txnId) { // if order doesn't have transaction (for instance: Pending Payment orders) + $tendopayOrderId = $order->getData('tendopay_order_id'); + $tendopayToken = $order->getData('tendopay_token'); + $tendopayDisposition = $order->getData('tendopay_disposition'); + $tendopayVerificationToken = $order->getData('tendopay_verification_token'); + $tendopayFetchedAt = $order->getData('tendopay_fetched_at'); + $transport->addData( + ['Tendopay Order ID' => $tendopayOrderId ? $tendopayOrderId : __('(none)')] + ); + $transport->addData( + ['Tendopay Order Token' => $tendopayToken ? $tendopayToken : __('(none)')] + ); + $transport->addData( + ['Tendopay Order Status' => $tendopayDisposition ? $tendopayDisposition : __('(none)')] + ); + $transport->addData( + ['Tendopay Verification Token' => $tendopayVerificationToken ? $tendopayVerificationToken : __('(none)')] + ); + $transport->addData( + ['Tendopay Token Fetched At' => ($tendopayFetchedAt && $tendopayFetchedAt != '0000-00-00 00:00:00') ? + $helper->getFormateDate($tendopayFetchedAt, 'M d, Y') : + __('(none)')] + ); + } else { // if order already has transaction + $transport->addData(['Transaction ID' => $txnId]); + + $additionalInfo = $info->getAdditionalInformation(); + + if (is_array($additionalInfo)) { + if (isset($additionalInfo['tp_transaction_status'])) { + $transport->addData(['Transaction Status' => $additionalInfo['tp_transaction_status']]); + } + if (isset($additionalInfo['tp_created_at'])) { + $transport->addData(['Transaction Created At' => $additionalInfo['tp_created_at']]); + } + } + } + } + + return $transport; + } +} diff --git a/Client/Client.php b/Client/Client.php new file mode 100755 index 0000000..49b44b5 --- /dev/null +++ b/Client/Client.php @@ -0,0 +1,15 @@ +objectManager = $objectManager; + $this->config = $config; + $this->instanceName = $instanceName; + } + + /** + * @inheritDoc + */ + public function create() + { + $config = [ + Data::CLIENT_ID_KEY => $this->config->getValue('api_client_id'), + Data::CLIENT_SECRET_KEY => $this->config->getValue('api_client_secret'), + Data::TENDOPAY_SANDBOX_ENABLED => $this->config->getValue('api_mode') + ]; + return $this->objectManager->create($this->instanceName, ['config' => $config]); + } +} diff --git a/Client/ClientFactoryInterface.php b/Client/ClientFactoryInterface.php new file mode 100755 index 0000000..4d13633 --- /dev/null +++ b/Client/ClientFactoryInterface.php @@ -0,0 +1,16 @@ +_initService(); +// $this->_service->placeOrder(); +// +// $quoteId = $this->getQuote()->getId(); +// $this->getCheckoutSession()->setLastQuoteId($quoteId)->setLastSuccessQuoteId($quoteId); +// +// $order = $this->_service->getOrder(); +// +// if ($order) { +// $this->getCheckoutSession()->setLastOrderId($order->getId()) +// ->setLastRealOrderId($order->getIncrementId()); +// } +// +// $this->_redirect('checkout/onepage/success'); + } +} diff --git a/Controller/Standard/Redirect.php b/Controller/Standard/Redirect.php index d0856d6..866403c 100644 --- a/Controller/Standard/Redirect.php +++ b/Controller/Standard/Redirect.php @@ -12,172 +12,88 @@ namespace TendoPay\TendopayPayment\Controller\Standard; +use Magento\Framework\Exception\LocalizedException; +use Magento\Quote\Model\Quote; +use TendoPay\SDK\Exception\TendoPayConnectionException; +use TendoPay\TendopayPayment\Controller\TendopayAbstract; +use TendoPay\TendopayPayment\Helper\Data; + /** * Class Redirect * @package TendoPay\TendopayPayment\Controller\Standard */ -class Redirect extends \TendoPay\TendopayPayment\Controller\TendopayAbstract +class Redirect extends TendopayAbstract { - /** - * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\Result\Json|\Magento\Framework\Controller\ResultInterface - * @throws \GuzzleHttp\Exception\GuzzleException - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ public function execute() { - $this->checkoutHelper->addTendopayLog( - 'Initiate Redirect Method', - 'info' - ); - if (!$this->getRequest()->isAjax()) { - $this->_cancelPayment(); - $this->checkoutSession->restoreQuote(); - $this->getResponse()->setRedirect( - $this->getCheckoutHelper()->getUrl('checkout') - ); - } - - $quote = $this->getQuote(); - $email = $this->getRequest()->getParam('email'); - if ($this->getCustomerSession()->isLoggedIn()) { - $this->getCheckoutSession()->loadCustomerQuote(); - $quote->updateCustomerData($this->getQuote()->getCustomer()); - } else { - $quote->setCustomerEmail($email); - } - - if ($this->getCustomerSession()->isLoggedIn()) { - $quote->setCheckoutMethod(\Magento\Checkout\Model\Type\Onepage::METHOD_CUSTOMER); - } else { - $quote->setCheckoutMethod(\Magento\Checkout\Model\Type\Onepage::METHOD_GUEST); - } - - $quote->setCustomerEmail($email); - $quote->save(); - - $order = $this->getOrder(); - if (!$order->getIncrementId()) { - $message = 'Payment redirect request: Cannot get order from session, redirecting customer to shopping cart'; - $this->messageManager->addErrorMessage( - $message - ); - $this->checkoutHelper->addTendopayLog($message, 'error'); - return $this->_redirect('checkout/cart'); - } - $this->checkoutHelper->addTendopayLog( - 'Payment redirect request for order ' . $order->getIncrementId(), - 'info' - ); - $this->checkoutHelper->addTendopayLog( - 'Redirecting customer to TendoPay website... order=' . $order->getIncrementId(), - 'info' - ); try { - $authToken = $this->requestToken($order); - $this->setDescription($authToken, $order); - } catch (\Exception $e) { - throw new \Magento\Framework\Exception\LocalizedException(__("Could not communicate with TendoPay.")); + $url = $this->getRedirectUrl(); + if ($url) { + $this->getResponse()->setRedirect($url); + return; + } + } catch (LocalizedException $e) { + $this->messageManager->addWarningMessage($e->getMessage()); + } catch (TendoPayConnectionException $e) { + $this->messageManager->addWarningMessage($e->getMessage()); } - $session = $this->checkoutHelper->getCheckoutSession(); - $session->setTendopayStandardQuoteId($session->getQuoteId()); - $params = $this->getPaymentMethod()->buildCheckoutRequest($authToken); - - if (empty($params)) { - $this->checkoutHelper->addTendopayLog( - 'Exception on processing payment redirect request', - 'error' - ); - $this->cancelAction(); - } - return $this->resultJsonFactory->create()->setData($params); + $this->_redirect('checkout/cart'); } /** - * Call API to get Authorization Token + * Call API to get Authorization Url * - * @param $order + * @param Quote $quote * @return string - * @throws \GuzzleHttp\Exception\GuzzleException - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException + * @throws TendoPayConnectionException */ - public function requestToken($order) + public function requestUrl(Quote $quote) { - $data = [ - $this->checkoutHelper->getAmountParam() => (int)($order->getGrandTotal()), - $this->checkoutHelper->getTendopayCustomerReferenceOne() => (string)$order->getIncrementId(), - $this->checkoutHelper->getTendopayCustomerReferencetwo() => "magento2_order_" . $order->getIncrementId(), - ]; + $quote = $quote->collectTotals(); - $response = $this->checkoutHelper->doCall($this->checkoutHelper->getAuthorizationEndpointUri(), $data); - $isValidResponse = $response->getCode() === 200 && !empty($response->getBody()); - - if (!$isValidResponse) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Got return code != 200 or empty body while requesting authorization token from TP') + if ($quote->getGrandTotal() < 100) { + throw new LocalizedException( + __('The minimum purchase amount is ₱100. Please increase your cart total.') ); } - return trim((string)$response->getBody(), "\""); - } - - /** - * Call API to send order details to tendopay - * - * @param $authorizationToken - * @param $order - * @throws \GuzzleHttp\Exception\GuzzleException - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function setDescription($authorizationToken, $order) - { - $orderDetails = null; - try { - $orderDetails = $this->checkoutHelper->getApiAdapter()->buildOrderTokenRequest($order); - if (!is_array($orderDetails) && !is_object($orderDetails)) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Order details parameter must be either ARRAY or OBJECT') - ); - } - } catch (\Exception $e) { - $this->checkoutHelper->addTendopayLog($e->getMessage(), 'warning'); - $orderDetails = new \StdClass; + $quote->reserveOrderId(); + $this->quoteRepository->save($quote); + $client = $this->_clientFactory->create(); + + $payment = $this->_paymentFactory->create(); + $payment->setMerchantOrderId($quote->getReservedOrderId()) + ->setRequestAmount((int)$quote->getGrandTotal()) + ->setCurrency($quote->getQuoteCurrencyCode()) + ->setRedirectUrl($this->checkoutHelper->getRedirectUrl()); + /* Debug */ + if ($this->_config->getValue('debug')) { + $requestData = [ + Data::PAYMENT_REQUST_PARAM_AMOUNT => $payment->getRequestAmount(), + Data::PAYMENT_REQUST_PARAM_MECHANT_ORDER_ID => $payment->getMerchantOrderId(), + Data::PAYMENT_REQUST_PARAM_CURRENCY => $payment->getCurrency(), + Data::PAYMENT_REQUST_PARAM_REDIRECT => $payment->getRedirectUrl(), + Data::PAYMENT_REQUST_PARAM_DESCRIPTION => $payment->getDescription() + ]; + $this->logger->debug(json_encode($requestData)); } + /* END Debug */ - $response = $this->checkoutHelper->doCall( - $this->checkoutHelper->getDescriptionEndpointUri(), - [ - $this->checkoutHelper->getAuthTokenParam() => $authorizationToken, - $this->checkoutHelper->getTendopayCustomerReferenceOne() => (string)$order->getIncrementId(), - $this->checkoutHelper->getTendopayCustomerReferencetwo() => "magento2_order_" . - $order->getIncrementId(), - $this->checkoutHelper->getDescParam() => json_encode($orderDetails), - ] - ); - - if ($response->getCode() !== 204) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Got response code != 204 while sending products description') - ); - } + $client->setPayment($payment); + return $client->getAuthorizeLink(); } /** - * Cancel Action - * - * @throws \Exception + * @throws LocalizedException + * @throws TendoPayConnectionException */ - private function cancelAction() + public function getRedirectUrl() { - $this->checkoutSession->setQuoteId($this->checkoutSession->getTendopayStandardQuoteId(true)); - if ($this->checkoutSession->getLastRealOrderId()) { - $order = $this->orderFactory->create()->loadByIncrementId($this->checkoutSession->getLastRealOrderId()); - if ($order->getId()) { - $order->cancel()->save(); - } - $this->checkoutHelper->restoreQuote(); - } - $this->_redirect('checkout/cart'); + $this->initCheckout(); + $quote = $this->getQuote(); + + return $this->requestUrl($quote); } } diff --git a/Controller/Standard/Success.php b/Controller/Standard/Success.php index 13601ad..ea55152 100644 --- a/Controller/Standard/Success.php +++ b/Controller/Standard/Success.php @@ -12,202 +12,73 @@ namespace TendoPay\TendopayPayment\Controller\Standard; -use \TendoPay\TendopayPayment\Model\Standard; -use \TendoPay\TendopayPayment\Helper\Data as tendoPayHelper; +use Exception; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Exception\LocalizedException; +use TendoPay\SDK\Models\VerifyTransactionRequest; +use TendoPay\SDK\V2\ConstantsV2; +use TendoPay\SDK\V2\TendoPayClient; +use TendoPay\TendopayPayment\Controller\TendopayAbstract; /** * Class Success * @package TendoPay\TendopayPayment\Controller\Standard */ -class Success extends \TendoPay\TendopayPayment\Controller\TendopayAbstract +class Success extends TendopayAbstract { /** - * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void - * @throws \Exception + * @return ResponseInterface|ResultInterface + * @throws Exception */ public function execute() { $postedData = $this->getRequest()->getParams(); - if (isset($postedData['action'])) { - unset($postedData['action']); - } - - $getTendopayCustomerReferenceOne = $this->checkoutHelper->getTendopayCustomerReferenceOne(); - $order = $this->orderFactory->create()->loadByIncrementId($postedData[$getTendopayCustomerReferenceOne]); - if ($order->getIncrementId()) { - $orderKey = $postedData[$getTendopayCustomerReferenceOne]; - if ($order->getIncrementId() !== $orderKey) { - $this->checkoutHelper->addTendopayError("Wrong order key provided"); - } else { - switch ($order->getStatus()) { - case tendoPayHelper::RESPONSE_STATUS_APPROVED: - $this->preVerifyPaymentAction($order, $postedData); - break; - case tendoPayHelper::RESPONSE_STATUS_PROCESSING: - case tendoPayHelper::RESPONSE_STATUS_PENDING: - $this->preVerifyPaymentAction($order, $postedData); - break; - case tendoPayHelper::RESPONSE_STATUS_DECLINED: - $this->cancelAction(); - $this->paymentMethod->resetTransactionToken(); - $this->checkoutHelper->addTendopayError( - 'TendoPay payment has been declined. Please use other payment method.' - ); - break; - default: - $this->cancelAction(); - $this->paymentMethod->resetTransactionToken(); - $this->checkoutHelper->addTendopayError( - 'Cannot find TendoPay payment. Please contact administrator.' - ); - break; - } - $this->tendopaySuccessAction($order); - } - } - $this->_redirect('checkout/onepage/success', ['_secure' => true]); - } + $client = $this->_clientFactory->create(); - /** - * @throws \Exception - */ - public function tendopaySuccessAction($order) - { - $this->checkoutSession->clearHelperData(); - $quoteId = $this->checkoutSession->getQuote()->getId(); - $this->checkoutSession->setLastQuoteId($quoteId)->setLastSuccessQuoteId($quoteId); - if ($order) { - $this->checkoutSession->setLastOrderId($order->getId()) - ->setLastRealOrderId($order->getIncrementId()) - ->setLastOrderStatus($order->getStatus()); - } - $this->_redirect('checkout/onepage/success', ['_secure' => true]); - } - - /** - * @param $order - * @param $postedData - */ - public function preVerifyPaymentAction($order, $postedData) - { - $this->performVerification($order, $postedData); - } - - /** - * @param $order - * @param $postedData - * @return bool - * @throws \GuzzleHttp\Exception\GuzzleException - */ - public function performVerification($order, $postedData) - { - $tendoPayMerchantId = $postedData[$this->checkoutHelper->getVendorIdParam()]; - $localTendoPayMerchantId = $this->checkoutHelper->getConfigValues( - $this->checkoutHelper->getAPIMerchantIDConfigField() - ); + if (TendoPayClient::isCallbackRequest($postedData)) { + $transaction = $client->verifyTransaction(new VerifyTransactionRequest($postedData)); - if ($tendoPayMerchantId !== $localTendoPayMerchantId) { - $this->checkoutHelper->addTendopayError("Malformed payload"); - } - - try { - $transactionVerified = $this->verifyPayment($order, $postedData); - } catch (\Exception $exception) { - $this->checkoutHelper->addTendopayError("Could not communicate with TendoPay properly"); - } + if (!$transaction->isVerified()) { + throw new LocalizedException(__('Invalid signature for the verification')); + } - if ($transactionVerified) { - return true; - } else { - $order->sendNewOrderEmail()->addStatusHistoryComment( - 'Could not get with TendoPay transaction verification properly' - ) - ->setIsCustomerNotified(false) - ->save(); - $this->_forward('error'); - $this->_redirect('checkout/cart'); + if ($transaction->getStatus() == ConstantsV2::STATUS_SUCCESS) { + $this->_initService(); + $this->_service->prepareCallbackData($transaction); + $this->_success(); + $this->_redirect('checkout/onepage/success'); + } elseif ($transaction->getStatus() == ConstantsV2::STATUS_FAILURE) { + $this->messageManager->addWarningMessage(__($transaction->getMessage())); + + $resultRedirect = $this->resultRedirectFactory->create(); + return $resultRedirect->setPath('checkout/cart'); + } elseif ($transaction->getStatus() === ConstantsV2::STATUS_CANCELED) { + if ($this->_config->getValue('debug')) { + $this->logger->debug(json_encode($transaction->toArray())); + + $resultRedirect = $this->resultRedirectFactory->create(); + return $resultRedirect->setPath('checkout/cart'); + } + } } } - /** - * Call API to verify Payment - * - * @param $order - * @param array $data - * @return bool - * @throws \GuzzleHttp\Exception\GuzzleException - * @throws \Magento\Framework\Exception\LocalizedException - */ - public function verifyPayment($order, array $data) + protected function _success() { - ksort($data); - $hash = $data[$this->checkoutHelper->getHashParam()]; - if ($hash !== $this->checkoutHelper->calculate($data)) { - throw new \Magento\Framework\Exception\LocalizedException( - __("Hash doesn't match", "tendopay") - ); - } - $disposition = $data[$this->checkoutHelper->getDispositionParam()]; - $tendoPayTransactionNumber = $data[$this->checkoutHelper->getTransactionNoParam()]; - $verificationToken = $data[$this->checkoutHelper->getVerificationTokenParam()]; - $tendoPayUserId = $data[$this->checkoutHelper->getUserIDParam()]; + $this->_initService(); + $this->_service->placeOrder(); - $verificationData = [ - $this->checkoutHelper->getTendopayCustomerReferenceOne() => (string)$order->getIncrementId(), - $this->checkoutHelper->getTendopayCustomerReferencetwo() => "magento2_order_" . $order->getIncrementId(), - $this->checkoutHelper->getDispositionParam() => $disposition, - $this->checkoutHelper->getVendorIdParam() => (string)$this->checkoutHelper->getConfigValues( - $this->checkoutHelper->getAPIMerchantIDConfigField() - ), - $this->checkoutHelper->getTransactionNoParam() => (string)$tendoPayTransactionNumber, - $this->checkoutHelper->getVerificationTokenParam() => $verificationToken, - $this->checkoutHelper->getUserIDParam() => $tendoPayUserId, - ]; + $quoteId = $this->getQuote()->getId(); + $this->getCheckoutSession()->setLastQuoteId($quoteId)->setLastSuccessQuoteId($quoteId); - $response = $this->checkoutHelper->doCall( - $this->checkoutHelper->getVerificationEndpointUri(), - $verificationData - ); + $order = $this->_service->getOrder(); - if ($response->getCode() !== 200) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Received error: %1 while trying to verify the transaction', $response->getCode()) - ); + if ($order) { + $this->getCheckoutSession()->setLastOrderId($order->getId()) + ->setLastRealOrderId($order->getIncrementId()); } - - $json = json_decode($response->getBody()); - - $session = $this->checkoutHelper->getCheckoutSession(); - $quote = $session->getQuote(); - $quote->setData('tendopay_order_id', $order->getTendopayOrderId())->save(); - $quote->setData('tendopay_token', $order->getTendopayToken())->save(); - - $order->setData('tendopay_token', $json->tendopay_hash); - $order->setData('tendopay_order_id', $json->tendopay_transaction_number); - $order->setData('tendopay_disposition', $this->getRequest()->getParam('tendopay_disposition')); - $order->setData('tendopay_verification_token', $this->getRequest()->getParam('tendopay_verification_token')); - $order->setData('tendopay_fetched_at', $this->checkoutHelper->getGmtDate()); - $order->save(); - - return $json->{$this->checkoutHelper->getStatusIDParam()} === 'success'; } - /** - * Cancel Action - * - * @throws \Exception - */ - public function cancelAction() - { - $this->checkoutSession->setQuoteId($this->checkoutSession->getTendopayStandardQuoteId(true)); - if ($this->checkoutSession->getLastRealOrderId()) { - $order = $this->orderFactory->create()->loadByIncrementId($this->checkoutSession->getLastRealOrderId()); - if ($order->getId()) { - $order->cancel()->save(); - } - $this->checkoutHelper->restoreQuote(); - } - $this->_redirect('checkout/cart'); - } } diff --git a/Controller/TendopayAbstract.php b/Controller/TendopayAbstract.php index f3838f4..e5c88b7 100644 --- a/Controller/TendopayAbstract.php +++ b/Controller/TendopayAbstract.php @@ -13,8 +13,9 @@ namespace TendoPay\TendopayPayment\Controller; use Magento\Framework\App\Action\Context; +use TendoPay\TendopayPayment\Client\ClientFactoryInterface; -class TendopayAbstract extends \Magento\Framework\App\Action\Action +abstract class TendopayAbstract extends \Magento\Framework\App\Action\Action { /** * @var \Magento\Checkout\Model\Session @@ -46,11 +47,6 @@ class TendopayAbstract extends \Magento\Framework\App\Action\Action */ protected $quote; - /** - * @var \TendoPay\TendopayPayment\Model\Standard - */ - protected $paymentMethod; - /** * @var \TendoPay\TendopayPayment\Helper\Data */ @@ -65,6 +61,30 @@ class TendopayAbstract extends \Magento\Framework\App\Action\Action * @var \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory */ protected $resultJsonFactory; + /** + * @var \TendoPay\TendopayPayment\Model\Service\TendoFactory + */ + private $_tendoService; + /** + * @var \TendoPay\TendopayPayment\Model\Service\Tendo + */ + protected $_service; + /** + * @var Context + */ + private $context; + /** + * @var ClientFactoryInterface + */ + protected $_clientFactory; + /** + * @var \TendoPay\TendopayPayment\Client\Model\PaymentFactory + */ + protected $_paymentFactory; + /** + * @var \Magento\Payment\Gateway\ConfigInterface + */ + protected $_config; public function __construct( Context $context, @@ -73,21 +93,29 @@ public function __construct( \Magento\Quote\Api\CartRepositoryInterface $quoteRepository, \Magento\Sales\Model\OrderFactory $orderFactory, \Psr\Log\LoggerInterface $logger, - \TendoPay\TendopayPayment\Model\Standard $paymentMethod, \TendoPay\TendopayPayment\Helper\Data $checkoutHelper, \Magento\Quote\Api\CartManagementInterface $cartManagement, - \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory + \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory, + + \Magento\Payment\Gateway\ConfigInterface $config, + \TendoPay\TendopayPayment\Model\Service\TendoFactory $tendoService, + ClientFactoryInterface $clientFactory, + \TendoPay\TendopayPayment\Client\Model\PaymentFactory $paymentFactory ) { parent::__construct($context); $this->customerSession = $customerSession; $this->checkoutSession = $checkoutSession; $this->quoteRepository = $quoteRepository; $this->orderFactory = $orderFactory; - $this->paymentMethod = $paymentMethod; $this->checkoutHelper = $checkoutHelper; $this->cartManagement = $cartManagement; $this->resultJsonFactory = $resultJsonFactory; $this->logger = $logger; + $this->_tendoService = $tendoService; + $this->context = $context; + $this->_clientFactory = $clientFactory; + $this->_paymentFactory = $paymentFactory; + $this->_config = $config; } /** @@ -173,14 +201,6 @@ public function getCustomerSession() return $this->customerSession; } - /** - * @return \TendoPay\TendopayPayment\Model\Standard - */ - public function getPaymentMethod() - { - return $this->paymentMethod; - } - /** * @return \TendoPay\TendopayPayment\Helper\Data */ @@ -189,7 +209,18 @@ protected function getCheckoutHelper() return $this->checkoutHelper; } - public function execute() + protected function _initService() { + $quote = $this->getQuote(); + + if (!$this->_service) { + $parameters = [ + 'params' => [ + 'quote' => $quote, +// 'config' => $this->_config, + ], + ]; + $this->_service = $this->_tendoService->create($parameters); + } } } diff --git a/Gateway/Http/Client/AbstractClient.php b/Gateway/Http/Client/AbstractClient.php new file mode 100755 index 0000000..f3b5111 --- /dev/null +++ b/Gateway/Http/Client/AbstractClient.php @@ -0,0 +1,71 @@ +logger = $logger; + $this->_clientFactory = $clientFactory; + } + + /** + * @inheritdoc + */ + public function placeRequest(TransferInterface $transferObject) + { + + $data = $transferObject->getBody(); + + $log = [ + 'request' => $transferObject->getBody(), + 'client' => static::class + ]; + + $response = []; + + try { + $response = $this->process($data); + } catch (\Exception $e) { + $message = $e->getMessage() ? $e->getMessage() : "Something went wrong during Gateway request."; + $log['error'] = $message; + $this->logger->debug($log); + } + + return $response; + } + + /** + * Process http request + * + * @param array $data + */ + abstract protected function process(array $data); +} diff --git a/Gateway/Http/Client/CaptureClient.php b/Gateway/Http/Client/CaptureClient.php new file mode 100755 index 0000000..a9a0b9d --- /dev/null +++ b/Gateway/Http/Client/CaptureClient.php @@ -0,0 +1,25 @@ +_clientFactory->create(); + $response = $client->getTransactionDetail($data[Data::PAYMENT_INFO_TP_TRANSACTION_ID]); + $response = $response->toArray(); + return $response; + } +} diff --git a/Gateway/Http/TransferFactory.php b/Gateway/Http/TransferFactory.php new file mode 100755 index 0000000..73af408 --- /dev/null +++ b/Gateway/Http/TransferFactory.php @@ -0,0 +1,37 @@ +transferBuilder = $transferBuilder; + } + + /** + * Builds gateway transfer object + * + * @param array $request + * @return TransferInterface + */ + public function create(array $request) + { + return $this->transferBuilder + ->setBody($request) + ->build(); + } +} diff --git a/Gateway/Request/AuthorizationRequestBuilder.php b/Gateway/Request/AuthorizationRequestBuilder.php new file mode 100755 index 0000000..b90962a --- /dev/null +++ b/Gateway/Request/AuthorizationRequestBuilder.php @@ -0,0 +1,54 @@ +subjectReader = $subjectReader; + $this->config = $config; + } + + /** + * Builds ENV request + * + * @param array $buildSubject + * @return array + */ + public function build(array $buildSubject) + { + $data = []; + + $paymentDO = $this->subjectReader->readPayment($buildSubject); + $payment = $paymentDO->getPayment(); + + $data[Data::PAYMENT_INFO_TP_TRANSACTION_ID] = $payment->getAdditionalInformation(Data::PAYMENT_INFO_TP_TRANSACTION_ID); + + return $data; + } +} diff --git a/Gateway/Response/CompleteSaleHandler.php b/Gateway/Response/CompleteSaleHandler.php new file mode 100755 index 0000000..dde4579 --- /dev/null +++ b/Gateway/Response/CompleteSaleHandler.php @@ -0,0 +1,65 @@ +logger = $logger; + $this->subjectReader = $subjectReader; + } + + /** + * @param array $handlingSubject + * @param array $response + * @throws \Exception + */ + public function handle(array $handlingSubject, array $response) + { + /** + * $payment \Magento\Sales\Model\Order\Payment + */ + + $paymentDO = $this->subjectReader->readPayment($handlingSubject); + $payment = $paymentDO->getPayment(); + + if ($response[Data::TRANSACTION_INFO_STATUS]) { + $payment->setTransactionId($response[Data::TRANSACTION_INFO_ID]); + $payment->setAdditionalInformation(Data::TRANSACTION_INFO_STATUS, $response[Data::TRANSACTION_INFO_STATUS]); + $payment->setAdditionalInformation(Data::TRANSACTION_INFO_CREATED_AT, $response[Data::TRANSACTION_INFO_CREATED_AT]); + $payment->setIsTransactionClosed(true); + + if ($response[Data::TRANSACTION_INFO_STATUS] === Data::TRANSACTION_STATUS_PAID) { + $payment->setIsTransactionApproved(true); + } elseif ($response[Data::TRANSACTION_INFO_STATUS] === Data::TRANSACTION_STATUS_CANCELED) { + $payment->setIsTransactionDenied(true); + $order = $payment->getOrder(); + $order->setState($order::STATE_CANCELED)->setStatus($order::STATE_CANCELED); + } + } + } +} diff --git a/Gateway/Validator/CurrencyValidator.php b/Gateway/Validator/CurrencyValidator.php new file mode 100755 index 0000000..a869811 --- /dev/null +++ b/Gateway/Validator/CurrencyValidator.php @@ -0,0 +1,67 @@ +config = $config; + parent::__construct($resultFactory); + $this->_storeManager = $storeManager; + } + + /** + * @param array $validationSubject + * @return ResultInterface + * @throws NoSuchEntityException + */ + public function validate(array $validationSubject) + { + $allowedCurrency = $this->config->getValue('currency'); + $currentCurrency = $this->_storeManager->getStore()->getCurrentCurrency()->getCode(); + + if ($allowedCurrency == $currentCurrency) { + return $this->createResult( + true, + ['status' => 200] + ); + } + + return $this->createResult( + false, + [__('The currency selected is not supported by Tendo Pay.')] + ); + } +} diff --git a/Helper/Data.php b/Helper/Data.php index c71f6da..0ebb298 100644 --- a/Helper/Data.php +++ b/Helper/Data.php @@ -12,9 +12,9 @@ namespace TendoPay\TendopayPayment\Helper; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Helper\AbstractHelper; use Magento\Framework\App\Helper\Context; -use Magento\Framework\App\Config\ScopeConfigInterface; /** * Class Data @@ -22,6 +22,28 @@ */ class Data extends AbstractHelper { + const CLIENT_ID_KEY = 'CLIENT_ID'; + const CLIENT_SECRET_KEY = 'CLIENT_SECRET'; + const REDIRECT_URL_KEY = 'REDIRECT_URL'; + const TENDOPAY_SANDBOX_ENABLED = 'TENDOPAY_SANDBOX_ENABLED'; + + const PAYMENT_INFO_TP_TRANSACTION_ID = 'tp_transaction_id'; + + const TRANSACTION_INFO_STATUS = 'tp_transaction_status'; + const TRANSACTION_INFO_ID = 'tp_transaction_id'; + const TRANSACTION_INFO_CREATED_AT = 'tp_created_at'; + + const TRANSACTION_STATUS_PAID = 'PAID'; + const TRANSACTION_STATUS_CANCELED = 'CANCELED'; + + const PAYMENT_REQUST_PARAM_AMOUNT = 'tp_amount'; + const PAYMENT_REQUST_PARAM_CURRENCY = 'tp_currency'; + const PAYMENT_REQUST_PARAM_MECHANT_ORDER_ID = 'tp_merchant_order_id'; + const PAYMENT_REQUST_PARAM_DESCRIPTION = 'tp_description'; + const PAYMENT_REQUST_PARAM_REDIRECT = 'tp_redirect_url'; + + + const PAYMANET_FAILED_QUERY_PARAM = 'tendopay_payment_failed'; const METHOD_WPS = 'tendopay'; const REDIRECT_URL_PATTERN = '^tendopay-result/?'; @@ -34,8 +56,9 @@ class Data extends AbstractHelper const REDIRECT_URI = 'https://app.tendopay.ph/payments/authorise'; const VIEW_URI_PATTERN = 'https://app.tendopay.ph/view/transaction/%s'; const VERIFICATION_ENDPOINT_URI = 'payments/api/v1/verification'; - const AUTHORIZATION_ENDPOINT_URI = 'payments/api/v1/authTokenRequest'; const DESCRIPTION_ENDPOINT_URI = 'payments/api/v1/paymentDescription'; + + const AUTHORIZATION_ENDPOINT_URI = 'payments/api/v2/order'; const BEARER_TOKEN_ENDPOINT_URI = 'oauth/token'; /** @@ -52,14 +75,14 @@ class Data extends AbstractHelper /** * Below constant names are used as keys of data send to or received from TP API */ - const AMOUNT_PARAM = 'tendopay_amount'; + const AUTH_TOKEN_PARAM = 'tendopay_authorisation_token'; const TENDOPAY_CUSTOMER_REFERENCE_1 = 'tendopay_customer_reference_1'; const TENDOPAY_CUSTOMER_REFERENCE_2 = 'tendopay_customer_reference_2'; - const REDIRECT_URL_PARAM = 'tendopay_redirect_url'; const VENDOR_ID_PARAM = 'tendopay_tendo_pay_vendor_id'; const VENDOR_PARAM = 'tendopay_vendor'; - const HASH_PARAM = 'tendopay_hash'; +// const HASH_PARAM = 'tendopay_hash'; + const DISPOSITION_PARAM = 'tendopay_disposition'; const TRANSACTION_NO_PARAM = 'tendopay_transaction_number'; const VERIFICATION_TOKEN_PARAM = 'tendopay_verification_token'; @@ -67,6 +90,12 @@ class Data extends AbstractHelper const STATUS_PARAM = 'tendopay_status'; const USER_ID_PARAM = 'tendopay_user_id'; + const CURRENCY_PARAM = 'tp_currency'; + const MERCHANT_ORDER_ID_PARAM = 'tp_merchant_order_id'; + const REDIRECT_URL_PARAM = 'tp_redirect_url'; + const AMOUNT_PARAM = 'tp_amount'; + const HASH_PARAM = 'x_signature'; + const TENDO_PAY_FAQ_URL = 'https://tendopay.ph/page-faq.html'; /** @@ -138,7 +167,12 @@ class Data extends AbstractHelper * @var string $bearerToken the bearer token requested in previous API calls. If it's null, it will be taken from * wordpress options. If it was null or expired in the options, it will be then requested from the API. */ - private static $_bearerToken; + private $_bearerToken; + + /** + * @var int + */ + private $_bearerTokenExpirationTimestamp; /** * @var string @@ -313,7 +347,7 @@ public function getVerificationEndpointUri() */ public function getAuthorizationEndpointUri() { - return $this->isSandboxEnabled() ? self::SANDBOX_AUTHORIZATION_ENDPOINT_URI : self::AUTHORIZATION_ENDPOINT_URI; + return self::AUTHORIZATION_ENDPOINT_URI; } /** @@ -333,7 +367,7 @@ public function getDescriptionEndpointUri() */ public function getBearerTokenEndpointUri() { - return $this->isSandboxEnabled() ? self::SANDBOX_BEARER_TOKEN_ENDPOINT_URI : self::BEARER_TOKEN_ENDPOINT_URI; + return self::BEARER_TOKEN_ENDPOINT_URI; } /** @@ -346,6 +380,7 @@ public function getRepaymentCalculatorApiEndpointUrl() $baseUrl = $this->isSandboxEnabled() ? self::SANDBOX_BASE_API_URL : self::BASE_API_URL; return $baseUrl . "/" . self::REPAYMENT_SCHEDULE_API_ENDPOINT_URI; } + /** * * @return bool true if sandbox is enabled @@ -464,6 +499,19 @@ public function getAmountParam() return self::AMOUNT_PARAM; } + /** + * @return string + */ + public function getCurrencyParam() + { + return self::CURRENCY_PARAM; + } + + public function getMerchantOrderIdParam() + { + return self::MERCHANT_ORDER_ID_PARAM; + } + /** * @return string */ @@ -642,6 +690,7 @@ public static function getTendoExampleInstallmentsEnabled() { return self::OPTION_TENDOPAY_EXAMPLE_INSTALLMENTS_ENABLE; } + /** * @return string */ @@ -728,23 +777,30 @@ public function versionCompare($ver) * @param array $data * @return string */ - public function calculate(array $data) + public function calculate(array $payload) { - $secret = $this->getConfigValues(self::getAPIMerchantSecretConfigField()); - $data = array_map( - function ($value) { - return trim($value); - }, - $data - ); + $client_secret = $this->getConfigValues(self::getAPIMerchantSecretConfigField()); +// $data = array_map( +// function ($value) { +// return trim($value); +// }, +// $data +// ); + +// $hashKeysExclusionList = [$this->getHashParam()]; +// $exclusionList = $hashKeysExclusionList; +// $data = $this->arrayFilterKeys($data, $exclusionList); + + ksort($payload); - $hashKeysExclusionList = [$this->getHashParam()]; - $exclusionList = $hashKeysExclusionList; - $data = $this->arrayFilterKeys($data, $exclusionList); + $message = array_reduce(array_keys($payload), static function ($p, $k) use ($payload) { + return strpos($k, 'tp_') === 0 ? $p . $k . trim($payload[$k]) : $p; + }, ''); + $hash = hash_hmac('sha256', $message, $client_secret); - ksort($data); - $message = join("", $data); - return hash_hmac($this->getHashAlgorithm(), $message, $secret, false); + return $hash; +// $message = join("", $data); +// return hash_hmac($this->getHashAlgorithm(), $message, $secret, false); } /** @@ -756,10 +812,10 @@ function ($value) { */ public function arrayFilterKeys($array, $exclusionList) { - $newArray= []; + $newArray = []; foreach ($array as $key => $value) { if (!in_array($key, $exclusionList) && !empty($value)) { - $newArray[$key]=$value; + $newArray[$key] = $value; } } return $newArray; @@ -775,18 +831,20 @@ public function arrayFilterKeys($array, $exclusionList) */ public function doCall($url, array $data) { - $merchantId = $this->getConfigValues(self::getAPIMerchantIDConfigField()); - $data[$this->getVendorIdParam()] = $merchantId; - $data[$this->getHashParam()] = $this->calculate($data); + +// $merchantId = $this->getConfigValues(self::getAPIMerchantIDConfigField()); +// $data[$this->getVendorIdParam()] = $merchantId; +// $data[$this->getHashParam()] = $this->calculate($data); + $data['x_signature'] = $this->calculate($data); $headers = [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->getBearerToken(), 'Accept' => 'application/json', 'Content-Type' => 'application/json', - 'X-Using' => 'TendoPay Magento2 Extension', +// 'X-Using' => 'TendoPay Magento2 Extension', ], - 'json' => $data + 'body' => json_encode($data) ]; $this->client = new \GuzzleHttp\Client( [ @@ -805,19 +863,19 @@ public function doCall($url, array $data) */ private function getBearerToken() { - - if (self::$_bearerToken === null) { - $bearerTokenConfigField = $this->getConfigValues(self::getBearerTokenConfigField()); - self::$_bearerToken = $bearerTokenConfigField; + if ($this->_bearerToken === null) { + $bearerTokenConfigField = json_decode($this->getConfigValues(self::getBearerTokenConfigField()), true); + $this->_bearerToken = $bearerTokenConfigField['token']; + $this->_bearerTokenExpirationTimestamp = $bearerTokenConfigField['expiration_timestamp']; } $bearerExpirationTimestamp = -1; - if (self::$_bearerToken !== null && property_exists(self::$_bearerToken, 'expiration_timestamp')) { - $bearerExpirationTimestamp = self::$_bearerToken->expiration_timestamp; + if ($this->_bearerTokenExpirationTimestamp !== null) { + $bearerExpirationTimestamp = $this->_bearerTokenExpirationTimestamp; } $currentTimestamp = $this->dateTime->gmtTimestamp(); - if ($bearerExpirationTimestamp <= $currentTimestamp - 30) { + if ($bearerExpirationTimestamp <= $currentTimestamp - 60) { $headers = [ 'headers' => [ 'Accept' => 'application/json', @@ -840,28 +898,32 @@ private function getBearerToken() $responseBody = (string)$response->getBody(); $responseBody = json_decode($responseBody); - self::$_bearerToken = new \stdClass(); - self::$_bearerToken->expiration_timestamp = $responseBody->expires_in + $currentTimestamp; - self::$_bearerToken->token = $responseBody->access_token; + $bearerToken = [ + 'expiration_timestamp' => $responseBody->expires_in + $currentTimestamp, + 'token' => $responseBody->access_token + ]; + + $this->_bearerToken = $bearerToken['token']; + $this->_bearerTokenExpirationTimestamp = $bearerToken['expiration_timestamp']; $this->configWriter->save( 'payment/tendopay/bearer_token', - $this->serializer->serialize(self::$_bearerToken), + json_encode($bearerToken), $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0 ); } - return self::$_bearerToken->token; + return $this->_bearerToken; } public function getDefaultHeaders() { return [ 'Authorization' => 'Bearer ' . $this->getBearerToken(), - 'Accept' => 'application/json', - 'Content-Type' => 'application/json', - 'X-Using' => 'TendoPay Magento2 Plugin', + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'X-Using' => 'TendoPay Magento2 Plugin', ]; } } diff --git a/Model/Api/Adapters/Adapterv1.php b/Model/Api/Adapters/Adapterv1.php index 30491d0..72ba56f 100644 --- a/Model/Api/Adapters/Adapterv1.php +++ b/Model/Api/Adapters/Adapterv1.php @@ -97,8 +97,8 @@ public function buildOrderTokenRequest($object) 'subtotal' => $object->getSubtotal(), 'total' => round((float)$object->getGrandTotal(), $precision) ]; - if (!empty($object) && $object->getReservedOrderId()) { - $params['merchantReference'] = (string)$object->getReservedOrderId(); + if (!empty($object) && $object->getId()) { + $params['merchantReference'] = (string)$object->getId(); } return $params; diff --git a/Model/ConfigProvider.php b/Model/ConfigProvider.php index 6f87a8d..d40910f 100644 --- a/Model/ConfigProvider.php +++ b/Model/ConfigProvider.php @@ -12,25 +12,20 @@ namespace TendoPay\TendopayPayment\Model; -use \TendoPay\TendopayPayment\Helper\Data as TendoPayHelper; -use Magento\Framework\View\Asset\Repository as AssetRepository; +use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Framework\App\RequestInterface; +use Magento\Framework\UrlInterface; +use Magento\Framework\View\Asset\Repository as AssetRepository; +use Magento\Payment\Gateway\ConfigInterface; +use TendoPay\TendopayPayment\Helper\Data as TendoPayHelper; /** * Class ConfigProvider * @package TendoPay\TendopayPayment\Model */ -class ConfigProvider implements \Magento\Checkout\Model\ConfigProviderInterface +class ConfigProvider implements ConfigProviderInterface { - /** - * @var string - */ - protected $methodCode = TendoPayHelper::METHOD_WPS; - - /** - * @var \Magento\Payment\Model\MethodInterface - */ - protected $method; + const CODE = 'tendopay'; /** * @var AssetRepository @@ -43,31 +38,31 @@ class ConfigProvider implements \Magento\Checkout\Model\ConfigProviderInterface protected $request; /** - * @var Standard + * @var UrlInterface */ - protected $tendopay; - public $url; + /** + * @var ConfigInterface + */ + private $config; + /** * ConfigProvider constructor. - * @param \Magento\Payment\Helper\Data $paymenthelper * @param AssetRepository $assetRepository + * @param UrlInterface $url + * @param ConfigInterface $config * @param RequestInterface $request - * @param Standard $tendopay - * @throws \Magento\Framework\Exception\LocalizedException */ public function __construct( - \Magento\Payment\Helper\Data $paymenthelper, AssetRepository $assetRepository, - RequestInterface $request, - \TendoPay\TendopayPayment\Model\Standard $tendopay, - \Magento\Framework\UrlInterface $url + UrlInterface $url, + ConfigInterface $config, + RequestInterface $request ) { - $this->method = $paymenthelper->getMethodInstance($this->methodCode); $this->assetRepository = $assetRepository; - $this->request = $request; - $this->tendopay = $tendopay; $this->url = $url; + $this->config = $config; + $this->request = $request; } /** @@ -77,28 +72,27 @@ public function getConfig() { $params = ['_secure' => $this->request->isSecure()]; $visibility = false; - if (!empty($this->tendopay->getConfigData('api_merchant_id')) && - !empty($this->tendopay->getConfigData('api_merchant_secret')) && - !empty($this->tendopay->getConfigData('api_client_id')) && - !empty($this->tendopay->getConfigData('api_client_secret')) + if (!empty($this->config->getValue('api_client_id')) && + !empty($this->config->getValue('api_client_secret')) ) { $visibility = true; } - return $this->method->isAvailable() ? [ - 'payment'=>[ - 'tendopay'=> [ + return $this->config->getValue('active') ? [ + 'payment' => [ + 'tendopay' => [ 'visibility' => $visibility, - 'redirectUrl' => $this->tendopay->getRedirectUrl(), + 'redirectUrl' => $this->url->getUrl($this->config->getValue('redirect_url'), $params), 'paymentAcceptanceMarkSrc' => $this->assetRepository->getUrlWithParams( 'TendoPay_TendopayPayment::images/tendopay.png', $params ), 'paymentAcceptanceMarkHref' => TendoPayHelper::TENDO_PAY_FAQ_URL, - 'paymentAcceptanceMarkMessage' => $this->tendopay->getConfigData('message'), - 'tendopayMarketingPopup' => $this->url->getUrl('tendopay/standard/popupbox') + 'paymentAcceptanceMarkMessage' => $this->config->getValue('message'), + 'tendopayMarketingPopup' => $this->url->getUrl('tendopay/standard/popupbox'), + 'minTotalAmount' => $this->config->getValue('min_total_amount'), ] ] - ]:[]; + ] : []; } } diff --git a/Model/Service/Tendo.php b/Model/Service/Tendo.php new file mode 100755 index 0000000..f439650 --- /dev/null +++ b/Model/Service/Tendo.php @@ -0,0 +1,130 @@ +_quote = $params['quote']; + } else { + // phpcs:ignore Magento2.Exceptions.DirectThrow + throw new Exception('Quote instance is required.'); + } + $this->_customerSession = $customerSession; + $this->_checkoutData = $checkoutData; + $this->_quoteManagement = $quoteManagement; + $this->quoteRepository = $quoteRepository; + } + + public function placeOrder() + { + if ($this->getCheckoutMethod() == Onepage::METHOD_GUEST) { + $this->prepareGuestQuote(); + } + + $this->_quote->collectTotals(); + $order = $this->_quoteManagement->submit($this->_quote); + + if (!$order) { + return; + } + + $this->_order = $order; + } + + protected function getCheckoutMethod() + { + if ($this->getCustomerSession()->isLoggedIn()) { + return Onepage::METHOD_CUSTOMER; + } + if (!$this->_quote->getCheckoutMethod()) { + if ($this->_checkoutData->isAllowedGuestCheckout($this->_quote)) { + $this->_quote->setCheckoutMethod(Onepage::METHOD_GUEST); + } else { + $this->_quote->setCheckoutMethod(Onepage::METHOD_REGISTER); + } + } + return $this->_quote->getCheckoutMethod(); + } + + /** + * @return Session + */ + public function getCustomerSession() + { + return $this->_customerSession; + } + + /** + * Prepare quote for guest checkout order submit + * + * @return $this + */ + protected function prepareGuestQuote() + { + $quote = $this->_quote; + $quote->setCustomerId(null) + ->setCustomerEmail($quote->getBillingAddress()->getEmail()) + ->setCustomerIsGuest(true) + ->setCustomerGroupId(Group::NOT_LOGGED_IN_ID); + return $this; + } + + public function getOrder() + { + return $this->_order; + } + + /** + * @param VerifyTransactionResponse $data + * @throws LocalizedException + */ + public function prepareCallbackData(VerifyTransactionResponse $data) + { + $quote = $this->_quote; + $payment = $quote->getPayment(); + $payment->setAdditionalInformation(Data::PAYMENT_INFO_TP_TRANSACTION_ID, $data->getTransactionNumber()); + $quote->collectTotals(); + $this->quoteRepository->save($quote); + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..2e194a2 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +**Step 1: Add TendoPaySDK** + +Run the command in terminal on a project + +composer require tendopay/tendopay-sdk-php + +Copy files of the module into /app/code/ + +**Step 2: Enable magento 2 extension in terminal** + +php bin/magento setup:upgrade + +php bin/magento setup:di:compile + diff --git a/composer.json b/composer.json old mode 100644 new mode 100755 index 226c735..3d0e396 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "tendopay/tendopay-payment", "description": "TendoPay works as a payment method and allows users to spread their purchase across multiple instalments. User instalment payments are clearly visible with no hidden or additional fees. Application to TendoPay is quick, a user can get approved within 24 hours. Also users can manage their account from anywhere - desktop, mobile or tablet through the TendoPay admin panel. TendoPay was built to help provide users a more flexible payment method suited for timing that works for them", "type": "magento2-module", - "version": "1.0.2", + "version": "1.1.0", "license": [ "GNU" ], @@ -14,10 +14,11 @@ } ], "require": { - "php": "~5.6.5|7.*", + "php": "~7.1.3||~7.2||~7.3||~7.4", "magento/framework": ">=100", "magento/module-backend": ">=100.0.0", - "magento/module-store": ">=100.0.0" + "magento/module-store": ">=100.0.0", + "tendopay/tendopay-sdk-php": "~0.9.1" }, "keywords": [ "magento 2", diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 5ae8874..09f7818 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -16,45 +16,38 @@
- + Magento\Config\Model\Config\Source\Yesno - + 1 - - - - 1 - - - - - required-entry + + 1 + Magento\Sales\Model\Config\Source\Order\Status\NewStatus - - - required-entry + + 1 - Magento\Config\Model\Config\Backend\Encrypted - + required-entry 1 + Magento\Config\Model\Config\Backend\Encrypted - + required-entry @@ -62,7 +55,7 @@ Magento\Config\Model\Config\Backend\Encrypted - + Set Yes to Enable "As low as %s/installment" label Magento\Config\Model\Config\Source\Yesno @@ -70,21 +63,14 @@ 1 - - - - 1 - - Magento\Sales\Model\Config\Source\Order\Status\NewStatus - - + 1 TendoPay\TendopayPayment\Model\System\Config\Source\ApiMode - + Debug logs will be available in file {{base_dir}}/var/log/tendopay.log @@ -92,34 +78,47 @@ Magento\Config\Model\Config\Source\Yesno - + + + + 1 + + + 1 Magento\Payment\Model\Config\Source\Allspecificcountries - + Magento\Directory\Model\Config\Source\Country 1 - + 1 validate-number - + + + + TendoPay\TendopayPayment\Block\Adminhtml\Form\Field\RedirectUrl + + 1 + +
diff --git a/etc/config.xml b/etc/config.xml index bddebf8..8abcbca 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -15,19 +15,24 @@ - TendoPay\TendopayPayment\Model\Standard - 1 - Installments by TendoPay + TendoPaymentGatewayFacade + 0 + Pay with TendoPay Your personal data will be used to process your order, support your experience throughout this website, and for other purposes described in our privacy policy.

]]>
1 + 1 + authorize_capture + 1 + 1 + 100 + PHP pending sandbox - 1 + 0 0 tendopay/standard/redirect tendopay/standard/response - tendopay/standard/cancel - +
diff --git a/etc/di.xml b/etc/di.xml index e9ce28d..6c39746 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -11,9 +11,19 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html */ --> - + + + + + TendoPaymentGatewayConfig + + + + + TendoPaymentGatewayConfig + + @@ -29,4 +39,82 @@ ObjectManager/etc/config.xsd"> + + + + + TendoPay\TendopayPayment\Model\ConfigProvider::CODE + Magento\Payment\Block\Form + TendoPay\TendopayPayment\Block\Adminhtml\Payment\Info + TendoPaymentGatewayValueHandlerPool + TendoPaymentGatewayCommandPool + TendoPaymentValidatorPool + + + + + + + + TendoPaymentGatewayConfigValueHandler + + + + + + TendoPaymentGatewayConfig + + + + + + + TendoPay\TendopayPayment\Model\ConfigProvider::CODE + + + + + + + + TendoPaymentSaleCommand + + + + + + + + TendoPay\TendopayPayment\Gateway\Request\AuthorizationRequestBuilder + TendoPay\TendopayPayment\Gateway\Response\CompleteSaleHandler + TendoPay\TendopayPayment\Gateway\Http\TransferFactory + TendoPay\TendopayPayment\Gateway\Http\Client\CaptureClient + + + + + + + + TendoPaymentGatewayConfig + + + + + + + + TendoPaymentGatewayConfig + + + + + + + TendoPaymentCurrencyValidator + + + + + diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index 58ccdba..b5ec5d7 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -11,8 +11,7 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html */ --> - + @@ -20,4 +19,9 @@ ObjectManager/etc/config.xsd"> + + + TendoPaymentGatewayConfig + + diff --git a/etc/module.xml b/etc/module.xml index 67faff2..e844693 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -13,7 +13,7 @@ --> - + diff --git a/i18n/en_US.csv b/i18n/en_US.csv new file mode 100755 index 0000000..01dfd35 --- /dev/null +++ b/i18n/en_US.csv @@ -0,0 +1,3 @@ +"Continue with TendoPay","Continue with TendoPay" +"The minimum purchase amount is ₱100. Please increase your cart total.","The minimum purchase amount is ₱100. Please increase your cart total." +"Min Total Order Amount %1₱","Min Total Order Amount %1₱" diff --git a/registration.php b/registration.php old mode 100644 new mode 100755 diff --git a/view/frontend/web/images/tendopay.png b/view/frontend/web/images/tendopay.png index cb4ecae..ec071d9 100644 Binary files a/view/frontend/web/images/tendopay.png and b/view/frontend/web/images/tendopay.png differ diff --git a/view/frontend/web/js/view/payment/method-renderer/tendopay-method.js b/view/frontend/web/js/view/payment/method-renderer/tendopay-method.js index 6682b5a..846bf0f 100644 --- a/view/frontend/web/js/view/payment/method-renderer/tendopay-method.js +++ b/view/frontend/web/js/view/payment/method-renderer/tendopay-method.js @@ -10,14 +10,18 @@ */ define([ 'jquery', + 'mage/translate', 'Magento_Checkout/js/view/payment/default', - 'TendoPay_TendopayPayment/js/action/set-payment-method' -],function ($, Component, setPaymentMethod) { + 'Magento_Checkout/js/model/payment/additional-validators', + 'Magento_Paypal/js/action/set-payment-method', + 'Magento_Customer/js/customer-data', + 'Magento_Checkout/js/model/quote' +], function ($, $t, Component, additionalValidators, setPaymentMethodAction, customerData, quote) { 'use strict'; return Component.extend({ - defaults:{ - 'template':'TendoPay_TendopayPayment/payment/tendopay' + defaults: { + 'template': 'TendoPay_TendopayPayment/payment/tendopay' }, initialize: function () { @@ -65,9 +69,47 @@ define([ getPaymentMethodVisibility: function () { return window.checkoutConfig.payment.tendopay.visibility; }, + getMinTotalAmount: function () { + return window.checkoutConfig.payment.tendopay.minTotalAmount; + }, + + continueToTendoPay() { + if (additionalValidators.validate()) { + //update payment method information if additional data was changed + this.selectPaymentMethod(); + setPaymentMethodAction(this.messageContainer).done( + function () { + customerData.invalidate(['cart']); + $.mage.redirect( + window.checkoutConfig.payment.tendopay.redirectUrl + ); + } + ); + + return false; + } + }, + + isButtonEnabled() { + return this.getCode() === this.isChecked() && this.checkMinTotalAmount(); + }, + + getGrandTotal: function () { + let totals = quote.getTotals()(); + + if (totals) { + return totals['grand_total']; + } + + return quote['grand_total']; + }, + + checkMinTotalAmount() { + return this.getGrandTotal() > this.getMinTotalAmount(); + }, - afterPlaceOrder: function () { - setPaymentMethod(); + getMinTotalAmountMessage() { + return $t('Min Total Order Amount %1₱').replace('%1', this.getMinTotalAmount); } }); }); diff --git a/view/frontend/web/template/payment/tendopay.html b/view/frontend/web/template/payment/tendopay.html index 80ab181..a839bd5 100644 --- a/view/frontend/web/template/payment/tendopay.html +++ b/view/frontend/web/template/payment/tendopay.html @@ -47,16 +47,19 @@ + + +
diff --git a/view/frontend/web/tendopay/css/marketing/popup-box.css b/view/frontend/web/tendopay/css/marketing/popup-box.css index c2575f8..1615bfe 100644 --- a/view/frontend/web/tendopay/css/marketing/popup-box.css +++ b/view/frontend/web/tendopay/css/marketing/popup-box.css @@ -1,215 +1,215 @@ -body { - margin: 0; - position: relative; -} - -#tendopay-popup { - display: flex; - flex-direction: column; - position: absolute; - max-width: 100%; - width: 100vw; - height: 100vh; - top: 50%; - left: 50%; - /*-webkit-transform: translate(-50%, -50%); - -moz-transform: translate(-50%, -50%); - -ms-transform: translate(-50%, -50%); - -o-transform: translate(-50%, -50%); - transform: translate(-50%, -50%);*/ - -webkit-transform: translate(-50%, 0%); - -moz-transform: translate(-50%, 0%); - -ms-transform: translate(-50%, 0%); - -o-transform: translate(-50%, 0%); - transform: translate(-50%, 0%); -} - -.text-align-center { - text-align: center; -} - -.mt-3 { - margin-top: 3rem; -} - -.p-1 { - padding: 1rem; -} - -.p-2 { - padding: 2rem; -} - -.p-3 { - padding: 3rem; -} - -.px-2 { - padding-left: 2rem; - padding-right: 2rem; -} - -.px-3 { - padding-left: 3rem; - padding-right: 3rem; -} - -.px-4 { - padding-left: 4rem; - padding-right: 4rem; -} - -.pt-2 { - padding-top: 2rem; -} - -.img-logo { - max-width: 195px; - height: auto; -} - -.img-icon { - max-width: 140px; - height: auto; -} - -.btn { - border: none; - color: #000000; - padding: 20px 35px; - text-align: center; - text-decoration: none; - display: inline-block; - font-size: 18px; - border-radius: 5px; - background-color: #00FFC4; -} - -.font-blue-color { - color: #25A5F1; -} - -.font-grey-color { - color: #a0a5aa; -} - -.p-font-text { - font-size: 14px; -} - -.font-weight-500 { - font-weight: 500; -} - -.font-family-verdana { - font-family: verdana, sans-serif; -} - -.background-color-lightgrey { - background-color: rgba(185, 202, 218, 0.1); -} - -footer { - background-color: rgba(185, 202, 218, 0.4); - height: 100%; -} - -.heading-text { - font-size: 24px; - letter-spacing: -1px; - margin: 10px; -} - -.footer { - color: #000000; - text-decoration: none; - height: auto; - padding: 20px 6rem 20px 6rem; - letter-spacing: 0.50px; - font-size: 12px; - opacity: 0.5; -} - -.page-container { - position: relative; -} - -.page-container p { - margin-bottom: 0; -} - -.page-container a { - margin: 2.33rem auto; -} - -.position-absolute { - position: absolute; -} - -.w-100 { - width: 100%; -} - -.bottom-0 { - bottom: 0; -} - -.d-md-flex { - display: flex; -} - -.justify-content-center { - justify-content: center; -} - -.row { - flex-direction: row; -} - -/**** Mobile Device ****/ - -@media (max-width: 600px) { - - .pb-1 { - padding-bottom: 1rem; - } - - .d-block { - display: block; - } - - .img-logo { - width: 40vw; - } - - .heading-text { - font-size: 25px; - } - - .footer { - font-size: 10px; - padding: 20px 3rem 20px 3rem; - } - - .px-sm-0 { - padding-left: 0 !important; - padding-right: 0 !important; - } - - .btn { - font-size: 12px; - } - - .img-icon { - width: 90px; - } - - .h3-font-size { - font-size: 15px; - } - - .p-font-size { - font-size: 13px; - } - -} +body { + margin: 0; + position: relative; +} + +#tendopay-popup { + display: flex; + flex-direction: column; + position: absolute; + max-width: 100%; + width: 100vw; + height: 100vh; + top: 50%; + left: 50%; + /*-webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + -o-transform: translate(-50%, -50%); + transform: translate(-50%, -50%);*/ + -webkit-transform: translate(-50%, 0%); + -moz-transform: translate(-50%, 0%); + -ms-transform: translate(-50%, 0%); + -o-transform: translate(-50%, 0%); + transform: translate(-50%, 0%); +} + +.text-align-center { + text-align: center; +} + +.mt-3 { + margin-top: 3rem; +} + +.p-1 { + padding: 1rem; +} + +.p-2 { + padding: 2rem; +} + +.p-3 { + padding: 3rem; +} + +.px-2 { + padding-left: 2rem; + padding-right: 2rem; +} + +.px-3 { + padding-left: 3rem; + padding-right: 3rem; +} + +.px-4 { + padding-left: 4rem; + padding-right: 4rem; +} + +.pt-2 { + padding-top: 2rem; +} + +.img-logo { + max-width: 195px; + height: auto; +} + +.img-icon { + max-width: 140px; + height: auto; +} + +.btn { + border: none; + color: #000000; + padding: 20px 35px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 18px; + border-radius: 5px; + background-color: #00FFC4; +} + +.font-blue-color { + color: #25A5F1; +} + +.font-grey-color { + color: #a0a5aa; +} + +.p-font-text { + font-size: 14px; +} + +.font-weight-500 { + font-weight: 500; +} + +.font-family-verdana { + font-family: verdana, sans-serif; +} + +.background-color-lightgrey { + background-color: rgba(185, 202, 218, 0.1); +} + +footer { + background-color: rgba(185, 202, 218, 0.4); + height: 100%; +} + +.heading-text { + font-size: 24px; + letter-spacing: -1px; + margin: 10px; +} + +.footer { + color: #000000; + text-decoration: none; + height: auto; + padding: 20px 6rem 20px 6rem; + letter-spacing: 0.50px; + font-size: 12px; + opacity: 0.5; +} + +.page-container { + position: relative; +} + +.page-container p { + margin-bottom: 0; +} + +.page-container a { + margin: 2.33rem auto; +} + +.position-absolute { + position: absolute; +} + +.w-100 { + width: 100%; +} + +.bottom-0 { + bottom: 0; +} + +.d-md-flex { + display: flex; +} + +.justify-content-center { + justify-content: center; +} + +.row { + flex-direction: row; +} + +/**** Mobile Device ****/ + +@media (max-width: 600px) { + + .pb-1 { + padding-bottom: 1rem; + } + + .d-block { + display: block; + } + + .img-logo { + width: 40vw; + } + + .heading-text { + font-size: 25px; + } + + .footer { + font-size: 10px; + padding: 20px 3rem 20px 3rem; + } + + .px-sm-0 { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .btn { + font-size: 12px; + } + + .img-icon { + width: 90px; + } + + .h3-font-size { + font-size: 15px; + } + + .p-font-size { + font-size: 13px; + } + +}