Merge pull request #68 from JoshuaEstes/CronJob

Cron and shell script to update orders and clean ipn table
This commit is contained in:
Joshua Estes 2014-07-24 14:36:03 -04:00
commit 4735d290c4
14 changed files with 797 additions and 136 deletions

View File

@ -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
@ -67,4 +67,107 @@ class Bitpay_Bitcoins_Helper_Data extends Mage_Core_Helper_Abstract
return !empty($speed);
}
/**
* This method is used to removed IPN records in the database that
* are expired and update the magento orders to canceled if they have
* expired.
*/
public function cleanExpired()
{
$expiredRecords = Mage::getModel('Bitcoins/ipn')->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;
}
}

View File

@ -31,9 +31,8 @@ class Bitpay_Bitcoins_Model_Ipn extends Mage_Core_Model_Abstract
*/
function _construct()
{
parent::_construct();
$this->_init('Bitcoins/ipn');
return parent::_construct();
}
/**
@ -133,4 +132,125 @@ class Bitpay_Bitcoins_Model_Ipn extends Mage_Core_Model_Abstract
{
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();
}
}

View File

@ -0,0 +1,95 @@
<?php
/**
* The MIT License (MIT)
*
* Copyright (c) 2011-2014 BitPay, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
class Bitpay_Bitcoins_Model_Observer
{
/**
* Queries BitPay to update the order states in magento to make sure that
* open orders are closed/canceled if the BitPay invoice expires or becomes
* invalid.
*/
public function updateOrderStates()
{
Mage::log(
'cronjob: started',
Zend_Log::DEBUG,
Mage::helper('bitpay')->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();
}
}

View File

@ -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
@ -24,7 +24,7 @@
* THE SOFTWARE.
*/
class Bitpay_Bitcoins_Model_Resource_Ipn extends Mage_Core_Model_Resource_Db_Abstract
class Bitpay_Bitcoins_Model_Resource_Ipn extends Mage_Core_Model_Mysql4_Abstract
{
protected function _construct()
{

View File

@ -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
@ -24,13 +24,21 @@
* THE SOFTWARE.
*/
class Bitpay_Bitcoins_Model_Resource_Ipn_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
class Bitpay_Bitcoins_Model_Resource_Ipn_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
{
/**
*/
protected function _construct()
{
parent::_construct();
$this->_init('Bitcoins/ipn');
}
public function delete()
{
foreach ($this->getItems() as $item) {
$item->delete();
}
}
}

View File

@ -1,118 +1,136 @@
<?xml version="1.0"?>
<!--
/**
* The MIT License (MIT)
*
* Copyright (c) 2011-2014 BitPay LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-->
<config>
<frontend>
<routers>
<bitpay_callback>
<use>standard</use>
<args>
<module>Bitpay_Bitcoins</module>
<frontName>bitpay_callback</frontName>
</args>
</bitpay_callback>
</routers>
<layout>
<updates>
<bitcoins>
<file>bitcoins.xml</file>
</bitcoins>
</updates>
</layout>
</frontend>
<modules>
<Bitpay_Bitcoins>
<!-- for database updates -->
<version>1.1.0</version>
</Bitpay_Bitcoins>
</modules>
<global>
<blocks>
<bitcoins>
<class>Bitpay_Bitcoins_Block</class>
</bitcoins>
</blocks>
<helpers>
<bitpay>
<class>Bitpay_Bitcoins_Helper</class>
</bitpay>
</helpers>
<models>
<Bitcoins>
<class>Bitpay_Bitcoins_Model</class>
<resourceModel>Bitcoins_resource</resourceModel>
</Bitcoins>
<Bitcoins_resource>
<class>Bitpay_Bitcoins_Model_Resource</class>
<entities>
<ipn>
<table>bitpay_ipns</table>
</ipn>
</entities>
</Bitcoins_resource>
</models>
<resources>
<Bitcoins_setup> <!-- keep this uppercase or you'll get duplicate errors -->
<setup>
<!-- which module to look for install/upgrade files in -->
<module>Bitpay_Bitcoins</module>
</setup>
<connection>
<use>core_setup</use>
</connection>
</Bitcoins_setup>
<Bitcoins_write>
<connection>
<use>core_write</use>
</connection>
</Bitcoins_write>
<Bitcoins_read>
<connection>
<use>core_read</use>
</connection>
</Bitcoins_read>
</resources>
</global>
<default>
<payment>
<Bitcoins>
<active>1</active>
<model>Bitcoins/paymentMethod</model>
<title>Bitcoins</title>
<speed>low</speed>
<fullscreen>0</fullscreen>
<currencies>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</currencies>
<payment_action>authorize</payment_action>
</Bitcoins>
</payment>
</default>
</config>
<?xml version="1.0"?>
<!--
/**
* The MIT License (MIT)
*
* Copyright (c) 2011-2014 BitPay, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-->
<config>
<frontend>
<routers>
<bitpay_callback>
<use>standard</use>
<args>
<module>Bitpay_Bitcoins</module>
<frontName>bitpay_callback</frontName>
</args>
</bitpay_callback>
</routers>
<layout>
<updates>
<bitcoins>
<file>bitcoins.xml</file>
</bitcoins>
</updates>
</layout>
</frontend>
<modules>
<Bitpay_Bitcoins>
<!-- for database updates -->
<version>1.1.0</version>
</Bitpay_Bitcoins>
</modules>
<global>
<blocks>
<bitcoins>
<class>Bitpay_Bitcoins_Block</class>
</bitcoins>
</blocks>
<helpers>
<bitpay>
<class>Bitpay_Bitcoins_Helper</class>
</bitpay>
</helpers>
<models>
<Bitcoins>
<class>Bitpay_Bitcoins_Model</class>
<resourceModel>Bitcoins_resource</resourceModel>
</Bitcoins>
<Bitcoins_resource>
<class>Bitpay_Bitcoins_Model_Resource</class>
<entities>
<ipn>
<table>bitpay_ipns</table>
</ipn>
</entities>
</Bitcoins_resource>
</models>
<resources>
<Bitcoins_setup> <!-- keep this uppercase or you'll get duplicate errors -->
<setup>
<!-- which module to look for install/upgrade files in -->
<module>Bitpay_Bitcoins</module>
</setup>
<connection>
<use>core_setup</use>
</connection>
</Bitcoins_setup>
<Bitcoins_write>
<connection>
<use>core_write</use>
</connection>
</Bitcoins_write>
<Bitcoins_read>
<connection>
<use>core_read</use>
</connection>
</Bitcoins_read>
</resources>
</global>
<crontab>
<jobs>
<!--
This will clean up all the expired IPNs in the database and
will also updated the magento order to cancelled
-->
<bitpay_clean_expired>
<schedule>
<!-- Run every 30 minutes -->
<cron_expr>*/30 * * * *</cron_expr>
</schedule>
<run>
<model>Bitcoins/Observer::cleanExpired</model>
</run>
</bitpay_clean_expired>
</jobs>
</crontab>
<default>
<payment>
<Bitcoins>
<active>1</active>
<model>Bitcoins/paymentMethod</model>
<title>Bitcoins</title>
<speed>low</speed>
<fullscreen>0</fullscreen>
<currencies>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</currencies>
<payment_action>authorize</payment_action>
</Bitcoins>
</payment>
</default>
</config>

View File

@ -123,6 +123,15 @@
-->
<target name="phpunit"
description="Runs unit tests">
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<arg value="cache:clean" />
</exec>
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<arg value="cache:flush" />
</exec>
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<arg value="cache:disable" />
</exec>
<exec executable="${project.bindir}/phpunit" passthru="true">
<arg value="-c" />
<arg path="${phpunit.configuration}" />
@ -206,7 +215,8 @@
<delete dir="build/magento" includeemptydirs="true" />
<mkdir dir="build/magento/app/etc/" />
<copy file="build/n98-magerun.yaml" tofile="build/magento/app/etc/n98-magerun.yaml" />
<exec executable="bin/n98-magerun" passthru="true">
<phingcall target="plugin:symlink" />
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true">
<arg value="install" />
<arg value="-n" />
<arg value="-vvv" />
@ -220,22 +230,31 @@
<arg value="--useDefaultConfigParams=yes" />
<arg value="--baseUrl=${magento.baseurl}" />
</exec>
<exec executable="bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<arg value="config:set" />
<arg value="dev/template/allow_symlink" />
<arg value="1" />
</exec>
<exec executable="bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<arg value="config:set" />
<arg value="dev/log/active" />
<arg value="1" />
</exec>
<exec executable="bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<phingcall target="plugin:enable" />
</target>
<target name="plugin:enable" hidden="true">
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<arg value="config:set" />
<arg value="payment/Bitcoins/api_key" />
<arg value="GArM63Kab9ahw2muesTWptJneXFoxUZoFXrAKWs5c" />
</exec>
<phingcall target="plugin:symlink" />
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<arg value="cache:clean" />
</exec>
<exec executable="${project.basedir}/bin/n98-magerun" passthru="true" dir="${project.basedir}/build/magento">
<arg value="cache:flush" />
</exec>
</target>
<!--
@ -243,9 +262,15 @@
environment. This should closely follow the `modman` file.
-->
<target name="plugin:symlink" hidden="true">
<mkdir dir="${project.basedir}/build/magento/app/code/community/Bitpay" />
<mkdir dir="${project.basedir}/build/magento/app/design/frontend/base/default/layout" />
<mkdir dir="${project.basedir}/build/magento/app/design/frontend/base/default/template" />
<mkdir dir="${project.basedir}/build/magento/app/etc/modules" />
<mkdir dir="${project.basedir}/build/magento/lib" />
<mkdir dir="${project.basedir}/build/magento/shell" />
<symlink
target="${project.basedir}/app/code/community/Bitpay"
link="${project.basedir}/build/magento/app/code/community/Bitpay"
target="${project.basedir}/app/code/community/Bitpay/Bitcoins"
link="${project.basedir}/build/magento/app/code/community/Bitpay/Bitcoins"
overwrite="true" />
<symlink
target="${project.basedir}/app/design/frontend/base/default/layout/bitcoins.xml"
@ -263,5 +288,9 @@
target="${project.basedir}/lib/bitpay"
link="${project.basedir}/build/magento/lib/bitpay"
overwrite="true" />
<symlink
target="${project.basedir}/shell/bitpay.php"
link="${project.basedir}/build/magento/shell/bitpay.php"
overwrite="true" />
</target>
</project>

View File

@ -46,7 +46,10 @@
<filter>
<whitelist>
<directory>../app/code/community/Bitpay/Bitcoins/</directory>
<directory>../lib/bitpay</directory>
<directory>../shell</directory>
<exclude>
<directory>../app/code/community/Bitpay/Bitcoins/sql</directory>
</exclude>
</whitelist>
</filter>

View File

@ -22,7 +22,8 @@
"pdepend/pdepend" : "1.1.0",
"squizlabs/php_codesniffer": "*",
"phpunit/phpunit": "*",
"phploc/phploc": "*"
"phploc/phploc": "*",
"fzaninotto/faker": "*"
},
"config": {
"bin-dir": "bin"

1
modman
View File

@ -25,3 +25,4 @@ app/design/frontend/base/default/layout/bitcoins.xml app/design/frontend/base/de
app/design/frontend/base/default/template/bitcoins app/design/frontend/base/default/template/bitcoins
app/etc/modules/Bitpay_Bitcoins.xml app/etc/modules/Bitpay_Bitcoins.xml
lib/bitpay lib/bitpay
shell/bitpay.php shell/bitpay.php

92
shell/bitpay.php Normal file
View File

@ -0,0 +1,92 @@
<?php
/**
* The MIT License (MIT)
*
* Copyright (c) 2011-2014 BitPay, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
require_once 'abstract.php';
/**
* This class is used to work with the bitpay api via the command line and to
* debug issues
*/
class Bitpay_Shell_Bitpay extends Mage_Shell_Abstract
{
public function run()
{
if ($clean = $this->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 <<<USAGE
{$figlet->render('BitPay')}
Usage: php -f bitpay.php
--clean <status> Delete all IPN records based on <status>
List of Statuses:
expired
USAGE;
}
}
$shell = new Bitpay_Shell_Bitpay();
$shell->run();

View File

@ -27,6 +27,13 @@
class Bitpay_Bitcoins_Helper_DataTest extends PHPUnit_Framework_TestCase
{
protected static $faker;
public static function setUpBeforeClass()
{
self::$faker = Faker\Factory::create();
}
public function testHasApiKeyFalse()
{
Mage::app()->getStore()->setConfig('payment/Bitcoins/api_key', null);
@ -54,4 +61,182 @@ class Bitpay_Bitcoins_Helper_DataTest extends PHPUnit_Framework_TestCase
$this->assertTrue(Mage::helper('bitpay')->hasTransactionSpeed());
}
public function testCleanExpired()
{
// Create a few expired/invalid ipns
$invalidIpn = $this->createInvalidIpn();
$expiredIpn = $this->createExpiredIpn();
// 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);
// clean them
Mage::helper('bitpay')->cleanExpired();
// 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);
}
private function createInvalidIpn()
{
$ipn = new Bitpay_Bitcoins_Model_Ipn();
$ipn->setData(
array(
'quote_id' => '',
'order_id' => '',
'invoice_id' => '',
'url' => '',
'pos_data' => '',
'status' => '',
'btc_price' => '',
'price' => '',
'currency' => '',
'invoice_time' => '',
'expiration_time' => '',
'current_time' => '',
)
);
$ipn->save();
$ipn->load($ipn->getId());
return $ipn;
}
private function createExpiredIpn()
{
$order = $this->createOrder();
$ipn = new Bitpay_Bitcoins_Model_Ipn();
$ipn->setData(
array(
'quote_id' => '',
'order_id' => $order->getIncrementId(),
'invoice_id' => '',
'url' => '',
'pos_data' => '',
'status' => '',
'btc_price' => '',
'price' => '',
'currency' => '',
'invoice_time' => '',
'expiration_time' => '',
'current_time' => '',
)
);
$ipn->save();
$ipn->load($ipn->getId());
return $ipn;
}
private function createOrder()
{
$product = $this->createProduct();
$quote = $this->createQuote();
$quote->addProduct(
$product,
new Varien_Object(
array(
'qty' => 1,
)
)
);
$address = array(
'firstname' => self::$faker->firstName,
'lastname' => self::$faker->lastName,
'company' => self::$faker->company,
'email' => self::$faker->email,
'city' => self::$faker->city,
'region_id' => '',
'region' => 'State/Province',
'postcode' => self::$faker->postcode,
'telephone' => self::$faker->phoneNumber,
'country_id' => self::$faker->state,
'customer_password' => '',
'confirm_password' => '',
'save_in_address_book' => 0,
'use_for_shipping' => 1,
'street' => array(
self::$faker->streetAddress
),
);
$quote->getBillingAddress()
->addData($address);
$quote->getShippingAddress()
->addData($address)
->setShippingMethod('flatrate_flatrate')
->setPaymentMethod('checkmo')
->setCollectShippingRates(true)
->collectTotals();
$quote
->setCheckoutMethod('guest')
->setCustomerId(null)
->setCustomerEmail($address['email'])
->setCustomerIsGuest(true)
->setCustomerGroupId(Mage_Customer_Model_Group::NOT_LOGGED_IN_ID);
$quote->getPayment()
->importData(array('method' => 'checkmo'));
$quote->save();
$service = Mage::getModel('sales/service_quote', $quote);
$service->submitAll();
$order = $service->getOrder();
$order->save();
$order->load($order->getId());
return $order;
}
private function createProduct()
{
$product = Mage::getModel('catalog/product');
$product->addData(
array(
'attribute_set_id' => 1,
'website_ids' => array(1),
'categories' => array(),
'type_id' => Mage_Catalog_Model_Product_Type::TYPE_SIMPLE,
'sku' => self::$faker->randomNumber,
'name' => self::$faker->name,
'weight' => self::$faker->randomDigit,
'status' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED,
'tax_class_id' => 2,
'visibility' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
'price' => self::$faker->randomFloat(2),
'description' => self::$faker->paragraphs,
'short_description' => self::$faker->sentence,
'stock_data' => array(
'is_in_stock' => 1,
'qty' => 100,
),
)
);
$product->save();
$product->load($product->getId());
return $product;
}
private function createQuote()
{
return Mage::getModel('sales/quote')
->setStoreId(Mage::app()->getStore('default')->getId());
}
}

View File

@ -3,7 +3,7 @@
/**
* 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

View File

@ -3,7 +3,7 @@
/**
* 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
@ -31,3 +31,9 @@ if ($mage = realpath(__DIR__ . '/../build/magento/app/Mage.php')) {
} else {
exit('Could not find Mage.php');
}
if ($composer = realpath(__DIR__ . '/../vendor/autoload.php')) {
require_once $composer;
} else {
exit('Composer not found');
}