From 8371f891258e96b3d89c36882088e5cad8223d0f Mon Sep 17 00:00:00 2001 From: Joshua Estes Date: Fri, 26 Sep 2014 10:52:42 -0400 Subject: [PATCH] Refactored and updated plugin to work with update API and to be able to be packaged and distributed using more than just GitHub Added a script to delete old extension, installing the new extension overrides the previous extension and the previous extensions does not function anymore, merchants should at least disable the previous plugin, the merchant will also need to also remove the extension from core_resource table Updated to encrypt/decrypt keys to and from the database Small bug fixes with the package script Added instructions on how a previous merchant can upgrade to the new 2.x one --- .gitattributes | 3 +- .gitignore | 1 + .travis.yml | 7 +- CHANGELOG | 34 +- LICENSE | 2 +- README.md | 229 ++++---- .../Bitpay/Bitcoins/Block/Iframe.php | 120 ---- .../community/Bitpay/Bitcoins/Helper/Data.php | 173 ------ .../community/Bitpay/Bitcoins/Model/Ipn.php | 256 -------- .../Bitpay/Bitcoins/Model/Observer.php | 95 --- .../Bitpay/Bitcoins/Model/PaymentMethod.php | 549 ------------------ .../Bitpay/Bitcoins/Model/Resource/Ipn.php | 33 -- .../Model/Resource/Ipn/Collection.php | 44 -- .../Bitpay/Bitcoins/Model/Source/Speed.php | 50 -- .../Bitcoins/controllers/IndexController.php | 118 ---- .../community/Bitpay/Bitcoins/etc/config.xml | 136 ----- .../community/Bitpay/Bitcoins/etc/system.xml | 122 ---- .../Bitcoins_setup/upgrade-0.1.0-1.0.0.php | 50 -- .../Bitcoins_setup/upgrade-1.0.0-1.1.0.php | 32 - .../System/Config/Form/Field/Extension.php | 26 + .../System/Config/Form/Field/Header.php | 28 + .../Bitpay/Core/Block/Form/Bitpay.php | 14 + .../community/Bitpay/Core/Block/Iframe.php | 68 +++ app/code/community/Bitpay/Core/Block/Info.php | 26 + .../community/Bitpay/Core/Helper/Data.php | 265 +++++++++ .../Bitpay/Core/Model/Config/PairingCode.php | 47 ++ .../community/Bitpay/Core/Model/Invoice.php | 70 +++ app/code/community/Bitpay/Core/Model/Ipn.php | 17 + .../Bitpay/Core/Model/Method/Bitcoin.php | 262 +++++++++ .../Bitpay/Core/Model/Mysql4/Invoice.php | 19 + .../Core/Model/Mysql4/Invoice/Collection.php | 19 + .../Bitpay/Core/Model/Mysql4/Ipn.php | 17 + .../Core/Model/Mysql4/Ipn/Collection.php | 16 + .../community/Bitpay/Core/Model/Network.php | 25 + .../community/Bitpay/Core/Model/Observer.php | 74 +++ .../Core/Model/Resource/Mysql4/Setup.php | 11 + .../community/Bitpay/Core/Model/Status.php | 30 + .../Bitpay/Core/Model/TransactionSpeed.php | 24 + .../Core/controllers/IndexController.php | 34 ++ .../Bitpay/Core/controllers/IpnController.php | 103 ++++ .../community/Bitpay/Core/etc/adminhtml.xml | 47 ++ app/code/community/Bitpay/Core/etc/config.xml | 130 +++++ app/code/community/Bitpay/Core/etc/system.xml | 274 +++++++++ .../sql/bitpay_setup/mysql4-install-2.0.0.php | 64 ++ .../default/default/layout/bitpay.xml | 9 + .../template/bitpay/info/default.phtml | 18 + .../bitpay/system/config/field/header.phtml | 17 + .../frontend/base/default/layout/bitcoins.xml | 33 -- .../frontend/base/default/layout/bitpay.xml | 9 + .../default/template/bitcoins/iframe.phtml | 67 --- .../default/template/bitpay/form/bitpay.phtml | 19 + .../base/default/template/bitpay/iframe.phtml | 50 ++ .../template/bitpay/info/default.phtml | 9 + .../base/default/template/bitpay/json.phtml | 9 + app/etc/modules/Bitpay_Bitcoins.xml | 36 -- app/etc/modules/Bitpay_Core.xml | 18 + app/locale/en_US/Bitpay_Core.csv | 15 + build.xml | 23 +- build/build.properties | 2 +- build/n98-magerun.yaml | 2 +- build/phpunit.xml.dist | 4 +- build/rulesets/phpmd.xml | 2 +- build/travis.properties | 2 +- composer.json | 26 +- docs/MagentoInvoiceSettings.png | Bin 0 -> 40649 bytes docs/MagentoSettings.png | Bin 0 -> 131349 bytes lib/Bitpay/Storage/MagentoStorage.php | 59 ++ lib/bitpay/.htaccess | 5 - lib/bitpay/bp_config_default.php | 49 -- lib/bitpay/bp_lib.php | 232 -------- lib/bitpay/bp_options.php | 42 -- modman | 14 +- scripts/delete | 10 + scripts/package | 144 +++++ shell/bitpay.php | 92 --- .../Bitcoins/Model/PaymentMethodTest.php | 102 ---- .../{Bitcoins => Core}/Helper/DataTest.php | 109 ++-- tests/bootstrap.php | 28 +- 78 files changed, 2328 insertions(+), 2692 deletions(-) delete mode 100644 app/code/community/Bitpay/Bitcoins/Block/Iframe.php delete mode 100644 app/code/community/Bitpay/Bitcoins/Helper/Data.php delete mode 100644 app/code/community/Bitpay/Bitcoins/Model/Ipn.php delete mode 100644 app/code/community/Bitpay/Bitcoins/Model/Observer.php delete mode 100644 app/code/community/Bitpay/Bitcoins/Model/PaymentMethod.php delete mode 100644 app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn.php delete mode 100644 app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn/Collection.php delete mode 100644 app/code/community/Bitpay/Bitcoins/Model/Source/Speed.php delete mode 100644 app/code/community/Bitpay/Bitcoins/controllers/IndexController.php delete mode 100644 app/code/community/Bitpay/Bitcoins/etc/config.xml delete mode 100644 app/code/community/Bitpay/Bitcoins/etc/system.xml delete mode 100644 app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-0.1.0-1.0.0.php delete mode 100644 app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-1.0.0-1.1.0.php create mode 100644 app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Extension.php create mode 100644 app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Header.php create mode 100644 app/code/community/Bitpay/Core/Block/Form/Bitpay.php create mode 100644 app/code/community/Bitpay/Core/Block/Iframe.php create mode 100644 app/code/community/Bitpay/Core/Block/Info.php create mode 100644 app/code/community/Bitpay/Core/Helper/Data.php create mode 100644 app/code/community/Bitpay/Core/Model/Config/PairingCode.php create mode 100644 app/code/community/Bitpay/Core/Model/Invoice.php create mode 100644 app/code/community/Bitpay/Core/Model/Ipn.php create mode 100644 app/code/community/Bitpay/Core/Model/Method/Bitcoin.php create mode 100644 app/code/community/Bitpay/Core/Model/Mysql4/Invoice.php create mode 100644 app/code/community/Bitpay/Core/Model/Mysql4/Invoice/Collection.php create mode 100644 app/code/community/Bitpay/Core/Model/Mysql4/Ipn.php create mode 100644 app/code/community/Bitpay/Core/Model/Mysql4/Ipn/Collection.php create mode 100644 app/code/community/Bitpay/Core/Model/Network.php create mode 100644 app/code/community/Bitpay/Core/Model/Observer.php create mode 100644 app/code/community/Bitpay/Core/Model/Resource/Mysql4/Setup.php create mode 100644 app/code/community/Bitpay/Core/Model/Status.php create mode 100644 app/code/community/Bitpay/Core/Model/TransactionSpeed.php create mode 100644 app/code/community/Bitpay/Core/controllers/IndexController.php create mode 100644 app/code/community/Bitpay/Core/controllers/IpnController.php create mode 100644 app/code/community/Bitpay/Core/etc/adminhtml.xml create mode 100644 app/code/community/Bitpay/Core/etc/config.xml create mode 100644 app/code/community/Bitpay/Core/etc/system.xml create mode 100644 app/code/community/Bitpay/Core/sql/bitpay_setup/mysql4-install-2.0.0.php create mode 100644 app/design/adminhtml/default/default/layout/bitpay.xml create mode 100644 app/design/adminhtml/default/default/template/bitpay/info/default.phtml create mode 100644 app/design/adminhtml/default/default/template/bitpay/system/config/field/header.phtml delete mode 100644 app/design/frontend/base/default/layout/bitcoins.xml create mode 100644 app/design/frontend/base/default/layout/bitpay.xml delete mode 100644 app/design/frontend/base/default/template/bitcoins/iframe.phtml create mode 100644 app/design/frontend/base/default/template/bitpay/form/bitpay.phtml create mode 100644 app/design/frontend/base/default/template/bitpay/iframe.phtml create mode 100644 app/design/frontend/base/default/template/bitpay/info/default.phtml create mode 100644 app/design/frontend/base/default/template/bitpay/json.phtml delete mode 100644 app/etc/modules/Bitpay_Bitcoins.xml create mode 100644 app/etc/modules/Bitpay_Core.xml create mode 100644 app/locale/en_US/Bitpay_Core.csv create mode 100644 docs/MagentoInvoiceSettings.png create mode 100644 docs/MagentoSettings.png create mode 100644 lib/Bitpay/Storage/MagentoStorage.php delete mode 100644 lib/bitpay/.htaccess delete mode 100644 lib/bitpay/bp_config_default.php delete mode 100644 lib/bitpay/bp_lib.php delete mode 100644 lib/bitpay/bp_options.php create mode 100755 scripts/delete create mode 100755 scripts/package delete mode 100644 shell/bitpay.php delete mode 100644 tests/Bitpay/Bitcoins/Model/PaymentMethodTest.php rename tests/Bitpay/{Bitcoins => Core}/Helper/DataTest.php (65%) diff --git a/.gitattributes b/.gitattributes index f8ef91d..045f0e7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2011-2014 BitPay LLC +# 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 @@ -23,6 +23,7 @@ # These files/directories are not included when making # an archive of this repository. /build export-ignore +/scripts export-ignore /tests export-ignore .gitattributes export-ignore .gitignore export-ignore diff --git a/.gitignore b/.gitignore index 8f41432..5520859 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ lib/bitpay/bp_config.php /bin/ /build/cache/ +/build/dist/ /build/docs/ /build/logs/ /build/magento/ diff --git a/.travis.yml b/.travis.yml index c4bebd5..0cec51f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,10 +26,15 @@ php: - 5.3 install: - composer install -script: ./bin/phing -verbose -propertyfile build/travis.properties build-travis +#script: ./bin/phing -verbose -propertyfile build/travis.properties build-travis +script: phpunit -c build/ cache: directories: - bin/ - build/cache/ - build/magento/ - vendor/ +matrix: + fast_finish: true + allow_failures: + - php: 5.3 diff --git a/CHANGELOG b/CHANGELOG index 9915687..a1907a9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,29 +1,5 @@ -Version 6 - * Updated BitPay logo in admin settings - * Tested & validated against latest 1.9.0.1 - * Tested & validated with default one-page checkout settings - -Version 5 - * Added new HTTP header for version tracking - -Version 4 - * Improved README documentation. - * Additional testing performed against 1.8.1.0 and installation instructions updated to reflect differences. - * Added parameter to automatically create a shipment and mark orders complete (off by default). - * Version incremented, other bug fixes and enhancements (see commit notes). - -Version 3 - * Now gives the option to show an iframe on the checkout page instead of redirecting to bitpay.com. - -Version 2 - * Now supports API keys instead of SSL files. Tested against 1.7.0.2. - -Version 1 - * Initial version, tested against Magento 1.6.0.0 - - - - - - - +2.0.0 + Initial release of the new Magento Extension that supports BitPay's new + cryptographically secure API. You can read more about the new API on the + blog at + http://blog.bitpay.com/2014/09/18/announcing-the-new-bitpay-api.html diff --git a/LICENSE b/LICENSE index 5dd2731..328b352 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2011-2014 BitPay +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 diff --git a/README.md b/README.md index b84a266..056981c 100644 --- a/README.md +++ b/README.md @@ -1,118 +1,134 @@ bitpay/magento-plugin -========================= +===================== -# Status +# Build Status [![Build Status](https://travis-ci.org/bitpay/magento-plugin.svg?branch=master)](https://travis-ci.org/bitpay/magento-plugin) +# Brief Description + +BitPay has the most powerful bitcoin infrastructure on the planet and is trusted by over 40,000 merchants. + +# Detail Description + +Bitcoin is a powerful new peer-to-peer platform for the next generation of +financial technology. The decentralized nature of the Bitcoin network allows +for a highly resilient value transfer infrastructure, and this allows merchants +to gain greater profits. + +This is because there are little to no fees for transferring Bitcoins from one +person to another. Unlike other payment methods, Bitcoin payments cannot be +reversed, so once you are paid you can ship! No waiting days for a payment to +clear. + +# Requirements + +* [Magento](http://magento.com/resources/system-requirements) >= 1.9.0.1 (Older version will work, but we do not test against those) +* [GMP](http://us2.php.net/gmp) You may have to install this as most servers do not come with it. +* [mcrypt](http://us2.php.net/mcrypt) Magento requires this so you're fine +* [OpenSSL](http://us2.php.net/openssl) Must be compiled with PHP +* PHP >= 5.4 + +# Upgrade From Version 1.x to 2.x + +Merchants who have previous been using the BitPay Magento plugin will need to remove +previous plugin. This can be done by using the [scripts/delete](https://github.com/bitpay/magento-plugin/blob/master/scripts/delete) +script before installing the new plugin. + # Installation +## Magento Connect Manager + +Goto [http://www.magentocommerce.com/magento-connect/bitpay-payment-method.html](http://www.magentocommerce.com/magento-connect/bitpay-payment-method.html) +and click the *Install Now* link which will give you the *Extension Key* needed +for the next step. + +Once you have the key, log into you Magento Store's Admin Panel and navigate to +**System > Magento Connect > Magento Connect Manager**. + +***NOTE*** It may ask you to log in again using the same credentials that you use +to log into the Admin Panel. + +All you need to do is paste the extension key and click on the *Install* button. + +***WARNING*** It is good practice to backup your database before installing +extensions. Please make sure you Create Backups. + ## Download -1. [Download](https://github.com/bitpay/magento-plugin/archive/master.zip) and Unzip this archive and copy the files to the location of your -[Magento CE](http://magento.com/) installation on your web server. For Ubuntu-based servers, the default -location for website files is the `/var/www` folder. Your web hosting provider may -use a different location for storing your website files so check with them if the -`/var/www` folder does not exist or your Magento files are in an otherwise unknown -location. -Many web hosting accounts have a graphical, web-based control panel for your server. -This is the easiest method for copying the [BitPay Magento Plugin](https://github.com/bitpay/magento-plugin) files to your Magenento -CE directory. If your provider has one of these graphical control panels, log into -your hosting account and move the files using that tool. However, if that is not -an option and you can only access your web server using a shell account via SSH, -open a new connection and issue these commands: +Visit the [Releases](https://github.com/bitpay/magento-plugin/releases) page of +this repository and download the latest version. Once this is done, you can just +unzip the contents and use any method you want to put them on your server. The +contents will mirror the Magento directory structure. -```bash -bitpay@bitpay:~$ unzip magento-plugin-master.zip -bitpay@bitpay:~$ cd magento-plugin-master -bitpay@bitpay:~$ cp -R ./* /location/of/your/magento/installation/ -``` -Note: You may need to have superuser privileges to copy files -to `/var/www` on Ubuntu-based servers. If you receive “Permission denied” errors -when using the cp command above, use sudo before the cp command and specify the -superuser password when asked: +***NOTE*** These files can also up uploaded using the *Magento Connect Manager* +that comes with your Magento Store -```bash -bitpay@bitpay:~$ sudo cp -R ./* /location/of/your/magento/installation/ -[sudo] password for (username): -``` -2. Verify the files have been copied correctly by checking -your Magento CE installation folder for one or more of them. You can choose -to check for any of the files present in the BitPay plugin archive. The file -I’m looking for in this example should be in the `/var/www/magento/app/code/community/Bitpay/Bitcoins/Model` -directory along with the `Ipn.php` file on my Ubuntu server: - -```bash -bitpay@bitpay:~$ ls -l /var/www/magento/app/code/community/Bitpay/Bitcoins/Model/ -total 24 --rw-r--r-- 1 root root 3097 Mar 25 14:06 Ipn.php --rw-r--r-- 1 root root 10786 Mar 25 14:06 PaymentMethod.php -drwxr-xr-x 3 root root 4096 Mar 25 13:54 Resource -drwxr-xr-x 2 root root 4096 Mar 25 13:54 Source -``` - -If the files were copied correctly and are present in the directory, you should -see the files listed when you issue the ls command. If you do not see any files -listed, try the cp command again to retry the copying procedure. However, if -you still do not see any files listed or you receive an error copying the files, -contact your web hosting support for assistance. +***WARNING*** It is good practice to backup your database before installing +extensions. Please make sure you Create Backups. ## modman + Using [modman](https://github.com/colinmollenhour/modman) you can install the BitPay Magento Plugin. Once you have modman installed, run `modman init` if you have not already done so. Next just run `modman clone https://github.com/bitpay/magento-plugin.git` in the root of the Magento installation. In this case it is `/var/www/magento`. -# Magento CE 1.8.x - 1.9.x Installation Tips -In some instances for merchants using Magento CE version 1.8.x, the BitPay -Bitcoins payment plugin might not appear in the Payment Methods configuration -section even though all plugin files have been correctly installed. To -resolve this issue, log into your admin control panel and choose the -System -> Cache Management configuration screen. Click the check box next -to the Configuration cache type and choose the Disable action from the Actions -drop-down list box. Click the Submit button to disable this cache. +## Development -Next, click both the Flush Magento Cache and Flush Cache Storage buttons (Clicked -"Ok" when the pop-up box is displayed) to remove the stale configuration cache files. +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. -Finally, log completely out of the administrative control panel and then log back -in. The Bitcoins option is now correctly displaying under Payment Methods in the -configuration screen. The BitPay plugin parameters are exactly the same on Magento -CE 1.8.x as on older Magento CE releases. +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. + +Once you are done, 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. # Configuration -NOTE: SSL is required for use of the BitPay plugin for Magento CE. -1. Create an API key at bitpay.com by clicking My Account > API Access Keys > Add New API Key. -2. In Admin panel under "System > Configuration > Sales > Payment Methods > Bitcoins": - - Verify that the module is enabled. - - Enter your API key. - - 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. The default setting is - "No". +Configuration can be done using the Administrator section of your Megento store. +Once Logged in, you will find the configuration settings under **System > Configuration > Sales > Payment Methods**. + +![BitPay Magento Settings](https://raw.githubusercontent.com/bitpay/magento-plugin/master/docs/MagentoSettings.png "BitPay Megento Settings") + +Here your will need to create a [pairing code](https://bitpay.com/api-tokens) using +your BitPay merchant account. Once you have a Pairing Code, put the code in the +Pairing Code field. This will take care of the rest for you. + +***NOTE*** Pairing Codes are only valid for a short period of time. If it expires +before you get to use it, you can always create a new one an use the new one. + +***NOTE*** You will only need to do this once since each time you do this, the +extension will generate public and private keys that are used to identify you +when using the API. + +You are also able to configure how BitPay's IPN (Instant Payment Notifications) +changes the order in your Magento store. + +![BitPay Invoice Settings](https://raw.githubusercontent.com/bitpay/magento-plugin/master/docs/MagentoInvoiceSettings.png "BitPay Invoice Settings") # 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. +Once enabled, your customers will be given the option to pay with Bitcoins. Once +they checkout they are redirected to a full screen BitPay invoice to pay for +the order. -Note: This extension does not provide a means of automatically pulling a current BTC -exchange rate for presenting BTC prices to shoppers. +As a merchant, the orders in your Magento store can be treated as any other +order. You may need to adjust the Invoice Settings depending on your order +fulfillment. # Support @@ -131,18 +147,33 @@ exchange rate for presenting BTC prices to shoppers. # 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 `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. -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! -5. If all else fails, send an email describing your issue *in detail* to support@bitpay.com +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 for the latest. +5. If all else fails, send an email describing your issue **in detail** to + support@bitpay.com -NOTE: When contacting support it will help us is you provide: -* Magento Version -* Other plugins you have installed -* Some configuration settings such as: - * Transaction Speed - * Set order complete with "complete" IPN +***TIP***: When contacting support it will help us is you provide: + +* Magento 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 @@ -152,7 +183,7 @@ To contribute to this project, please fork and submit a pull request. The MIT License (MIT) -Copyright (c) 2011-2014 BitPay LLC +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 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 cf1f31f..0000000 --- a/app/code/community/Bitpay/Bitcoins/Block/Iframe.php +++ /dev/null @@ -1,120 +0,0 @@ -setTemplate('bitcoins/iframe.phtml'); - parent::_construct(); - } - - /** - * @return string - */ - 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 - * - * @return string - */ - public function GetIframeUrl() - { - // are they using bitpay? - // @todo refactor this - if (!($quote = Mage::getSingleton('checkout/session')->getQuote()) - or !($payment = $quote->getPayment()) - or !$payment->getMethod() - 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); - - $invoice = bpCreateInvoice($quoteId, $price, array('quoteId' => $quoteId, 'quoteHash' => $hash), $options); - - if (array_key_exists('error', $invoice)) - { - Mage::log('Error creating bitpay invoice', null, Mage::helper('bitpay')->getLogFile()); - Mage::log($invoice['error'], null, Mage::helper('bitpay')->getLogFile()); - 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/Helper/Data.php b/app/code/community/Bitpay/Bitcoins/Helper/Data.php deleted file mode 100644 index 4d0072d..0000000 --- a/app/code/community/Bitpay/Bitcoins/Helper/Data.php +++ /dev/null @@ -1,173 +0,0 @@ -getExpired(); - - foreach ($expiredRecords as $ipn) { - $incrementId = $ipn->getOrderId(); - if (empty($incrementId)) { - $this->logIpnParseError($ipn); - continue; - } - - // Cancel the order - $order = Mage::getModel('sales/order')->loadByIncrementId($incrementId); - $this->cancelOrder($order); - - // Delete all IPN records for order id - Mage::getModel('Bitcoins/ipn') - ->deleteByOrderId($ipn->getOrderId()); - Mage::log( - sprintf('Deleted Record: %s', $ipn->toJson()), - Zend_Log::DEBUG, - self::LOG_FILE - ); - } - } - - /** - * Log error if there is an issue parsing an IPN record - * - * @param Bitpay_Bitcoins_Model_Ipn $ipn - * @param boolean $andDelete - */ - private function logIpnParseError(Bitpay_Bitcoins_Model_Ipn $ipn, $andDelete = true) - { - Mage::log( - 'Error processing IPN record', - Zend_Log::DEBUG, - self::LOG_FILE - ); - Mage::log( - $ipn->toJson(), - Zend_Log::DEBUG, - self::LOG_FILE - ); - - if ($andDelete) { - $ipn->delete(); - Mage::log( - 'IPN record deleted from database', - Zend_Log::DEBUG, - self::LOG_FILE - ); - } - } - - /** - * This will cancel the order in the magento database, this will return - * true if the order was canceled or it will return false if the order - * was not updated. For example, if the order is complete, we don't want - * to cancel that order so this method would return false. - * - * @param Mage_Sales_Model_Order - * - * @return boolean - */ - private function cancelOrder(Mage_Sales_Model_Order $order) - { - $orderState = $order->getState(); - - /** - * These order states are useless and can just be skipped over. No - * need to cancel an order that is alread canceled. - */ - $statesWeDontCareAbout = array( - Mage_Sales_Model_Order::STATE_CANCELED, - Mage_Sales_Model_Order::STATE_CLOSED, - Mage_Sales_Model_Order::STATE_COMPLETE, - ); - - if (in_array($orderState, $statesWeDontCareAbout)) { - return false; - } - - $order->setState( - Mage_Sales_Model_Order::STATE_CANCELED, - true, - 'BitPay Invoice has expired', // Comment - false // notifiy customer? - )->save(); - Mage::log( - sprintf('Order "%s" has been canceled', $order->getIncrementId()), - Zend_Log::DEBUG, - self::LOG_FILE - ); - - return true; - } -} 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 d84012f..0000000 --- a/app/code/community/Bitpay/Bitcoins/Model/Ipn.php +++ /dev/null @@ -1,256 +0,0 @@ -_init('Bitcoins/ipn'); - } - - /** - * @param $invoice - * - * @return - */ - 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(); - } - - /** - * @param string $quoteId - * @param array $statuses - * - * @return boolean - */ - function GetStatusReceived($quoteId, $statuses) - { - if (!$quoteId) - { - return false; - } - - $quote = Mage::getModel('sales/quote')->load($quoteId, 'entity_id'); - - if (!$quote) - { - Mage::log('quote not found', Zend_Log::WARN, 'bitpay.log'); - - return false; - } - - $quoteHash = Mage::getModel('Bitcoins/paymentMethod')->getQuoteHash($quoteId); - - if (!$quoteHash) - { - Mage::log('Could not find quote hash for quote '.$quoteId, Zend_Log::WARN, '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; - } - - /** - * @param string $quoteId - * - * @return boolean - */ - function GetQuotePaid($quoteId) - { - return $this->GetStatusReceived($quoteId, array('paid', 'confirmed', 'complete')); - } - - /** - * @param string $quoteId - * - * @return boolean - */ - function GetQuoteComplete($quoteId) - { - return $this->GetStatusReceived($quoteId, array('confirmed', 'complete')); - } - - /** - * This method returns an array of orders in the database that have paid - * using bitcoins, but are still open and we need to query the invoice - * IDs at BitPay and see if they invoice has expired, is invalid, or is - * complete. - * - * @return array - */ - public function getOpenOrders() - { - $doneCollection = $this->getCollection(); - - /** - * Get all the IPNs that have been completed - * - * SELECT - * order_id - * FROM - * bitpay_ipns - * WHERE - * status IN ('completed','invalid','expired') AND order_id IS NOT NULL - * GROUP BY - * order_id - */ - $doneCollection - ->addFieldToSelect('order_id') - ->addFieldToFilter( - 'status', - array( - 'in' => array( - 'complete', - 'invalid', - 'expired', - ) - ) - ); - $doneCollection - ->getSelect() - ->where('order_id IS NOT NULL') - ->group('order_id'); - - $collection = $this->getCollection(); - - /** - * Get all the open orders that have not received a IPN that closes - * the invoice. - * - * SELECT - * * - * FROM - * bitpay_ipns - * JOIN - * 'sales/order' ON bitpay_ipns.order_id='sales/order'.increment_id - * WHERE - * order_id NOT IN (?) AND order_id IS NOT NULL - * GROUP BY - * order_id - */ - if (0 < $doneCollection->count()) { - $collection - ->addFieldToFilter( - 'status', - array( - 'in' => $doneCollection->getColumnValues('order_id') - ) - ); - } - - $collection - ->getSelect() - ->where('order_id IS NOT NULL') - ->group('order_id'); - - return $collection->getItems(); - } - - /** - * Returns all records that have expired - */ - public function getExpired() - { - $collection = $this->getCollection(); - $now = new DateTime('now', new DateTimezone('UTC')); - - $collection - ->removeFieldFromSelect('status') - ->addFieldToFilter( - 'expiration_time', - array( - 'lteq' => $now->getTimestamp() - ) - ); - - $collection - ->getSelect() - ->group('order_id') - // Newest to oldest - ->order('expiration_time DESC'); - - return $collection->getItems(); - } - - /** - * This will delete all records that match the order id (order id is also - * the increment id of the magento order) - * - * @see Bitpay_Bitcoins_Model_Resource_Ipn_Collection::delete() - * - * @param string $orderId - */ - public function deleteByOrderId($orderId) - { - $collection = Mage::getModel('Bitcoins/ipn') - ->getCollection(); - $collection - ->getSelect() - ->where('order_id = ?', $orderId); - - $collection->delete(); - } -} diff --git a/app/code/community/Bitpay/Bitcoins/Model/Observer.php b/app/code/community/Bitpay/Bitcoins/Model/Observer.php deleted file mode 100644 index 781e281..0000000 --- a/app/code/community/Bitpay/Bitcoins/Model/Observer.php +++ /dev/null @@ -1,95 +0,0 @@ -getLogFile() - ); - - $apiKey = Mage::getStoreConfig('payment/Bitcoins/api_key'); - - if (empty($apiKey)) { - Mage::log( - 'cronjob: Api Key not set.', - Zend_Log::ERR, - Mage::helper('bitpay')->getLogFile() - ); - return; // Api Key needs to be set - } - - - /** - * Get all of the orders that are open and have not received an IPN for - * complete, expired, or invalid. - * - * If anyone knows of a better way to do this, please let me know - */ - $orders = Mage::getModel('Bitcoins/ipn')->getOpenOrders(); - - /** - * 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; - - // Does the order need to be updated? - // Yes? Update Order Status - // No? continue - } - - Mage::log( - 'cronjob: end', - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - } - - /** - * Method that is called via the magento cron to update orders if the - * invoice has expired - */ - public function cleanExpired() - { - Mage::helper('bitpay')->cleanExpired(); - } -} 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 8542061..0000000 --- a/app/code/community/Bitpay/Bitcoins/Model/PaymentMethod.php +++ /dev/null @@ -1,549 +0,0 @@ -getLogFile() - ); - - $currencies = Mage::getStoreConfig('payment/Bitcoins/currencies'); - $currencies = array_map('trim', explode(',', $currencies)); - - return array_search($currencyCode, $currencies) !== false; - } - - /** - * Can be used in regular checkout - * @see Mage_Payment_Model_Method_Abstract::canUseCheckout() - * - * @return bool - */ - public function canUseCheckout() - { - $helper = Mage::helper('bitpay'); - - if (!$helper->hasApiKey()) - { - Mage::log( - 'Bitpay/Bitcoins: API key not entered', - Zend_Log::ERR, - Mage::helper('bitpay')->getLogFile() - ); - - return false; - } - - if (!$helper->hasTransactionSpeed()) - { - Mage::log( - 'Bitpay/Bitcoins: Transaction Speed has not been set', - Zend_Log::ERR, - Mage::helper('bitpay')->getLogFile() - ); - - return false; - } - - return $this->_canUseCheckout; - } - - /** - * Authorize payment method - * - * @param Varien_Object $payment - * @param float $amount - * - * @return Bitpay_Bitcoins_Model_PaymentMethod - */ - public function authorize(Varien_Object $payment, $amount) - { - Mage::log( - sprintf('Authorizing payment'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - if (!Mage::getStoreConfig('payment/Bitcoins/fullscreen')) - { - return $this->CheckForPayment($payment); - } - else - { - return $this->CreateInvoiceAndRedirect($payment, $amount); - } - } - - /** - * @param Varien_Object $payment - * - * @return Bitpay_Bitcoins_Model_PaymentMethod - */ - public function CheckForPayment($payment) - { - Mage::log( - sprintf('Checking for payment'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - $quoteId = $payment->getOrder()->getQuoteId(); - $ipn = Mage::getModel('Bitcoins/ipn'); - - if (!$ipn->GetQuotePaid($quoteId)) - { - // This is the error that is displayed to the customer during checkout. - Mage::throwException("Order not paid for. Please pay first and then Place your Order."); - Mage::log('Order not paid for. Please pay first and then Place Your Order.', Zend_Log::CRIT, Mage::helper('bitpay')->getLogFile()); - } - else if (!$ipn->GetQuoteComplete($quoteId)) - { - // order status will be PAYMENT_REVIEW instead of PROCESSING - $payment->setIsTransactionPending(true); - } else { - $this->MarkOrderPaid($payment->getOrder()); - } - - return $this; - } - - /** - * @param Varien_Object $order - */ - public function invoiceOrder($order) - { - Mage::log( - sprintf('Invoicing order'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - try - { - if (!$order->canInvoice()) - { - Mage::throwException(Mage::helper('core')->__('Cannot create an invoice.')); - } - - $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(); - } - catch (Exception $e) - { - Mage::log($e->getMessage(), Zend_Log::EMERG, Mage::helper('bitpay')->getLogFile()); - Mage::logException($e); - } - } - - /** - * @param $order - */ - public function MarkOrderPaid($order) - { - Mage::log( - sprintf('Marking order paid'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true)->save(); - - if ($order->getTotalDue() > 0) - { - if (!count($order->getInvoiceCollection())) - { - $this->invoiceOrder($order); - } - } - else - { - Mage::log('MarkOrderPaid called but order '. $order->getId() .' does not have a balance due.', Zend_Log::WARN, Mage::helper('bitpay')->getLogFile()); - } - } - - /** - * @param $order - */ - public function MarkOrderComplete($order) - { - Mage::log( - sprintf('Marking order paid'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - if ($order->getTotalDue() >= 0 && $order->canInvoice()) - { - if ($order->hasInvoices()) - { - foreach ($order->getInvoiceCollection() as $_eachInvoice) - { - try - { - $_eachInvoice->setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE); - $_eachInvoice->capture()->save(); - } - catch (Exception $e) - { - Mage::log($e->getMessage(), Zend_Log::EMERG, Mage::helper('bitpay')->getLogFile()); - Mage::logException($e); - } - } - } - } - - // If the $_bpCreateShipment option is set to true above, this code will - // programmatically create a shipment for you. By design, this will mark - // the entire order as 'complete'. - if(isset($_bpCreateShipment) && $_bpCreateShipment == true) - { - try - { - $shipment = $order->prepareShipment(); - if ($shipment) - { - $shipment->register(); - $order->setIsInProcess(true); - $transaction_save = Mage::getModel('core/resource_transaction') - ->addObject($shipment) - ->addObject($shipment->getOrder()) - ->save(); - } - } - catch (Exception $e) - { - Mage::log('Error creating shipment for order '. $order->getId() .'.', Zend_Log::ERR, Mage::helper('bitpay')->getLogFile()); - Mage::logException($e); - } - } - - try - { - if((isset($_bpCreateShipment) && $_bpCreateShipment == true) || Mage::getStoreConfig('payment/Bitcoins/order_disposition')) - { - $order->setState('Complete', 'complete', 'Completed by BitPay payments.', true); - } - else - { - $order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, 'processing', 'BitPay has confirmed the payment.', false); - } - - if(!$order->getEmailSent()) - { - $order->sendNewOrderEmail(); - } - - $order->save(); - } - catch (Exception $e) - { - Mage::log($e->getMessage(), Zend_Log::EMERG, Mage::helper('bitpay')->getLogFile()); - Mage::logException($e); - } - - } - - /** - * @param $order - */ - public function MarkOrderCancelled($order) - { - Mage::log( - sprintf('Marking order cancelled'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - try - { - $order->setState(Mage_Sales_Model_Order::STATE_CANCELED, true)->save(); - } - catch (Exception $e) - { - Mage::log('Could not cancel order '. $order->getId() .'.', null, Mage::helper('bitpay')->getLogFile()); - Mage::logException($e); - } - } - - /** - * given Mage_Core_Model_Abstract, return api-friendly address - * - * @param $address - * - * @return array - */ - public function ExtractAddress($address) - { - Mage::log( - sprintf('Extracting addess'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - $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; - } - - /** - * @param $payment - * @param $amount - * - * @return Bitpay_Bitcoins_Model_PaymentMethod - */ - public function CreateInvoiceAndRedirect($payment, $amount) - { - Mage::log( - sprintf('Creating invoice and redirecting'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - 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, - ); - - /** - * Some merchants are using custom extensions where the shipping - * address may not be set, this will only extract the shipping - * address if there is one already set. - */ - if ($order->getShippingAddress()) { - $options = array_merge( - $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', Zend_Log::CRIT, Mage::helper('bitpay')->getLogFile()); - Mage::log($invoice['error'], Zend_Log::CRIT, Mage::helper('bitpay')->getLogFile()); - Mage::throwException("Error creating BitPay 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; - } - - /** - * @return string - */ - public function getOrderPlaceRedirectUrl() - { - Mage::log( - sprintf('Getting order place redirect url'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - 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 - * - * @param string $quoteId - * - * @return boolean|string - */ - public function getQuoteHash($quoteId) - { - Mage::log( - sprintf('Getting the quote hash'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - $quote = Mage::getModel('sales/quote')->load($quoteId, 'entity_id'); - if (!$quote) - { - Mage::log('getQuoteTimestamp: quote not found', Zend_Log::ERR, Mage::helper('bitpay')->getLogFile()); - - 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 - - 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 2df09ee..0000000 --- a/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn.php +++ /dev/null @@ -1,33 +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 b5b563a..0000000 --- a/app/code/community/Bitpay/Bitcoins/Model/Resource/Ipn/Collection.php +++ /dev/null @@ -1,44 +0,0 @@ -_init('Bitcoins/ipn'); - } - - public function delete() - { - foreach ($this->getItems() as $item) { - $item->delete(); - } - } -} 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 a70e8e1..0000000 --- a/app/code/community/Bitpay/Bitcoins/Model/Source/Speed.php +++ /dev/null @@ -1,50 +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 9286605..0000000 --- a/app/code/community/Bitpay/Bitcoins/controllers/IndexController.php +++ /dev/null @@ -1,118 +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() { - Mage::log( - sprintf('Incoming IPN from bitpay'), - Zend_Log::DEBUG, - Mage::helper('bitpay')->getLogFile() - ); - - require Mage::getBaseDir('lib').'/bitpay/bp_lib.php'; - $apiKey = Mage::getStoreConfig('payment/Bitcoins/api_key'); - $invoice = bpVerifyNotification($apiKey); - - if (is_string($invoice)) - { - Mage::log("bitpay callback error: $invoice", Zend_Log::ERR, Mage::helper('bitpay')->getLogFile()); - throw new Exception('Bitpay callback error:' . $invoice); - } - - // get the order - if (isset($invoice['posData']['quoteId'])) - { - $quoteId = $invoice['posData']['quoteId']; - $order = Mage::getModel('sales/order')->load($quoteId, 'quote_id'); - } - elseif (isset($invoice['posData']['orderId'])) - { - $orderId = $invoice['posData']['orderId']; - $order = Mage::getModel('sales/order')->loadByIncrementId($orderId); - } - else - { - Mage::log('Invalid posData, does not contain quoteId or orderId.', Zend_Log::ERR, Mage::helper('bitpay')->getLogFile()); - throw new Exception('Invalid Bitpay IPN received.'); - } - - // save the ipn so that we can find it when the user clicks "Place Order" - Mage::getModel('Bitcoins/ipn')->Record($invoice); - - if (!$order->getId()) - { - Mage::log('Order object does not contain an ID', Zend_Log::ERR, Mage::helper('bitpay')->getLogFile()); - throw new Exception('Order object does not contain an ID'); - } - - // update the order if it exists already - // BitPay Statuses - // new, paid, confirmed, complete, expired, invalid - Mage::log('Received IPN with "' . $invoice['status'] . '" status', Zend_Log::DEBUG, Mage::helper('bitpay')->getLogFile()); - switch($invoice['status']) - { - - // Map to Magento state Processing - case 'paid': - // Mark paid if there is an outstanding total - $method = Mage::getModel('Bitcoins/paymentMethod'); - $method->MarkOrderPaid($order); - break; - - // Map to Magento status Complete - case 'confirmed': - case 'complete': - // Mark confirmed/complete if the order has been paid - $method = Mage::getModel('Bitcoins/paymentMethod'); - $method->MarkOrderComplete($order); - //Mage::log('Received a ' . $invoice['status'] . ' notification from BitPay but this order is not paid yet. Possible internal error with Magento. Check order status to confirm.', Zend_Log::ERR, Mage::helper('bitpay')->getLogFile()); - break; - - // Map to Magento State Closed - 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 e669244..0000000 --- a/app/code/community/Bitpay/Bitcoins/etc/config.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - standard - - Bitpay_Bitcoins - bitpay_callback - - - - - - - bitcoins.xml - - - - - - - - - 1.1.0 - - - - - - - Bitpay_Bitcoins_Block - - - - - - Bitpay_Bitcoins_Helper - - - - - - Bitpay_Bitcoins_Model - Bitcoins_resource - - - Bitpay_Bitcoins_Model_Resource - - - bitpay_ipns
-
-
-
-
- - - - - - Bitpay_Bitcoins - - - core_setup - - - - - core_write - - - - - core_read - - - -
- - - - - - - - */30 * * * * - - - Bitcoins/Observer::cleanExpired - - - - - - - - - 1 - Bitcoins/paymentMethod - Bitcoins - low - 0 - BTC, USD, EUR, GBP, JPY, CAD, AUD, CNY, CHF, SEK, NZD, KRW, AED, AFN, ALL, AMD, ANG, AOA, ARS, AWG, AZN, BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BRL, BSD, BTN, BWP, BYR, BZD, CDF, CLF, CLP, COP, CRC, CVE, CZK, DJF, DKK, DOP, DZD, EEK, EGP, ETB, FJD, FKP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, HKD, HNL, HRK, HTG, HUF, IDR, ILS, INR, IQD, ISK, JEP, JMD, JOD, KES, KGS, KHR, KMF, KWD, KYD, KZT, LAK, LBP, LKR, LRD, LSL, LTL, LVL, LYD, MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRO, MUR, MVR, MWK, MXN, MYR, MZN, NAD, NGN, NIO, NOK, NPR, OMR, PAB, PEN, PGK, PHP, PKR, PLN, PYG, QAR, RON, RSD, RUB, RWF, SAR, SBD, SCR, SDG, SGD, SHP, SLL, SOS, SRD, STD, SVC, SYP, SZL, THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, UAH, UGX, UYU, UZS, VEF, VND, VUV, WST, XAF, XAG, XAU, XCD, XOF, XPF, YER, ZAR, ZMW, ZWL - authorize - - - -
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 9dfced2..0000000 --- a/app/code/community/Bitpay/Bitcoins/etc/system.xml +++ /dev/null @@ -1,122 +0,0 @@ - - - - - - - - - 670 - 1 - 1 - 0 -
To log into your merchant account or download the latest version of this plugin, visit our website: https://bitpay.com/
]]>
- - - - select - adminhtml/system_config_source_yesno - 0 - 1 - 1 - 0 - - - - <label>Title</label> - <frontend_type>text</frontend_type> - <sort_order>1</sort_order> - <show_in_default>1</show_in_default> - <show_in_website>1</show_in_website> - <show_in_store>0</show_in_store> - - - - - 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 - High: an invoice is confirmed immediately when payment received.
Medium: an invoice is confirmed after 1 block confirmation by the network (~10 mins).
Low: an invoice is confirmed after 6 block confirmations by the network (~1 hour).
The default and safest setting is "Low". A "High" setting is quicker to generate a payment confirmation but is riskier since the transaction could have not been officially confirmed by the Bitcoin network itself.]]>
-
- - - - text - 6 - 1 - 1 - 0 - - - - - text - 7 - 1 - 1 - 0 - - - - - select - adminhtml/system_config_source_yesno - 8 - 1 - 1 - 0 - If you want to automatically set an order's state to complete when BitPay sends a completed payment notification, change this option to "Yes". The default setting of "No" will keep the order in a processing state for you to update as needed. Invoices contained within the order will be paid regardless of this setting. - - -
-
-
-
-
-
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 89fe86f..0000000 --- a/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-0.1.0-1.0.0.php +++ /dev/null @@ -1,50 +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(); 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 b893298..0000000 --- a/app/code/community/Bitpay/Bitcoins/sql/Bitcoins_setup/upgrade-1.0.0-1.1.0.php +++ /dev/null @@ -1,32 +0,0 @@ -startSetup(); - -$installer->run("ALTER TABLE `{$installer->getTable('Bitcoins/ipn')}` ADD `pos_data` VARCHAR( 256 ) NOT NULL AFTER `url`;"); - -$installer->endSetup(); 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..3e0ec96 --- /dev/null +++ b/app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Extension.php @@ -0,0 +1,26 @@ +getFieldConfig()->php_extension; + + if (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..2b9454a --- /dev/null +++ b/app/code/community/Bitpay/Core/Block/Adminhtml/System/Config/Form/Field/Header.php @@ -0,0 +1,28 @@ +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..0dce41c --- /dev/null +++ b/app/code/community/Bitpay/Core/Block/Form/Bitpay.php @@ -0,0 +1,14 @@ +setTemplate('bitpay/form/bitpay.phtml'); + } +} 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..0d1dd23 --- /dev/null +++ b/app/code/community/Bitpay/Core/Block/Iframe.php @@ -0,0 +1,68 @@ +setTemplate('bitpay/iframe.phtml'); + } + + /** + * create an invoice and return the url so that iframe.phtml can display it + * + * @return string + */ + public function getIframeUrl() + { + if (Mage::getModel('bitpay/ipn')->getQuotePaid($this->getQuote()->getId())) { + return 'paid'; // quote's already paid, so don't show the iframe + } + + /*** @var Bitpay_Core_Model_PaymentMethod ***/ + $method = $this->getQuote()->getPayment()->getMethodInstance(); + $options = array_merge( + array( + 'currency' => $this->getQuote()->getQuoteCurrencyCode(), + 'fullNotifications' => 'true', + 'notificationURL' => Mage::getUrl('bitpay/ipn'), + 'redirectURL' => Mage::getUrl('checkout/onepage/success'), + 'transactionSpeed' => Mage::getStoreConfig('payment/bitpay/speed'), + ), + $method->extractAddress($this->getQuote()->getShippingAddress()) + ); + Mage::helper('bitpay')->debugData($options); + + // 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($this->getQuote()->getGrandTotal(), 4); + + //serialize info about the quote to detect changes + $hash = $method->getQuoteHash($this->getQuote()->getId()); + + Mage::helper('bitpay')->registerAutoloader(); + //$invoice = bpCreateInvoice($quoteId, $price, array('quoteId' => $quoteId, 'quoteHash' => $hash), $options); + $invoice = array('url' => 'https://test.bitpay.com/invoice?id=5NxFkXcJbCSivtQRJa4kHP'); + + if (array_key_exists('error', $invoice)) { + Mage::helper('bitpay')->debugData( + array( + 'Error creating bitpay invoice', + $invoice['error'], + ) + ); + Mage::throwException("Error creating BitPay invoice. Please try again or use another payment option."); + + return false; + } + + return $invoice['url'].'&view=iframe'; + } +} 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..4e003b1 --- /dev/null +++ b/app/code/community/Bitpay/Core/Block/Info.php @@ -0,0 +1,26 @@ +setTemplate('bitpay/info/default.phtml'); + } + + public function getBitpayInvoiceUrl() + { + $order = $this->getInfo()->getOrder(); + $incrementId = $order->getIncrementId(); + + $bitpayInvoice = Mage::getModel('bitpay/invoice')->load($incrementId, 'increment_id'); + + if ($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..b67a2ab --- /dev/null +++ b/app/code/community/Bitpay/Core/Helper/Data.php @@ -0,0 +1,265 @@ +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 (null === $this->_autoloaderRegistered) { + require_once Mage::getBaseDir('lib').'/Bitpay/Autoloader.php'; + \Bitpay\Autoloader::register(); + $this->_autoloaderRegistered = true; + $this->debugData('BitPay Autoloader has been registered'); + } + } + + /** + * This function will generate keys that will need to be paired with BitPay + * using + */ + public function generateAndSaveKeys() + { + $this->debugData('Generating Keys'); + $this->registerAutoloader(); + + $this->_privateKey = new Bitpay\PrivateKey('payment/bitpay/private_key'); + $this->_privateKey->generate(); + + $this->_publicKey = new Bitpay\PublicKey('payment/bitpay/public_key'); + $this->_publicKey + ->setPrivateKey($this->_privateKey) + ->generate(); + + $this->getKeyManager()->persist($this->_publicKey); + $this->getKeyManager()->persist($this->_privateKey); + + $this->debugData('Keys persisted to database'); + } + + /** + * Send a pairing request to BitPay to receive a Token + */ + public function sendPairingRequest($pairingCode) + { + $this->debugData( + sprintf('Sending Paring Request with pairing code "%s"', $pairingCode) + ); + + // Generate/Regenerate keys + $this->generateAndSaveKeys(); + $sin = $this->getSinKey(); + + $this->debugData( + sprintf('Sending Pairing Request for SIN "%s"', (string) $sin) + ); + + $token = $this->getBitpayClient()->createToken( + array( + 'id' => (string) $sin, + 'pairingCode' => $pairingCode, + 'label' => sprintf('[Magento Store] %s', Mage::app()->getStore()->getName()), + ) + ); + + $this->debugData('Token Obtained'); + + $config = new \Mage_Core_Model_Config(); + $config->saveConfig('payment/bitpay/token', $token->getToken()); + + $this->debugData('Token Persisted persisted to database'); + } + + /** + * @return Bitpay\SinKey + */ + public function getSinKey() + { + if (null !== $this->_sin) { + return $this->_sin; + } + + $this->debugData('Getting SIN Key'); + + $this->registerAutoloader(); + $this->_sin = new Bitpay\SinKey(); + $this->_sin + ->setPublicKey($this->getPublicKey()) + ->generate(); + + return $this->_sin; + } + + public function getPublicKey() + { + if (null !== $this->_publicKey) { + return $this->_publicKey; + } + + $this->debugData('Getting Public Key'); + + $this->_publicKey = $this->getKeyManager()->load('payment/bitpay/public_key'); + + if (!$this->_publicKey) { + $this->generateAndSaveKeys(); + } + + return $this->_publicKey; + } + + public function getPrivateKey() + { + if (null !== $this->_privateKey) { + return $this->_privateKey; + } + + $this->debugData('Getting Private Key'); + + $this->_privateKey = $this->getKeyManager()->load('payment/bitpay/private_key'); + + if (!$this->_publicKey) { + $this->generateAndSaveKeys(); + } + + return $this->_privateKey; + } + + /** + * @return Bitpay\KeyManager + */ + public function getKeyManager() + { + if (null == $this->_keyManager) { + $this->registerAutoloader(); + $this->debugData('Creating instance of KeyManager'); + $this->_keyManager = new Bitpay\KeyManager(new Bitpay\Storage\MagentoStorage()); + } + + return $this->_keyManager; + } + + /** + * Initialize an instance of Bitpay or return the one that has already + * been created. + * + * @return Bitpay\Bitpay + */ + public function getBitpay() + { + if (null === $this->_bitpay) { + $this->registerAutoloader(); + $this->_bitpay = new Bitpay\Bitpay(array('bitpay' => $this->getBitpayConfig())); + } + + return $this->_bitpay; + } + + /** + * Sets up the bitpay container with settings for magento + * + * @return array + */ + protected function getBitpayConfig() + { + return array( + 'public_key' => 'payment/bitpay/public_key', + 'private_key' => 'payment/bitpay/private_key', + 'network' => Mage::getStoreConfig('payment/bitpay/network'), + 'key_storage' => '\\Bitpay\\Storage\\MagentoStorage', + ); + } + + /** + * @return Bitpay\Client + */ + public function getBitpayClient() + { + if (null !== $this->_client) { + return $this->_client; + } + + $this->registerAutoloader(); + + $this->_client = new Bitpay\Client\Client(); + $this->_client->setPublicKey($this->getPublicKey()); + $this->_client->setPrivateKey($this->getPrivateKey()); + $this->_client->setNetwork($this->getBitpay()->get('network')); + $this->_client->setAdapter($this->getBitpay()->get('adapter')); + $this->_client->setToken($this->getToken()); + + return $this->_client; + } + + public function getToken() + { + $this->registerAutoloader(); + $token = new Bitpay\Token(); + $token->setToken(Mage::getStoreConfig('payment/bitpay/token')); + + return $token; + } +} 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..410fd1e --- /dev/null +++ b/app/code/community/Bitpay/Core/Model/Config/PairingCode.php @@ -0,0 +1,47 @@ +getValue()); + + if (empty($pairingCode)) { + return; + } + + Mage::helper('bitpay')->debugData('Attempting Pair Code'); + + try { + Mage::helper('bitpay')->sendPairingRequest($pairingCode); + } catch (Exception $e) { + Mage::helper('bitpay')->debugData( + sprintf('Error Pairing Code "%s"', $e->getMessage()) + ); + Mage::getSingleton('core/session')->addError( + 'There was an error while trying to pair the pairing code. Please try again or enabled debug mode and send the "payment_bitpay.log" file to support.' + ); + + return; + } + + Mage::getSingleton('core/session')->addSuccess('Pairing Code 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..7733daa --- /dev/null +++ b/app/code/community/Bitpay/Core/Model/Invoice.php @@ -0,0 +1,70 @@ +_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) + { + $this->addData( + array( + 'id' => $invoice->getId(), + //'updated_at' => 'NOW()', + 'url' => $invoice->getUrl(), + 'pos_data' => $invoice->getPosData(), + 'status' => $invoice->getStatus(), + 'btc_price' => $invoice->getBtcPrice(), + //'btc_due' => $invoice->getBtcDue(), + 'price' => $invoice->getPrice(), + 'currency' => $invoice->getCurrency()->getCode(), + //'ex_rates' => $invoice->getExRates(), + 'order_id' => $invoice->getOrderId(), + 'invoice_time' => $invoice->getInvoiceTime(), + 'expiration_time' => $invoice->getExpirationTime(), + 'current_time' => $invoice->getCurrentTime(), + 'btc_paid' => $invoice->getBtcPaid(), + 'rate' => $invoice->getRate(), + 'exception_status' => $invoice->getExceptionStatus(), + //'token' => $invoice->getToken(), + ) + ); + + 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 prepateWithOrder($order) + { + $this->addData( + array( + 'quote_id' => $order->getQuoteId(), + 'increment_id' => $order->getIncrementId(), + ) + ); + + 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..9c05f7e --- /dev/null +++ b/app/code/community/Bitpay/Core/Model/Ipn.php @@ -0,0 +1,17 @@ +_init('bitpay/ipn'); + } +} 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..0c20e7b --- /dev/null +++ b/app/code/community/Bitpay/Core/Model/Method/Bitcoin.php @@ -0,0 +1,262 @@ +debugData('authorizing new order'); + + // Create BitPay Invoice + $invoice = $this->initializeInvoice(); + $invoice = $this->prepareInvoice($invoice, $payment, $amount); + + try { + $bitpayInvoice = Mage::helper('bitpay')->getBitpayClient()->createInvoice($invoice); + } catch (Exception $e) { + $this->debugData($e->getMessage()); + $this->debugData( + array( + Mage::helper('bitpay')->getBitpayClient()->getRequest()->getBody(), + Mage::helper('bitpay')->getBitpayClient()->getResponse()->getBody(), + ) + ); + Mage::throwException('Could not authorize transaction.'); + } + + self::$_redirectUrl = $bitpayInvoice->getUrl(); + $this->debugData( + array( + 'BitPay Invoice created', + sprintf('Invoice URL: "%s"', $bitpayInvoice->getUrl()), + ) + ); + + // Save BitPay Invoice in database for reference + $mirrorInvoice = Mage::getModel('bitpay/invoice') + ->prepareWithBitpayInvoice($bitpayInvoice) + ->prepateWithOrder($payment->getOrder()) + ->save(); + + $this->debugData($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 (empty($token)) { + /** + * Merchant must goto their account and create a pairing code to + * enter in. + */ + $this->debugData('Magento store does not have a BitPay token.'); + + return false; + } + + return true; + } + + /** + * Fetchs an invoice from BitPay + * + * @param string $id + * @return Bitpay\Invoice + */ + public function fetchInvoice($id) + { + Mage::helper('bitpay')->registerAutoloader(); + + $client = Mage::helper('bitpay')->getBitpayClient(); + $invoice = $client->getInvoice($id); + + return $invoice; + } + + /** + * given Mage_Core_Model_Abstract, return api-friendly address + * + * @param $address + * + * @return array + */ + public function extractAddress($address) + { + $this->debugData( + sprintf('Extracting addess') + ); + + $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; + } + + /** + * This is called when a user clicks the `Place Order` button + * + * @return string + */ + public function getOrderPlaceRedirectUrl() + { + $this->debugData( + 'Customer wants to place the order. Create invoice and redirect user to invoice' + ); + + 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(); + $invoice->setFullNotifications(true); + $invoice->setTransactionSpeed(Mage::getStoreConfig('payment/bitpay/speed')); + $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) + { + $invoice->setOrderId($payment->getOrder()->getIncrementId()); + $invoice->setPosData( + json_encode( + array( + 'id' => $payment->getOrder()->getIncrementId(), + ) + ) + ); + + $invoice = $this->addCurrencyInfo($invoice, $payment->getOrder()); + $invoice = $this->addPriceInfo($invoice, $amount); + $invoice = $this->addBuyerInfo($invoice, $payment->getOrder()); + + 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) + { + $buyer = new Bitpay\Buyer(); + $buyer->setFirstName($order->getCustomerFirstname()); + $buyer->setLastName($order->getCustomerLastname()); + $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) + { + $currency = new Bitpay\Currency(); + $currency->setCode($order->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) + { + $item = new \Bitpay\Item(); + $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..3f40e16 --- /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..e86d071 --- /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..bd39876 --- /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..4ce1711 --- /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..159e267 --- /dev/null +++ b/app/code/community/Bitpay/Core/Model/Network.php @@ -0,0 +1,25 @@ + self::NETWORK_LIVENET, 'label' => Mage::helper('bitpay')->__('Livenet')), + array('value' => self::NETWORK_TESTNET, 'label' => Mage::helper('bitpay')->__('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..b925580 --- /dev/null +++ b/app/code/community/Bitpay/Core/Model/Observer.php @@ -0,0 +1,74 @@ +debugData( + 'cronjob: started' + ); + + $apiKey = Mage::getStoreConfig('payment/bitpay/api_key'); + + if (empty($apiKey)) { + Mage::helper('bitpay')->debugData( + 'cronjob: Api Key not set.' + ); + + return; // Api Key needs to be set + } + + /** + * Get all of the orders that are open and have not received an IPN for + * complete, expired, or invalid. + * + * If anyone knows of a better way to do this, please let me know + */ + $orders = Mage::getModel('bitpay/ipn')->getOpenOrders(); + + /** + * 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; + + // Does the order need to be updated? + // Yes? Update Order Status + // No? continue + } + + Mage::helper('bitpay')->debugData( + 'cronjob: end' + ); + } + + /** + * Method that is called via the magento cron to update orders if the + * invoice has expired + */ + public function cleanExpired() + { + Mage::helper('bitpay')->cleanExpired(); + } +} 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..ab584b8 --- /dev/null +++ b/app/code/community/Bitpay/Core/Model/Resource/Mysql4/Setup.php @@ -0,0 +1,11 @@ + self::STATUS_NEW, 'label' => Mage::helper('bitpay')->__('New')), + array('value' => self::STATUS_PAID, 'label' => Mage::helper('bitpay')->__('Paid')), + array('value' => self::STATUS_CONFIRMED, 'label' => Mage::helper('bitpay')->__('Confirmed')), + array('value' => self::STATUS_COMPLETE, 'label' => Mage::helper('bitpay')->__('Complete')), + array('value' => self::STATUS_EXPIRED, 'label' => Mage::helper('bitpay')->__('Expired')), + array('value' => self::STATUS_INVALID, 'label' => Mage::helper('bitpay')->__('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..387b37a --- /dev/null +++ b/app/code/community/Bitpay/Core/Model/TransactionSpeed.php @@ -0,0 +1,24 @@ + self::SPEED_LOW, 'label' => Mage::helper('bitpay')->__('Low')), + array('value' => self::SPEED_MEDIUM, 'label' => Mage::helper('bitpay')->__('Medium')), + array('value' => self::SPEED_HIGH, 'label' => Mage::helper('bitpay')->__('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..98263e5 --- /dev/null +++ b/app/code/community/Bitpay/Core/controllers/IndexController.php @@ -0,0 +1,34 @@ +getRequest()->getParams(); + $paid = false; + if (isset($params['paid'])) { + Mage::helper('bitpay')->registerAutoloader(); + Mage::helper('bitpay')->debugData( + $params + ); + //$quoteId = $params['quote']; + //$paid = Mage::getModel('bitpay/ipn')->getQuotePaid($quoteId); + } + + $this->loadLayout(); + $this->getResponse()->setHeader('Content-type', 'application/json'); + $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..490b2fd --- /dev/null +++ b/app/code/community/Bitpay/Core/controllers/IpnController.php @@ -0,0 +1,103 @@ +registerAutoloader(); + Mage::helper('bitpay')->debugData( + array( + sprintf('Incoming IPN from bitpay'), + getallheaders(), + file_get_contents('php://input'), + ) + ); + + // Magento doesn't seem to have a way to get the Request body + $ipn = json_decode(file_get_contents('php://input')); + $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 : '', + 'btc_price' => isset($ipn->btcPrice) ? $ipn->btcPrice : '', + 'price' => isset($ipn->price) ? $ipn->price : '', + 'currency' => isset($ipn->currency) ? $ipn->currency : '', + 'invoice_time' => isset($ipn->invoiceTime) ? $ipn->invoiceTime : '', + 'expiration_time' => isset($ipn->expirationTime) ? $ipn->expirationTime : '', + 'current_time' => isset($ipn->currentTime) ? $ipn->currentTime : '', + 'btc_paid' => isset($ipn->btcPaid) ? $ipn->btcPaid : '', + 'rate' => isset($ipn->rate) ? $ipn->rate : '', + 'exception_status' => isset($ipn->exceptionStatus) ? $ipn->exceptionStatus : '', + ) + )->save(); + + if (empty($ipn->id) || !isset($ipn->posData->id)) { + Mage::helper('bitpay')->debugData( + sprintf('Did not receive order id in IPN. See IPN "%s" in database.', $mageIpn->getId()) + ); + throw new Exception('Invalid Bitpay IPN received.'); + } + + $order = Mage::getModel('sales/order')->loadByIncrementId($ipn->posData->id); + + if (!$order->getId()) { + Mage::helper('bitpay')->debugData('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); + + // Does the status match? + if ($invoice && $invoice->getStatus() != $ipn->status) { + Mage::getModel('bitpay/method_bitcoin')->debugData('IPN status and status from BitPay are different'); + Mage::throwException('There was an error processing the ipn'); + } + + // Does the price match? + if ($invoice && $invoice->getPrice() != $ipn->price) { + Mage::getModel('bitpay/method_bitcoin')>debugData('Price difference'); + Mage::throwException('There was an error processing the ipn'); + } + + // Update the order to notifiy that it has been paid + if (in_array($invoice->getStatus(), array('paid', 'confirmed', 'complete'))) { + $payment = Mage::getModel('sales/order_payment')->setOrder($order); + $payment->registerCaptureNotification($invoice->getPrice()); + $order->addPayment($payment)->save(); + } + + // use state as defined by Merchant + $state = Mage::getStoreConfig(sprintf('payment/bitpay/invoice_%s', $invoice->getStatus())); + $order->addStatusToHistory( + $state, + sprintf('Incoming IPN status "%s" updateded order state to "%s"', $invoice->getStatus(), $state) + )->save(); + } +} 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..877f18d --- /dev/null +++ b/app/code/community/Bitpay/Core/etc/config.xml @@ -0,0 +1,130 @@ + + + + + + 2.0.0 + + + + + + + standard + + 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 + + + bitpay_invoices
+
+ + bitpay_ipns
+
+
+
+
+ + + + + core_write + + + + + core_read + + + + + Bitpay_Core + Bitpay_Core_Model_Resource_Mysql4_Setup + + + core_setup + + + +
+ + + + + bitpay/method_bitcoin + new + authorize + 0 + Bitcoin + testnet + 0 + bitpay/ipn + checkout/onepage/success + medium + new + processing + processing + complete + canceled + canceled + + + +
diff --git a/app/code/community/Bitpay/Core/etc/system.xml b/app/code/community/Bitpay/Core/etc/system.xml new file mode 100644 index 0000000..df77255 --- /dev/null +++ b/app/code/community/Bitpay/Core/etc/system.xml @@ -0,0 +1,274 @@ + + + + + + + + + 670 + 1 + 1 + 1 + +
+ + complex + bitpay/adminhtml_system_config_form_field_header + 0 + 1 + 1 + 1 +
+ + + + https://bitpay.com/api-tokens and put + the code that was generated in this field. Once you have paired your Mangento store you can begin accepted Bitcoins as payment + on your store.]]> + + text + bitpay/config_pairingCode + 1 + 1 + 1 + 1 + + + + select + adminhtml/system_config_source_yesno + 10 + 1 + 1 + 1 + + + <label>Title</label> + <comment> + What your customers will see during their checkout + experience. + </comment> + <frontend_type>text</frontend_type> + <sort_order>20</sort_order> + <show_in_default>1</show_in_default> + <show_in_website>1</show_in_website> + <show_in_store>1</show_in_store> + + + + + test.bitpay.com.]]> + + select + bitpay/network + 30 + 1 + 1 + 1 + + + + By enabling this, it will output more verbose information in log files. + select + adminhtml/system_config_source_yesno + 40 + 1 + 1 + 1 + + + + text + 50 + 1 + 1 + 1 + + + + text + 60 + 1 + 1 + 1 + + + + select + bitpay/transactionSpeed + 70 + 1 + 1 + 1 + High: an invoice is confirmed immediately when payment received.
Medium: an invoice is confirmed after 1 block confirmation by the network (~10 mins).
Low: an invoice is confirmed after 6 block confirmations by the network (~1 hour).
The default and safest setting is "Low". A "High" setting is quicker to generate a payment confirmation but is riskier since the transaction could have not been officially confirmed by the Bitcoin network itself.]]>
+
+ + + adminhtml/system_config_form_field_heading + 75 + 1 + 1 + 1 + + + + allowspecific + 76 + adminhtml/system_config_source_payment_allspecificcountries + 1 + 1 + 0 + + + + multiselect + 77 + adminhtml/system_config_source_country + 1 + 1 + 0 + + + + text + 80 + 1 + 1 + 0 + + + + text + 90 + 1 + 1 + 0 + + + + text + 99 + 1 + 1 + 0 + validate-number + + + + adminhtml/system_config_form_field_heading + 100 + 1 + 1 + 1 + + + + + + An invoice is considered "paid" when the bitcoin + network sees a transaction. + + select + adminhtml/system_config_source_order_status + 120 + 1 + 1 + 1 + + + + + A confirmed invoice means that the Bitcoin network + has approved the transaction. + + select + adminhtml/system_config_source_order_status + 130 + 1 + 1 + 1 + + + + + Complete invoices mean that you have gotten credit + for the payment in your BitPay merchant account. + + select + adminhtml/system_config_source_order_status + 140 + 1 + 1 + 1 + + + + + adminhtml/system_config_form_field_heading + + + + 500 + 1 + 1 + 1 + + + + gmp + bitpay/adminhtml_system_config_form_field_extension + 510 + 1 + 1 + 1 + + + + openssl + bitpay/adminhtml_system_config_form_field_extension + 510 + 1 + 1 + 1 + +
+
+
+
+
+
diff --git a/app/code/community/Bitpay/Core/sql/bitpay_setup/mysql4-install-2.0.0.php b/app/code/community/Bitpay/Core/sql/bitpay_setup/mysql4-install-2.0.0.php new file mode 100644 index 0000000..3425f49 --- /dev/null +++ b/app/code/community/Bitpay/Core/sql/bitpay_setup/mysql4-install-2.0.0.php @@ -0,0 +1,64 @@ +startSetup(); + +/** + * IPN Log Table, used to keep track of incoiming IPNs + */ +$this->run(sprintf('DROP TABLE IF EXISTS `%s`;', $this->getTable('bitpay/ipn'))); +$ipnTable = new Varien_Db_Ddl_Table(); +$ipnTable->setName($this->getTable('bitpay/ipn')); +$ipnTable->addColumn('id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11, array('auto_increment' => true, 'nullable' => false, 'primary' => true,)); +$ipnTable->addColumn('invoice_id', Varien_Db_Ddl_Table::TYPE_TEXT, 200); +$ipnTable->addColumn('url', Varien_Db_Ddl_Table::TYPE_TEXT, 400); +$ipnTable->addColumn('status', Varien_Db_Ddl_Table::TYPE_TEXT, 20); +$ipnTable->addColumn('btc_price', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$ipnTable->addColumn('price', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$ipnTable->addColumn('currency', Varien_Db_Ddl_Table::TYPE_TEXT, 10); +$ipnTable->addColumn('invoice_time', Varien_Db_Ddl_Table::TYPE_INTEGER, 11); +$ipnTable->addColumn('expiration_time', Varien_Db_Ddl_Table::TYPE_INTEGER, 11); +$ipnTable->addColumn('curent_time', Varien_Db_Ddl_Table::TYPE_INTEGER, 11); +$ipnTable->addColumn('pos_data', Varien_Db_Ddl_Table::TYPE_TEXT, 255); +$ipnTable->addColumn('btc_paid', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$ipnTable->addColumn('rate', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$ipnTable->addColumn('exception_status', Varien_Db_Ddl_Table::TYPE_TEXT, 255); + +$ipnTable->setOption('type', 'InnoDB'); +$ipnTable->setOption('charset', 'utf8'); +$this->getConnection()->createTable($ipnTable); + +/** + * Table used to keep track of invoices that have been created. The + * IPNs that are received are used to update this table. + */ +$this->run(sprintf('DROP TABLE IF EXISTS `%s`;', $this->getTable('bitpay/invoice'))); +$invoiceTable = new Varien_Db_Ddl_Table(); +$invoiceTable->setName($this->getTable('bitpay/invoice')); +$invoiceTable->addColumn('id', Varien_Db_Ddl_Table::TYPE_TEXT, 64, array('nullable' => false, 'primary' => true)); +$invoiceTable->addColumn('quote_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11); +$invoiceTable->addColumn('increment_id', Varien_Db_Ddl_Table::TYPE_INTEGER, 11); +$invoiceTable->addColumn('updated_at', Varien_Db_Ddl_Table::TYPE_TIMESTAMP); +$invoiceTable->addColumn('url', Varien_Db_Ddl_Table::TYPE_TEXT, 200); +$invoiceTable->addColumn('pos_data', Varien_Db_Ddl_Table::TYPE_TEXT, 255); +$invoiceTable->addColumn('status', Varien_Db_Ddl_Table::TYPE_TEXT, 20); +$invoiceTable->addColumn('btc_price', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$invoiceTable->addColumn('btc_due', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$invoiceTable->addColumn('price', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$invoiceTable->addColumn('currency', Varien_Db_Ddl_Table::TYPE_TEXT, 10); +$invoiceTable->addColumn('ex_rates', Varien_Db_Ddl_Table::TYPE_TEXT, 255); +$invoiceTable->addColumn('order_id', Varien_Db_Ddl_Table::TYPE_TEXT, 64); +$invoiceTable->addColumn('invoice_time', Varien_Db_Ddl_Table::TYPE_INTEGER, 11); +$invoiceTable->addColumn('expiration_time', Varien_Db_Ddl_Table::TYPE_INTEGER, 11); +$invoiceTable->addColumn('current_time', Varien_Db_Ddl_Table::TYPE_INTEGER, 11); +$invoiceTable->addColumn('btc_paid', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$invoiceTable->addColumn('rate', Varien_Db_Ddl_Table::TYPE_DECIMAL, array(16, 8)); +$invoiceTable->addColumn('exception_status', Varien_Db_Ddl_Table::TYPE_TEXT, 255); +$invoiceTable->addColumn('token', Varien_Db_Ddl_Table::TYPE_TEXT, 164); +$invoiceTable->setOption('type', 'InnoDB'); +$invoiceTable->setOption('charset', 'utf8'); +$this->getConnection()->createTable($invoiceTable); + +$this->endSetup(); diff --git a/app/design/adminhtml/default/default/layout/bitpay.xml b/app/design/adminhtml/default/default/layout/bitpay.xml new file mode 100644 index 0000000..eac80cb --- /dev/null +++ b/app/design/adminhtml/default/default/layout/bitpay.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/design/adminhtml/default/default/template/bitpay/info/default.phtml b/app/design/adminhtml/default/default/template/bitpay/info/default.phtml new file mode 100644 index 0000000..89dbbf8 --- /dev/null +++ b/app/design/adminhtml/default/default/template/bitpay/info/default.phtml @@ -0,0 +1,18 @@ + + +

Ordered with BitPay

+ +getBitpayInvoiceUrl()): ?> +

+ View Invoice +

+ 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..031e411 --- /dev/null +++ b/app/design/adminhtml/default/default/template/bitpay/system/config/field/header.phtml @@ -0,0 +1,17 @@ + + +
+ BitPay + 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 dc8cbd8..0000000 --- a/app/design/frontend/base/default/layout/bitcoins.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - 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..eac80cb --- /dev/null +++ b/app/design/frontend/base/default/layout/bitpay.xml @@ -0,0 +1,9 @@ + + + + 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 eb0faf1..0000000 --- a/app/design/frontend/base/default/template/bitcoins/iframe.phtml +++ /dev/null @@ -1,67 +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..858f869 --- /dev/null +++ b/app/design/frontend/base/default/template/bitpay/form/bitpay.phtml @@ -0,0 +1,19 @@ + + +getMethodCode() ?> + 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..a4c49ee --- /dev/null +++ b/app/design/frontend/base/default/template/bitpay/iframe.phtml @@ -0,0 +1,50 @@ +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..444ac8b --- /dev/null +++ b/app/design/frontend/base/default/template/bitpay/info/default.phtml @@ -0,0 +1,9 @@ + +

escapeHtml($this->getMethod()->getTitle()) ?>

+ +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 4317ca4..0000000 --- a/app/etc/modules/Bitpay_Bitcoins.xml +++ /dev/null @@ -1,36 +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..86c25cc --- /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 index 34e324b..7cb68f3 100644 --- a/build.xml +++ b/build.xml @@ -1,27 +1,8 @@ diff --git a/build/build.properties b/build/build.properties index 8881bae..d10344e 100644 --- a/build/build.properties +++ b/build/build.properties @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2011-2014 BitPay LLC +# 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 diff --git a/build/n98-magerun.yaml b/build/n98-magerun.yaml index 246b2ea..d797295 100644 --- a/build/n98-magerun.yaml +++ b/build/n98-magerun.yaml @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2011-2014 BitPay LLC +# 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 diff --git a/build/phpunit.xml.dist b/build/phpunit.xml.dist index 03a3d1a..72a17db 100644 --- a/build/phpunit.xml.dist +++ b/build/phpunit.xml.dist @@ -45,10 +45,10 @@ - ../app/code/community/Bitpay/Bitcoins/ + ../app/code/community/Bitpay/Core/ ../shell - ../app/code/community/Bitpay/Bitcoins/sql + ../app/code/community/Bitpay/Core/sql diff --git a/build/rulesets/phpmd.xml b/build/rulesets/phpmd.xml index acf46e2..feb5ee7 100644 --- a/build/rulesets/phpmd.xml +++ b/build/rulesets/phpmd.xml @@ -3,7 +3,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2011-2014 BitPay LLC + * 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 diff --git a/build/travis.properties b/build/travis.properties index cb38dee..a21c8f1 100644 --- a/build/travis.properties +++ b/build/travis.properties @@ -1,6 +1,6 @@ # The MIT License (MIT) # -# Copyright (c) 2011-2014 BitPay LLC +# 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 diff --git a/composer.json b/composer.json index 72607a7..af6ed06 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "bitpay/magento-plugin", "description": "Bitcoin payment module using the bitpay.com service", "keywords": ["magento","bitcoin"], - "minimum-stability": "dev", + "minimum-stability": "stable", "type": "magento-plugin", "homepage": "https://github.com/bitpay/magento-plugin", "license": "MIT", @@ -12,25 +12,27 @@ "source": "https://github.com/bitpay/magento-plugin" }, "require": { - "composer/installers": "~1.0" + "composer/installers": "~1.0", + "bitpay/php-client": "~2.0@dev" }, "require-dev": { - "n98/magerun": "*", - "phpmd/phpmd": "*", - "phpdocumentor/phpdocumentor": "*", - "phing/phing": "*", - "pdepend/pdepend" : "1.1.0", - "squizlabs/php_codesniffer": "*", - "phpunit/phpunit": "*", - "phploc/phploc": "*", - "fzaninotto/faker": "*" + "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": "6.0.x-dev" + "dev-master": "2.0.x-dev" } }, "archive": { diff --git a/docs/MagentoInvoiceSettings.png b/docs/MagentoInvoiceSettings.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb016346a53dbe7b501a7b7bf7908576b9680c8 GIT binary patch literal 40649 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

ir8z1b_oaKUo^|ksuQW$Pnf~JbY`%7$-|NN^zAJ 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

#xap@8 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[$key->getId()] = $key; + + $data = serialize($key); + $encryptedData = \Mage::helper('core')->encrypt($data); + $config = new \Mage_Core_Model_Config(); + $config->saveConfig($key->getId(), $encryptedData); + } + + /** + * @inheritdoc + */ + public function load($id) + { + if (isset($this->_keys[$id])) { + return $this->_keys[$id]; + } + + $entity = \Mage::getStoreConfig($id); + + /** + * Not in database + */ + if (empty($entity)) { + return false; + } + + $decodedEntity = unserialize(\Mage::helper('core')->decrypt($entity)); + + if (empty($decodedEntity)) { + return false; + } + + return $decodedEntity; + } +} diff --git a/lib/bitpay/.htaccess b/lib/bitpay/.htaccess deleted file mode 100644 index d107f53..0000000 --- a/lib/bitpay/.htaccess +++ /dev/null @@ -1,5 +0,0 @@ - - Order allow,deny - Deny from all - Satisfy All - diff --git a/lib/bitpay/bp_config_default.php b/lib/bitpay/bp_config_default.php deleted file mode 100644 index 33df7fa..0000000 --- a/lib/bitpay/bp_config_default.php +++ /dev/null @@ -1,49 +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). - * - * @param string $orderId - * @param string $price - * @param string $posData - * @param array $options - * - * @return array - */ -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; - - $post = array(); - $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 - * - * @param boolean|string $apiKey - * - * @return string|array - */ -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']; - - if (!array_key_exists('id', $json)) - { - return 'Cannot find invoice ID'; - } - - return bpGetInvoice($json['id'], $apiKey); -} - -/** - * $options can include ('apiKey') - * - * @param string $invoiceId - * @param boolean|string $apiKey - * - * @return string|array - */ -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); - if($bpOptions['verifyPos']) - { - $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 edff94f..0000000 --- a/lib/bitpay/bp_options.php +++ /dev/null @@ -1,42 +0,0 @@ -files() + ->in($vendorDir . '/bitpay/php-client/src') + ->in($vendorDir . '/symfony/config/') + ->in($vendorDir . '/symfony/filesystem/') + ->in($vendorDir . '/symfony/dependency-injection/') + ->exclude('Tests'); +foreach ($finder as $file) { + $path = $file->getRelativePathname(); + $filesystem->mkdir( + sprintf( + '%s/lib/%s', + $tmpDistDir, + dirname($file->getRelativePathname()) + ) + ); + $filesystem->copy( + $file->getRealPath(), + sprintf( + '%s/lib/%s', + $tmpDistDir, + $file->getRelativePathname() + ), + true + ); +} +$filesystem->mirror('app/', sprintf('%s/app/', $tmpDistDir)); +$filesystem->mirror('lib/', sprintf('%s/lib/', $tmpDistDir)); +$filesystem->copy('LICENSE', sprintf('%s/app/code/community/Bitpay/Core/LICENSE', $tmpDistDir)); +$filesystem->copy('README.md', sprintf('%s/app/code/community/Bitpay/Core/README.md', $tmpDistDir)); +// All required files are in the temp. distribution directory + +/** + * Need to create the package.xml file required by Magento + */ +$xml = simplexml_load_string(''); +$xml->addChild('name', 'Bitpay_Core'); +$xml->addChild('version', $version); +$xml->addChild('stability', 'stable'); +$xml->addChild('license', 'MIT') + ->addAttribute('uri', 'https://github.com/bitpay/magento-plugin/blob/master/LICENSE'); +$xml->addChild('channel', 'community'); +$xml->addChild('extends'); +$xml->addChild('summary'); +$xml->addChild('description'); +$xml->addChild('notes'); +$authorsNode = $xml->addChild('authors'); +$authors = array( + array( + 'Joshua Estes', // Name + 'BitPayJoshua', // Magento Connect Username + 'support@bitpay.com', // Email + ), +); +foreach ($authors as $author) { + $authorNode = $authorsNode->addChild('author'); + $authorNode->addChild('name', $author[0]); + $authorNode->addChild('user', $author[1]); + $authorNode->addChild('email', $author[2]); +} +$xml->addChild('date', date('Y-m-d')); +$xml->addChild('time', date('G:i:s')); +$xml->addChild('compatible'); +$xml->addChild('dependencies'); +$requiredNode = $xml->addChild('required', 'php'); +$requiredNode->addAttribute('php_min', '5.4.0'); +$requiredNode->addAttribute('php_max', '6.0.0'); +$extensionsNode = $xml->addChild('extensions'); +foreach (array('gmp', '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; zip -r %s .', $tmpDistDir, $distFile.'.zip') +); +$process->run(); +$process = new \Symfony\Component\Process\Process( + sprintf('cd %s; tar -czf %s *', $tmpDistDir, $distFile.'.tgz') +); +$process->run(); + +// Cleanup +$filesystem->remove($tmpDistDir); diff --git a/shell/bitpay.php b/shell/bitpay.php deleted file mode 100644 index 4257309..0000000 --- a/shell/bitpay.php +++ /dev/null @@ -1,92 +0,0 @@ -getArg('clean')) { - switch ($clean) { - case 'expired': - $this->cleanExpired(); - break; - default: - echo $this->usageHelp(); - return 1; - break; - } - - return 0; - } - - echo $this->usageHelp(); - } - - /** - * Removes expired IPNs from database and updates order if they are - * open - */ - public function cleanExpired() - { - Mage::helper('bitpay')->clearExpired(); - } - - /** - * Display help on how to use this bad boy - */ - public function usageHelp() - { - $figlet = new Zend_Text_Figlet( - array( - 'justification' => Zend_Text_Figlet::JUSTIFICATION_CENTER - ) - ); - - return <<render('BitPay')} - -Usage: php -f bitpay.php - - --clean Delete all IPN records based on - -List of Statuses: - - expired - -USAGE; - } -} - -$shell = new Bitpay_Shell_Bitpay(); -$shell->run(); diff --git a/tests/Bitpay/Bitcoins/Model/PaymentMethodTest.php b/tests/Bitpay/Bitcoins/Model/PaymentMethodTest.php deleted file mode 100644 index 4040d96..0000000 --- a/tests/Bitpay/Bitcoins/Model/PaymentMethodTest.php +++ /dev/null @@ -1,102 +0,0 @@ -assertTrue($paymentMethod->canUseForCurrency('USD')); - $this->assertFalse($paymentMethod->canUseForCurrency('ASDF')); - } - - public function testCanUseCheckout() - { - $this->markTestIncomplete(); - } - - public function testIsApiKeyConfigured() - { - $this->markTestIncomplete(); - } - - public function testIsTransactionSpeedConfigured() - { - $this->markTestIncomplete(); - } - - public function testAuthorize() - { - $this->markTestIncomplete(); - } - - public function testCheckForPayment() - { - $this->markTestIncomplete(); - } - - public function testInvoiceOrder() - { - $this->markTestIncomplete(); - } - - public function testMarkOrderPaid() - { - $this->markTestIncomplete(); - } - - public function testMarkOrderComplete() - { - $this->markTestIncomplete(); - } - - public function testMarkOrderCancelled() - { - $this->markTestIncomplete(); - } - - public function testExtractAddress() - { - $this->markTestIncomplete(); - } - - public function testCreateInvoiceAndRedirect() - { - $this->markTestIncomplete(); - } - - public function testGetOrderPlaceRedirectUrl() - { - $this->markTestIncomplete(); - } - - public function testGetQuoteHash() - { - $this->markTestIncomplete(); - } -} diff --git a/tests/Bitpay/Bitcoins/Helper/DataTest.php b/tests/Bitpay/Core/Helper/DataTest.php similarity index 65% rename from tests/Bitpay/Bitcoins/Helper/DataTest.php rename to tests/Bitpay/Core/Helper/DataTest.php index c077cb1..536a73c 100644 --- a/tests/Bitpay/Bitcoins/Helper/DataTest.php +++ b/tests/Bitpay/Core/Helper/DataTest.php @@ -1,32 +1,11 @@ getStore()->setConfig('payment/Bitcoins/api_key', null); - - $this->assertFalse(Mage::helper('bitpay')->hasApiKey()); + $this->assertSame( + 'payment_bitpay.log', + Mage::helper('bitpay')->getLogFile() + ); } - public function testHasApiKeyTrue() + public function testDebugData() { - Mage::app()->getStore()->setConfig('payment/Bitcoins/api_key', 'ThisIsMyApiKey'); + Mage::helper('bitpay')->debugData('Testing'); + } - $this->assertTrue(Mage::helper('bitpay')->hasApiKey()); + 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/Bitcoins/speed', null); + Mage::app()->getStore()->setConfig('payment/bitpay/speed', null); $this->assertFalse(Mage::helper('bitpay')->hasTransactionSpeed()); } public function testHasTransactionSpeedTrue() { - Mage::app()->getStore()->setConfig('payment/Bitcoins/speed', 'low'); + Mage::app()->getStore()->setConfig('payment/bitpay/speed', 'low'); $this->assertTrue(Mage::helper('bitpay')->hasTransactionSpeed()); } - public function testCleanExpired() + /** + * Location where BitPay IPNs will go + */ + public function testGetNotificationUrl() { - // Create a few expired/invalid ipns - $invalidIpn = $this->createInvalidIpn(); - $expiredIpn = $this->createExpiredIpn(); + $this->assertSame( + 'http://www.localhost.com/bitpay/ipn/', + Mage::helper('bitpay')->getNotificationUrl() + ); + } - // Are the IPNs in the database? - $ipn = Mage::getModel('Bitcoins/ipn'); - $dbInvalidIpn = $ipn->load($invalidIpn->getId())->toArray(); - $dbExpiredIpn = $ipn->load($expiredIpn->getId())->toArray(); - $this->assertArrayHasKey('id', $dbInvalidIpn); - $this->assertArrayHasKey('id', $dbExpiredIpn); - unset($dbInvalidIpn, $dbExpiredIpn); + public function testGetRedirectUrl() + { + $this->assertSame( + 'http://www.localhost.com/checkout/onepage/success/', + Mage::helper('bitpay')->getRedirectUrl() + ); + } - // clean them - Mage::helper('bitpay')->cleanExpired(); + public function testRegisterAutoloader() + { + Mage::helper('bitpay')->registerAutoloader(); + } - // check the database and see if they are still there - $ipn = Mage::getModel('Bitcoins/ipn'); - $dbInvalidIpn = $ipn->load($invalidIpn->getId())->toArray(); - $dbExpiredIpn = $ipn->load($expiredIpn->getId())->toArray(); - $this->assertEmpty($dbInvalidIpn); - $this->assertEmpty($dbExpiredIpn); + public function testGenerateAndSaveKeys() + { + Mage::helper('bitpay')->generateAndSaveKeys(); + } + + public function testGetSinKey() + { + Mage::helper('bitpay')->getSinKey(); } private function createInvalidIpn() { - $ipn = new Bitpay_Bitcoins_Model_Ipn(); + $ipn = new Bitpay_Core_Model_Ipn(); $ipn->setData( array( 'quote_id' => '', @@ -115,7 +114,7 @@ class Bitpay_Bitcoins_Helper_DataTest extends PHPUnit_Framework_TestCase private function createExpiredIpn() { $order = $this->createOrder(); - $ipn = new Bitpay_Bitcoins_Model_Ipn(); + $ipn = new Bitpay_Core_Model_Ipn(); $ipn->setData( array( 'quote_id' => '', @@ -166,7 +165,7 @@ class Bitpay_Bitcoins_Helper_DataTest extends PHPUnit_Framework_TestCase 'save_in_address_book' => 0, 'use_for_shipping' => 1, 'street' => array( - self::$faker->streetAddress + self::$faker->streetAddress, ), ); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index b0a1ca1..1879b74 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,30 +1,10 @@