Merge pull request #68 from JoshuaEstes/CronJob
Cron and shell script to update orders and clean ipn table
This commit is contained in:
commit
4735d290c4
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
43
build.xml
43
build.xml
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
1
modman
|
@ -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
|
||||
|
|
|
@ -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();
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue