Select a transaction speed. The **high** speed will send a confirmation as soon as a transaction is received in the bitcoin network (usually a few seconds). A **medium** speed setting will typically take 10 minutes. The **low** speed setting usually takes around 1 hour. See the bitpay.com merchant documentation for a full description of the transaction speed settings.
-
Verify that the currencies option includes your store's currencies. If it doesn't, check bitpay.com to see if they support your desired currency. If so, you may simply add the currency to the list using this setting. If not, you will not be able to use that currency.
-
(optional) Adjust the "Fullscreen Invoice" setting. "No" means that payment instructions are embedded in the checkout page. "Yes" means that the buyer will be redirected to bitpay.com to pay their order.
-
-
-Usage
------
-When a shopper chooses the Bitcoin payment method, they will be presented with an order summary as the next step (prices are shown in whatever currency they've selected for shopping). If the fullscreen option is disabled, they can pay for their order using the address shown on the screen. Otherwise they will place their order and be redirected to bitpay.com to pay.
-
-The order status in the admin panel will be "Processing" if payment has been confirmed.
-
-Note: This extension does not provide a means of automatically pulling a current BTC exchange rate for presenting BTC prices to shoppers.
-
-Change Log
-----------
-Version 1
- - Initial version, tested against Magento 1.6.0.0
-
-Version 2
- - Now supports API keys instead of SSL files. Tested against 1.7.0.2.
-
-Version 3
- - Now gives the option to show an iframe on the checkout page instead of redirecting to bitpay.com.
+# NOTICE
+This is a Community-supported project.
+
+If you are interested in becoming a maintainer of this project, please contact us at integrations@bitpay.com. Developers at BitPay will attempt to work along the new maintainers to ensure the project remains viable for the foreseeable future.
+
+# Description
+
+Bitcoin payment plugin for Magento using the bitpay.com service.
+
+[](https://travis-ci.org/bitpay/magento-plugin)
+
+
+## Quick Start Guide
+
+To get up and running with our plugin quickly, see the GUIDE here: https://github.com/bitpay/magento-plugin/blob/master/GUIDE.md
+
+## Support
+
+**BitPay Support:**
+
+* Last Cart Version Tested: 1.9.3.8
+* [GitHub Issues](https://github.com/bitpay/magento-plugin/issues)
+ * Open an issue if you are having issues with this plugin.
+* [Support](https://help.bitpay.com)
+ * BitPay merchant support documentation
+
+**Magento Support:**
+
+* [Homepage](http://magento.com)
+* [Documentation](http://docs.magentocommerce.com)
+* [Community Edition Support Forums](https://www.magentocommerce.com/support/ce/)
+
+## Troubleshooting
+
+1. Ensure a valid SSL certificate is installed on your server. Also ensure your root CA cert is updated. If your CA cert is not current, you will see curl SSL verification errors.
+2. Verify that your web server is not blocking POSTs from servers it may not recognize. Double check this on your firewall as well, if one is being used.
+3. Check the `payment_bitpay.log` file for any errors during BitPay payment attempts. If you contact BitPay support, they will ask to see the log file to help diagnose the problem. The log file will be found inside your Magento's `var/log/` directory. **NOTE:** You will need to enable the debugging setting for the extension to output information into the log file.
+4. Check the version of this plugin against the official plugin repository to ensure you are using the latest version. Your issue might have been addressed in a newer version! See the [Releases](https://github.com/bitpay/magento-plugin/releases) page or the Magento Connect store for the latest version.
+5. If all else fails, send an email describing your issue **in detail** to support@bitpay.com
+
+**TIP:** When contacting support it will help us is you provide:
+
+* Magento CE Version (Found at the bottom page in the Administration section)
+* Other extensions you have installed
+ * Some extensions do not play nice
+* Configuration settings for the extension (Most merchants take screen grabs)
+* Any log files that will help
+ * web server error logs
+ * enabled debugging for this extension and send us `var/log/payment_bitpay.log`
+* Screen grabs of error message if applicable.
+
+
+## Contribute
+
+For developers wanting to contribute to this project, it is assumed you have a stable Magento environment to work with, and are familiar with developing for Magento. You will need to clone this repository or fork and clone the repository you created.
+
+Once you have cloned the repository, you will need to run [composer install](https://getcomposer.org/doc/00-intro.md#using-composer). Using and setting up composer is outside the scope, however you will find the documentation on their site comprehensive. You can then run the ``scripts/package`` script to create a distribution files which you can find in ``build/dist``. This is the file that you can upload to your server to unzip or do with what you will.
+
+If you encounter any issues or implement any updates or changes, please open an [issue](https://github.com/bitpay/magento-plugin/issues) or submit a Pull Request.
+
+**NOTE:** The ``scripts/package`` file contains some configuration settings that will need to change for different releases. If you are using this script to build files that are for distribution, these will need to be updated.
+
+
+## License
+
+The MIT License (MIT)
+
+Copyright (c) 2011-2018 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/community/Bitpay/Bitcoins/Block/Iframe.php b/app/code/community/Bitpay/Bitcoins/Block/Iframe.php
deleted file mode 100644
index 4630a62..0000000
--- a/app/code/community/Bitpay/Bitcoins/Block/Iframe.php
+++ /dev/null
@@ -1,94 +0,0 @@
-setTemplate('bitcoins/iframe.phtml');
- parent::_construct();
- }
-
- public function GetQuoteId() {
- $quote = $this->getQuote();
- $quoteId = $quote->getId();
- return $quoteId;
- }
-
- // create an invoice and return the url so that iframe.phtml can display it
- public function GetIframeUrl() {
- // are they using bitpay?
- if (!($quote = Mage::getSingleton('checkout/session')->getQuote())
- or !($payment = $quote->getPayment())
- or !($instance = $payment->getMethodInstance())
- or ($instance->getCode() != 'Bitcoins'))
- return 'notbitpay';
-
- // fullscreen disabled?
- if (Mage::getStoreConfig('payment/Bitcoins/fullscreen'))
- return 'disabled';
-
- include Mage::getBaseDir('lib').'/bitpay/bp_lib.php';
-
- $apiKey = Mage::getStoreConfig('payment/Bitcoins/api_key');
- $speed = Mage::getStoreConfig('payment/Bitcoins/speed');
-
- $quote = $this->getQuote();
- $quoteId = $quote->getId();
-
- if (Mage::getModel('Bitcoins/ipn')->GetQuotePaid($quoteId))
- return 'paid'; // quote's already paid, so don't show the iframe
-
-
- $options = array(
- 'currency' => $quote->getQuoteCurrencyCode(),
- 'fullNotifications' => 'true',
- 'notificationURL' => Mage::getUrl('bitpay_callback'),
- 'redirectURL' => Mage::getUrl('checkout/onepage/success'),
- 'transactionSpeed' => $speed,
- 'apiKey' => $apiKey,
- );
-
- // customer data
- $method = Mage::getModel('Bitcoins/paymentMethod');
- $options += $method->ExtractAddress($quote->getShippingAddress());
-
- // Mage doesn't round the total until saving and it can have more precision at this point which would be bad for later comparing records w/ bitpay. So round here to match what the price will be saved as:
- $price = round($quote->getGrandTotal(),4);
-
- //serialize info about the quote to detect changes
- $hash = $method->getQuoteHash($quoteId);
-
- Mage::log('invoicing for '.$price.' '.$quote->getQuoteCurrencyCode(), NULL, 'bitpay.log');
-
- $invoice = bpCreateInvoice($quoteId, $price, array('quoteId' => $quoteId, 'quoteHash' => $hash), $options);
-
- Mage::log($invoice, NULL, 'bitpay.log');
-
- if (array_key_exists('error', $invoice)) {
- Mage::log('Error creating bitpay invoice', null, 'bitpay.log');
- Mage::log($invoice['error'], null, 'bitpay.log');
- Mage::throwException("Error creating bit-pay invoice. Please try again or use another payment option.");
- return false;
- }
-
- return $invoice['url'].'&view=iframe';
- }
-
-}
diff --git a/app/code/community/Bitpay/Bitcoins/Model/Ipn.php b/app/code/community/Bitpay/Bitcoins/Model/Ipn.php
deleted file mode 100644
index 5ad9776..0000000
--- a/app/code/community/Bitpay/Bitcoins/Model/Ipn.php
+++ /dev/null
@@ -1,92 +0,0 @@
-_init('Bitcoins/ipn');
- return parent::_construct();
- }
-
- function Record($invoice) {
- return $this
- ->setQuoteId(isset($invoice['posData']['quoteId']) ? $invoice['posData']['quoteId'] : NULL)
- ->setOrderId(isset($invoice['posData']['orderId']) ? $invoice['posData']['orderId'] : NULL)
- ->setPosData(json_encode($invoice['posData']))
- ->setInvoiceId($invoice['id'])
- ->setUrl($invoice['url'])
- ->setStatus($invoice['status'])
- ->setBtcPrice($invoice['btcPrice'])
- ->setPrice($invoice['price'])
- ->setCurrency($invoice['currency'])
- ->setInvoiceTime(intval($invoice['invoiceTime']/1000.0))
- ->setExpirationTime(intval($invoice['expirationTime']/1000.0))
- ->setCurrentTime(intval($invoice['currentTime']/1000.0))
- ->save();
- }
-
- function GetStatusReceived($quoteId, $statuses) {
- if (!$quoteId)
- return false;
-
- $quote = Mage::getModel('sales/quote')->load($quoteId, 'entity_id');
-
- if (!$quote) {
- Mage::log('quote not found', NULL, 'bitpay.log');
- return false;
- }
-
- $quoteHash = Mage::getModel('Bitcoins/paymentMethod')->getQuoteHash($quoteId);
-
- if (!$quoteHash) {
- Mage::log('Could not find quote hash for quote '.$quoteId, NULL, 'bitpay.log');
- return false;
- }
-
- $collection = $this->getCollection()->AddFilter('quote_id', $quoteId);
-
- foreach($collection as $i) {
- if (in_array($i->getStatus(), $statuses)) {
- // check that quote data was not updated after IPN sent
- $posData = json_decode($i->getPosData());
-
- if (!$posData)
- continue;
-
- if ($quoteHash == $posData->quoteHash)
- return true;
- }
- }
-
- return false;
- }
-
- function GetQuotePaid($quoteId) {
- return $this->GetStatusReceived($quoteId, array('paid', 'confirmed', 'complete'));
- }
-
- function GetQuoteComplete($quoteId) {
- return $this->GetStatusReceived($quoteId, array('confirmed', 'complete'));
- }
-
-}
-
-?>
diff --git a/app/code/community/Bitpay/Bitcoins/Model/PaymentMethod.php b/app/code/community/Bitpay/Bitcoins/Model/PaymentMethod.php
deleted file mode 100644
index 1c7ec81..0000000
--- a/app/code/community/Bitpay/Bitcoins/Model/PaymentMethod.php
+++ /dev/null
@@ -1,267 +0,0 @@
-_canUseCheckout;
- }
-
- public function authorize(Varien_Object $payment, $amount) {
- if (!Mage::getStoreConfig('payment/Bitcoins/fullscreen'))
- return $this->CheckForPayment($payment);
- else
- return $this->CreateInvoiceAndRedirect($payment, $amount);
- }
-
- function CheckForPayment($payment) {
- $quoteId = $payment->getOrder()->getQuoteId();
- $ipn = Mage::getModel('Bitcoins/ipn');
-
- if (!$ipn->GetQuotePaid($quoteId)) {
- Mage::throwException("Order not paid for. Please pay first and then Place your Order.");
- } else if (!$ipn->GetQuoteComplete($quoteId)) {
- // order status will be PAYMENT_REVIEW instead of PROCESSING
- $payment->setIsTransactionPending(true);
- } else {
- $this->MarkOrderPaid($payment->getOrder());
- }
-
- return $this;
- }
-
- function MarkOrderPaid($order) {
- $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true)->save();
-
- if (!count($order->getInvoiceCollection())) {
- $invoice = $order->prepareInvoice()
- ->setTransactionId(1)
- ->addComment('Invoiced automatically by Bitpay/Bitcoins/controllers/IndexController.php')
- ->register()
- ->pay();
-
- $transactionSave = Mage::getModel('core/resource_transaction')
- ->addObject($invoice)
- ->addObject($invoice->getOrder());
-
- $transactionSave->save();
- $order->addStatusToHistory(Mage_Sales_Model_Order::STATE_COMPLETE);
- try {
- $order->sendNewOrderEmail();
- } catch (Exception $e) {
- Mage::logException($e);
- }
- } else {
- Mage::log('Count of InvoiceCollection was zero! Order not invoiced.', null, 'bitpay.log');
- }
- }
-
- function MarkOrderCancelled($order) {
- $order->setState(Mage_Sales_Model_Order::STATE_CANCELLED, true)->save();
-}
-
- // given Mage_Core_Model_Abstract, return api-friendly address
- function ExtractAddress($address) {
- $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)
- $options[$f] = substr($options[$f], 0, 100);
-
- return $options;
- }
-
- function CreateInvoiceAndRedirect($payment, $amount) {
- include Mage::getBaseDir('lib').'/bitpay/bp_lib.php';
-
- $apiKey = Mage::getStoreConfig('payment/Bitcoins/api_key');
- $speed = Mage::getStoreConfig('payment/Bitcoins/speed');
- $order = $payment->getOrder();
- $orderId = $order->getIncrementId();
-
- $options = array(
- 'currency' => $order->getBaseCurrencyCode(),
- 'buyerName' => $order->getCustomerFirstname().' '.$order->getCustomerLastname(),
- 'fullNotifications' => 'true',
- 'notificationURL' => Mage::getUrl('bitpay_callback'),
- 'redirectURL' => Mage::getUrl('checkout/onepage/success'),
- 'transactionSpeed' => $speed,
- 'apiKey' => $apiKey,
- );
-
- $options += $this->ExtractAddress($order->getShippingAddress());
- $invoice = bpCreateInvoice($orderId, $amount, array('orderId' => $orderId), $options);
- $payment->setIsTransactionPending(true); // status will be PAYMENT_REVIEW instead of PROCESSING
-
- if (array_key_exists('error', $invoice)) {
- Mage::log('Error creating bitpay invoice', null, 'bitpay.log');
- Mage::log($invoice['error'], null, 'bitpay.log');
- Mage::throwException("Error creating bit-pay invoice. Please try again or use another payment option.");
- } else {
- $invoiceId = Mage::getModel('sales/order_invoice_api')->create($orderId, array());
- Mage::getSingleton('customer/session')->setRedirectUrl($invoice['url']);
- }
-
- return $this;
- }
-
- public function getOrderPlaceRedirectUrl() {
- if (Mage::getStoreConfig('payment/Bitcoins/fullscreen'))
- return Mage::getSingleton('customer/session')->getRedirectUrl();
- else
- return '';
- }
-
- // computes a unique hash determined by the contents of the cart
- public function getQuoteHash($quoteId) {
- $quote = Mage::getModel('sales/quote')->load($quoteId, 'entity_id');
- if (!$quote) {
- Mage::log('getQuoteTimestamp: quote not found', NULL, 'bitpay.log');
- return false;
- }
-
- // encode items
- $items = $quote->getAllItems();
- $latest = NULL;
- $description = '';
-
- foreach($items as $i) {
- $description.= 'i'.$i->getItemId().'q'.$i->getQty();
- // could encode $i->getOptions() here but item ids are incremented if options are changed
- }
-
- $hash = base64_encode(hash_hmac('sha256', $description, $quoteId));
- $hash = substr($hash, 0, 30); // fit it in posData maxlen
-
- Mage::log("quote $quoteId descr $description hash $hash", NULL, 'bitpay.log');
-
- return $hash;
- }
-
-}
-?>
diff --git a/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn.php b/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn.php
deleted file mode 100644
index c192d53..0000000
--- a/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn.php
+++ /dev/null
@@ -1,28 +0,0 @@
-_init('Bitcoins/ipn', 'id');
- }
-}
-
-?>
diff --git a/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn/Collection.php b/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn/Collection.php
deleted file mode 100644
index 871918b..0000000
--- a/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn/Collection.php
+++ /dev/null
@@ -1,28 +0,0 @@
-_init('Bitcoins/ipn');
- }
-}
-
-?>
diff --git a/app/code/community/Bitpay/Bitcoins/Model/Source/Speed.php b/app/code/community/Bitpay/Bitcoins/Model/Source/Speed.php
deleted file mode 100644
index 145a1c6..0000000
--- a/app/code/community/Bitpay/Bitcoins/Model/Source/Speed.php
+++ /dev/null
@@ -1,40 +0,0 @@
- 'low',
- 'label' => 'Low',
- ),
- array(
- 'value' => 'medium',
- 'label' => 'Medium',
- ),
- array(
- 'value' => 'high',
- 'label' => 'High',
- ));
- }
-}
-
-?>
diff --git a/app/code/community/Bitpay/Bitcoins/controllers/IndexController.php b/app/code/community/Bitpay/Bitcoins/controllers/IndexController.php
deleted file mode 100644
index da32957..0000000
--- a/app/code/community/Bitpay/Bitcoins/controllers/IndexController.php
+++ /dev/null
@@ -1,73 +0,0 @@
-getRequest()->getParams();
- $quoteId = $params['quote'];
- $paid = Mage::getModel('Bitcoins/ipn')->GetQuotePaid($quoteId);
- print json_encode(array('paid' => $paid));
- exit();
- }
-
- // bitpay's IPN lands here
- public function indexAction() {
- require Mage::getBaseDir('lib').'/bitpay/bp_lib.php';
- Mage::log(file_get_contents('php://input'), null, 'bitpay.log');
- $apiKey = Mage::getStoreConfig('payment/Bitcoins/api_key');
- $invoice = bpVerifyNotification($apiKey);
-
- if (is_string($invoice))
- Mage::log("bitpay callback error: $invoice", null, 'bitpay.log');
- else {
- // get the order
- if (isset($invoice['posData']['quoteId'])) {
- $quoteId = $invoice['posData']['quoteId'];
- $order = Mage::getModel('sales/order')->load($quoteId, 'quote_id');
- } else {
- $orderId = $invoice['posData']['orderId'];
- $order = Mage::getModel('sales/order')->loadByIncrementId($orderId);
- }
-
- // save the ipn so that we can find it when the user clicks "Place Order"
- Mage::getModel('Bitcoins/ipn')->Record($invoice);
-
- // update the order if it exists already
- if ($order->getId())
- switch($invoice['status']) {
- case 'confirmed':
- case 'complete':
- $method = Mage::getModel('Bitcoins/paymentMethod');
- $method->MarkOrderPaid($order);
- break;
- case 'invalid':
- $method = Mage::getModel('Bitcoins/paymentMethod');
- $method->MarkOrderCancelled($order);
- break;
- }
-
- }
-
- }
-
-}
diff --git a/app/code/community/Bitpay/Bitcoins/etc/config.xml b/app/code/community/Bitpay/Bitcoins/etc/config.xml
deleted file mode 100644
index 0a83912..0000000
--- a/app/code/community/Bitpay/Bitcoins/etc/config.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
-
-
-
-
- Bitpay_Bitcoins
- bitpay_callback
-
-
-
-
-
-
- bitcoins.xml
-
-
-
-
-
-
-
-
- 1.1.0
-
-
-
-
-
-
- Bitpay_Bitcoins_Block
-
-
-
-
-
- Bitpay_Bitcoins_Model
- Bitcoins_resource
-
-
- Bitpay_Bitcoins_Model_Resource
-
-
-
bitpay_ipns
-
-
-
-
-
-
-
-
-
- Bitpay_Bitcoins
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1
- Bitcoins/paymentMethod
- Bitcoins
- low
- 0
- BTC, USD, EUR, GBP, AUD, BGN, BRL, CAD, CHF, CNY, CZK, DKK, HKD, HRK, HUF, IDR, ILS, INR, JPY, KRW, LTL, LVL, MXN, MYR, NOK, NZD, PHP, PLN, RON, RUB, SEK, SGD, THB, TRY, ZAR
- authorize
-
-
-
-
\ No newline at end of file
diff --git a/app/code/community/Bitpay/Bitcoins/etc/system.xml b/app/code/community/Bitpay/Bitcoins/etc/system.xml
deleted file mode 100644
index f610b95..0000000
--- a/app/code/community/Bitpay/Bitcoins/etc/system.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
- 670
- 1
- 1
- 0
-
-
-
- select
- adminhtml/system_config_source_yesno
- 0
- 1
- 1
- 0
-
-
-
-
- text
- 1
- 1
- 1
- 0
-
-
-
-
- select
- adminhtml/system_config_source_yesno
- 2
- 1
- 1
- 0
-
-
-
-
- text
- 3
- 1
- 1
- 0
-
-
-
-
- select
- Bitpay_Bitcoins_Model_Source_Speed
- 5
- 1
- 1
- 0
-
-
-
-
- text
- 6
- 1
- 1
- 0
-
-
-
-
- text
- 7
- 1
- 1
- 0
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-0.1.0-1.0.0.php b/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-0.1.0-1.0.0.php
deleted file mode 100644
index 6d486c2..0000000
--- a/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-0.1.0-1.0.0.php
+++ /dev/null
@@ -1,26 +0,0 @@
-startSetup();
-
-$installer->run("
-CREATE TABLE IF NOT EXISTS `{$installer->getTable('Bitcoins/ipn')}` (
- `id` int(10) unsigned NOT NULL auto_increment,
- `quote_id` int(10) unsigned default NULL,
- `order_id` int(10) unsigned default NULL,
- `invoice_id` varchar(200) NOT NULL,
- `url` varchar(400) NOT NULL,
- `status` varchar(20) NOT NULL,
- `btc_price` decimal(16,8) NOT NULL,
- `price` decimal(16,8) NOT NULL,
- `currency` varchar(10) NOT NULL,
- `invoice_time` int(11) unsigned NOT NULL,
- `expiration_time` int(11) unsigned NOT NULL,
- `current_time` int(11) unsigned NOT NULL,
- PRIMARY KEY (`id`),
- KEY `quote_id` (`quote_id`)
-) ENGINE=MyISAM DEFAULT CHARSET=latin1 ;
-
-");
-
-$installer->endSetup();
\ No newline at end of file
diff --git a/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-1.0.0-1.1.0.php b/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-1.0.0-1.1.0.php
deleted file mode 100644
index 470be03..0000000
--- a/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-1.0.0-1.1.0.php
+++ /dev/null
@@ -1,8 +0,0 @@
-startSetup();
-
-$installer->run("ALTER TABLE `{$installer->getTable('Bitcoins/ipn')}` ADD `pos_data` VARCHAR( 256 ) NOT NULL AFTER `url`;");
-
-$installer->endSetup();
\ No newline at end of file
diff --git a/app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Extension.php b/app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Extension.php
new file mode 100644
index 0000000..812836a
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Extension.php
@@ -0,0 +1,31 @@
+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.');
+ }
+
+ $phpExtension = $element->getFieldConfig()->php_extension;
+
+ if (true === in_array($phpExtension, get_loaded_extensions())) {
+ return 'Installed';
+ }
+
+ return 'Not Installed';
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Header.php b/app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Header.php
new file mode 100644
index 0000000..80ab46f
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Header.php
@@ -0,0 +1,33 @@
+debugData('[ERROR] In Bitpay_Core_Block_Adminhtml_System_Config_Form_Field_Header::render(): Missing or invalid $element parameter passed to function.');
+ throw new \Exception('In Bitpay_Core_Block_Adminhtml_System_Config_Form_Field_Header::render(): Missing or invalid $element parameter passed to function.');
+ }
+
+ return $this->toHtml();
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Block/Form/Bitpay.php b/app/code/community/Bitpay/Core/Block/Form/Bitpay.php
new file mode 100644
index 0000000..78bd184
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Block/Form/Bitpay.php
@@ -0,0 +1,17 @@
+setTemplate($payment_template);
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Block/Iframe.php b/app/code/community/Bitpay/Core/Block/Iframe.php
new file mode 100644
index 0000000..327894b
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Block/Iframe.php
@@ -0,0 +1,49 @@
+setTemplate('bitpay/iframe.phtml');
+ }
+
+ /**
+ * create an invoice and return the url so that iframe.phtml can display it
+ *
+ * @return string
+ */
+ public function getIframeUrl()
+ {
+
+ if (!($quote = Mage::getSingleton('checkout/session')->getQuote())
+ or !($payment = $quote->getPayment())
+ or !($paymentMethod = $payment->getMethod())
+ or ($paymentMethod !== 'bitpay')
+ or (Mage::getStoreConfig('payment/bitpay/fullscreen')))
+ {
+ return 'notbitpay';
+ }
+
+ \Mage::helper('bitpay')->registerAutoloader();
+
+ // fullscreen disabled?
+ if (Mage::getStoreConfig('payment/bitpay/fullscreen'))
+ {
+ return 'disabled';
+ }
+
+ if (\Mage::getModel('bitpay/ipn')->getQuotePaid($this->getQuote()->getId())) {
+ return 'paid'; // quote's already paid, so don't show the iframe
+ }
+
+ return 'bitpay';
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Block/Info.php b/app/code/community/Bitpay/Core/Block/Info.php
new file mode 100644
index 0000000..32761bb
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Block/Info.php
@@ -0,0 +1,37 @@
+setTemplate('bitpay/info/default.phtml');
+ }
+
+ public function getBitpayInvoiceUrl()
+ {
+ $order = $this->getInfo()->getOrder();
+
+ if (false === isset($order) || true === empty($order)) {
+ \Mage::helper('bitpay')->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)) {
+ \Mage::helper('bitpay')->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 = \Mage::getModel('bitpay/invoice')->load($incrementId, 'increment_id');
+
+ if (true === isset($bitpayInvoice) && false === empty($bitpayInvoice)) {
+ return $bitpayInvoice->getUrl();
+ }
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Helper/Data.php b/app/code/community/Bitpay/Core/Helper/Data.php
new file mode 100644
index 0000000..6d5680f
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Helper/Data.php
@@ -0,0 +1,398 @@
+getExtensionVersion(),
+ );
+ foreach($extendedDebugData as &$param)
+ {
+ $param = PHP_EOL . "\t\t" . $param;
+ }
+
+ if (true === isset($debugData) && false === empty($debugData)) {
+ \Mage::getModel('bitpay/method_bitcoin')->debugData($extendedDebugData);
+ \Mage::getModel('bitpay/method_bitcoin')->debugData($debugData);
+ }
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isDebug()
+ {
+ return (boolean) \Mage::getStoreConfig('payment/bitpay/debug');
+ }
+
+ /**
+ * Returns true if Transaction Speed has been configured
+ *
+ * @return boolean
+ */
+ public function hasTransactionSpeed()
+ {
+ $speed = \Mage::getStoreConfig('payment/bitpay/speed');
+
+ return !empty($speed);
+ }
+
+ /**
+ * Returns the URL where the IPN's are sent
+ *
+ * @return string
+ */
+ public function getNotificationUrl()
+ {
+ return \Mage::getUrl(\Mage::getStoreConfig('payment/bitpay/notification_url'));
+ }
+
+ /**
+ * Returns the URL where customers are redirected
+ *
+ * @return string
+ */
+ public function getRedirectUrl()
+ {
+ return \Mage::getUrl(\Mage::getStoreConfig('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)) {
+ $autoloader_filename = \Mage::getBaseDir('lib').'/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);
+ }
+
+ 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
+ $label = preg_replace('/[^a-zA-Z0-9 ]/', '', \Mage::app()->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,
+ )
+ );
+ $network = \Mage::getStoreConfig('payment/bitpay/network');
+ $this->debugData('[INFO] In Bitpay_Core_Helper_Data::sendPairingRequest(): using the network "' . $network . '".');
+
+ 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 = new \Mage_Core_Model_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())) {
+ $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(\Mage::getStoreConfig('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(\Mage::getStoreConfig('payment/bitpay/token'));
+
+ return $token;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLogFile()
+ {
+ return "payment_bitpay.log";
+ }
+
+ public function getExtensionVersion()
+ {
+ return (string) \Mage::getConfig()->getNode()->modules->Bitpay_Core->version;
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Config/PairingCode.php b/app/code/community/Bitpay/Core/Model/Config/PairingCode.php
new file mode 100644
index 0000000..4526fc2
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Config/PairingCode.php
@@ -0,0 +1,43 @@
+getValue());
+
+ if (true === empty($pairingCode)) {
+ return;
+ }
+
+ \Mage::helper('bitpay')->debugData('[INFO] In Bitpay_Core_Model_Config_PairingCode::save(): attempting to pair with BitPay with pairing code ' . $pairingCode);
+
+ try {
+ \Mage::helper('bitpay')->sendPairingRequest($pairingCode);
+ } catch (\Exception $e) {
+ \Mage::helper('bitpay')->debugData(sprintf('[ERROR] Exception thrown while calling the sendPairingRequest() function. The specific error message is: "%s"', $e->getMessage()));
+ \Mage::getSingleton('core/session')->addError('There was an error while trying to pair with BitPay using the pairing code '.$pairingCode.'. Please make sure you select the correct Network (Livenet vs Testnet) and try again with a new 7 character pairing code or enable debug mode and send the "payment_bitpay.log" file to support@bitpay.com for more help.');
+
+ return;
+ }
+
+ \Mage::getSingleton('core/session')->addSuccess('Pairing with BitPay was successful.');
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Invoice.php b/app/code/community/Bitpay/Core/Model/Invoice.php
new file mode 100644
index 0000000..c1c41b9
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Invoice.php
@@ -0,0 +1,74 @@
+_init('bitpay/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)
+ {
+ if (false === isset($invoice) || true === empty($invoice)) {
+ \Mage::helper('bitpay')->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.');
+ }
+
+ $this->addData(
+ array(
+ 'id' => $invoice->getId(),
+ 'url' => $invoice->getUrl(),
+ 'pos_data' => $invoice->getPosData(),
+ 'status' => $invoice->getStatus(),
+ 'price' => $invoice->getPrice(),
+ 'currency' => $invoice->getCurrency()->getCode(),
+ 'order_id' => $invoice->getOrderId(),
+ 'invoice_time' => intval(date_format($invoice->getInvoiceTime(), 'U')),
+ 'expiration_time' => intval(date_format($invoice->getExpirationTime(), 'U')),
+ 'current_time' => intval(date_format($invoice->getCurrentTime(), 'U')),
+ 'exception_status' => $invoice->getExceptionStatus(),
+ 'transactionCurrency' => $invoice->getTransactionCurrency()
+ )
+ );
+
+ 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)
+ {
+ if (false === isset($order) || true === empty($order)) {
+ \Mage::helper('bitpay')->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->addData(
+ array(
+ 'quote_id' => $order['quote_id'],
+ 'increment_id' => $order['increment_id'],
+ )
+ );
+
+ return $this;
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Ipn.php b/app/code/community/Bitpay/Core/Model/Ipn.php
new file mode 100644
index 0000000..428c9c9
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Ipn.php
@@ -0,0 +1,71 @@
+_init('bitpay/ipn');
+ }
+
+ /**
+ * @param string $quoteId
+ * @param array $statuses
+ *
+ * @return boolean
+ */
+ function GetStatusReceived($quoteId, $statuses)
+ {
+ if (!$quoteId)
+ {
+ return false;
+ }
+
+ $order = \Mage::getModel('sales/order')->load($quoteId, 'quote_id');
+
+ if (false === isset($order) || true === empty($order)) {
+ \Mage::helper('bitpay')->debugData('[DEBUG] Bitpay_Core_Model_Ipn::GetStatusReceived(), order not found for quoteId' . $quoteId);
+ return false;
+ }
+
+
+ $orderId = $order->getIncrementId();
+
+ if (false === isset($orderId) || true === empty($orderId)) {
+ \Mage::helper('bitpay')->debugData('[DEBUG] Bitpay_Core_Model_Ipn::GetStatusReceived(), orderId not found for quoteId' . $quoteId);
+ return false;
+ }
+
+ $collection = $this->getCollection();
+
+ foreach ($collection as $i)
+ {
+ if ($orderId == json_decode($i->pos_data, true)['orderId']) {
+ if (in_array($i->status, $statuses)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param string $quoteId
+ *
+ * @return boolean
+ */
+ function GetQuotePaid($quoteId)
+ {
+ return $this->GetStatusReceived($quoteId, array('paid', 'confirmed', 'complete'));
+ }
+
+}
diff --git a/app/code/community/Bitpay/Core/Model/Method/Bitcoin.php b/app/code/community/Bitpay/Core/Model/Method/Bitcoin.php
new file mode 100644
index 0000000..4d97d2b
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Method/Bitcoin.php
@@ -0,0 +1,363 @@
+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.');
+ }
+
+ // use the price in the currency of the store (not in the user selected currency)
+ $amount = $payment->getOrder()->getQuote()->getBaseGrandTotal();
+
+ $this->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->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.');
+ }
+
+ //add order id to the redirect url to match order in the checkout/onepage/success if bitpay invoice expired
+ $invoice->setRedirectUrl(\Mage::getUrl(\Mage::getStoreConfig('payment/bitpay/redirect_url') . '/order_id/'.$payment->getOrder()->getId()));
+
+ $invoice = $this->prepareInvoice($invoice, $payment, $amount);
+
+ try {
+ $bitpayInvoice = \Mage::helper('bitpay')->getBitpayClient()->createInvoice($invoice);
+ } catch (\Exception $e) {
+ $this->debugData('[ERROR] In Bitpay_Core_Model_Method_Bitcoin::authorize(): ' . $e->getMessage());
+ //display min invoice value error
+ if(strpos($e->getMessage(), 'Invoice price must be') !== FALSE)
+ {
+ \Mage::throwException($e->getMessage());
+ }
+ \Mage::throwException('In Bitpay_Core_Model_Method_Bitcoin::authorize(): Could not authorize transaction.');
+ }
+
+ self::$_redirectUrl = (Mage::getStoreConfig('payment/bitpay/fullscreen')) ? $bitpayInvoice->getUrl(): $bitpayInvoice->getUrl().'&view=iframe';
+
+ $this->debugData(
+ array(
+ '[INFO] BitPay Invoice created',
+ sprintf('Invoice URL: "%s"', $bitpayInvoice->getUrl()),
+ )
+ );
+
+ $quote = \Mage::getSingleton('checkout/session')->getQuote();
+ $order = \Mage::getModel('sales/order')->load($quote->getId(), 'quote_id');
+
+ // Save BitPay Invoice in database for reference
+ $mirrorInvoice = \Mage::getModel('bitpay/invoice')
+ ->prepareWithBitpayInvoice($bitpayInvoice)
+ ->prepareWithOrder(array('increment_id' => $order->getIncrementId(), 'quote_id'=> $quote->getId()))
+ ->save();
+
+ $this->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 = \Mage::getStoreConfig('payment/bitpay/token');
+
+ if (false === isset($token) || true === empty($token)) {
+ /**
+ * Merchant must goto their account and create a pairing code to
+ * enter in.
+ */
+ $this->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->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->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->debugData('[INFO] In Bitpay_Core_Model_Method_Bitcoin::fetchInvoice(): function called with id ' . $id);
+ }
+
+ \Mage::helper('bitpay')->registerAutoloader();
+
+ $client = \Mage::helper('bitpay')->getBitpayClient();
+
+ if (false === isset($client) || true === empty($client)) {
+ $this->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->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->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->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->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->debugData('[INFO] In Bitpay_Core_Model_Method_Bitcoin::extractAddress(): called with good address parameter, extracting now.');
+ }
+
+ $options = array();
+ $options['buyerEmail'] = $address->getEmail();
+
+ // trim to fit API specs
+ foreach (array('buyerEmail') as $f) {
+ if (true === isset($options[$f]) && strlen($options[$f]) > 100) {
+ $this->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->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()
+ {
+ \Mage::helper('bitpay')->registerAutoloader();
+
+ $invoice = new Bitpay\Invoice();
+
+ if (false === isset($invoice) || true === empty($invoice)) {
+ $this->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->debugData('[INFO] In Bitpay_Core_Model_Method_Bitcoin::initializeInvoice(): constructed new BitPay invoice object successfully.');
+ }
+
+ $invoice->setFullNotifications(true);
+ $invoice->setTransactionSpeed('medium');
+ $invoice->setNotificationUrl(\Mage::getUrl(\Mage::getStoreConfig('payment/bitpay/notification_url')));
+ $invoice->setRedirectUrl(\Mage::getUrl(\Mage::getStoreConfig('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->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->debugData('[INFO] In Bitpay_Core_Model_Method_Bitcoin::prepareInvoice(): entered function with good invoice, payment and amount parameters.');
+ }
+
+ $quote = Mage::getSingleton('checkout/session')->getQuote();
+ $order = \Mage::getModel('sales/order')->load($quote->getId(), 'quote_id');
+
+ $invoice->setOrderId($order->getIncrementId());
+ $invoice->setExtendedNotifications(true);
+ $invoice->setPosData(json_encode(array('orderId' => $order->getIncrementId())));
+
+ $invoice = $this->addCurrencyInfo($invoice, $order);
+ $invoice = $this->addPriceInfo($invoice, $amount);
+ $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->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->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->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.');
+ }
+
+ if (Mage::getStoreConfig('payment/bitpay/fullscreen')) {
+ $address = $order->getBillingAddress();
+ } else {
+ $quote = Mage::getSingleton('checkout/session')->getQuote();
+ $address = $quote->getBillingAddress();
+ }
+
+ $email = $address->getEmail();
+ if (null !== $email && '' !== $email) {
+ $buyer->setEmail($email);
+ }
+
+ $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->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->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->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());
+ //use the store currency code (not the customer selected currency)
+ $currency->setCode(\Mage::app()->getStore()->getBaseCurrencyCode());
+ $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->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->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->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/community/Bitpay/Core/Model/Mysql4/Invoice.php b/app/code/community/Bitpay/Core/Model/Mysql4/Invoice.php
new file mode 100644
index 0000000..b4ad214
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Mysql4/Invoice.php
@@ -0,0 +1,19 @@
+_init('bitpay/invoice', 'id');
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Mysql4/Invoice/Collection.php b/app/code/community/Bitpay/Core/Model/Mysql4/Invoice/Collection.php
new file mode 100644
index 0000000..a02ae15
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Mysql4/Invoice/Collection.php
@@ -0,0 +1,19 @@
+_init('bitpay/invoice');
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Mysql4/Ipn.php b/app/code/community/Bitpay/Core/Model/Mysql4/Ipn.php
new file mode 100644
index 0000000..b997234
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Mysql4/Ipn.php
@@ -0,0 +1,17 @@
+_init('bitpay/ipn', 'id');
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Mysql4/Ipn/Collection.php b/app/code/community/Bitpay/Core/Model/Mysql4/Ipn/Collection.php
new file mode 100644
index 0000000..37dda59
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Mysql4/Ipn/Collection.php
@@ -0,0 +1,16 @@
+_init('bitpay/ipn');
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Network.php b/app/code/community/Bitpay/Core/Model/Network.php
new file mode 100644
index 0000000..b5a4aaa
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Network.php
@@ -0,0 +1,25 @@
+ self::NETWORK_LIVENET, 'label' => \Mage::helper('bitpay')->__(ucwords(self::NETWORK_LIVENET))),
+ array('value' => self::NETWORK_TESTNET, 'label' => \Mage::helper('bitpay')->__(ucwords(self::NETWORK_TESTNET))),
+ );
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Observer.php b/app/code/community/Bitpay/Core/Model/Observer.php
new file mode 100644
index 0000000..1dac437
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Observer.php
@@ -0,0 +1,124 @@
+ getOrder();
+ $paymentCode = $order -> getPayment() -> getMethodInstance() -> getCode();
+ if ($paymentCode == 'bitpay') {
+ $order -> setState(Mage_Sales_Model_Order::STATE_NEW, true);
+ $order -> save();
+ }
+
+ // Mage::log('$order = $event->getOrder();' . $order -> getState());
+ }
+
+ /*
+ * Queries BitPay to update the order states in magento to make sure that
+ * open orders are closed/canceled if the BitPay invoice expires or becomes
+ * invalid.
+ */
+ public function updateOrderStates() {
+ $apiKey = \Mage::getStoreConfig('payment/bitpay/api_key');
+
+ if (false === isset($apiKey) || empty($apiKey)) {
+ \Mage::helper('bitpay') -> debugData('[INFO] Bitpay_Core_Model_Observer::updateOrderStates() could not start job to update the order states because the API key was not set.');
+ return;
+ } else {
+ \Mage::helper('bitpay') -> debugData('[INFO] Bitpay_Core_Model_Observer::updateOrderStates() started job to query BitPay to update the existing order states.');
+ }
+
+ /*
+ * Get all of the orders that are open and have not received an IPN for
+ * complete, expired, or invalid.
+ */
+ $orders = \Mage::getModel('bitpay/ipn') -> getOpenOrders();
+
+ if (false === isset($orders) || empty($orders)) {
+ \Mage::helper('bitpay') -> debugData('[INFO] Bitpay_Core_Model_Observer::updateOrderStates() could not retrieve the open orders.');
+ return;
+ } else {
+ \Mage::helper('bitpay') -> debugData('[INFO] Bitpay_Core_Model_Observer::updateOrderStates() successfully retrieved existing open orders.');
+ }
+
+ /*
+ * Get all orders that have been paid using bitpay and
+ * are not complete/closed/etc
+ */
+ foreach ($orders as $order) {
+ /*
+ * Query BitPay with the invoice ID to get the status. We must take
+ * care not to anger the API limiting gods and disable our access
+ * to the API.
+ */
+ $status = null;
+
+ // TODO:
+ // Does the order need to be updated?
+ // Yes? Update Order Status
+ // No? continue
+ }
+
+ \Mage::helper('bitpay') -> debugData('[INFO] Bitpay_Core_Model_Observer::updateOrderStates() order status update job finished.');
+ }
+
+ /**
+ * Method that is called via the magento cron to update orders if the
+ * invoice has expired
+ */
+ public function cleanExpired() {
+ \Mage::helper('bitpay') -> debugData('[INFO] Bitpay_Core_Model_Observer::cleanExpired() called.');
+ \Mage::helper('bitpay') -> cleanExpired();
+ }
+
+ /**
+ * Event Hook: checkout_onepage_controller_success_action
+ * @param $observer Varien_Event_Observer
+ */
+ public function redirectToCartIfExpired(Varien_Event_Observer $observer)
+ {
+ if ($observer->getEvent()->getName() == 'checkout_onepage_controller_success_action')
+ {
+ $lastOrderId = null;
+ foreach(\Mage::app()->getRequest()->getParams() as $key=>$value)
+ {
+ if($key == 'order_id')
+ $lastOrderId = $value;
+ }
+
+ if($lastOrderId != null)
+ {
+ //get order
+ $order = \Mage::getModel('sales/order')->load($lastOrderId);
+ if (false === isset($order) || true === empty($order)) {
+ \Mage::helper('bitpay')->debugData('[ERROR] In Bitpay_Core_Model_Observer::redirectToCartIfExpired(), Invalid Order ID received.');
+ return;
+ }
+ //check if order is pending
+ if($order->getStatus() != 'pending')
+ {
+ return;
+ }
+
+ //check if invoice for order exist in bitpay_invoices table
+ $bitpayInvoice = \Mage::getModel('bitpay/invoice')->load($order->getIncrementId(), 'increment_id');
+ $bitpayInvoiceData = $bitpayInvoice->getData();
+ //if is empty or not is array abort
+ if(!is_array($bitpayInvoiceData) || is_array($bitpayInvoiceData) && empty($bitpayInvoiceData))
+ return;
+
+ //check if bitpay invoice id expired
+ $invoiceExpirationTime = $bitpayInvoiceData['expiration_time'];
+ if($invoiceExpirationTime < strtotime('now'))
+ {
+ $failure_url = \Mage::getUrl(\Mage::getStoreConfig('payment/bitpay/failure_url'));
+ \Mage::app()->getResponse()->setRedirect($failure_url)->sendResponse();
+ }
+ }
+ }
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Order/Payment.php b/app/code/community/Bitpay/Core/Model/Order/Payment.php
new file mode 100644
index 0000000..fd5a945
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Order/Payment.php
@@ -0,0 +1,273 @@
+setShouldCloseParentTransaction(false);
+ $isSameCurrency = $this->_isSameCurrency();
+ if (!$isSameCurrency || !$this->_isCaptureFinal($amount)) {
+ $this->setIsFraudDetected(true);
+ }
+
+ // update totals
+ $amount = $this->_formatAmount($amount, true);
+ $this->setBaseAmountAuthorized($amount);
+
+ // do authorization
+ $order = $this->getOrder();
+ $payment = $order -> getPayment();
+ $paymentMethodCode = $payment -> getMethodInstance() -> getCode();
+ if ($paymentMethodCode != 'bitpay'){
+ $state = Mage_Sales_Model_Order::STATE_PROCESSING;
+ }
+ else {
+ $state = Mage_Sales_Model_Order::STATE_NEW;
+ }
+ $status = true;
+ if ($isOnline) {
+ // invoke authorization on gateway
+ $this->getMethodInstance()->setStore($order->getStoreId())->authorize($this, $amount);
+ }
+
+ // similar logic of "payment review" order as in capturing
+ if ($this->getIsTransactionPending()) {
+ $message = Mage::helper('sales')->__('Authorizing amount of %s is pending approval on gateway.', $this->_formatPrice($amount));
+ $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
+ if ($this->getIsFraudDetected()) {
+ $status = Mage_Sales_Model_Order::STATUS_FRAUD;
+ }
+ } else {
+ if ($this->getIsFraudDetected()) {
+ $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
+ $message = Mage::helper('sales')->__('Order is suspended as its authorizing amount %s is suspected to be fraudulent.', $this->_formatPrice($amount, $this->getCurrencyCode()));
+ $status = Mage_Sales_Model_Order::STATUS_FRAUD;
+ } else {
+ $message = Mage::helper('sales')->__('Authorized amounta of %s.', $this->_formatPrice($amount));
+ }
+ }
+
+ // update transactions, order state and add comments
+ $transaction = $this->_addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_AUTH);
+ if ($order->isNominal()) {
+ $message = $this->_prependMessage(Mage::helper('sales')->__('Nominal order registered.'));
+ } else {
+ $message = $this->_prependMessage($message);
+ $message = $this->_appendTransactionToMessage($transaction, $message);
+ }
+ $order->setState($state, $status, $message);
+
+ return $this;
+ }
+
+ public function registerCaptureNotification($amount, $skipFraudDetection = false)
+ {
+ $this->_generateTransactionId(Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE,
+ $this->getAuthorizationTransaction()
+ );
+
+ $order = $this->getOrder();
+ $amount = (float)$amount;
+ $invoice = $this->_getInvoiceForTransactionId($this->getTransactionId());
+
+ // register new capture
+ if (!$invoice) {
+ $isSameCurrency = $this->_isSameCurrency();
+ if ($isSameCurrency && $this->_isCaptureFinal($amount)) {
+ $invoice = $order->prepareInvoice()->register();
+ $order->addRelatedObject($invoice);
+ $this->setCreatedInvoice($invoice);
+ } else {
+ if (!$skipFraudDetection || !$isSameCurrency) {
+ $this->setIsFraudDetected(true);
+ }
+ $this->_updateTotals(array('base_amount_paid_online' => $amount));
+ }
+ }
+
+ $status = true;
+ if ($this->getIsTransactionPending()) {
+ $message = Mage::helper('sales')->__('Capturing amount of %s is pending approval on gateway.', $this->_formatPrice($amount));
+ $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
+ if ($this->getIsFraudDetected()) {
+ $message = Mage::helper('sales')->__('Order is suspended as its capture amount %s is suspected to be fraudulent.', $this->_formatPrice($amount, $this->getCurrencyCode()));
+ $status = Mage_Sales_Model_Order::STATUS_FRAUD;
+ }
+ } else {
+ $message = Mage::helper('sales')->__('Registered notification about captured amount of %s.', $this->_formatPrice($amount));
+ $payment = $order -> getPayment();
+ $paymentMethodCode = $payment -> getMethodInstance() -> getCode();
+ if ($paymentMethodCode != 'bitpay'){
+ $state = Mage_Sales_Model_Order::STATE_PROCESSING;
+ }
+ else {
+ $state = Mage_Sales_Model_Order::STATE_NEW;
+ }
+
+ if ($this->getIsFraudDetected()) {
+ $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW;
+ $message = Mage::helper('sales')->__('Order is suspended as its capture amount %s is suspected to be fraudulent.', $this->_formatPrice($amount, $this->getCurrencyCode()));
+ $status = Mage_Sales_Model_Order::STATUS_FRAUD;
+ }
+ // register capture for an existing invoice
+ if ($invoice && Mage_Sales_Model_Order_Invoice::STATE_OPEN == $invoice->getState()) {
+ $invoice->pay();
+ $this->_updateTotals(array('base_amount_paid_online' => $amount));
+ $order->addRelatedObject($invoice);
+ }
+ }
+
+ $transaction = $this->_addTransaction(Mage_Sales_Model_Order_Payment_Transaction::TYPE_CAPTURE, $invoice, true);
+ $message = $this->_prependMessage($message);
+ $message = $this->_appendTransactionToMessage($transaction, $message);
+ $order->setState($state, $status, $message);
+ return $this;
+ }
+
+
+ public function place()
+ {
+ Mage::dispatchEvent('sales_order_payment_place_start', array('payment' => $this));
+ $order = $this->getOrder();
+
+ $this->setAmountOrdered($order->getTotalDue());
+ $this->setBaseAmountOrdered($order->getBaseTotalDue());
+ $this->setShippingAmount($order->getShippingAmount());
+ $this->setBaseShippingAmount($order->getBaseShippingAmount());
+
+ $methodInstance = $this->getMethodInstance();
+ $methodInstance->setStore($order->getStoreId());
+ $orderState = Mage_Sales_Model_Order::STATE_NEW;
+ $stateObject = new Varien_Object();
+
+ /**
+ * Do order payment validation on payment method level
+ */
+ $methodInstance->validate();
+ $action = $methodInstance->getConfigPaymentAction();
+ if ($action) {
+ if ($methodInstance->isInitializeNeeded()) {
+ /**
+ * For method initialization we have to use original config value for payment action
+ */
+ $methodInstance->initialize($methodInstance->getConfigData('payment_action'), $stateObject);
+ } else {
+ $payment = $order -> getPayment();
+ $paymentMethodCode = $payment -> getMethodInstance() -> getCode();
+ if ($paymentMethodCode != 'bitpay'){
+ $orderState = Mage_Sales_Model_Order::STATE_PROCESSING;
+ }
+ else {
+ $orderState = Mage_Sales_Model_Order::STATE_NEW;
+ }
+
+ switch ($action) {
+ case Mage_Payment_Model_Method_Abstract::ACTION_ORDER:
+ $this->_order($order->getBaseTotalDue());
+ break;
+ case Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE:
+ $this->_authorize(true, $order->getBaseTotalDue()); // base amount will be set inside
+ $this->setAmountAuthorized($order->getTotalDue());
+ break;
+ case Mage_Payment_Model_Method_Abstract::ACTION_AUTHORIZE_CAPTURE:
+ $this->setAmountAuthorized($order->getTotalDue());
+ $this->setBaseAmountAuthorized($order->getBaseTotalDue());
+ $this->capture(null);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ $this->_createBillingAgreement();
+
+ $orderIsNotified = null;
+ if ($stateObject->getState() && $stateObject->getStatus()) {
+ $orderState = $stateObject->getState();
+ $orderStatus = $stateObject->getStatus();
+ $orderIsNotified = $stateObject->getIsNotified();
+ } else {
+ $orderStatus = $methodInstance->getConfigData('order_status');
+ if (!$orderStatus) {
+ $orderStatus = $order->getConfig()->getStateDefaultStatus($orderState);
+ } else {
+ // check if $orderStatus has assigned a state
+ if(method_exists($order->getConfig(), 'getStatusStates'))
+ {
+ $states = $order->getConfig()->getStatusStates($orderStatus);
+ }
+ else
+ {
+ $states = $this->getStatusStates($orderStatus);
+ }
+
+ if (count($states) == 0) {
+ $orderStatus = $order->getConfig()->getStateDefaultStatus($orderState);
+ }
+ }
+ }
+ $isCustomerNotified = (null !== $orderIsNotified) ? $orderIsNotified : $order->getCustomerNoteNotify();
+ $message = $order->getCustomerNote();
+
+ // add message if order was put into review during authorization or capture
+ if ($order->getState() == Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW) {
+ if ($message) {
+ $order->addStatusToHistory($order->getStatus(), $message, $isCustomerNotified);
+ }
+ } elseif ($order->getState() && ($orderStatus !== $order->getStatus() || $message)) {
+ // add message to history if order state already declared
+ $order->setState($orderState, $orderStatus, $message, $isCustomerNotified);
+ } elseif (($order->getState() != $orderState) || ($order->getStatus() != $orderStatus) || $message) {
+ // set order state
+ $order->setState($orderState, $orderStatus, $message, $isCustomerNotified);
+ }
+
+ Mage::dispatchEvent('sales_order_payment_place_end', array('payment' => $this));
+
+ return $this;
+ }
+
+ /**
+ * Check whether payment currency corresponds to order currency
+ *
+ * @return bool
+ */
+ public function _isSameCurrency()
+ {
+ return !$this->getCurrencyCode() || $this->getCurrencyCode() == $this->getOrder()->getBaseCurrencyCode();
+ }
+
+ /**
+ * Retrieve state available for status
+ * Get all assigned states for specified status
+ *
+ * @param string $status
+ * @return array
+ */
+
+ private function getStatusStates($status)
+ {
+ $states = array();
+ $collectionObj = Mage::getResourceModel('sales/order_status_collection');
+ $collection = $this->addStatusFilter($collectionObj, $status);
+
+ foreach ($collection as $state) {
+ $states[] = $state;
+ }
+ return $states;
+ }
+ /**
+ * add status code filter to collection
+ *
+ * @param object Mage_Sales_Model_Resource_Order_Status_Collection
+ * @param string $status
+ * @return Mage_Sales_Model_Resource_Order_Status_Collection
+ */
+ private function addStatusFilter($collectionObj, $status)
+ {
+ $collectionObj->joinStates();
+ $collectionObj->getSelect()->where('state_table.status=?', $status);
+ return $collectionObj;
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Resource/Ipn/Collection.php b/app/code/community/Bitpay/Core/Model/Resource/Ipn/Collection.php
new file mode 100644
index 0000000..fbde0e4
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Resource/Ipn/Collection.php
@@ -0,0 +1,44 @@
+_init('bitpay/ipn');
+ }
+
+ public function delete()
+ {
+ foreach ($this->getItems() as $item) {
+ $item->delete();
+ }
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Resource/Mysql4/Setup.php b/app/code/community/Bitpay/Core/Model/Resource/Mysql4/Setup.php
new file mode 100644
index 0000000..b91a0fc
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Resource/Mysql4/Setup.php
@@ -0,0 +1,11 @@
+toOptionArray();
+
+ $allowCountry = array();
+ foreach($country as $v)
+ {
+ if($v['value'] != '' && $v['value'] != 'SY' && $v['value'] != 'IR' && $v['value'] != 'KP' && $v['value'] != 'SD' && $v['value'] != 'CU')
+ {
+ $allowCountry[] = $v;
+ }
+ }
+
+ return $allowCountry;
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/Status.php b/app/code/community/Bitpay/Core/Model/Status.php
new file mode 100644
index 0000000..10cecad
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/Status.php
@@ -0,0 +1,30 @@
+ self::STATUS_NEW, 'label' => \Mage::helper('bitpay')->__(ucwords(self::STATUS_NEW))),
+ array('value' => self::STATUS_PAID, 'label' => \Mage::helper('bitpay')->__(ucwords(self::STATUS_PAID))),
+ array('value' => self::STATUS_CONFIRMED, 'label' => \Mage::helper('bitpay')->__(ucwords(self::STATUS_CONFIRMED))),
+ array('value' => self::STATUS_COMPLETE, 'label' => \Mage::helper('bitpay')->__(ucwords(self::STATUS_COMPLETE))),
+ array('value' => self::STATUS_EXPIRED, 'label' => \Mage::helper('bitpay')->__(ucwords(self::STATUS_EXPIRED))),
+ array('value' => self::STATUS_INVALID, 'label' => \Mage::helper('bitpay')->__(ucwords(self::STATUS_INVALID))),
+ );
+ }
+}
diff --git a/app/code/community/Bitpay/Core/Model/TransactionSpeed.php b/app/code/community/Bitpay/Core/Model/TransactionSpeed.php
new file mode 100644
index 0000000..a23f752
--- /dev/null
+++ b/app/code/community/Bitpay/Core/Model/TransactionSpeed.php
@@ -0,0 +1,24 @@
+ self::SPEED_LOW, 'label' => \Mage::helper('bitpay')->__(ucwords(self::SPEED_LOW))),
+ array('value' => self::SPEED_MEDIUM, 'label' => \Mage::helper('bitpay')->__(ucwords(self::SPEED_MEDIUM))),
+ array('value' => self::SPEED_HIGH, 'label' => \Mage::helper('bitpay')->__(ucwords(self::SPEED_HIGH))),
+ );
+ }
+}
diff --git a/app/code/community/Bitpay/Core/controllers/IndexController.php b/app/code/community/Bitpay/Core/controllers/IndexController.php
new file mode 100644
index 0000000..f954ddb
--- /dev/null
+++ b/app/code/community/Bitpay/Core/controllers/IndexController.php
@@ -0,0 +1,34 @@
+registerAutoloader();
+ \Mage::helper('bitpay')->debugData($params);
+
+ $params = $this->getRequest()->getParams();
+ $quoteId = $params['quote'];
+
+ if (!is_numeric($quoteId))
+ {
+ return $this->getResponse()->setHttpResponseCode(400);
+ }
+
+ $paid = \Mage::getModel('bitpay/ipn')->GetQuotePaid($quoteId);
+ $this->loadLayout();
+ $this->getResponse()->setHeader('Content-type', 'application/json');
+
+ return $this->getResponse()->setBody(json_encode(array('paid' => $paid)));
+ }
+}
diff --git a/app/code/community/Bitpay/Core/controllers/IpnController.php b/app/code/community/Bitpay/Core/controllers/IpnController.php
new file mode 100644
index 0000000..7c58d60
--- /dev/null
+++ b/app/code/community/Bitpay/Core/controllers/IpnController.php
@@ -0,0 +1,196 @@
+debugData('[ERROR] In Bitpay_Core_IpnController::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.');
+ }
+
+ \Mage::helper('bitpay')->registerAutoloader();
+
+ \Mage::helper('bitpay')->debugData(array(sprintf('[INFO] In Bitpay_Core_IpnController::indexAction(), Incoming IPN message from BitPay: '),$raw_post_data,));
+
+ // Magento doesn't seem to have a way to get the Request body
+ $ipn = json_decode($raw_post_data);
+
+ if(isset($ipn->data))
+ {
+ $ipn = $ipn->data;
+ }
+
+ if (true === empty($ipn)) {
+ \Mage::helper('bitpay')->debugData('[ERROR] In Bitpay_Core_IpnController::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)) {
+ \Mage::helper('bitpay')->debugData(sprintf('[ERROR] In Bitpay_Core_IpnController::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();
+
+ \Mage::helper('bitpay')->debugData($ipn);
+
+ // Log IPN
+ $mageIpn = \Mage::getModel('bitpay/ipn')->addData(
+ array(
+ '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 : '',
+ '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) : '',
+ 'exception_status' => isset($ipn->exceptionStatus) ? $ipn->exceptionStatus : '',
+ 'transactionCurrency' => isset($ipn->transactionCurrency) ? $ipn->transactionCurrency : ''
+ )
+ )->save();
+
+
+ // Order isn't being created for iframe...
+ if (isset($ipn->posData->orderId)) {
+ $order = \Mage::getModel('sales/order')->loadByIncrementId($ipn->posData->orderId);
+ } else {
+ $order = \Mage::getModel('sales/order')->load($ipn->posData->quoteId, 'quote_id');
+ }
+
+ if (false === isset($order) || true === empty($order)) {
+ \Mage::helper('bitpay')->debugData('[ERROR] In Bitpay_Core_IpnController::indexAction(), Invalid Bitpay IPN received.');
+ \Mage::throwException('Invalid Bitpay IPN received.');
+ }
+
+ $orderId = $order->getId();
+ if (false === isset($orderId) || true === empty($orderId)) {
+ \Mage::helper('bitpay')->debugData('[ERROR] In Bitpay_Core_IpnController::indexAction(), Invalid Bitpay IPN received.');
+ \Mage::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 = \Mage::getModel('bitpay/method_bitcoin')->fetchInvoice($ipn->id);
+
+ if ($invoice->getStatus() == 'paid')
+ {
+ $invoice_magento = \Mage::getModel('bitpay/invoice')->load($ipn->id);
+ $quoteID_magento = $invoice_magento->getData('quote_id');
+ $quoteData = Mage::getModel('sales/quote')->setStoreId(Mage::app()->getStore('default')->getId())->load($quoteID_magento);
+ $quoteData->setIsActive(0)->save();
+ }
+
+ if (false === isset($invoice) || true === empty($invoice)) {
+ \Mage::helper('bitpay')->debugData('[ERROR] In Bitpay_Core_IpnController::indexAction(), Could not retrieve the invoice details for the ipn ID of ' . $ipn->id);
+ \Mage::throwException('Could not retrieve the invoice details for the ipn ID of ' . $ipn->id);
+ }
+
+ // Does the status match?
+ /* if ($invoice->getStatus() != $ipn->status) {
+ \Mage::getModel('bitpay/method_bitcoin')->debugData('[ERROR] In Bitpay_Core_IpnController::indexAction(), IPN status and status from BitPay are different. Rejecting this IPN!');
+ \Mage::throwException('There was an error processing the IPN - statuses are different. Rejecting this IPN!');
+ }*/
+
+ // Does the price match?
+ if ($invoice->getPrice() != $ipn->price) {
+ \Mage::getModel('bitpay/method_bitcoin')>debugData('[ERROR] In Bitpay_Core_IpnController::indexAction(), IPN price and invoice price are different. Rejecting this IPN!');
+ \Mage::throwException('There was an error processing the IPN - invoice price does not match the IPN price. Rejecting this IPN!');
+ }
+
+ // use state as defined by Merchant
+ $state = \Mage::getStoreConfig(sprintf('payment/bitpay/invoice_%s', $invoice->getStatus()));
+
+ if (false === isset($state) || true === empty($state)) {
+ \Mage::helper('bitpay')->debugData('[ERROR] In Bitpay_Core_IpnController::indexAction(), Could not retrieve the defined state parameter to update this order to in the Bitpay IPN controller.');
+ \Mage::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 Mage_Sales_Model_Order::STATE_CANCELED:
+ case Mage_Sales_Model_Order::STATUS_FRAUD:
+ case Mage_Sales_Model_Order::STATE_CLOSED:
+ case Mage_Sales_Model_Order::STATE_COMPLETE:
+ case Mage_Sales_Model_Order::STATE_HOLDED:
+ // Do not Update
+ break;
+ case Mage_Sales_Model_Order::STATE_PENDING_PAYMENT:
+ case Mage_Sales_Model_Order::STATE_PROCESSING:
+ default:
+ $order->addStatusToHistory(
+ $state,
+ sprintf('[INFO] In Bitpay_Core_IpnController::indexAction(), Incoming IPN status "%s" updated order state to "%s"', $invoice->getStatus(), $state)
+ )->save();
+ break;
+ }
+
+ if($ipn->status == 'expired')
+ {
+ $order->cancel();
+ $order->setState(Mage_Sales_Model_Order::STATE_CANCELED, true, 'Cancel Transaction.');
+ $order->setStatus("canceled");
+ $order->save();
+ }
+
+ if ($ipn->status === 'paid') {
+ \Mage::helper('bitpay')->debugData('[INFO] Receiving paid IPN, creating invoice for order.');
+ // Create a Magento invoice for the order when the 'paid' notification comes in
+ if ($payments = $order->getPaymentsCollection())
+ {
+ $payment = count($payments->getItems())>0 ? end($payments->getItems()) : \Mage::getModel('sales/order_payment')->setOrder($order);
+ }
+
+ if (true === isset($payment) && false === empty($payment)) {
+ $payment->registerCaptureNotification($invoice->getPrice());
+ $order->setPayment($payment);
+
+ $order_confirmation = \Mage::getStoreConfig('payment/bitpay/order_confirmation');
+ if($order_confirmation == '1') {
+ if (!$order->getEmailSent()) {
+ \Mage::helper('bitpay')->debugData('[INFO] In Bitpay_Core_IpnController::indexAction(), Order email not sent so I am calling $order->sendNewOrderEmail() now...');
+ $order->sendNewOrderEmail();
+ }
+ else
+ {
+ \Mage::helper('bitpay')->debugData('[INFO] Plugin configured to send order confirmation, but order confirmation already sent.');
+ }
+ }
+ else {
+ \Mage::helper('bitpay')->debugData('[INFO] Plugin configured to not send order confirmation.');
+ }
+ $order->save();
+ }
+ else {
+ \Mage::helper('bitpay')->debugData('[ERROR] In Bitpay_Core_IpnController::indexAction(), Could not create a payment object in the Bitpay IPN controller.');
+ \Mage::throwException('Could not create a payment object in the Bitpay IPN controller.');
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/code/community/Bitpay/Core/etc/adminhtml.xml b/app/code/community/Bitpay/Core/etc/adminhtml.xml
new file mode 100644
index 0000000..362344d
--- /dev/null
+++ b/app/code/community/Bitpay/Core/etc/adminhtml.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+ BitPay
+
+
+
+
+
+
+ BitPay Settings
+
+
+
+
+
+
+
+
+
+
+
+
+ bitpay.xml
+
+
+
+
+
+
+
+ Bitpay_Core.csv
+
+
+
+
+
diff --git a/app/code/community/Bitpay/Core/etc/config.xml b/app/code/community/Bitpay/Core/etc/config.xml
new file mode 100644
index 0000000..d8406cc
--- /dev/null
+++ b/app/code/community/Bitpay/Core/etc/config.xml
@@ -0,0 +1,154 @@
+
+
+
+
+
+ 2.1.20
+
+
+
+
+
+
+
+ Bitpay_Core
+ bitpay
+
+
+
+
+
+
+ bitpay.xml
+
+
+
+
+
+
+
+ Bitpay_Core.csv
+
+
+
+
+
+
+
+
+
+
+ Bitpay_Core_Adminhtml
+
+
+
+
+
+
+
+
+ Bitpay_Core_Block
+
+
+
+
+ Bitpay_Core_Helper
+
+
+
+
+ Bitpay_Core_Model
+ bitpay_mysql4
+
+
+
+ Bitpay_Core_Model_Mysql4
+
+
+
';
+}
+
+?>
diff --git a/app/design/adminhtml/default/default/template/bitpay/system/config/field/header.phtml b/app/design/adminhtml/default/default/template/bitpay/system/config/field/header.phtml
new file mode 100644
index 0000000..6c74aed
--- /dev/null
+++ b/app/design/adminhtml/default/default/template/bitpay/system/config/field/header.phtml
@@ -0,0 +1,18 @@
+' .
+ '' .
+ 'Support' .
+ 'Sign Up' .
+ 'Login' .
+ '';
+
+?>
diff --git a/app/design/frontend/base/default/layout/bitcoins.xml b/app/design/frontend/base/default/layout/bitcoins.xml
deleted file mode 100644
index e2ae5ad..0000000
--- a/app/design/frontend/base/default/layout/bitcoins.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/design/frontend/base/default/layout/bitpay.xml b/app/design/frontend/base/default/layout/bitpay.xml
new file mode 100644
index 0000000..f90422b
--- /dev/null
+++ b/app/design/frontend/base/default/layout/bitpay.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/design/frontend/base/default/template/bitcoins/iframe.phtml b/app/design/frontend/base/default/template/bitcoins/iframe.phtml
deleted file mode 100644
index 7a18f4b..0000000
--- a/app/design/frontend/base/default/template/bitcoins/iframe.phtml
+++ /dev/null
@@ -1,34 +0,0 @@
-GetIframeUrl();
-switch($url){
- case 'notbitpay': break; // customer is using another payment method
- case 'paid':
- echo 'Order payment received. Place Order to complete.'; break;
- case 'disabled':
- echo 'Please click Place Order to continue to bitpay.com.'; break;
- case false:
- echo 'Error creating invoice. Please try again or try another payment solution.'; break;
- default:
- echo ''; break;
-}
-$quoteId = $this->GetQuoteId();
-?>
-
-getRequest();
-$url = Mage::getUrl('bitpay_callback/index/checkForPayment/');
-if ($request->getScheme() == 'https')
- $url = str_replace('http://', 'https://', $url);
-?>
-
diff --git a/app/design/frontend/base/default/template/bitpay/form/bitpay.phtml b/app/design/frontend/base/default/template/bitpay/form/bitpay.phtml
new file mode 100644
index 0000000..87091e0
--- /dev/null
+++ b/app/design/frontend/base/default/template/bitpay/form/bitpay.phtml
@@ -0,0 +1,17 @@
+getMethodCode();
+if (Mage::getStoreConfig('payment/bitpay/fullscreen')) {
+ echo '
' .
+ '
You will be transfered to BitPay to complete your purchase when using this payment method.
' .
+ '
';
+}
+?>
diff --git a/app/design/frontend/base/default/template/bitpay/iframe.phtml b/app/design/frontend/base/default/template/bitpay/iframe.phtml
new file mode 100644
index 0000000..50143d8
--- /dev/null
+++ b/app/design/frontend/base/default/template/bitpay/iframe.phtml
@@ -0,0 +1,92 @@
+getIframeUrl();
+switch($url) {
+ case 'notbitpay':
+ break; // customer is using another payment method
+ case 'paid':
+ echo 'Order payment received. Place Order to complete.';
+ break;
+ case 'disabled':
+ echo 'Please click Place Order to continue to bitpay.com.';
+ break;
+ case false:
+ echo 'Error creating invoice. Please try again or try another payment solution.';
+ break;
+ default:
+ echo '';
+ break;
+}
+$quoteId = $this->getQuote()->getId();
+$request = Mage::app()->getRequest();
+$url = Mage::getUrl('bitpay/index/index/');
+if ($request->getScheme() == 'https') {
+ $url = str_replace('http://', 'https://', $url);
+}
+
+?>
+
+
+
diff --git a/app/design/frontend/base/default/template/bitpay/info/default.phtml b/app/design/frontend/base/default/template/bitpay/info/default.phtml
new file mode 100644
index 0000000..905db79
--- /dev/null
+++ b/app/design/frontend/base/default/template/bitpay/info/default.phtml
@@ -0,0 +1,10 @@
+' . $this->escapeHtml($this->getMethod()->getTitle()) . '';
+
+echo $this->getChildHtml();
+?>
diff --git a/app/design/frontend/base/default/template/bitpay/json.phtml b/app/design/frontend/base/default/template/bitpay/json.phtml
new file mode 100644
index 0000000..f6802fd
--- /dev/null
+++ b/app/design/frontend/base/default/template/bitpay/json.phtml
@@ -0,0 +1,9 @@
+
+{
+ "paid": isPaid(); ?>
+}
diff --git a/app/etc/modules/Bitpay_Bitcoins.xml b/app/etc/modules/Bitpay_Bitcoins.xml
deleted file mode 100644
index aadee6e..0000000
--- a/app/etc/modules/Bitpay_Bitcoins.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
- true
- community
-
-
-
-
-
-
diff --git a/app/etc/modules/Bitpay_Core.xml b/app/etc/modules/Bitpay_Core.xml
new file mode 100644
index 0000000..896fa96
--- /dev/null
+++ b/app/etc/modules/Bitpay_Core.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ true
+ community
+
+
+
+
+
+
diff --git a/app/locale/en_US/Bitpay_Core.csv b/app/locale/en_US/Bitpay_Core.csv
new file mode 100644
index 0000000..bc98c80
--- /dev/null
+++ b/app/locale/en_US/Bitpay_Core.csv
@@ -0,0 +1,15 @@
+"Enabled","Enabled"
+"Title","Title"
+"Network","Network"
+"Debug","Debug"
+"New","New"
+"Paid","Paid"
+"Confirmed","Confirmed"
+"Complete","Complete"
+"Expired","Expired"
+"Invalid","Invalid"
+"Low","Low"
+"Medium","Medium"
+"High","High"
+"Livenet","Livenet"
+"Testnet","Testnet"
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..7cb68f3
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/build.properties b/build/build.properties
new file mode 100644
index 0000000..d10344e
--- /dev/null
+++ b/build/build.properties
@@ -0,0 +1,154 @@
+# 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.
+
+# Running a build locally, this also is used
+# to define all the default properties.
+db.user=root
+db.pass=
+db.name=magento
+db.host=127.0.0.1
+magento.version=1.9.0.1
+magento.baseurl=http://www.localhost.com
+
+####
+#
+# This properties file is used to configure the entire build. The purpose of
+# this file is so that it is easy to drop it into a project and change a few
+# of these settings and have a build run successfully.
+#
+# NOTE: You may still need to take a look at and edit `phpunit.xml.dist`.
+#
+
+####
+#
+# Directory Configuration
+#
+
+# The main location of all the projects source code, this should be the only
+# property that you need to change. Code can live in the root directory or it
+# can live inside of it's own directory.
+project.source=${project.basedir}
+#project.source=${project.basedir}/src
+
+# --- You shouldn't need to edit any of the below value ---
+
+# Location of all executables, this is the location on the `bin-dir` that you
+# have configured in `composer.json`. If you have not configured this in
+# 1composer.json` the default is to put it in the `vendor` directory.
+project.bindir=${project.basedir}/bin
+#project.bindir=${project.basedir}/vendor/bin
+
+# Where to put the build artifacts, cache, logs, docs, etc.
+project.builddir=${project.basedir}/build
+
+# Location of vendor directory created by composer.
+project.vendordir=${project.basedir}/vendor
+
+# build artifacts for code analysis
+project.logsdir=${project.builddir}/logs
+
+# Build artifacts that can be reused or updated, they do not need to be deleted
+# every build. These would include charts and images.
+project.cachedir=${project.builddir}/cache
+
+# Location of generated documentation such as API and code coverage
+project.docsdir=${project.builddir}/docs
+#### directory ####
+
+####
+#
+# phpunit configuration
+#
+
+# Directory that contains phpunit.xml or the phpunit.xml file itself
+phpunit.configuration=${project.builddir}/phpunit.xml.dist
+#### phpunit ####
+
+####
+#
+# phpmd configuration
+#
+
+# php source code filename or directory. Can be a comma-separated string
+phpmd.source=${project.source}
+
+# report format, text, xml, or html
+phpmd.report.format=xml
+
+# ruleset filename or a comma-separated string of rulesetfilenames
+phpmd.ruleset=${project.builddir}/rulesets/phpmd.xml
+
+# send report output to this file
+phpmd.report.file=${project.logsdir}/phpmd.xml
+
+# comma-separated string of patterns that are used to ignore directories
+phpmd.exclude=${project.bindir},${project.builddir},${project.vendordir},app/code/community/Bitpay/Bitcoins/tests
+#### phpmd ####
+
+####
+#
+# phploc Configuration
+#
+
+# Source directory of project
+phploc.source=${project.source}/
+
+# Where to put the csv log
+phploc.log.csv=${project.logsdir}/phploc.csv
+
+# Location of xml log
+phploc.log.xml=${project.logsdir}/phploc.xml
+
+# Small hack to exclude multiple directories
+phploc.exclude=vendor --exclude=build --exclude=bin --exclude=app/code/community/Bitpay/Bitcoins/tests
+#### phploc ####
+
+####
+#
+# pdepend configuration
+#
+pdepend.source=${project.source}
+pdepend.jdepend.chart=${project.cachedir}/jdepend_chart.svg
+pdepend.jdepend.xml=${project.logsdir}/jdepend.xml
+pdepend.overview.pyramid=${project.cachedir}/pyramid.svg
+pdepend.summary.xml=${project.logsdir}/jdepend_summary.xml
+pdepend.ignore=bin,build,vendor,app/code/community/Bitpay/Bitcoins/tests
+#### pdepend ####
+
+####
+#
+# phpcs configuration
+#
+phpcs.source=${project.source}
+phpcs.report.xml=${project.logsdir}/phpcs.xml
+phpcs.standard=PSR1 --standard=PSR2
+phpcs.ignore=vendor --ignore=bin --ignore=build --ignore=app/code/community/Bitpay/Bitcoins/tests/
+#### phpcs ####
+
+####
+#
+# phpdoc configuration
+#
+phpdoc.directory=${project.source}/
+phpdoc.target=${project.docsdir}/api
+phpdoc.ignore=vendor/,bin/,app/code/community/Bitpay/Bitcoins/tests/,build/
+#### phpdoc ####
diff --git a/build/n98-magerun.yaml b/build/n98-magerun.yaml
new file mode 100644
index 0000000..d797295
--- /dev/null
+++ b/build/n98-magerun.yaml
@@ -0,0 +1,28 @@
+# 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.
+commands:
+ N98\Magento\Command\Installer\InstallCommand:
+ installation:
+ defaults:
+ currency: USD
+ locale: en_US
+ timezone: America/New_York
diff --git a/build/phpunit.xml.dist b/build/phpunit.xml.dist
new file mode 100644
index 0000000..72a17db
--- /dev/null
+++ b/build/phpunit.xml.dist
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+ ../tests/
+
+
+
+
+
+ ../app/code/community/Bitpay/Core/
+ ../shell
+
+ ../app/code/community/Bitpay/Core/sql
+
+
+
+
+
+
+
+
+
diff --git a/build/rulesets/phpmd.xml b/build/rulesets/phpmd.xml
new file mode 100644
index 0000000..feb5ee7
--- /dev/null
+++ b/build/rulesets/phpmd.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ Custom phpmd ruleset to be used with BitPay projects.
+
+
+
+
+
+
+
+
+
+
diff --git a/build/travis.properties b/build/travis.properties
new file mode 100644
index 0000000..a21c8f1
--- /dev/null
+++ b/build/travis.properties
@@ -0,0 +1,24 @@
+# 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.
+
+# Properties file for running a build on Travis CI
+db.user=travis
diff --git a/composer.json b/composer.json
index 4686e66..c33070e 100644
--- a/composer.json
+++ b/composer.json
@@ -1,9 +1,47 @@
{
- "name": "bitpay/bitcoins",
- "description": "Bitcoin payment module using the bitpay.com service",
- "minimum-stability": "dev",
- "type": "magento-module",
- "require": {
- "magento-hackathon/magento-composer-installer": "dev-master"
- }
+ "name": "bitpay/magento-plugin",
+ "description": "Bitcoin payment module using the bitpay.com service",
+ "keywords": ["magento","bitcoin", "bitcoin cash"],
+ "minimum-stability": "stable",
+ "type": "magento-plugin",
+ "homepage": "https://github.com/bitpay/magento-plugin",
+ "license": "MIT",
+ "support": {
+ "email": "support@bitpay.com",
+ "issues": "https://github.com/bitpay/magento-plugin/issues",
+ "source": "https://github.com/bitpay/magento-plugin"
+ },
+ "require": {
+ "bitpay/php-client": "~2.2"
+ },
+ "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/docs/MagentoInvoiceSettings.png b/docs/MagentoInvoiceSettings.png
new file mode 100644
index 0000000..eeb0163
Binary files /dev/null and b/docs/MagentoInvoiceSettings.png differ
diff --git a/docs/MagentoSettings.png b/docs/MagentoSettings.png
new file mode 100644
index 0000000..d31bbea
Binary files /dev/null and b/docs/MagentoSettings.png differ
diff --git a/lib/Bitpay/Storage/MagentoStorage.php b/lib/Bitpay/Storage/MagentoStorage.php
new file mode 100644
index 0000000..fbff56d
--- /dev/null
+++ b/lib/Bitpay/Storage/MagentoStorage.php
@@ -0,0 +1,69 @@
+_keys[$key->getId()] = $key;
+
+ $data = serialize($key);
+ $encryptedData = \Mage::helper('core')->encrypt($data);
+ $config = new \Mage_Core_Model_Config();
+
+ if (true === isset($config) && false === empty($config)) {
+ $config->saveConfig($key->getId(), $encryptedData);
+ } else {
+ \Mage::helper('bitpay')->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];
+ }
+
+ $entity = \Mage::getStoreConfig($id);
+
+ /**
+ * Not in database
+ */
+ if (false === isset($entity) || true === empty($entity)) {
+ \Mage::helper('bitpay')->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(\Mage::helper('core')->decrypt($entity));
+
+ if (false === isset($decodedEntity) || true === empty($decodedEntity)) {
+ \Mage::helper('bitpay')->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 . '.');
+ }
+
+ \Mage::helper('bitpay')->debugData('[INFO] Call to MagentoStorage::load($id) with the id of ' . $id . ' successfully decrypted & unserialized the entity ' . $entity . '.');
+
+ return $decodedEntity;
+ }
+}
diff --git a/lib/bitpay/bp_config_default.php b/lib/bitpay/bp_config_default.php
deleted file mode 100644
index 36f9941..0000000
--- a/lib/bitpay/bp_config_default.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
diff --git a/lib/bitpay/bp_lib.php b/lib/bitpay/bp_lib.php
deleted file mode 100644
index a46e03f..0000000
--- a/lib/bitpay/bp_lib.php
+++ /dev/null
@@ -1,160 +0,0 @@
- curl_error($curl));
- } else {
- $response = json_decode($responseString, true);
- if (!$response)
- $response = array('error' => 'invalid json: '.$responseString);
- }
-
- curl_close($curl);
- return $response;
-}
-
-// $orderId: Used to display an orderID to the buyer. In the account summary view, this value is used to
-// identify a ledger entry if present.
-//
-// $price: by default, $price is expressed in the currency you set in bp_options.php. The currency can be
-// changed in $options.
-//
-// $posData: this field is included in status updates or requests to get an invoice. It is intended to be used by
-// the merchant to uniquely identify an order associated with an invoice in their system. Aside from that, Bit-Pay does
-// not use the data in this field. The data in this field can be anything that is meaningful to the merchant.
-//
-// $options keys can include any of:
-// ('itemDesc', 'itemCode', 'notificationEmail', 'notificationURL', 'redirectURL', 'apiKey'
-// 'currency', 'physical', 'fullNotifications', 'transactionSpeed', 'buyerName',
-// 'buyerAddress1', 'buyerAddress2', 'buyerCity', 'buyerState', 'buyerZip', 'buyerEmail', 'buyerPhone')
-// If a given option is not provided here, the value of that option will default to what is found in bp_options.php
-// (see api documentation for information on these options).
-function bpCreateInvoice($orderId, $price, $posData, $options = array()) {
- global $bpOptions, $bpconfig;
-
- $options = array_merge($bpOptions, $options); // $options override any options found in bp_options.php
- $pos = array('posData' => $posData);
-
- if ($bpOptions['verifyPos'])
- $pos['hash'] = crypt(serialize($posData), $options['apiKey']);
-
- $options['posData'] = json_encode($pos);
- $options['orderID'] = $orderId;
- $options['price'] = $price;
-
- $postOptions = array('orderID', 'itemDesc', 'itemCode', 'notificationEmail', 'notificationURL', 'redirectURL',
- 'posData', 'price', 'currency', 'physical', 'fullNotifications', 'transactionSpeed', 'buyerName',
- 'buyerAddress1', 'buyerAddress2', 'buyerCity', 'buyerState', 'buyerZip', 'buyerEmail', 'buyerPhone');
-
- foreach($postOptions as $o)
- if (array_key_exists($o, $options))
- $post[$o] = $options[$o];
-
- $post = json_encode($post);
-
- $response = bpCurl('https://'.$bpconfig['hostAndPort'].'/api/invoice/', $options['apiKey'], $post);
-
- return $response;
-}
-
-// Call from your notification handler to convert $_POST data to an object containing invoice data
-function bpVerifyNotification($apiKey = false) {
- global $bpOptions, $bpconfig;
-
- if (!$apiKey)
- $apiKey = $bpOptions['apiKey'];
-
- $post = file_get_contents("php://input");
-
- if (!$post)
- return 'No post data';
-
- $json = json_decode($post, true);
-
- if (is_string($json))
- return $json; // error
-
- if (!array_key_exists('posData', $json))
- return 'no posData';
-
- $posData = json_decode($json['posData'], true);
-
- if($bpOptions['verifyPos'] and $posData['hash'] != crypt(serialize($posData['posData']), $apiKey))
- return 'authentication failed (bad hash)';
-
- $json['posData'] = $posData['posData'];
-
- return $json;
-}
-
-// $options can include ('apiKey')
-function bpGetInvoice($invoiceId, $apiKey=false) {
- global $bpOptions, $bpconfig;
-
- if (!$apiKey)
- $apiKey = $bpOptions['apiKey'];
-
- $response = bpCurl('https://'.$bpconfig['hostAndPort'].'/api/invoice/'.$invoiceId, $apiKey);
-
- if (is_string($response))
- return $response; // error
-
- $response['posData'] = json_decode($response['posData'], true);
- $response['posData'] = $response['posData']['posData'];
-
- return $response;
-}
-
-?>
diff --git a/lib/bitpay/bp_options.php b/lib/bitpay/bp_options.php
deleted file mode 100644
index be28f91..0000000
--- a/lib/bitpay/bp_options.php
+++ /dev/null
@@ -1,49 +0,0 @@
-
diff --git a/modman b/modman
index baa95dd..43f0a58 100644
--- a/modman
+++ b/modman
@@ -1,5 +1,40 @@
-app/code/community/Bitpay/Bitcoins app/code/community/Bitpay/Bitcoins
-app/design/frontend/base/default/layout/bitcoins.xml app/design/frontend/base/default/layout/bitcoins.xml
-app/design/frontend/base/default/template/bitcoins app/design/frontend/base/default/template/bitcoins
-app/etc/modules/Bitpay_Bitcoins.xml app/etc/modules/Bitpay_Bitcoins.xml
-lib/bitpay lib/bitpay
+# 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.
+
+# Modman file generated by 'generate-modman'
+# app/code
+app/code/community/Bitpay/Core app/code/community/Bitpay/Core
+# app/etc
+app/etc/modules/Bitpay_Core.xml app/etc/modules/Bitpay_Core.xml
+# app/locale
+app/locale/en_US/Bitpay_Core.csv app/locale/en_US/Bitpay_Core.csv
+# app/design
+app/design/adminhtml/default/default/template/bitpay/info/default.phtml app/design/adminhtml/default/default/template/bitpay/info/default.phtml
+app/design/adminhtml/default/default/template/bitpay/system/config/field/header.phtml app/design/adminhtml/default/default/template/bitpay/system/config/field/header.phtml
+app/design/adminhtml/default/default/layout/bitpay.xml app/design/adminhtml/default/default/layout/bitpay.xml
+app/design/frontend/base/default/template/bitpay/iframe.phtml app/design/frontend/base/default/template/bitpay/iframe.phtml
+app/design/frontend/base/default/template/bitpay/info/default.phtml app/design/frontend/base/default/template/bitpay/info/default.phtml
+app/design/frontend/base/default/template/bitpay/json.phtml app/design/frontend/base/default/template/bitpay/json.phtml
+app/design/frontend/base/default/template/bitpay/form/bitpay.phtml app/design/frontend/base/default/template/bitpay/form/bitpay.phtml
+app/design/frontend/base/default/layout/bitpay.xml app/design/frontend/base/default/layout/bitpay.xml
+# lib
+lib/Bitpay/Storage/MagentoStorage.php lib/Bitpay/Storage/MagentoStorage.php
diff --git a/scripts/config.json b/scripts/config.json
new file mode 100644
index 0000000..412a628
--- /dev/null
+++ b/scripts/config.json
@@ -0,0 +1,11 @@
+{
+ "mysql": {
+ "host": "localhost",
+ "user": "magento",
+ "password": "magento",
+ "database": "magento"
+ },
+ "host": "http://127.0.0.1/magento/index.php/bitpay/ipn",
+ "status": "confirmed"
+}
+
diff --git a/scripts/delete.sh b/scripts/delete.sh
new file mode 100755
index 0000000..1ffb56c
--- /dev/null
+++ b/scripts/delete.sh
@@ -0,0 +1,156 @@
+#!/usr/bin/env sh
+#
+# (c) 2014 BitPay, Inc.
+#
+# Shell script that locates and removes
+# any previously installed BitPay Magento
+# plugin files.
+#
+# Written by Rich Morgan
+
+# For full transparency I've listed all the depricated files and folders
+# related to the old plugin here for your information. This is the complete
+# list of items you would want to backup and/or remove if you wanted to do
+# this by hand yourself or if you just wanted a list for tracking purposes.
+old_files[0]="/lib/bitpay/bp_config_default.php"
+old_files[1]="/lib/bitpay/bp_lib.php"
+old_files[2]="/lib/bitpay/bp_options.php"
+old_files[3]="/lib/bitpay"
+old_files[4]="/app/design/frontend/base/default/template/bitcoins/iframe.phtml"
+old_files[5]="/app/design/frontend/base/default/template/bitcoins"
+old_files[6]="/app/design/frontend/base/default/layout/bitcoins.xml"
+old_files[7]="/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn/Collection.php"
+old_files[8]="/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn"
+old_files[9]="/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn.php"
+old_files[10]="/app/code/community/Bitpay/Bitcoins/Model/PaymentMethod.php"
+old_files[11]="/app/code/community/Bitpay/Bitcoins/Model/Ipn.php"
+old_files[12]="/app/code/community/Bitpay/Bitcoins/Model/Source/Speed.php"
+old_files[13]="/app/code/community/Bitpay/Bitcoins/Model/Source"
+old_files[14]="/app/code/community/Bitpay/Bitcoins/Model/Resource"
+old_files[15]="/app/code/community/Bitpay/Bitcoins/Model"
+old_files[16]="/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-0.1.0-1.0.0.php"
+old_files[17]="/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-1.0.0-1.1.0.php"
+old_files[18]="/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup"
+old_files[19]="/app/code/community/Bitpay/Bitcoins/sql"
+old_files[20]="/app/code/community/Bitpay/Bitcoins/Block/Iframe.php"
+old_files[21]="/app/code/community/Bitpay/Bitcoins/Block"
+old_files[22]="/app/code/community/Bitpay/Bitcoins/controllers/IndexController.php"
+old_files[23]="/app/code/community/Bitpay/Bitcoins/controllers"
+old_files[24]="/app/code/community/Bitpay/Bitcoins/etc/config.xml"
+old_files[25]="/app/code/community/Bitpay/Bitcoins/etc/system.xml"
+old_files[26]="/app/code/community/Bitpay/Bitcoins/etc"
+old_files[27]="/app/code/community/Bitpay/Bitcoins"
+old_files[28]="/app/code/community/Bitpay"
+old_files[29]="/app/etc/modules/Bitpay_Bitcoins.xml"
+old_files[30]="composer.json"
+old_files[31]="magento-plugin-master.zip"
+old_files[32]="modman"
+old_files[33]="README.md"
+
+CLEAN="true"
+RMOPTS="-vrfd"
+
+echo "Looking for your Magento installation. Please stand by - this may take a few minutes while I search..."
+
+# In case we have multiple Magento installs on this one
+# server, we will just take the first one and ask...
+i=`find /var /usr /opt -name Mage.php -type f | head -n 1 2>/dev/null`
+
+if [ -e $i ]
+then
+ DIR=`dirname $i`
+ cd $DIR && cd ../
+ mage_dir=`pwd`
+ echo "It looks like Magento is installed in the $mage_dir directory."
+ echo "Is this correct? (y/n) > "
+else
+ # In case we can't find the Magento folder, we are just
+ # providing a default value here. This happens to be the
+ # default for an Ubuntu-based machine, for example.
+ DIR="/var/www/html/magento"
+ cd $DIR
+ mage_dir=`pwd`
+ echo "You don't have Magento installed or this script doesn't have permissions to view the directory it's contained in."
+ echo "Should I default to $mage_dir ? (y/n) > "
+fi
+
+read answer
+
+if [ $answer = "y" ]
+then
+ echo "Attempting to delete the old plugin files now. Please stand by..."
+ for filename in "${old_files[@]}"
+ do
+ fullname=$mage_dir$filename
+ if [ -e $fullname ]
+ then
+ echo " Found $fullname - removing!"
+ rm $RMOPTS $fullname
+ else
+ echo " $filename does not exist - skipping!"
+ fi
+ done
+ echo "File removal process complete. Checking to make sure your Magento environment was completely cleaned of old BitPay files..."
+ echo ""
+ for filename in "${old_files[@]}"
+ do
+ fullname=$mage_dir$filename
+ if [ -e $fullname ]
+ then
+ echo " The old plugin file $fullname is still present!"
+ CLEAN="false"
+ else
+ echo " $filename is not present - good!"
+ fi
+ done
+ if [ $CLEAN = "false" ]
+ then
+ echo "Old BitPay plugin files are still present in your Magento directory. This is likely due to this script not having permissions to delete them. You can fix this by running this script as superuser or you can remove the files by hand."
+ else
+ echo "Good! I didn't find any remaining old BitPay plugin files in your Magento directory! You can now safely install the new BitPay plugin."
+ fi
+ echo "Process complete."
+ echo ""
+else
+ echo "Okay, please enter the path you would like me to use or QUIT if you wish to abort the process."
+ echo "Full path or QUIT ? > "
+ read answer
+ if [ $answer = "QUIT" ]
+ then
+ echo "Quitting!"
+ else
+ echo "Attempting to delete the old plugin files at the directory you provided. Please stand by..."
+ for filename in "${old_files[@]}"
+ do
+ fullname=$answer$filename
+ if [ -e $fullname ]
+ then
+ echo " Found $fullname - removing!"
+ rm $RMOPTS $fullname
+ else
+ echo " $filename does not exist - skipping!"
+ fi
+ done
+ echo "File removal process complete. Checking to make sure your Magento environment was completely cleaned of old BitPay files..."
+ echo ""
+ for filename in "${old_files[@]}"
+ do
+ fullname=$mage_dir$filename
+ if [ -e $fullname ]
+ then
+ echo " The old plugin file $fullname is still present!"
+ CLEAN="false"
+ else
+ echo " $filename is not present - good!"
+ fi
+ done
+ if [ $CLEAN = "false" ]
+ then
+ echo "Old BitPay plugin files are still present in your Magento directory. This is likely due to this script not having permissions to delete them. You can fix this by running this script as superuser or you can remove the files by hand."
+ else
+ echo "Good! I didn't find any remaining old BitPay plugin files in your Magento directory! You can now safely install the new BitPay plugin."
+ fi
+ echo "Process complete."
+ echo ""
+ fi
+fi
diff --git a/scripts/package b/scripts/package
new file mode 100755
index 0000000..fa55a52
--- /dev/null
+++ b/scripts/package
@@ -0,0 +1,154 @@
+#!/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', 'Accept Bitcoin and Bitcoin Cash on your Magento-based e-commerce site via BitPay.');
+$xml->addChild('description', 'Use BitPays plugin to accept Bitcoin and Bitcoin Cash payments from customers anywhere on earth. You do not need to worry about bitcoin as a currency, because the plugin uses your local currency to create a BitPay invoice. BitPay will instantly update the Magento order status once the bitcoin payment has been received.');
+$xml->addChild('notes', 'Full support for Magento 1.9');
+
+$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', '7.2.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();
+
+$process = new \Symfony\Component\Process\Process(
+ sprintf('cd %s; zip -r %s *', $tmpDistDir, $distFile.'.zip')
+);
+$process->run();
+
+// Cleanup
+$filesystem->remove($tmpDistDir);
diff --git a/scripts/send_ipn_for_last_order_created.js b/scripts/send_ipn_for_last_order_created.js
new file mode 100644
index 0000000..b52db24
--- /dev/null
+++ b/scripts/send_ipn_for_last_order_created.js
@@ -0,0 +1,95 @@
+var mysql = require('mysql');
+var format = require('string-template');
+var query = 'select * from bitpay_invoices where quote_id=(select MAX(quote_id) from bitpay_invoices)';
+var spawn = require('child_process').spawn;
+var config = require('./config.json');
+var data = {};
+var connection = mysql.createConnection(config.mysql);
+
+function postIpn() {
+ connection.connect();
+ connection.query(query, processRows);
+}
+
+function send(curl_args) {
+ var curl = spawn('curl', curl_args);
+ var stderr;
+ curl.stdout.on('data', function(data) {
+ console.log(data.toString());
+ });
+ curl.stderr.on('data', function(data) {
+ stderr = data;
+ });
+ curl.on('close', function(code) {
+ if (code === 0) {
+ console.log('curl exited successfully');
+ } else {
+ console.log('curl exited with an error: ' + stderr);
+ }
+ });
+}
+
+function processRows(err, rows, fields) {
+ if (err) {
+ throw err;
+ }
+ var curl_args = [
+ '-X', 'POST', '-H',
+ 'Content-Type: application/json',
+ '-H', "Content-Length: {length}",
+ '-H', 'Connection: close',
+ '-H', 'Accept: application/json',
+ '-d', '',
+ config.host ];
+ var convertedKeys = convertNames(fields);
+ var timeRegExp = new RegExp(/.*Time$/);
+ for (var i=0; iassertSame(
+ '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 @@
+