From bbd0c3cb758fdb7d871aa172cce42d5cb1e65558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ariano=20=C3=82ngelo?= Date: Thu, 24 Mar 2022 15:33:09 +0000 Subject: [PATCH 1/4] v3.0 - New settings and color schemes to fit dark mode - New settings to add CryptAPI's services fees to the checkout - New settings to add blockchain fees to the checkout - Upgrade the settings - Added a history of transactions to the order payment page - Better handling of partial payments - Disable QR Code with value in certain currencies due to some wallets not supporting it - Minor fixes - UI Improvements --- Block/Adminhtml/Sales/Order/Fee.php | 71 +++ Block/Adminhtml/Sales/Order/View.php | 4 +- Block/{Success.php => Payment.php} | 61 ++- Controller/Index/Callback.php | 88 +++- Controller/Index/CartQuote.php | 40 ++ Controller/Index/Payment.php | 24 + Controller/Index/Status.php | 112 ++-- Cron/CryptapiCronjob.php | 121 +++++ Helper/Decimal.php | 53 ++ Model/{ => Config}/ConfigPlugin.php | 4 +- Model/Config/Source/CancellationList.php | 18 + Model/Config/Source/FeesList.php | 42 ++ Model/Config/Source/QrcodeOptions.php | 16 + Model/Config/Source/RefreshList.php | 19 + Model/Config/Source/SchemeList.php | 15 + Model/{Pay.php => Method/CryptapiPayment.php} | 51 +- Model/Total/Fee.php | 117 +++++ .../CryptapiConfigProvider.php} | 24 +- Observer/AfterSuccess.php | 45 ++ Observer/QuoteSubmitBefore.php | 41 ++ README.md | 34 +- Setup/InstallSchema.php | 36 -- composer.json | 2 +- etc/adminhtml/di.xml | 2 +- etc/adminhtml/system.xml | 80 ++- etc/config.xml | 9 +- etc/crontab.xml | 8 + etc/db_schema.xml | 14 + etc/events.xml | 6 + etc/extension_attributes.xml | 8 + etc/fieldset.xml | 11 + etc/frontend/di.xml | 2 +- etc/frontend/events.xml | 7 + etc/module.xml | 2 +- etc/sales.xml | 9 + lib/CryptAPIHelper.php | 61 ++- view/adminhtml/layout/sales_order_view.xml | 11 +- view/frontend/layout/checkout_cart_index.xml | 32 ++ view/frontend/layout/checkout_index_index.xml | 43 +- ...success.xml => cryptapi_index_payment.xml} | 8 +- view/frontend/layout/sales_order_view.xml | 15 + view/frontend/requirejs-config.js | 7 + view/frontend/templates/payment.phtml | 497 ++++++++++++++++++ view/frontend/templates/success.phtml | 230 -------- view/frontend/web/css/cryptapi.css | 309 ++++++++++- .../web/{images => files}/200_logo_ca.png | Bin .../web/{images => files}/ca_copy_icon.svg | 0 view/frontend/web/files/ca_copy_icon_dark.svg | 3 + .../web/{images => files}/ca_loader.svg | 0 .../web/{images => files}/logo_ca.png | Bin .../web/js/model/validate-cryptocurrency.js | 21 + .../web/js/view/checkout/cart/cart-script.js | 42 ++ .../web/js/view/checkout/cart/totals/fee.js | 22 + .../checkout/payment}/cryptapi-payments.js | 4 +- .../{ => view/checkout/payment}/cryptapi.js | 6 +- .../web/js/view/checkout/summary/fee.js | 37 ++ .../web/js/view/cryptapi-validation.js | 12 + .../template/checkout/cart/totals/fee.html | 15 + .../{ => checkout/payment}/cryptapi.html | 28 +- .../web/template/checkout/summary/fee.html | 20 + 60 files changed, 2140 insertions(+), 479 deletions(-) create mode 100644 Block/Adminhtml/Sales/Order/Fee.php rename Block/{Success.php => Payment.php} (57%) create mode 100644 Controller/Index/CartQuote.php create mode 100644 Controller/Index/Payment.php create mode 100644 Cron/CryptapiCronjob.php create mode 100644 Helper/Decimal.php rename Model/{ => Config}/ConfigPlugin.php (94%) create mode 100644 Model/Config/Source/CancellationList.php create mode 100644 Model/Config/Source/FeesList.php create mode 100644 Model/Config/Source/QrcodeOptions.php create mode 100644 Model/Config/Source/RefreshList.php create mode 100644 Model/Config/Source/SchemeList.php rename Model/{Pay.php => Method/CryptapiPayment.php} (77%) create mode 100644 Model/Total/Fee.php rename Model/{ConfigProvider.php => Ui/CryptapiConfigProvider.php} (80%) create mode 100644 Observer/AfterSuccess.php create mode 100644 Observer/QuoteSubmitBefore.php delete mode 100644 Setup/InstallSchema.php create mode 100644 etc/crontab.xml create mode 100644 etc/db_schema.xml create mode 100644 etc/events.xml create mode 100644 etc/extension_attributes.xml create mode 100644 etc/fieldset.xml create mode 100644 etc/frontend/events.xml create mode 100644 etc/sales.xml create mode 100644 view/frontend/layout/checkout_cart_index.xml rename view/frontend/layout/{checkout_onepage_success.xml => cryptapi_index_payment.xml} (54%) create mode 100644 view/frontend/layout/sales_order_view.xml create mode 100644 view/frontend/requirejs-config.js create mode 100644 view/frontend/templates/payment.phtml delete mode 100644 view/frontend/templates/success.phtml rename view/frontend/web/{images => files}/200_logo_ca.png (100%) rename view/frontend/web/{images => files}/ca_copy_icon.svg (100%) create mode 100644 view/frontend/web/files/ca_copy_icon_dark.svg rename view/frontend/web/{images => files}/ca_loader.svg (100%) rename view/frontend/web/{images => files}/logo_ca.png (100%) create mode 100644 view/frontend/web/js/model/validate-cryptocurrency.js create mode 100644 view/frontend/web/js/view/checkout/cart/cart-script.js create mode 100644 view/frontend/web/js/view/checkout/cart/totals/fee.js rename view/frontend/web/js/{ => view/checkout/payment}/cryptapi-payments.js (79%) rename view/frontend/web/js/{ => view/checkout/payment}/cryptapi.js (80%) create mode 100644 view/frontend/web/js/view/checkout/summary/fee.js create mode 100644 view/frontend/web/js/view/cryptapi-validation.js create mode 100644 view/frontend/web/template/checkout/cart/totals/fee.html rename view/frontend/web/template/{ => checkout/payment}/cryptapi.html (71%) create mode 100644 view/frontend/web/template/checkout/summary/fee.html diff --git a/Block/Adminhtml/Sales/Order/Fee.php b/Block/Adminhtml/Sales/Order/Fee.php new file mode 100644 index 0000000..85e415b --- /dev/null +++ b/Block/Adminhtml/Sales/Order/Fee.php @@ -0,0 +1,71 @@ +_config = $taxConfig; + parent::__construct($context, $data); + } + + public function displayFullSummary() + { + return true; + } + + public function getSource() + { + return $this->source; + } + + public function getStore() + { + return $this->order->getStore(); + } + + public function getOrder() + { + return $this->order; + } + + public function getLabelProperties() + { + return $this->getParentBlock()->getLabelProperties(); + } + + public function getValueProperties() + { + return $this->getParentBlock()->getValueProperties(); + } + + public function initTotals() + { + $parent = $this->getParentBlock(); + $this->order = $parent->getOrder(); + $this->source = $parent->getSource(); + $fee = new \Magento\Framework\DataObject( + [ + 'code' => 'cryptapi_fee', + 'strong' => false, + 'value' => $this->order->getData('cryptapi_fee'), + 'label' => __('Service Fee'), + ] + ); + $parent->addTotal($fee, 'cryptapi_fee'); + return $this; + } +} diff --git a/Block/Adminhtml/Sales/Order/View.php b/Block/Adminhtml/Sales/Order/View.php index ba3f3ef..9cd845e 100644 --- a/Block/Adminhtml/Sales/Order/View.php +++ b/Block/Adminhtml/Sales/Order/View.php @@ -1,7 +1,9 @@ helper = $helper; $this->payment = $payment; $this->scopeConfig = $scopeConfig; - $this->logger = $logger; + $this->request = $request; + $this->orderRepository = $orderRepository; } public function getTemplateValues() { - $order = $this->payment->getOrder(); + $order_id = (int)$this->request->getParam('order_id'); + $nonce = (string)$this->request->getParam('nonce'); + $order = $this->orderRepository->get($order_id); $total = $order->getGrandTotal(); $currencySymbol = $order->getOrderCurrencyCode(); - $metaData = $this->helper->getPaymentResponse($order->getQuoteId()); if (empty($metaData)) { return false; } + $qrCodeSize = $this->scopeConfig->getValue('payment/cryptapi/qrcode_size', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + + $branding = $this->scopeConfig->getValue('payment/cryptapi/show_branding', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + $metaData = json_decode($metaData, true); + if ($nonce != $metaData['cryptapi_nonce']) { + return false; + } + $cryptoValue = $metaData['cryptapi_total']; $cryptoCoin = $metaData['cryptapi_currency']; if (isset($metaData['cryptapi_address']) && !empty($metaData['cryptapi_address'])) { $addressIn = $metaData['cryptapi_address']; } else { - $selected = $cryptoCoin; $address = ''; @@ -69,37 +80,41 @@ public function getTemplateValues() $api = new CryptAPIHelper($selected, $address, $callbackUrl, $params, true); $addressIn = $api->get_address(); + $qrCode = $api->get_qrcode('', $qrCodeSize); + $qrCodeValue = $api->get_qrcode($cryptoValue, $qrCodeSize); + $this->helper->updatePaymentData($order->getQuoteId(), 'cryptapi_address', $addressIn); + $this->helper->updatePaymentData($order->getQuoteId(), 'cryptapi_qr_code_value', $qrCodeValue['qr_code']); + $this->helper->updatePaymentData($order->getQuoteId(), 'cryptapi_qr_code', $qrCode['qr_code']); } $ajaxParams = [ - 'order_id' => $order->getId() + 'order_id' => $order_id, ]; $ajaxUrl = $this->payment->getAjaxStatusUrl($ajaxParams); - $qrCodeSize = $this->scopeConfig->getValue('payment/cryptapi/qrcode_size', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - - $branding = $this->scopeConfig->getValue('payment/cryptapi/show_branding', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); - - $qrCode = $api->get_qrcode('', $qrCodeSize); - $qrCodeValue = $api->get_qrcode($cryptoValue, $qrCodeSize); + $metaData = $this->helper->getPaymentResponse($order->getQuoteId()); + $metaData = json_decode($metaData, true); - $values = [ - 'crypto_value' => $cryptoValue, + return [ + 'crypto_value' => floatval($cryptoValue), 'currency_symbol' => $currencySymbol, 'total' => $total, 'address_in' => $addressIn, 'crypto_coin' => $cryptoCoin, 'ajax_url' => $ajaxUrl, 'qrcode_size' => $qrCodeSize, - 'qrcode' => $qrCode['qr_code'], - 'qrcode_value' => $qrCodeValue['qr_code'], + 'qrcode' => $metaData['cryptapi_qr_code'], + 'qrcode_value' => $metaData['cryptapi_qr_code_value'], 'qrcode_default' => $this->scopeConfig->getValue('payment/cryptapi/qrcode_default', \Magento\Store\Model\ScopeInterface::SCOPE_STORE), - 'payment_uri' => $qrCode['uri'], - 'show_branding' => $branding + 'show_branding' => $branding, + 'qr_code_setting' => $this->scopeConfig->getValue('payment/cryptapi/qrcode_setting', \Magento\Store\Model\ScopeInterface::SCOPE_STORE), + 'order_timestamp' => strtotime($order->getCreatedAt()), + 'order_cancelation_timeout' => $this->scopeConfig->getValue('payment/cryptapi/order_cancelation_timeout', \Magento\Store\Model\ScopeInterface::SCOPE_STORE), + 'refresh_value_interval' => $this->scopeConfig->getValue('payment/cryptapi/refresh_value_interval', \Magento\Store\Model\ScopeInterface::SCOPE_STORE), + 'last_price_update' => $metaData['cryptapi_last_price_update'], + 'min_tx' => $metaData['cryptapi_min'], ]; - - return $values; } } diff --git a/Controller/Index/Callback.php b/Controller/Index/Callback.php index 16d727d..b2262bc 100644 --- a/Controller/Index/Callback.php +++ b/Controller/Index/Callback.php @@ -3,9 +3,9 @@ namespace Cryptapi\Cryptapi\Controller\Index; use Cryptapi\Cryptapi\lib\CryptAPIHelper; -use Magento\Framework\Controller\ResultFactory; +use Magento\Framework\App\Action\HttpGetActionInterface; -class Callback extends \Magento\Framework\App\Action\Action +class Callback implements HttpGetActionInterface { protected $helper; protected $payment; @@ -13,65 +13,95 @@ class Callback extends \Magento\Framework\App\Action\Action public function __construct( \Cryptapi\Cryptapi\Helper\Data $helper, - \Cryptapi\Cryptapi\Model\Pay $payment, - \Magento\Sales\Model\OrderFactory $orderFactory, + \Cryptapi\Cryptapi\Model\Method\CryptapiPayment $payment, + \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Framework\App\Action\Context $context + \Magento\Framework\App\Request\Http $request, + \Magento\Framework\App\Response\Http\Interceptor $response ) { $this->helper = $helper; $this->payment = $payment; - $this->orderFactory = $orderFactory; + $this->orderRepository = $orderRepository; $this->scopeConfig = $scopeConfig; - parent::__construct($context); + $this->request = $request; + $this->response = $response; } public function execute() { - $params = $this->getRequest()->getParams(); + $params = $this->request->getParams(); $data = CryptAPIHelper::process_callback($params); - $order = $this->orderFactory->create()->load($data['order_id']); + $order = $this->orderRepository->get($data['order_id']); + $orderId = $order->getQuoteId(); - $metaData = $this->helper->getPaymentResponse($order->getQuoteId()); + $currencySymbol = $order->getOrderCurrencyCode(); - if (!empty($metaData)) { - $metaData = json_decode($metaData, true); - } + $metaData = json_decode($this->helper->getPaymentResponse($orderId), true); if ($this->payment->hasBeenPaid($order) || $data['nonce'] != $metaData['cryptapi_nonce']) { - return $this->getResponse()->setBody("*ok*"); + return $this->response->setBody("*ok*"); } - $alreadyPaid = 0; + $paid = floatval($data['value_coin']); - if (isset($metaData['cryptapi_paid'])) { - $alreadyPaid = $metaData['cryptapi_paid']; - } + $min_tx = floatval($metaData['cryptapi_min']); + + $history = json_decode($metaData['cryptapi_history'], true); + + $update_history = true; - $paid = floatval($alreadyPaid) + floatval($data['value_coin']); + foreach ($history as $uuid => $item) { + if ($uuid === $data['uuid']) { + $update_history = false; + } + } - if (!$data['pending']) { - $this->helper->updatePaymentData($order->getQuoteId(), 'cryptapi_paid', $paid); + if ($update_history) { + $fiat_conversion = CryptAPIHelper::get_conversion($metaData['cryptapi_currency'], $currencySymbol, $paid, $this->scopeConfig->getValue('payment/cryptapi/disable_conversion', \Magento\Store\Model\ScopeInterface::SCOPE_STORE)); + + $history[$data['uuid']] = [ + 'timestamp' => time(), + 'value_paid' => $paid, + 'value_paid_fiat' => $fiat_conversion, + 'pending' => $data['pending'] + ]; + } else { + $history[$data['uuid']]['pending'] = $data['pending']; } - if ($paid >= $metaData['cryptapi_total']) { - if ($data['pending']) { - $this->helper->updatePaymentData($order->getQuoteId(), 'cryptapi_pending', "1"); - } else { - $this->helper->deletePaymentData($order->getQuoteId(), 'cryptapi_pending'); + $this->helper->updatePaymentData($orderId, 'cryptapi_history', json_encode($history)); + + $metaData = json_decode($this->helper->getPaymentResponse($orderId), true); + + $history = json_decode($metaData['cryptapi_history'], true); + $calc = $this->payment::calcOrder($history, $metaData); + + $remaining = $calc['remaining']->result(); + $remaining_pending = $calc['remaining_pending']->result(); + + if ($remaining <= 0) { + if ($data['pending'] === '0') { $state = \Magento\Sales\Model\Order::STATE_PROCESSING; $status = \Magento\Sales\Model\Order::STATE_PROCESSING; $order->setState($state); $order->setStatus($status); $order->setTotalPaid($order->getGrandTotal()); $order->save(); - - $this->helper->updatePaymentData($order->getQuoteId(), 'cryptapi_txid', $data['txid_in']); } } - return $this->getResponse()->setBody("*ok*"); + + if ($remaining_pending <= $min_tx) { + $this->helper->updatePaymentData($orderId, 'cryptapi_qr_code_value', CryptAPIHelper::get_static_qrcode($metaData['cryptapi_address'], $metaData['cryptapi_currency'], $min_tx, $this->scopeConfig->getValue('payment/cryptapi/qrcode_size', \Magento\Store\Model\ScopeInterface::SCOPE_STORE))['qr_code']); + } else { + $this->helper->updatePaymentData($orderId, 'cryptapi_qr_code_value', CryptAPIHelper::get_static_qrcode($metaData['cryptapi_address'], $metaData['cryptapi_currency'], $remaining_pending, $this->scopeConfig->getValue('payment/cryptapi/qrcode_size', \Magento\Store\Model\ScopeInterface::SCOPE_STORE))['qr_code']); + } + + + + return $this->response->setBody("*ok*"); } } diff --git a/Controller/Index/CartQuote.php b/Controller/Index/CartQuote.php new file mode 100644 index 0000000..44dc761 --- /dev/null +++ b/Controller/Index/CartQuote.php @@ -0,0 +1,40 @@ +checkoutSession = $checkoutSession; + $this->request = $request; + $this->response = $response; + $this->logger = $logger; + } + + public function execute() + { + $selected = (string)$this->request->getParam('selected'); + + $this->checkoutSession->setCurrency($selected); + + $data = [ + 'status' => 'done' + ]; + + $response = json_encode($data); + return $this->response->setBody($response); + } +} diff --git a/Controller/Index/Payment.php b/Controller/Index/Payment.php new file mode 100644 index 0000000..8e5c910 --- /dev/null +++ b/Controller/Index/Payment.php @@ -0,0 +1,24 @@ +pageFactory = $pageFactory; + } + + public function execute() + { + return $this->pageFactory->create(); + } +} + diff --git a/Controller/Index/Status.php b/Controller/Index/Status.php index df7e074..8589652 100644 --- a/Controller/Index/Status.php +++ b/Controller/Index/Status.php @@ -1,52 +1,98 @@ helper = $helper; $this->payment = $payment; - $this->orderFactory = $orderFactory; - parent::__construct($context); + $this->orderRepository = $orderRepository; + $this->scopeConfig = $scopeConfig; + $this->cronjob = $cronjob; + $this->request = $request; + $this->response = $response; + $this->priceHelper = $priceHelper; + $this->logger = $logger; } public function execute() { - $order_id = (int)$this->getRequest()->getParam('order_id'); - - try { - $order = $this->orderFactory->create()->load($order_id); - $metaData = $this->helper->getPaymentResponse($order->getQuoteId()); - if (!empty($metaData)) { - $metaData = json_decode($metaData, true); - } - - $cryptapi_pending = 0; - if (isset($metaData['cryptapi_pending'])) { - $cryptapi_pending = $metaData['cryptapi_pending']; - } - - $data = [ - 'is_paid' => $this->payment->hasBeenPaid($order), - 'is_pending' => (int)($cryptapi_pending), - ]; - - $response = json_encode($data); - return $this->getResponse()->setBody($response); - } catch (\Exception $e) { - ; + $orderId = (int)$this->request->getParam('order_id'); + + $order = $this->orderRepository->get($orderId); + $metaData = $this->helper->getPaymentResponse($order->getQuoteId()); + + if (!empty($metaData)) { + $metaData = json_decode($metaData, true); } - $response = json_encode(['status' => 'error', 'error' => 'Not a valid order id']); + + $showMinFee = '0'; + + $history = json_decode($metaData['cryptapi_history'], true); + + $calc = $this->payment::calcOrder($history, $metaData); + + $already_paid = $calc['already_paid']->result(); + $already_paid_fiat = $calc['already_paid_fiat']->result() <= 0 ? 0 : $calc['already_paid_fiat']->result(); + + $min_tx = floatval($metaData['cryptapi_min']); + + // $remaining = $calc['remaining']; + $remaining_pending = $calc['remaining_pending']->result(); + $remaining_fiat = $calc['remaining_fiat']->result(); + + $cryptapi_pending = '0'; + if ($remaining_pending <= 0 && !$this->payment->hasBeenPaid($order)) { + $cryptapi_pending = '1'; + } + + $counter_calc = (int)$metaData['cryptapi_last_price_update'] + (int)$this->scopeConfig->getValue('payment/cryptapi/refresh_value_interval', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) - time(); + if (!$this->payment->hasBeenPaid($order) && $counter_calc <= 0) { + $this->cronjob->execute(); + } + + if ($remaining_pending <= $min_tx && $remaining_fiat > 0) { + $remaining_pending = $min_tx; + $showMinFee = '1'; + } + + $data = [ + 'is_paid' => $this->payment->hasBeenPaid($order), + 'is_pending' => (int)($cryptapi_pending), + 'crypto_total' => floatval($metaData['cryptapi_total']), + 'qr_code_value' => $metaData['cryptapi_qr_code_value'], + 'cancelled' => $metaData['cryptapi_cancelled'], + 'remaining' => $remaining_pending <= 0 ? 0 : $remaining_pending, + 'fiat_remaining' => $this->priceHelper->currency($remaining_fiat, true, false), + 'coin' => strtoupper($metaData['cryptapi_currency']), + 'show_min_fee' => $showMinFee, + 'order_history' => $history, + 'already_paid' => $already_paid, + 'already_paid_fiat' => $this->priceHelper->currency(floatval($already_paid_fiat) <= 0 ? 0 : floatval($already_paid_fiat), true, false), + 'counter' => (string)$counter_calc, + 'fiat_symbol' => $order->getOrderCurrencyCode() + ]; + $response = json_encode($data); - return $this->getResponse()->setBody($response); + return $this->response->setBody($response); } } diff --git a/Cron/CryptapiCronjob.php b/Cron/CryptapiCronjob.php new file mode 100644 index 0000000..978a14d --- /dev/null +++ b/Cron/CryptapiCronjob.php @@ -0,0 +1,121 @@ +scopeConfig = $scopeConfig; + $this->orderCollectionFactory = $orderCollectionFactory; + $this->helper = $helper; + $this->orderRepository = $orderRepository; + $this->payment = $payment; + $this->logger = $logger; + } + + public function execute() + { + $order_timeout = (int)$this->scopeConfig->getValue('payment/cryptapi/order_cancelation_timeout'); + $value_refresh = (int)$this->scopeConfig->getValue('payment/cryptapi/refresh_value_interval'); + + if ($order_timeout === 0 && $value_refresh === 0) { + return; + } + + $orders = $this->getOrderCollectionPaymentMethod(); + + if (empty($orders)) { + return; + } + + $disable_conversion = $this->scopeConfig->getValue('payment/cryptapi/disable_conversion'); + + foreach ($orders as $order) { + $orderQuoteId = $order->getQuoteId(); + + $metaData = json_decode($this->helper->getPaymentResponse($order->getQuoteId()), true); + + $history = json_decode($metaData['cryptapi_history'], true); + + $min_tx = floatval($metaData['cryptapi_min']); + + $calc = $this->payment::calcOrder($history, $metaData); + + $remaining = $calc['remaining']->result(); + $remaining_pending = $calc['remaining_pending']->result(); + $remaining_fiat = $calc['remaining_fiat']->result(); + + if (!empty($metaData['cryptapi_address']) && $value_refresh !== 0 && $metaData['cryptapi_cancelled'] !== '1' && (int)$metaData['cryptapi_last_price_update'] + $value_refresh <= time() && $remaining_pending > 0) { + + if ($remaining === $remaining_pending) { + $cryptapi_coin = $metaData['cryptapi_currency']; + + $crypto_total = CryptAPIHelper::get_conversion($order->getOrderCurrencyCode(), $cryptapi_coin, $metaData['cryptapi_total_fiat'], $disable_conversion); + $this->helper->updatePaymentData($orderQuoteId, 'cryptapi_total', $crypto_total); + + $calc_cron = $this->payment::calcOrder($history, $metaData); + $crypto_remaining_total = $calc_cron['remaining_pending']->result(); + + if ($remaining_pending <= $min_tx && !$remaining_pending <= 0) { + $qr_code_data_value = CryptAPIHelper::get_static_qrcode($metaData['cryptapi_address'], $cryptapi_coin, $min_tx, $this->scopeConfig->getValue('payment/cryptapi/qrcode_size')); + } else { + $qr_code_data_value = CryptAPIHelper::get_static_qrcode($metaData['cryptapi_address'], $cryptapi_coin, $crypto_remaining_total, $this->scopeConfig->getValue('payment/cryptapi/qrcode_size')); + } + + $this->helper->updatePaymentData($orderQuoteId, 'cryptapi_qr_code_value', $qr_code_data_value['qr_code']); + + } + + $this->helper->updatePaymentData($orderQuoteId, 'cryptapi_last_price_update', time()); + } + + if ($order_timeout !== 0 && ((int)strtotime($order->getCreatedAt()) + $order_timeout) <= time() && empty($metaData['cryptapi_pending']) && $remaining_fiat <= $order->getGrandTotal() && (string)$metaData['cryptapi_cancelled'] === '0') { + $state = \Magento\Sales\Model\Order::STATE_CANCELED; + $status = \Magento\Sales\Model\Order::STATE_CANCELED; + $order->setState($state); + $order->setStatus($status); + $this->helper->updatePaymentData($orderQuoteId, 'cryptapi_cancelled', '1'); + $order->save(); + } + } + } + + private function getOrderCollectionPaymentMethod() + { + $orders = $this->orderCollectionFactory->create() + ->addFieldToSelect('*') + ->addFieldToFilter('status', + ['in' => ['pending']] + ); + + $orders->getSelect() + ->join( + ["sop" => "sales_order_payment"], + 'main_table.entity_id = sop.parent_id', + array('method') + ) + ->where('sop.method = ?', 'cryptapi'); + + $orders->setOrder( + 'created_at', + 'desc' + ); + + return $orders; + } +} diff --git a/Helper/Decimal.php b/Helper/Decimal.php new file mode 100644 index 0000000..f54e113 --- /dev/null +++ b/Helper/Decimal.php @@ -0,0 +1,53 @@ +precision; + } + + private function maybe_reduce_precision($new_val) { + while ($new_val >= PHP_INT_MAX) { + $new_val = ($new_val / 10 ** $this->precision) * 10 ** ($this->precision - 1); + $this->precision -= 1; + } + + $this->val = intval($new_val); + } + + function __construct($float_val) { + $this->sum($float_val); + } + + function mult($float_val) { + $new_val = $this->val * $this->to_int($float_val); + $this->maybe_reduce_precision($new_val); + return $this; + } + + function div($float_val) { + $new_val = $this->val / $this->to_int($float_val); + $this->maybe_reduce_precision($new_val); + return $this; + } + + function sum($float_val) { + $new_val = $this->val + $this->to_int($float_val); + $this->maybe_reduce_precision($new_val); + return $this; + } + + function sub($float_val) { + $new_val = $this->val - $this->to_int($float_val); + $this->maybe_reduce_precision($new_val); + return $this; + } + + function result() { + return $this->val / 10 ** $this->precision; + } +} diff --git a/Model/ConfigPlugin.php b/Model/Config/ConfigPlugin.php similarity index 94% rename from Model/ConfigPlugin.php rename to Model/Config/ConfigPlugin.php index ca549bc..f14f3ee 100644 --- a/Model/ConfigPlugin.php +++ b/Model/Config/ConfigPlugin.php @@ -1,8 +1,6 @@ 'Never', + '3600' => '1 Hour', + '21600' => '6 Hours', + '43200' => '12 Hours', + '64800' => '18 Hours', + '86400' => '24 Hours', + ]; + } +} diff --git a/Model/Config/Source/FeesList.php b/Model/Config/Source/FeesList.php new file mode 100644 index 0000000..9edc0a6 --- /dev/null +++ b/Model/Config/Source/FeesList.php @@ -0,0 +1,42 @@ + '5%', + '0.048' => '4.8%', + '0.045' => '4.5%', + '0.042' => '4.2%', + '0.04' => '4%', + '0.038' => '3.8%', + '0.035' => '3.5%', + '0.032' => '3.2%', + '0.03' => '3%', + '0.028' => '2.8%', + '0.025' => '2.5%', + '0.022' => '2.2%', + '0.02' => '2%', + '0.018' => '1.8%', + '0.015' => '1.5%', + '0.012' => '1.2%', + '0.01' => '1%', + '0.0090' => '0.90%', + '0.0085' => '0.85%', + '0.0080' => '0.80%', + '0.0075' => '0.75%', + '0.0070' => '0.70%', + '0.0065' => '0.65%', + '0.0060' => '0.60%', + '0.0055' => '0.55%', + '0.0050' => '0.50%', + '0.0040' => '0.40%', + '0.0030' => '0.30%', + '0.0025' => '0.25%', + 'none' => '0%', + ]; + } +} diff --git a/Model/Config/Source/QrcodeOptions.php b/Model/Config/Source/QrcodeOptions.php new file mode 100644 index 0000000..487a6a4 --- /dev/null +++ b/Model/Config/Source/QrcodeOptions.php @@ -0,0 +1,16 @@ + 'Default Without Ammount', + 'ammount' => 'Default Ammount', + 'hide_ammount' => 'Hide Ammount', + 'hide_without_ammount' => 'Hide Without Ammount', + ]; + } +} diff --git a/Model/Config/Source/RefreshList.php b/Model/Config/Source/RefreshList.php new file mode 100644 index 0000000..fb86c0d --- /dev/null +++ b/Model/Config/Source/RefreshList.php @@ -0,0 +1,19 @@ + 'Never', + '300' => 'Every 5 Minutes', + '600' => 'Every 10 Minutes', + '900' => 'Every 15 Minutes', + '1800' => 'Every 30 Minutes', + '2700' => 'Every 45 Minutes', + '3600' => 'Every 60 Minutes', + ]; + } +} diff --git a/Model/Config/Source/SchemeList.php b/Model/Config/Source/SchemeList.php new file mode 100644 index 0000000..6e991ac --- /dev/null +++ b/Model/Config/Source/SchemeList.php @@ -0,0 +1,15 @@ + 'Light', + 'dark' => 'Dark', + 'auto' => 'Auto', + ]; + } +} diff --git a/Model/Pay.php b/Model/Method/CryptapiPayment.php similarity index 77% rename from Model/Pay.php rename to Model/Method/CryptapiPayment.php index d86bfdb..fd6ffe3 100644 --- a/Model/Pay.php +++ b/Model/Method/CryptapiPayment.php @@ -1,12 +1,15 @@ getAdditionalInformation('cryptapi_coin'); if (empty($selected)) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Please select a cryptocurrency.') - ); + return $this; } $nonce = $this->generateNonce(); @@ -135,9 +136,9 @@ public function validate() $total = $quote->getGrandTotal(); $cryptoTotal = CryptAPIHelper::get_conversion( + $currencyCode, $selected, $total, - $currencyCode, $this->scopeConfig->getValue('payment/cryptapi/disable_conversion', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) ); @@ -153,7 +154,14 @@ public function validate() 'cryptapi_nonce' => $nonce, 'cryptapi_address' => '', 'cryptapi_total' => $cryptoTotal, + 'cryptapi_total_fiat' => $total, 'cryptapi_currency' => $selected, + 'cryptapi_history' => json_encode([]), + 'cryptapi_cancelled' => '0', + 'cryptapi_last_price_update' => time(), + 'cryptapi_min' => $minTx, + 'cryptapi_qr_code_value' => '', + 'cryptapi_qr_code' => '', ]; $paymentData = json_encode($paymentData); @@ -197,6 +205,37 @@ public function hasBeenPaid($order) } } + public static function calcOrder($history, $meta) + { + $already_paid = new Decimal(0); + $already_paid_fiat = new Decimal(0); + $remaining = new Decimal($meta['cryptapi_total']); + $remaining_pending = new Decimal($meta['cryptapi_total']); + $remaining_fiat = new Decimal($meta['cryptapi_total_fiat']); + + if (count($history) > 0) { + foreach ($history as $uuid => $item) { + if ((int)$item['pending'] === 0) { + $remaining = $remaining->sub($item['value_paid']); + } + + $remaining_pending = $remaining_pending->sub($item['value_paid']); + $remaining_fiat = $remaining_fiat->sub($item['value_paid_fiat']); + + $already_paid = $already_paid->sum($item['value_paid']); + $already_paid_fiat = $already_paid_fiat->sum($item['value_paid_fiat']); + } + } + + return [ + 'already_paid' => $already_paid, + 'already_paid_fiat' => $already_paid_fiat, + 'remaining' => $remaining, + 'remaining_pending' => $remaining_pending, + 'remaining_fiat' => $remaining_fiat + ]; + } + public function generateNonce($len = 32) { $data = str_split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'); diff --git a/Model/Total/Fee.php b/Model/Total/Fee.php new file mode 100644 index 0000000..ce5c787 --- /dev/null +++ b/Model/Total/Fee.php @@ -0,0 +1,117 @@ +checkoutSession = $checkoutSession; + $this->scopeConfig = $scopeConfig; + $this->totals = $orderTotals; + $this->logger = $logger; + } + + public function collect( + Quote $quote, + ShippingAssignmentInterface $shippingAssignment, + Total $total + ) + { + parent::collect($quote, $shippingAssignment, $total); + + if (!count($shippingAssignment->getItems())) { + return $this; + } + + $fee = $this->calculateFee($quote); + + $total->setTotalAmount('fee', $fee); + $total->setBaseTotalAmount('fee', $fee); + $total->setFee($fee); + $total->setBaseFee($fee); + $total->setGrandTotal($total->getGrandTotal()); + $total->setBaseGrandTotal($total->getBaseGrandTotal()); + + + $quote->setFee($fee); + + return $this; + } + + protected function clearValues(Total $total) + { + $total->setTotalAmount('subtotal', 0); + $total->setBaseTotalAmount('subtotal', 0); + $total->setTotalAmount('tax', 0); + $total->setBaseTotalAmount('tax', 0); + $total->setTotalAmount('discount_tax_compensation', 0); + $total->setBaseTotalAmount('discount_tax_compensation', 0); + $total->setTotalAmount('shipping_discount_tax_compensation', 0); + $total->setBaseTotalAmount('shipping_discount_tax_compensation', 0); + $total->setSubtotalInclTax(0); + $total->setBaseSubtotalInclTax(0); + } + + public function fetch(Quote $quote, Total $total) + { + return [ + 'code' => 'fee', + 'title' => __('Service Fee'), + 'value' => $this->calculateFee($quote), + ]; + } + + private function calculateFee(Quote $quote) + { + + try { + $paymentMethod = $quote->getPayment()->getMethodInstance()->getCode(); + + if ($paymentMethod === 'cryptapi') { + $conv = 0; + $totalPrice = 0; + + $feePercentage = $this->scopeConfig->getValue('payment/cryptapi/fee_order_percentage', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + $estimateBlockchain = $this->scopeConfig->getValue('payment/cryptapi/add_blockchain_fee', \Magento\Store\Model\ScopeInterface::SCOPE_STORE); + $coin = $this->checkoutSession->getCurrency(); + + if ($feePercentage !== 'none') { + $totalPrice = $quote->getGrandTotal() * $feePercentage; + } + + if (!empty($coin) && $estimateBlockchain) { + $conv = CryptAPIHelper::get_estimate($coin)->{$quote->getQuoteCurrencyCode()}; + } + + return $totalPrice + $conv; + } + } catch (\Exception $ex) { + return 0; + } + + return 0; + } +} diff --git a/Model/ConfigProvider.php b/Model/Ui/CryptapiConfigProvider.php similarity index 80% rename from Model/ConfigProvider.php rename to Model/Ui/CryptapiConfigProvider.php index 2cf8c4a..d801f9f 100644 --- a/Model/ConfigProvider.php +++ b/Model/Ui/CryptapiConfigProvider.php @@ -1,20 +1,16 @@ $coin) { - foreach ($selected as $uid => $data) { - if ($ticker == $data['cryptocurrency']) - $output[] = [ - 'value' => $data['cryptocurrency'], - 'type' => $coin, - ]; + if (!empty($selected)) { + foreach (json_decode($available_cryptos) as $ticker => $coin) { + foreach ($selected as $uuid => $data) { + if ($ticker == $data['cryptocurrency']) + $output[] = [ + 'value' => $data['cryptocurrency'], + 'type' => $coin, + ]; + } } } diff --git a/Observer/AfterSuccess.php b/Observer/AfterSuccess.php new file mode 100644 index 0000000..a6416cf --- /dev/null +++ b/Observer/AfterSuccess.php @@ -0,0 +1,45 @@ +helper = $helper; + $this->payment = $payment; + $this->url = $url; + $this->responseFactory = $responseFactory; + $this->logger = $logger; + } + + public function execute(Observer $observer) + { + $order = $this->payment->getOrder(); + $paymentMethod = $order->getPayment()->getMethodInstance()->getCode(); + + if ($paymentMethod === 'cryptapi') { + $metaData = json_decode($this->helper->getPaymentResponse($order->getQuoteId()), true); + + $params = [ + 'order_id' => $order->getId(), + 'nonce' => $metaData['cryptapi_nonce'] + ]; + + $redirectOrder = $this->url->getUrl('cryptapi/index/payment', $params); + $this->responseFactory->create()->setRedirect($redirectOrder)->sendResponse(); + die(); + } + } +} diff --git a/Observer/QuoteSubmitBefore.php b/Observer/QuoteSubmitBefore.php new file mode 100644 index 0000000..6df7214 --- /dev/null +++ b/Observer/QuoteSubmitBefore.php @@ -0,0 +1,41 @@ +orderResourceModel = $orderResourceModel; + $this->orderRepository = $orderRepository; + $this->payment = $payment; + $this->objectCopyService = $objectCopyService; + $this->logger = $logger; + } + + public function execute(Observer $observer) + { + + $quote = $observer->getQuote(); + $order = $observer->getOrder(); + $paymentMethod = $order->getPayment()->getMethodInstance()->getCode(); + + if ($paymentMethod === 'cryptapi') { + $order =$observer->getOrder(); + $order->setData('cryptapi_fee', (float)$quote->getData('fee')); + } + + } +} diff --git a/README.md b/README.md index 5fd3244..8dc78b8 100644 --- a/README.md +++ b/README.md @@ -18,19 +18,21 @@ All you need is to provide your crypto address. The CryptAPI extension enables your Magento store to get receive payments in cryptocurrency, with a simple setup and no sign-ups required. -Currently accepted cryptocurrencies are: +#### Accepted cryptocurrencies & tokens include: * (BTC) Bitcoin +* (ETH) Ethereum * (BCH) Bitcoin Cash * (LTC) Litecoin -* (ETH) Ethereum * (XMR) Monero -* (IOTA) IOTA +* (TRX) Tron +* (BNB) Binance Coin +* (USDT) USDT -CryptAPI will attempt to automatically convert the value you set on your store to the cryptocurrency your customer chose. -Exchange rates are fetched hourly from CoinMarketCap. +CryptAPI plugin will attempt to automatically convert the value you set on your store to the cryptocurrency your customer chose. +Exchange rates are fetched every 5 minutes. -Supported currencies for automatic exchange rates are: +### Supported currencies for automatic exchange rates are: * (USD) United States Dollar * (EUR) Euro @@ -58,12 +60,15 @@ For more info on our fees [click here](https://cryptapi.io/get_started/#fees) ### Installation -1. Upload code to folder app/code/Cryptapi/Cryptapi +1. Upload code to the folder app/code/Cryptapi/Cryptapi 2. Enter following commands to install module: ```bash -php bin/magento setup:upgrade -php bin/magento setup:static-content:deploy +php bin/magento module:enable Cryptapi_Cryptapi +php bin/magento setup:upgrade +php bin/magento setup:di:compile +php bin/magento setup:static-content:deploy -f +php bin/magento cache:flush php bin/magento cache:enable cryptapi_cryptocurrencies ``` @@ -118,5 +123,16 @@ The easiest and fastest way is via our live chat on our [website](https://crypta * UI Improvements * Minor Bug Fixes +#### 3.0 +* New settings and color schemes to fit dark mode +* New settings to add CryptAPI's services fees to the checkout +* New settings to add blockchain fees to the checkout +* Upgrade the settings +* Added a history of transactions to the order payment page +* Better handling of partial payments +* Disable QR Code with value in certain currencies due to some wallets not supporting it +* Minor fixes +* UI Improvements + ### Upgrade Notice * No breaking changes. diff --git a/Setup/InstallSchema.php b/Setup/InstallSchema.php deleted file mode 100644 index e317e8b..0000000 --- a/Setup/InstallSchema.php +++ /dev/null @@ -1,36 +0,0 @@ -startSetup(); - - $table = $installer->getConnection()->newTable($installer->getTable('cryptapi')) - ->addColumn( - 'order_id', - \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, - null, - ['unsigned' => true], - 'Order Id' - )->addColumn( - 'response', - \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, - null, - [], - 'Response' - )->setComment( - 'Cryptapi Table' - ); - - $installer->getConnection()->createTable($table); - - $installer->endSetup(); - } -} diff --git a/composer.json b/composer.json index 786a492..3c8d241 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "cryptapi/cryptapi", "description": "CryptAPI's Magento extension", "type": "magento2-module", - "version": "2.0.0", + "version": "3.0", "require": { "magento/module-payment": "100.1.*", "magento/module-checkout": "100.1.*", diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml index 0fe12a6..3ea2313 100644 --- a/etc/adminhtml/di.xml +++ b/etc/adminhtml/di.xml @@ -2,6 +2,6 @@ - + diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 0aaab7c..2fcedc4 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -5,57 +5,83 @@
- + Magento\Config\Model\Config\Source\Yesno - + - - - - - Cryptapi\Cryptapi\Block\Adminhtml\Cryptocurrencies - Magento\Config\Model\Config\Backend\Serialized\ArraySerialized - Important: Add only 1 address per cryptocurrency! - - - + Show CryptAPI Logo Magento\Config\Model\Config\Source\Yesno - - - - Attention: - This option will disable the price conversion for ALL cryptocurrencies! If you check this, pricing will not be converted from the currency of your - shop to the cryptocurrency selected by the user, and users will be requested to pay the same value as shown on your shop, regardless of the cryptocurrency selected - + + + This will add an estimation of the blockchain fee to the order value Magento\Config\Model\Config\Source\Yesno - - + + + Set the CryptAPI service fee you want to charge the costumer. Note: Fee you want to charge your costumers (to cover CryptAPI\'s fees fully or partially) + Cryptapi\Cryptapi\Model\Config\Source\FeesList - + Show the QR Code by default Magento\Config\Model\Config\Source\Yesno - + + + + + + Selects the color scheme of the plugin to match your website (Light, Dark and Auto to automatically detect it). + Cryptapi\Cryptapi\Model\Config\Source\QrcodeOptions + + + + Select how you want to show the QR Code to the user. Either select a default to show first, or hide one of them. + Cryptapi\Cryptapi\Model\Config\Source\SchemeList + + + + The system will automatically update the conversion value of the invoices (with real-time data), every X minutes. This feature is helpful whenever a customer takes long time to pay a generated invoice and the selected crypto a volatile coin/token (not stable coin). Warning: Setting this setting to none might create conversion issues, as we advise you to keep it at 5 minutes. + Cryptapi\Cryptapi\Model\Config\Source\RefreshList + + + + Selects the ammount of time the user has to pay for the order. When this time is over, order will be marked as 'Cancelled' and every paid value will be ignored. Notice: If the user still sends money to the generated address, value will still be redirected to you. Warning: We do not advice more than 1 Hour. + Cryptapi\Cryptapi\Model\Config\Source\CancellationList + + + + + + Cryptapi\Cryptapi\Block\Adminhtml\Cryptocurrencies + Magento\Config\Model\Config\Backend\Serialized\ArraySerialized + Important: Add only 1 address per cryptocurrency! + + + Magento\Payment\Model\Config\Source\Allspecificcountries - + Magento\Directory\Model\Config\Source\Country - + + + + Attention: This option will disable the price conversion for ALL cryptocurrencies! If you check this, pricing will not be converted from the currency of your shop to the cryptocurrency selected by the user, and users will be requested to pay the same value as shown on your shop, regardless of the cryptocurrency selected + Magento\Config\Model\Config\Source\Yesno +
diff --git a/etc/config.xml b/etc/config.xml index 461b7d0..efc60cf 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -4,7 +4,7 @@ - Cryptapi\Cryptapi\Model\Pay + Cryptapi\Cryptapi\Model\Method\CryptapiPayment Cryptocurrency 0 1 @@ -12,6 +12,13 @@ 300 1 0 + without_ammount + light + 300 + 3600 + none + 0 + 1 diff --git a/etc/crontab.xml b/etc/crontab.xml new file mode 100644 index 0000000..7c7881d --- /dev/null +++ b/etc/crontab.xml @@ -0,0 +1,8 @@ + + + + + * * * * * + + + diff --git a/etc/db_schema.xml b/etc/db_schema.xml new file mode 100644 index 0000000..bd83683 --- /dev/null +++ b/etc/db_schema.xml @@ -0,0 +1,14 @@ + + + + + +
+ + +
+ + +
+
diff --git a/etc/events.xml b/etc/events.xml new file mode 100644 index 0000000..c9c5fb7 --- /dev/null +++ b/etc/events.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/etc/extension_attributes.xml b/etc/extension_attributes.xml new file mode 100644 index 0000000..8016c2d --- /dev/null +++ b/etc/extension_attributes.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/etc/fieldset.xml b/etc/fieldset.xml new file mode 100644 index 0000000..067c62a --- /dev/null +++ b/etc/fieldset.xml @@ -0,0 +1,11 @@ + + + +
+ + + +
+
+
diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index 4159601..f570430 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -5,7 +5,7 @@ - Cryptapi\Cryptapi\Model\ConfigProvider + Cryptapi\Cryptapi\Model\Ui\CryptapiConfigProvider diff --git a/etc/frontend/events.xml b/etc/frontend/events.xml new file mode 100644 index 0000000..4b46364 --- /dev/null +++ b/etc/frontend/events.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/etc/module.xml b/etc/module.xml index b6b12b4..21c293a 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -5,7 +5,7 @@ */ --> - + diff --git a/etc/sales.xml b/etc/sales.xml new file mode 100644 index 0000000..2bb1f30 --- /dev/null +++ b/etc/sales.xml @@ -0,0 +1,9 @@ + + + +
+ + + +
+
diff --git a/lib/CryptAPIHelper.php b/lib/CryptAPIHelper.php index b4fda96..ab069f8 100644 --- a/lib/CryptAPIHelper.php +++ b/lib/CryptAPIHelper.php @@ -173,19 +173,48 @@ public static function process_callback($_get) return $params; } - public static function get_conversion($coin, $total, $currency, $disable_conversion) + public static function get_static_qrcode($address, $coin, $value, $size = 300) + { + if (empty($address)) { + return null; + } + + if (!empty($value)) { + $params = [ + 'address' => $address, + 'value' => $value, + 'size' => $size, + ]; + } else { + $params = [ + 'address' => $address, + 'size' => $size, + ]; + } + + $response = CryptAPIHelper::_request($coin, 'qrcode', $params); + + if ($response->status == 'success') { + return ['qr_code' => $response->qr_code, 'uri' => $response->payment_uri]; + } + + return null; + } + + public static function get_conversion($from, $to, $value, $disable_conversion) { if ($disable_conversion) { - return $total; + return $value; } $params = [ - 'value' => $total, - 'from' => $currency, + 'from' => $from, + 'to' => $to, + 'value' => $value, ]; - $response = CryptAPIHelper::_request($coin, 'convert', $params); + $response = CryptAPIHelper::_request('', 'convert', $params); if ($response->status == 'success') { return $response->value_coin; @@ -194,6 +223,28 @@ public static function get_conversion($coin, $total, $currency, $disable_convers return null; } + public static function get_estimate($coin) + { + + $params = [ + 'addresses' => 1, + 'priority' => 'default', + ]; + + $response = CryptAPIHelper::_request($coin, 'estimate', $params); + + if ($response->status == 'success') { + + return $response->estimated_cost_currency; + } + + return null; + } + + public static function my_decimal() { + + } + private static function _request($coin, $endpoint, $params = [], $assoc = false) { diff --git a/view/adminhtml/layout/sales_order_view.xml b/view/adminhtml/layout/sales_order_view.xml index bd8711e..b70a8be 100644 --- a/view/adminhtml/layout/sales_order_view.xml +++ b/view/adminhtml/layout/sales_order_view.xml @@ -2,11 +2,14 @@ - + + + - \ No newline at end of file + diff --git a/view/frontend/layout/checkout_cart_index.xml b/view/frontend/layout/checkout_cart_index.xml new file mode 100644 index 0000000..e760fba --- /dev/null +++ b/view/frontend/layout/checkout_cart_index.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + Cryptapi_Cryptapi/js/view/checkout/cart/totals/fee + 20 + + Cryptapi_Cryptapi/checkout/cart/totals/fee + Service Fee + + + + + + + + + + + diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml index cf2c7d2..9bc4866 100644 --- a/view/frontend/layout/checkout_index_index.xml +++ b/view/frontend/layout/checkout_index_index.xml @@ -1,5 +1,8 @@ + + + +
+
+
+
+ +
+
+ + src="data:image/png;base64," alt=""/> + + src="data:image/png;base64," + alt=""/> + +
+ +
+ + + + +
+ +
+ +
+
+ <?= __('QR Code without value') ?> +
+
+ +
+
+ +
+
+
+ + + (" . $values['total'] . ""; ?>) +
+ + + +
+ ' . date('i:s', $conversion_timer) . '' + ); + ?> +
+ +
+ + +
')">
+
+
+ + + ' . date('H:i', $cancel_timer) . '', + ); ?> + + + + + + +
+ + + + +
+
+ + + +

+
+
+ + + +

+
+
+ + + +

+
+
+
+
+ +

+ diff --git a/view/frontend/templates/success.phtml b/view/frontend/templates/success.phtml deleted file mode 100644 index ddb10e9..0000000 --- a/view/frontend/templates/success.phtml +++ /dev/null @@ -1,230 +0,0 @@ -getTemplateValues(); -if ($values) { - ?> - - -
-
-
-
-
-
- QR Code without value - -
-
- - -
-
-
-
-
- PLEASE SEND - - () -
-
- - -
')">
-
-
- - - - -
- -
-
- - - -

Waiting for payment

-
-
- - - -

Waiting for network confirmation

-
-
- - - -

Waiting for network confirmation

-
-
-
-
- diff --git a/view/frontend/web/css/cryptapi.css b/view/frontend/web/css/cryptapi.css index 4a94dd8..4bcb579 100644 --- a/view/frontend/web/css/cryptapi.css +++ b/view/frontend/web/css/cryptapi.css @@ -14,9 +14,10 @@ .ca_details_copy { padding: 7px 10px; margin: 0 5px; - background: #FAFAFA !important; + background: #e0e0e0 !important; font-weight: bold; color: #000 !important; + border: 0 !important; } .ca_details_box { @@ -57,20 +58,20 @@ .ca_qrcode_buttons button { display: inline-block; - width: 50%; + width: 100%; height: 42px; - font-size: 14px; + font-size: 12px; line-height: 1; letter-spacing: .045em; - background-color: #FAFAF9 !important; + background: #E0E0E0 !important; color: #000 !important; border: 0 !important; } .ca_qrcode_buttons button.active { - background: #E0E0E0 !important; + background-color: #FAFAF9 !important; color: #000; - box-shadow: 1px 0 4px rgba(0, 0, 0, 0.25) !important; + box-shadow: none !important; } .ca_details_input { @@ -83,19 +84,23 @@ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25); } +.ca_details_input .ca_copy .ca_tooltip { + left: 25%; +} + .ca_details_input span { - font-size: 22px; + font-size: 20px; display: flex; align-items: center; justify-content: center; font-weight: 600; height: 100%; - padding: 0 8% + padding: 0 8%; + line-height: 20px; } .ca_copy { position: relative; - border: 0 !important; } .ca_copy .ca_copy_icon_tooltip { @@ -138,9 +143,9 @@ width: 24px; height: 28px; right: 25px; - background-size: contain !important; - background-repeat: no-repeat !important; - background-color: transparent !important; + background-size: contain; + background: transparent url(../files/ca_copy_icon.svg) no-repeat !important; + border: 0 !important; } .ca_copy_icon_tooltip { @@ -155,20 +160,30 @@ left: 10px; width: 50px; height: 50px; - background-image: url(./files/ca_loader.svg); + background-image: url(../files/ca_loader.svg); background-size: contain; top: calc(50% - 25px); bottom: 0; background-repeat: no-repeat; } +.ca_loader_payment_processing { + background-image: url(../files/ca_loader.svg); + background-size: 150%; + background-repeat: no-repeat; + width: 120px; + height: 120px; + margin: 0 auto; + background-position: center; +} + .ca_buttons_container { display: flex; justify-content: center; padding: 13px 0; background-color: #FAFAFA; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25); - max-width: 250px; + max-width: 180px; width: 100%; margin: 0 auto; border-radius: 50px; @@ -178,16 +193,16 @@ display: flex; justify-content: center; align-items: center; - text-decoration: none; color: #2695D3; font-weight: 600; letter-spacing: .045em; - width: 100px; + width: 140px; height: 38px; border-radius: 50px; border: 3px solid #2695D3; font-size: 13px; margin: 0 6px; + text-decoration: none !important; } .ca_buttons_container a:hover { @@ -195,11 +210,34 @@ background-color: #2695D3; } +.ca_buttons_container a span { + display: none; + white-space: nowrap; +} + +.ca_buttons_container a span.active { + display: block; +} + .ca_branding { margin-top: 26px; text-align: center; } +.ca_branding a { + text-decoration: none; +} + +.ca_branding span { + display: block; + font-size: 10px; + font-weight: 500; + font-style: italic; + margin-bottom: 4px; + color: #757575; + letter-spacing: 1px; +} + .ca_branding img { display: inline-block !important; } @@ -207,14 +245,30 @@ .ca_progress { display: flex; margin-top: 52px; + flex-wrap: wrap; } .ca_progress .ca_progress_icon { position: relative; width: 33.33%; + flex: 0 0 33.33%; + text-align: center; +} + +.ca_notification_text { + display: block; + width: 100%; + flex: 0 0 100%; + margin-top: 35px; text-align: center; } +.ca_notification_text span { + display: block; + margin-bottom: 3px; + font-size: 14px; +} + .ca_progress .ca_progress_icon.waiting_network { position: relative; } @@ -254,25 +308,99 @@ width: 100%; } -.ca_payment_confirmed h2 { +.ca_payment_confirmed h2, +.ca_payment_processing h2, +.ca_payment_cancelled h2 { + display: block; text-align: center; font-size: 28px; text-transform: uppercase; font-weight: 700; margin-top: 26px; + margin-bottom: 15px; } -.ca_payment_confirmed .ca_payment_confirmed_icon { +.ca_payment_confirmed .ca_payment_confirmed_icon, +.ca_payment_processing .ca_payment_confirmed_icon, +.ca_payment_cancelled .ca_payment_cancelled_icon { text-align: center !important; + margin-bottom: 15px; } -.ca_payment_confirmed .ca_payment_confirmed_icon svg { +.ca_payment_confirmed .ca_payment_confirmed_icon svg, +.ca_payment_cancelled .ca_payment_cancelled_icon svg { max-width: 100% !important; width: 100px !important; margin: 0 auto !important; display: inline-block !important; } +.ca_payment_processing h5 { + text-align: center; + font-size: 13px; + text-transform: uppercase; + font-weight: 600; + margin-top: -10px; +} + +.ca_progress .ca_progress_icon.done { + padding: unset !important; + margin: unset !important; +} + +.ca_payment_notification { + text-align: center; + margin-top: 15px; + font-size: 14px; +} + +.ca_history .ca_history_fill { + border-radius: 15px !important; + font-size: 14px !important; + text-align: center !important; + width: 70% !important; + margin: 20px auto !important; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25) !important; + overflow: hidden !important; + border-collapse: collapse !important; +} + +.ca_history .ca_history_fill th { + border: 0 !important; + padding: 15px !important; + text-align: center !important; + border-bottom: 1px solid #ccc !important; + vertical-align: middle !important; +} + +.ca_history .ca_history_fill td { + border: 0 !important; + overflow: auto !important; + text-align: center !important; + padding: 15px !important; + vertical-align: middle !important; +} + +.ca_history_date { + font-size: 10px !important; + display: block !important; +} + +.ca_notification_cancel { + display: block; + text-align: center; + font-size: 14px; + margin-bottom: 25px; +} + +.ca_time_refresh { + display: block; + text-align: center; + margin: 10px 0 20px; + font-size: 14px; + font-weight: 500; +} + @media screen and (max-width: 768.98px) { .ca_details_input span { font-size: 16px; @@ -287,16 +415,16 @@ } .ca_progress .ca_progress_icon.waiting_network:before, - .ca_progress .ca_progress_icon.waiting_network:after { - width: 60px; + .ca_progress .ca_progress_icon.waiting_payment:before { + width: 60px !important; } .ca_progress .ca_progress_icon.waiting_network:before { - left: -22%; + right: -17%; } - .ca_progress .ca_progress_icon.waiting_network:after { - right: -22%; + .ca_progress .ca_progress_icon.waiting_payment:before { + right: -17%; } } @@ -307,7 +435,8 @@ } .ca_details_input span { - padding: 0 50px 0 8%; + padding: 0 60px 0 30px; + font-size: 14px; } .ca_progress { @@ -316,11 +445,12 @@ .ca_progress .ca_progress_icon { width: 100%; + flex: 0 0 100%; } .ca_progress .ca_progress_icon.waiting_payment:before, .ca_progress .ca_progress_icon.waiting_network:before { - width: 2px; + width: 2px !important; height: 40px; left: 0; right: 0; @@ -339,9 +469,134 @@ .ca_loader { top: unset; - bottom: -243px; + bottom: -56px; right: 0; left: 0; margin: 0 auto; } + + .ca_copy_icon { + right: 0; + } +} + +.ca_payment-panel.dark { + color: #fafafa; +} + +.ca_payment-panel.dark .ca_details_input { + background-color: #424242 !important; + color: #FAFAFA !important; +} + +.ca_payment-panel.dark .ca_loader { + filter: invert(1); +} + +.ca_payment-panel.dark .ca_details_copy { + background-color: #424242 !important; + color: #FAFAFA !important; +} + +.ca_payment-panel.dark .ca_copy .ca_copy_icon_tooltip { + background-color: rgba(245, 245, 245, 1) !important; + color: #121212 !important; +} + +.ca_payment-panel.dark .ca_copy .ca_copy_icon_tooltip::after { + border-color: rgba(245, 245, 245, 1) transparent transparent transparent !important; +} + +.ca_payment-panel.dark .ca_buttons_container { + background-color: #424242 !important; + color: #FAFAFA !important; +} + +.ca_payment-panel.dark .ca_buttons_container a { + color: #6ac5ff !important; + border: 3px solid #6ac5ff !important; +} + +.ca_payment-panel.dark .ca_buttons_container a:hover { + background-color: #6ac5ff !important; + color: #212121 !important; +} + +.ca_payment-panel.dark .ca_copy_icon { + background: transparent url(../files/ca_copy_icon_dark.svg) no-repeat !important +} + +.ca_payment-panel.dark .ca_progress_icon svg path { + fill: #6ac5ff !important; +} + +.ca_payment-panel.dark .ca_progress .ca_progress_icon.waiting_payment:before, +.ca_payment-panel.dark .ca_progress .ca_progress_icon.waiting_network:before { + background: #6ac5ff !important; +} + +.ca_payment-panel.dark .ca_payment_confirmed h2, +.ca_payment-panel.dark .ca_payment_cancelled h2 { + color: #FFF; +} + +@media (prefers-color-scheme: dark) { + .ca_payment-panel.auto { + color: #fafafa; + } + + .ca_payment-panel.auto .ca_details_input { + background-color: #424242 !important; + color: #FAFAFA !important; + } + + .ca_payment-panel.auto .ca_loader { + filter: invert(1); + } + + .ca_payment-panel.auto .ca_details_copy { + background-color: #424242 !important; + color: #FAFAFA !important; + } + + .ca_payment-panel.auto .ca_copy .ca_copy_icon_tooltip { + background-color: rgba(245, 245, 245, 1) !important; + color: #121212 !important; + } + + .ca_payment-panel.auto .ca_copy .ca_copy_icon_tooltip::after { + border-color: rgba(245, 245, 245, 1) transparent transparent transparent !important; + } + + .ca_payment-panel.auto .ca_buttons_container { + background-color: #424242 !important; + color: #FAFAFA !important; + } + + .ca_payment-panel.auto .ca_buttons_container a { + color: #6ac5ff !important; + border: 3px solid #6ac5ff !important; + } + + .ca_payment-panel.auto .ca_buttons_container a:hover { + background-color: #6ac5ff !important; + color: #212121 !important; + } + + .ca_payment-panel.auto .ca_copy_icon { + background: transparent url(../files/ca_copy_icon_dark.svg) no-repeat !important + } + + .ca_payment-panel.auto .ca_progress_icon svg path { + fill: #6ac5ff !important; + } + + .ca_payment-panel.auto .ca_progress .ca_progress_icon.waiting_payment:before, + .ca_payment-panel.auto .ca_progress .ca_progress_icon.waiting_network:before { + background: #6ac5ff !important; + } + + .ca_payment-panel.auto .ca_payment_confirmed h2, .ca_payment-panel.auto .ca_payment_cancelled h2 { + color: #FFF; + } } diff --git a/view/frontend/web/images/200_logo_ca.png b/view/frontend/web/files/200_logo_ca.png similarity index 100% rename from view/frontend/web/images/200_logo_ca.png rename to view/frontend/web/files/200_logo_ca.png diff --git a/view/frontend/web/images/ca_copy_icon.svg b/view/frontend/web/files/ca_copy_icon.svg similarity index 100% rename from view/frontend/web/images/ca_copy_icon.svg rename to view/frontend/web/files/ca_copy_icon.svg diff --git a/view/frontend/web/files/ca_copy_icon_dark.svg b/view/frontend/web/files/ca_copy_icon_dark.svg new file mode 100644 index 0000000..fa5eb65 --- /dev/null +++ b/view/frontend/web/files/ca_copy_icon_dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/view/frontend/web/images/ca_loader.svg b/view/frontend/web/files/ca_loader.svg similarity index 100% rename from view/frontend/web/images/ca_loader.svg rename to view/frontend/web/files/ca_loader.svg diff --git a/view/frontend/web/images/logo_ca.png b/view/frontend/web/files/logo_ca.png similarity index 100% rename from view/frontend/web/images/logo_ca.png rename to view/frontend/web/files/logo_ca.png diff --git a/view/frontend/web/js/model/validate-cryptocurrency.js b/view/frontend/web/js/model/validate-cryptocurrency.js new file mode 100644 index 0000000..bf45a8a --- /dev/null +++ b/view/frontend/web/js/model/validate-cryptocurrency.js @@ -0,0 +1,21 @@ +define( + ['mage/translate', 'Magento_Ui/js/model/messageList'], + function ($t, messageList) { + 'use strict'; + return { + validate: function () { + var isValid = false; + + if (document.getElementById("cryptapi_payment_cryptocurrency_id").value) { + isValid = true; + } + + if (!isValid) { + messageList.addErrorMessage({message: $t('Please select a cryptocurrency.')}); + } + + return isValid; + } + } + } +); diff --git a/view/frontend/web/js/view/checkout/cart/cart-script.js b/view/frontend/web/js/view/checkout/cart/cart-script.js new file mode 100644 index 0000000..a040d0f --- /dev/null +++ b/view/frontend/web/js/view/checkout/cart/cart-script.js @@ -0,0 +1,42 @@ +require([ + 'jquery', + 'mage/url', + 'Magento_Checkout/js/model/quote', + 'Magento_Checkout/js/model/cart/totals-processor/default' +], function ($, url, quote, + totalsDefaultProvider) { + $(function () { + $.getJSON(url.build('cryptapi/index/cartquote') + '?selected=', function (data) { + totalsDefaultProvider.estimateTotals(quote.shippingAddress()); + }); + + $('body').on('change', function () { + + var cryptoSelector = $('#cryptapi_payment_cryptocurrency_id'); + + var linkUrl = url.build('cryptapi/index/cartquote'); + + var feeContainer = $('.totals.fee.excl'); + + setInterval(function () { + if($('body').attr('aria-busy') === 'false') { + if (quote.paymentMethod._latestValue.method === 'cryptapi' && parseFloat($('.totals.fee.excl .price').html().replace(/\D/g,'')) > 0 ) { + feeContainer.show(); + } else { + feeContainer.hide(); + } + } + }, 1000); + + cryptoSelector.unbind('change'); + cryptoSelector.on('change', function () { + $.getJSON(linkUrl + '?selected=' + cryptoSelector.val(), function (data) { + totalsDefaultProvider.estimateTotals(quote.shippingAddress()); + }); + }); + + + }) + }); +}); + diff --git a/view/frontend/web/js/view/checkout/cart/totals/fee.js b/view/frontend/web/js/view/checkout/cart/totals/fee.js new file mode 100644 index 0000000..bd6c415 --- /dev/null +++ b/view/frontend/web/js/view/checkout/cart/totals/fee.js @@ -0,0 +1,22 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +define( + [ + 'Cryptapi_Cryptapi/js/view/checkout/summary/fee' + ], + function (Component) { + 'use strict'; + + return Component.extend({ + + /** + * @override + */ + isDisplayed: function () { + return true; + } + }); + } +); diff --git a/view/frontend/web/js/cryptapi-payments.js b/view/frontend/web/js/view/checkout/payment/cryptapi-payments.js similarity index 79% rename from view/frontend/web/js/cryptapi-payments.js rename to view/frontend/web/js/view/checkout/payment/cryptapi-payments.js index 4e8a491..072345f 100644 --- a/view/frontend/web/js/cryptapi-payments.js +++ b/view/frontend/web/js/view/checkout/payment/cryptapi-payments.js @@ -12,9 +12,9 @@ define( rendererList.push( { type: 'cryptapi', - component: 'Cryptapi_Cryptapi/js/cryptapi' + component: 'Cryptapi_Cryptapi/js/view/checkout/payment/cryptapi' } ); return Component.extend({}); } -); \ No newline at end of file +); diff --git a/view/frontend/web/js/cryptapi.js b/view/frontend/web/js/view/checkout/payment/cryptapi.js similarity index 80% rename from view/frontend/web/js/cryptapi.js rename to view/frontend/web/js/view/checkout/payment/cryptapi.js index e403178..e05a4f8 100644 --- a/view/frontend/web/js/cryptapi.js +++ b/view/frontend/web/js/view/checkout/payment/cryptapi.js @@ -6,7 +6,7 @@ define([ return Component.extend({ defaults: { - template: 'Cryptapi_Cryptapi/cryptapi' + template: 'Cryptapi_Cryptapi/checkout/payment/cryptapi' }, getCryptocurrencies: function () { @@ -27,9 +27,7 @@ define([ }, getSelectedCoin() { - var selected = document.getElementById("cryptapi_payment_cryptocurrency_id").value; - - return selected; + return document.getElementById("cryptapi_payment_cryptocurrency_id").value; } }); }); diff --git a/view/frontend/web/js/view/checkout/summary/fee.js b/view/frontend/web/js/view/checkout/summary/fee.js new file mode 100644 index 0000000..432bafa --- /dev/null +++ b/view/frontend/web/js/view/checkout/summary/fee.js @@ -0,0 +1,37 @@ +/*jshint browser:true jquery:true*/ +/*global alert*/ +define( + [ + 'Magento_Checkout/js/view/summary/abstract-total', + 'Magento_Checkout/js/model/quote', + 'Magento_Catalog/js/price-utils', + 'Magento_Checkout/js/model/totals', + 'Magento_Checkout/js/model/cart/totals-processor/default' + ], + function (Component, quote, priceUtils, totals) { + "use strict"; + return Component.extend({ + defaults: { + isFullTaxSummaryDisplayed: window.checkoutConfig.isFullTaxSummaryDisplayed || false, + template: 'Cryptapi_Cryptapi/checkout/summary/fee' + }, + totals: quote.getTotals(), + isTaxDisplayedInGrandTotal: window.checkoutConfig.includeTaxInGrandTotal || false, + + getValue: function () { + var price = 0; + if (this.totals()) { + price = totals.getSegment('fee').value; + } + return this.getFormattedPrice(price); + }, + getBaseValue: function () { + var price = 0; + if (this.totals()) { + price = this.totals().base_fee; + } + return priceUtils.formatPrice(price, quote.getBasePriceFormat()); + } + }); + } +); diff --git a/view/frontend/web/js/view/cryptapi-validation.js b/view/frontend/web/js/view/cryptapi-validation.js new file mode 100644 index 0000000..e42decd --- /dev/null +++ b/view/frontend/web/js/view/cryptapi-validation.js @@ -0,0 +1,12 @@ +define( + [ + 'uiComponent', + 'Magento_Checkout/js/model/payment/additional-validators', + 'Cryptapi_Cryptapi/js/model/validate-cryptocurrency' + ], + function (Component, additionalValidators, validateCryptocurrency) { + 'use strict'; + additionalValidators.registerValidator(validateCryptocurrency); + return Component.extend({}); + } +); diff --git a/view/frontend/web/template/checkout/cart/totals/fee.html b/view/frontend/web/template/checkout/cart/totals/fee.html new file mode 100644 index 0000000..2aa1c42 --- /dev/null +++ b/view/frontend/web/template/checkout/cart/totals/fee.html @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/view/frontend/web/template/cryptapi.html b/view/frontend/web/template/checkout/payment/cryptapi.html similarity index 71% rename from view/frontend/web/template/cryptapi.html rename to view/frontend/web/template/checkout/payment/cryptapi.html index 53ad420..d0f132a 100644 --- a/view/frontend/web/template/cryptapi.html +++ b/view/frontend/web/template/checkout/payment/cryptapi.html @@ -1,31 +1,29 @@
- - - +
+ + +

-
-
- - -
+
-
diff --git a/view/frontend/web/template/checkout/summary/fee.html b/view/frontend/web/template/checkout/summary/fee.html new file mode 100644 index 0000000..cc34e63 --- /dev/null +++ b/view/frontend/web/template/checkout/summary/fee.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + From 40705884ff5dd44cb9503d72a880650d809c1d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ariano=20=C3=82ngelo?= Date: Mon, 11 Apr 2022 17:14:18 +0100 Subject: [PATCH 2/4] v3.0 - New settings and color schemes to fit dark mode - New settings to add CryptAPI's services fees to the checkout - New settings to add blockchain fees to the checkout - Upgrade the settings - Added a history of transactions to the order payment page - Better handling of partial payments - Disable QR Code with value in certain currencies due to some wallets not supporting it - Minor fixes - UI Improvements --- .gitignore | 1 - Controller/Index/Callback.php | 35 ++++++------------ Controller/Index/CartQuote.php | 8 ++-- Controller/Index/Status.php | 24 ++++++------ Cron/CryptapiCronjob.php | 12 +++--- Helper/Decimal.php | 53 --------------------------- Model/Method/CryptapiPayment.php | 36 +++++++++--------- composer.json | 5 ++- view/frontend/templates/payment.phtml | 4 +- 9 files changed, 57 insertions(+), 121 deletions(-) delete mode 100644 .gitignore delete mode 100644 Helper/Decimal.php diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 62c8935..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.idea/ \ No newline at end of file diff --git a/Controller/Index/Callback.php b/Controller/Index/Callback.php index b2262bc..a96d598 100644 --- a/Controller/Index/Callback.php +++ b/Controller/Index/Callback.php @@ -17,7 +17,7 @@ public function __construct( \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Framework\App\Request\Http $request, - \Magento\Framework\App\Response\Http\Interceptor $response + \Magento\Framework\App\Response\Http $response ) { $this->helper = $helper; @@ -37,30 +37,20 @@ public function execute() $order = $this->orderRepository->get($data['order_id']); $orderId = $order->getQuoteId(); - $currencySymbol = $order->getOrderCurrencyCode(); - - $metaData = json_decode($this->helper->getPaymentResponse($orderId), true); + $metaData = json_decode($this->helper->getPaymentResponse($orderId), true); if ($this->payment->hasBeenPaid($order) || $data['nonce'] != $metaData['cryptapi_nonce']) { return $this->response->setBody("*ok*"); } - $paid = floatval($data['value_coin']); + $paid = $data['value_coin']; $min_tx = floatval($metaData['cryptapi_min']); $history = json_decode($metaData['cryptapi_history'], true); - $update_history = true; - - foreach ($history as $uuid => $item) { - if ($uuid === $data['uuid']) { - $update_history = false; - } - } - - if ($update_history) { - $fiat_conversion = CryptAPIHelper::get_conversion($metaData['cryptapi_currency'], $currencySymbol, $paid, $this->scopeConfig->getValue('payment/cryptapi/disable_conversion', \Magento\Store\Model\ScopeInterface::SCOPE_STORE)); + if (empty($history[$data['uuid']])) { + $fiat_conversion = CryptAPIHelper::get_conversion($metaData['cryptapi_currency'], $order->getOrderCurrencyCode(), $paid, $this->scopeConfig->getValue('payment/cryptapi/disable_conversion', \Magento\Store\Model\ScopeInterface::SCOPE_STORE)); $history[$data['uuid']] = [ 'timestamp' => time(), @@ -74,17 +64,17 @@ public function execute() $this->helper->updatePaymentData($orderId, 'cryptapi_history', json_encode($history)); - $metaData = json_decode($this->helper->getPaymentResponse($orderId), true); + $metaData = json_decode($this->helper->getPaymentResponse($orderId), true); $history = json_decode($metaData['cryptapi_history'], true); - $calc = $this->payment::calcOrder($history, $metaData); + $calc = $this->payment::calcOrder($history, $metaData['cryptapi_total'], $metaData['cryptapi_total_fiat']); - $remaining = $calc['remaining']->result(); - $remaining_pending = $calc['remaining_pending']->result(); + $remaining = $calc['remaining']; + $remaining_pending = $calc['remaining_pending']; - if ($remaining <= 0) { - if ($data['pending'] === '0') { + if ($remaining_pending <= 0) { + if ($remaining <= 0) { $state = \Magento\Sales\Model\Order::STATE_PROCESSING; $status = \Magento\Sales\Model\Order::STATE_PROCESSING; $order->setState($state); @@ -92,6 +82,7 @@ public function execute() $order->setTotalPaid($order->getGrandTotal()); $order->save(); } + return $this->response->setBody("*ok*"); } if ($remaining_pending <= $min_tx) { @@ -100,8 +91,6 @@ public function execute() $this->helper->updatePaymentData($orderId, 'cryptapi_qr_code_value', CryptAPIHelper::get_static_qrcode($metaData['cryptapi_address'], $metaData['cryptapi_currency'], $remaining_pending, $this->scopeConfig->getValue('payment/cryptapi/qrcode_size', \Magento\Store\Model\ScopeInterface::SCOPE_STORE))['qr_code']); } - - return $this->response->setBody("*ok*"); } } diff --git a/Controller/Index/CartQuote.php b/Controller/Index/CartQuote.php index 44dc761..cf0bf02 100644 --- a/Controller/Index/CartQuote.php +++ b/Controller/Index/CartQuote.php @@ -12,10 +12,10 @@ class CartQuote implements HttpGetActionInterface protected $quoteRepository; public function __construct( - \Magento\Checkout\Model\Session $checkoutSession, - \Magento\Framework\App\Request\Http $request, - \Magento\Framework\App\Response\Http\Interceptor $response, - \Psr\Log\LoggerInterface $logger + \Magento\Checkout\Model\Session $checkoutSession, + \Magento\Framework\App\Request\Http $request, + \Magento\Framework\App\Response\Http $response, + \Psr\Log\LoggerInterface $logger ) { $this->checkoutSession = $checkoutSession; diff --git a/Controller/Index/Status.php b/Controller/Index/Status.php index 8589652..46effc0 100644 --- a/Controller/Index/Status.php +++ b/Controller/Index/Status.php @@ -18,7 +18,7 @@ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Cryptapi\Cryptapi\Cron\CryptapiCronjob $cronjob, \Magento\Framework\App\Request\Http $request, - \Magento\Framework\App\Response\Http\Interceptor $response, + \Magento\Framework\App\Response\Http $response, \Magento\Framework\Pricing\Helper\Data $priceHelper, \Psr\Log\LoggerInterface $logger ) @@ -49,20 +49,20 @@ public function execute() $history = json_decode($metaData['cryptapi_history'], true); - $calc = $this->payment::calcOrder($history, $metaData); + $calc = $this->payment::calcOrder($history, $metaData['cryptapi_total'], $metaData['cryptapi_total_fiat']); - $already_paid = $calc['already_paid']->result(); - $already_paid_fiat = $calc['already_paid_fiat']->result() <= 0 ? 0 : $calc['already_paid_fiat']->result(); + $already_paid = $calc['already_paid']; + $already_paid_fiat = $calc['already_paid_fiat'] <= 0 ? 0 : $calc['already_paid_fiat']; $min_tx = floatval($metaData['cryptapi_min']); - // $remaining = $calc['remaining']; - $remaining_pending = $calc['remaining_pending']->result(); - $remaining_fiat = $calc['remaining_fiat']->result(); + $remaining = $calc['remaining']; + $remaining_pending = $calc['remaining_pending']; + $remaining_fiat = $calc['remaining_fiat']; $cryptapi_pending = '0'; if ($remaining_pending <= 0 && !$this->payment->hasBeenPaid($order)) { - $cryptapi_pending = '1'; + $cryptapi_pending = 1; } $counter_calc = (int)$metaData['cryptapi_last_price_update'] + (int)$this->scopeConfig->getValue('payment/cryptapi/refresh_value_interval', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) - time(); @@ -70,21 +70,21 @@ public function execute() $this->cronjob->execute(); } - if ($remaining_pending <= $min_tx && $remaining_fiat > 0) { + if ($remaining_pending <= $min_tx && $remaining_pending > 0) { $remaining_pending = $min_tx; $showMinFee = '1'; } $data = [ 'is_paid' => $this->payment->hasBeenPaid($order), - 'is_pending' => (int)($cryptapi_pending), + 'is_pending' => (int)$cryptapi_pending, 'crypto_total' => floatval($metaData['cryptapi_total']), 'qr_code_value' => $metaData['cryptapi_qr_code_value'], 'cancelled' => $metaData['cryptapi_cancelled'], - 'remaining' => $remaining_pending <= 0 ? 0 : $remaining_pending, + 'remaining' => $remaining_pending < 0 ? 0 : $remaining_pending, 'fiat_remaining' => $this->priceHelper->currency($remaining_fiat, true, false), 'coin' => strtoupper($metaData['cryptapi_currency']), - 'show_min_fee' => $showMinFee, + 'show_min_fee' => (int)$showMinFee, 'order_history' => $history, 'already_paid' => $already_paid, 'already_paid_fiat' => $this->priceHelper->currency(floatval($already_paid_fiat) <= 0 ? 0 : floatval($already_paid_fiat), true, false), diff --git a/Cron/CryptapiCronjob.php b/Cron/CryptapiCronjob.php index 978a14d..5e9b933 100644 --- a/Cron/CryptapiCronjob.php +++ b/Cron/CryptapiCronjob.php @@ -54,11 +54,11 @@ public function execute() $min_tx = floatval($metaData['cryptapi_min']); - $calc = $this->payment::calcOrder($history, $metaData); + $calc = $this->payment::calcOrder($history, $metaData['cryptapi_total'], $metaData['cryptapi_total_fiat']); - $remaining = $calc['remaining']->result(); - $remaining_pending = $calc['remaining_pending']->result(); - $remaining_fiat = $calc['remaining_fiat']->result(); + $remaining = $calc['remaining']; + $remaining_pending = $calc['remaining_pending']; + $remaining_fiat = $calc['remaining_fiat']; if (!empty($metaData['cryptapi_address']) && $value_refresh !== 0 && $metaData['cryptapi_cancelled'] !== '1' && (int)$metaData['cryptapi_last_price_update'] + $value_refresh <= time() && $remaining_pending > 0) { @@ -68,8 +68,8 @@ public function execute() $crypto_total = CryptAPIHelper::get_conversion($order->getOrderCurrencyCode(), $cryptapi_coin, $metaData['cryptapi_total_fiat'], $disable_conversion); $this->helper->updatePaymentData($orderQuoteId, 'cryptapi_total', $crypto_total); - $calc_cron = $this->payment::calcOrder($history, $metaData); - $crypto_remaining_total = $calc_cron['remaining_pending']->result(); + $calc_cron = $this->payment::calcOrder($history, $metaData['cryptapi_total'], $metaData['cryptapi_total_fiat']); + $crypto_remaining_total = $calc_cron['remaining_pending']; if ($remaining_pending <= $min_tx && !$remaining_pending <= 0) { $qr_code_data_value = CryptAPIHelper::get_static_qrcode($metaData['cryptapi_address'], $cryptapi_coin, $min_tx, $this->scopeConfig->getValue('payment/cryptapi/qrcode_size')); diff --git a/Helper/Decimal.php b/Helper/Decimal.php deleted file mode 100644 index f54e113..0000000 --- a/Helper/Decimal.php +++ /dev/null @@ -1,53 +0,0 @@ -precision; - } - - private function maybe_reduce_precision($new_val) { - while ($new_val >= PHP_INT_MAX) { - $new_val = ($new_val / 10 ** $this->precision) * 10 ** ($this->precision - 1); - $this->precision -= 1; - } - - $this->val = intval($new_val); - } - - function __construct($float_val) { - $this->sum($float_val); - } - - function mult($float_val) { - $new_val = $this->val * $this->to_int($float_val); - $this->maybe_reduce_precision($new_val); - return $this; - } - - function div($float_val) { - $new_val = $this->val / $this->to_int($float_val); - $this->maybe_reduce_precision($new_val); - return $this; - } - - function sum($float_val) { - $new_val = $this->val + $this->to_int($float_val); - $this->maybe_reduce_precision($new_val); - return $this; - } - - function sub($float_val) { - $new_val = $this->val - $this->to_int($float_val); - $this->maybe_reduce_precision($new_val); - return $this; - } - - function result() { - return $this->val / 10 ** $this->precision; - } -} diff --git a/Model/Method/CryptapiPayment.php b/Model/Method/CryptapiPayment.php index fd6ffe3..0e250f3 100644 --- a/Model/Method/CryptapiPayment.php +++ b/Model/Method/CryptapiPayment.php @@ -7,7 +7,7 @@ use Magento\Framework\DataObject; use Cryptapi\Cryptapi\lib\CryptAPIHelper; use Magento\Payment\Model\Method\AbstractMethod; -use Cryptapi\Cryptapi\Helper\Decimal; + class CryptapiPayment extends AbstractMethod { @@ -205,34 +205,34 @@ public function hasBeenPaid($order) } } - public static function calcOrder($history, $meta) + public static function calcOrder($history, $total, $total_fiat) { - $already_paid = new Decimal(0); - $already_paid_fiat = new Decimal(0); - $remaining = new Decimal($meta['cryptapi_total']); - $remaining_pending = new Decimal($meta['cryptapi_total']); - $remaining_fiat = new Decimal($meta['cryptapi_total_fiat']); + $already_paid = 0; + $already_paid_fiat = 0; + $remaining = $total; + $remaining_pending = $total; + $remaining_fiat = $total_fiat; - if (count($history) > 0) { + if (!empty($history)) { foreach ($history as $uuid => $item) { if ((int)$item['pending'] === 0) { - $remaining = $remaining->sub($item['value_paid']); + $remaining = bcsub($remaining, $item['value_paid'], 18); } - $remaining_pending = $remaining_pending->sub($item['value_paid']); - $remaining_fiat = $remaining_fiat->sub($item['value_paid_fiat']); + $remaining_pending = bcsub($remaining_pending, $item['value_paid'], 18); + $remaining_fiat = bcsub($remaining_fiat, $item['value_paid_fiat'], 18); - $already_paid = $already_paid->sum($item['value_paid']); - $already_paid_fiat = $already_paid_fiat->sum($item['value_paid_fiat']); + $already_paid = bcadd($already_paid, $item['value_paid'], 18); + $already_paid_fiat = bcadd($already_paid_fiat, $item['value_paid_fiat'], 18); } } return [ - 'already_paid' => $already_paid, - 'already_paid_fiat' => $already_paid_fiat, - 'remaining' => $remaining, - 'remaining_pending' => $remaining_pending, - 'remaining_fiat' => $remaining_fiat + 'already_paid' => floatval($already_paid), + 'already_paid_fiat' => floatval($already_paid_fiat), + 'remaining' => floatval($remaining), + 'remaining_pending' => floatval($remaining_pending), + 'remaining_fiat' => floatval($remaining_fiat) ]; } diff --git a/composer.json b/composer.json index 3c8d241..3b6b7b7 100644 --- a/composer.json +++ b/composer.json @@ -2,13 +2,14 @@ "name": "cryptapi/cryptapi", "description": "CryptAPI's Magento extension", "type": "magento2-module", - "version": "3.0", + "version": "3.0.0", "require": { "magento/module-payment": "100.1.*", "magento/module-checkout": "100.1.*", "magento/module-sales": "100.1.*", "ext-curl": "*", - "ext-json": "*" + "ext-json": "*", + "ext-bcmath": "*" }, "autoload": { "files": [ "registration.php" ], diff --git a/view/frontend/templates/payment.phtml b/view/frontend/templates/payment.phtml index 2d267e9..6900db2 100644 --- a/view/frontend/templates/payment.phtml +++ b/view/frontend/templates/payment.phtml @@ -47,7 +47,7 @@ if ($values) { is_paid = true; } - if (data.is_pending) { + if (data.is_pending === 1) { waiting_payment.addClass('done'); waiting_network.addClass('done'); jQuery('.ca_loader').remove(); @@ -81,7 +81,7 @@ if ($values) { jQuery('.ca_qrcode.value').attr("src", "data:image/png;base64," + data.qr_code_value); } - if (data.show_min_fee === '1') { + if (data.show_min_fee === 1) { jQuery('.ca_notification_remaining').show(); } else { jQuery('.ca_notification_remaining').hide(); From c13729c8d66df8b9d4686079ff2b867c75f07e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ariano=20Fonseca=20=C3=82ngelo?= Date: Wed, 4 May 2022 17:47:01 +0100 Subject: [PATCH 3/4] v3.0.2 - Minor bugfix --- Block/Payment.php | 20 +- Controller/Index/Callback.php | 18 +- Controller/Index/CartQuote.php | 4 +- Controller/Index/Status.php | 14 +- Cron/CryptapiCronjob.php | 4 +- Helper/Decimal.php | 53 ++ Model/Method/CryptapiPayment.php | 21 +- Model/Total/Fee.php | 1 - Observer/AfterSuccess.php | 18 +- README.md | 8 +- composer.json | 6 +- etc/config.xml | 3 +- etc/frontend/events.xml | 2 +- etc/frontend/routes.xml | 7 +- lib/CryptAPIHelper.php | 13 +- view/frontend/layout/checkout_index_index.xml | 6 +- .../layout/checkout_onepage_success.xml | 15 + view/frontend/layout/sales_order_view.xml | 7 - view/frontend/requirejs-config.js | 2 +- view/frontend/templates/payment.phtml | 832 +++++++++--------- view/frontend/templates/script.phtml | 7 + .../web/js/model/validate-cryptocurrency.js | 9 +- .../web/js/view/checkout/cart/cart-script.js | 49 +- .../checkout/payment/cryptapi-payments.js | 4 +- 24 files changed, 624 insertions(+), 499 deletions(-) create mode 100644 Helper/Decimal.php create mode 100644 view/frontend/layout/checkout_onepage_success.xml create mode 100644 view/frontend/templates/script.phtml diff --git a/Block/Payment.php b/Block/Payment.php index eff5c75..eafee0e 100644 --- a/Block/Payment.php +++ b/Block/Payment.php @@ -17,6 +17,7 @@ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, \Magento\Framework\App\Request\Http $request, + \Magento\Framework\App\ProductMetadataInterface $productMetadata, array $data = [] ) { @@ -26,13 +27,18 @@ public function __construct( $this->scopeConfig = $scopeConfig; $this->request = $request; $this->orderRepository = $orderRepository; + $this->productMetadata = $productMetadata; } public function getTemplateValues() { - $order_id = (int)$this->request->getParam('order_id'); - $nonce = (string)$this->request->getParam('nonce'); - $order = $this->orderRepository->get($order_id); + if ($this->productMetadata->getVersion() >= 2.3 && $this->productMetadata->getVersion() < 2.4) { + $order = $this->payment->getOrder(); + } else { + $order_id = (int)$this->request->getParam('order_id'); + $nonce = (string)$this->request->getParam('nonce'); + $order = $this->orderRepository->get($order_id); + } $total = $order->getGrandTotal(); $currencySymbol = $order->getOrderCurrencyCode(); @@ -48,8 +54,10 @@ public function getTemplateValues() $metaData = json_decode($metaData, true); - if ($nonce != $metaData['cryptapi_nonce']) { - return false; + if (!$this->productMetadata->getVersion() >= 2.3 && $this->productMetadata->getVersion() < 2.4) { + if ($nonce != $metaData['cryptapi_nonce']) { + return false; + } } $cryptoValue = $metaData['cryptapi_total']; @@ -89,7 +97,7 @@ public function getTemplateValues() } $ajaxParams = [ - 'order_id' => $order_id, + 'order_id' => $order->getId(), ]; $ajaxUrl = $this->payment->getAjaxStatusUrl($ajaxParams); diff --git a/Controller/Index/Callback.php b/Controller/Index/Callback.php index a96d598..7c75d81 100644 --- a/Controller/Index/Callback.php +++ b/Controller/Index/Callback.php @@ -37,20 +37,30 @@ public function execute() $order = $this->orderRepository->get($data['order_id']); $orderId = $order->getQuoteId(); + $currencySymbol = $order->getOrderCurrencyCode(); + $metaData = json_decode($this->helper->getPaymentResponse($orderId), true); if ($this->payment->hasBeenPaid($order) || $data['nonce'] != $metaData['cryptapi_nonce']) { return $this->response->setBody("*ok*"); } - $paid = $data['value_coin']; + $paid = floatval($data['value_coin']); $min_tx = floatval($metaData['cryptapi_min']); $history = json_decode($metaData['cryptapi_history'], true); - if (empty($history[$data['uuid']])) { - $fiat_conversion = CryptAPIHelper::get_conversion($metaData['cryptapi_currency'], $order->getOrderCurrencyCode(), $paid, $this->scopeConfig->getValue('payment/cryptapi/disable_conversion', \Magento\Store\Model\ScopeInterface::SCOPE_STORE)); + $update_history = true; + + foreach ($history as $uuid => $item) { + if ($uuid === $data['uuid']) { + $update_history = false; + } + } + + if ($update_history) { + $fiat_conversion = CryptAPIHelper::get_conversion($metaData['cryptapi_currency'], $currencySymbol, $paid, $this->scopeConfig->getValue('payment/cryptapi/disable_conversion', \Magento\Store\Model\ScopeInterface::SCOPE_STORE)); $history[$data['uuid']] = [ 'timestamp' => time(), @@ -68,7 +78,7 @@ public function execute() $history = json_decode($metaData['cryptapi_history'], true); - $calc = $this->payment::calcOrder($history, $metaData['cryptapi_total'], $metaData['cryptapi_total_fiat']); + $calc = $this->payment::calcOrder($history, $metaData); $remaining = $calc['remaining']; $remaining_pending = $calc['remaining_pending']; diff --git a/Controller/Index/CartQuote.php b/Controller/Index/CartQuote.php index cf0bf02..5fbdede 100644 --- a/Controller/Index/CartQuote.php +++ b/Controller/Index/CartQuote.php @@ -14,14 +14,12 @@ class CartQuote implements HttpGetActionInterface public function __construct( \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\App\Request\Http $request, - \Magento\Framework\App\Response\Http $response, - \Psr\Log\LoggerInterface $logger + \Magento\Framework\App\Response\Http $response ) { $this->checkoutSession = $checkoutSession; $this->request = $request; $this->response = $response; - $this->logger = $logger; } public function execute() diff --git a/Controller/Index/Status.php b/Controller/Index/Status.php index 46effc0..93c5e7d 100644 --- a/Controller/Index/Status.php +++ b/Controller/Index/Status.php @@ -49,20 +49,20 @@ public function execute() $history = json_decode($metaData['cryptapi_history'], true); - $calc = $this->payment::calcOrder($history, $metaData['cryptapi_total'], $metaData['cryptapi_total_fiat']); + $calc = $this->payment::calcOrder($history, $metaData); $already_paid = $calc['already_paid']; $already_paid_fiat = $calc['already_paid_fiat'] <= 0 ? 0 : $calc['already_paid_fiat']; $min_tx = floatval($metaData['cryptapi_min']); - $remaining = $calc['remaining']; + // $remaining = $calc['remaining']; $remaining_pending = $calc['remaining_pending']; $remaining_fiat = $calc['remaining_fiat']; $cryptapi_pending = '0'; if ($remaining_pending <= 0 && !$this->payment->hasBeenPaid($order)) { - $cryptapi_pending = 1; + $cryptapi_pending = '1'; } $counter_calc = (int)$metaData['cryptapi_last_price_update'] + (int)$this->scopeConfig->getValue('payment/cryptapi/refresh_value_interval', \Magento\Store\Model\ScopeInterface::SCOPE_STORE) - time(); @@ -77,17 +77,17 @@ public function execute() $data = [ 'is_paid' => $this->payment->hasBeenPaid($order), - 'is_pending' => (int)$cryptapi_pending, + 'is_pending' => (int)($cryptapi_pending), 'crypto_total' => floatval($metaData['cryptapi_total']), 'qr_code_value' => $metaData['cryptapi_qr_code_value'], 'cancelled' => $metaData['cryptapi_cancelled'], - 'remaining' => $remaining_pending < 0 ? 0 : $remaining_pending, + 'remaining' => $remaining_pending <= 0 ? 0 : $remaining_pending, 'fiat_remaining' => $this->priceHelper->currency($remaining_fiat, true, false), 'coin' => strtoupper($metaData['cryptapi_currency']), - 'show_min_fee' => (int)$showMinFee, + 'show_min_fee' => $showMinFee, 'order_history' => $history, 'already_paid' => $already_paid, - 'already_paid_fiat' => $this->priceHelper->currency(floatval($already_paid_fiat) <= 0 ? 0 : floatval($already_paid_fiat), true, false), + 'already_paid_fiat' => $this->priceHelper->currency($remaining_pending <= 0 ? 0 : floatval($already_paid_fiat), true, false), 'counter' => (string)$counter_calc, 'fiat_symbol' => $order->getOrderCurrencyCode() ]; diff --git a/Cron/CryptapiCronjob.php b/Cron/CryptapiCronjob.php index 5e9b933..b6d7324 100644 --- a/Cron/CryptapiCronjob.php +++ b/Cron/CryptapiCronjob.php @@ -54,7 +54,7 @@ public function execute() $min_tx = floatval($metaData['cryptapi_min']); - $calc = $this->payment::calcOrder($history, $metaData['cryptapi_total'], $metaData['cryptapi_total_fiat']); + $calc = $this->payment::calcOrder($history, $metaData); $remaining = $calc['remaining']; $remaining_pending = $calc['remaining_pending']; @@ -68,7 +68,7 @@ public function execute() $crypto_total = CryptAPIHelper::get_conversion($order->getOrderCurrencyCode(), $cryptapi_coin, $metaData['cryptapi_total_fiat'], $disable_conversion); $this->helper->updatePaymentData($orderQuoteId, 'cryptapi_total', $crypto_total); - $calc_cron = $this->payment::calcOrder($history, $metaData['cryptapi_total'], $metaData['cryptapi_total_fiat']); + $calc_cron = $this->payment::calcOrder($history, $metaData); $crypto_remaining_total = $calc_cron['remaining_pending']; if ($remaining_pending <= $min_tx && !$remaining_pending <= 0) { diff --git a/Helper/Decimal.php b/Helper/Decimal.php new file mode 100644 index 0000000..f54e113 --- /dev/null +++ b/Helper/Decimal.php @@ -0,0 +1,53 @@ +precision; + } + + private function maybe_reduce_precision($new_val) { + while ($new_val >= PHP_INT_MAX) { + $new_val = ($new_val / 10 ** $this->precision) * 10 ** ($this->precision - 1); + $this->precision -= 1; + } + + $this->val = intval($new_val); + } + + function __construct($float_val) { + $this->sum($float_val); + } + + function mult($float_val) { + $new_val = $this->val * $this->to_int($float_val); + $this->maybe_reduce_precision($new_val); + return $this; + } + + function div($float_val) { + $new_val = $this->val / $this->to_int($float_val); + $this->maybe_reduce_precision($new_val); + return $this; + } + + function sum($float_val) { + $new_val = $this->val + $this->to_int($float_val); + $this->maybe_reduce_precision($new_val); + return $this; + } + + function sub($float_val) { + $new_val = $this->val - $this->to_int($float_val); + $this->maybe_reduce_precision($new_val); + return $this; + } + + function result() { + return $this->val / 10 ** $this->precision; + } +} diff --git a/Model/Method/CryptapiPayment.php b/Model/Method/CryptapiPayment.php index 0e250f3..0c23f75 100644 --- a/Model/Method/CryptapiPayment.php +++ b/Model/Method/CryptapiPayment.php @@ -2,12 +2,11 @@ namespace Cryptapi\Cryptapi\Model\Method; - use Magento\Quote\Api\Data\PaymentInterface; use Magento\Framework\DataObject; use Cryptapi\Cryptapi\lib\CryptAPIHelper; use Magento\Payment\Model\Method\AbstractMethod; - +use Cryptapi\Cryptapi\Helper\Decimal; class CryptapiPayment extends AbstractMethod { @@ -205,25 +204,25 @@ public function hasBeenPaid($order) } } - public static function calcOrder($history, $total, $total_fiat) + public static function calcOrder($history, $meta) { $already_paid = 0; $already_paid_fiat = 0; - $remaining = $total; - $remaining_pending = $total; - $remaining_fiat = $total_fiat; + $remaining = $meta['cryptapi_total']; + $remaining_pending = $meta['cryptapi_total']; + $remaining_fiat = $meta['cryptapi_total_fiat']; if (!empty($history)) { foreach ($history as $uuid => $item) { if ((int)$item['pending'] === 0) { - $remaining = bcsub($remaining, $item['value_paid'], 18); + $remaining = bcsub(CryptAPIHelper::sig_fig($remaining, 6), $item['value_paid'], 8); } - $remaining_pending = bcsub($remaining_pending, $item['value_paid'], 18); - $remaining_fiat = bcsub($remaining_fiat, $item['value_paid_fiat'], 18); + $remaining_pending = bcsub(CryptAPIHelper::sig_fig($remaining_pending, 6), $item['value_paid'], 8); + $remaining_fiat = bcsub(CryptAPIHelper::sig_fig($remaining_fiat, 6), $item['value_paid_fiat'], 8); - $already_paid = bcadd($already_paid, $item['value_paid'], 18); - $already_paid_fiat = bcadd($already_paid_fiat, $item['value_paid_fiat'], 18); + $already_paid = bcadd(CryptAPIHelper::sig_fig($already_paid, 6), $item['value_paid'], 8); + $already_paid_fiat = bcadd(CryptAPIHelper::sig_fig($already_paid_fiat, 6), $item['value_paid_fiat'], 8); } } diff --git a/Model/Total/Fee.php b/Model/Total/Fee.php index ce5c787..c137cfd 100644 --- a/Model/Total/Fee.php +++ b/Model/Total/Fee.php @@ -55,7 +55,6 @@ public function collect( $total->setGrandTotal($total->getGrandTotal()); $total->setBaseGrandTotal($total->getBaseGrandTotal()); - $quote->setFee($fee); return $this; diff --git a/Observer/AfterSuccess.php b/Observer/AfterSuccess.php index a6416cf..064a1f8 100644 --- a/Observer/AfterSuccess.php +++ b/Observer/AfterSuccess.php @@ -13,7 +13,9 @@ public function __construct( \Cryptapi\Cryptapi\Model\Method\CryptapiPayment $payment, \Magento\Framework\App\ResponseFactory $responseFactory, \Magento\Framework\UrlInterface $url, - \Psr\Log\LoggerInterface $logger + \Psr\Log\LoggerInterface $logger, + \Magento\Framework\App\ProductMetadataInterface $productMetadata, + \Magento\Framework\App\Response\Http $redirect ) { @@ -22,10 +24,22 @@ public function __construct( $this->url = $url; $this->responseFactory = $responseFactory; $this->logger = $logger; + $this->productMetadata = $productMetadata; + $this->redirect = $redirect; } public function execute(Observer $observer) { + $version_check = 1; + + if ($this->productMetadata->getVersion() >= 2.3 && $this->productMetadata->getVersion() < 2.4) { + $version_check = 0; + } + + if (empty($version_check)) { + return false; + } + $order = $this->payment->getOrder(); $paymentMethod = $order->getPayment()->getMethodInstance()->getCode(); @@ -39,7 +53,7 @@ public function execute(Observer $observer) $redirectOrder = $this->url->getUrl('cryptapi/index/payment', $params); $this->responseFactory->create()->setRedirect($redirectOrder)->sendResponse(); - die(); + return $this->redirect->setRedirect($redirectOrder); } } } diff --git a/README.md b/README.md index 8dc78b8..55e30a1 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Accept cryptocurrency payments on your Magento store ### Requirements: ``` -Magento >= 2.3 +Magento >= 2.4 ``` ### Description @@ -134,5 +134,11 @@ The easiest and fastest way is via our live chat on our [website](https://crypta * Minor fixes * UI Improvements +#### 3.0.1 +* Minor fixes + +#### 3.0.2 +* Minor fixes + ### Upgrade Notice * No breaking changes. diff --git a/composer.json b/composer.json index 3b6b7b7..8e8cb17 100644 --- a/composer.json +++ b/composer.json @@ -2,14 +2,14 @@ "name": "cryptapi/cryptapi", "description": "CryptAPI's Magento extension", "type": "magento2-module", - "version": "3.0.0", + "version": "3.0.2", "require": { "magento/module-payment": "100.1.*", "magento/module-checkout": "100.1.*", "magento/module-sales": "100.1.*", + "ext-bcmath": "*", "ext-curl": "*", - "ext-json": "*", - "ext-bcmath": "*" + "ext-json": "*" }, "autoload": { "files": [ "registration.php" ], diff --git a/etc/config.xml b/etc/config.xml index efc60cf..e5b884c 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -13,11 +13,10 @@ 1 0 without_ammount - light + auto 300 3600 none - 0 1 diff --git a/etc/frontend/events.xml b/etc/frontend/events.xml index 4b46364..6fca4ee 100644 --- a/etc/frontend/events.xml +++ b/etc/frontend/events.xml @@ -1,7 +1,7 @@ - + diff --git a/etc/frontend/routes.xml b/etc/frontend/routes.xml index 42309ff..6fe6ccc 100644 --- a/etc/frontend/routes.xml +++ b/etc/frontend/routes.xml @@ -1,7 +1,8 @@ - + + - + - \ No newline at end of file + diff --git a/lib/CryptAPIHelper.php b/lib/CryptAPIHelper.php index ab069f8..30b10c7 100644 --- a/lib/CryptAPIHelper.php +++ b/lib/CryptAPIHelper.php @@ -241,8 +241,19 @@ public static function get_estimate($coin) return null; } - public static function my_decimal() { + public static function sig_fig($value, $digits) + { + if ($value == 0) { + $decimalPlaces = $digits - 1; + } elseif ($value < 0) { + $decimalPlaces = $digits - floor(log10($value * -1)) - 1; + } else { + $decimalPlaces = $digits - floor(log10($value)) - 1; + } + $answer = ($decimalPlaces > 0) ? + number_format($value, $decimalPlaces) : round($value, $decimalPlaces); + return $answer; } private static function _request($coin, $endpoint, $params = [], $assoc = false) diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml index 9bc4866..e9aa58f 100644 --- a/view/frontend/layout/checkout_index_index.xml +++ b/view/frontend/layout/checkout_index_index.xml @@ -1,8 +1,5 @@ - - - + -
-
-
-
- -
-
+ }); + +
+
+
+
+ +
+
+ + src="data:image/png;base64," alt=""/> + + src="data:image/png;base64," + alt=""/> + +
- src="data:image/png;base64," alt=""/> +
- + + + +
+ src="data:image/png;base64," - alt=""/> - -
+
+ } else { + ?> +
+
+ <?= __('QR Code without value') ?> +
- - - - -
- -
- -
-
- <?= __('QR Code without value') ?> -
-
- +
-
- -
-
-
- -
+
+
+ + - (" . $values['total'] . ""; ?>) -
- - - -
- ' . date('i:s', $conversion_timer) . '' - ); - ?> + + + + + (" . $values['total'] . ""; ?>) +
+ + -
+ if (intval($values['refresh_value_interval']) !== 0) { + ?> +
+ ' . date('i:s', $conversion_timer) . '' + ); + ?> +
+ +
- -
')">
+ +
')">
+
-
- - + + ' . date('H:i', $cancel_timer) . '', ); ?> - - - -