diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..045f0e7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,33 @@ +# The MIT License (MIT) +# +# Copyright (c) 2011-2014 BitPay, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# These files/directories are not included when making +# an archive of this repository. +/build export-ignore +/scripts export-ignore +/tests export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +build.xml export-ignore +composer.json export-ignore +modman export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5520859 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# The MIT License (MIT) +# +# Copyright (c) 2011-2014 BitPay +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +*.swp +lib/bitpay/bp_config.php +/bin/ +/build/cache/ +/build/dist/ +/build/docs/ +/build/logs/ +/build/magento/ +/vendor/ +composer.lock +composer.phar diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..48df50d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +# The MIT License (MIT) +# +# Copyright (c) 2011-2015 BitPay +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +language: php +php: + - 5.6 + - 5.5 + - 5.4 +install: + - composer install +#script: ./bin/phing -verbose -propertyfile build/travis.properties build-travis +script: phpunit -c build/ +cache: + directories: + - bin/ + - build/cache/ + - build/magento/ + - vendor/ +matrix: + fast_finish: true diff --git a/GUIDE.md b/GUIDE.md new file mode 100644 index 0000000..5fed503 --- /dev/null +++ b/GUIDE.md @@ -0,0 +1,66 @@ +# Using the BitPay plugin for Magento + +## Prerequisites +You must have a BitPay merchant account to use this plugin. It's free to [sign-up for a BitPay merchant account](https://bitpay.com/start). + + +## Server Requirements + +* Last Cart Version Tested: 2.0.5 +* [Magento CE](http://magento.com/resources/system-requirements) 2.0.0 or higher. Older versions might work, however this plugin has been validated to work against the 2.0.5 Community Edition release. +* [GMP](http://us2.php.net/gmp) or [BC Math](http://us2.php.net/manual/en/book.bc.php) PHP extensions. GMP is preferred for performance reasons but you may have to install this as most servers do not come with it installed by default. BC Math is commonly installed however and the plugin will fall back to this method if GMP is not found. +* [OpenSSL](http://us2.php.net/openssl) Must be compiled with PHP and is used for certain cryptographic operations. +* [PHP](http://us2.php.net/downloads.php) 5.4 or higher. This plugin will not work on PHP 5.3 and below. This plugin was tested with PHP 5.5.35 + + +## Installation + +**From the Magento Connect Manager:** + +Goto [http://www.magentocommerce.com/magento-connect/bitpay-payment-method.html](http://www.magentocommerce.com/magento-connect/bitpay-payment-method.html) and click the *Install Now* link which will give you the *Extension Key* needed for the next step. + +Once you have the key, log into you Magento Store's Admin Panel and navigate to **System > Magento Connect > Magento Connect Manager**. + +**NOTE:** It may ask you to log in again using the same credentials that you use to log into the Admin Panel. + +All you need to do is paste the extension key and click on the *Install* button. + +**WARNING:** It is good practice to backup your database before installing extensions. Please make sure you Create Backups. + + +**From the Releases Page:** + +Visit the [Releases](https://github.com/bitpay/magento2-plugin/releases) page of this repository and download the latest version. Once this is done, you can just unzip the contents and use any method you want to put them on your server. The contents will mirror the Magento directory structure. + +**NOTE:** These files can also up uploaded using the *Magento Connect Manager* that comes with your Magento Store + +**WARNING:** It is good practice to backup your database before installing extensions. Please make sure you Create Backups. + + +**Using Modman:** + +Using [modman](https://github.com/colinmollenhour/modman) you can install the BitPay Magento Plugin. Once you have modman installed, run `modman init` if you have not already done so. Next just run `modman clone https://github.com/bitpay/magento2-plugin.git` in the root of the Magento installation. In this case it is `/var/www/magento`. + + +## Configuration + +Configuration can be done using the Administrator section of your Magento store. Once Logged in, you will find the configuration settings under **Stores > Configuration > Sales > Payment Methods**. + +![BitPay Magento Settings](https://raw.githubusercontent.com/bitpay/magento2-plugin/master/docs/MagentoSettings.png "BitPay Magento Settings") + +Here your will need to create a [pairing code](https://bitpay.com/api-tokens) using your BitPay merchant account. Once you have a Pairing Code, put the code in the Pairing Code field. This will take care of the rest for you. + +**NOTE:** Pairing Codes are only valid for a short period of time. If it expires before you get to use it, you can always create a new one an use the new one. + +**NOTE:** You will only need to do this once since each time you do this, the extension will generate public and private keys that are used to identify you when using the API. + +You are also able to configure how BitPay's IPN (Instant Payment Notifications) changes the order in your Magento store. + +![BitPay Invoice Settings](https://raw.githubusercontent.com/bitpay/magento2-plugin/master/docs/MagentoInvoiceSettings.png "BitPay Invoice Settings") + + +## Usage + +Once enabled, your customers will be given the option to pay with Bitcoins. Once they checkout they are redirected to a full screen BitPay invoice to pay for the order. + +As a merchant, the orders in your Magento store can be treated as any other order. You may need to adjust the Invoice Settings depending on your order fulfillment. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..328b352 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011-2014 BitPay, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/app/code/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Extension.php b/app/code/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Extension.php new file mode 100755 index 0000000..747dd71 --- /dev/null +++ b/app/code/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Extension.php @@ -0,0 +1,39 @@ +get('Bitpay\Core\Helper\Data'); + $helper->debugData('[ERROR] In Bitpay_Core_Block_Adminhtml_System_Config_Form_Field_Extension::_getElementHtml(): Missing or invalid $element parameter passed to function.'); + throw new \Exception('In Bitpay_Core_Block_Adminhtml_System_Config_Form_Field_Extension::_getElementHtml(): Missing or invalid $element parameter passed to function.'); + } + + $config = $element->getFieldConfig(); + $phpExtension = isset($config['php_extension']) ? $config['php_extension'] : 'null'; + + if (true === in_array($phpExtension, get_loaded_extensions())) { + return 'Installed'; + } + + return 'Not Installed'; + } +} diff --git a/app/code/Bitpay/Core/Block/Form/Bitpay.php b/app/code/Bitpay/Core/Block/Form/Bitpay.php new file mode 100755 index 0000000..c45804e --- /dev/null +++ b/app/code/Bitpay/Core/Block/Form/Bitpay.php @@ -0,0 +1,44 @@ +setTemplate($template); + parent::__construct(); + } + + +} \ No newline at end of file diff --git a/app/code/Bitpay/Core/Block/Iframe.php b/app/code/Bitpay/Core/Block/Iframe.php new file mode 100755 index 0000000..d08cf12 --- /dev/null +++ b/app/code/Bitpay/Core/Block/Iframe.php @@ -0,0 +1,83 @@ +_bitpayModel = $_bitpayModel; + $this->_dataHelper = $_dataHelper; + parent::__construct($context,$data); + } + + + + /** + * + * + **/ + protected function getHelper() + { + $bitpayHelper = $this->_dataHelper; + return $bitpayHelper; + } + + /** + * create an invoice and return the url so that iframe.phtml can display it + * + * @return string + */ + public function getFrameActionUrl() + { + $last_success_quote_id = $this->getLastQuoteId(); + $invoiceFactory = $this->_bitpayModel; + $invoice = $invoiceFactory->load($last_success_quote_id,'quote_id'); + return $invoice->getData('url').'&view=iframe'; + } + + public function getLastQuoteId() + { + $objectmanager = \Magento\Framework\App\ObjectManager::getInstance(); + $quote = $objectmanager->get('\Magento\Checkout\Model\Session'); + return $quote->getData('last_success_quote_id'); + } + + public function getValidateUrl() + { + $validateUrl = $this->getUrl('bitpay/index/index'); + return $validateUrl; + } + + public function getSuccessUrl() + { + $successUrl = $this->getUrl('checkout/onepage/success'); + return $successUrl; + } +} diff --git a/app/code/Bitpay/Core/Block/Info.php b/app/code/Bitpay/Core/Block/Info.php new file mode 100755 index 0000000..2f11b54 --- /dev/null +++ b/app/code/Bitpay/Core/Block/Info.php @@ -0,0 +1,44 @@ +getInfo()->getOrder(); + $bitpayHelper = \Magento\Framework\App\ObjectManager::getInstance()->get('\Bitpay\Core\Helper\Data'); + $bitpayModelInvoice = \Magento\Framework\App\ObjectManager::getInstance()->get('\Bitpay\Core\Model\Invoice'); + + if (false === isset($order) || true === empty($order)) { + $bitpayHelper->debugData('[ERROR] In Bitpay_Core_Block_Info::getBitpayInvoiceUrl(): could not obtain the order.'); + throw new \Exception('In Bitpay_Core_Block_Info::getBitpayInvoiceUrl(): could not obtain the order.'); + } + + $incrementId = $order->getIncrementId(); + + if (false === isset($incrementId) || true === empty($incrementId)) { + $bitpayHelper->debugData('[ERROR] In Bitpay_Core_Block_Info::getBitpayInvoiceUrl(): could not obtain the incrementId.'); + throw new \Exception('In Bitpay_Core_Block_Info::getBitpayInvoiceUrl(): could not obtain the incrementId.'); + } + + $bitpayInvoice = $bitpayModelInvoice->load($incrementId, 'increment_id'); + + if (true === isset($bitpayInvoice) && false === empty($bitpayInvoice)) { + return $bitpayInvoice->getUrl(); + } + } + +} diff --git a/app/code/Bitpay/Core/Controller/Iframe/Index.php b/app/code/Bitpay/Core/Controller/Iframe/Index.php new file mode 100755 index 0000000..e880e31 --- /dev/null +++ b/app/code/Bitpay/Core/Controller/Iframe/Index.php @@ -0,0 +1,55 @@ +config = $config; + $this->_bitpayHelper = $_bitpayHelper; + $this->_bitpayModel = $_bitpayModel; + $this->cart = $cart; + $this->quoteFactory = $quoteFactory; + parent::__construct($context); + } + + + /** + * @route bitpay/iframe/index + */ + public function execute() + { + + if($this->config->getValue('payment/bitpay/fullscreen')){ + $html = 'You will be transfered to BitPay to complete your purchase when using this payment method.'; + }else{ + + $html = ''; + } + + $this->getResponse()->setBody(json_encode(array('html' => $html))); + } + +} diff --git a/app/code/Bitpay/Core/Controller/Index/Index.php b/app/code/Bitpay/Core/Controller/Index/Index.php new file mode 100755 index 0000000..8b4cb20 --- /dev/null +++ b/app/code/Bitpay/Core/Controller/Index/Index.php @@ -0,0 +1,45 @@ +_bitpayHelper = $_bitpayHelper; + $this->_bitpayModel = $_bitpayModel; + } + + + /** + * @route bitpay/index/index?quote=n + */ + public function execute() + { + $params = $this->getRequest()->getParams(); + $quoteId = $params['quote']; + $this->_bitpayHelper->registerAutoloader(); + $this->_bitpayHelper->debugData(json_encode($params)); + $paid = $this->_bitpayModel->GetQuotePaid($quoteId); + + $this->_view->loadLayout(); + + $this->getResponse()->setHeader('Content-type', 'application/json'); + + $this->getResponse()->setBody(json_encode(array('paid' => $paid))); + } +} diff --git a/app/code/Bitpay/Core/Controller/Invoice/Index.php b/app/code/Bitpay/Core/Controller/Invoice/Index.php new file mode 100755 index 0000000..d8a2cb9 --- /dev/null +++ b/app/code/Bitpay/Core/Controller/Invoice/Index.php @@ -0,0 +1,70 @@ +_coreRegistry = $coreRegistry; + $this->_bitpayHelper = $_bitpayHelper; + $this->_bitpayModel = $_bitpayModel; + $this->config = $config; + $this->resultPageFactory = $resultPageFactory; + parent::__construct($context); + } + + + /** + * @route bitpay invoice url + */ + public function execute() + { + $objectmanager = \Magento\Framework\App\ObjectManager::getInstance(); + $quote = $objectmanager->get('\Magento\Checkout\Model\Session'); + if(!$quote->getData('last_success_quote_id')){ + return $this->_redirect('checkout/cart'); + } + if($this->config->getValue('payment/bitpay/fullscreen')){ + $invoiceFactory = $this->_bitpayModel; + $invoice = $invoiceFactory->load($quote->getData('last_success_quote_id'),'quote_id'); + $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + $resultRedirect->setUrl($invoice->getData('url')); + return $resultRedirect; + }else{ + $resultPage = $this->resultPageFactory->create(); + $resultPage->getConfig()->getTitle()->set(__('Pay with BitCoin')); + return $resultPage; + } + } +} diff --git a/app/code/Bitpay/Core/Controller/Ipn/Index.php b/app/code/Bitpay/Core/Controller/Ipn/Index.php new file mode 100755 index 0000000..b441f36 --- /dev/null +++ b/app/code/Bitpay/Core/Controller/Ipn/Index.php @@ -0,0 +1,195 @@ +_bitpayHelper = $_bitpayHelper; + $this->_bitpayModel = $_bitpayModel; + $this->_orderModel = $_orderModel; + $this->_bitpayInvoiceModel = $_bitpayInvoiceModel; + } + + /** + * bitpay's IPN lands here + * + * @route /bitpay/ipn + * @route /bitpay/ipn/index + */ + public function execute() + { + + if (false === ini_get('allow_url_fopen')) { + ini_set('allow_url_fopen', true); + } + + $raw_post_data = file_get_contents('php://input'); + + if (false === $raw_post_data) { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), Could not read from the php://input stream or invalid Bitpay IPN received.'); + throw new \Exception('Could not read from the php://input stream or invalid Bitpay IPN received.'); + } + + $this->_bitpayHelper->registerAutoloader(); + + $this->_bitpayHelper->debugData(sprintf('[INFO] In \Bitpay\Core\Controller\Ipn::indexAction(), Incoming IPN message from BitPay: ').' '.json_encode($raw_post_data)); + + // Magento doesn't seem to have a way to get the Request body + $ipn = json_decode($raw_post_data); + + if (true === empty($ipn)) { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), Could not decode the JSON payload from BitPay.'); + throw new \Exception('Could not decode the JSON payload from BitPay.'); + } + + if (true === empty($ipn->id) || false === isset($ipn->posData)) { + $this->_bitpayHelper->debugData(sprintf('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), Did not receive order ID in IPN: ', $ipn)); + throw new \Exception('Invalid Bitpay payment notification message received - did not receive order ID.'); + } + + $ipn->posData = is_string($ipn->posData) ? json_decode($ipn->posData) : $ipn->posData; + $ipn->buyerFields = isset($ipn->buyerFields) ? $ipn->buyerFields : new stdClass(); + + $this->_bitpayHelper->debugData(json_encode($ipn)); + + $invoice_id = isset($ipn->id) ? $ipn->id : ''; + $url = isset($ipn->url) ? $ipn->url : ''; + $pos_data = json_encode($ipn->posData); + $status = isset($ipn->status) ? $ipn->status : ''; + $btc_price = isset($ipn->btcPrice) ? $ipn->btcPrice : ''; + $price = isset($ipn->price) ? $ipn->price : ''; + $currency = isset($ipn->currency) ? $ipn->currency : ''; + $invoice_time = isset($ipn->invoiceTime) ? intval($ipn->invoiceTime / 1000) : ''; + $expiration_time = isset($ipn->expirationTime) ? intval($ipn->expirationTime / 1000) : ''; + $current_time = isset($ipn->currentTime) ? intval($ipn->currentTime / 1000) : ''; + $btc_paid = isset($ipn->btcPaid) ? $ipn->btcPaid : ''; + $rate = isset($ipn->rate) ? $ipn->rate : ''; + $exception_status = isset($ipn->exceptionStatus) ? $ipn->exceptionStatus : ''; + + $resources = \Magento\Framework\App\ObjectManager::getInstance()->get('Magento\Framework\App\ResourceConnection'); + $connection= $resources->getConnection(); + $ipnTable = $resources->getTableName('bitpay_ipns'); + + $sql = "Insert into " . $ipnTable . "(invoice_id,url,pos_data,status,btc_price,price,currency,invoice_time,expiration_time,btc_paid,rate,exception_status) Values ('" . $invoice_id . "','" . $url . "','" . $pos_data . "','" . $status . "','" . $btc_price . "','" . $price . "','" . $currency . "','" . $invoice_time . "','" . $expiration_time . "','".$btc_paid."','" . $rate . "','" . $exception_status . "')"; + $connection->query($sql); + + + // Order isn't being created for iframe... + if (isset($ipn->posData->orderId)) { + $order = $this->_orderModel->loadByIncrementId($ipn->posData->orderId); + } else { + $order = $this->_orderModel->load($ipn->posData->quoteId, 'quote_id'); + } + + if (false === isset($order) || true === empty($order)) { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), Invalid Bitpay IPN received.'); + $this->throwException('Invalid Bitpay IPN received.'); + } + + $orderId = $order->getId(); + if (false === isset($orderId) || true === empty($orderId)) { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), Invalid Bitpay IPN received.'); + $this->throwException('Invalid Bitpay IPN received.'); + } + + /** + * Ask BitPay to retreive the invoice so we can make sure the invoices + * match up and no one is using an automated tool to post IPN's to merchants + * store. + */ + $invoice = \Magento\Framework\App\ObjectManager::getInstance()->get('Bitpay\Core\Model\Method\Bitcoin')->fetchInvoice($ipn->id); + + if (false === isset($invoice) || true === empty($invoice)) { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), Could not retrieve the invoice details for the ipn ID of ' . $ipn->id); + $this->throwException('Could not retrieve the invoice details for the ipn ID of ' . $ipn->id); + } + + // Does the status match? + if ($invoice->getStatus() != $ipn->status) { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), IPN status and status from BitPay are different. Rejecting this IPN!'); + $this->throwException('There was an error processing the IPN - statuses are different. Rejecting this IPN!'); + } + + // Does the price match? + if ($invoice->getPrice() != $ipn->price) { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), IPN price and invoice price are different. Rejecting this IPN!'); + $this->throwException('There was an error processing the IPN - invoice price does not match the IPN price. Rejecting this IPN!'); + } + + // Update the order to notifiy that it has been paid + $transactionSpeed = \Magento\Framework\App\ObjectManager::getInstance()->create('Magento\Framework\App\Config\ScopeConfigInterface')->getValue('payment/bitpay/speed'); + if ($invoice->getStatus() === 'paid' + || ($invoice->getStatus() === 'confirmed' && $transactionSpeed === 'high')) { + + $payment = \Magento\Framework\App\ObjectManager::getInstance()->create('Magento\Sales\Model\Order\Payment')->setOrder($order); + + if (true === isset($payment) && false === empty($payment)) { + $payment->registerCaptureNotification($invoice->getPrice()); + $order->addPayment($payment); + + // If the customer has not already been notified by email + // send the notification now that there's a new order. + if (!$order->getEmailSent()) { + $this->_bitpayHelper->debugData('[INFO] In \Bitpay\Core\Controller\Ipn::indexAction(), Order email not sent so I am calling $order->sendNewOrderEmail() now...'); + $order->sendNewOrderEmail(); + } + + $order->save(); + + } else { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), Could not create a payment object in the Bitpay IPN controller.'); + $this->throwException('Could not create a payment object in the Bitpay IPN controller.'); + } + } + + // use state as defined by Merchant + $state = \Magento\Framework\App\ObjectManager::getInstance()->create('Magento\Framework\App\Config\ScopeConfigInterface')->getValue(sprintf('payment/bitpay/invoice_%s', $invoice->getStatus())); + + if (false === isset($state) || true === empty($state)) { + $this->_bitpayHelper->debugData('[ERROR] In \Bitpay\Core\Controller\Ipn::indexAction(), Could not retrieve the defined state parameter to update this order to in the Bitpay IPN controller.'); + $this->throwException('Could not retrieve the defined state parameter to update this order in the Bitpay IPN controller.'); + } + + // Check if status should be updated + switch ($order->getStatus()) { + case \Magento\Sales\Model\Order::STATE_CANCELED: + case \Magento\Sales\Model\Order::STATUS_FRAUD: + case \Magento\Sales\Model\Order::STATE_CLOSED: + case \Magento\Sales\Model\Order::STATE_COMPLETE: + case \Magento\Sales\Model\Order::STATE_HOLDED: + // Do not Update + break; + case \Magento\Sales\Model\Order::STATE_PENDING_PAYMENT: + case \Magento\Sales\Model\Order::STATE_PROCESSING: + default: + $order->addStatusToHistory( + $state, + sprintf('[INFO] In \Bitpay\Core\Controller\Ipn::indexAction(), Incoming IPN status "%s" updated order state to "%s"', $invoice->getStatus(), $state) + )->save(); + break; + } + } +} diff --git a/app/code/Bitpay/Core/Helper/Data.php b/app/code/Bitpay/Core/Helper/Data.php new file mode 100755 index 0000000..d25cceb --- /dev/null +++ b/app/code/Bitpay/Core/Helper/Data.php @@ -0,0 +1,419 @@ +_bitpayModel = $bitpayModel; + $this->directory_list = $directory_list; + $this->logger = $logger; + $this->config = $config; + parent::__construct($context); + } + + /** + * @param mixed $debugData + */ + public function debugData($debugData) + { + if (true === isset($debugData) && false === empty($debugData)) { + $this->logger->debug($debugData); + } + } + + /** + * @return boolean + */ + public function isDebug() + { + return (boolean) $this->scopeConfig->getValue('payment/bitpay/debug'); + } + + /** + * Returns true if Transaction Speed has been configured + * + * @return boolean + */ + public function hasTransactionSpeed() + { + $speed = $this->scopeConfig->getValue('payment/bitpay/speed'); + + return !empty($speed); + } + + /** + * Returns the URL where the IPN's are sent + * + * @return string + */ + public function getNotificationUrl() + { + return $this->_storeManager->getStore()->getUrl($this->scopeConfig->getValue('payment/bitpay/notification_url')); + } + + /** + * Returns the URL where customers are redirected + * + * @return string + */ + public function getRedirectUrl() + { + return $this->_storeManager->getStore()->getUrl($this->scopeConfig->getValue('payment/bitpay/redirect_url')); + } + + /** + * Registers the BitPay autoloader to run before Magento's. This MUST be + * called before using any bitpay classes. + */ + public function registerAutoloader() + { + if (true === empty($this->_autoloaderRegistered)) { + $base = $this->directory_list->getPath('lib_internal'); + $autoloader_filename = $base.'/Bitpay/Autoloader.php'; + if (true === is_file($autoloader_filename) && true === is_readable($autoloader_filename)) { + require_once $autoloader_filename; + \Bitpay\Autoloader::register(); + $this->_autoloaderRegistered = true; + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::registerAutoloader(): autoloader file was found and has been registered.'); + } else { + $this->_autoloaderRegistered = false; + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::registerAutoloader(): autoloader file was not found or is not readable. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::registerAutoloader(): autoloader file was not found or is not readable. Cannot continue!'); + } + } + } + + /** + * This function will generate keys that will need to be paired with BitPay + * using + */ + public function generateAndSaveKeys() + { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::generateAndSaveKeys(): attempting to generate new keypair and save to database.'); + + if (true === empty($this->_autoloaderRegistered)) { + $this->registerAutoloader(); + } + + $this->_privateKey = new \Bitpay\PrivateKey('payment/bitpay/private_key'); + + if (false === isset($this->_privateKey) || true === empty($this->_privateKey)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::generateAndSaveKeys(): could not create new Bitpay private key object. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::generateAndSaveKeys(): could not create new Bitpay private key object. Cannot continue!'); + } else { + $this->_privateKey->generate(); + } + + $this->_publicKey = new \Bitpay\PublicKey('payment/bitpay/public_key'); + + if (false === isset($this->_publicKey) || true === empty($this->_publicKey)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::generateAndSaveKeys(): could not create new Bitpay public key object. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::generateAndSaveKeys(): could not create new Bitpay public key object. Cannot continue!'); + } else { + $this->_publicKey + ->setPrivateKey($this->_privateKey) + ->generate(); + } + + $this->getKeyManager()->persist($this->_publicKey); + $this->getKeyManager()->persist($this->_privateKey); + + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::generateAndSaveKeys(): key manager called to persist keypair to database.'); + } + + /** + * Send a pairing request to BitPay to receive a Token + */ + public function sendPairingRequest($pairingCode) + { + if (false === isset($pairingCode) || true === empty($pairingCode)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::sendPairingRequest(): missing or invalid pairingCode parameter.'); + throw new \Exception('In \Bitpay\Core\Helper\Data::sendPairingRequest(): missing or invalid pairingCode parameter.'); + } else { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::sendPairingRequest(): function called with the pairingCode parameter: ' . $pairingCode); + } + + $configFactory = \Magento\Framework\App\ObjectManager::getInstance()->get('\Magento\Config\Model\Config\Factory'); + $configData = [ + 'section' => 'payment', + 'website' => null, + 'store' => null, + 'groups' => [ + 'bitpay' => [ + 'fields' => [ + 'public_key' => [ + 'value' => null, + ], + 'private_key' => [ + 'value' => null, + ], + 'token' => [ + 'value' => null, + ], + ], + ], + ], + ]; + $configModel = $configFactory->create(['data' => $configData]); + $configModel->save(); + + if (true === empty($this->_autoloaderRegistered)) { + $this->registerAutoloader(); + } + + // Generate/Regenerate keys + $this->generateAndSaveKeys(); + $sin = $this->getSinKey(); + + if (false === isset($sin) || true === empty($sin)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::sendPairingRequest(): could not retrieve the SIN parameter. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::sendPairingRequest(): could not retrieve the SIN parameter. Cannot continue!'); + } else { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::sendPairingRequest(): attempting to pair with the SIN parameter: ' . $sin); + } + + // Sanitize label + $store = \Magento\Framework\App\ObjectManager::getInstance()->get('\Magento\Store\Model\StoreManagerInterface'); + $label = preg_replace('/[^a-zA-Z0-9 ]/', '', $store->getStore()->getName()); + $label = substr('Magento ' . $label, 0, 59); + + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::sendPairingRequest(): using the label "' . $label . '".'); + + $token = $this->getBitpayClient()->createToken( + array( + 'id' => (string) $sin, + 'pairingCode' => (string) $pairingCode, + 'label' => (string) $label, + ) + ); + + if (false === isset($token) || true === empty($token)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::sendPairingRequest(): could not obtain the token from the pairing process. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::sendPairingRequest(): could not obtain the token from the pairing process. Cannot continue!'); + } else { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::sendPairingRequest(): token successfully obtained.'); + } + + $config = $this->config; + + if (false === isset($config) || true === empty($config)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::sendPairingRequest(): could not create new Mage_Core_Model_Config object. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::sendPairingRequest(): could not create new Mage_Core_Model_Config object. Cannot continue!'); + } + + if($config->saveConfig('payment/bitpay/token', $token->getToken(), $store->getStore()->getCode(), 0)) { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::sendPairingRequest(): token saved to database.'); + } else { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::sendPairingRequest(): token could not be saved to database.'); + throw new \Exception('In \Bitpay\Core\Helper\Data::sendPairingRequest(): token could not be saved to database.'); + } + } + + /** + * @return \Bitpay\SinKey + */ + public function getSinKey() + { + if (false === empty($this->_sin)) { + return $this->_sin; + } + + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getSinKey(): attempting to get the SIN parameter.'); + + if (true === empty($this->_autoloaderRegistered)) { + $this->registerAutoloader(); + } + + $this->_sin = new \Bitpay\SinKey(); + + if (false === isset($this->_sin) || true === empty($this->_sin)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::getSinKey(): could not create new BitPay SinKey object. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::getSinKey(): could not create new BitPay SinKey object. Cannot continue!'); + } + + $this->_sin + ->setPublicKey($this->getPublicKey()) + ->generate(); + + if (false === isset($this->_sin) || true === empty($this->_sin)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::getSinKey(): could not generate a new SIN from the public key. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::getSinKey(): could not generate a new SIN from the public key. Cannot continue!'); + } + + return $this->_sin; + } + + public function getPublicKey() + { + if (true === isset($this->_publicKey) && false === empty($this->_publicKey)) { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPublicKey(): found an existing public key, returning that.'); + return $this->_publicKey; + } + + if (true === empty($this->_autoloaderRegistered)) { + $this->registerAutoloader(); + } + + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPublicKey(): did not find an existing public key, attempting to load one from the key manager.'); + + $this->_publicKey = $this->getKeyManager()->load('payment/bitpay/public_key'); + + if (true === empty($this->_publicKey)) { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPublicKey(): could not load a public key from the key manager, generating a new one.'); + $this->generateAndSaveKeys(); + } else { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPublicKey(): successfully loaded public key from the key manager, returning that.'); + return $this->_publicKey; + } + + if (false === empty($this->_publicKey)) { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPublicKey(): successfully generated a new public key.'); + return $this->_publicKey; + } else { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::getPublicKey(): could not load or generate a new public key. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::getPublicKey(): could not load or generate a new public key. Cannot continue!'); + } + } + + public function getPrivateKey() + { + if (false === empty($this->_privateKey)) { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPrivateKey(): found an existing private key, returning that.'); + return $this->_privateKey; + } + + if (true === empty($this->_autoloaderRegistered)) { + $this->registerAutoloader(); + } + + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPrivateKey(): did not find an existing private key, attempting to load one from the key manager.'); + + $this->_privateKey = $this->getKeyManager()->load('payment/bitpay/private_key'); + + if (true === empty($this->_privateKey)) { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPrivateKey(): could not load a private key from the key manager, generating a new one.'); + $this->generateAndSaveKeys(); + } else { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPrivateKey(): successfully loaded private key from the key manager, returning that.'); + return $this->_privateKey; + } + + if (false === empty($this->_privateKey)) { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getPrivateKey(): successfully generated a new private key.'); + return $this->_privateKey; + } else { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::getPrivateKey(): could not load or generate a new private key. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::getPrivateKey(): could not load or generate a new private key. Cannot continue!'); + } + } + + /** + * @return \Bitpay\KeyManager + */ + public function getKeyManager() + { + if (true === empty($this->_keyManager)) { + if (true === empty($this->_autoloaderRegistered)) { + $this->registerAutoloader(); + } + + $this->_keyManager = new \Bitpay\KeyManager(new \Bitpay\Storage\MagentoStorage()); + + if (false === isset($this->_keyManager) || true === empty($this->_keyManager)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::getKeyManager(): could not create new BitPay KeyManager object. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::getKeyManager(): could not create new BitPay KeyManager object. Cannot continue!'); + } else { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getKeyManager(): successfully created new BitPay KeyManager object.'); + } + } + + return $this->_keyManager; + } + + /** + * @return \Bitpay\Client + */ + public function getBitpayClient() + { + if (false === empty($this->_client)) { + return $this->_client; + } + + if (true === empty($this->_autoloaderRegistered)) { + $this->registerAutoloader(); + } + + $this->_client = new \Bitpay\Client\Client(); + + if (false === isset($this->_client) || true === empty($this->_client)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::getBitpayClient(): could not create new BitPay Client object. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::getBitpayClient(): could not create new BitPay Client object. Cannot continue!'); + } else { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getBitpayClient(): successfully created new BitPay Client object.'); + } + + if($this->scopeConfig->getValue('payment/bitpay/network') === 'livenet') { + $network = new \Bitpay\Network\Livenet(); + } else { + $network = new \Bitpay\Network\Testnet(); + } + $adapter = new \Bitpay\Client\Adapter\CurlAdapter(); + + $this->_client->setPublicKey($this->getPublicKey()); + $this->_client->setPrivateKey($this->getPrivateKey()); + $this->_client->setNetwork($network); + $this->_client->setAdapter($adapter); + $this->_client->setToken($this->getToken()); + + return $this->_client; + } + + public function getToken() + { + if (true === empty($this->_autoloaderRegistered)) { + $this->registerAutoloader(); + } + + $token = new \Bitpay\Token(); + + if (false === isset($token) || true === empty($token)) { + $this->debugData('[ERROR] In \Bitpay\Core\Helper\Data::getToken(): could not create new BitPay Token object. Cannot continue!'); + throw new \Exception('In \Bitpay\Core\Helper\Data::getToken(): could not create new BitPay Token object. Cannot continue!'); + } else { + $this->debugData('[INFO] In \Bitpay\Core\Helper\Data::getToken(): successfully created new BitPay Token object.'); + } + + $token->setToken($this->scopeConfig->getValue('payment/bitpay/token')); + + return $token; + } + +} diff --git a/app/code/Bitpay/Core/Logger/Handler.php b/app/code/Bitpay/Core/Logger/Handler.php new file mode 100755 index 0000000..21e5309 --- /dev/null +++ b/app/code/Bitpay/Core/Logger/Handler.php @@ -0,0 +1,23 @@ +_scopeConfig = $scopeConfig; + $this->_dataStorage = $dataStorage; + $this->_paymentMethodFactory = $paymentMethodFactory; + $this->localeResolver = $localeResolver; + $this->_date = $date; + } + + + /** + * Retrieve active system payments + * + * @return array + * @api + */ + public function getActiveMethods() + { + $methods = []; + if (isset($data['active'], $data['model']) && (bool)$data['active']) { + /** @var MethodInterface $methodModel Actually it's wrong interface */ + $methodModel = $this->_paymentMethodFactory->create($data['model']); + $methodModel->setId($code); + $methodModel->setStore(null); + if ($methodModel->getConfigData('active', null)) { + $methods[$code] = $methodModel; + } + } + return $methods; + } + + /** + * Retrieve array of payment methods information + * + * @return array + * @api + */ + public function getMethodsInfo() + { + return $this->_dataStorage->get('methods'); + } + + + +} \ No newline at end of file diff --git a/app/code/Bitpay/Core/Model/Config/Backend/PairingCode.php b/app/code/Bitpay/Core/Model/Config/Backend/PairingCode.php new file mode 100755 index 0000000..eb036e2 --- /dev/null +++ b/app/code/Bitpay/Core/Model/Config/Backend/PairingCode.php @@ -0,0 +1,64 @@ +messageManager = $messageManager; + $this->helper = $_helper; + } + + + /** + * @inheritdoc + */ + public function save() + { + /** + * If the user has put a paring code into the text field, we want to + * pair the magento store to the stores keys. If the merchant is just + * updating a configuration setting, we could care less about the + * pairing code. + */ + $pairingCode = trim($this->getValue()); + + if (true === empty($pairingCode)) { + return; + } + + $this->helper->debugData('[INFO] In Bitpay_Core_Model_Config_PairingCode::save(): attempting to pair with BitPay with pairing code ' . $pairingCode); + try { + $this->helper->sendPairingRequest($pairingCode); + } catch (\Exception $e) { + $this->helper->debugData(sprintf('[ERROR] Exception thrown while calling the sendPairingRequest() function. The specific error message is: "%s"', $e->getMessage())); + $this->messageManager->addError('There was an error while trying to pair with BitPay using the pairing code '.$pairingCode.'. Please try again or enable debug mode and send the "payment_bitpay.log" file to support@bitpay.com for more help.'); + + return; + } + $this->messageManager->addSuccess('Pairing with BitPay was successful.'); + } +} diff --git a/app/code/Bitpay/Core/Model/Config/Source/Network.php b/app/code/Bitpay/Core/Model/Config/Source/Network.php new file mode 100755 index 0000000..f065bb9 --- /dev/null +++ b/app/code/Bitpay/Core/Model/Config/Source/Network.php @@ -0,0 +1,34 @@ + self::NETWORK_LIVENET, + 'label' => ucwords(self::NETWORK_LIVENET), + ], + [ + 'value' => self::NETWORK_TESTNET, + 'label' => ucwords(self::NETWORK_TESTNET) + ] + ]; + } +} diff --git a/app/code/Bitpay/Core/Model/Config/Source/TransactionSpeed.php b/app/code/Bitpay/Core/Model/Config/Source/TransactionSpeed.php new file mode 100755 index 0000000..0bf6857 --- /dev/null +++ b/app/code/Bitpay/Core/Model/Config/Source/TransactionSpeed.php @@ -0,0 +1,40 @@ + self::SPEED_LOW, + 'label' => ucwords(self::SPEED_LOW), + ], + [ + 'value' => self::SPEED_MEDIUM, + 'label' => ucwords(self::SPEED_MEDIUM) + ], + [ + 'value' => self::SPEED_HIGH, + 'label' => ucwords(self::SPEED_HIGH) + ] + ]; + } +} diff --git a/app/code/Bitpay/Core/Model/ConfigProvider.php b/app/code/Bitpay/Core/Model/ConfigProvider.php new file mode 100755 index 0000000..caedb1b --- /dev/null +++ b/app/code/Bitpay/Core/Model/ConfigProvider.php @@ -0,0 +1,108 @@ +config = $config; + $this->currentCustomer = $currentCustomer; + $this->bitpayHelper = $bitpayHelper; + $this->paymentHelper = $paymentHelper; + $this->urlBuilder = $urlBuilder; + $this->methodCode = $methodCode; + $this->method = $this->paymentHelper->getMethodInstance($methodCode); + } + + + public function getConfig() + { + /* $config = [ + 'payment' => [ + $this->methodCode => [ + 'fullScreen' => $this->config->getFullscreen(),//$this->getMethodRedirectUrl($this->methodCode)], + ], + ], + ]; + // $config['payment']['redirectUrl'][$code] = $this->getMethodRedirectUrl($code); + // $config['payment']['fullScreen'][$code] = $this->config->getValue('fullscreen');*/ + $config = []; + return $config; + } + + /** + * Return redirect URL for method + * + * @param string $code + * @return mixed + */ + protected function getMethodRedirectUrl($code) + { + return $this->method->getOrderPlaceRedirectUrl(); + } + +} \ No newline at end of file diff --git a/app/code/Bitpay/Core/Model/Invoice.php b/app/code/Bitpay/Core/Model/Invoice.php new file mode 100755 index 0000000..cc4ffff --- /dev/null +++ b/app/code/Bitpay/Core/Model/Invoice.php @@ -0,0 +1,117 @@ +_init('Bitpay\Core\Model\ResourceModel\Invoice'); + } + + /** + * Adds data to model based on an Invoice that has been retrieved from + * BitPay's API + * + * @param Bitpay\Invoice $invoice + * @return \Bitpay\Core\Model\Invoice + */ + public function prepareWithBitpayInvoice($invoice,$order) + { + $objectmanager = \Magento\Framework\App\ObjectManager::getInstance(); + $helper = $objectmanager->get('\Bitpay\Core\Helper\Data'); + if (false === isset($invoice) || true === empty($invoice)) { + $helper->debugData('[ERROR] In Bitpay_Core_Model_Invoice::prepareWithBitpayInvoice(): Missing or empty $invoice parameter.'); + throw new \Exception('In Bitpay_Core_Model_Invoice::prepareWithBitpayInvoice(): Missing or empty $invoice parameter.'); + } + $objectmanager->create('\Bitpay\Core\Model\Invoice')->setData( + array( + 'id' => $invoice->getId(), + 'url' => $invoice->getUrl(), + 'pos_data' => $invoice->getPosData(), + 'status' => $invoice->getStatus(), + 'btc_price' => $invoice->getBtcPrice(), + 'price' => $invoice->getPrice(), + 'currency' => $invoice->getCurrency()->getCode(), + 'order_id' => $invoice->getOrderId(), + 'invoice_time' => intval($invoice->getInvoiceTime() / 1000), + 'expiration_time' => intval($invoice->getExpirationTime() / 1000), + 'current_time' => intval($invoice->getCurrentTime() / 1000), + 'btc_paid' => $invoice->getBtcPaid(), + 'rate' => $invoice->getRate(), + 'exception_status' => $invoice->getExceptionStatus(), + ) + ); + + if (false === isset($order) || true === empty($order)) { + $helper->debugData('[ERROR] In Bitpay_Core_Model_Invoice::prepateWithOrder(): Missing or empty $order parameter.'); + throw new \Exception('In Bitpay_Core_Model_Invoice::prepateWithOrder(): Missing or empty $order parameter.'); + } + + $this->setData( + array( + 'quote_id' => $order['quote_id'], + 'increment_id' => $order['increment_id'], + ) + ); + return $this; + } + + /** + * Adds information to based on the order object inside magento + * + * @param Mage_Sales_Model_Order $order + * @return Bitpay_Core_Model_Invoice + */ + public function prepareWithOrder($order) + { + $objectmanager = \Magento\Framework\App\ObjectManager::getInstance(); + $helper = $objectmanager->get('\Bitpay\Core\Helper\Data'); + if (false === isset($order) || true === empty($order)) { + $helper->debugData('[ERROR] In Bitpay_Core_Model_Invoice::prepateWithOrder(): Missing or empty $order parameter.'); + throw new \Exception('In Bitpay_Core_Model_Invoice::prepateWithOrder(): Missing or empty $order parameter.'); + } + + $this->setData( + array( + 'quote_id' => $order['quote_id'], + 'increment_id' => $order['increment_id'], + ) + ); + + return $this; + } + + +} \ No newline at end of file diff --git a/app/code/Bitpay/Core/Model/Ipn.php b/app/code/Bitpay/Core/Model/Ipn.php new file mode 100755 index 0000000..cc8dbc0 --- /dev/null +++ b/app/code/Bitpay/Core/Model/Ipn.php @@ -0,0 +1,93 @@ +_ipnCollectionFactory = $_ipnCollectionFactory; + parent::__construct($context, $registry, $resource, $resourceCollection, $data); + } + + /** + * @return void + */ + public function _construct() + { + parent::_construct(); + $this->_init('Bitpay\Core\Model\ResourceModel\Ipn'); + } + + /** + * @param string $quoteId + * @param array $statuses + * + * @return boolean + */ + function GetStatusReceived($quoteId, $statuses) + { + $objectmanager = \Magento\Framework\App\ObjectManager::getInstance(); + if (!$quoteId) + { + return false; + } + $quote_model = $objectmanager->get('\Magento\Quote\Model\Quote'); + $quote = $quote_model->load($quoteId, 'entity_id'); + if (!$quote) + { + $objectmanager->get('\Bitpay\Core\Helper\Data')->debugData('[ERROR] quote not found.'); + return false; + } + + $collection = $this->_ipnCollectionFactory->load();//$objectmanager->create('\Bitpay\Core\Model\Ipn')->getCollection(); + + foreach ($collection as $i) + { + $postData = json_decode($i->getData('pos_data'), true); + if (isset($postData['quoteId']) && $quoteId == $postData['quoteId']) { + if (in_array($i->getData('status'), $statuses)) { + return true; + } + } + } + return false; + } + + /** + * @param string $quoteId + * + * @return boolean + */ + function GetQuotePaid($quoteId) + { + return $this->GetStatusReceived($quoteId, array('paid', 'confirmed', 'complete')); + } + +} \ No newline at end of file diff --git a/app/code/Bitpay/Core/Model/Method/Bitcoin.php b/app/code/Bitpay/Core/Model/Method/Bitcoin.php new file mode 100755 index 0000000..83ac5f9 --- /dev/null +++ b/app/code/Bitpay/Core/Model/Method/Bitcoin.php @@ -0,0 +1,607 @@ +orderFactory = $orderFactory; + $this->_storeManager = $storeManager; + $this->quoteRepository = $quoteRepository; + $this->orderSender = $orderSender; + $this->transactionRepository = $transactionRepository; + $this->priceCurrency = $priceCurrency; + $this->quoteManagement = $quoteManagement; + $this->invoiceFactory = $invoiceFactory; + $this->cart = $cart; + $this->quoteFactory = $quoteFactory; + + parent::__construct( + $context, + $registry, + $extensionFactory, + $customAttributeFactory, + $paymentData, + $_scopeConfig, + $logger, + $resource, + $resourceCollection, + $data + ); + + } + + + /** + * + * + **/ + protected function getHelper() + { + $bitpayHelper = \Magento\Framework\App\ObjectManager::getInstance()->get('\Bitpay\Core\Helper\Data'); + return $bitpayHelper; + } + + + public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null) + { + return parent::isAvailable($quote); + } + + /** + * @param Mage_Sales_Model_Order_Payment $payment + * @param float $amount + * @return Bitpay_Core_Model_PaymentMethod + */ + public function authorize(\Magento\Payment\Model\InfoInterface $payment, $amount,$iframe = false) + { + + // Check if coming from iframe or submit button + if ((!$this->_scopeConfig->getValue('payment/bitpay/fullscreen') && $iframe === false) + || ($this->_scopeConfig->getValue('payment/bitpay/fullscreen') && $iframe === true)) { + $quoteId = $payment->getOrder()->getQuoteId(); + $ipn = \Magento\Framework\App\ObjectManager::getInstance()->get('\Bitpay\Core\Model\Ipn'); + + if (!$ipn->GetQuotePaid($quoteId)) + { + $this->getHelper()->debugData('[ERROR] Order not paid for. Please pay first and then Place Your Order.'); + // This is the error that is displayed to the customer during checkout. + throw new \Magento\Framework\Exception\CouldNotSaveException("Order not paid for. Please pay first and then Place your Order."); + + } + + return $this; + } + + if (false === isset($payment) || false === isset($amount) || true === empty($payment) || true === empty($amount)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::authorize(): missing payment or amount parameters.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::authorize(): missing payment or amount parameters.'); + } + + $this->getHelper()->debugData('[INFO] \Bitpay\Core\Model\Method\Bitcoin::authorize(): authorizing new order.'); + + // Create BitPay Invoice + $invoice = $this->initializeInvoice(); + + if (false === isset($invoice) || true === empty($invoice)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::authorize(): could not initialize invoice.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::authorize(): could not initialize invoice.'); + } + + $objectmanager = \Magento\Framework\App\ObjectManager::getInstance(); + + $quote = $objectmanager->get('\Magento\Checkout\Model\Session')->getQuote(); + + $invoice = $this->prepareInvoice($invoice, $payment, $amount); + + try { + $bitpayInvoice = $this->getHelper()->getBitpayClient()->createInvoice($invoice); + } catch (\Exception $e) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::authorize(): ' . $e->getMessage()); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::authorize(): Could not authorize transaction.'); + } + + self::$_redirectUrl = ($this->_scopeConfig->getValue('payment/bitpay/fullscreen')) ? $bitpayInvoice->getUrl(): $bitpayInvoice->getUrl().'&view=iframe'; + + + $this->getHelper()->debugData('[INFO] BitPay Invoice created. Invoice URL: '.$bitpayInvoice->getUrl()); + + + $order = $payment->getOrder(); + + // $mirrorInvoice = $this->invoiceFactory->create()->prepareWithBitpayInvoice($bitpayInvoice,$order)->save();//->prepareWithOrder(array('increment_id' => $order->getIncrementId(), 'quote_id'=> $quote->getId()));//->save(); + // throw new \Magento\Framework\Exception\LocalizedException("Error Processing Request"); + // $this->getHelper()->debugData(json_encode($mirrorInvoice->getData())); + + if (false === isset($bitpayInvoice) || true === empty($bitpayInvoice)) { + $this->getHelper()->debugData('[ERROR] In Bitpay_Core_Model_Invoice::prepareWithBitpayInvoice(): Missing or empty $invoice parameter.'); + throw new \Exception('In Bitpay_Core_Model_Invoice::prepareWithBitpayInvoice(): Missing or empty $invoice parameter.'); + } + + if (false === isset($order) || true === empty($order)) { + $this->getHelper()->debugData('[ERROR] In Bitpay_Core_Model_Invoice::prepateWithOrder(): Missing or empty $order parameter.'); + throw new \Exception('In Bitpay_Core_Model_Invoice::prepateWithOrder(): Missing or empty $order parameter.'); + } + + $id = $invoice->getId(); + $url = $invoice->getUrl(); + $pos_data = $invoice->getPosData(); + $status = $invoice->getStatus(); + $btc_price = $invoice->getBtcPrice(); + $price = $invoice->getPrice(); + $currency = $invoice->getCurrency()->getCode(); + $order_id = $invoice->getOrderId(); + $invoice_time = intval($invoice->getInvoiceTime() / 1000); + $expiration_time = intval($invoice->getExpirationTime() / 1000); + $current_time = intval($invoice->getCurrentTime() / 1000); + $btc_paid = $invoice->getBtcPaid(); + $rate = $invoice->getRate(); + $exception_status = !empty($invoice->getExceptionStatus()) ? $invoice->getExceptionStatus() : null; + $quote_id = $order['quote_id']; + $increment_id = $order['increment_id']; + + $resources = \Magento\Framework\App\ObjectManager::getInstance()->get('Magento\Framework\App\ResourceConnection'); + $connection= $resources->getConnection(); + $invoiceTable = $resources->getTableName('bitpay_invoices'); + + + $sql = "Insert into " . $invoiceTable . "(id,quote_id,url,increment_id,pos_data,status,btc_price,price,currency,order_id,invoice_time,expiration_time,btc_paid,rate,exception_status) Values ('" . $id . "','".$quote_id."','".$url."','".$increment_id."','" . $pos_data . "','" . $status . "','" . $btc_price . "','" . $price . "','" . $currency . "','" .$order_id ."','". $invoice_time . "','" . $expiration_time . "','".$btc_paid."','" . $rate . "','" . $exception_status . "')"; + $connection->query($sql); + + $this->getHelper()->debugData($invoiceTable. ' table updated'); + + $this->getHelper()->debugData('[INFO] Leaving \Bitpay\Core\Model\Method\Bitcoin::authorize(): invoice id ' . $bitpayInvoice->getId()); + + return $this; + } + + /** + * This makes sure that the merchant has setup the extension correctly + * and if they have not, it will not show up on the checkout. + * + * @see Mage_Payment_Model_Method_Abstract::canUseCheckout() + * @return bool + */ + public function canUseCheckout() + { + $token = $this->_scopeConfig->getValue('payment/bitpay/token'); + + if (false === isset($token) || true === empty($token)) { + /** + * Merchant must goto their account and create a pairing code to + * enter in. + */ + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::canUseCheckout(): There was an error retrieving the token store param from the database or this Magento store does not have a BitPay token.'); + + return false; + } + + $this->getHelper()->debugData('[INFO] Leaving \Bitpay\Core\Model\Method\Bitcoin::canUseCheckout(): token obtained from storage successfully.'); + + return true; + } + + /** + * Fetchs an invoice from BitPay + * + * @param string $id + * @return Bitpay\Invoice + */ + public function fetchInvoice($id) + { + if (false === isset($id) || true === empty($id)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): missing or invalid id parameter.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): missing or invalid id parameter.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): function called with id ' . $id); + } + + $this->getHelper()->registerAutoloader(); + + $client = $this->getHelper()->getBitpayClient(); + + if (false === isset($client) || true === empty($client)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): could not obtain BitPay client.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): could not obtain BitPay client.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): obtained BitPay client successfully.'); + } + + $invoice = $client->getInvoice($id); + + if (false === isset($invoice) || true === empty($invoice)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): could not retrieve invoice from BitPay.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): could not retrieve invoice from BitPay.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::fetchInvoice(): successfully retrieved invoice id ' . $id . ' from BitPay.'); + } + + return $invoice; + } + + /** + * given Mage_Core_Model_Abstract, return api-friendly address + * + * @param $address + * + * @return array + */ + public function extractAddress($address) + { + if (false === isset($address) || true === empty($address)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::extractAddress(): missing or invalid address parameter.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::extractAddress(): missing or invalid address parameter.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::extractAddress(): called with good address parameter, extracting now.'); + } + + $options = array(); + $options['buyerName'] = $address->getName(); + + if ($address->getCompany()) { + $options['buyerName'] = $options['buyerName'].' c/o '.$address->getCompany(); + } + + $options['buyerAddress1'] = $address->getStreet1(); + $options['buyerAddress2'] = $address->getStreet2(); + $options['buyerAddress3'] = $address->getStreet3(); + $options['buyerAddress4'] = $address->getStreet4(); + $options['buyerCity'] = $address->getCity(); + $options['buyerState'] = $address->getRegionCode(); + $options['buyerZip'] = $address->getPostcode(); + $options['buyerCountry'] = $address->getCountry(); + $options['buyerEmail'] = $address->getEmail(); + $options['buyerPhone'] = $address->getTelephone(); + + // trim to fit API specs + foreach (array('buyerName', 'buyerAddress1', 'buyerAddress2', 'buyerAddress3', 'buyerAddress4', 'buyerCity', 'buyerState', 'buyerZip', 'buyerCountry', 'buyerEmail', 'buyerPhone') as $f) { + if (true === isset($options[$f]) && strlen($options[$f]) > 100) { + $this->getHelper()->debugData('[WARNING] In \Bitpay\Core\Model\Method\Bitcoin::extractAddress(): the ' . $f . ' parameter was greater than 100 characters, trimming.'); + $options[$f] = substr($options[$f], 0, 100); + } + } + + return $options; + } + + /** + * This is called when a user clicks the `Place Order` button + * + * @return string + */ + public function getOrderPlaceRedirectUrl() + { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::getOrderPlaceRedirectUrl(): $_redirectUrl is ' . self::$_redirectUrl); + + return self::$_redirectUrl; + + } + + + /** + * Create a new invoice with as much info already added. It should add + * some basic info and setup the invoice object. + * + * @return Bitpay\Invoice + */ + private function initializeInvoice() + { + $this->getHelper()->registerAutoloader(); + + $invoice = new \Bitpay\Invoice(); + + if (false === isset($invoice) || true === empty($invoice)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::initializeInvoice(): could not construct new BitPay invoice object.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::initializeInvoice(): could not construct new BitPay invoice object.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::initializeInvoice(): constructed new BitPay invoice object successfully.'); + } + + $invoice->setFullNotifications(true); + $invoice->setTransactionSpeed($this->_scopeConfig->getValue('payment/bitpay/speed')); + $invoice->setNotificationUrl($this->_storeManager->getStore()->getUrl($this->_scopeConfig->getValue('payment/bitpay/notification_url'))); + $invoice->setRedirectUrl($this->_storeManager->getStore()->getUrl($this->_scopeConfig->getValue('payment/bitpay/redirect_url'))); + + return $invoice; + } + + /** + * Prepares the invoice object to be sent to BitPay's API. This method sets + * all the other info that we have to rely on other objects for. + * + * @param Bitpay\Invoice $invoice + * @param Mage_Sales_Model_Order_Payment $payment + * @param float $amount + * @return Bitpay\Invoice + */ + private function prepareInvoice($invoice, $payment, $amount) + { + if (false === isset($invoice) || true === empty($invoice) || false === isset($payment) || true === empty($payment) || false === isset($amount) || true === empty($amount)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::prepareInvoice(): missing or invalid invoice, payment or amount parameter.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::prepareInvoice(): missing or invalid invoice, payment or amount parameter.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::prepareInvoice(): entered function with good invoice, payment and amount parameters.'); + } + $objectmanager = \Magento\Framework\App\ObjectManager::getInstance(); + + $quote_session = $objectmanager->get('\Magento\Checkout\Model\Cart')->getQuote(); + $quote = $objectmanager->create('\Magento\Quote\Model\Quote')->load($quote_session->getId()); + $order = $payment->getOrder(); + + if ($this->_scopeConfig->getValue('payment/bitpay/fullscreen')) { + $invoice->setOrderId($order->getIncrementId()); + $invoice->setPosData(json_encode(array('orderId' => $order->getIncrementId()))); + } else { + $invoice->setOrderId($quote->getId()); + $invoice->setPosData(json_encode(array('quoteId' => $quote->getId()))); + // $convertQuote = $this->quoteManagement;//$objectmanager->create('\Magento\Quote\Model\QuoteManagement'); + // $order = $convertQuote->submit($quote); + } + $invoice = $this->addCurrencyInfo($invoice, $order); + $invoice = $this->addPriceInfo($invoice, $order->getGrandTotal()); + $invoice = $this->addBuyerInfo($invoice, $order); + + return $invoice; + } + + /** + * This adds the buyer information to the invoice. + * + * @param Bitpay\Invoice $invoice + * @param Mage_Sales_Model_Order $order + * @return Bitpay\Invoice + */ + private function addBuyerInfo($invoice, $order) + { + if (false === isset($invoice) || true === empty($invoice) || false === isset($order) || true === empty($order)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::addBuyerInfo(): missing or invalid invoice or order parameter.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::addBuyerInfo(): missing or invalid invoice or order parameter.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::addBuyerInfo(): function called with good invoice and order parameters.'); + } + + $buyer = new \Bitpay\Buyer(); + + if (false === isset($buyer) || true === empty($buyer)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::addBuyerInfo(): could not construct new BitPay buyer object.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::addBuyerInfo(): could not construct new BitPay buyer object.'); + } + + + $buyer->setFirstName($order->getCustomerFirstname()); + $buyer->setLastName($order->getCustomerLastname()); + $objectmanager = \Magento\Framework\App\ObjectManager::getInstance(); + + if ($this->_scopeConfig->getValue('payment/bitpay/fullscreen')) { + $address = $order->getBillingAddress(); + } else { + $quote = $objectmanager->get('\Magento\Checkout\Model\Session')->getQuote(); + $address = $quote->getBillingAddress(); + } + + $street = $address->getStreet1(); + if (null !== $street && '' !== $street) { + $buyer->setAddress( + array( + $street, + $address->getStreet2(), + $address->getStreet3(), + $address->getStreet4() + ) + ); + } + + $region = $address->getRegion(); + $regioncode = $address->getRegionCode(); + if (null !== $regioncode && '' !== $regioncode) { + $buyer->setState($regioncode); + } else if (null !== $region && '' !== $region) { + $buyer->setState($region); + } + + $country = $address->getCountry(); + if (null !== $country && '' !== $country) { + $buyer->setCountry($country); + } + + $city = $address->getCity(); + if (null !== $city && '' !== $city) { + $buyer->setCity($city); + } + + $postcode = $address->getPostcode(); + if (null !== $postcode && '' !== $postcode) { + $buyer->setZip($postcode); + } + + $email = $address->getEmail(); + if (null !== $email && '' !== $email) { + $buyer->setEmail($email); + } + + $telephone = $address->getTelephone(); + if (null !== $telephone && '' !== $telephone) { + $buyer->setPhone($telephone); + } + + $invoice->setBuyer($buyer); + + return $invoice; + } + + /** + * Adds currency information to the invoice + * + * @param Bitpay\Invoice $invoice + * @param Mage_Sales_Model_Order $order + * @return Bitpay\Invoice + */ + private function addCurrencyInfo($invoice, $order) + { + if (false === isset($invoice) || true === empty($invoice) || false === isset($order) || true === empty($order)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::addCurrencyInfo(): missing or invalid invoice or order parameter.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::addCurrencyInfo(): missing or invalid invoice or order parameter.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::addCurrencyInfo(): function called with good invoice and order parameters.'); + } + + $currency = new \Bitpay\Currency(); + + if (false === isset($currency) || true === empty($currency)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::addCurrencyInfo(): could not construct new BitPay currency object.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::addCurrencyInfo(): could not construct new BitPay currency object.'); + } + + $currency->setCode($order->getOrderCurrencyCode()); + $invoice->setCurrency($currency); + + return $invoice; + } + + /** + * Adds pricing information to the invoice + * + * @param Bitpay\Invoice invoice + * @param float $amount + * @return Bitpay\Invoice + */ + private function addPriceInfo($invoice, $amount) + { + if (false === isset($invoice) || true === empty($invoice) || false === isset($amount) || true === empty($amount)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::addPriceInfo(): missing or invalid invoice or amount parameter.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::addPriceInfo(): missing or invalid invoice or amount parameter.'); + } else { + $this->getHelper()->debugData('[INFO] In \Bitpay\Core\Model\Method\Bitcoin::addPriceInfo(): function called with good invoice and amount parameters.'); + } + + $item = new \Bitpay\Item(); + + if (false === isset($item) || true === empty($item)) { + $this->getHelper()->debugData('[ERROR] In \Bitpay\Core\Model\Method\Bitcoin::addPriceInfo(): could not construct new BitPay item object.'); + throw new \Exception('In \Bitpay\Core\Model\Method\Bitcoin::addPriceInfo(): could not construct new BitPay item object.'); + } + + $item->setPrice($amount); + $invoice->setItem($item); + + return $invoice; + } + + +} diff --git a/app/code/Bitpay/Core/Model/ResourceModel/Invoice.php b/app/code/Bitpay/Core/Model/ResourceModel/Invoice.php new file mode 100755 index 0000000..e1b907a --- /dev/null +++ b/app/code/Bitpay/Core/Model/ResourceModel/Invoice.php @@ -0,0 +1,24 @@ +_init('bitpay_invoices', 'id'); + } + + +} diff --git a/app/code/Bitpay/Core/Model/ResourceModel/Invoice/Collection.php b/app/code/Bitpay/Core/Model/ResourceModel/Invoice/Collection.php new file mode 100755 index 0000000..1a18882 --- /dev/null +++ b/app/code/Bitpay/Core/Model/ResourceModel/Invoice/Collection.php @@ -0,0 +1,26 @@ +_init('Bitpay\Core\Model\Invoice', 'Bitpay\Core\Model\ResourceModel\Invoice'); + } +} diff --git a/app/code/Bitpay/Core/Model/ResourceModel/Ipn.php b/app/code/Bitpay/Core/Model/ResourceModel/Ipn.php new file mode 100755 index 0000000..76c64ff --- /dev/null +++ b/app/code/Bitpay/Core/Model/ResourceModel/Ipn.php @@ -0,0 +1,24 @@ +_init('bitpay_ipns', 'id'); + } + + +} diff --git a/app/code/Bitpay/Core/Model/ResourceModel/Ipn/Collection.php b/app/code/Bitpay/Core/Model/ResourceModel/Ipn/Collection.php new file mode 100755 index 0000000..7df220e --- /dev/null +++ b/app/code/Bitpay/Core/Model/ResourceModel/Ipn/Collection.php @@ -0,0 +1,24 @@ +_init('Bitpay\Core\Model\Ipn', 'Bitpay\Core\Model\ResourceModel\Ipn'); + } +} diff --git a/app/code/Bitpay/Core/Setup/InstallSchema.php b/app/code/Bitpay/Core/Setup/InstallSchema.php new file mode 100755 index 0000000..b3a3918 --- /dev/null +++ b/app/code/Bitpay/Core/Setup/InstallSchema.php @@ -0,0 +1,291 @@ +startSetup(); + + /** + * IPN Log Table, used to keep track of incoming IPNs + */ + + $table = $installer->getConnection()->newTable( + $installer->getTable('bitpay_ipns') + ) + ->addColumn( + 'id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['identity' => true,'nullable' => false, 'primary' => true], + 'bitpay_ipns' + ) + ->addColumn( + 'invoice_id', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '200', + [], + 'Invoice Id' + ) + ->addColumn( + 'url', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '400', + [], + 'URL' + ) + ->addColumn( + 'status', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '20', + [], + 'Status' + ) + ->addColumn( + 'btc_price', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'BTC Price' + ) + ->addColumn( + 'price', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'Price' + ) + ->addColumn( + 'currency', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '10', + [], + 'Currency' + ) + ->addColumn( + 'invoice_time', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + '11', + [], + 'Invoice Time' + ) + ->addColumn( + 'expiration_time', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + '11', + [], + 'Expiration Time' + ) + ->addColumn( + 'current_time', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + '11', + [], + 'Current Time' + ) + ->addColumn( + 'pos_data', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '255', + [], + 'POS Data' + ) + ->addColumn( + 'btc_paid', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'BTC Paid' + ) + ->addColumn( + 'rate', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'Rate' + ) + ->addColumn( + 'exception_status', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '255', + [], + 'Exception Status' + ) + ->setComment('Bitpay Core bitpay_ipns') + ->setOption('type', 'InnoDB') + ->setOption('charset', 'utf8'); + + $installer->getConnection()->createTable($table); + + + /** + * Table used to keep track of invoices that have been created. The + * IPNs that are received are used to update this table. + */ + $table = $installer->getConnection()->newTable( + $installer->getTable('bitpay_invoices') + ) + ->addColumn( + 'id', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 64, + ['nullable' => false, 'primary' => true, 'auto_increment' => false], + 'bitpay_invoices' + ) + ->addColumn( + 'quote_id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + '11', + [], + 'Quote Id' + ) + ->addColumn( + 'increment_id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + '11', + [], + 'Increment Id' + ) + ->addColumn( + 'updated_at', + \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP, + null, + [], + 'Updated At' + ) + ->addColumn( + 'url', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '200', + [], + 'URL' + ) + ->addColumn( + 'pos_data', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '255', + [], + 'POS Data' + ) + ->addColumn( + 'status', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '20', + [], + 'Status' + ) + ->addColumn( + 'btc_price', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'BTC Price' + ) + ->addColumn( + 'btc_due', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'BTC Due' + ) + ->addColumn( + 'price', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'Price' + ) + ->addColumn( + 'currency', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '10', + [], + 'Currency' + ) + ->addColumn( + 'ex_rates', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '255', + [], + 'Ex Rates' + ) + ->addColumn( + 'order_id', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '64', + [], + 'Order Id' + ) + ->addColumn( + 'invoice_time', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + '11', + [], + 'Invoice Time' + ) + ->addColumn( + 'expiration_time', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + '11', + [], + 'Expiration Time' + ) + ->addColumn( + 'current_time', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + '11', + [], + 'Current Time' + ) + ->addColumn( + 'btc_paid', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'BTC Paid' + ) + ->addColumn( + 'rate', + \Magento\Framework\DB\Ddl\Table::TYPE_DECIMAL, + '16,8', + [], + 'Rate' + ) + ->addColumn( + 'exception_status', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '255', + [], + 'Exception Status' + ) + ->addColumn( + 'token', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + '164', + [], + 'Token' + ) + ->setComment('Bitpay Core bitpay_invoices') + ->setOption('type', 'InnoDB') + ->setOption('charset', 'utf8'); + + $installer->getConnection()->createTable($table); + + $installer->endSetup(); + + } +} diff --git a/app/code/Bitpay/Core/Setup/UpgradeSchema.php b/app/code/Bitpay/Core/Setup/UpgradeSchema.php new file mode 100755 index 0000000..6c13b4e --- /dev/null +++ b/app/code/Bitpay/Core/Setup/UpgradeSchema.php @@ -0,0 +1,39 @@ +startSetup(); + + if (version_compare($context->getVersion(), '2.0.1') < 0) { + /** + * IPN Log Table, used to keep track of incoming IPNs + * + * Fixes `curent_time` typo + */ + $tableName = $installer->getTable('bitpay_ipns'); + + if ($installer->getConnection()->isTableExists($tableName) == true) { + + $installer->getConnection()->changeColumn($tableName, 'current_time', 'current_time', array('type' => \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER)); + } + } + + $installer->endSetup(); + } +} diff --git a/app/code/Bitpay/Core/composer.json b/app/code/Bitpay/Core/composer.json new file mode 100755 index 0000000..be80e92 --- /dev/null +++ b/app/code/Bitpay/Core/composer.json @@ -0,0 +1,27 @@ +{ + "name": "magento/module-core", + "description": "N/A", + "require": { + "php": "~5.5.0|~5.6.0|~7.0.0", + "magento/module-config": "100.0.*", + "magento/module-store": "100.0.*", + "magento/module-backend": "100.0.*", + "magento/module-customer": "100.0.*", + "magento/module-cms": "100.0.*", + "magento/framework": "100.0.*" + }, + "type": "magento2-module", + "version": "100.0.2", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Bitpay\\Core\\": "" + } + } +} diff --git a/app/code/Bitpay/Core/etc/adminhtml/system.xml b/app/code/Bitpay/Core/etc/adminhtml/system.xml new file mode 100755 index 0000000..894003f --- /dev/null +++ b/app/code/Bitpay/Core/etc/adminhtml/system.xml @@ -0,0 +1,155 @@ + + + + +
+ + + + BitPaySupportSign UpLogin + ]]> + + + + 1 + Magento\Config\Block\System\Config\Form\Fieldset + + + + https://bitpay.com/api-tokens and copy/paste + that code here. Once you have successfully paired this Magento store with your BitPay Merchant account, you can begin accepting Bitcoins as payment.]]> + + Bitpay\Core\Model\Config\Backend\PairingCode + payment/bitpay/pairing_code + + + + Magento\Config\Model\Config\Source\Yesno + payment/bitpay/fullscreen + + + + Magento\Config\Model\Config\Source\Yesno + payment/bitpay/active + + + + + + + payment/bitpay/title + + + + Bitpay\Core\Model\Config\Source\Network + payment/bitpay/network + + test.bitpay.com.]]> + + + + + Magento\Config\Model\Config\Source\Yesno + payment/bitpay/debug + + + + + + + payment/bitpay/notification_url + + + + payment/bitpay/redirect_url + + + + Bitpay\Core\Model\Config\Source\TransactionSpeed + payment/bitpay/speed + + High: an invoice is confirmed immediately when payment received.
Medium: an invoice is confirmed after 1 block confirmation by the Bitcoin network (~10 mins).
Low: an invoice is confirmed after 6 block confirmations by the Bitcoin network (~1 hour).
The default and safest setting is "Low". A "High" setting is quicker to generate a payment confirmation but is riskier since the transaction could have not been officially confirmed by the Bitcoin network itself.]]> +
+
+
+ + + 1 + Magento\Config\Block\System\Config\Form\Fieldset + + + Magento\Payment\Model\Config\Source\Allspecificcountries + payment/bitpay/allowspecific + + + + Magento\Directory\Model\Config\Source\Country + 1 + payment/bitpay/specificcountry + + + + payment/bitpay/min_order_total + + + + payment/bitpay/max_order_total + + + + validate-number + payment/bitpay/sort_order + + + + + 1 + Magento\Config\Block\System\Config\Form\Fieldset + + + Magento\Sales\Model\Config\Source\Order\Status + payment/bitpay/invoice_paid + + + + + + + Magento\Sales\Model\Config\Source\Order\Status + payment/bitpay/invoice_confirmed + + + + + + + Magento\Sales\Model\Config\Source\Order\Status + payment/bitpay/invoice_complete + + + + + + + + 1 + Magento\Config\Block\System\Config\Form\Fieldset + + + + + + openssl + Bitpay\Core\Block\Adminhtml\System\Config\Form\Field\Extension + + +
+
+
+
diff --git a/app/code/Bitpay/Core/etc/config.xml b/app/code/Bitpay/Core/etc/config.xml new file mode 100755 index 0000000..7cafb85 --- /dev/null +++ b/app/code/Bitpay/Core/etc/config.xml @@ -0,0 +1,34 @@ + + + + + + + 0 + 1 + \Bitpay\Core\Model\Method\Bitcoin + new + authorize + Bitcoin + testnet + 0 + bitpay/ipn + checkout/onepage/success + medium + 0 + 1 + new + processing + processing + complete + canceled + canceled + + + + diff --git a/app/code/Bitpay/Core/etc/di.xml b/app/code/Bitpay/Core/etc/di.xml new file mode 100755 index 0000000..537538f --- /dev/null +++ b/app/code/Bitpay/Core/etc/di.xml @@ -0,0 +1,27 @@ + + + + + + Magento\Payment\Model\Config\Data + + + + + Magento\Framework\Filesystem\Driver\File + + + + + Bitpay + + Bitpay\Core\Logger\Handler + + + + diff --git a/app/code/Bitpay/Core/etc/frontend/di.xml b/app/code/Bitpay/Core/etc/frontend/di.xml new file mode 100755 index 0000000..ba86ea8 --- /dev/null +++ b/app/code/Bitpay/Core/etc/frontend/di.xml @@ -0,0 +1,19 @@ + + + + + + + Bitpay\Core\Model\Method\Bitcoin::CODE + + + + + + + + Bitpay\Core\Model\ConfigProvider + + + + diff --git a/app/code/Bitpay/Core/etc/frontend/routes.xml b/app/code/Bitpay/Core/etc/frontend/routes.xml new file mode 100755 index 0000000..9acc450 --- /dev/null +++ b/app/code/Bitpay/Core/etc/frontend/routes.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/code/Bitpay/Core/etc/module.xml b/app/code/Bitpay/Core/etc/module.xml new file mode 100755 index 0000000..9ee6539 --- /dev/null +++ b/app/code/Bitpay/Core/etc/module.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/app/code/Bitpay/Core/etc/payment.xml b/app/code/Bitpay/Core/etc/payment.xml new file mode 100755 index 0000000..e72f367 --- /dev/null +++ b/app/code/Bitpay/Core/etc/payment.xml @@ -0,0 +1,15 @@ + + + + + + 1 + + + \ No newline at end of file diff --git a/app/code/Bitpay/Core/registration.php b/app/code/Bitpay/Core/registration.php new file mode 100755 index 0000000..5f1d9a1 --- /dev/null +++ b/app/code/Bitpay/Core/registration.php @@ -0,0 +1,11 @@ +Ordered with BitPay'; + +if ($url = $block->getBitpayInvoiceUrl()) { + echo '

View Invoice

'; +} + +?> diff --git a/app/code/Bitpay/Core/view/frontend/layout/bitpay_invoice_index.xml b/app/code/Bitpay/Core/view/frontend/layout/bitpay_invoice_index.xml new file mode 100755 index 0000000..32e246e --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/layout/bitpay_invoice_index.xml @@ -0,0 +1,17 @@ + + + + + + Pay with BitCoin + + + + + + diff --git a/app/code/Bitpay/Core/view/frontend/layout/checkout_index_index.xml b/app/code/Bitpay/Core/view/frontend/layout/checkout_index_index.xml new file mode 100755 index 0000000..c4206de --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/layout/checkout_index_index.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + uiComponent + + + + + + + + Bitpay_Core/js/view/payment/bitpay + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/code/Bitpay/Core/view/frontend/templates/bitpay/form/bitpay.phtml b/app/code/Bitpay/Core/view/frontend/templates/bitpay/form/bitpay.phtml new file mode 100755 index 0000000..cfcc4fb --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/templates/bitpay/form/bitpay.phtml @@ -0,0 +1,18 @@ +getMethodCode(); +$bitpayScope = \Magento\Framework\App\ObjectManager::getInstance()->get('\Magento\Framework\App\Config\ScopeConfigInterface'); +if ($bitpayScope->getValue('payment/bitpay/fullscreen')) { + echo ''; +} +?> diff --git a/app/code/Bitpay/Core/view/frontend/templates/bitpay/iframe.phtml b/app/code/Bitpay/Core/view/frontend/templates/bitpay/iframe.phtml new file mode 100755 index 0000000..57ff6e4 --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/templates/bitpay/iframe.phtml @@ -0,0 +1,41 @@ +getFrameActionUrl(); +?> +
+
+
+getLastQuoteId(); +$check_url = $block->getValidateUrl(); +?> + + \ No newline at end of file diff --git a/app/code/Bitpay/Core/view/frontend/templates/bitpay/info/default.phtml b/app/code/Bitpay/Core/view/frontend/templates/bitpay/info/default.phtml new file mode 100755 index 0000000..905db79 --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/templates/bitpay/info/default.phtml @@ -0,0 +1,10 @@ +' . $this->escapeHtml($this->getMethod()->getTitle()) . '

'; + +echo $this->getChildHtml(); +?> diff --git a/app/code/Bitpay/Core/view/frontend/templates/bitpay/json.phtml b/app/code/Bitpay/Core/view/frontend/templates/bitpay/json.phtml new file mode 100755 index 0000000..e9743e1 --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/templates/bitpay/json.phtml @@ -0,0 +1,9 @@ + +{ + "paid": isPaid(); ?> +} diff --git a/app/code/Bitpay/Core/view/frontend/web/js/action/set-payment-method.js b/app/code/Bitpay/Core/view/frontend/web/js/action/set-payment-method.js new file mode 100755 index 0000000..25b2876 --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/web/js/action/set-payment-method.js @@ -0,0 +1,56 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +define( + [ + 'jquery', + 'Magento_Checkout/js/model/quote', + 'Magento_Checkout/js/model/url-builder', + 'mage/storage', + 'Magento_Checkout/js/model/error-processor', + 'Magento_Customer/js/model/customer', + 'Magento_Checkout/js/model/full-screen-loader' + ], + function ($, quote, urlBuilder, storage, errorProcessor, customer, fullScreenLoader) { + 'use strict'; + + return function (messageContainer) { + var serviceUrl, + payload, + method = 'put', + paymentData = quote.paymentMethod(); + + /** + * Checkout for guest and registered customer. + */ + if (!customer.isLoggedIn()) { + serviceUrl = urlBuilder.createUrl('/guest-carts/:cartId/set-payment-information', { + cartId: quote.getQuoteId() + }); + payload = { + cartId: quote.getQuoteId(), + email: quote.guestEmail, + paymentMethod: paymentData + }; + method = 'post'; + } else { + serviceUrl = urlBuilder.createUrl('/carts/mine/selected-payment-method', {}); + payload = { + cartId: quote.getQuoteId(), + method: paymentData + }; + } + fullScreenLoader.startLoader(); + + return storage[method]( + serviceUrl, JSON.stringify(payload) + ).fail( + function (response) { + errorProcessor.process(response, messageContainer); + fullScreenLoader.stopLoader(); + } + ); + }; + } +); diff --git a/app/code/Bitpay/Core/view/frontend/web/js/view/payment/bitpay.js b/app/code/Bitpay/Core/view/frontend/web/js/view/payment/bitpay.js new file mode 100755 index 0000000..49f1ccc --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/web/js/view/payment/bitpay.js @@ -0,0 +1,26 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ +define( + [ + 'uiComponent', + 'Magento_Checkout/js/model/payment/renderer-list' + ], + function ( + Component, + rendererList + ) { + 'use strict'; + rendererList.push( + { + type: 'bitpay', + component: 'Bitpay_Core/js/view/payment/method-renderer/bitpay' + } + ); + /** Add view logic here if needed */ + return Component.extend({}); + } +); \ No newline at end of file diff --git a/app/code/Bitpay/Core/view/frontend/web/js/view/payment/method-renderer/bitpay.js b/app/code/Bitpay/Core/view/frontend/web/js/view/payment/method-renderer/bitpay.js new file mode 100755 index 0000000..a3ae4d6 --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/web/js/view/payment/method-renderer/bitpay.js @@ -0,0 +1,89 @@ +/** + * Copyright © 2015 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +define( + [ + 'ko', + 'Magento_Checkout/js/view/payment/default', + 'Magento_Checkout/js/model/quote', + 'jquery', + 'Magento_Checkout/js/action/place-order', + 'Magento_Checkout/js/action/select-payment-method', + 'Magento_Customer/js/model/customer', + 'Magento_Checkout/js/checkout-data', + 'Magento_Checkout/js/model/payment/additional-validators', + 'mage/url', + 'Bitpay_Core/js/action/set-payment-method', + 'Magento_Checkout/js/model/full-screen-loader' + ], + function (ko,Component, quote,$,placeOrderAction,selectPaymentMethodAction,customer, checkoutData, additionalValidators, url,setPaymentMethodAction,fullScreenLoader) { + 'use strict'; + return Component.extend({ + defaults: { + template: 'Bitpay_Core/payment/bitpay', + timeoutMessage: 'Sorry, but something went wrong. Please contact the seller.' + }, + getCode: function() { + return 'bitpay'; + }, + + isActive: function() { + return true; + }, + + getRedirectionText: function () { + + var iframeHtml; + jQuery.ajax( { + url: url.build('bitpay/iframe/index/'), + async: false , + dataType: "json", + success: function(a) { + iframeHtml = a.html; + } + + }); + return iframeHtml; //'You will be transfered to BitPay to complete your purchase when using this payment method.'; + }, + + placeOrder: function (data, event) { + if (event) { + event.preventDefault(); + } + var self = this, + placeOrder, + emailValidationResult = customer.isLoggedIn(), + loginFormSelector = 'form[data-role=email-with-possible-login]'; + if (!customer.isLoggedIn()) { + $(loginFormSelector).validation(); + emailValidationResult = Boolean($(loginFormSelector + ' input[name=username]').valid()); + } + if (emailValidationResult && this.validate() && additionalValidators.validate()) { + this.isPlaceOrderActionAllowed(false); + placeOrder = placeOrderAction(this.getData(), false, this.messageContainer); + + $.when(placeOrder).fail(function () { + self.isPlaceOrderActionAllowed(true); + }).done(this.afterPlaceOrder.bind(this)); + return true; + } + return false; + }, + + selectPaymentMethod: function() { + selectPaymentMethodAction(this.getData()); + checkoutData.setSelectedPaymentMethod(this.item.method); + return true; + }, + + afterPlaceOrder: function () { + window.location.replace(url.build('bitpay/invoice/index/')); + }, + /** Returns send check to info */ + getMailingAddress: function() { + return window.checkoutConfig.payment.checkmo.mailingAddress; + }, + }); + } +); diff --git a/app/code/Bitpay/Core/view/frontend/web/template/payment/bitpay.html b/app/code/Bitpay/Core/view/frontend/web/template/payment/bitpay.html new file mode 100755 index 0000000..4966571 --- /dev/null +++ b/app/code/Bitpay/Core/view/frontend/web/template/payment/bitpay.html @@ -0,0 +1,46 @@ + +
+
+ + +
+
+ + + +
+
+ +
+
+
+ + + +
+
+
+ +
+
+
+
diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..3ae06e2 --- /dev/null +++ b/build.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b54aa11 --- /dev/null +++ b/composer.json @@ -0,0 +1,48 @@ +{ + "name": "bitpay/magento2-plugin", + "description": "Bitcoin payment module using the bitpay.com service", + "keywords": ["magento","bitcoin"], + "minimum-stability": "stable", + "type": "magento2-plugin", + "homepage": "https://github.com/bitpay/magento2-plugin", + "license": "MIT", + "support": { + "email": "support@bitpay.com", + "issues": "https://github.com/bitpay/magento2-plugin/issues", + "source": "https://github.com/bitpay/magento2-plugin" + }, + "require": { + "composer/installers": "~1.0", + "bitpay/php-client": "dev-master#9027ce67e4b28516ff1ebd1046bdd15c37a7a59f" + }, + "require-dev": { + "symfony/finder": "~2.3", + "symfony/process": "~2.3", + "phpmd/phpmd": "~2.1", + "phpdocumentor/phpdocumentor": "~2.7", + "daedalus/daedalus": "~1.0@dev", + "pdepend/pdepend" : "~2.0", + "squizlabs/php_codesniffer": "~1.5", + "phpunit/phpunit": "~3.7", + "phploc/phploc": "~2.0", + "fzaninotto/faker": "~1.3" + }, + "config": { + "bin-dir": "bin" + }, + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "archive": { + "exclude": [ + "tests/", + ".gitattributes", + ".gitignore", + ".travis.yml", + "build/", + "build.xml" + ] + } +} diff --git a/lib/internal/Bitpay/Storage/MagentoStorage.php b/lib/internal/Bitpay/Storage/MagentoStorage.php new file mode 100755 index 0000000..90db233 --- /dev/null +++ b/lib/internal/Bitpay/Storage/MagentoStorage.php @@ -0,0 +1,73 @@ +_keys[$key->getId()] = $key; + + $data = serialize($key); + + $encryptedData = \Magento\Framework\App\ObjectManager::getInstance()->get('\Magento\Framework\Encryption\EncryptorInterface')->encrypt($data); + $config = \Magento\Framework\App\ObjectManager::getInstance()->get('\Magento\Config\Model\ResourceModel\Config'); + $store = \Magento\Framework\App\ObjectManager::getInstance()->get('\Magento\Store\Model\StoreManagerInterface'); + $helper = \Magento\Framework\App\ObjectManager::getInstance()->get('\Bitpay\Core\Helper\Data'); + + if (true === isset($config) && false === empty($config)) { + $config->saveConfig($key->getId(), $encryptedData, $store->getStore()->getCode(), 0); + } else { + $helper->debugData('[ERROR] In file lib/Bitpay/Storage/MagentoStorage.php, class MagentoStorage::persist - Could not instantiate a \Mage_Core_Model_Config object.'); + throw new \Exception('[ERROR] In file lib/Bitpay/Storage/MagentoStorage.php, class MagentoStorage::persist - Could not instantiate a \Mage_Core_Model_Config object.'); + } + } + + /** + * @inheritdoc + */ + public function load($id) + { + if (true === isset($id) && true === isset($this->_keys[$id])) { + return $this->_keys[$id]; + } + $helper = \Magento\Framework\App\ObjectManager::getInstance()->get('\Bitpay\Core\Helper\Data'); + $storeconfig = \Magento\Framework\App\ObjectManager::getInstance()->get('\Magento\Framework\App\Config\ScopeConfigInterface'); + $entity = $storeconfig->getValue($id); + + /** + * Not in database + */ + if (false === isset($entity) || true === empty($entity)) { + $helper->debugData('[INFO] Call to MagentoStorage::load($id) with the id of ' . $id . ' did not return the store config parameter because it was not found in the database.'); + throw new \Exception('[INFO] Call to MagentoStorage::load($id) with the id of ' . $id . ' did not return the store config parameter because it was not found in the database.'); + } + + $decodedEntity = unserialize(\Magento\Framework\App\ObjectManager::getInstance()->get('\Magento\Framework\Encryption\EncryptorInterface')->decrypt($entity)); + + if (false === isset($decodedEntity) || true === empty($decodedEntity)) { + $helper->debugData('[INFO] Call to MagentoStorage::load($id) with the id of ' . $id . ' could not decrypt & unserialize the entity ' . $entity . '.'); + throw new \Exception('[INFO] Call to MagentoStorage::load($id) with the id of ' . $id . ' could not decrypt & unserialize the entity ' . $entity . '.'); + } + + $helper->debugData('[INFO] Call to MagentoStorage::load($id) with the id of ' . $id . ' successfully decrypted & unserialized the entity ' . $entity . '.'); + + return $decodedEntity; + } +} diff --git a/modman b/modman new file mode 100644 index 0000000..310f7c0 --- /dev/null +++ b/modman @@ -0,0 +1,27 @@ +# The MIT License (MIT) +# +# Copyright (c) 2011-2014 BitPay, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +app/code/community/Bitpay/Core app/code/community/Bitpay/Core +app/design/frontend/base/default/layout/bitpay.xml app/design/frontend/base/default/layout/bitpay.xml +app/design/frontend/base/default/template/bitpay app/design/frontend/base/default/template/bitpay +app/etc/modules/Bitpay_Core.xml app/etc/modules/Bitpay_Core.xml +lib/Bitpay lib/Bitpay diff --git a/scripts/package b/scripts/package new file mode 100755 index 0000000..53ab261 --- /dev/null +++ b/scripts/package @@ -0,0 +1,150 @@ +#!/usr/bin/env php +files() + ->in($vendorDir . '/bitpay/php-client/src') + ->exclude('Tests'); + +foreach ($finder as $file) { + $path = $file->getRelativePathname(); + $filesystem->mkdir( + sprintf( + '%s/lib/%s', + $tmpDistDir, + dirname($file->getRelativePathname()) + ) + ); + $filesystem->copy( + $file->getRealPath(), + sprintf( + '%s/lib/%s', + $tmpDistDir, + $file->getRelativePathname() + ), + true + ); +} +$filesystem->mirror('app/', sprintf('%s/app/', $tmpDistDir)); +$filesystem->mirror('lib/', sprintf('%s/lib/', $tmpDistDir)); +$filesystem->copy('LICENSE', sprintf('%s/app/code/community/Bitpay/Core/LICENSE', $tmpDistDir)); +$filesystem->copy('README.md', sprintf('%s/app/code/community/Bitpay/Core/README.md', $tmpDistDir)); + +// All required files are in the temp. distribution directory + +/** + * Need to create the package.xml file required by Magento + */ +$xml = simplexml_load_string(''); +$xml->addChild('name', 'Bitpay_Core'); +$xml->addChild('version', $version); +$xml->addChild('stability', 'stable'); +$xml->addChild('license', 'MIT') + ->addAttribute('uri', 'https://github.com/bitpay/magento-plugin/blob/master/LICENSE'); +$xml->addChild('channel', 'community'); +$xml->addChild('extends'); +$xml->addChild('summary'); +$xml->addChild('description'); +$xml->addChild('notes'); + +$authorsNode = $xml->addChild('authors'); + +$authors = array( + array( + 'Integrations Team', // Name + 'BitPayInc', // Magento Connect Username + 'support@bitpay.com', // Email + ), +); + +foreach ($authors as $author) { + $authorNode = $authorsNode->addChild('author'); + $authorNode->addChild('name', $author[0]); + $authorNode->addChild('user', $author[1]); + $authorNode->addChild('email', $author[2]); +} + +$xml->addChild('date', date('Y-m-d')); +$xml->addChild('time', date('G:i:s')); +$xml->addChild('compatible'); +$xml->addChild('dependencies'); + +$requiredNode = $xml->addChild('required', 'php'); +$requiredNode->addAttribute('php_min', '5.4.0'); +$requiredNode->addAttribute('php_max', '6.0.0'); + +$extensionsNode = $xml->addChild('extensions'); + +foreach (array('openssl', 'mcrypt') as $ext) { + $extNode = $extensionsNode->addChild('name', $ext); + $extNode->addChild('min'); + $extNode->addChild('max'); +} + +$targetNode = $xml->addChild('contents')->addChild('target'); +$targetNode->addAttribute('name', 'mage'); + +$finder = new \Symfony\Component\Finder\Finder(); +$finder + ->files() + ->in($tmpDistDir); + +foreach ($finder as $file) { + $node = $targetNode; + $directories = explode('/', $file->getRelativePathname()); + $filename = array_pop($directories); + + for ($i = 1; $i <= count($directories); $i++) { + $dir = $directories[$i - 1]; + $nodes = $node->xpath('dir[@name="' . $dir . '"]'); + if (count($nodes)) { + $node = array_pop($nodes); + } else { + $node = $node->addChild('dir'); + $node->addAttribute('name', $dir); + } + } + + $fileNode = $node->addChild('file'); + $fileNode->addAttribute('name', $file->getBaseName()); + $fileNode->addAttribute('hash', md5_file($file->getRealPath())); +} + +$xml->asXml($tmpDistDir . '/package.xml'); + +// package.xml created, just need to tar/zip everything +$filesystem->remove($distFile.'.zip'); +$filesystem->remove($distFile.'.tgz'); + +$process = new \Symfony\Component\Process\Process( + sprintf('cd %s; tar -czf %s *', $tmpDistDir, $distFile.'.tgz') +); + +$process->run(); + +// Cleanup +$filesystem->remove($tmpDistDir); diff --git a/tests/Bitpay/Core/Helper/DataTest.php b/tests/Bitpay/Core/Helper/DataTest.php new file mode 100644 index 0000000..536a73c --- /dev/null +++ b/tests/Bitpay/Core/Helper/DataTest.php @@ -0,0 +1,241 @@ +assertSame( + 'payment_bitpay.log', + Mage::helper('bitpay')->getLogFile() + ); + } + + public function testDebugData() + { + Mage::helper('bitpay')->debugData('Testing'); + } + + public function testIsDebugMode() + { + Mage::app()->getStore()->setConfig('payment/bitpay/debug', null); + $this->assertFalse(Mage::helper('bitpay')->isDebug()); + + Mage::app()->getStore()->setConfig('payment/bitpay/debug', false); + $this->assertFalse(Mage::helper('bitpay')->isDebug()); + + Mage::app()->getStore()->setConfig('payment/bitpay/debug', true); + $this->assertTrue(Mage::helper('bitpay')->isDebug()); + } + + public function testHasTransactionSpeedFalse() + { + Mage::app()->getStore()->setConfig('payment/bitpay/speed', null); + + $this->assertFalse(Mage::helper('bitpay')->hasTransactionSpeed()); + } + + public function testHasTransactionSpeedTrue() + { + Mage::app()->getStore()->setConfig('payment/bitpay/speed', 'low'); + + $this->assertTrue(Mage::helper('bitpay')->hasTransactionSpeed()); + } + + /** + * Location where BitPay IPNs will go + */ + public function testGetNotificationUrl() + { + $this->assertSame( + 'http://www.localhost.com/bitpay/ipn/', + Mage::helper('bitpay')->getNotificationUrl() + ); + } + + public function testGetRedirectUrl() + { + $this->assertSame( + 'http://www.localhost.com/checkout/onepage/success/', + Mage::helper('bitpay')->getRedirectUrl() + ); + } + + public function testRegisterAutoloader() + { + Mage::helper('bitpay')->registerAutoloader(); + } + + public function testGenerateAndSaveKeys() + { + Mage::helper('bitpay')->generateAndSaveKeys(); + } + + public function testGetSinKey() + { + Mage::helper('bitpay')->getSinKey(); + } + + private function createInvalidIpn() + { + $ipn = new Bitpay_Core_Model_Ipn(); + $ipn->setData( + array( + 'quote_id' => '', + 'order_id' => '', + 'invoice_id' => '', + 'url' => '', + 'pos_data' => '', + 'status' => '', + 'btc_price' => '', + 'price' => '', + 'currency' => '', + 'invoice_time' => '', + 'expiration_time' => '', + 'current_time' => '', + ) + ); + $ipn->save(); + $ipn->load($ipn->getId()); + + return $ipn; + } + + private function createExpiredIpn() + { + $order = $this->createOrder(); + $ipn = new Bitpay_Core_Model_Ipn(); + $ipn->setData( + array( + 'quote_id' => '', + 'order_id' => $order->getIncrementId(), + 'invoice_id' => '', + 'url' => '', + 'pos_data' => '', + 'status' => '', + 'btc_price' => '', + 'price' => '', + 'currency' => '', + 'invoice_time' => '', + 'expiration_time' => '', + 'current_time' => '', + ) + ); + $ipn->save(); + $ipn->load($ipn->getId()); + + return $ipn; + } + + private function createOrder() + { + $product = $this->createProduct(); + $quote = $this->createQuote(); + $quote->addProduct( + $product, + new Varien_Object( + array( + 'qty' => 1, + ) + ) + ); + $address = array( + 'firstname' => self::$faker->firstName, + 'lastname' => self::$faker->lastName, + 'company' => self::$faker->company, + 'email' => self::$faker->email, + 'city' => self::$faker->city, + 'region_id' => '', + 'region' => 'State/Province', + 'postcode' => self::$faker->postcode, + 'telephone' => self::$faker->phoneNumber, + 'country_id' => self::$faker->state, + 'customer_password' => '', + 'confirm_password' => '', + 'save_in_address_book' => 0, + 'use_for_shipping' => 1, + 'street' => array( + self::$faker->streetAddress, + ), + ); + + $quote->getBillingAddress() + ->addData($address); + + $quote->getShippingAddress() + ->addData($address) + ->setShippingMethod('flatrate_flatrate') + ->setPaymentMethod('checkmo') + ->setCollectShippingRates(true) + ->collectTotals(); + + $quote + ->setCheckoutMethod('guest') + ->setCustomerId(null) + ->setCustomerEmail($address['email']) + ->setCustomerIsGuest(true) + ->setCustomerGroupId(Mage_Customer_Model_Group::NOT_LOGGED_IN_ID); + + $quote->getPayment() + ->importData(array('method' => 'checkmo')); + + $quote->save(); + + $service = Mage::getModel('sales/service_quote', $quote); + $service->submitAll(); + $order = $service->getOrder(); + + $order->save(); + $order->load($order->getId()); + + return $order; + } + + private function createProduct() + { + $product = Mage::getModel('catalog/product'); + + $product->addData( + array( + 'attribute_set_id' => 1, + 'website_ids' => array(1), + 'categories' => array(), + 'type_id' => Mage_Catalog_Model_Product_Type::TYPE_SIMPLE, + 'sku' => self::$faker->randomNumber, + 'name' => self::$faker->name, + 'weight' => self::$faker->randomDigit, + 'status' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED, + 'tax_class_id' => 2, + 'visibility' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH, + 'price' => self::$faker->randomFloat(2), + 'description' => self::$faker->paragraphs, + 'short_description' => self::$faker->sentence, + 'stock_data' => array( + 'is_in_stock' => 1, + 'qty' => 100, + ), + ) + ); + + $product->save(); + $product->load($product->getId()); + + return $product; + } + + private function createQuote() + { + return Mage::getModel('sales/quote') + ->setStoreId(Mage::app()->getStore('default')->getId()); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..a2a0e1a --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,18 @@ +