├── docs ├── Payment Method Settings.png ├── Module version info in admin.png ├── Afterpay Magento Order Statuses.png ├── Afterpay Magento Order Statuses.vsdx ├── Afterpay Magento Order Processing.png ├── Afterpay Magento Order Processing.vsdx └── Installments Amount configuration.png ├── src ├── skin │ └── frontend │ │ └── base │ │ └── default │ │ └── afterpay │ │ ├── images │ │ └── ap-logo-152x31.png │ │ ├── js │ │ └── afterpay_youtube.js │ │ └── css │ │ └── afterpay.css ├── app │ ├── etc │ │ └── modules │ │ │ └── Afterpay_Afterpay.xml │ ├── code │ │ └── community │ │ │ └── Afterpay │ │ │ └── Afterpay │ │ │ ├── Exception.php │ │ │ ├── Block │ │ │ ├── Config.php │ │ │ ├── Require.php │ │ │ ├── Adminhtml │ │ │ │ └── System │ │ │ │ │ └── Config │ │ │ │ │ └── Form │ │ │ │ │ └── Field │ │ │ │ │ ├── ModuleVersion.php │ │ │ │ │ └── Update │ │ │ │ │ └── Limits.php │ │ │ ├── Form │ │ │ │ ├── Abstract.php │ │ │ │ └── Payovertime.php │ │ │ ├── Banner.php │ │ │ ├── Redirect.php │ │ │ ├── Info.php │ │ │ ├── Onetouch.php │ │ │ └── Catalog │ │ │ │ └── Installments.php │ │ │ ├── Model │ │ │ ├── System │ │ │ │ └── Config │ │ │ │ │ └── Source │ │ │ │ │ ├── RedirectMode.php │ │ │ │ │ ├── CartMode.php │ │ │ │ │ ├── Category.php │ │ │ │ │ └── ApiMode.php │ │ │ ├── Api │ │ │ │ ├── Http │ │ │ │ │ └── Client.php │ │ │ │ ├── Routers │ │ │ │ │ └── Routerv1.php │ │ │ │ └── Adapters │ │ │ │ │ └── Adapterv1.php │ │ │ ├── Method │ │ │ │ └── Payovertime.php │ │ │ ├── Observer.php │ │ │ └── Order.php │ │ │ ├── sql │ │ │ └── afterpay_setup │ │ │ │ ├── upgrade-0.2.2-0.3.0.php │ │ │ │ ├── upgrade-0.2.1-0.2.2.php │ │ │ │ ├── install-0.2.0.php │ │ │ │ ├── upgrade-0.12.9-0.13.0.php │ │ │ │ ├── upgrade-0.3.2-0.4.0.php │ │ │ │ ├── upgrade-0.7.3-0.8.0.php │ │ │ │ ├── upgrade-0.5.0-0.6.0.php │ │ │ │ ├── uninstall.php │ │ │ │ ├── upgrade-0.8.0-0.9.0.php │ │ │ │ ├── install-0.13.0.php │ │ │ │ └── install-0.5.0.php │ │ │ ├── controllers │ │ │ ├── OnetouchController.php │ │ │ └── Adminhtml │ │ │ │ └── AfterpayController.php │ │ │ ├── etc │ │ │ ├── adminhtml.xml │ │ │ └── config.xml │ │ │ └── Helper │ │ │ └── Checkout.php │ └── design │ │ ├── frontend │ │ └── base │ │ │ └── default │ │ │ ├── template │ │ │ └── afterpay │ │ │ │ ├── failure.phtml │ │ │ │ ├── checkout │ │ │ │ ├── title_custom.phtml │ │ │ │ ├── magestore_onestep.phtml │ │ │ │ ├── require.phtml │ │ │ │ ├── quickcheckout.phtml │ │ │ │ ├── gomage.phtml │ │ │ │ ├── iwd_opc.phtml │ │ │ │ ├── firecheckout.phtml │ │ │ │ ├── awesomecheckout.phtml │ │ │ │ ├── aw_onestepcheckout.phtml │ │ │ │ ├── config.phtml │ │ │ │ ├── onestep.phtml │ │ │ │ ├── default.phtml │ │ │ │ └── onetouch.phtml │ │ │ │ ├── catalog │ │ │ │ └── installments.phtml │ │ │ │ ├── form │ │ │ │ └── payovertime_custom.phtml │ │ │ │ ├── banner_requirements.phtml │ │ │ │ └── redirect.phtml │ │ │ └── layout │ │ │ └── afterpay.xml │ │ └── adminhtml │ │ └── default │ │ └── default │ │ └── template │ │ └── afterpay │ │ └── limits.phtml └── js │ └── Afterpay │ ├── checkout │ ├── payovertime.js │ ├── firecheckout.js │ ├── onepage.js │ ├── quickcheckout.js │ ├── aw_onestepcheckout.js │ ├── gomage.js │ ├── mw_onestepcheckout.js │ ├── amasty_onestepcheckout.js │ ├── idev_onestep.js │ ├── awesomecheckout.js │ ├── iwd_opc.js │ └── magestore_onestep.js │ └── Installments.js ├── README.md └── LICENSE.md /docs/Payment Method Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterpay/afterpay-magento/HEAD/docs/Payment Method Settings.png -------------------------------------------------------------------------------- /docs/Module version info in admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterpay/afterpay-magento/HEAD/docs/Module version info in admin.png -------------------------------------------------------------------------------- /docs/Afterpay Magento Order Statuses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterpay/afterpay-magento/HEAD/docs/Afterpay Magento Order Statuses.png -------------------------------------------------------------------------------- /docs/Afterpay Magento Order Statuses.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterpay/afterpay-magento/HEAD/docs/Afterpay Magento Order Statuses.vsdx -------------------------------------------------------------------------------- /docs/Afterpay Magento Order Processing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterpay/afterpay-magento/HEAD/docs/Afterpay Magento Order Processing.png -------------------------------------------------------------------------------- /docs/Afterpay Magento Order Processing.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterpay/afterpay-magento/HEAD/docs/Afterpay Magento Order Processing.vsdx -------------------------------------------------------------------------------- /docs/Installments Amount configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterpay/afterpay-magento/HEAD/docs/Installments Amount configuration.png -------------------------------------------------------------------------------- /src/skin/frontend/base/default/afterpay/images/ap-logo-152x31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afterpay/afterpay-magento/HEAD/src/skin/frontend/base/default/afterpay/images/ap-logo-152x31.png -------------------------------------------------------------------------------- /src/app/etc/modules/Afterpay_Afterpay.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | community 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Exception.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/failure.phtml: -------------------------------------------------------------------------------- 1 | 2 |
3 |

__('Order Declined') ?>

4 |
5 | getChildHtml(); ?> 6 |

__('Continue to shop', $this->getContinueShoppingUrl()) ?>

7 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Block/Config.php: -------------------------------------------------------------------------------- 1 | getTemplateHelper()->getTitleTemplateId() ?>"> 2 | 9 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Block/Require.php: -------------------------------------------------------------------------------- 1 | $this->getSkinUrl('afterpay/css/afterpay.css'), 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/magestore_onestep.phtml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/require.phtml: -------------------------------------------------------------------------------- 1 | 9 | getRequireStyle() as $css) :?> 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/System/Config/Source/RedirectMode.php: -------------------------------------------------------------------------------- 1 | 'Redirect', 17 | self::LIGHTBOX => 'Lightbox', 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/quickcheckout.phtml: -------------------------------------------------------------------------------- 1 | setAfterpayErrorRedirect('checkout/cart'); ?> 2 | noConflict()) { ?> 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/gomage.phtml: -------------------------------------------------------------------------------- 1 | noConflict()) { ?> 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/upgrade-0.2.2-0.3.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 12 | 13 | 14 | $table = $installer->getTable('sales_flat_order_payment'); 15 | $installer->getConnection()->addColumn($table, "afterpay_fetched_at", "TIMESTAMP NULL"); 16 | 17 | 18 | $installer->endSetup(); 19 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/iwd_opc.phtml: -------------------------------------------------------------------------------- 1 | noConflict()) { ?> 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/firecheckout.phtml: -------------------------------------------------------------------------------- 1 | noConflict()) { ?> 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/awesomecheckout.phtml: -------------------------------------------------------------------------------- 1 | noConflict()) { ?> 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Block/Adminhtml/System/Config/Form/Field/ModuleVersion.php: -------------------------------------------------------------------------------- 1 | getModuleVersion(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/aw_onestepcheckout.phtml: -------------------------------------------------------------------------------- 1 | noConflict()) { ?> 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/config.phtml: -------------------------------------------------------------------------------- 1 | noConflict()) { ?> 2 | 3 | 10 | 11 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/System/Config/Source/CartMode.php: -------------------------------------------------------------------------------- 1 | 'Yes - Magento Checkout', 18 | self::EXPRESS => 'Yes - Afterpay Express Checkout', 19 | self::NO => 'No', 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/upgrade-0.2.1-0.2.2.php: -------------------------------------------------------------------------------- 1 | startSetup(); 12 | 13 | 14 | $table = $installer->getTable('sales_flat_order_payment'); 15 | $installer->getConnection()->addColumn($table, "afterpay_order_id", "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order ID'"); 16 | $installer->getConnection()->dropIndex($table, "IDX_SALES_FLAT_ORDER_PAYMENT_AFTERPAY_TOKEN"); 17 | 18 | 19 | $installer->endSetup(); 20 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/install-0.2.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 13 | 14 | 15 | $table = $installer->getTable('sales_flat_order_payment'); 16 | $installer->getConnection()->addColumn($table, "afterpay_token", "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order Token'"); 17 | $installer->getConnection()->addIndex($table, "IDX_SALES_FLAT_ORDER_PAYMENT_AFTERPAY_TOKEN", "afterpay_token", Varien_Db_Adapter_Interface::INDEX_TYPE_UNIQUE); 18 | 19 | 20 | $installer->endSetup(); 21 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/upgrade-0.12.9-0.13.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 10 | 11 | /** 12 | * Setup script to create new column on sales_flat_quote_payment for: 13 | * - afterpay_token 14 | * - afterpay_order_id 15 | */ 16 | 17 | $table = $installer->getTable('sales/quote_payment'); 18 | $installer->getConnection()->addColumn($table, 'afterpay_token', "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order Token'"); 19 | $installer->getConnection()->addColumn($table, 'afterpay_order_id', "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order ID'"); 20 | 21 | $installer->endSetup(); 22 | ?> -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/upgrade-0.3.2-0.4.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 12 | 13 | 14 | $status = 'afterpay_payment_review'; 15 | $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW; 16 | 17 | $installer->run("INSERT INTO `{$this->getTable('sales_order_status')}` (`status`, `label`) VALUES ('{$status}', 'Afterpay Processing');"); 18 | $installer->run("INSERT INTO `{$this->getTable('sales/order_status_state')}` (`status`, `state`, `is_default`) VALUES ('{$status}', '{$state}', '0');"); 19 | 20 | 21 | $installer->endSetup(); 22 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/upgrade-0.7.3-0.8.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 13 | $installer->run(" 14 | CREATE TABLE `{$installer->getTable('afterpay/afterpay_shipped_api_queue')}` ( 15 | `shipped_api_queue_id` INT(11) NOT NULL auto_increment, 16 | `payment_id` INT(11) NOT NULL, 17 | `tracking_number` VARCHAR(255), 18 | `courier` VARCHAR(255), 19 | `errors_count` INT(11), 20 | PRIMARY KEY (`shipped_api_queue_id`) 21 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 22 | "); 23 | $installer->endSetup(); 24 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/controllers/OnetouchController.php: -------------------------------------------------------------------------------- 1 | getQuote(); 18 | 19 | $quote->getPayment()->setMethod('afterpaypayovertime') 20 | ->save(); 21 | 22 | $helper = Mage::helper('checkout/url'); 23 | $this->_redirectUrl($helper->getCheckoutUrl()); 24 | } 25 | } -------------------------------------------------------------------------------- /src/app/design/adminhtml/default/default/template/afterpay/limits.phtml: -------------------------------------------------------------------------------- 1 | getWebsite())) { 3 | $website_code = Mage::getSingleton('adminhtml/config_data')->getWebsite(); 4 | $website_id = Mage::getModel('core/website')->load($website_code)->getId(); 5 | 6 | $overrides = array('website_id' => $website_id); 7 | 8 | $target = array('website', $website_id); 9 | } 10 | else { 11 | $target_id = 0; 12 | $level = 'default'; 13 | 14 | $target = array(); 15 | } 16 | 17 | $target = implode($target, "/"); 18 | ?> 19 | 20 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/etc/adminhtml.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Afterpay 13 | 1000 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Block/Adminhtml/System/Config/Form/Field/Update/Limits.php: -------------------------------------------------------------------------------- 1 | getTemplate()) { 10 | $this->setTemplate('afterpay/limits.phtml'); 11 | } 12 | } 13 | 14 | public function render(Varien_Data_Form_Element_Abstract $element) 15 | { 16 | $element->unsScope() 17 | ->unsCanUseWebsiteValue() 18 | ->unsCanUseDefaultValue(); 19 | 20 | return parent::render($element); 21 | } 22 | 23 | public function _getElementHtml(Varien_Data_Form_Element_Abstract $elemen) 24 | { 25 | return $this->_toHtml(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Block/Form/Abstract.php: -------------------------------------------------------------------------------- 1 | hasData('redirect_message')) { 25 | return $this->getData('redirect_message'); 26 | } else { 27 | return $this->getMethod()->getConfigData('message'); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/catalog/installments.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/form/payovertime_custom.phtml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 21 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/controllers/Adminhtml/AfterpayController.php: -------------------------------------------------------------------------------- 1 | updateOrderLimits(); 21 | $this->_getSession()->addSuccess(Mage::helper('afterpay')->__('Successfully updated limits')); 22 | } catch (Exception $e) { 23 | $this->_getSession()->addError($e->getMessage()); 24 | } 25 | 26 | $this->_redirectReferer(); 27 | } 28 | } -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/banner_requirements.phtml: -------------------------------------------------------------------------------- 1 | 2 | noConflict()) { ?> 3 | 6 | 7 | 25 | 26 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/onestep.phtml: -------------------------------------------------------------------------------- 1 | noConflict()) { ?> 2 | 3 | 7 | 8 | isModuleEnabled("Magestore_Onestepcheckout"); 10 | $idev = Mage::helper('core')->isModuleEnabled("Idev_Onestepcheckout"); 11 | $iksanika = Mage::helper('core')->isModuleEnabled("Iksanika_Onestepcheckout"); 12 | ?> 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/upgrade-0.5.0-0.6.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 13 | 14 | $blockData = array( 15 | 'identifier' => 'afterpay-order-declined', 16 | 'title' => 'Afterpay Order Declined', 17 | 'content' => '

Your order was not approved on this occasion due to an issue with your card.

18 |

Please contact your card issuer to ensure your card details are valid and the card provides sufficient funds to cover the payment amounts.

', 19 | 'is_active' => 1, 20 | 'stores' => array(0) 21 | ); 22 | 23 | /** @var $block Mage_Cms_Model_Block */ 24 | $block = Mage::getModel('cms/block'); 25 | $block->load($blockData['identifier'], 'identifier'); 26 | if (!$block->isObjectNew()) { 27 | unset($blockData['identifier']); 28 | } 29 | $block->addData($blockData); 30 | $block->save(); 31 | 32 | $installer->endSetup(); 33 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/uninstall.php: -------------------------------------------------------------------------------- 1 | startSetup(); 13 | $installer->run(" 14 | ALTER TABLE `{$installer->getTable('sales_flat_order_payment')}` 15 | DROP COLUMN afterpay_token, 16 | DROP COLUMN afterpay_order_id, 17 | DROP COLUMN afterpay_fetched_at; 18 | "); 19 | 20 | $installer->run(" 21 | DELETE FROM `{$installer->getTable('sales_order_status_state')}` WHERE status='afterpay_payment_review'; 22 | "); 23 | 24 | $installer->run(" 25 | DELETE FROM `{$installer->getTable('sales_order_status')}` WHERE status='afterpay_payment_review'; 26 | "); 27 | 28 | $installer->run(" 29 | DROP TABLE IF EXISTS `{$installer->getTable('afterpay_shipped_api_queue')}`; 30 | "); 31 | 32 | $installer->run(" 33 | DELETE FROM `{$installer->getTable('core_resource')}` WHERE code='afterpay_setup'; 34 | "); 35 | 36 | $installer->endSetup(); 37 | ?> -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/checkout/default.phtml: -------------------------------------------------------------------------------- 1 | noConflict()) { ?> 2 | 3 | 7 | 8 | isModuleEnabled("MW_Onestepcheckout"); 10 | $amasty_onestepcheckout = Mage::helper('core')->isModuleEnabled("Amasty_Scheckout"); 11 | ?> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/upgrade-0.8.0-0.9.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 13 | 14 | $blockData = array( 15 | 'identifier' => 'afterpay_video_banner', 16 | 'title' => 'Afterpay Video Banner', 17 | 'content' => '
18 | AfterPay 19 |
20 | 21 | 27 | 28 | 40 | ', 41 | 'is_active' => 1, 42 | 'stores' => array(0) 43 | ); 44 | 45 | /** @var $block Mage_Cms_Model_Block */ 46 | $block = Mage::getModel('cms/block'); 47 | 48 | $block->load($blockData['identifier'], 'identifier'); 49 | 50 | if (!$block->isObjectNew()) { 51 | unset($blockData['identifier']); 52 | } 53 | $block->addData($blockData)->save(); 54 | 55 | $installer->endSetup(); 56 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/template/afterpay/redirect.phtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 42 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/System/Config/Source/Category.php: -------------------------------------------------------------------------------- 1 | getCollection() 10 | ->addAttributeToSelect('name') 11 | ->addAttributeToSort('path', 'asc') 12 | ->addFieldToFilter('is_active', array('eq'=>'1')) 13 | ->load() 14 | ->toArray(); 15 | 16 | foreach ($categories as $catId => $category) 17 | { 18 | if (isset($category['name'])) { 19 | $categoryList[] = array( 20 | 'label' => $category['name'], 21 | 'level' =>$category['level'], 22 | 'value' => $catId 23 | ); 24 | } 25 | } 26 | 27 | return $categoryList; 28 | } 29 | 30 | public function toOptionArray() 31 | { 32 | $options = array(); 33 | 34 | $categoriesTreeView = $this->getCategoriesTreeView(); 35 | 36 | foreach ($categoriesTreeView as $cat) 37 | { 38 | $hyphen = ''; 39 | for ($i = 1; $i < $cat['level']; $i++) { 40 | $hyphen .= '--'; 41 | } 42 | $options[] = array( 43 | 'label' => $hyphen . $cat['label'], 44 | 'value' => $cat['value'] 45 | ); 46 | } 47 | 48 | return $options; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/install-0.13.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 10 | 11 | /** 12 | * Setup script to create new column on sales_flat_quote_payment for: 13 | * - afterpay_token 14 | * - afterpay_order_id 15 | */ 16 | 17 | // add columns to sales/order_payment 18 | $table = $installer->getTable('sales_flat_order_payment'); 19 | $installer->getConnection()->addColumn($table, "afterpay_token", "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order Token'"); 20 | $installer->getConnection()->addColumn($table, "afterpay_order_id", "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order ID'"); 21 | $installer->getConnection()->addColumn($table, "afterpay_fetched_at", "TIMESTAMP NULL"); 22 | 23 | // add new status and map it to Payment Review state 24 | $status = 'afterpay_payment_review'; 25 | $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW; 26 | $installer->run("INSERT INTO `{$this->getTable('sales_order_status')}` (`status`, `label`) VALUES ('{$status}', 'Afterpay Processing');"); 27 | $installer->run("INSERT INTO `{$this->getTable('sales_order_status_state')}` (`status`, `state`, `is_default`) VALUES ('{$status}', '{$state}', '0');"); 28 | 29 | $table = $installer->getTable('sales/quote_payment'); 30 | $installer->getConnection()->addColumn($table, 'afterpay_token', "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order Token'"); 31 | $installer->getConnection()->addColumn($table, 'afterpay_order_id', "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order ID'"); 32 | 33 | $installer->endSetup(); 34 | 35 | ?> 36 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/sql/afterpay_setup/install-0.5.0.php: -------------------------------------------------------------------------------- 1 | startSetup(); 13 | 14 | //-------------------------------------------------- 15 | /* 16 | # add custom columns 17 | ALTER TABLE sales_flat_order_payment 18 | ADD COLUMN afterpay_token varchar(255) DEFAULT NULL COMMENT 'Afterpay Order Token', 19 | ADD COLUMN afterpay_order_id varchar(255) DEFAULT NULL COMMENT 'Afterpay Order ID', 20 | ADD COLUMN afterpay_fetched_at TIMESTAMP NULL; 21 | 22 | # add custom order status 23 | INSERT INTO sales_order_status (`status`, `label`) VALUES ('afterpay_payment_review', 'Afterpay Processing'); 24 | INSERT INTO sales_order_status_state (`status`, `state`, `is_default`) VALUES ('afterpay_payment_review', 'payment_review', '0'); 25 | */ 26 | //-------------------------------------------------- 27 | 28 | // add columns to sales/order_payment 29 | $table = $installer->getTable('sales_flat_order_payment'); 30 | $installer->getConnection()->addColumn($table, "afterpay_token", "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order Token'"); 31 | $installer->getConnection()->addColumn($table, "afterpay_order_id", "varchar(255) DEFAULT NULL COMMENT 'Afterpay Order ID'"); 32 | $installer->getConnection()->addColumn($table, "afterpay_fetched_at", "TIMESTAMP NULL"); 33 | 34 | // add new status and map it to Payment Review state 35 | $status = 'afterpay_payment_review'; 36 | $state = Mage_Sales_Model_Order::STATE_PAYMENT_REVIEW; 37 | $installer->run("INSERT INTO `{$this->getTable('sales_order_status')}` (`status`, `label`) VALUES ('{$status}', 'Afterpay Processing');"); 38 | $installer->run("INSERT INTO `{$this->getTable('sales_order_status_state')}` (`status`, `state`, `is_default`) VALUES ('{$status}', '{$state}', '0');"); 39 | //-------------------------------------------------- 40 | 41 | $installer->endSetup(); 42 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Block/Banner.php: -------------------------------------------------------------------------------- 1 | getModuleVersion(); 33 | } 34 | 35 | return "document.write(' 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/firecheckout.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | if (typeof window.checkout !== "undefined") { 3 | /** 4 | * Override the save method when placing order 5 | * 6 | * @type {FireCheckout.save} 7 | */ 8 | var save = window.FireCheckout.prototype.save; 9 | window.FireCheckout.prototype.save = function (urlSuffix, forceSave) { 10 | // if we have paid with the afterpay pay over time method 11 | if (payment.currentMethod == 'afterpaypayovertime') { 12 | this.urls.save = window.AfterpayM1.saveUrl; 13 | 14 | /** 15 | * Override response if using Afterpay. 16 | * Check with response and do redirect or popup 17 | * 18 | * @param transport 19 | */ 20 | this.setResponse = function(transport) { 21 | var response = {}; 22 | // Parse the response - lifted from original method 23 | try { 24 | response = eval('(' + transport.responseText + ')'); 25 | } 26 | catch (e) { 27 | response = {}; 28 | } 29 | 30 | // if the order has been successfully placed 31 | if (response.success) { 32 | window.checkout.setLoadWaiting(false); 33 | 34 | //modified to suit API V1 35 | if( window.afterpayReturnUrl === false ) { 36 | if (typeof AfterPay.initialize === "function") { 37 | AfterPay.initialize(window.afterpayCountryCode); 38 | } else { 39 | AfterPay.init(); 40 | } 41 | } 42 | else { 43 | AfterPay.init({ 44 | relativeCallbackURL: window.afterpayReturnUrl 45 | }); 46 | } 47 | 48 | switch (window.AfterpayM1.redirectMode) { 49 | case 'lightbox': 50 | AfterPay.display({ 51 | token: response.token 52 | }); 53 | break; 54 | 55 | case 'redirect': 56 | AfterPay.redirect({ 57 | token: response.token 58 | }); 59 | break; 60 | } 61 | } else { 62 | if (response.redirect) { 63 | this.isSuccess = false; 64 | location.href = response.redirect; 65 | } else { 66 | alert(response.message); 67 | } 68 | } 69 | 70 | }; 71 | } 72 | 73 | // call the original function 74 | save.apply(this, arguments); 75 | } 76 | } 77 | })(); 78 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/onepage.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | if (typeof window.Review !== "undefined") { 3 | var target = window.Review; 4 | } 5 | else if (typeof window.Payment !== "undefined") { 6 | var target = window.Payment; 7 | } 8 | else { 9 | var target = false; 10 | } 11 | 12 | if (target) { 13 | var reviewSave = target.prototype.save; 14 | target.prototype.save = function() { 15 | // check payment method 16 | if (payment.currentMethod == 'afterpaypayovertime') { 17 | this.saveUrl = window.AfterpayM1.saveUrl; 18 | /** 19 | * Override on complete to do afterpay payment 20 | * 21 | * @param transport 22 | */ 23 | this.onComplete = function(transport) { 24 | var response = {}; 25 | 26 | // Parse the response - lifted from original method 27 | try { 28 | response = eval('(' + transport.responseText + ')'); 29 | } catch (e) { 30 | response = {}; 31 | } 32 | 33 | // If response success 34 | if (response.success) { 35 | //modified to suit API V1 36 | if( window.afterpayReturnUrl === false ) { 37 | if (typeof AfterPay.initialize === "function") { 38 | AfterPay.initialize(window.afterpayCountryCode); 39 | } else { 40 | AfterPay.init(); 41 | } 42 | } 43 | else { 44 | AfterPay.init({ 45 | relativeCallbackURL: window.afterpayReturnUrl 46 | }); 47 | } 48 | 49 | switch (window.AfterpayM1.redirectMode) { 50 | case 'lightbox': 51 | AfterPay.display({ 52 | token: response.token 53 | }); 54 | break; 55 | 56 | case 'redirect': 57 | AfterPay.redirect({ 58 | token: response.token 59 | }); 60 | break; 61 | } 62 | } else { 63 | if (response.redirect) { 64 | this.isSuccess = false; 65 | location.href = response.redirect; 66 | } else { 67 | alert(response.message); 68 | } 69 | } 70 | }; 71 | 72 | /** 73 | * Override the redirect if lightbox is selected 74 | */ 75 | if (window.AfterpayM1.redirectMode == 'lightbox') { 76 | this.onSave = function(){}; 77 | } 78 | } 79 | 80 | /** 81 | * Call original function 82 | */ 83 | reviewSave.apply(this, arguments); 84 | }; 85 | } 86 | })(); 87 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/quickcheckout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Override the FME Quick checkout JS function 3 | */ 4 | (function() { 5 | if (typeof window.Review !== 'undefined') { 6 | var original = window.Review.prototype.save; 7 | 8 | /** 9 | * Adding calculation for Afterpay payment method 10 | */ 11 | window.Review.prototype.save = function () { 12 | // if we have paid with the afterpay pay over time method and use new flow 13 | if (payment.currentMethod == 'afterpaypayovertime') { 14 | // As per sage payment on checkout it self 15 | if ($("billing:use_for_shipping_yes").checked != true) { 16 | shipping.setSameAsBilling(true); 17 | } 18 | // Set the Ajax Url to use afterpay url 19 | review_url = AfterpayM1.saveUrl; 20 | 21 | /** 22 | * Override the response after ajax for afterpay method 23 | * 24 | * @param transport 25 | */ 26 | onestepcheckout.processRespone = function(transport) { 27 | var response = {}; 28 | 29 | // Parse the response - lifted from original method 30 | try { 31 | response = eval('(' + transport.responseText + ')'); 32 | } catch (e) { 33 | response = {}; 34 | } 35 | 36 | // If response success 37 | if (response.success) { 38 | 39 | //modified to suit API V1 40 | if( window.afterpayReturnUrl === false ) { 41 | if (typeof AfterPay.initialize === "function") { 42 | AfterPay.initialize(window.afterpayCountryCode); 43 | } else { 44 | AfterPay.init(); 45 | } 46 | } 47 | else { 48 | AfterPay.init({ 49 | relativeCallbackURL: window.afterpayReturnUrl 50 | }); 51 | } 52 | 53 | switch (window.AfterpayM1.redirectMode) { 54 | case 'lightbox': 55 | AfterPay.display({ 56 | token: response.token 57 | }); 58 | break; 59 | 60 | case 'redirect': 61 | AfterPay.redirect({ 62 | token: response.token 63 | }); 64 | break; 65 | } 66 | } else { 67 | if (response.redirect) { 68 | this.isSuccess = false; 69 | location.href = response.redirect; 70 | return; 71 | } else { 72 | alert(response.message); 73 | } 74 | } 75 | }; 76 | } 77 | 78 | // call the original function 79 | original.apply(this, arguments); 80 | }; 81 | } 82 | })(); 83 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/aw_onestepcheckout.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | if (typeof window.AWOnestepcheckoutForm !== 'undefined') { 3 | /** 4 | * Changing the function before placing the order 5 | * 6 | * @type {AWOnestepcheckoutForm._sendPlaceOrderRequest} 7 | */ 8 | var sendPlaceOrder = window.AWOnestepcheckoutForm.prototype._sendPlaceOrderRequest; 9 | window.AWOnestepcheckoutForm.prototype._sendPlaceOrderRequest = function () { 10 | // check if using afterpay as a payment method 11 | if (awOSCPayment.currentMethod == 'afterpaypayovertime' || payment.currentMethod == 'afterpaypayovertime') { 12 | // Set the place order URL based on the configuration 13 | this.placeOrderUrl = window.AfterpayM1.saveUrl; 14 | 15 | /** 16 | * onComplete function will run after Ajax is finished. 17 | * 1. It will check if ajax return success and continue Afterpay 18 | * 2. Redirect or alert when it's failed 19 | * 20 | * @param transport 21 | */ 22 | this.onComplete = function(transport) { 23 | var response = {}; 24 | 25 | // Parse the response - lifted from original method 26 | try { 27 | response = eval('(' + transport.responseText + ')'); 28 | } catch (e) { 29 | response = {}; 30 | } 31 | 32 | // If response success 33 | if (response.success) { 34 | 35 | //modified to suit API V1 36 | if( window.afterpayReturnUrl === false ) { 37 | if (typeof AfterPay.initialize === "function") { 38 | AfterPay.initialize(window.afterpayCountryCode); 39 | } else { 40 | AfterPay.init(); 41 | } 42 | } 43 | else { 44 | AfterPay.init({ 45 | relativeCallbackURL: window.afterpayReturnUrl 46 | }); 47 | } 48 | 49 | switch (window.AfterpayM1.redirectMode) { 50 | case 'lightbox': 51 | AfterPay.display({ 52 | token: response.token 53 | }); 54 | break; 55 | 56 | case 'redirect': 57 | AfterPay.redirect({ 58 | token: response.token 59 | }); 60 | break; 61 | } 62 | } else { 63 | if (response.redirect) { 64 | this.isSuccess = false; 65 | location.href = response.redirect; 66 | } else { 67 | alert(response.message); 68 | } 69 | } 70 | }; 71 | } 72 | 73 | /** 74 | * Call original function 75 | */ 76 | sendPlaceOrder.apply(this, arguments); 77 | }; 78 | } 79 | })(); 80 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/System/Config/Source/ApiMode.php: -------------------------------------------------------------------------------- 1 | $settings) { 26 | $options[$name] = $settings[self::KEY_NAME]; 27 | } 28 | 29 | return $options; 30 | } 31 | 32 | /** 33 | * Get config prefix for selected API mode 34 | * 35 | * @param string $environment 36 | * @return null|string 37 | */ 38 | public static function getEnvironmentSettings($environment) 39 | { 40 | $settings = self::_getConfigSettings(); 41 | 42 | if (isset($settings[$environment])) { 43 | return $settings[$environment]; 44 | } 45 | 46 | return null; 47 | } 48 | 49 | /** 50 | * Get configured Afterpay environments from config.xml 51 | * 52 | * @return array 53 | */ 54 | protected static function _getConfigSettings() 55 | { 56 | if(Mage::app()->getStore()->isAdmin()) { 57 | $websiteCode = Mage::app()->getRequest()->getParam('website'); 58 | 59 | if ($websiteCode) { 60 | $website = Mage::getModel('core/website')->load($websiteCode); 61 | $websiteId = $website->getId(); 62 | } else { 63 | $order_id = Mage::app()->getRequest()->getParam('order_id'); 64 | 65 | if($order_id) { 66 | $websiteId = Mage::getModel('core/store')->load(Mage::getModel('sales/order')->load($order_id)->getStoreId())->getWebsiteId(); 67 | } else { 68 | $websiteId = 0; 69 | } 70 | } 71 | } else { 72 | $websiteId = null; 73 | } 74 | 75 | if (Mage::app()->getWebsite($websiteId)->getConfig(Mage_Directory_Model_Currency::XML_PATH_CURRENCY_DEFAULT) == 'USD' || 76 | Mage::app()->getWebsite($websiteId)->getConfig(Mage_Directory_Model_Currency::XML_PATH_CURRENCY_DEFAULT) == 'CAD') { 77 | $api = 'api_us_url'; 78 | $web = 'web_us_url'; 79 | } else { 80 | $api = 'api_url'; 81 | $web = 'web_url'; 82 | } 83 | 84 | $options = array(); 85 | 86 | foreach (Mage::getConfig()->getNode('afterpay/environments')->children() as $environment) { 87 | $options[$environment->getName()] = array( 88 | self::KEY_NAME => (string) $environment->name, 89 | self::KEY_API_URL => (string) $environment->{$api}, 90 | self::KEY_WEB_URL => (string) $environment->{$web}, 91 | ); 92 | } 93 | 94 | return $options; 95 | } 96 | 97 | /** 98 | * Get currencyCode for the store 99 | * 100 | * @return array 101 | */ 102 | public static function getCurrencyCode() 103 | { 104 | return Mage::app()->getStore()->getCurrentCurrencyCode(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/gomage.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | if (typeof window.Lightcheckout !== "undefined") { 3 | /** 4 | * Override saveorder ajax to get order token 5 | * 6 | * @type {Lightcheckout.prototype.saveorder|*|Lightcheckout.saveorder} 7 | */ 8 | window.Lightcheckout.prototype.saveorder = function() { 9 | // Check wether is currently using afterpay 10 | if (payment.currentMethod == 'afterpaypayovertime') { 11 | this.showLoadinfo(); 12 | // prepare params 13 | var params = this.getFormData(); 14 | // Ajax to start order token 15 | var request = new Ajax.Request( 16 | window.AfterpayM1.saveUrl, // use Afterpay controller 17 | { 18 | method: 'post', 19 | parameters: params, 20 | onSuccess: function (transport) { 21 | var response = {}; 22 | 23 | // Parse the response - lifted from original method 24 | try { 25 | response = eval('(' + transport.responseText + ')'); 26 | } 27 | catch (e) { 28 | response = {}; 29 | } 30 | 31 | // if the order has been successfully placed 32 | if (response.success) { 33 | 34 | //modified to suit API V1 35 | if( window.afterpayReturnUrl === false ) { 36 | if (typeof AfterPay.initialize === "function") { 37 | AfterPay.initialize(window.afterpayCountryCode); 38 | } else { 39 | AfterPay.init(); 40 | } 41 | } 42 | else { 43 | AfterPay.init({ 44 | relativeCallbackURL: window.afterpayReturnUrl 45 | }); 46 | } 47 | 48 | switch (window.AfterpayM1.redirectMode) { 49 | case 'lightbox': 50 | AfterPay.display({ 51 | token: response.token 52 | }); 53 | 54 | break; 55 | 56 | case 'redirect': 57 | AfterPay.redirect({ 58 | token: response.token 59 | }); 60 | 61 | break; 62 | } 63 | } else { 64 | if (response.redirect) { 65 | this.isSuccess = false; 66 | location.href = response.redirect; 67 | } else { 68 | alert(response.message); 69 | } 70 | } 71 | 72 | }.bind(this), 73 | onFailure: function () { 74 | alert('Afterpay Gateway is not available.'); 75 | } 76 | } 77 | ); 78 | } 79 | }; 80 | } 81 | 82 | })(); 83 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/mw_onestepcheckout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Function specifically for MW OneStepCheckout. The class especially for it 3 | */ 4 | jQuery( document ).ready( function() { 5 | var form = document.getElementById('onestep_form'); 6 | var action = form.getAttribute('action'); 7 | 8 | // save in variable for default .submit 9 | var original = form.submit; 10 | 11 | /** 12 | * Create new definition on .submit 13 | * 14 | * - Check if using Afterpay and use new flow, do Ajax and pop up or redirect 15 | */ 16 | 17 | //hacks the form to prevent override by other plugins 18 | jQuery(".btn-checkout").on("click", function(e) { 19 | 20 | if (payment.currentMethod == 'afterpaypayovertime') { 21 | 22 | e.preventDefault(); 23 | e.stopPropagation(); 24 | 25 | // prepare params 26 | var params = form.serialize(true); 27 | 28 | // Ajax to start order token 29 | var request = new Ajax.Request( 30 | window.AfterpayM1.saveUrl, // use Afterpay controller 31 | { 32 | method: 'post', 33 | parameters: params, 34 | onSuccess: function (transport) { 35 | var response = {}; 36 | 37 | // Parse the response - lifted from original method 38 | try { 39 | response = eval('(' + transport.responseText + ')'); 40 | } 41 | catch (e) { 42 | response = {}; 43 | } 44 | 45 | // if the order has been successfully placed 46 | if (response.success) { 47 | 48 | //modified to suit API V1 49 | if( window.afterpayReturnUrl === false ) { 50 | if (typeof AfterPay.initialize === "function") { 51 | AfterPay.initialize(window.afterpayCountryCode); 52 | } else { 53 | AfterPay.init(); 54 | } 55 | } 56 | else { 57 | AfterPay.init({ 58 | relativeCallbackURL: window.afterpayReturnUrl 59 | }); 60 | } 61 | 62 | switch (window.AfterpayM1.redirectMode) { 63 | case 'lightbox': 64 | AfterPay.display({ 65 | token: response.token 66 | }); 67 | 68 | break; 69 | 70 | case 'redirect': 71 | AfterPay.redirect({ 72 | token: response.token 73 | }); 74 | 75 | break; 76 | } 77 | } else { 78 | if (response.redirect) { 79 | this.isSuccess = false; 80 | location.href = response.redirect; 81 | } else { 82 | alert(response.message); 83 | } 84 | } 85 | 86 | }.bind(this), 87 | onFailure: function () { 88 | alert('Afterpay Gateway is not available.'); 89 | } 90 | } 91 | ); 92 | } 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/amasty_onestepcheckout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Function specifically for MW OneStepCheckout. The class especially for it 3 | */ 4 | jQuery( document ).ready( function() { 5 | var form = document.getElementById('amscheckout-onepage'); 6 | var action = form.getAttribute('action'); 7 | 8 | // save in variable for default .submit 9 | var original = form.submit; 10 | 11 | /** 12 | * Create new definition on .submit 13 | * 14 | * - Check if using Afterpay and use new flow, do Ajax and pop up or redirect 15 | */ 16 | 17 | //hacks the form to prevent override by other plugins 18 | jQuery("#amscheckout-submit").on("click", function(e) { 19 | 20 | if (payment.currentMethod == 'afterpaypayovertime') { 21 | 22 | e.preventDefault(); 23 | e.stopPropagation(); 24 | 25 | // prepare params 26 | var params = form.serialize(true); 27 | 28 | // Ajax to start order token 29 | var request = new Ajax.Request( 30 | window.AfterpayM1.saveUrl, // use Afterpay controller 31 | { 32 | method: 'post', 33 | parameters: params, 34 | onSuccess: function (transport) { 35 | var response = {}; 36 | 37 | // Parse the response - lifted from original method 38 | try { 39 | response = eval('(' + transport.responseText + ')'); 40 | } 41 | catch (e) { 42 | response = {}; 43 | } 44 | 45 | // if the order has been successfully placed 46 | if (response.success) { 47 | 48 | //modified to suit API V1 49 | if( window.afterpayReturnUrl === false ) { 50 | if (typeof AfterPay.initialize === "function") { 51 | AfterPay.initialize(window.afterpayCountryCode); 52 | } else { 53 | AfterPay.init(); 54 | } 55 | } 56 | else { 57 | AfterPay.init({ 58 | relativeCallbackURL: window.afterpayReturnUrl 59 | }); 60 | } 61 | 62 | switch (window.AfterpayM1.redirectMode) { 63 | case 'lightbox': 64 | AfterPay.display({ 65 | token: response.token 66 | }); 67 | 68 | break; 69 | 70 | case 'redirect': 71 | AfterPay.redirect({ 72 | token: response.token 73 | }); 74 | 75 | break; 76 | } 77 | } else { 78 | if (response.redirect) { 79 | this.isSuccess = false; 80 | location.href = response.redirect; 81 | } else { 82 | alert(response.message); 83 | } 84 | } 85 | 86 | }.bind(this), 87 | onFailure: function () { 88 | alert('Afterpay Gateway is not available.'); 89 | } 90 | } 91 | ); 92 | } 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/idev_onestep.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Function specifically for Idev OneStepCheckout. The class especially for it 3 | */ 4 | (function() { 5 | var form = $('onestepcheckout-form'); 6 | var action = form.getAttribute('action'); 7 | 8 | // save in variable for default .submit 9 | var original = form.submit; 10 | 11 | /** 12 | * Create new definition on .submit 13 | * 14 | * - Check if using Afterpay and use new flow, do Ajax and pop up or redirect 15 | */ 16 | form.submit = function() { 17 | //jQuery("#onestepcheckout-place-order").on("click", function() { 18 | if (payment.currentMethod == 'afterpaypayovertime') { 19 | // prepare params 20 | var params = form.serialize(true); 21 | 22 | var customer_password = jQuery('#billing\\:customer_password').val(); 23 | 24 | if( typeof customer_password !== 'undefined' && customer_password.length ) { 25 | params.create_account = 1; 26 | } 27 | 28 | // Registration handling 29 | doAfterpayAPICall(window.AfterpayM1.saveUrl, params); 30 | return false; 31 | 32 | } else { 33 | original.apply(this, arguments); 34 | } 35 | //}); For the jQuery version 36 | }; 37 | })(); 38 | 39 | 40 | function doAfterpayAPICall( saveURL, params ) { 41 | // Ajax to start order token 42 | var request = new Ajax.Request( 43 | saveURL, // use Afterpay controller 44 | { 45 | method: 'post', 46 | parameters: params, 47 | onSuccess: function (transport) { 48 | var response = {}; 49 | 50 | // Parse the response - lifted from original method 51 | try { 52 | response = eval('(' + transport.responseText + ')'); 53 | } 54 | catch (e) { 55 | response = {}; 56 | } 57 | 58 | // if the order has been successfully placed 59 | if (response.success) { 60 | 61 | //modified to suit API V1 62 | if( window.afterpayReturnUrl === false ) { 63 | if (typeof AfterPay.initialize === "function") { 64 | AfterPay.initialize(window.afterpayCountryCode); 65 | } else { 66 | AfterPay.init(); 67 | } 68 | } 69 | else { 70 | AfterPay.init({ 71 | relativeCallbackURL: window.afterpayReturnUrl 72 | }); 73 | } 74 | 75 | switch (window.AfterpayM1.redirectMode) { 76 | case 'lightbox': 77 | AfterPay.display({ 78 | token: response.token 79 | }); 80 | 81 | break; 82 | 83 | case 'redirect': 84 | AfterPay.redirect({ 85 | token: response.token 86 | }); 87 | 88 | break; 89 | } 90 | } else { 91 | if (response.redirect) { 92 | this.isSuccess = false; 93 | location.href = response.redirect; 94 | } else { 95 | alert(response.message); 96 | } 97 | } 98 | 99 | }.bind(this), 100 | onFailure: function () { 101 | alert('Afterpay Gateway is not available.'); 102 | } 103 | } 104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/Api/Http/Client.php: -------------------------------------------------------------------------------- 1 | _helper = Mage::helper('afterpay'); 11 | 12 | if (!extension_loaded('curl')) { 13 | throw Mage::exception( 14 | 'Afterpay_Afterpay', 15 | $this->_helper->__('cURL extension has to be loaded to use this Afterpay Http Client.') 16 | ); 17 | } 18 | 19 | $this->_url = $url; 20 | } 21 | 22 | public function setConfigs($configs = array()) 23 | { 24 | if (isset($configs['auth_user']) && isset($configs['auth_pwd'])) { 25 | $this->_configs[CURLOPT_USERPWD] = $configs['auth_user'].':'.$configs['auth_pwd']; 26 | } 27 | 28 | if (isset($configs['useragent'])) { 29 | $this->_configs[CURLOPT_USERAGENT] = $configs['useragent']; 30 | } 31 | 32 | if (isset($configs['timeout'])) { 33 | $this->_configs[CURLOPT_TIMEOUT] = $configs['timeout']; 34 | } 35 | } 36 | 37 | public function request($method = 'GET', $data = array()) 38 | { 39 | $opts = $this->_configs + array( 40 | CURLOPT_URL => $this->_url, 41 | CURLOPT_HTTPHEADER => array( 42 | 'Accept: application/json', 43 | ), 44 | CURLOPT_HTTPAUTH => CURLAUTH_BASIC, 45 | CURLOPT_RETURNTRANSFER => TRUE, 46 | CURLOPT_TIMEOUT => 80, 47 | ); 48 | 49 | if (!empty($data)) { 50 | $opts[CURLOPT_POSTFIELDS] = json_encode($data); 51 | $opts[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json'; 52 | $opts[CURLOPT_HTTPHEADER][] = 'Content-Length: '.strlen($opts[CURLOPT_POSTFIELDS]); 53 | 54 | switch (strtoupper($method)) { 55 | case 'POST': 56 | $opts[CURLOPT_POST] = TRUE; 57 | break; 58 | case 'PUT': 59 | $opts[CURLOPT_CUSTOMREQUEST] = 'PUT'; 60 | break; 61 | } 62 | } 63 | 64 | $ch = curl_init(); 65 | 66 | if (!curl_setopt_array($ch, $opts)) { 67 | // If an option could not be successfully set 68 | curl_close($ch); 69 | $this->_helper->log('Unable to set cURL with options:'); 70 | $this->_helper->log($opts); 71 | 72 | throw Mage::exception('Afterpay_Afterpay', $this->_helper->__('Unable to set options for cURL.')); 73 | } 74 | 75 | $output = curl_exec($ch); 76 | $errno = curl_errno($ch); 77 | $error = curl_error($ch); 78 | 79 | if ($errno) { 80 | // i.e. It cannot get a response 81 | $this->_helper->log('cURL error number: ' . $errno . ', error message: ' . $error); 82 | $output = json_encode(array( 83 | 'errorCode' => $errno, 84 | 'errorId' => null, 85 | 'message' => $error, 86 | 'httpStatusCode' => null 87 | )); 88 | } 89 | elseif (is_null(json_decode($output))) { 90 | // i.e. It does return a response but it is not in JSON format 91 | $output = json_encode(array( 92 | 'errorCode' => 'non_json', 93 | 'errorId' => null, 94 | 'message' => curl_getinfo($ch, CURLINFO_CONTENT_TYPE) . ' response could not be decoded: ' . json_last_error_msg(), 95 | 'httpStatusCode' => curl_getinfo($ch, CURLINFO_RESPONSE_CODE) 96 | )); 97 | } 98 | 99 | curl_close($ch); 100 | 101 | return $output; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/awesomecheckout.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | if (typeof window.Review !== "undefined") { 3 | /** 4 | * Override save on order review function 5 | */ 6 | var reviewSave = window.Review.prototype.save; 7 | window.Review.prototype.save = function() { 8 | // check payment method 9 | if (payment.currentMethod == 'afterpaypayovertime') { 10 | if (checkout.loadWaiting != false) return; 11 | checkout.setLoadWaiting('review'); 12 | /** 13 | * Override on complete to do afterpay payment 14 | * 15 | * @param transport 16 | */ 17 | // Run ajax to process Afterpay payment using Protorype 18 | var request = new Ajax.Request( 19 | window.AfterpayM1.saveUrl, // use Afterpay controller 20 | { 21 | method: 'post', 22 | onSuccess: function (transport) { 23 | var response = {}; 24 | 25 | // Parse the response - lifted from original method 26 | try { 27 | response = eval('(' + transport.responseText + ')'); 28 | } 29 | catch (e) { 30 | response = {}; 31 | } 32 | 33 | // if the order has been successfully placed 34 | if (response.success) { 35 | 36 | //modified to suit API V1 37 | if( window.afterpayReturnUrl === false ) { 38 | if (typeof AfterPay.initialize === "function") { 39 | AfterPay.initialize(window.afterpayCountryCode); 40 | } else { 41 | AfterPay.init(); 42 | } 43 | } 44 | else { 45 | AfterPay.init({ 46 | relativeCallbackURL: window.afterpayReturnUrl 47 | }); 48 | } 49 | 50 | switch (window.AfterpayM1.redirectMode) { 51 | case 'lightbox': 52 | AfterPay.display({ 53 | token: response.token 54 | }); 55 | 56 | break; 57 | 58 | case 'redirect': 59 | AfterPay.redirect({ 60 | token: response.token 61 | }); 62 | 63 | break; 64 | } 65 | } else { 66 | if (response.redirect) { 67 | this.isSuccess = false; 68 | location.href = response.redirect; 69 | } else { 70 | alert(response.message); 71 | } 72 | } 73 | 74 | }.bind(this), 75 | onFailure: function () { 76 | alert('Afterpay Gateway is not available.'); 77 | } 78 | } 79 | ); 80 | } else { 81 | /** 82 | * Call original function 83 | */ 84 | reviewSave.apply(this, arguments); 85 | } 86 | }; 87 | } 88 | })(); 89 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/iwd_opc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Override the IWD checkout JS function 3 | */ 4 | (function() { 5 | if (typeof window.IWD !== 'undefined' && typeof window.IWD.OPC !== 'undefined') { 6 | /** 7 | * Override Saving order checkout to run Afterpay method first 8 | * 9 | * @type {IWD.OPC.callSaveOrder|*} 10 | */ 11 | var saveOrder = window.IWD.OPC.callSaveOrder; 12 | window.IWD.OPC.callSaveOrder = function (form) { 13 | // if we have paid with the afterpay pay over time method 14 | if (payment.currentMethod == 'afterpaypayovertime') { 15 | 16 | // perform dispatch as per original 17 | IWD.OPC.Plugin.dispatch('saveOrder'); 18 | 19 | // Run ajax to process Afterpay payment using Protorype 20 | var request = new Ajax.Request( 21 | window.AfterpayM1.saveUrl, // use Afterpay controller 22 | { 23 | method: 'post', 24 | parameters: form, 25 | onSuccess: function (transport) { 26 | var response = {}; 27 | 28 | // Parse the response - lifted from original method 29 | try { 30 | response = eval('(' + transport.responseText + ')'); 31 | } 32 | catch (e) { 33 | response = {}; 34 | } 35 | 36 | // if the order has been successfully placed 37 | if (response.success) { 38 | 39 | //modified to suit API V1 40 | if( window.afterpayReturnUrl === false ) { 41 | if (typeof AfterPay.initialize === "function") { 42 | AfterPay.initialize(window.afterpayCountryCode); 43 | } else { 44 | AfterPay.init(); 45 | } 46 | } 47 | else { 48 | AfterPay.init({ 49 | relativeCallbackURL: window.afterpayReturnUrl 50 | }); 51 | } 52 | 53 | switch (window.AfterpayM1.redirectMode) { 54 | case 'lightbox': 55 | AfterPay.display({ 56 | token: response.token 57 | }); 58 | 59 | break; 60 | 61 | case 'redirect': 62 | AfterPay.redirect({ 63 | token: response.token 64 | }); 65 | 66 | break; 67 | } 68 | } else { 69 | if (response.redirect) { 70 | this.isSuccess = false; 71 | location.href = response.redirect; 72 | } else { 73 | alert(response.message); 74 | } 75 | } 76 | 77 | }.bind(this), 78 | onFailure: function () { 79 | alert('Afterpay Gateway is not available.'); 80 | } 81 | } 82 | ); 83 | } else { 84 | // call the original function 85 | saveOrder.apply(this, arguments); 86 | } 87 | }; 88 | } 89 | })(); 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![deprecated](https://img.shields.io/badge/stability-deprecated-red)](https://business.adobe.com/blog/basics/support-magento-1-software-ends-june-30-2020) 2 | 3 | ## 1.1 New Afterpay Installation 4 | 5 | This section outlines the steps to install the Afterpay plugin on your Magento instance for the first time. 6 | 7 | Magento can be installed in any folder on your server. For the purposes of this document, `[MAGENTO]` will refer to the root folder where Magento is installed. 8 | 9 | 1. The Afterpay plugin for Magento 1 is available as a `.zip` or `tar.gz` file from the Afterpay GitHub repository 10 | 1. Unzip the file and copy everything in `/src/` to `[MAGENTO]/` 11 | 1. Login to the Magento Admin and navigate to "System > Cache Management" 12 | 1. Flush the cache storage by selecting _Flush Cache Storage_ 13 | 14 | ## 1.2 Website Configuration 15 | 16 | Afterpay operates under a list of assumptions based on Magento configurations. To align with these assumptions, the Magento configurations must reflect the below. 17 | 18 | 1. **Website Currency must be set to AUD, NZD, USD or CAD** 19 | 20 | Inside the Magento Admin, navigate to "System > Configuration > Currency Setup". Set the base, display and allowed currency as appropriate to match your Afterpay Merchant Account. 21 | 22 | 1. **Postal Code must be mandatory** 23 | 24 | Inside the Magento Admin, navigate to "System > Configuration > General". Ensure _Postal Code_ is **not** configured as optional for any country that Afterpay is being applied to. 25 | 26 | ## 1.3 Afterpay Merchant Setup 27 | 28 | To configure your Afterpay Merchant Account credentials in the Magento Admin, please complete the steps below. The prerequisite for this section is to obtain an Afterpay Merchant ID and Secret Key from Afterpay. 29 | 30 | 1. Inside the Magento Admin, navigate to "System > Configuration > Sales > Payment Methods > Afterpay" 31 | 1. Enter the _Merchant ID_ and _Merchant Secret Key_ (provided by Afterpay) 32 | 1. Enable Afterpay by selecting "Yes" from the _Enabled_ dropdown 33 | 1. Configure the _API Mode_ - select _Sandbox_ for testing on a staging instance, or _Production_ for a live website with legitimate transactions 34 | 1. Save the configuration 35 | 1. Click the _Update Payment Limits_ button to retrieve the Minimum and Maximum Afterpay Order values 36 | 37 | ## 1.4 Afterpay Display Configuration 38 | 39 | 1. Inside the Magento Admin, navigate to "System > Configuration > Sales > Afterpay" 40 | 1. Enable _Debug Mode_ to log transactions and additional valuable data 41 | 1. Configure the display of the Afterpay elements presented on Product Detail Pages (PDP), the cart page and at the checkout 42 | 1. After saving any changes, navigate to "System > Cache Management" 43 | 1. Flush the cache storage by selecting _Flush Cache Storage_ 44 | 45 | ## 1.5 Upgrade Of Afterpay Installation 46 | 47 | This section outlines the steps to upgrade an existing installation of the Afterpay plugin to a new version. 48 | 49 | The process of upgrading the Afterpay plugin for Magento 1 involves the complete removal of all plugin files, followed by copying the new files. 50 | 51 | `[MAGENTO]` will refer to the root folder where you have installed your version of Magento. 52 | 53 | 1. Remove folder: `[MAGENTO]/app/code/community/Afterpay` 54 | 1. Remove folder: `[MAGENTO]/app/design/adminhtml/default/default/template/afterpay` 55 | 1. Remove file: `[MAGENTO]/app/design/frontend/base/default/layout/afterpay.xml` 56 | 1. Remove folder: `[MAGENTO]/app/design/frontend/base/default/template/afterpay` 57 | 1. Remove file: `[MAGENTO]/app/etc/modules/Afterpay_Afterpay.xml` 58 | 1. Remove folder: `[MAGENTO]/js/Afterpay` 59 | 1. Remove folder: `[MAGENTO]/skin/frontend/base/default/afterpay` 60 | 1. The Afterpay plugin for Magento 1 is available as a `.zip` or `tar.gz` file from the Afterpay GitHub repository 61 | 1. Unzip the file and copy everything in `/src/` to `[MAGENTO]/` 62 | 1. Login to the Magento Admin and navigate to "System > Cache Management" 63 | 1. Flush the cache storage by selecting _Flush Cache Storage_ 64 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Block/Catalog/Installments.php: -------------------------------------------------------------------------------- 1 | _getData('product'); 23 | if (!$product) { 24 | $product = Mage::registry('product'); 25 | } 26 | return $product; 27 | } 28 | 29 | public function isEnabled() 30 | { 31 | $product = $this->getProduct(); 32 | return Mage::getStoreConfigFlag(self::XML_CONFIG_PREFIX . 'enable_' . $this->getPageType()) 33 | && Mage::helper('afterpay/checkout')->noConflict() 34 | && Mage::getModel('afterpay/method_payovertime')->canUseForProduct($product) 35 | && !$product->isGrouped(); 36 | } 37 | 38 | public function getCssSelectors() 39 | { 40 | $selectors = Mage::getStoreConfig(self::XML_CONFIG_PREFIX . $this->getPageType() . '_price_block_selectors'); 41 | return explode("\n", $selectors); 42 | } 43 | 44 | public function getHtmlTemplate() 45 | { 46 | ob_start(); 47 | ?> 48 |
49 | or 4 interest-free payments of {price_here} with
50 | 51 | Learn more 52 |
53 | 54 | $this->getCssSelectors(), 97 | 'template' => $this->getHtmlTemplate(), 98 | 'priceSubstitution' => '{price_here}', 99 | 'minPriceLimit' => $this->getMinPriceLimit(), 100 | 'maxPriceLimit' => $this->getMaxPriceLimit(), 101 | 'installmentsAmount' => $this->getInstallmentsAmount(), 102 | 'afterpayEnabled' => $this->getStoreConfigEnabled(), 103 | 'priceFormat' => Mage::app()->getLocale()->getJsPriceFormat(), 104 | 'currencySymbol' => Mage::app()->getLocale()->currency(Mage::app()->getStore()->getCurrentCurrencyCode())->getSymbol(), 105 | 'className' => 'afterpay-installments-amount' 106 | ); 107 | } 108 | 109 | protected function _toHtml() 110 | { 111 | if (!$this->isEnabled()) { 112 | return ''; 113 | } else { 114 | return parent::_toHtml(); 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Helper/Checkout.php: -------------------------------------------------------------------------------- 1 | isAheadworksCheckout()) { 28 | return false; 29 | } 30 | 31 | if ($this->isIwdCheckout()) { 32 | return false; 33 | } 34 | 35 | if ($this->isTmFireCheckout()) { 36 | return false; 37 | } 38 | 39 | // has an extension we don't recognise changed the checkout URL? 40 | if ($this->isDifferentUrl()) { 41 | return true; 42 | } 43 | 44 | // has an extension rewritten the onepage checkout controller 45 | if ($this->defaultUrlExtended()) { 46 | return true; 47 | } 48 | 49 | // probably we are using the default checkout 50 | return false; 51 | } 52 | 53 | /** 54 | * Determine if the current site checkout URL is different to the default 55 | * 56 | * This is achieved by comparing the result of Mage_Checkout_Helper_Url::getCheckoutUrl() with the result of the 57 | * checkout/url helper's same method. In default Magento, these will be the same method and so will return the same 58 | * URL. However, almost all checkout extensions that use a custom URL rewrite this class to themselves and change 59 | * the return value of this method. 60 | * 61 | * This class extends Mage_Checkout_Helper_Url specifically so that it can have easy access to this default method. 62 | * 63 | * @return bool 64 | */ 65 | public function isDifferentUrl() 66 | { 67 | $defaultUrl = $this->getCheckoutUrl(); 68 | $actualUrl = Mage::helper('checkout/url')->getCheckoutUrl(); 69 | 70 | return $defaultUrl != $actualUrl; 71 | } 72 | 73 | /** 74 | * Determine if we are using the Aheadworks one step checkout. 75 | * 76 | * This checks that the global setting added by the extension is enabled and set to true 77 | * 78 | * @return bool 79 | */ 80 | public function isAheadworksCheckout() 81 | { 82 | return Mage::helper('core')->isModuleEnabled('AW_Onestepcheckout'); 83 | } 84 | 85 | /** 86 | * Determine if we are using the IWD one step checkout 87 | * 88 | * This checks the global IWD checkout setting 89 | * 90 | * @return bool 91 | */ 92 | public function isIwdCheckout() 93 | { 94 | return Mage::helper('core')->isModuleEnabled('IWD_Opc'); 95 | } 96 | 97 | /** 98 | * Determine if we are using the TM FireCheckout 99 | * 100 | * This checks the global TM Firecheckout setting 101 | * 102 | * @return bool 103 | */ 104 | public function isTmFireCheckout() 105 | { 106 | return Mage::helper('core')->isModuleEnabled('TM_FireCheckout'); 107 | } 108 | 109 | /** 110 | * Determine if anything is rewriting the default onepage controller 111 | * 112 | * This is not a foolproof way of determining that we are using a custom checkout, on the default URL, but it's 113 | * a reasonably accurate one. 114 | * 115 | * @return bool 116 | */ 117 | public function defaultUrlExtended() 118 | { 119 | $config = Mage::getConfig()->getNode('frontend/routers/checkout/args/modules'); 120 | 121 | if ($config) { 122 | foreach ($config->children() as $override) { 123 | if ($override->getAttribute('before') == 'Mage_Checkout_OnepageController') { 124 | return true; 125 | } 126 | } 127 | } 128 | 129 | return false; 130 | } 131 | 132 | public function noConflict() 133 | { 134 | $currency = Mage::app()->getStore()->getCurrentCurrencyCode(); 135 | $base = Mage::app()->getStore()->getBaseCurrencyCode(); 136 | return $currency == $base && in_array($currency, Afterpay_Afterpay_Model_Method_Base::SUPPORTED_CURRENCIES); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/js/Afterpay/Installments.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Afterpay Installments JS library 3 | * 4 | * @package Afterpay_Afterpay 5 | * @author VEN Development Team 6 | * @copyright Copyright (c) 2015 VEN Commerce Ltd (http://www.ven.com) 7 | * 8 | * How to use: 9 | * 10 | * Define configuration (@see Afterpay_Afterpay_Block_Catalog_Installments::getJsConfig() for detail): 11 | * AfterpayM1.Installments.config = { ... } 12 | * 13 | * Render installments amount on page: 14 | * AfterpayM1.Installments.render(); 15 | * 16 | * @see app/design/frontend/base/default/template/afterpay/catalog/installments.phtml 17 | */ 18 | ;(function (Prototype, Element, Product, console) { 19 | 20 | // window.console fallback 21 | if (!console) { 22 | var f = function () { }; 23 | console = { 24 | log: f, info: f, warn: f, debug: f, error: f 25 | }; 26 | } 27 | 28 | var AfterpayM1 = window.AfterpayM1 = window.AfterpayM1 || {}; 29 | AfterpayM1.Installments = AfterpayM1.Installments || {}; 30 | 31 | /** @see Afterpay_Afterpay_Block_Catalog_Installments::getJsConfig() for details */ 32 | AfterpayM1.Installments.config = null; 33 | 34 | AfterpayM1.Installments.render = function () { 35 | 36 | // check all pre-requisites 37 | if (!Prototype || !Element) { 38 | console.warn('Afterpay: window.Prototype or window.Element is not defined, cannot render installments amount'); 39 | return; 40 | } 41 | if (!Product) { 42 | console.warn('Afterpay: window.Product is not defined, cannot render installments amount'); 43 | return; 44 | } 45 | if (!this.config instanceof Object) { 46 | console.warn('Afterpay: AfterpayM1.Installments.config is not set, cannot render installments amount'); 47 | return; 48 | } 49 | 50 | // find all price-box elements (according to configured selectors) 51 | this.config.selectors = this.config.selectors.filter(function(str) { 52 | return str.replace(/\s/g, '').length; 53 | }); 54 | var priceBoxes = Prototype.Selector.select(this.config.selectors.join(','), document); 55 | 56 | for (var i = 0; i < priceBoxes.length; i++) { 57 | try { 58 | // if price-box is visible 59 | if (!priceBoxes[i].offsetWidth || !priceBoxes[i].offsetHeight) { 60 | continue; 61 | } 62 | 63 | // find 'price' elements and take value from 1st not empty one if there are several of them 64 | // 1st priority - "special price" 65 | var priceElements = Prototype.Selector.select('.special-price .price', priceBoxes[i]); 66 | priceElements = priceElements.concat(Prototype.Selector.select('.price', priceBoxes[i])); 67 | 68 | var price = null; 69 | for (var j = 0; j < priceElements.length; j++) { 70 | price = parseFloat(priceElements[j].textContent.replace(/[^\d.]/g, '')); 71 | if (price != NaN) { 72 | break; 73 | } 74 | } 75 | 76 | // if price isn't empty and min/max order total condition is satisfied then render installments amount 77 | if (price 78 | && (!this.config.minPriceLimit || price >= this.config.minPriceLimit) 79 | && (!this.config.maxPriceLimit || price <= this.config.maxPriceLimit) 80 | && (this.config.afterpayEnabled) 81 | ) { 82 | 83 | var oldElement = priceBoxes[i].nextSibling; 84 | if (oldElement && oldElement instanceof Element 85 | && Element.hasClassName(oldElement, this.config.className)) { 86 | 87 | oldElement.parentNode.removeChild(oldElement); 88 | } 89 | 90 | var individualInstalment = price / this.config.installmentsAmount; 91 | individualInstalment = Math.round(individualInstalment * 100) / 100; 92 | 93 | Element.insert(priceBoxes[i], { 94 | after: this.config.template.replace(this.config.priceSubstitution, 95 | this.config.currencySymbol + individualInstalment.toFixed(2) 96 | ) 97 | }); 98 | 99 | Element.addClassName(priceBoxes[i].nextSibling, this.config.className); 100 | } 101 | else { 102 | var oldElement = priceBoxes[i].nextSibling; 103 | if (oldElement && oldElement instanceof Element 104 | && Element.hasClassName(oldElement, this.config.className)) { 105 | 106 | oldElement.parentNode.removeChild(oldElement); 107 | } 108 | } 109 | 110 | } catch (e) { 111 | console.log('Afterpay: Error on processing price-box element: ', e); 112 | } 113 | } 114 | 115 | }; 116 | 117 | })(window.Prototype, window.Element, window.Product, window.console); 118 | -------------------------------------------------------------------------------- /src/js/Afterpay/checkout/magestore_onestep.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Override function for MageStore checkout when submit order 3 | */ 4 | (function() { 5 | if (typeof window.oscPlaceOrder !== 'undefined') { 6 | var original = window.oscPlaceOrder; 7 | 8 | /** 9 | * Function from original place order and submit the order 10 | * 11 | * This will include: 12 | * - Validation 13 | * - Prepare parameters 14 | * - Ajax 15 | * - Popup/Redirect Afterpay 16 | * 17 | * @param element 18 | */ 19 | window.oscPlaceOrder = function (element) { 20 | if ($('p_method_afterpaypayovertime') && $('p_method_afterpaypayovertime').checked) { 21 | var validator = new Validation('one-step-checkout-form'); 22 | var form = $('one-step-checkout-form'); 23 | if (validator.validate()) { 24 | $('onestepcheckout-place-order-loading').show(); 25 | $('onestepcheckout-button-place-order').removeClassName('onestepcheckout-btn-checkout'); 26 | $('onestepcheckout-button-place-order').addClassName('place-order-loader'); 27 | 28 | var payment_method = $RF(form, 'payment[method]'); 29 | var shipping_method = $RF(form, 'shipping_method'); 30 | var parameters = { 31 | payment: payment_method, 32 | shipping_method: shipping_method 33 | }; 34 | get_billing_data(parameters); 35 | get_shipping_data(parameters); 36 | 37 | if ($('giftmessage-type') && $('giftmessage-type').value != '') { 38 | parameters[$('giftmessage-type').name] = $('giftmessage-type').value; 39 | } 40 | if ($('create_account_checkbox_id') && $('create_account_checkbox_id').checked) { 41 | parameters['create_account_checkbox'] = 1; 42 | } 43 | if ($('gift-message-whole-from') && $('gift-message-whole-from').value != '') { 44 | parameters[$('gift-message-whole-from').name] = $('gift-message-whole-from').value; 45 | } 46 | if ($('gift-message-whole-to') && $('gift-message-whole-to').value != '') { 47 | parameters[$('gift-message-whole-to').name] = $('gift-message-whole-to').value; 48 | } 49 | if ($('gift-message-whole-message') && $('gift-message-whole-message').value != '') { 50 | parameters[$('gift-message-whole-message').name] = $('gift-message-whole-message').value; 51 | } 52 | if ($('billing-address-select') && $('billing-address-select').value != '') { 53 | parameters[$('billing-address-select').name] = $('billing-address-select').value; 54 | } 55 | if ($('shipping-address-select') && $('shipping-address-select').value != '') { 56 | parameters[$('shipping-address-select').name] = $('shipping-address-select').value; 57 | } 58 | 59 | /** 60 | * Perform ajax to Afterpay to get order token 61 | */ 62 | new Ajax.Request( 63 | window.AfterpayM1.saveUrl, // use Afterpay controller 64 | { 65 | method: 'post', 66 | parameters: parameters, 67 | onSuccess: function (transport) { 68 | var response = {}; 69 | 70 | // Parse the response - lifted from original method 71 | try { 72 | response = eval('(' + transport.responseText + ')'); 73 | } 74 | catch (e) { 75 | response = {}; 76 | } 77 | 78 | // if the order has been successfully placed 79 | if (response.success) { 80 | 81 | //modified to suit API V1 82 | if( window.afterpayReturnUrl === false ) { 83 | if (typeof AfterPay.initialize === "function") { 84 | AfterPay.initialize(window.afterpayCountryCode); 85 | } else { 86 | AfterPay.init(); 87 | } 88 | } 89 | else { 90 | AfterPay.init({ 91 | relativeCallbackURL: window.afterpayReturnUrl 92 | }); 93 | } 94 | 95 | switch (window.AfterpayM1.redirectMode) { 96 | case 'lightbox': 97 | AfterPay.display({ 98 | token: response.token 99 | }); 100 | 101 | break; 102 | 103 | case 'redirect': 104 | AfterPay.redirect({ 105 | token: response.token 106 | }); 107 | 108 | break; 109 | } 110 | } else { 111 | if (response.redirect) { 112 | this.isSuccess = false; 113 | location.href = response.redirect; 114 | } else { 115 | alert(response.message); 116 | } 117 | } 118 | 119 | }.bind(this), 120 | onFailure: function () { 121 | alert('Afterpay Gateway is not available.'); 122 | } 123 | } 124 | ); 125 | } 126 | } else { 127 | original.apply(this, arguments); 128 | } 129 | }; 130 | } 131 | })(); 132 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/Api/Routers/Routerv1.php: -------------------------------------------------------------------------------- 1 | true)); 25 | } 26 | 27 | /** 28 | * Only used for Version 1 of the API 29 | * @return string 30 | */ 31 | public function getConfirmOrderUrl() 32 | { 33 | return Mage::getUrl('afterpay/payment/return', array('_secure' => true)); 34 | } 35 | 36 | /** 37 | * Get the URL for valid payment types 38 | * 39 | * @param string $method Which payment method to get the URL for 40 | * @param int|null $store_id Which store to get the config from 41 | * @return string 42 | */ 43 | public function getPaymentUrl($method, $store_id = null) 44 | { 45 | $apiMode = Mage::getStoreConfig('payment/' . $method . '/' . Afterpay_Base::API_MODE_CONFIG_FIELD, $store_id); 46 | $settings = Afterpay_Afterpay_Model_System_Config_Source_ApiMode::getEnvironmentSettings($apiMode); 47 | 48 | //make sure we are using the same version of API for consistency purpose 49 | return $settings[Afterpay_Afterpay_Model_System_Config_Source_ApiMode::KEY_API_URL] . 'v1/configuration'; 50 | } 51 | 52 | 53 | /** 54 | * Function for gateway URL doing Get Payment Update Version 1 55 | * 56 | * @return string|null 57 | */ 58 | public function getOrdersApiUrl( $search_target = NULL, $type = NULL ) 59 | { 60 | $apiMode = Mage::getStoreConfig('payment/afterpaypayovertime/' . Afterpay_Base::API_MODE_CONFIG_FIELD); 61 | $settings = Afterpay_Afterpay_Model_System_Config_Source_ApiMode::getEnvironmentSettings($apiMode); 62 | 63 | //make sure we are using the same version of API for consistency purpose 64 | $gatewayUrl = $settings[Afterpay_Afterpay_Model_System_Config_Source_ApiMode::KEY_API_URL] . 'v1/payments/'; 65 | 66 | if( !empty($type) && $type == $search_target ) { 67 | $url = (substr($gatewayUrl, -1) == '/' ? $gatewayUrl : $gatewayUrl . '/') . 'token:' . urlencode($search_target); 68 | } 69 | else if( !empty($type) && $type == 'id') { 70 | $url = (substr($gatewayUrl, -1) == '/' ? $gatewayUrl : $gatewayUrl . '/') . $search_target; 71 | } 72 | else if( !empty($type) && $type == 'courier') { 73 | $url = (substr($gatewayUrl, -1) == '/' ? $gatewayUrl : $gatewayUrl . '/') . $search_target . "/courier/"; 74 | } 75 | else if( !empty($type) && $type == 'token' ) { 76 | $url = $settings[Afterpay_Afterpay_Model_System_Config_Source_ApiMode::KEY_API_URL] . 'v1/orders/' . $search_target; 77 | } 78 | else { 79 | $url = $settings[Afterpay_Afterpay_Model_System_Config_Source_ApiMode::KEY_API_URL] . 'v1/orders/'; 80 | } 81 | 82 | return $url; 83 | } 84 | 85 | /** 86 | * Get configured gateway URL for payment method 87 | * 88 | * @return string 89 | */ 90 | public function getRefundUrl($payment) 91 | { 92 | $store_id = $payment->getOrder()->getStoreId(); 93 | $order_id = $payment->getData('afterpay_order_id'); 94 | 95 | $apiMode = Mage::getStoreConfig('payment/afterpaypayovertime/' . Afterpay_Base::API_MODE_CONFIG_FIELD, $store_id); 96 | $settings = Afterpay_Afterpay_Model_System_Config_Source_ApiMode::getEnvironmentSettings($apiMode); 97 | 98 | //make sure we are using the same version of API for consistency purpose 99 | return $settings[Afterpay_Afterpay_Model_System_Config_Source_ApiMode::KEY_API_URL] . 'v1/payments/' . $order_id . '/refund'; 100 | } 101 | 102 | /** 103 | * Redirect URL 104 | * 105 | * @return string 106 | */ 107 | public function getOrderPlaceRedirectUrl() 108 | { 109 | return Mage::getUrl('afterpay/payment/redirect', array('_secure' => true)); 110 | } 111 | 112 | /** 113 | * Get configured gateway URL for payment method 114 | * 115 | * @return string|null 116 | */ 117 | public function getWebRedirectJsUrl() 118 | { 119 | $apiMode = Mage::getStoreConfig('payment/afterpaypayovertime/' . Afterpay_Base::API_MODE_CONFIG_FIELD); 120 | $settings = Afterpay_Afterpay_Model_System_Config_Source_ApiMode::getEnvironmentSettings($apiMode); 121 | 122 | return $settings[Afterpay_Afterpay_Model_System_Config_Source_ApiMode::KEY_WEB_URL] . 'afterpay.js'; 123 | } 124 | 125 | 126 | /* ONLY ON VER 1 */ 127 | /** 128 | * Get configured gateway URL for API Ver 1 Direct Capture 129 | * 130 | * @return string|null 131 | */ 132 | public function getDirectCaptureApiUrl() 133 | { 134 | $apiMode = Mage::getStoreConfig('payment/afterpaypayovertime/' . Afterpay_Base::API_MODE_CONFIG_FIELD); 135 | $settings = Afterpay_Afterpay_Model_System_Config_Source_ApiMode::getEnvironmentSettings($apiMode); 136 | 137 | return $settings[Afterpay_Afterpay_Model_System_Config_Source_ApiMode::KEY_API_URL] . 'v1/payments/capture/'; 138 | } 139 | 140 | 141 | /** 142 | * Function for gateway URL doing Get Payment Update Version 1 143 | * 144 | * @return string|null 145 | */ 146 | public function getGatewayApiUrl( $token = NULL ) 147 | { 148 | $apiMode = Mage::getStoreConfig('payment/afterpaypayovertime/' . Afterpay_Base::API_MODE_CONFIG_FIELD); 149 | $settings = Afterpay_Afterpay_Model_System_Config_Source_ApiMode::getEnvironmentSettings($apiMode); 150 | $currency = Mage::app()->getStore()->getCurrentCurrencyCode(); 151 | 152 | $countryCode = 'au'; 153 | if (array_key_exists($currency, Afterpay_Base::CURRENCY_PROPERTIES)){ 154 | $countryCode = Afterpay_Base::CURRENCY_PROPERTIES[$currency]['webCountry']; 155 | } 156 | 157 | //make sure we are using the same version of API for consistency purpose 158 | $gatewayUrl = $settings[Afterpay_Afterpay_Model_System_Config_Source_ApiMode::KEY_WEB_URL] . $countryCode . '/checkout'; 159 | 160 | if( !empty($token) ) { 161 | $url = (substr($gatewayUrl, -1) == '/' ? $gatewayUrl : $gatewayUrl . '/') . '?token=' . urlencode($token) . '&redirected=1&relativeCallbackUrl='; 162 | } 163 | 164 | return $url; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/skin/frontend/base/default/afterpay/css/afterpay.css: -------------------------------------------------------------------------------- 1 | /* What is Afterpay modal */ 2 | #afterpay-what-is-modal img.afterpay-modal-image { 3 | display: block; 4 | } 5 | #afterpay-what-is-modal img.afterpay-modal-image-mobile{ 6 | display: none; 7 | } 8 | 9 | @media only screen 10 | and (max-width : 480px) { 11 | #afterpay-what-is-modal img.afterpay-modal-image-mobile { 12 | display: block; 13 | } 14 | #afterpay-what-is-modal img.afterpay-modal-image{ 15 | display: none; 16 | } 17 | } 18 | 19 | /* Afterpay info in checkout */ 20 | #what-is-afterpay-trigger { 21 | margin-top: 0; 22 | margin-left: 0; 23 | float: none; 24 | } 25 | 26 | #afterpay-logo { 27 | height: 18px; 28 | float: none; 29 | } 30 | 31 | #dt_method_afterpaypayovertime span { 32 | margin-left: 10px; 33 | } 34 | 35 | #payment_form_afterpaypayovertime.form-list { 36 | padding-left: 0; 37 | } 38 | 39 | #payment_form_afterpaypayovertime .form-alt { 40 | display: inline-block; /* shrink-wrap the form, to make the footer align nicely */ 41 | } 42 | 43 | #payment_form_afterpaypayovertime .total-due p { 44 | font-family: Arial, sans-serif; 45 | font-weight: 600; 46 | font-size: 12px; 47 | color: #A1A4A6; 48 | margin: 0 5px 0 0; 49 | text-transform: uppercase; 50 | float: left; 51 | height: 35px; 52 | line-height: 38px; 53 | } 54 | 55 | #payment_form_afterpaypayovertime .total-due span { 56 | font-family: Arial, sans-serif; 57 | font-weight: normal; 58 | font-size: 30px; 59 | height: 35px; 60 | line-height: 35px; 61 | color: #000000; 62 | } 63 | 64 | #payment_form_afterpaypayovertime .instalments { 65 | margin-bottom: 30px; 66 | position: relative; 67 | } 68 | 69 | #payment_form_afterpaypayovertime .instalments .header-text { 70 | font-family: Arial, sans-serif; 71 | font-weight: 600; 72 | font-size: 9px; 73 | color: #A1A4A6; 74 | margin: 10px 0; 75 | text-transform: uppercase; 76 | line-height: 20px; 77 | } 78 | 79 | #payment_form_afterpaypayovertime .instalments .cost, 80 | #payment_form_afterpaypayovertime .instalments .icon, 81 | #payment_form_afterpaypayovertime .instalments .instalment { 82 | display: table-row; 83 | position: relative; 84 | } 85 | 86 | #payment_form_afterpaypayovertime .instalments .icon:after { 87 | content: ""; 88 | background: #f7f7f7; 89 | display: block; 90 | height: 1px; 91 | left: 30px; 92 | right: 30px; 93 | margin-top: -5px; 94 | } 95 | 96 | #payment_form_afterpaypayovertime .instalments li { 97 | display: table-cell; 98 | text-align: center; 99 | padding: 0 10px; 100 | } 101 | 102 | #payment_form_afterpaypayovertime .instalments li:first-child { 103 | padding-left: 0; 104 | } 105 | 106 | #payment_form_afterpaypayovertime .instalments li:last-child { 107 | padding-right: 0; 108 | } 109 | 110 | #payment_form_afterpaypayovertime .instalments .cost { 111 | font-size: 14px; 112 | color: #379dd6; 113 | } 114 | 115 | #payment_form_afterpaypayovertime .instalments .icon img { 116 | width: 12px; 117 | display: inline; 118 | position: relative; 119 | z-index: 1; 120 | } 121 | 122 | #payment_form_afterpaypayovertime .instalments .instalment { 123 | font-family: Arial, sans-serif; 124 | font-size: 11px; 125 | color: #A1A4A6; 126 | } 127 | 128 | #payment_form_afterpaypayovertime .instalment-footer { 129 | font-family: Arial, sans-serif; 130 | font-size: 11px; 131 | color: #636363; 132 | text-align: center; 133 | } 134 | 135 | #payment_form_afterpaypayovertime .instalment-footer a { 136 | font-family: Arial, sans-serif; 137 | font-size: 11px; 138 | color: #A1A4A6; 139 | text-decoration: underline; 140 | } 141 | 142 | @media (min-width: 560px) { 143 | .checkout-onepage-index #payment_form_afterpaypayovertime .instalments li { 144 | padding: 0 30px; 145 | } 146 | 147 | .checkout-onepage-index #payment_form_afterpaypayovertime .instalments .header-text { 148 | font-size: 12px; 149 | } 150 | 151 | .checkout-onepage-index #payment_form_afterpaypayovertime .instalments .cost { 152 | font-size: 25px; 153 | } 154 | } 155 | 156 | /* By default, only show the callout text on the default checkout */ 157 | #afterpay-callout { 158 | display: none; 159 | } 160 | 161 | .checkout-onepage-index #afterpay-callout { 162 | display: block; 163 | margin-left: 0; 164 | } 165 | 166 | /* AW_Onestepcheckout support */ 167 | #aw-onestepcheckout-payment-method #payment_form_afterpaypayovertime .instalments .header-text { 168 | height: auto; 169 | line-height: 1.5; 170 | } 171 | 172 | /* IWD_Opc support */ 173 | .opc-index-index #dt_method_afterpaypayovertime img { 174 | height: auto; 175 | display: inline-block; 176 | margin: 7px auto 0; 177 | } 178 | 179 | .opc-index-index #dt_method_afterpaypayovertime label a { 180 | display: none; 181 | } 182 | 183 | .opc-index-index #payment_form_afterpaypayovertime .form-alt { 184 | font-style: normal; 185 | } 186 | 187 | .opc-index-index #payment_form_afterpaypayovertime .instalments .header-text { 188 | height: auto; 189 | line-height: 1.5; 190 | } 191 | 192 | .opc-index-index #payment_form_afterpaypayovertime .instalments .cost li { 193 | color: #379dd6; 194 | padding: 0 10px; 195 | font-size: 20px; 196 | } 197 | 198 | .opc-index-index #payment_form_afterpaypayovertime .instalments .cost li:first-child { 199 | padding-left: 0; 200 | } 201 | 202 | .opc-index-index #payment_form_afterpaypayovertime .instalment-footer p { 203 | color: #636363; 204 | font-size: 11px; 205 | } 206 | 207 | .opc-index-index #payment_form_afterpaypayovertime .instalments .instalment li { 208 | font-family: Arial, sans-serif; 209 | font-size: 11px; 210 | color: #A1A4A6; 211 | } 212 | 213 | .opc-index-index #payment_form_afterpaypayovertime .instalments li { 214 | padding: 0 20px; 215 | } 216 | 217 | /* GoMage Lightcheckout support */ 218 | #gcheckout-payment-methods-available #payment_form_afterpaypayovertime .instalments .header-text { 219 | height: auto; 220 | line-height: 1.55; 221 | margin-top: 10px; 222 | } 223 | 224 | /* Onetouch on cart page */ 225 | .method-afterpay_onetouch h3 { 226 | font-family: Arial, sans-serif; 227 | font-weight: bold; 228 | color: #379dd6; 229 | font-size: 15px; 230 | margin-bottom: 5px; 231 | text-transform: none; 232 | } 233 | 234 | .method-afterpay_onetouch p { 235 | font-size: 12px; 236 | } 237 | 238 | .method-afterpay_onetouch .button { 239 | margin-top: 10px; 240 | padding: 7px 30px 7px 30px; 241 | } 242 | 243 | .method-afterpay_onetouch .button img { 244 | display:block; 245 | width: 90px; 246 | height: 18px; 247 | } 248 | 249 | .method-afterpay_onetouch .express-button { 250 | width: 267px; 251 | max-width: 100%; 252 | margin-top: 10px; 253 | cursor: pointer; 254 | } 255 | 256 | #payment_form_afterpaypayovertime li { 257 | float:none 258 | } 259 | 260 | /* Hide the doubled Afterpay Assets */ 261 | .product-view .afterpay-installments-amount { 262 | display:none; 263 | } 264 | .product-view .afterpay-installments-amount:last-of-type { 265 | display:inline; 266 | } 267 | -------------------------------------------------------------------------------- /src/app/design/frontend/base/default/layout/afterpay.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | skin_cssafterpay/css/afterpay.css 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | product_page 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | afterpay-order-declined 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Block/Form/Payovertime.php: -------------------------------------------------------------------------------- 1 | getBlockClassName('core/template'); 28 | $block = new $block; 29 | $block->setTemplateHelper($this); 30 | $block->setTemplate(self::TEMPLATE_OPTION_TITLE_CUSTOM); 31 | 32 | if (Mage::getStoreConfigFlag(self::CONFIG_PATH_SHOW_DETAILS)) { 33 | $this->setTemplate(self::TEMPLATE_OPTION_DETAILS_CUSTOM); 34 | } else { 35 | $this->setTemplate(''); 36 | } 37 | $this->setMethodTitle('') 38 | ->setMethodLabelAfterHtml($block->toHtml()); 39 | } 40 | 41 | public function getInstalmentAmount() 42 | { 43 | if (!$this->hasData('instalment_amount')) { 44 | $formatted = Mage::helper('afterpay')->calculateInstalment(); 45 | $this->setData('instalment_amount', $formatted); 46 | } 47 | 48 | return $this->getData('instalment_amount'); 49 | } 50 | 51 | public function getInstalmentAmountCreditUsed() 52 | { 53 | if (!$this->hasData('instalment_amount_credit_used')) { 54 | $formatted = Mage::helper('afterpay')->calculateInstalment(true); 55 | $this->setData('instalment_amount_credit_used', $formatted); 56 | } 57 | 58 | return $this->getData('instalment_amount_credit_used'); 59 | } 60 | 61 | public function getInstalmentAmountLast() 62 | { 63 | if (!$this->hasData('instalment_amount_last')) { 64 | $formatted = Mage::helper('afterpay')->calculateInstalmentLast(); 65 | $this->setData('instalment_amount_last', $formatted); 66 | } 67 | 68 | return $this->getData('instalment_amount_last'); 69 | } 70 | 71 | public function getInstalmentAmountLastCreditUsed() 72 | { 73 | if (!$this->hasData('instalment_amount_last_credit_used')) { 74 | $formatted = Mage::helper('afterpay')->calculateInstalmentLast(true); 75 | $this->setData('instalment_amount_last_credit_used', $formatted); 76 | } 77 | 78 | return $this->getData('instalment_amount_last_credit_used'); 79 | } 80 | 81 | public function getOrderTotal() 82 | { 83 | $total = Mage::getSingleton('checkout/session')->getQuote()->getGrandTotal(); 84 | return Mage::app()->getStore()->formatPrice($total, false); 85 | } 86 | 87 | public function getOrderTotalCreditUsed() 88 | { 89 | $total = Mage::getSingleton('checkout/session')->getQuote()->getGrandTotal(); 90 | $total = $total - Mage::helper('afterpay')->getCustomerBalance(); 91 | if ($total < 0) { 92 | $total = 0; 93 | } 94 | return Mage::app()->getStore()->formatPrice($total, false); 95 | } 96 | 97 | public function getDetailsConfiguration() 98 | { 99 | $config = $this->_getCommonConfiguration(); 100 | $config['template'] = $this->_getCustomDetailTemplate(); 101 | $config['cssSelector'] = '#' . $this->getDetailsTemplateId(); 102 | return $config; 103 | } 104 | 105 | public function getTitleConfiguration() 106 | { 107 | $config = $this->_getCommonConfiguration(); 108 | $config['template'] = $this->_getCustomTitleTemplate(); 109 | $config['cssSelector'] = '#' . $this->getTitleTemplateId(); 110 | return $config; 111 | } 112 | 113 | public function getDetailsTemplateId() 114 | { 115 | return self::DETAIL_TEMPLATE_SELECTOR_ID; 116 | } 117 | 118 | public function getTitleTemplateId() 119 | { 120 | return self::TITLE_TEMPLATE_SELECTOR_ID; 121 | } 122 | 123 | private function _getCommonConfiguration() 124 | { 125 | return array( 126 | 'afterpayLogoSubstitution' => '{afterpay_logo}', 127 | 'afterpayLogo' => 'https://static.afterpay.com/integration/logo-afterpay-colour-72x15@2x.png', 128 | 'orderAmountSubstitution' => '{order_amount}', 129 | 'orderAmount' => $this->getOrderTotal(), 130 | 'orderAmountCreditUsed' => $this->getOrderTotalCreditUsed(), 131 | 'installmentAmountSubstitution' => '{instalment_amount}', 132 | 'installmentAmount' => $this->getInstalmentAmount(), 133 | 'installmentAmountCreditUsed' => $this->getInstalmentAmountCreditUsed(), 134 | 'installmentAmountSubstitutionLast' => '{instalment_amount_last}', 135 | 'installmentAmountLast' => $this->getInstalmentAmountLast(), 136 | 'installmentAmountLastCreditUsed' => $this->getInstalmentAmountLastCreditUsed(), 137 | 'imageCircleOneSubstitution' => '{img_circle_1}', 138 | 'imageCircleOne' => 'https://static.afterpay.com/checkout/circle_1@2x.png', 139 | 'imageCircleTwoSubstitution' => '{img_circle_2}', 140 | 'imageCircleTwo' => 'https://static.afterpay.com/checkout/circle_2@2x.png', 141 | 'imageCircleThreeSubstitution' => '{img_circle_3}', 142 | 'imageCircleThree' => 'https://static.afterpay.com/checkout/circle_3@2x.png', 143 | 'imageCircleFourSubstitution' => '{img_circle_4}', 144 | 'imageCircleFour' => 'https://static.afterpay.com/checkout/Circle_4@2x.png', 145 | 'creditUsedSelector' => '#use_customer_balance' 146 | ); 147 | } 148 | 149 | private function _getCustomDetailTemplate() 150 | { 151 | ob_start(); 152 | ?> 153 | 192 | getQuote(); 56 | 57 | $orderToken = $payment->getData('afterpay_token'); 58 | $reserved_order_id = $quote->getReservedOrderId(); 59 | 60 | //Check for stock levels here 61 | if( empty($orderToken) ) { 62 | // Perform the fallback in case of Unsupported checkout 63 | $this->fallbackMechanism('token_missing'); 64 | } 65 | 66 | // Check total amount 67 | $data = Mage::getModel('afterpay/order')->getOrderByToken( $orderToken ); 68 | 69 | /** 70 | * Validation to check between session and post request 71 | */ 72 | if( !$data ) { 73 | // Check the order token being use 74 | $this->resetTransactionToken($quote); 75 | 76 | Mage::helper('afterpay')->log( 77 | 'Afterpay gateway has rejected request. Invalid token. ' . 78 | ' Token Value: ' . $orderToken 79 | ); 80 | 81 | Mage::throwException( 82 | Mage::helper('afterpay')->__('Afterpay gateway has rejected request. Invalid token.') 83 | ); 84 | } 85 | else if( $reserved_order_id != $data->merchantReference ) { 86 | // Check order id 87 | $this->resetTransactionToken($quote); 88 | 89 | Mage::helper('afterpay')->log( 90 | 'Afterpay gateway has rejected request. Incorrect merchant reference. ' . 91 | ' Quote Value: ' . $reserved_order_id . 92 | ' Afterpay API: ' . $data->merchantReference 93 | ); 94 | 95 | Mage::throwException( 96 | Mage::helper('afterpay')->__('Afterpay gateway has rejected request. Incorrect merchant reference.') 97 | ); 98 | } 99 | else if( round($quote->getGrandTotal(), 2) != round($data->totalAmount->amount, 2) ) { 100 | 101 | // Check the order amount 102 | $this->resetTransactionToken($quote); 103 | 104 | Mage::helper('afterpay')->log( 105 | 'Afterpay gateway has rejected request. Invalid amount. ' . 106 | ' Quote Amount: ' . round($quote->getGrandTotal(), 2) . 107 | ' Afterpay API: ' . round($data->totalAmount->amount, 2) 108 | ); 109 | 110 | Mage::throwException( 111 | Mage::helper('afterpay')->__('Afterpay gateway has rejected request. Invalid amount.') 112 | ); 113 | } 114 | 115 | try { 116 | $data = Mage::getModel('afterpay/order')->directCapture( $orderToken, $reserved_order_id, $quote ); 117 | } 118 | catch( Exception $e ) { 119 | $this->resetTransactionToken($quote); 120 | $this->resetPayment($payment); 121 | 122 | Mage::helper('afterpay')->log( 'Direct Capture Failed: ' . $e->getMessage() ); 123 | 124 | Mage::throwException( 125 | Mage::helper('afterpay')->__( $e->getMessage() ) 126 | ); 127 | } 128 | 129 | 130 | if( !empty($data) && !empty($data->id) ) { 131 | $afterpayOrderId = $data->id; 132 | 133 | // save orderid to payment 134 | if ($payment) { 135 | $payment->setData('afterpay_order_id', $afterpayOrderId)->save(); 136 | $quote->setData('afterpay_order_id', $afterpayOrderId)->save(); 137 | } 138 | } 139 | 140 | 141 | switch($data->status) { 142 | case Afterpay_Afterpay_Model_Method_Base::RESPONSE_STATUS_APPROVED: 143 | $payment->setTransactionId($payment->getData('afterpay_order_id'))->save(); 144 | break; 145 | case Afterpay_Afterpay_Model_Method_Base::RESPONSE_STATUS_DECLINED: 146 | 147 | $this->resetTransactionToken($quote); 148 | 149 | Mage::throwException( 150 | Mage::helper('afterpay')->__('Afterpay payment has been declined. Please use other payment method.') 151 | ); 152 | break; 153 | 154 | default: 155 | 156 | $this->resetTransactionToken($quote); 157 | Mage::throwException( 158 | Mage::helper('afterpay')->__('Cannot find Afterpay payment. Please contact administrator.') 159 | ); 160 | break; 161 | } 162 | 163 | return $this; 164 | } 165 | 166 | /** 167 | * Resetting the token the session 168 | * 169 | * @return bool 170 | */ 171 | public function resetTransactionToken($quote) { 172 | 173 | $quote 174 | ->setData('afterpay_express_checkout', false) 175 | ->setData('afterpay_express_amount', null) 176 | ->setData('afterpay_express_shipping', null) 177 | ->save(); 178 | 179 | Mage::getSingleton("checkout/session")->getQuote()->getPayment()->setData('afterpay_token', NULL)->save(); 180 | 181 | if( Mage::getEdition() == Mage::EDITION_ENTERPRISE ) { 182 | Mage::helper('afterpay')->storeCreditSessionUnset(); 183 | Mage::helper('afterpay')->giftCardsSessionUnset(); 184 | } 185 | 186 | return true; 187 | } 188 | 189 | /** 190 | * Resetting the payment in the capture step 191 | * 192 | * @return bool 193 | */ 194 | public function resetPayment($payment) { 195 | 196 | $payment->setData('afterpay_token', NULL)->save(); 197 | 198 | return true; 199 | } 200 | 201 | /** 202 | * Fallback Mechanism hwen Capture is failing 203 | * 204 | * @param string $error_code 205 | * 206 | * @return void 207 | * @throws Afterpay_Afterpay_Exception 208 | */ 209 | 210 | private function fallbackMechanism($error_code) { 211 | //Unsupported checkout with unattached payovertime.js 212 | //Or checkout with payovertime.js attached, but no checkout specific JS codes 213 | $error_array = array( 214 | // 'invalid_object' 215 | // 'invalid_order_transaction_status', 216 | // 'invalid_token', 217 | 'token_missing' 218 | ); 219 | 220 | if( in_array($error_code, $error_array) ) { 221 | 222 | Mage::helper('afterpay')->log( 223 | sprintf('Unsupported Checkout detected, starting fallback mechanism: ' . $error_code ), 224 | Zend_Log::NOTICE 225 | ); 226 | 227 | $fallback_url = Mage::getUrl( 'afterpay/payment/redirectFallback', array('_secure' => true) ); 228 | 229 | Mage::app()->getResponse()->setRedirect($fallback_url); 230 | Mage::app()->getResponse()->sendResponse(); 231 | 232 | // Throw this exception to avoid sending the PaymentFailedEmail 233 | throw new Mage_Payment_Model_Info_Exception( 234 | Mage::helper('afterpay')->__('Fallback Mechanism Triggered') 235 | ); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 3.2.0 6 | 7 | 8 | 9 | 10 | 11 | Sandbox 12 | https://api-sandbox.afterpay.com/ 13 | https://api.us-sandbox.afterpay.com/ 14 | https://portal.sandbox.afterpay.com/ 15 | https://portal.sandbox.afterpay.com/ 16 | 17 | 18 | Production 19 | https://api.afterpay.com/ 20 | https://api.us.afterpay.com/ 21 | https://portal.afterpay.com/ 22 | https://portal.afterpay.com/ 23 | 24 | 25 | 26 | 27 | 28 | /afterpay/payment/ 29 | 30 | 31 | 32 | standard 33 | 34 | Afterpay_Afterpay 35 | afterpay 36 | 37 | 38 | 39 | 40 | 41 | 42 | afterpay.xml 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | * 52 | 53 | 54 | * 55 | 56 | 57 | 58 | 59 | 60 | Afterpay_Afterpay_Model 61 | afterpay_resource 62 | 63 | 64 | Afterpay_Afterpay_Model_Resource 65 | 66 | 67 | 68 | 69 | 70 | 71 | Afterpay_Afterpay_Block 72 | 73 | 74 | 75 | 76 | Afterpay_Afterpay_Helper 77 | 78 | 79 | 80 | 81 | 82 | Afterpay_Afterpay 83 | Mage_Sales_Model_Resource_Setup 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | afterpay/observer 92 | updateOrderLimits 93 | 94 | 95 | 96 | 97 | 98 | 99 | afterpay/observer 100 | addLogWarningMessage 101 | 102 | 103 | 104 | 105 | 106 | 107 | afterpay/observer 108 | addTokenToOrderResponse 109 | 110 | 111 | 112 | 113 | 114 | 115 | afterpay/observer 116 | addTokenToOrderResponse 117 | 118 | 119 | 120 | 121 | 122 | 123 | afterpay/observer 124 | addTokenToOrderResponse 125 | 126 | 127 | 128 | 129 | 130 | 131 | afterpay/observer 132 | addTokenToOrderResponse 133 | 134 | 135 | 136 | 137 | 138 | 139 | afterpay/observer 140 | addTokenToOrderResponse 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | singleton 149 | afterpay/observer 150 | addModuleToHandle 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | Afterpay_Afterpay_Adminhtml 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 0 6 * * * 172 | 173 | 174 | afterpay/observer::updateOrderLimits 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 1 183 | 184 | 185 | 1 186 | 188 | 1 189 | 190 | 191 | 193 | Interest-free payments. 194 | What is Afterpay? 195 | ]]> 196 | 197 | 1 198 | lightbox 199 | 200 | 201 | 1 202 | 203 | 204 | 205 | 206 | Afterpay - Interest-free payments 207 | No payment today!
208 | 4 equal payments over the next 60 days.]]>
209 | processing 210 | 0 211 | 1 212 | 1 213 | 1 214 | afterpay/method_payovertime 215 | sandbox 216 | authorize_capture 217 | 218 | 4 219 |
220 |
221 |
222 |
223 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/Observer.php: -------------------------------------------------------------------------------- 1 | getAllStatusHistory() as $history) { 28 | if ($history->getComment() === $comment) { 29 | return true; 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | /** 36 | * @return Afterpay_Afterpay_Helper_Data 37 | */ 38 | protected function helper() 39 | { 40 | return Mage::helper('afterpay'); 41 | } 42 | 43 | /** 44 | * Get a database connection 45 | * 46 | * @return Varien_Db_Adapter_Interface 47 | */ 48 | public function getConnection() 49 | { 50 | return Mage::getSingleton('core/resource')->getConnection('core_write'); 51 | } 52 | 53 | /** 54 | * Set a system configuration value 55 | * 56 | * This method is a copy of the Mage_Core_Model_Resource_Setup method, that can't really be called directly 57 | * 58 | * @param string $path Path to system config value 59 | * @param mixed $value Value to set 60 | * @param string $scope Scope level to delete 61 | * @param int $scopeId Scope ID to delete 62 | * @return $this 63 | */ 64 | protected function _setConfigData($path, $value, $scope = 'default', $scopeId = 0) 65 | { 66 | $table = Mage::getSingleton('core/resource')->getTableName('core/config_data'); 67 | // this is a fix for mysql 4.1 68 | $this->getConnection()->showTableStatus($table); 69 | 70 | $data = array( 71 | 'scope' => $scope, 72 | 'scope_id' => $scopeId, 73 | 'path' => $path, 74 | 'value' => $value 75 | ); 76 | $this->getConnection()->insertOnDuplicate($table, $data, array('value')); 77 | return $this; 78 | } 79 | 80 | /** 81 | * Delete a system configuration value 82 | * 83 | * This method is a copy of the Mage_Core_Model_Resource_Setup method, that can't really be called directly 84 | * 85 | * @param string $path Path to system config value 86 | * @param string $scope Configuration scope 87 | * @return $this 88 | */ 89 | protected function _deleteConfigData($path, $scope = null) 90 | { 91 | $where = array('path = ?' => $path); 92 | if (!is_null($scope)) { 93 | $where['scope = ?'] = $scope; 94 | } 95 | $this->getConnection()->delete(Mage::getSingleton('core/resource')->getTableName('core/config_data'), $where); 96 | return $this; 97 | } 98 | 99 | /** 100 | * Update the order limit values for the Afterpay payment methods 101 | * 102 | * Note that in order to fully set configuration values, we clear the config cache after this method runs 103 | * 104 | * @param mixed $observer This is unused and is here for compatibility with the Magento event system 105 | * @throws Mage_Core_Exception 106 | */ 107 | public function updateOrderLimits($observer = null) 108 | { 109 | $configs = array( 110 | 'PAY_BY_INSTALLMENT' => 'afterpaypayovertime', 111 | ); 112 | 113 | $website_param = Mage::app()->getRequest()->getParam('website'); 114 | 115 | foreach ($configs as $tla => $payment) { 116 | 117 | $base = new Afterpay_Afterpay_Model_Method_Payovertime(); 118 | 119 | if (strlen($code = Mage::getSingleton('adminhtml/config_data')->getWebsite())) // website level 120 | { 121 | 122 | $website_code = Mage::getSingleton('adminhtml/config_data')->getWebsite(); 123 | $website_id = Mage::getModel('core/website')->load($website_code)->getId(); 124 | 125 | if (!Mage::app()->getWebsite($website_id)->getConfig('payment/' . $payment . '/active')) { 126 | continue; 127 | } 128 | 129 | $overrides = array('website_id' => $website_id); 130 | $level = 'websites'; 131 | $target_id = $website_id; 132 | } 133 | else if( !empty( $website_param ) ) { 134 | 135 | $website_id = $website_param; 136 | 137 | if (!Mage::app()->getWebsite($website_id)->getConfig('payment/' . $payment . '/active')) { 138 | continue; 139 | } 140 | 141 | $overrides = array('website_id' => $website_id); 142 | $level = 'websites'; 143 | $target_id = $website_id; 144 | } 145 | else // default level 146 | { 147 | $target_id = 0; 148 | $level = 'default'; 149 | 150 | if (!Mage::getStoreConfigFlag('payment/' . $payment . '/active')) { 151 | continue; 152 | } 153 | 154 | $website_code = Mage::getSingleton('adminhtml/config_data')->getWebsite(); 155 | $overrides = array(); 156 | } 157 | 158 | $values = $base->getPaymentAmounts($payment, $tla, $overrides); 159 | 160 | //skip if there is no values 161 | if( !$values ) { 162 | continue; 163 | } 164 | 165 | $this->_doPaymentLimitUpdate($payment, $values, $level, $target_id); 166 | } 167 | 168 | // after changing system configuration, we need to clear the config cache 169 | Mage::app()->cleanCache(array(Mage_Core_Model_Config::CACHE_TAG)); 170 | } 171 | 172 | private function _doPaymentLimitUpdate($payment, $values, $level, $target_id) 173 | { 174 | if (!isset($values['minimumAmount'])) { 175 | $values['minimumAmount'] = ['amount' => 0]; 176 | } 177 | $this->_setConfigData('payment/'.$payment.'/min_order_total', $values['minimumAmount']['amount'], $level, $target_id); 178 | 179 | if (!isset($values['maximumAmount'])) { 180 | $values['maximumAmount'] = ['amount' => 0]; 181 | } 182 | $this->_setConfigData('payment/'.$payment.'/max_order_total', $values['maximumAmount']['amount'], $level, $target_id); 183 | } 184 | 185 | /** 186 | * Add a warning message to show that Magento logging is disabled 187 | * 188 | * @param mixed $observer 189 | */ 190 | public function addLogWarningMessage($observer) 191 | { 192 | $params = Mage::app()->getRequest()->getParams(); 193 | 194 | // if we are editing the afterpay section of the system configuration 195 | if (isset ($params['section']) && $params['section'] == 'afterpay') { 196 | if (Mage::getStoreConfigFlag('afterpay/general/debug') && 197 | !Mage::getStoreConfigFlag('dev/log/active')) { 198 | // Afterpay debug process is enabled, but magento core logging is disabled 199 | Mage::getSingleton('core/session')->addNotice( 200 | Mage::helper('afterpay')->__('Afterpay logging is enabled however core Magento logging is disabled, so Afterpay debug logs will not be written. Please turn on Magento logging at Developer -> Log Settings.') 201 | ); 202 | } 203 | 204 | if (Mage::helper('afterpay/checkout')->isUsingUnsupportedCheckout()) { 205 | Mage::getSingleton('core/session')->addNotice( 206 | Mage::helper('afterpay')->__('You appear to be using a checkout extension not fully supported by Afterpay. The Afterpay popup will be displayed over a new, empty, page.') 207 | ); 208 | } 209 | } 210 | } 211 | 212 | /** 213 | * Set the afterpay order token as part of the order body response 214 | * 215 | * @param $observer 216 | * @return $this 217 | */ 218 | public function addTokenToOrderResponse($observer) 219 | { 220 | $order = Mage::getModel('sales/order')->loadByIncrementId(Mage::getSingleton('checkout/session')->getLastRealOrderId()); 221 | if ($order instanceof Mage_Sales_Model_Order) { 222 | $payment = $order->getPayment(); 223 | if ($payment instanceof Mage_Payment_Model_Info && $payment->getMethodInstance() instanceof Afterpay_Afterpay_Model_Method_Base) { 224 | $response = Mage::app()->getResponse(); 225 | $helper = Mage::helper('core'); 226 | $responseBody = $helper->jsonDecode($response->getBody()); 227 | 228 | $afterpayToken = $payment->getData('afterpay_token'); 229 | 230 | $responseBody['afterpayToken'] = $afterpayToken; 231 | $response->setBody($helper->jsonEncode($responseBody)); 232 | 233 | $this->helper()->log('Setting afterpay token to order (' . $order->getIncrementId() . ') response : ' . $afterpayToken, Zend_Log::DEBUG); 234 | } 235 | } 236 | return $this; 237 | } 238 | 239 | /** 240 | * Adding extra layout handle (i.e _MODULE_) 241 | * 242 | * Due to some other checkout extension will use the same handle but different methodologies. 243 | * Example: Idev_OneStepCheckout and MageStore_Onestepcheckout 244 | * 245 | * @param $observer 246 | */ 247 | public function addModuleToHandle($observer) 248 | { 249 | // Get request 250 | $request = Mage::app()->getRequest(); 251 | 252 | // Applied for only url onestepcheckout/index/index 253 | if ($request->getModuleName() == 'onestepcheckout' && 254 | $request->getControllerName() == 'index' && 255 | $request->getActionName() == 'index') { 256 | 257 | /* @var $update Mage_Core_Model_Layout_Update */ 258 | $update = $observer->getEvent()->getLayout()->getUpdate(); 259 | $update->addHandle('onestepcheckout_index_index_MODULE_' . $request->getControllerModule()); 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/Order.php: -------------------------------------------------------------------------------- 1 | log(array( 18 | 'url' => $url, 19 | 'type' => $type, 20 | 'call' => $call, 21 | 'body' => $this->_sanitizeContent($body) 22 | ), $level); 23 | } 24 | 25 | /** 26 | * The actual function that obfuscates personal info. 27 | * The keywords ['consumer', 'billing', 'shipping'] are derived from the API documents 28 | */ 29 | protected function _sanitizeContent($body) 30 | { 31 | if (is_array($body)) { 32 | foreach ($body as $i => $val) 33 | { 34 | if (in_array($i, ['consumer', 'billing', 'shipping'], TRUE)) { 35 | foreach ($body[$i] as $j => $sensitive) 36 | { 37 | $body[$i][$j] = preg_replace('/\S/', '*', $sensitive); 38 | } 39 | } else { 40 | $body[$i] = $this->_sanitizeContent($val); 41 | } 42 | } 43 | } 44 | return $body; 45 | } 46 | 47 | /** 48 | * @return Mage_Checkout_Model_Session 49 | */ 50 | protected function _getSession() 51 | { 52 | return Mage::getSingleton('checkout/session'); 53 | } 54 | 55 | /** 56 | * Start creating order for Afterpay 57 | * 58 | * @param $quote 59 | * 60 | * @return mixed 61 | * @throws Mage_Core_Exception 62 | */ 63 | public function start($quote) 64 | { 65 | 66 | // Magento calculate the totals 67 | $quote->collectTotals(); 68 | 69 | // Check if total is 0 and Afterpay won't processing it 70 | if (!$quote->getGrandTotal() && !$quote->hasNominalItems()) { 71 | Mage::throwException(Mage::helper('afterpay')->__('Afterpay does not support processing orders with zero amount. To complete your purchase, proceed to the standard checkout process.')); 72 | } 73 | 74 | // Reserved order Id and save it to quote 75 | $quote->reserveOrderId()->save(); 76 | 77 | // Afterpay build order token request - accommodate both Ver 0 and 1 78 | $postData = $this->getApiAdapter()->buildOrderTokenRequest($quote, array('merchantOrderId' => $quote->getReservedOrderId()), $this->afterPayPaymentTypeCode); 79 | 80 | $gatewayUrl = $this->getApiAdapter()->getApiRouter()->getOrdersApiUrl(); 81 | 82 | // Request order token to API 83 | $result = $this->_sendRequest($gatewayUrl, $postData, 'POST', 'StartAfterpayPayment'); 84 | $resultObject = json_decode($result); 85 | 86 | // Check if token is NOT in response 87 | if ( empty($resultObject->orderToken) && empty($resultObject->token) ) { 88 | throw Mage::exception('Afterpay_Afterpay', 'Afterpay API Gateway Error.'); 89 | } else { 90 | // Save token to the sales_flat_quote_payment 91 | 92 | //API Ver 0 93 | if( !empty($resultObject->orderToken) ) { 94 | $orderToken = $resultObject->orderToken; 95 | } 96 | else if( !empty($resultObject->token) ) { 97 | $orderToken = $resultObject->token; 98 | } 99 | 100 | try { 101 | $payment = $quote->getPayment(); 102 | $payment->setData('afterpay_token', $orderToken); 103 | $payment->save(); 104 | 105 | // Added to log 106 | Mage::helper('afterpay')->log( 107 | sprintf('Token successfully saved for reserved order %s. token=%s', $quote->getReservedOrderId(), $orderToken), 108 | Zend_Log::NOTICE 109 | ); 110 | } 111 | catch (Exception $e) { 112 | // Add error message 113 | $message = 'Exception during initial Afterpay Token saving.'; 114 | 115 | $this->helper()->log($this->__($message . ' %s', $e->getMessage()), Zend_Log::ERR); 116 | 117 | Mage::throwException( 118 | Mage::helper('afterpay')->__($message) 119 | ); 120 | } 121 | 122 | return $orderToken; 123 | } 124 | 125 | } 126 | 127 | /** 128 | * Start Express Checkout 129 | * 130 | * @param $quote 131 | * 132 | * @return mixed 133 | * @throws Mage_Core_Exception 134 | */ 135 | public function startExpress($quote) 136 | { 137 | // Magento calculate the totals 138 | $quote->collectTotals(); 139 | 140 | // Check if total is 0 and Afterpay won't processing it 141 | if (!$quote->getGrandTotal() && !$quote->hasNominalItems()) { 142 | Mage::throwException(Mage::helper('afterpay')->__('Afterpay does not support processing orders with zero amount. To complete your purchase, proceed to the standard checkout process.')); 143 | } 144 | 145 | // Reserved order Id and save it to quote 146 | $quote->reserveOrderId()->save(); 147 | 148 | // Afterpay build order token request - accommodate both Ver 0 and 1 149 | $postData = $this->getApiAdapter()->buildExpressOrderTokenRequest($quote); 150 | 151 | $gatewayUrl = $this->getApiAdapter()->getApiRouter()->getOrdersApiUrl(); 152 | 153 | // Request order token to API 154 | $result = $this->_sendRequest($gatewayUrl, $postData, 'POST', 'StartAfterpayExpress'); 155 | $resultObject = json_decode($result); 156 | 157 | // Check if token is NOT in response 158 | if ( empty($resultObject->token) ) { 159 | throw Mage::exception('Afterpay_Afterpay', 'Afterpay API Gateway Error.'); 160 | } else { 161 | // Save token to the sales_flat_quote_payment 162 | 163 | $orderToken = $resultObject->token; 164 | 165 | try { 166 | $payment = $quote->getPayment(); 167 | $payment->setData('afterpay_token', $orderToken); 168 | $payment->save(); 169 | 170 | // Added to log 171 | Mage::helper('afterpay')->log( 172 | sprintf('Token successfully saved for reserved order %s. token=%s', $quote->getReservedOrderId(), $orderToken), 173 | Zend_Log::NOTICE 174 | ); 175 | } 176 | catch (Exception $e) { 177 | // Add error message 178 | $message = 'Exception during initial Afterpay Token saving.'; 179 | 180 | $this->helper()->log($this->__($message . ' %s', $e->getMessage()), Zend_Log::ERR); 181 | 182 | Mage::throwException( 183 | Mage::helper('afterpay')->__($message) 184 | ); 185 | } 186 | 187 | return $orderToken; 188 | } 189 | 190 | } 191 | 192 | /** 193 | * Start creating order for Afterpay 194 | * 195 | * @param string $orderToken 196 | * @param string $merchantOrderId 197 | * @param Mage_Sales_Model_Quote $quote 198 | * 199 | * @return mixed 200 | * @throws Afterpay_Afterpay_Exception 201 | */ 202 | public function directCapture( $orderToken, $merchantOrderId, $quote ) 203 | { 204 | $postData = $this->getApiAdapter()->buildDirectCaptureRequest($orderToken,$merchantOrderId,$quote); 205 | 206 | $gatewayUrl = $this->getApiAdapter()->getApiRouter()->getDirectCaptureApiUrl(); 207 | 208 | // Request order token to API 209 | $result = $this->_sendRequest($gatewayUrl, $postData, 'POST', 'StartAfterpayDirectCapture'); 210 | $resultObject = json_decode($result); 211 | 212 | // Check if token is NOT in response 213 | if( !empty($resultObject->errorCode) || !empty($resultObject->errorId) ) { 214 | 215 | throw Mage::exception('Afterpay_Afterpay', $resultObject->message); 216 | } 217 | else if ( empty($resultObject->id) ) { 218 | throw Mage::exception('Afterpay_Afterpay', 'Afterpay API Gateway Error'); 219 | } 220 | else { 221 | return $resultObject; 222 | } 223 | } 224 | 225 | /** 226 | * Check Afterpay order details using the token 227 | * 228 | * @param string $orderToken 229 | * @param Mage_Sales_Model_Quote $quote 230 | * 231 | * @return mixed 232 | * @throws Afterpay_Afterpay_Exception 233 | */ 234 | public function getOrderByToken( $orderToken ) { 235 | $gatewayUrl = $this->getApiAdapter()->getApiRouter()->getOrdersApiUrl( $orderToken, 'token' ); 236 | 237 | // Request order token to API 238 | $result = $this->_sendRequest($gatewayUrl, false, 'GET', 'Get order by token ' . $orderToken); 239 | $resultObject = json_decode($result); 240 | 241 | return $resultObject; 242 | } 243 | 244 | /** 245 | * Placing order to Magento 246 | * 247 | * @return bool 248 | * @throws Exception 249 | */ 250 | public function place() 251 | { 252 | // Converting quote to order 253 | $service = Mage::getModel('sales/service_quote', $this->_getSession()->getQuote()); 254 | $service->submitAll(); 255 | 256 | $session = $this->_getSession(); 257 | $quote = $session->getQuote(); 258 | 259 | $session->setLastQuoteId($quote->getId()) 260 | ->setLastSuccessQuoteId($quote->getId()) 261 | ->clearHelperData(); 262 | 263 | $order = $service->getOrder(); 264 | if ($order) { 265 | $order->setData('afterpay_order_id', $quote->getData('afterpay_order_id')); 266 | $order->save(); 267 | 268 | $paymentMethod = $order->getPayment()->getMethodInstance(); 269 | if (!$order->getEmailSent() && $paymentMethod->getConfigData('order_email')) { 270 | $order->sendNewOrderEmail(); 271 | } 272 | 273 | // add order information to the session 274 | $session->setLastOrderId($order->getId()) 275 | ->setLastRealOrderId($order->getIncrementId()); 276 | } 277 | 278 | Mage::dispatchEvent( 279 | 'checkout_submit_all_after', 280 | array('order' => $order, 'quote' => $quote, 'recurring_profiles' => array()) 281 | ); 282 | 283 | return $order ? true : false; 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016-2018 Afterpay Touch Group Limited 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/app/code/community/Afterpay/Afterpay/Model/Api/Adapters/Adapterv1.php: -------------------------------------------------------------------------------- 1 | _validateData($object); 31 | 32 | $data = $object->getData(); 33 | 34 | $billingAddress = $object->getBillingAddress(); 35 | 36 | $taxTotal = 0; 37 | 38 | $params['consumer'] = array( 39 | 'email' => (string)$object->getCustomerEmail(), 40 | 'givenNames' => $object->getCustomerFirstname() ? (string)$object->getCustomerFirstname() : $billingAddress->getFirstname(), 41 | 'surname' => $object->getCustomerLastname() ? (string)$object->getCustomerLastname() : $billingAddress->getLastname(), 42 | 'phoneNumber' => substr( (string)$billingAddress->getTelephone(), 0, 32 ) 43 | ); 44 | 45 | $params['items'] = array(); 46 | 47 | foreach ($object->getAllVisibleItems() as $item) { 48 | /** @var Mage_Sales_Model_Order_Item $orderItem */ 49 | $params['items'][] = array( 50 | 'name' => (string)$item->getName(), 51 | 'sku' => $this->_truncateString( (string)$item->getSku() ), 52 | 'quantity' => (int)$item->getQty(), 53 | 'price' => array( 54 | 'amount' => number_format((float)$item->getPriceInclTax(), $precision, '.', ''), 55 | 'currency' => (string)Mage::app()->getStore()->getCurrentCurrencyCode() 56 | ) 57 | ); 58 | 59 | //get the total discount amount 60 | $discount_amount = $item->getDiscountAmount(); 61 | 62 | if ( !empty($discount_amount) && round((float)$discount_amount, $precision) > 0 ) { 63 | 64 | $discount_name = (string)$object->getCouponCode(); 65 | 66 | if( empty($discount_name) || strlen(trim($discount_name)) == '' ) { 67 | $discount_name = 'Discount:'; 68 | } 69 | 70 | $params['discounts'][] = array( 71 | 'displayName' => substr( $discount_name . ' - ' . (string)$item->getName(), 0, 128 ), 72 | 'amount' => array( 73 | 'amount' => number_format((float)$item->getDiscountAmount(), $precision, '.', ''), 74 | 'currency' => (string)Mage::app()->getStore()->getCurrentCurrencyCode() 75 | ), 76 | ); 77 | } 78 | 79 | //get the total discount amount 80 | $taxTotal += $item->getTaxAmount(); 81 | } 82 | 83 | if( isset($taxTotal) && round((float)$taxTotal, $precision) > 0 ) { 84 | $params['taxAmount'] = array( 85 | 'amount' => number_format((float)$taxTotal, $precision, '.', ''), 86 | 'currency' => (string)Mage::app()->getStore()->getCurrentCurrencyCode() 87 | ); 88 | } 89 | 90 | $params['billing'] = array( 91 | 'name' => (string)$billingAddress->getFirstname() . ' ' . $billingAddress->getLastname(), 92 | 'line1' => (string)$billingAddress->getStreet1(), 93 | 'line2' => (string)$billingAddress->getStreet2(), 94 | 'suburb' => (string)$billingAddress->getCity(), 95 | 'postcode' => (string)$billingAddress->getPostcode(), 96 | 'state' => (string)$billingAddress->getRegion(), 97 | 'phoneNumber' => (string)$billingAddress->getTelephone(), 98 | 'countryCode' => (string)$billingAddress->getCountry(), 99 | ); 100 | 101 | if (!$object->isVirtual()) 102 | { 103 | $shippingAddress = $object->getShippingAddress(); 104 | $shippingMethods = $shippingAddress->getShippingRatesCollection(); 105 | 106 | foreach ($shippingMethods as $method) { 107 | $params['courier'] = array( 108 | 'name' => substr($method->getMethodTitle() . " " . $method->getCarrierTitle(), 0, 128), 109 | 'priority' => "STANDARD", 110 | ); 111 | } 112 | 113 | if ($shippingAddress->getShippingInclTax()) { 114 | $params['shippingAmount'] = array( 115 | 'amount' => number_format((float)$shippingAddress->getShippingInclTax(), $precision, '.', ''), // with tax 116 | 'currency' => (string)Mage::app()->getStore()->getCurrentCurrencyCode() 117 | ); 118 | } 119 | 120 | if( !empty( $shippingAddress ) ) { 121 | if( !empty( $shippingAddress->getStreet1() ) ) { 122 | $params['shipping'] = array( 123 | 'name' => (string)$shippingAddress->getFirstname() . ' ' . $shippingAddress->getLastname(), 124 | 'line1' => (string)$shippingAddress->getStreet1(), 125 | 'line2' => (string)$shippingAddress->getStreet2(), 126 | 'suburb' => (string)$shippingAddress->getCity(), 127 | 'postcode' => (string)$shippingAddress->getPostcode(), 128 | 'state' => (string)$shippingAddress->getRegion(), 129 | 'phoneNumber' => (string)$shippingAddress->getTelephone(), 130 | 'countryCode' => (string)$shippingAddress->getCountry(), 131 | ); 132 | } 133 | } 134 | } 135 | 136 | $params['totalAmount'] = array( 137 | 'amount' => number_format((float)$object->getGrandTotal(), $precision, '.', ''), 138 | 'currency' => (string)Mage::app()->getStore()->getCurrentCurrencyCode(), 139 | ); 140 | 141 | $params['merchant'] = array( 142 | 'redirectConfirmUrl' => $this->getApiRouter()->getConfirmOrderUrl(), 143 | 'redirectCancelUrl' => $this->getApiRouter()->getCancelOrderUrl(), 144 | ); 145 | 146 | if( !empty($object) && $object->getReservedOrderId() ) { 147 | $params['merchantReference'] = (string)$object->getReservedOrderId(); 148 | } 149 | 150 | return $params; 151 | } 152 | 153 | public function buildExpressOrderTokenRequest($object) 154 | { 155 | $precision = 2; 156 | 157 | $params = array( 158 | 'mode' => 'express', 159 | 'totalAmount' => array( 160 | 'amount' => round((float)$object->getSubtotalWithDiscount(), $precision), 161 | 'currency' => (string)Mage::app()->getStore()->getCurrentCurrencyCode(), 162 | ), 163 | 'merchant' => array( 164 | 'redirectConfirmUrl' => $this->getApiRouter()->getConfirmOrderUrl(), 165 | 'redirectCancelUrl' => $this->getApiRouter()->getCancelOrderUrl(), 166 | ), 167 | 'merchantReference' => (string)$object->getReservedOrderId(), 168 | 'items' => array(), 169 | 'discounts' => array(), 170 | ); 171 | 172 | foreach ($object->getAllVisibleItems() as $item) { 173 | /** @var Mage_Sales_Model_Order_Item $item */ 174 | $params['items'][] = array( 175 | 'name' => (string)$item->getName(), 176 | 'sku' => $this->_truncateString( (string)$item->getSku() ), 177 | 'quantity' => (int)$item->getQty(), 178 | 'price' => array( 179 | 'amount' => round((float)$item->getPriceInclTax(), $precision), 180 | 'currency' => (string)Mage::app()->getStore()->getCurrentCurrencyCode() 181 | ) 182 | ); 183 | //get the total discount amount 184 | $discount_amount = $item->getDiscountAmount(); 185 | if ( !empty($discount_amount) && round((float)$discount_amount, $precision) > 0 ) { 186 | $discount_name = (string)$object->getCouponCode(); 187 | if( empty($discount_name) || strlen(trim($discount_name)) == '' ) { 188 | $discount_name = 'Discount:'; 189 | } 190 | $params['discounts'][] = array( 191 | 'displayName' => substr( $discount_name . ' - ' . (string)$item->getName(), 0, 128 ), 192 | 'amount' => array( 193 | 'amount' => round((float)$item->getDiscountAmount(), $precision), 194 | 'currency' => (string)Mage::app()->getStore()->getCurrentCurrencyCode() 195 | ), 196 | ); 197 | } 198 | } 199 | return $params; 200 | } 201 | 202 | public function buildDirectCaptureRequest($orderToken, $merchantOrderId, $quote) 203 | { 204 | if ($quote->getData('afterpay_express_checkout') ) { 205 | $params = array( 206 | 'amount' => json_decode($quote->getData('afterpay_express_amount'), true), 207 | 'isCheckoutAdjusted' => true, 208 | 'shipping' => json_decode($quote->getData('afterpay_express_shipping'), true), 209 | ); 210 | } else { 211 | $params = array(); 212 | } 213 | 214 | $params['token'] = $orderToken; 215 | $params['merchantReference'] = $merchantOrderId; 216 | $params['webhookEventUrl'] = ''; 217 | 218 | return $params; 219 | } 220 | 221 | /** 222 | * Get the URL for Refunds API Ver 1 223 | * 224 | * @param float $amount 225 | * @param object $payment 226 | * 227 | * @param string $method Which payment method to get the URL for 228 | * @return array 229 | */ 230 | public function buildRefundRequest($amount, $payment) 231 | { 232 | $params['amount'] = array( 233 | 'amount' => number_format($amount, 2, '.', ''), 234 | 'currency' => $payment->getOrder()->getOrderCurrencyCode(), 235 | ); 236 | 237 | $params['merchantReference'] = NULL; 238 | 239 | return $params; 240 | } 241 | 242 | 243 | /** 244 | * Since 0.12.7 245 | * Truncate the string in case of very long custom values 246 | * 247 | * @param string $string string to truncate 248 | * @param string $length string truncation length 249 | * @param string $appendStr string to be appended after truncate 250 | * @return string 251 | */ 252 | private function _truncateString($string, $length = 64, $appendStr = "") { 253 | $truncated_str = ""; 254 | $useAppendStr = (strlen($string) > intval($length))? true:false; 255 | $truncated_str = substr($string,0,$length); 256 | $truncated_str .= ($useAppendStr)? $appendStr:""; 257 | return $truncated_str; 258 | } 259 | 260 | private function _validateData( $object ) { 261 | 262 | $errors = array(); 263 | 264 | $billingAddress = $object->getBillingAddress(); 265 | 266 | $billing_postcode = $billingAddress->getPostcode(); 267 | $billing_telephone = $billingAddress->getTelephone(); 268 | $billing_city = $billingAddress->getCity(); 269 | $billing_street = $billingAddress->getStreet1(); 270 | 271 | if( empty($billing_postcode) ) { 272 | $errors[] = "Billing Postcode is required"; 273 | } 274 | if( empty($billing_telephone) ) { 275 | $errors[] = "Billing Phone is required"; 276 | } 277 | if( empty($billing_city) ) { 278 | $errors[] = "Billing City/Suburb is required"; 279 | } 280 | if( empty($billing_street) ) { 281 | $errors[] = "Billing Address is required"; 282 | } 283 | 284 | if (!$object->isVirtual()) { 285 | $shippingAddress = $object->getShippingAddress(); 286 | 287 | $shipping_postcode = $shippingAddress->getPostcode(); 288 | $shipping_telephone = $shippingAddress->getTelephone(); 289 | $shipping_city = $shippingAddress->getCity(); 290 | $shipping_street = $shippingAddress->getStreet1(); 291 | 292 | if( empty($shipping_postcode) ) { 293 | $errors[] = "Shipping Postcode is required"; 294 | } 295 | if( empty($shipping_telephone) ) { 296 | $errors[] = "Shipping Phone is required"; 297 | } 298 | if( empty($shipping_city) ) { 299 | $errors[] = "Shipping City/Suburb is required"; 300 | } 301 | if( empty($shipping_street) ) { 302 | $errors[] = "Shipping Address is required"; 303 | } 304 | } 305 | 306 | if( !empty($errors) && count($errors) ) { 307 | throw new InvalidArgumentException( "
" . implode($errors, '
') ); 308 | } 309 | } 310 | } 311 | --------------------------------------------------------------------------------