Przeglądaj źródła

Ticket #259 - Payment system: Payment module.

AntonLV 10 lat temu
rodzic
commit
a1270b326e
48 zmienionych plików z 4480 dodań i 0 usunięć
  1. 221 0
      modules/boonex/payment/classes/BxPaymentCart.php
  2. 150 0
      modules/boonex/payment/classes/BxPaymentConfig.php
  3. 304 0
      modules/boonex/payment/classes/BxPaymentDb.php
  4. 192 0
      modules/boonex/payment/classes/BxPaymentDetails.php
  5. 20 0
      modules/boonex/payment/classes/BxPaymentFormView.php
  6. 35 0
      modules/boonex/payment/classes/BxPaymentGridHistory.php
  7. 90 0
      modules/boonex/payment/classes/BxPaymentGridPending.php
  8. 124 0
      modules/boonex/payment/classes/BxPaymentGridProcessed.php
  9. 118 0
      modules/boonex/payment/classes/BxPaymentJoin.php
  10. 364 0
      modules/boonex/payment/classes/BxPaymentModule.php
  11. 101 0
      modules/boonex/payment/classes/BxPaymentOrders.php
  12. 20 0
      modules/boonex/payment/classes/BxPaymentPageCart.php
  13. 20 0
      modules/boonex/payment/classes/BxPaymentPageDetails.php
  14. 20 0
      modules/boonex/payment/classes/BxPaymentPageHistory.php
  15. 20 0
      modules/boonex/payment/classes/BxPaymentPageJoin.php
  16. 20 0
      modules/boonex/payment/classes/BxPaymentPageOrders.php
  17. 143 0
      modules/boonex/payment/classes/BxPaymentProvider2Checkout.php
  18. 585 0
      modules/boonex/payment/classes/BxPaymentProviderBitPay.php
  19. 269 0
      modules/boonex/payment/classes/BxPaymentProviderPayPal.php
  20. 48 0
      modules/boonex/payment/classes/BxPaymentResponse.php
  21. 317 0
      modules/boonex/payment/classes/BxPaymentTemplate.php
  22. 70 0
      modules/boonex/payment/install/config.php
  23. 30 0
      modules/boonex/payment/install/installer.php
  24. 289 0
      modules/boonex/payment/install/langs/en.xml
  25. 39 0
      modules/boonex/payment/install/sql/disable.sql
  26. 115 0
      modules/boonex/payment/install/sql/enable.sql
  27. 227 0
      modules/boonex/payment/install/sql/install.sql
  28. 30 0
      modules/boonex/payment/install/sql/uninstall.sql
  29. 112 0
      modules/boonex/payment/js/cart.js
  30. 56 0
      modules/boonex/payment/js/orders.js
  31. 18 0
      modules/boonex/payment/request.php
  32. 4 0
      modules/boonex/payment/template/add_to_cart.html
  33. 1 0
      modules/boonex/payment/template/add_to_cart_js.html
  34. 43 0
      modules/boonex/payment/template/cart.html
  35. 60 0
      modules/boonex/payment/template/css/cart.css
  36. 68 0
      modules/boonex/payment/template/css/orders.css
  37. BIN
      modules/boonex/payment/template/images/icons/std-mi.png
  38. BIN
      modules/boonex/payment/template/images/icons/std-pi.png
  39. BIN
      modules/boonex/payment/template/images/icons/std-si.png
  40. BIN
      modules/boonex/payment/template/images/icons/std-wi.png
  41. 23 0
      modules/boonex/payment/template/items.html
  42. 31 0
      modules/boonex/payment/template/order_history.html
  43. 1 0
      modules/boonex/payment/template/order_pending.html
  44. 19 0
      modules/boonex/payment/template/order_pending_process.html
  45. 32 0
      modules/boonex/payment/template/order_processed.html
  46. 21 0
      modules/boonex/payment/template/order_processed_add.html
  47. 1 0
      modules/boonex/payment/template/profile_link.html
  48. 9 0
      modules/boonex/payment/template/redirect.html

+ 221 - 0
modules/boonex/payment/classes/BxPaymentCart.php

@@ -0,0 +1,221 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentCart extends BxBaseModPaymentCart
+{
+    protected $_sLangsPrefix;
+
+    function __construct()
+    {
+    	$this->MODULE = 'bx_payment';
+
+    	parent::__construct();
+
+        $this->_sLangsPrefix = $this->_oModule->_oConfig->getPrefix('langs');
+    }
+
+    /*
+     * Service methods
+     */
+	public function serviceGetBlockCart($iVendor = BX_PAYMENT_EMPTY_ID)
+    {
+    	$iVendor = (int)$iVendor;
+
+    	$iUserId = $this->_oModule->getProfileId();
+        if(empty($iUserId))
+            return MsgBox(_t($this->_sLangsPrefix . 'err_required_login'));
+
+        $aCartInfo = $this->getInfo($iUserId, $iVendor);
+        return array(
+        	'content' => $this->_oModule->_oTemplate->displayBlockCart($aCartInfo, $iVendor),
+        	'menu' => $this->_oModule->_oConfig->getObject('menu_cart_submenu')
+        );
+    }
+
+    public function serviceGetBlockCartHistory()
+    {
+		$iVendorId = bx_get('vendor') !== false ? (int)bx_get('vendor') : 0;
+
+    	$iUserId = $this->_oModule->getProfileId();
+        if(empty($iUserId))
+            return MsgBox(_t($this->_sLangsPrefix . 'err_required_login'));
+
+        return array(
+        	'content' => $this->_oModule->_oTemplate->displayBlockHistory($iUserId, $iVendorId),
+        	'menu' => $this->_oModule->_oConfig->getObject('menu_cart_submenu')
+		);
+    }
+
+    public function serviceAddToCart($iVendorId, $iModuleId, $iItemId, $iItemCount)
+    {
+    	if($iVendorId == BX_PAYMENT_EMPTY_ID || empty($iModuleId) || empty($iItemId) || empty($iItemCount))
+            return array('code' => 1, 'message' => _t($this->_sLangsPrefix . 'err_wrong_data'));
+
+		$iClientId = $this->_oModule->getProfileId();
+        if(empty($iClientId))
+            return array('code' => 2, 'message' => _t($this->_sLangsPrefix . 'err_required_login'));
+
+        if($iClientId == $iVendorId)
+            return array('code' => 3, 'message' => _t($this->_sLangsPrefix . 'err_purchase_from_yourself'));
+
+        $aVendor = $this->_oModule->getVendorInfo($iVendorId);
+        if(!$aVendor['active'])
+            return array('code' => 4, 'message' => _t($this->_sLangsPrefix . 'err_inactive_vendor'));
+
+        $aVendorProviders = $this->_oModule->_oDb->getVendorInfoProviders($iVendorId);
+        if(empty($aVendorProviders))
+            return array('code' => 5, 'message' => _t($this->_sLangsPrefix . 'err_not_accept_payments'));
+
+        $sCartItem = $this->_oModule->_oConfig->descriptorA2S(array($iVendorId, $iModuleId, $iItemId, $iItemCount));
+        $sCartItems = $this->_oModule->_oDb->getCartItems($iClientId);
+
+        if(strpos($sCartItems, $this->_oModule->_oConfig->descriptorA2S(array($iVendorId, $iModuleId, $iItemId))) !== false)
+            $sCartItems = preg_replace_callback(
+            	"/" . $this->_oModule->_oConfig->descriptorA2S(array($iVendorId, $iModuleId, $iItemId, '([0-9])+')) . "/", 
+            	create_function('$aMatches', 'return ' . $this->_oModule->_oConfig->descriptorA2S(array("'" . $iVendorId, $iModuleId, $iItemId, "' . (\$aMatches[1] + " . $iItemCount . ")")) . ';'),
+            	$sCartItems
+			);
+        else
+            $sCartItems = empty($sCartItems) ? $sCartItem : $sCartItems . $this->_oModule->_oConfig->getDivider('descriptors') . $sCartItem;
+
+        $this->_oModule->_oDb->setCartItems($iClientId, $sCartItems);
+
+        $aInfo = $this->getInfo($iClientId);
+        $iTotalQuantity = 0;
+        foreach($aInfo as $aCart)
+           $iTotalQuantity += $aCart['items_count'];
+
+        return array(
+        	'code' => 0, 
+        	'message' => _t($this->_sLangsPrefix . 'msg_successfully_added'), 
+        	'total_quantity' => $iTotalQuantity,
+	        //TODO: Update account submenu if it's needed.  
+        	'content' => '' //$this->_oModule->_oTemplate->displayToolbarSubmenu($aInfo)
+        );
+    }
+
+    public function serviceDeleteFromCart($iVendorId, $iModuleId = 0, $iItemId = 0)
+    {
+        if($iVendorId == BX_PAYMENT_EMPTY_ID)
+            return array('code' => 1, 'message' => _t($this->_sLangsPrefix . 'err_wrong_data'));
+
+		$iClientId = $this->_oModule->getProfileId();
+        if(empty($iClientId))
+            return array('code' => 2, 'message' => _t($this->_sLangsPrefix . 'err_required_login'));
+
+        if(!empty($iModuleId) && !empty($iItemId))
+            $sPattern = "'" . $iVendorId . "_" . $iModuleId . "_" . $iItemId . "_[0-9]+:?'";
+        else
+            $sPattern = "'" . $iVendorId . "_[0-9]+_[0-9]+_[0-9]+:?'";
+
+        $sCartItems = $this->_oModule->_oDb->getCartItems($iClientId);
+        $sCartItems = trim(preg_replace($sPattern, "", $sCartItems), ":");
+        $this->_oModule->_oDb->setCartItems($iClientId, $sCartItems);
+
+        return array('code' => 0, 'message' => _t($this->_sLangsPrefix . 'inf_successfully_deleted'));
+    }
+
+    public function getInfo($iUserId, $iVendorId = BX_PAYMENT_EMPTY_ID, $aItems = array())
+    {
+        if($iVendorId != BX_PAYMENT_EMPTY_ID && !empty($aItems))
+            return $this->_getInfo($iUserId, $iVendorId, $this->_oModule->_oConfig->descriptorsM2A($aItems));
+
+        $aContent = $this->_parseByVendor($iUserId);
+
+        if($iVendorId != BX_PAYMENT_EMPTY_ID)
+            return isset($aContent[$iVendorId]) ? $this->_getInfo($iUserId, $iVendorId, $aContent[$iVendorId]) : array();
+
+        $aResult = array();
+        foreach($aContent as $iVendorId => $aVendorItems)
+            $aResult[$iVendorId] = $this->_getInfo($iUserId, $iVendorId, $aVendorItems);
+
+        return $aResult;
+    }
+
+    public function updateInfo($mixedPending)
+    {
+    	$aPending = is_array($mixedPending) ? $mixedPending : $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => (int)$mixedPending));
+    	if(empty($aPending) || !is_array($aPending))
+    		return false;
+
+		if((int)$aPending['processed'] == 1)
+			return true;
+
+		$iClientId = (int)$aPending['client_id'];
+		$sOrderId = $this->_oModule->_oConfig->getLicense();
+
+        $sCartItems = $this->_oModule->_oDb->getCartItems($iClientId);
+        $aItems = $this->_oModule->_oConfig->descriptorsM2A($aPending['items']);
+
+        foreach($aItems as $aItem) {
+            $aItemInfo = $this->_oModule->callRegisterCartItem((int)$aItem['module_id'], array($aPending['client_id'], $aPending['seller_id'], $aItem['item_id'], $aItem['item_count'], $sOrderId));
+            if(!is_array($aItemInfo) || empty($aItemInfo))
+                continue;
+
+            $this->_oModule->_oDb->insertOrderProcessed(array(
+                'pending_id' => $aPending['id'],
+                'order_id' => $sOrderId,
+                'client_id' => $aPending['client_id'],
+                'seller_id' => $aPending['seller_id'],
+                'module_id' => $aItem['module_id'],
+                'item_id' => $aItem['item_id'],
+                'item_count' => $aItem['item_count'],
+                'amount' => $aItemInfo['price'] * $aItem['item_count'],
+            ));
+
+            $sCartItems = trim(preg_replace("'" . $this->_oModule->_oConfig->descriptorA2S($aItem) . ":?'", "", $sCartItems), ":");
+        }
+
+		$this->_oModule->_oDb->setCartItems($iClientId, $sCartItems);
+        return $this->_oModule->_oDb->updateOrderPending($aPending['id'], array('processed' => 1));
+    }
+
+    /**
+     * Enter description here...
+     *
+     * @param  integer $iClientId client's ID
+     * @param  integer $iVendorId vendor's ID
+     * @param  array   $aItems    item descriptors(quaternions) from shopping cart.
+     * @return array   with full info about vendor and items.
+     */
+    protected function _getInfo($iClientId, $iVendorId, $aItems)
+    {
+        $iItemsCount = 0;
+        $fItemsPrice = 0;
+        $aItemsInfo = array();
+        foreach($aItems as $aItem) {
+            $aItemInfo = $this->_oModule->callGetCartItem((int)$aItem['module_id'], array($aItem['item_id']));
+            $aItemInfo['module_id'] = (int)$aItem['module_id'];
+            $aItemInfo['quantity'] = (int)$aItem['item_count'];
+
+            $iItemsCount += $aItem['item_count'];
+            $fItemsPrice += $aItem['item_count'] * $aItemInfo['price'];
+            $aItemsInfo[] = $aItemInfo;
+        }
+
+        $aVendor = $this->_oModule->getVendorInfo((int)$iVendorId);
+        return array(
+        	'client_id' => $iClientId,
+            'vendor_id' => $aVendor['id'],
+            'vendor_name' => $aVendor['name'],
+        	'vendor_link' => $aVendor['link'],
+            'vendor_icon' => $aVendor['icon'],
+        	'vendor_unit' => $aVendor['unit'],
+            'vendor_currency_code' => $aVendor['currency_code'],
+            'vendor_currency_sign' => $aVendor['currency_sign'],
+            'items_count' => $iItemsCount,
+            'items_price' => $fItemsPrice,
+            'items' => $aItemsInfo
+        );
+    }
+}
+
+/** @} */

+ 150 - 0
modules/boonex/payment/classes/BxPaymentConfig.php

@@ -0,0 +1,150 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentConfig extends BxBaseModPaymentConfig
+{
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_aUrls = array(
+        	'join' => 'page.php?i=payment-join',
+        	'cart' => 'page.php?i=payment-cart',
+        	'history' => 'page.php?i=payment-history',
+        	'orders' => 'page.php?i=payment-orders',
+        	'details' => 'page.php?i=payment-details',
+
+        	'return' => 'page.php?i=payment-cart',
+        	'return_data' => BX_DOL_URL_ROOT . $this->getBaseUri() . 'finalize_checkout/'
+        );
+
+        $this->_aHtmlIds = array(
+        	'history' => array(
+        		'order_history_view' => $this->_sName . '-order-view-history',
+        	),
+        	'pending' => array(
+        		'order_pending_view' => $this->_sName . '-order-view-pending',
+        		'order_pending_process' => $this->_sName . '-order-process',
+        	),
+        	'processed' => array(
+        		'order_processed_view' => $this->_sName . '-order-view-processed',
+        		'order_processed_add' => $this->_sName . '-order-add',
+        		'order_processed_client_id' => $this->_sName . '-oa-client-id',
+        		'order_processed_client' => $this->_sName . '-oa-client',
+        		'order_processed_items' => $this->_sName . '-oa-items',	
+        	),
+        );
+
+        $this->_aPerPage = array(
+        	'orders' => 10,
+        	'history' => 10
+        );
+        $this->_aHandlers = array();
+
+        $this->_aJsClasses = array(
+        	'cart' => 'BxPaymentCart',
+        	'history' => 'BxPaymentOrders',
+        	'pending' => 'BxPaymentOrders',
+        	'processed' => 'BxPaymentOrders',
+        );
+
+        $this->_aJsObjects = array(
+        	'cart' => 'oPaymentCart',
+        	'history' => 'oPaymentOrders',
+        	'pending' => 'oPaymentOrders',
+        	'processed' => 'oPaymentOrders',
+        );
+    }
+
+	public function getLicense()
+    {
+        list($fMilliSec, $iSec) = explode(' ', microtime());
+        $fSeed = (float)$iSec + ((float)$fMilliSec * 100000);
+        srand($fSeed);
+
+        $sResult = '';
+        for($i=0; $i < 16; ++$i) {
+            switch(rand(1,2)) {
+                case 1:
+                    $c = chr(rand(ord('A'),ord('Z')));
+                    break;
+                case 2:
+                    $c = chr(rand(ord('0'),ord('9')));
+                    break;
+            }
+            $sResult .= $c;
+        }
+
+        return $sResult;
+    }
+
+    public function getModuleId($mixedId)
+    {
+    	if(is_int($mixedId))
+    		return (int)$mixedId;
+
+		if(is_string($mixedId)) {
+            $aInfo = $this->_oDb->getModuleByName($mixedId);
+            if(!is_array($aInfo) || empty($aInfo['id']))
+            	$aInfo = $this->_oDb->getModuleByUri($mixedId);
+
+            if(is_array($aInfo) && !empty($aInfo['id']))
+            	return (int)$aInfo['id'];
+        } 
+
+        return 0;
+    }
+
+    public function descriptorA2S($a) 
+    {
+    	return implode($this->getDivider('descriptor'), $a);
+    }
+
+	public function descriptorS2A($s) 
+    {
+    	return explode($this->getDivider('descriptor'), $s);
+    }
+
+	/**
+     * Conver items to array with necessary structure.
+     *
+     * @param  string/array $mixed - string with cart items divided with (:) or an array of cart items.
+     * @return array        with items.
+     */
+    public function descriptorsM2A($mixed)
+    {
+        $aResult = array();
+
+        if(is_string($mixed))
+           $aItems = explode($this->getDivider('descriptors'), $mixed);
+        else if(is_array($mixed))
+           $aItems = $mixed;
+        else
+            $aItems = array();
+
+        foreach($aItems as $sItem) {
+            $aItem = $this->descriptorS2A($sItem);
+            $aResult[] = array('vendor_id' => $aItem[0], 'module_id' => $aItem[1], 'item_id' => $aItem[2], 'item_count' => $aItem[3]);
+        }
+
+        return $aResult;
+    }
+
+	public function http2https($s)
+    {
+    	if(strncmp($s, 'https://', 8) == 0)
+    		return $s;
+
+        return 'https://' . bx_ltrim_str($s, 'http://');
+    }
+}
+
+/** @} */

+ 304 - 0
modules/boonex/payment/classes/BxPaymentDb.php

@@ -0,0 +1,304 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentDb extends BxBaseModPaymentDb
+{
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+
+    /**
+     * Payment details methods
+     */
+    public function getForm()
+    {
+        $sQuery = "SELECT
+                `tp`.`id` AS `provider_id`,
+                `tp`.`name` AS `provider_name`,
+                `tp`.`caption` AS `provider_caption`,
+                `tp`.`description` AS `provider_description`,
+                `tp`.`option_prefix` AS `provider_option_prefix`,
+                `tpo`.`id` AS `id`,
+                `tpo`.`name` AS `name`,
+                `tpo`.`type` AS `type`,
+                `tpo`.`caption` AS `caption`,
+                `tpo`.`description` AS `description`,
+                `tpo`.`extra` AS `extra`,
+                `tpo`.`check_type` AS `check_type`,
+                `tpo`.`check_params` AS `check_params`,
+                `tpo`.`check_error` AS `check_error`
+            FROM `" . $this->_sPrefix . "providers` AS `tp`
+            LEFT JOIN `" . $this->_sPrefix . "providers_options` AS `tpo` ON `tp`.`id`=`tpo`.`provider_id`
+            ORDER BY `tp`.`id` ASC, `tpo`.`order` ASC";
+
+        return $this->getAll($sQuery);
+    }
+
+    public function getFormData($iUserId)
+    {
+        $sQuery = $this->prepare("SELECT
+                `tuv`.`option_id` AS `option_id`,
+                `tuv`.`value` AS `value`
+            FROM `" . $this->_sPrefix . "user_values` AS `tuv`
+            WHERE `tuv`.`user_id`=?", $iUserId);
+
+        return $this->getAllWithKey($sQuery, 'option_id');
+    }
+
+	public function getProviders($sName = '')
+    {
+        $sWhereClause = "1";
+        if(!empty($sName))
+            $sWhereClause = $this->prepare("`tp`.`name`=?", $sName);
+
+        $sQuery = "SELECT
+                `tp`.`id` AS `id`,
+                `tp`.`name` AS `name`,
+                `tp`.`caption` AS `caption`,
+                `tp`.`description` AS `description`,
+                `tp`.`option_prefix` AS `option_prefix`,
+                `tp`.`for_visitor` AS `for_visitor`,
+                `tp`.`class_name` AS `class_name`,
+                `tp`.`class_file` AS `class_file`
+            FROM `" . $this->_sPrefix . "providers` AS `tp`
+            WHERE " . $sWhereClause;
+
+        return !empty($sName) ? $this->getRow($sQuery) : $this->getAll($sQuery);
+    }
+
+    public function getOptions($iUserId = BX_PAYMENT_EMPTY_ID, $iProviderId = 0)
+    {
+        if($iUserId == BX_PAYMENT_EMPTY_ID && empty($iProviderId))
+           return $this->getAll("SELECT `id`, `name`, `type` FROM `" . $this->_sPrefix . "providers_options`");
+
+        $sWhereAddon = "";
+        if(!empty($iProviderId))
+            $sWhereAddon = $this->prepare(" AND `tpo`.`provider_id`=?", $iProviderId);
+
+        $sQuery = $this->prepare("SELECT
+               `tpo`.`name` AS `name`,
+               `tuv`.`value` AS `value`
+            FROM `" . $this->_sPrefix . "providers_options` AS `tpo`
+            LEFT JOIN `" . $this->_sPrefix . "user_values` AS `tuv` ON `tpo`.`id`=`tuv`.`option_id`
+            WHERE 1" . $sWhereAddon . " AND `tuv`.`user_id`=?", $iUserId);
+
+        return $this->getAllWithKey($sQuery, 'name');
+    }
+
+    public function updateOption($iUserId, $iOptionId, $sValue)
+    {
+        $sQuery = $this->prepare("REPLACE INTO `" . $this->_sPrefix . "user_values` SET `user_id`=?, `option_id`=?, `value`=?", $iUserId, $iOptionId, $sValue);
+        return $this->query($sQuery);
+    }
+
+
+    /**
+     * Shopping cart methods.
+     */
+    public function getCartItems($iId)
+    {
+    	$sQuery = $this->prepare("SELECT `items` FROM `" . $this->_sPrefix . "cart` WHERE `client_id`=? LIMIT 1", $iId);
+        return $this->getOne($sQuery);
+    }
+    public function setCartItems($iId, $sItems)
+    {
+        $sItems = trim($sItems, ":");
+        if(empty($sItems))
+            $sQuery = $this->prepare("DELETE FROM `" . $this->_sPrefix . "cart` WHERE `client_id`=? LIMIT 1", $iId);
+        else
+            $sQuery = $this->prepare("REPLACE INTO `" . $this->_sPrefix . "cart` SET `client_id`=?, `items`=?", $iId, $sItems);
+
+        return $this->query($sQuery);
+    }
+    public function getVendorInfoProviders($iId, $sProvider = '')
+    {
+        $aResult = array();
+
+        //--- Get specified payment provider if it's available ---//
+        if(!empty($sProvider)) {
+            $aProvider = $this->getProviders($sProvider);
+            $aOptions = $this->getOptions($iId, $aProvider['id']);
+
+            if(isset($aOptions[$aProvider['option_prefix'] . 'active']) && $aOptions[$aProvider['option_prefix'] . 'active']['value'] == 'on') {
+                $aProvider['options'] = $aOptions;
+                $aResult = $aProvider;
+            }
+        }
+        //--- Get all available payment providers ---//
+        else{
+            $aProviders = $this->getProviders();
+            $aOptions = $this->getOptions($iId);
+
+            foreach($aProviders as $aProvider)
+               if(isset($aOptions[$aProvider['option_prefix'] . 'active']) && $aOptions[$aProvider['option_prefix'] . 'active']['value'] == 'on') {
+                   foreach($aOptions as $sName => $aOption)
+                       if(strpos($sName, $aProvider['option_prefix']) !== false)
+                           $aProvider['options'][$sName] = $aOption;
+                   $aResult[] = $aProvider;
+               }
+        }
+
+        return $aResult;
+    }
+    public function getFirstAdminId()
+    {
+    	$sQuery = $this->prepare("SELECT `profile_id` FROM `sys_accounts` WHERE `role`&" . BX_DOL_ROLE_ADMIN . " ORDER BY `profile_id` ASC LIMIT 1");
+        return (int)$this->getOne($sQuery);
+    }
+    public function getAdminsIds()
+    {
+    	$sQuery = $this->prepare("SELECT `profile_id` FROM `sys_accounts` WHERE `role`&" . BX_DOL_ROLE_ADMIN . " ORDER BY `profile_id` ASC");
+        return $this->getColumn($sQuery);
+    }
+
+
+	/*
+     * Pending Orders methods
+     */
+    public function getOrderPending($aParams)
+    {
+    	$aMethod = array('name' => 'getRow', 'params' => array(0 => 'query'));
+
+    	$sWhereClause = $sLimitClause = '';
+        switch($aParams['type']) {
+            case 'id':
+                $sWhereClause = $this->prepare(" AND `id`=?", $aParams['id']);
+                $sLimitClause = " LIMIT 1";
+                break;
+        }
+
+        $aMethod['params'][0] = "SELECT * FROM `" . $this->_sPrefix . "transactions_pending` WHERE 1 " . $sWhereClause . $sLimitClause;
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+
+    public function insertOrderPending($iClientId, $sProviderName, $aCartInfo)
+    {
+        $sItems = "";
+        foreach($aCartInfo['items'] as $aItem)
+            $sItems .= $aCartInfo['vendor_id'] . '_' . $aItem['module_id'] . '_' . $aItem['id'] . '_' . $aItem['quantity'] . ':';
+
+        $sQuery = $this->prepare("INSERT INTO `" . $this->_sPrefix . "transactions_pending` SET
+                    `client_id`=?,
+                    `seller_id`=?,
+                    `items`=?,
+                    `amount`=?,
+                    `provider`=?,
+                    `date`=UNIX_TIMESTAMP()", $iClientId, $aCartInfo['vendor_id'], trim($sItems, ':'), $aCartInfo['items_price'], $sProviderName);
+
+        return (int)$this->query($sQuery) > 0 ? $this->lastId() : 0;
+    }
+
+    public function updateOrderPending($iId, $aValues)
+    {
+        $sQuery = $this->prepare("UPDATE `" . $this->_sPrefix . "transactions_pending` SET " . $this->arrayToSQL($aValues) . " WHERE `id`=?", $iId);
+        return (int)$this->query($sQuery) > 0;
+    }
+
+    public function cancelOrderPending($mixedId)
+    {
+    	if(!is_array($mixedId))
+    		$mixedId = array($mixedId);
+
+        return (int)$this->query("DELETE FROM `" . $this->_sPrefix . "transactions_pending` WHERE `id` IN (" . $this->implode_escape($mixedId) . ")") > 0;
+    }
+    
+    /*
+     * Processed Orders
+     */
+    public function getOrderProcessed($aParams)
+    {
+    	$aMethod = array('name' => 'getRow', 'params' => array(0 => 'query'));
+
+        $sWhereClause = "";
+        switch($aParams['type']) {
+            case 'id':
+                $sWhereClause = $this->prepare(" AND `tt`.`id`=?", $aParams['id']);
+                break;
+
+			case 'pending_id':
+                $aMethod['name'] = 'getAll';
+                if(!empty($aParams['with_key'])) {
+					$aMethod['name'] = 'getAllWithKey';
+					$aMethod['params'][1] = $aParams['with_key'];
+                }
+                $sWhereClause = $this->prepare(" AND `tt`.`pending_id`=?", $aParams['pending_id']);
+                break;
+
+            case 'order_id':
+                $aMethod['name'] = 'getAll';
+                $sWhereClause = $this->prepare(" AND `tt`.`order_id`=?", $aParams['order_id']);
+                break;
+
+            case 'mixed':
+                $aMethod['name'] = 'getAll';
+                foreach($aParams['conditions'] as $sKey => $sValue)
+                    $sWhereClause .= $this->prepare(" AND `tt`.`" . $sKey . "`=?", $sValue);
+                break;
+
+        }
+
+        $aMethod['params'][0] = "SELECT
+        		`tt`.`id`,
+                `tt`.`order_id`,
+                `tt`.`client_id`,
+                `tt`.`seller_id`,
+                `tt`.`module_id`,
+                `tt`.`item_id`,
+                `tt`.`item_count`,
+                `tt`.`amount`,
+                `tt`.`date`,
+                `ttp`.`order`,
+                `ttp`.`error_msg`,
+                `ttp`.`provider`
+            FROM `" . $this->_sPrefix . "transactions` AS `tt`
+            LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id`
+            WHERE 1" . $sWhereClause;
+
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+
+    public function insertOrderProcessed($aValues)
+    {
+        return $this->query("INSERT INTO `" . $this->_sPrefix . "transactions` SET " . $this->arrayToSQL($aValues) . ", `date`=UNIX_TIMESTAMP()");
+    }
+
+	public function updateOrderProcessed($iId, $aValues)
+    {
+        $sQuery = $this->prepare("UPDATE `" . $this->_sPrefix . "transactions` SET " . $this->arrayToSQL($aValues) . " WHERE `id`=?", $iId);
+        return (int)$this->query($sQuery) > 0;
+    }
+
+    public function cancelOrderProcessed($mixedId)
+    {
+    	if(!is_array($mixedId))
+    		$mixedId = array($mixedId);
+
+        return (int)$this->query("DELETE FROM `" . $this->_sPrefix . "transactions` WHERE `id` IN (" . $this->implode_escape($mixedId) . ")") > 0;
+    }
+
+    public function getOrderHistory($aParams)
+    {
+        return $this->getOrderProcessed($aParams);
+    }
+
+    //--- Order Administration ---//
+    public function onProfileDelete($iId)
+    {
+    	$sQuery = $this->prepare("DELETE FROM `" . $this->_sPrefix . "cart` WHERE `client_id`=?", $iId);
+    	$this->query($sQuery);
+
+    	$sQuery = $this->prepare("DELETE FROM `" . $this->_sPrefix . "user_values` WHERE `user_id`=?", $iId);
+    	$this->query($sQuery);
+    }
+}
+
+/** @} */

+ 192 - 0
modules/boonex/payment/classes/BxPaymentDetails.php

@@ -0,0 +1,192 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+bx_import('BxDolForm');
+
+class BxPaymentDetailsFormCheckerHelper extends BxDolFormCheckerHelper
+{
+	function checkHttps ($s)
+    {
+        return empty($s) || substr(BX_DOL_URL_ROOT, 0, 5) == 'https';
+    }
+}
+
+class BxPaymentDetails extends BxDol
+{
+    protected $MODULE;
+	protected $_oModule;
+
+	protected $_sLangsPrefix;
+    protected $_bCollapseFirst;
+
+    function __construct()
+    {
+    	$this->MODULE = 'bx_payment';
+
+    	parent::__construct();
+
+    	$this->_oModule = BxDolModule::getInstance($this->MODULE);
+
+    	$this->_sLangsPrefix = $this->_oModule->_oConfig->getPrefix('langs');
+        $this->_bCollapseFirst = true;
+    }
+
+    public function serviceGetBlockDetails($iUserId = BX_PAYMENT_EMPTY_ID)
+    {
+        if(!$this->_oModule->isLogged())
+			return MsgBox(_t($this->_sLangsPrefix . 'err_required_login'));
+
+        $iUserId = $iUserId != BX_PAYMENT_EMPTY_ID ? $iUserId : $this->_oModule->getProfileId();
+
+		$sContent = $this->getForm($iUserId);
+		if(empty($sContent))
+			$sContent = MsgBox(_t($this->_sLangsPrefix . 'msg_no_results'));
+
+        return array(
+        	'content' => $sContent,
+        	'menu' => $this->_oModule->_oConfig->getObject('menu_orders_submenu')
+        );
+    }
+
+    public function getForm($iUserId)
+    {
+        $aInputs = $this->_oModule->_oDb->getForm();
+        if(empty($aInputs))
+            return '';
+
+		$aForm = array(
+            'form_attrs' => array(
+                'id' => 'pmt_details',
+                'name' => 'pmt_details',
+                'action' => BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=payment-details'),
+                'method' => 'post',
+                'enctype' => 'multipart/form-data'
+            ),
+            'params' => array(
+                'db' => array(
+                    'table' => '',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'submit'
+                ),
+                'checker_helper' => 'BxPaymentDetailsFormCheckerHelper'
+            ),
+            'inputs' => array (
+            )
+        );
+
+        $bCollapsed = $this->_bCollapseFirst;
+        $iProviderId = 0;
+        $sProviderName = "";
+        $aUserValues = $this->_oModule->_oDb->getFormData($iUserId);
+        foreach($aInputs as $aInput) {
+            $sReturnDataUrl = $this->_oModule->_oConfig->getUrl('return_data') . $sProviderName . '/' . $iUserId;
+
+            if($iProviderId != $aInput['provider_id']) {
+                if(!empty($iProviderId))
+                    $aForm['inputs']['provider_' . $iProviderId . '_end'] = array(
+                        'type' => 'block_end'
+                    );
+
+                $aForm['inputs']['provider_' . $aInput['provider_id'] . '_begin'] = array(
+                    'type' => 'block_header',
+                    'caption' => _t($aInput['provider_caption']),
+                    'collapsable' => true,
+                    'collapsed' => $bCollapsed
+                );
+
+                $iProviderId = $aInput['provider_id'];
+                $sProviderName = $aInput['provider_name'];
+                $bCollapsed = true;
+            }
+
+            $aForm['inputs'][$aInput['name']] = array(
+				'type' => $aInput['type'],
+                'name' => $aInput['name'],
+                'caption' => _t($aInput['caption']),
+                'value' => isset($aUserValues[$aInput['id']]['value']) ? $aUserValues[$aInput['id']]['value'] : '',
+                'info' => _t($aInput['description']),
+            	'attrs' => array(
+            		'bx-data-provider' => $iProviderId
+            	),
+                'checker' => array (
+                    'func' => $aInput['check_type'],
+                    'params' => $aInput['check_params'],
+                    'error' => _t($aInput['check_error']),
+                )
+            );
+
+            //--- Make some field dependent actions ---//
+            switch($aInput['type']) {
+                case 'select':
+                    if(empty($aInput['extra']))
+                       break;
+
+                    $aAddon = array('values' => array());
+
+                    $aPairs = explode(',', $aInput['extra']);
+                    foreach($aPairs as $sPair) {
+                        $aPair = explode('|', $sPair);
+                        $aAddon['values'][] = array('key' => $aPair[0], 'value' => _t($aPair[1]));
+                    }
+                    break;
+
+                case 'checkbox':
+                    $aForm['inputs'][$aInput['name']]['value'] = 'on';
+                    $aAddon = array('checked' => isset($aUserValues[$aInput['id']]['value']) && $aUserValues[$aInput['id']]['value'] == 'on' ? true : false);
+                    break;
+
+				 case 'value':
+				 	if(str_replace($aInput['provider_option_prefix'], '', $aInput['name']) == 'return_url')
+				 		$aForm['inputs'][$aInput['name']]['value'] = $sReturnDataUrl;
+				 	break;
+            }
+
+            if(!empty($aAddon) && is_array($aAddon))
+                $aForm['inputs'][$aInput['name']] = array_merge($aForm['inputs'][$aInput['name']], $aAddon);
+        }
+
+        $aForm['inputs']['provider_' . $iProviderId . '_end'] = array(
+            'type' => 'block_end'
+        );
+        $aForm['inputs']['submit'] = array(
+            'type' => 'submit',
+            'name' => 'submit',
+            'value' => _t($this->_sLangsPrefix . 'form_details_input_do_submit'),
+        );
+
+        bx_import('BxTemplFormView');
+        $oForm = new BxTemplFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $aOptions = $this->_oModule->_oDb->getOptions();
+            foreach($aOptions as $aOption) {
+            	$sValue = bx_get($aOption['name']) !== false ? bx_get($aOption['name']) : '';
+                $this->_oModule->_oDb->updateOption($iUserId, $aOption['id'], bx_process_input($sValue, BX_TAGS_STRIP));
+            }
+
+            header('Location: ' . $oForm->aFormAttrs['action']);
+        }
+        else {
+        	foreach($oForm->aInputs as $aInput)
+        		if(!empty($aInput['error'])) {
+        			$iProviderId = (int)$aInput['attrs']['bx-data-provider'];
+        			$oForm->aInputs['provider_' . $iProviderId . '_begin']['collapsed'] = false;
+        		}
+
+			return $oForm->getCode();
+        }
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/payment/classes/BxPaymentFormView.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Developer Developer
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentFormView extends BxTemplStudioFormView
+{
+    function __construct($aInfo, $oTemplate = false)
+    {
+        parent::__construct($aInfo, $oTemplate);
+    }
+}
+
+/** @} */

+ 35 - 0
modules/boonex/payment/classes/BxPaymentGridHistory.php

@@ -0,0 +1,35 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ * 
+ * @{
+ */
+
+
+class BxPaymentGridHistory extends BxBaseModPaymentGridOrders
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_payment';
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sOrdersType = 'history';
+    }
+
+	protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+		if(empty($this->_aQueryAppend['client_id']))
+			return array();
+
+		$this->_aOptions['source'] .= $this->_oModule->_oDb->prepare(" AND `tt`.`client_id`=?", $this->_aQueryAppend['client_id']);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+}
+
+/** @} */

+ 90 - 0
modules/boonex/payment/classes/BxPaymentGridPending.php

@@ -0,0 +1,90 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ * 
+ * @{
+ */
+
+
+class BxPaymentGridPending extends BxBaseModPaymentGridOrders
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_payment';
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sOrdersType = 'pending';
+    }
+
+	public function performActionProcess()
+    {
+    	$aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $iId = (int)bx_get('id');
+            if(!$iId)
+                return echoJson(array());
+
+            $aIds = array($iId);
+        }
+
+        $iId = (int)$aIds[0];
+
+    	$sAction = 'process';
+        $sFormObject = $this->_oModule->_oConfig->getObject('form_pendings');
+        $sFormDisplay = $this->_oModule->_oConfig->getObject('display_pendings_process');
+
+        $oForm = BxDolForm::getObjectInstance($sFormObject, $sFormDisplay, $this->_oModule->_oTemplate);
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction;
+
+        $oForm->aInputs['id']['value'] = $iId;
+        $oForm->aInputs['seller_id']['value'] = $this->_aQueryAppend['seller_id'];
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+        	$iId = $oForm->getCleanValue('id');
+
+			$this->_oModule->_oDb->updateOrderPending($iId, array(
+				'order' => $oForm->getCleanValue('order'),
+				'error_code' => 0,
+				'error_msg' => 'Manually processed'
+			));
+
+            if($this->_oModule->getObjectCart()->updateInfo($iId))
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
+            else
+                $aRes = array('msg' => _t($this->_sLangsPrefix . 'err_cannot_perform'));
+
+            return echoJson($aRes);
+        }
+
+        $sKey = 'order_' . $this->_sOrdersType . '_process';
+        $sId = $this->_oModule->_oConfig->getHtmlIds($sKey);
+    	$sTitle = _t($this->_sLangsPrefix . 'grid_orders_popup_' . $sKey);
+
+		$sContent = BxTemplStudioFunctions::getInstance()->popupBox($sId, $sTitle, $this->_oModule->_oTemplate->parseHtmlByName('order_pending_process.html', array(
+			'form_id' => $oForm->aFormAttrs['id'],
+			'form' => $oForm->getCode(true),
+			'object' => $this->_sObject,
+			'action' => $sAction
+		)));
+
+		echoJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))));
+    }
+
+	protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+    	if(empty($this->_aQueryAppend['seller_id']))
+    		return array();
+
+		$this->_aOptions['source'] .= $this->_oModule->_oDb->prepare(" AND `tt`.`seller_id`=?", $this->_aQueryAppend['seller_id']);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+}
+
+/** @} */

+ 124 - 0
modules/boonex/payment/classes/BxPaymentGridProcessed.php

@@ -0,0 +1,124 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ * 
+ * @{
+ */
+
+
+class BxPaymentGridProcessed extends BxBaseModPaymentGridOrders
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+    	$this->MODULE = 'bx_payment';
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sOrdersType = 'processed';
+    }
+
+    public function performActionAdd()
+    {
+    	$sAction = 'add';
+    	$sJsObject = $this->_oModule->_oConfig->getJsObject('processed');
+
+        $sFormObject = $this->_oModule->_oConfig->getObject('form_processed');
+        $sFormDisplay = $this->_oModule->_oConfig->getObject('display_processed_add');
+
+        $oForm = BxDolForm::getObjectInstance($sFormObject, $sFormDisplay, $this->_oModule->_oTemplate);
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction;
+
+        $oForm->aInputs['seller_id']['value'] = $this->_aQueryAppend['seller_id'];
+
+        $oForm->aInputs['client_id']['attrs'] = array(
+        	'id' => $this->_oModule->_oConfig->getHtmlIds('processed', 'order_processed_client_id'),
+        );
+        $oForm->aInputs['client']['attrs'] = array(
+        	'id' => $this->_oModule->_oConfig->getHtmlIds('processed', 'order_processed_client'),
+        );
+
+        $oForm->aInputs['module_id']['attrs'] = array(
+			'onchange' => 'javascript:' . $sJsObject . '.paOnSelectModule(this);'
+		);
+        $oForm->aInputs['module_id']['values'] = array(
+            array('key' => '0', 'value' => _t('_Select_one'))
+        );
+
+        $aModules = $this->_oModule->_oDb->getModules();
+        foreach($aModules as $aModule)
+           $oForm->aInputs['module_id']['values'][] = array('key' => $aModule['id'], 'value' => $aModule['title']);
+
+		$oForm->aInputs['items']['content'] = $this->_oModule->_oTemplate->displayItems();
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+        	$sFormMethod = $oForm->aFormAttrs['method'];
+
+        	$aItemIds = $oForm->getSubmittedValue('items', $sFormMethod);
+        	if(empty($aItemIds) || !is_array($aItemIds))
+            	return echoJson(array('msg' => $this->_sLangsPrefix . 'err_empty_items'));
+
+			$aItems = array();
+			foreach($aItemIds as $iItemId) {
+	            $iItemId = (int)$iItemId;
+	            $fItemPrice = (float)$oForm->getSubmittedValue('item-price-' . $iItemId, $sFormMethod);
+	            $iItemQuantity = (int)$oForm->getSubmittedValue('item-quantity-' . $iItemId, $sFormMethod);
+
+	            if($iItemQuantity <= 0)
+	                return echoJson(array('msg' => $this->_sLangsPrefix . 'err_wrong_quantity'));
+
+				$aItems[] = array('id' => $iItemId, 'price' => $fItemPrice, 'quantity' => $iItemQuantity);
+			}
+
+        	$mixedResult = $this->_oModule->getObjectOrders()->addOrder(array(
+        		'provider' => 'manual',
+        		'error_code' => 0,
+        		'error_msg' => 'Manually processed',
+        		'seller_id' => $oForm->getCleanValue('seller_id'),
+        		'client_id' => $oForm->getCleanValue('client_id'),
+        		'order' => $oForm->getCleanValue('order'),
+        		'module_id' => $oForm->getCleanValue('module_id'),
+        		'items' => $aItems
+        	));
+        	if(is_array($mixedResult))
+        		return echoJson($mixedResult);
+
+			$iPendingId = (int)$mixedResult;
+        	if(!$this->_oModule->getObjectCart()->updateInfo($iPendingId))
+        		return echoJson(array('msg' => _t($this->_sLangsPrefix . 'err_cannot_perform')));
+
+			$aOrders = $this->_oModule->_oDb->getOrderProcessed(array('type' => 'pending_id', 'pending_id' => $iPendingId, 'with_key' => 'id'));
+            return echoJson(array('grid' => $this->getCode(false), 'blink' => array_keys($aOrders)));
+        }
+
+        $sKey = 'order_' . $this->_sOrdersType . '_add';
+        $sId = $this->_oModule->_oConfig->getHtmlIds($sKey);
+    	$sTitle = _t($this->_sLangsPrefix . 'grid_orders_popup_' . $sKey);
+
+		$sContent = BxTemplStudioFunctions::getInstance()->popupBox($sId, $sTitle, $this->_oModule->_oTemplate->parseHtmlByName('order_processed_add.html', array(
+			'js_object' => $sJsObject,
+			'form_id' => $oForm->aFormAttrs['id'],
+			'form' => $oForm->getCode(true),
+			'object' => $this->_sObject,
+			'action' => $sAction
+		)));
+
+		echoJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))));
+    }
+
+	protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+    	if(empty($this->_aQueryAppend['seller_id']))
+    		return array();
+
+		$this->_aOptions['source'] .= $this->_oModule->_oDb->prepare(" AND `tt`.`seller_id`=?", $this->_aQueryAppend['seller_id']);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+}
+
+/** @} */

+ 118 - 0
modules/boonex/payment/classes/BxPaymentJoin.php

@@ -0,0 +1,118 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentJoin extends BxBaseModPaymentJoin
+{
+    protected $_sLangsPrefix;
+
+    function __construct()
+    {
+    	$this->MODULE = 'bx_payment';
+
+    	parent::__construct();
+
+        $this->_sLangsPrefix = $this->_oModule->_oConfig->getPrefix('langs');
+    }
+
+    /*
+     * Service methods
+     * 
+     * TODO: Should be tested when paid memberships with "Paid Before Join" or some similar feature will appear.
+     */
+	public function serviceGetBlockJoin()
+    {
+    	$sSessionKeyPending = $this->_oModule->_oConfig->getKey('session_key_pending');
+    	$sRequestKeyPending = $this->_oModule->_oConfig->getKey('request_key_pending');
+
+    	$oSession = BxDolSession::getInstance();
+    	$iPendingId = (int)$oSession->getValue($sSessionKeyPending);
+
+    	if(empty($iPendingId) && bx_get($sRequestKeyPending) !== false) {
+    		$iPendingId = (int)bx_get($sRequestKeyPending);
+
+    		$oSession->setValue($sSessionKeyPending, $iPendingId);
+    	}
+
+		if(empty($iPendingId))
+			return MsgBox(_t($this->_sLangsPrefix . 'err_not_allowed'));
+
+		$aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+	    if(empty($aPending['order']) || (int)$aPending['error_code'] != 1)
+			return MsgBox(_t($this->_sLangsPrefix . 'err_not_processed'));
+
+		if((int)$aPending['processed'] == 1)
+			return MsgBox(_t($this->_sLangsPrefix . 'err_already_processed'));
+
+		//--- 'System' -> 'Join after Payment' for Alerts Engine ---//
+		$bOverride = false;
+		$sOverrideError = '';
+
+		bx_import('BxDolAlerts');
+        $oAlert = new BxDolAlerts('system', 'join_after_payment', 0, 0, array('override' => &$bOverride, 'override_error' => &$sOverrideError));
+        $oAlert->alert();
+
+        if($bOverride)
+        	return;
+		//--- 'System' -> 'Join after Payment' for Alerts Engine ---//
+
+    	$sContent = BxDolService::call('system', 'create_account_form', array(array('action' => $this->_oModule->_oConfig->getUrl('join'))), 'TemplServiceAccount');
+    	if(!empty($sOverrideError))
+    		$sContent = MsgBox(_t($sOverrideError)) . $sContent;
+
+		return array(
+        	'content' => $sContent
+		);
+    }
+
+	public function performJoin($iPendingId, $aPayment = array())
+    {
+		BxDolSession::getInstance()->setValue($this->_oModule->_oConfig->getKey('session_key_pending'), (int)$iPendingId);
+
+		if(!empty($aPayment['payer_name']) && !empty($aPayment['payer_email'])) {
+			bx_import('BxDolEmailTemplates');
+			$oEmailTemplates = new BxDolEmailTemplates();
+
+			$aTemplate = $oEmailTemplates->parseTemplate($this->_oModule->_oConfig->getPrefix('general') . 'paid_need_join', array(
+				'RealName' => $aPayment['payer_name'],
+				'JoinLink' => $this->_oModule->_oConfig->getUrl('join', array($this->_oModule->_oConfig->getKey('request_key_pending') => (int)$iPendingId))
+			));
+
+			sendMail($aPayment['payer_email'], $aTemplate['Subject'], $aTemplate['Body'], 0, array(), 'html', false, true);
+		}
+
+		header('Location: ' . $this->_oModule->_oConfig->getUrl('join'));
+		exit;
+	}
+
+	public function onProfileJoin($iProfileId)
+    {
+    	$sSessionKeyPending = $this->_oModule->_oConfig->getKey('session_key_pending');
+
+    	$oSession = BxDolSession::getInstance();
+		$iPendingId = (int)$oSession->getValue($sSessionKeyPending);
+
+        if(empty($iProfileId) || empty($iPendingId))
+        	return;
+ 
+		$aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+		if(empty($aPending) || (isset($aPending['client_id']) && (int)$aPending['client_id'] != 0))
+			return;
+
+		if(!$this->_oModule->_oDb->updateOrderPending($iPendingId, array('client_id' => $iProfileId)))
+			return;
+
+		$this->_oModule->getObjectCart()->updateInfo($iPendingId);
+
+		$oSession->unsetValue($sSessionKeyPending);
+    }
+}
+
+/** @} */

+ 364 - 0
modules/boonex/payment/classes/BxPaymentModule.php

@@ -0,0 +1,364 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+define('BX_PAYMENT_ORDERS_TYPE_PENDING', 'pending');
+define('BX_PAYMENT_ORDERS_TYPE_PROCESSED', 'processed');
+define('BX_PAYMENT_ORDERS_TYPE_SUBSCRIPTION', 'subscription');
+define('BX_PAYMENT_ORDERS_TYPE_HISTORY', 'history');
+
+define('BX_PAYMENT_EMPTY_ID', 0);
+
+define('BX_PAYMENT_RESULT_SUCCESS', 0);
+
+/**
+ * Payment module by BoonEx
+ *
+ * This module is needed to work with payment providers and organize the process
+ * of some item purchasing. Shopping Cart and Orders Manager are included.
+ *
+ * Integration notes:
+ * To integrate your module with this one, you need:
+ * 1. Get 'Add To Cart' button using serviceGetAddToCartLink service.
+ * 2. Add info about your module in the 'bx_pmt_modules' table.
+ * 3. Realize the following service methods in your Module class.
+ *   a. serviceGetItems($iVendorId) - Is used in Orders Administration to get all products of the requested seller(vendor).
+ *   b. serviceGetCartItem($iClientId, $iItemId) - Is used in Shopping Cart to get one product by specified id.
+ *   c. serviceRegisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrderId) - Register purchased product.
+ *   d. serviceUnregisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrderId) - Unregister the product purchased earlier.
+ * @see You may see an example of integration in Membership module.
+ *
+ *
+ * Profile's Wall:
+ * no spy events
+ *
+ *
+ *
+ * Spy:
+ * no spy events
+ *
+ *
+ *
+ * Memberships/ACL:
+ * Doesn't depend on user's membership.
+ *
+ *
+ *
+ * Service methods:
+ *
+ * Is used to get "Add to cart" link for some item(s) in your module.
+ * @see BxPmtModule::serviceGetAddToCartLink
+ * BxDolService::call('payment', 'get_add_to_cart_link', array($iVendorId, $mixedModuleId, $iItemId, $iItemCount));
+ *
+ * Check transaction(s) in database which satisty all conditions.
+ * @see BxPmtModule::serviceGetTransactionsInfo
+ * BxDolService::call('payment', 'get_transactions_info', array($aConditions));
+ *
+ * Get total count of items in Shopping Cart.
+ * @see BxPmtModule::serviceGetCartItemsCount
+ * BxDolService::call('payment', 'get_cart_items_count', array($iUserId, $iOldCount));
+ * @note is needed for internal usage(integration with member tool bar).
+ *
+ * Get Shopping cart content.
+ * @see BxPmtModule::serviceGetCartItems
+ * BxDolService::call('payment', 'get_cart_items');
+ * @note is needed for internal usage(integration with member tool bar).
+ *
+ *
+ * Alerts:
+ * no alerts available
+ *
+ */
+class BxPaymentModule extends BxBaseModPaymentModule
+{
+    protected $_iUserId;
+
+    protected $_aOrderTypes;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_iUserId = $this->getProfileId();
+
+        $this->_aOrderTypes = array(
+        	BX_PAYMENT_ORDERS_TYPE_PENDING, 
+        	BX_PAYMENT_ORDERS_TYPE_PROCESSED, 
+        	BX_PAYMENT_ORDERS_TYPE_SUBSCRIPTION, 
+        	BX_PAYMENT_ORDERS_TYPE_HISTORY
+        );
+    }
+
+    /**
+     * Manage Orders Methods
+     */
+	public function actionGetClients()
+    {
+        $sTerm = bx_get('term');
+
+        $aResult = BxDolService::call('system', 'profiles_search', array($sTerm), 'TemplServiceProfiles');
+
+        echoJson($aResult);
+    }
+
+    public function actionGetItems($iModuleId)
+    {
+    	$iSellerId = $this->getProfileId();
+        $aItems = $this->callGetCartItems((int)$iModuleId, array($iSellerId));
+
+        $aResult = array('code' => 0);
+        if(is_array($aItems) && !empty($aItems))
+            $aResult['data'] = $this->_oTemplate->displayItems($aItems);
+
+		echoJson($aResult);
+    }
+
+
+    /**
+     * Payment Details Methods
+     */
+    public function serviceGetOptionsSiteAdmin()
+    {
+        $aResult = array(
+            array('key' => '', 'value' => _t('_Select_one'))
+        );
+
+        $aIds = $this->_oDb->getAdminsIds();
+        foreach($aIds as $iId) {
+        	$aUser = $this->getProfileInfo($iId);
+
+            $aResult[] = array(
+                'key' => $iId,
+                'value' => $aUser['name']
+            );
+        }
+
+        return $aResult;
+    }
+
+    /**
+     * Check transaction(s) in database which satisty all conditions.
+     *
+     * @param array $aConditions an array of pears('key' => 'value'). Available keys are the following:
+     * a. order_id - internal order ID (string)
+     * b. client_id - client's ID (integer)
+     * c. seller_id - seller's ID (integer)
+     * d. module_id - modules's where the purchased product is located. (integer)
+     * e. item_id - item id in the database. (integer)
+     * f. date - the date when the payment was processed(UNIXTIME STAMP)
+     *
+     * @return array of transactions. Each transaction has full info(client ID, seller ID, external transaction ID, date and so on)
+     */
+    public function serviceGetTransactionsInfo($aConditions)
+    {
+        return $this->_oDb->getOrderProcessed(array('type' => 'mixed', 'conditions' => $aConditions));
+    }
+
+
+    /**
+     * Cart Processing Methods
+     */
+    public function actionCartSubmit()
+    {
+    	if(!$this->isLogged())
+            return $this->_oTemplate->displayPageCodeError($this->_sLangsPrefix . 'err_required_login');
+
+        if(bx_get('bx-payment-delete') !== false && bx_get('items') !== false){
+        	$aItems = bx_get('items');
+            foreach($aItems as $sItem) {
+                list($iVendorId, $iModuleId, $iItemId, $iItemCount) = $this->_oConfig->descriptorS2A($sItem);
+                $this->getObjectCart()->serviceDeleteFromCart($iVendorId, $iModuleId, $iItemId);
+            }
+        }
+		else if(bx_get('bx-payment-checkout') !== false && bx_get('items') !== false) {
+			$iVendorId = (int)bx_get('vendor_id');
+			$sProvider = bx_process_input(bx_get('provider'));
+			$aItems = bx_get('items');
+
+			$sError = $this->serviceInitializeCheckout($iVendorId, $sProvider, $aItems);
+			if(!empty($sError))
+	    		return $this->_oTemplate->displayPageCodeError($sError, false);
+		}
+
+        header('Location: ' . $this->_oConfig->getUrl('cart'));
+        exit;
+    }
+
+    public function actionAddToCart($iVendorId, $iModuleId, $iItemId, $iItemCount)
+    {
+        $aResult = $this->getObjectCart()->serviceAddToCart($iVendorId, $iModuleId, $iItemId, $iItemCount);
+		echoJson($aResult);
+    }
+
+    /**
+     * Isn't used yet.
+     */
+    public function actionDeleteFromCart($iVendorId, $iModuleId, $iItemId)
+    {
+        $aResult = $this->getObjectCart()->serviceDeleteFromCart($iVendorId, $iModuleId, $iItemId);
+        echoJson($aResult);
+    }
+
+    /**
+     * Isn't used yet.
+     */
+    public function actionEmptyCart($iVendorId)
+    {
+        $aResult = $this->getObjectCart()->serviceDeleteFromCart($this->_iUserId, $iVendorId);
+		echoJson($aResult);
+    }
+
+
+    /**
+     * Payment Processing Methods
+     */
+	public function serviceInitializeCheckout($iVendorId, $sProvider, $aItems = array())
+	{
+		if(!is_array($aItems))
+			$aItems = array($aItems);
+
+		$iVendorId = (int)$iVendorId;
+        if($iVendorId == BX_PAYMENT_EMPTY_ID)
+            return MsgBox(_t($this->_sLangsPrefix . 'err_unknown_vendor'));
+
+		$oProvider = $this->getObjectProvider($sProvider, $iVendorId);
+        if($oProvider === false)
+        	return MsgBox(_t($this->_sLangsPrefix . 'err_incorrect_provider'));
+
+        $aInfo = $this->getObjectCart()->getInfo($this->_iUserId, $iVendorId, $aItems);
+        if(empty($aInfo) || $aInfo['vendor_id'] == BX_PAYMENT_EMPTY_ID || empty($aInfo['items']))
+            return MsgBox(_t($this->_sLangsPrefix . 'err_empty_order'));
+
+		/*
+		 * Process FREE (price = 0) items for LOGGED IN members
+		 * WITHOUT processing via payment provider.
+		 */
+		$bProcessedFree = false;
+		foreach($aInfo['items'] as $iIndex => $aItem)
+			if((int)$aInfo['client_id'] != 0 && (float)$aItem['price'] == 0) {
+				$aItemInfo = $this->callRegisterCartItem((int)$aItem['module_id'], array($aInfo['client_id'], $aInfo['vendor_id'], $aItem['id'], $aItem['quantity'], $this->_oConfig->getLicense()));
+	            if(is_array($aItemInfo) && !empty($aItemInfo))
+	            	$bProcessedFree = true;
+
+	            $aInfo['items_count'] -= 1;
+	            unset($aInfo['items'][$iIndex]);
+
+	            $sCartItems = $this->_oDb->getCartItems($aInfo['client_id']);
+	            $sCartItems = trim(preg_replace("'" . $this->_oConfig->descriptorA2S(array($aInfo['vendor_id'], $aItem['module_id'], $aItem['id'], $aItem['quantity'])) . ":?'", "", $sCartItems), ":");
+	            $this->_oDb->setCartItems($aInfo['client_id'], $sCartItems);
+			}
+
+		if(empty($aInfo['items']))
+            return MsgBox(_t($this->_sLangsPrefix . ($bProcessedFree ? 'msg_successfully_processed_free' : 'err_empty_order')));
+
+        $iPendingId = $this->_oDb->insertOrderPending($this->_iUserId, $sProvider, $aInfo);
+        if(empty($iPendingId))
+            return MsgBox(_t($this->_sLangsPrefix . 'err_access_db'));
+
+		/*
+		 * Perform Join WITHOUT processing via payment provider
+		 * if a client ISN'T logged in and has only ONE FREE item in the card.
+		 */
+		if((int)$aInfo['client_id'] == 0 && (int)$aInfo['items_count'] == 1) {
+			reset($aInfo['items']);
+			$aItem = current($aInfo['items']);
+
+			if(!empty($aItem) && $aItem['price'] == 0) {
+				$this->_oDb->updateOrderPending($iPendingId, array(
+		            'order' => $this->_oConfig->getLicense(),
+		            'error_code' => '1',
+		            'error_msg' => ''
+		        ));
+
+				$this->getObjectJoin()->performJoin($iPendingId);
+			}
+		}
+
+		$sError = $oProvider->initializeCheckout($iPendingId, $aInfo);
+		if(!empty($sError))
+			return MsgBox($sError);
+
+        return true;
+	}
+
+    public function actionFinalizeCheckout($sProvider, $mixedVendorId = "")
+    {
+        $aData = &$_REQUEST;
+
+        $oProvider = $this->getObjectProvider($sProvider, $mixedVendorId);
+        if($oProvider === false)
+        	return $this->_oTemplate->displayPageCodeError($this->_sLangsPrefix . 'err_incorrect_provider');
+
+        $aResult = $oProvider->finalizeCheckout($aData);
+        if((int)$aResult['code'] != BX_PAYMENT_RESULT_SUCCESS) 
+        	return $this->_oTemplate->displayPageCodeError($aResult['message']);
+
+		$aPending = $this->_oDb->getOrderPending(array('type' => 'id', 'id' => (int)$aResult['pending_id']));
+
+		//--- Check "Pay Before Join" situation
+		if((int)$aPending['client_id'] == 0)
+			$this->getObjectJoin()->performJoin((int)$aPending['id'], $aResult);
+
+		//--- Register payment for purchased items in associated modules 
+		$this->getObjectCart()->updateInfo($aPending);
+
+		if($oProvider->needRedirect()) {
+			header('Location: ' . $this->_oConfig->getUrl('return'));
+			exit;
+		}
+
+		return $this->_oTemplate->displayPageCodeResponse($aResult['message']);
+    }
+
+    public function actionCheckoutFinished($sProvider, $mixedVendorId = "")
+    {
+        $oProvider = $this->getObjectProvider($sProvider, $mixedVendorId);
+        if($oProvider === false)
+        	return $this->_oTemplate->displayPageCodeError($this->_sLangsPrefix . 'err_incorrect_provider');
+
+        $aResult = $oProvider->checkoutFinished();
+
+        $this->_oTemplate->displayPageCodeResponse($aResult['message']);
+        exit;
+    }
+
+
+    public function onProfileJoin($iProfileId)
+    {
+    	$this->getObjectJoin()->onProfileJoin($iProfileId);
+    }
+
+    public function onProfileDelete($iProfileId)
+    {
+		$this->_oDb->onProfileDelete($iProfileId);
+    }
+
+    public function isAllowedSell($aItem, $bPerform = false)
+    {
+		$iUserId = (int)$this->getProfileId();
+        if(!$iUserId)
+        	return false;
+
+		$aItemInfo = $this->callGetCartItem($aItem['module_id'], array($aItem['item_id']));
+		if(empty($aItemInfo))
+			return false;
+
+        if(isAdmin())
+            return true;
+
+        $aCheckResult = checkActionModule($iUserId, 'sell', $this->getName(), $bPerform);
+        if((int)$aItemInfo['author_id'] == $iUserId && $aCheckResult[CHECK_ACTION_RESULT] == CHECK_ACTION_RESULT_ALLOWED)
+			return true;
+
+        return $aCheckResult[CHECK_ACTION_MESSAGE];
+    }
+}
+
+/** @} */

+ 101 - 0
modules/boonex/payment/classes/BxPaymentOrders.php

@@ -0,0 +1,101 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentOrders extends BxBaseModPaymentOrders
+{
+    protected $_sLangsPrefix;
+
+    function __construct()
+    {
+    	$this->MODULE = 'bx_payment';
+
+    	parent::__construct();
+
+        $this->_sLangsPrefix = $this->_oModule->_oConfig->getPrefix('langs');
+    }
+
+	/*
+     * Service methods
+     */
+	public function serviceGetBlockOrders($sType = '', $iUserId = BX_PAYMENT_EMPTY_ID)
+    {
+    	if(empty($sType))
+    		$sType = bx_get('type') !== false ? bx_process_input(bx_get('type')) : BX_PAYMENT_ORDERS_TYPE_PROCESSED;
+
+    	if(!$this->_oModule->isLogged())
+            return MsgBox(_t($this->_sLangsPrefix . 'err_required_login'));
+
+        $iUserId = $iUserId != BX_PAYMENT_EMPTY_ID ? $iUserId : $this->_oModule->getProfileId();
+        return array(
+        	'title' => _t($this->_sLangsPrefix . 'page_block_title_orders_' . $sType),
+        	'content' => $this->_oModule->_oTemplate->displayBlockOrders($sType, $iUserId),
+        	'menu' => $this->_oModule->_oConfig->getObject('menu_orders_submenu')
+        );
+    }
+
+	public function addOrder($aData)
+    {
+        $iSellerId = isset($aData['seller_id']) ? (int)$aData['seller_id'] : $this->_oModule->getProfileId();
+        if($iSellerId == $aData['client_id'])
+            return array('msg' => $this->_sLangsPrefix . 'err_purchase_from_yourself');
+
+        $sOrder = trim($aData['order']);
+        if(empty($sOrder))
+            return array('msg' => $this->_sLangsPrefix . 'form_processed_input_order_err');
+
+        $aCartInfo = array('vendor_id' => $iSellerId, 'items_price' => 0, 'items' => array());
+        foreach($aData['items'] as $aItem) {
+        	if(!$this->_oModule->isAllowedSell(array('module_id' => $aData['module_id'], 'item_id' => $aItem['id']), true))
+        		continue;
+
+            $aCartInfo['items_price'] += $aItem['price'] * $aItem['quantity'];
+            $aCartInfo['items'][] = array(
+                'vendor_id' => $iSellerId,
+                'module_id' => $aData['module_id'],
+                'id' => $aItem['id'],
+                'quantity' => $aItem['quantity']
+            );
+        }
+
+        $iPendingId = $this->_oModule->_oDb->insertOrderPending($aData['client_id'], $aData['provider'], $aCartInfo);
+        $this->_oModule->_oDb->updateOrderPending($iPendingId, array(
+            'order' => $sOrder,
+            'error_code' => $aData['error_code'],
+            'error_msg' => $aData['error_msg']
+        ));
+
+        return (int)$iPendingId;
+    }
+
+    public function getOrder($sType, $iId)
+    {
+        return $this->_oModule->_oTemplate->displayOrder($sType, $iId);
+    }
+
+    public function cancel($sType, $iOrderId)
+    {
+    	$sMethodName = "getOrder" . bx_gen_method_name($sType);
+    	$aOrder = $this->_oModule->_oDb->$sMethodName(array('type' => 'id', 'id' => $iOrderId));
+		if(empty($aOrder) || !is_array($aOrder))
+	    	return true;
+
+		if($sType == BX_PAYMENT_ORDERS_TYPE_PROCESSED && !BxDolService::call((int)$aOrder['module_id'], 'unregister_cart_item', array($aOrder['client_id'], $aOrder['seller_id'], $aOrder['item_id'], $aOrder['item_count'], $aOrder['order_id'])))
+			return false;
+
+		$sMethodName = "cancelOrder" . bx_gen_method_name($sType);
+		if(!$this->_oModule->_oDb->$sMethodName($iOrderId))
+			return false;
+
+        return true;
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/payment/classes/BxPaymentPageCart.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentPageCart extends BxTemplPage
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/payment/classes/BxPaymentPageDetails.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentPageDetails extends BxTemplPage
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/payment/classes/BxPaymentPageHistory.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentPageHistory extends BxTemplPage
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/payment/classes/BxPaymentPageJoin.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentPageJoin extends BxTemplPage
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/payment/classes/BxPaymentPageOrders.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentPageOrders extends BxTemplPage
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 143 - 0
modules/boonex/payment/classes/BxPaymentProvider2Checkout.php

@@ -0,0 +1,143 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+define('TUCO_MODE_LIVE', 1);
+define('TUCO_MODE_TEST', 2);
+
+define('TUCO_PAYMENT_METHOD_CC', 'CC');
+define('TUCO_PAYMENT_METHOD_CK', 'CK');
+define('TUCO_PAYMENT_METHOD_AL', 'AL');
+define('TUCO_PAYMENT_METHOD_PPI', 'PPI');
+
+class BxPaymentProvider2Checkout extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
+{
+    protected $_sDataReturnUrl;
+
+    function __construct($aConfig)
+    {
+		$this->MODULE = 'bx_payment';
+
+        parent::__construct($aConfig);
+
+        $this->_bRedirectOnResult = true;
+        $this->_sDataReturnUrl = $this->_oModule->_oConfig->getUrl('return_data') . $this->_sName . '/';
+    }
+
+    public function initializeCheckout($iPendingId, $aCartInfo, $bRecurring = false, $iRecurringDays = 0)
+    {
+    	$bTest = (int)$this->getOption('mode') == TUCO_MODE_TEST;
+
+        $aFormData = array(
+            'sid' => $this->getOption('account_id'),
+        	'mode' => '2CO',
+        	'demo' => $bTest ? 'Y' : '',
+        	'merchant_order_id' => $iPendingId,
+            'total' => sprintf("%.2f", (float)$aCartInfo['items_price']),
+        	'currency_code' => $aCartInfo['vendor_currency_code'],
+            'pay_method' => $this->getOption('payment_method'),
+            'x_receipt_link_url' => $this->_sDataReturnUrl . $aCartInfo['vendor_id']
+        );
+
+        $iIndex = 0;
+        foreach($aCartInfo['items'] as $aItem) {
+        	$aFormData['li_' . $iIndex . '_type'] = 'product';
+            $aFormData['li_' . $iIndex . '_name'] = $aItem['title'];
+            $aFormData['li_' . $iIndex . '_price'] = $aItem['price'];
+            $aFormData['li_' . $iIndex . '_quantity'] = $aItem['quantity'];
+            $aFormData['li_' . $iIndex . '_tangible'] = 'N';
+
+            $iIndex++;
+        }
+
+        $sActionURL = 'https://' . ($bTest ? 'sandbox' : 'www') . '.2checkout.com/checkout/purchase';
+        $this->_oModule->_oTemplate->displayPageCodeRedirect($sActionURL, $aFormData);
+        exit;
+    }
+
+    public function finalizeCheckout(&$aData)
+    {
+        return $this->_registerCheckout($aData);
+    }
+
+	public function checkoutFinished() {}
+
+    /**
+     *
+     * @param $aData - data from payment provider.
+     * @param $bSubscription - Is not needed. May be used in the future for subscriptions.
+     * @param $iPendingId - Is not needed. May be used in the future for subscriptions.
+     * @return array with results.
+     */
+    protected function _registerCheckout(&$aData, $bSubscription = false, $iPendingId = 0)
+    {
+        if(empty($this->_aOptions) && isset($aData['merchant_order_id']))
+            $this->_aOptions = $this->getOptionsByPending($aData['merchant_order_id']);
+
+        if(empty($this->_aOptions))
+            return array('code' => 2, 'message' => _t('_payment_2co_err_no_vendor_given'));
+
+        $aResult = $this->_validateCheckout($aData);
+
+        if(empty($aResult['pending_id']))
+            return $aResult;
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => (int)$aResult['pending_id']));
+        if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
+            return array('code' => 6, 'message' => _t('_payment_2co_err_already_processed'));
+
+        $this->_oModule->_oDb->updateOrderPending((int)$aResult['pending_id'], array(
+            'order' => $aData['order_number'],
+            'error_code' => $aResult['code'],
+            'error_msg' => $aResult['message']
+        ));
+        return $aResult;
+    }
+
+    protected function _validateCheckout(&$aData)
+    {
+        if(empty($aData['order_number']) || empty($aData['total']) || empty($aData['key']) || empty($aData['merchant_order_id']))
+            return array('code' => 3, 'message' => _t('_payment_2co_err_no_data_given'));
+
+        $sOrder = bx_process_input($aData['order_number'], BX_TAGS_STRIP);
+        $sAmount = bx_process_input($aData['total'], BX_TAGS_STRIP);
+        $iPendingId = (int)$aData['merchant_order_id'];
+
+        if($aData['key'] != $this->_generateKey($sOrder, $sAmount))
+            return array('code' => 4, 'message' => _t('_payment_2co_err_wrong_key'), 'pending_id' => $iPendingId);
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+        if((float)$sAmount != (float)$aPending['amount'])
+            return array('code' => 5, 'message' => _t('_payment_2co_err_wrong_payment'), 'pending_id' => $iPendingId);
+
+		$sBuyerFirstName = bx_process_input($aData['first_name'], BX_TAGS_STRIP);
+        $sBuyerLastName = bx_process_input($aData['last_name'], BX_TAGS_STRIP);
+        $sBuyerEmail = bx_process_input($aData['email'], BX_TAGS_STRIP);
+
+        return array(
+        	'code' => 1, 
+        	'message' => _t('_payment_2co_msg_verified'), 
+        	'pending_id' => $iPendingId,
+        	'payer_name' => _t('_payment_txt_buyer_name_mask', $sBuyerFirstName, $sBuyerLastName),
+        	'payer_email' => $sBuyerEmail
+        );
+    }
+
+    protected function _generateKey($sOrder, $sAmount)
+    {
+    	if((int)$this->getOption('mode') == TUCO_MODE_TEST)
+    		$sOrder = '1';
+
+        $sKey = $this->getOption('secret_word') . $this->getOption('account_id') . $sOrder . $sAmount;
+        return strtoupper(md5($sKey));
+    }
+}
+
+/** @} */

+ 585 - 0
modules/boonex/payment/classes/BxPaymentProviderBitPay.php

@@ -0,0 +1,585 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+define('BP_STATUS_NEW', 'new');
+define('BP_STATUS_PAID', 'paid');
+define('BP_STATUS_CONFIRMED', 'confirmed');
+define('BP_STATUS_COMPLETE', 'complete');
+define('BP_STATUS_EXPIRED', 'expired');
+define('BP_STATUS_INVALID', 'invalid');
+
+define('BP_SPEED_HIGH', 'high');
+define('BP_SPEED_MEDIUM', 'medium');
+define('BP_SPEED_LOW', 'low');
+
+class BxPaymentProviderBitPay extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
+{
+    function __construct($aConfig)
+    {
+    	$this->MODULE = 'bx_payment';
+
+        parent::__construct($aConfig);
+
+        $this->_bRedirectOnResult = false;
+
+        $this->_initializeOptions();
+    }
+
+    public function initializeCheckout($iPendingId, $aCartInfo, $bRecurring = false, $iRecurringDays = 0)
+    {
+    	$this->aBpOptions['redirectURL'] .= $aCartInfo['vendor_id'];
+
+    	$this->aBpOptions['notificationURL'] .= $aCartInfo['vendor_id'];
+    	$this->aBpOptions['notificationURL'] = bx_append_url_params($this->aBpOptions['notificationURL'], array('bxssl' => 1));
+
+	    switch ($aCartInfo['vendor_currency_code']) {
+			case 'USD':
+			case 'EUR':
+			case 'BTC':
+				$this->aBpOptions['currency'] = $aCartInfo['vendor_currency_code'];
+		}
+
+		$aPosData = array(
+			'vnd' => (string)$aCartInfo['vendor_id'], 
+			'clt' => (string)$aCartInfo['client_id'], 
+			'pnd' => (string)$iPendingId
+		);
+		$aOptions = array(
+			'itemDesc' => 'Payment to ' . $aCartInfo['vendor_profile_name']
+		);
+		$aResponse = $this->createInvoice($iPendingId, (float)$aCartInfo['items_price'], $aPosData, $aOptions);
+		if(!empty($aResponse['error']))
+			return _t(is_array($aResponse['error']) ? $aResponse['error']['message'] : $aResponse['error']);
+
+		header('Location: ' . $aResponse['url']);
+		exit;
+    }
+
+    public function finalizeCheckout(&$aData)
+    {
+    	$aData = $this->_verifyNotification();
+    	if($aData === false)
+    		return array('code' => 2, 'message' => _t('_payment_bp_err_no_data_given'));
+
+		if(empty($this->_aOptions) && isset($aData['posData']['d']['pnd'])) {
+            $this->_aOptions = $this->getOptionsByPending($aData['posData']['d']['pnd']);
+			if(empty($this->_aOptions))
+            	return array('code' => 3, 'message' => _t('_payment_bp_err_no_vendor_given'));
+
+            $this->_initializeOptions();
+		}
+
+		$aPosData = $this->_verifyPosData($aData['posData']);
+		if($aPosData === false)
+			return array('code' => 4, 'message' => _t('_payment_bp_err_incorrect_data'));
+
+		//--- Update pending transaction ---//
+		$sStatus = $aData['status'];
+		$sMessage = '';
+
+		$sResult = $this->_verifyAmount($aPosData, $aData['price']);
+		if($sResult === false) {
+			$sStatus = BP_STATUS_INVALID;
+			$sMessage = _t('_payment_bp_err_wrong_amount');
+		}
+
+		$iPendingId = (int)$aPosData['pnd'];
+		$sOrderId = Bx_process_input($aData['id']);
+        $this->_oModule->_oDb->updateOrderPending($iPendingId, array(
+            'order' => $sOrderId,
+            'error_code' => $sStatus,
+            'error_msg' => $sMessage
+        ));
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+        if((int)$aPending['processed'] != 0)
+            return array('code' => 6, 'message' => _t('_payment_bp_err_already_processed'));
+
+        //--- Process purchased items in the database if STATUS became CONFIRMED (HIGH and MEDIUM speed), COMPLETE (LOW speed)
+		$sSpeed = $this->aBpOptions['transactionSpeed'];
+		if((in_array($sSpeed, array(BP_SPEED_HIGH, BP_SPEED_MEDIUM)) && $sStatus == BP_STATUS_CONFIRMED) || ($sSpeed == BP_SPEED_LOW && $sStatus == BP_STATUS_COMPLETE))		
+	        return array('code' => 1, 'message' => '', 'pending_id' => $iPendingId);
+
+		return array('code' => 7, 'message' => _t('_payment_bp_err_no_confirmation_given'));
+    }
+
+    public function checkoutFinished()
+    {
+    	return array(
+    		'message' => _t('_payment_bp_msg_checkout_finished')
+    	);
+    }
+
+	/**
+	 *
+	 * Creates BitPay invoice via Bitpay::curl.
+	 *
+	 * @param string $orderId, string $price, string $posData, array $options
+	 * @return array $response
+	 * @throws Exception $e
+	 *
+	 */
+	public function createInvoice($orderId, $price, $posData, $options = array()) {
+		// $orderId: Used to display an orderID to the buyer. In the account summary view, this value is used to
+		// identify a ledger entry if present. Maximum length is 100 characters.
+		//
+		// $price: by default, $price is expressed in the currency you set in Bitpay::aOptions['currency'].
+		//
+		// $posData: this field is included in status updates or requests to get an invoice.  It is intended to be used by
+		// the merchant to uniquely identify an order associated with an invoice in their system.  Aside from that, Bit-Pay does
+		// not use the data in this field.  The data in this field can be anything that is meaningful to the merchant.
+		// Maximum length is 100 characters.
+		//
+		// Note:  Using the posData hash option will APPEND the hash to the posData field and could push you over the 100
+		//        character limit.
+		//
+		// $options keys can include any of:
+		//	'itemDesc', 'itemCode', 'notificationEmail', 'notificationURL', 'redirectURL', 'apiKey'
+		//	'currency', 'physical', 'fullNotifications', 'transactionSpeed', 'buyerName',
+		//	'buyerAddress1', 'buyerAddress2', 'buyerCity', 'buyerState', 'buyerZip', 'buyerEmail', 'buyerPhone'
+		//
+		// If a given option is not provided here, the value of that option will default to what is found in Bitpay::aOptions
+
+		try {
+			$options = array_merge($this->aBpOptions, $options);  // $options override any options found in Bitpay::aOptions
+			$pos = array('d' => $posData);
+
+			if ($this->aBpOptions['verifyPos']) 
+				$pos['h'] = $this->_hash(serialize($posData), $options['apiKey']);
+
+			$options['posData'] = json_encode($pos);
+			if(strlen($options['posData']) > 100)
+				return array('error' => '_payment_bp_err_posdata_exceed_limit');
+
+			$options['orderID'] = $orderId;
+			$options['price'] = $price;
+
+			$postOptions = array('orderID', 'itemDesc', 'itemCode', 'notificationEmail', 'notificationURL', 'redirectURL', 
+	        	'posData', 'price', 'currency', 'physical', 'fullNotifications', 'transactionSpeed', 'buyerName', 
+	            'buyerAddress1', 'buyerAddress2', 'buyerCity', 'buyerState', 'buyerZip', 'buyerEmail', 'buyerPhone');
+
+			foreach($postOptions as $o) {
+				if (array_key_exists($o, $options))
+					$post[$o] = $options[$o];
+			}
+			$post = json_encode($post);
+
+			$response = $this->_curl('https://bitpay.com/api/invoice/', $options['apiKey'], $post);
+
+			if($this->aBpOptions['useLogging']) {
+				$this->_log('Create Invoice: ');
+				$this->_log('-- Data: ' . $post);
+				$this->_log('Response: ');
+				$this->_log($response);
+			}
+
+			return $response;
+
+		}
+		catch (Exception $e) {
+			if($this->aBpOptions['useLogging'])
+				$this->_log('Error: ' . $e->getMessage());
+
+			return array('error' => $e->getMessage());
+		}
+	}
+
+	/**
+	 *
+	 * Retrieves an invoice from BitPay.  $options can include 'apiKey'
+	 *
+	 * @param string $invoiceId, boolean $apiKey
+	 * @return mixed $json
+	 * @throws Exception $e
+	 *
+	 */
+	public function getInvoice($invoiceId, $apiKey=false) {
+		try {
+			if (!$apiKey)
+				$apiKey = $this->aBpOptions['apiKey'];
+
+			$response = $this->_curl('https://bitpay.com/api/invoice/'.$invoiceId, $apiKey);
+
+			if (is_string($response))
+				return $response; // error
+
+			$response['posData'] = json_decode($response['posData'], true);
+			$response['posData'] = $response['posData']['d'];
+
+			return $response;
+		}
+		catch (Exception $e) {
+			if($this->aBpOptions['useLogging'])
+				$this->_log('Error: ' . $e->getMessage());
+
+			return 'Error: ' . $e->getMessage();
+		}
+	}
+
+	/**
+	 *
+	 * Retrieves a list of all supported currencies
+	 * and returns associative array.
+	 * 
+	 * @param none
+	 * @return array $currencies
+	 * @throws Exception $e
+	 * 
+	 */
+	function getCurrencyList() {
+		$currencies = array();
+		$rate_url = 'https://bitpay.com/api/rates';
+
+		try {
+			$clist = json_decode(file_get_contents($rate_url),true);
+
+			foreach($clist as $key => $value)
+				$currencies[$value['code']] = $value['name'];
+
+			return $currencies;
+		}
+		catch (Exception $e) {
+			if($this->aBpOptions['useLogging'])
+				$this->_log('Error: ' . $e->getMessage());
+
+			return 'Error: ' . $e->getMessage();
+		}
+	}
+
+	/**
+	 * 
+	 * Retrieves the current rate based on $code.
+	 * The default code us USD, so calling the 
+	 * function without a parameter will return
+	 * the current BTC/USD price.
+	 * 
+	 * @param string $code
+	 * @return string $rate
+	 * @throws Exception $e
+	 * 
+	 */
+	public function getRate($code = 'USD') {
+		$rate_url = 'https://bitpay.com/api/rates';
+
+		try {
+			$clist = json_decode(file_get_contents($rate_url),true);
+
+			foreach($clist as $key => $value) {
+				if($value['code'] == $code)
+					$rate = number_format($value['rate'], 2, '.', '');
+			}
+
+			return $rate;
+		}
+		catch (Exception $e) {
+			if($this->aBpOptions['useLogging'])
+				$this->_log('Error: ' . $e->getMessage());
+			return 'Error: ' . $e->getMessage();
+		}
+	}
+
+	protected function _initializeOptions() {
+		// REQUIRED Api key you created at bitpay.com
+		$this->aBpOptions['apiKey'] = $this->getOption('api_key');
+
+		// whether to verify POS data by hashing above api key.  If set to false, you should
+		// have some way of verifying that callback data comes from bitpay.com
+		// note: this option can only be changed here.  It cannot be set dynamically. 
+		$this->aBpOptions['verifyPos'] = true;
+
+		// email where invoice update notifications should be sent
+		$this->aBpOptions['notificationEmail'] = $this->getOption('notification_email');
+
+		// url where bit-pay server should send update notifications.  See API doc for more details.
+		// example: $bpNotificationUrl = 'http://www.example.com/callback.php';
+		$this->aBpOptions['notificationURL'] = $this->_oModule->_oConfig->getUrl('return_data', array(), true) . $this->_sName . '/';
+
+		// url where the customer should be directed to after paying for the order
+		// example: $bpNotificationUrl = 'http://www.example.com/confirmation.php';
+		$this->aBpOptions['redirectURL'] = BX_DOL_URL_ROOT . $this->_oModule->_oConfig->getBaseUri() . 'act_checkout_finished/' . $this->_sName . '/';
+
+		// This is the currency used for the price setting.  A list of other pricing
+		// currencies supported is found at bitpay.com
+		$this->aBpOptions['currency'] = 'BTC';
+
+		// Indicates whether anything is to be shipped with the order
+		// (if false, the buyer will be informed that nothing is to be shipped)
+		$this->aBpOptions['physical'] = false;
+
+		// If set to false, then notificaitions are only
+		// sent when an invoice is confirmed (according the the
+		// transactionSpeed setting). If set to true, then a notification
+		// will be sent on every status change
+		$this->aBpOptions['fullNotifications'] = $this->getOption('full_notifications') == 'on';
+
+		// transaction speed: low/medium/high.   See API docs for more details.
+		$this->aBpOptions['transactionSpeed'] = $this->getOption('transaction_speed'); 
+
+		// Logfile for use by the bpLog function.  Note: ensure the web server process has write access
+		// to this file and/or directory!
+		$this->aBpOptions['logFile'] = $GLOBALS['dir']['tmp'] . 'bx_payment_bp.log';
+
+		// Change to 'true' if you would like automatic logging of invoices and errors.
+		// Otherwise you will have to call the bpLog function manually to log any information.
+		$this->aBpOptions['useLogging'] = true;
+	}
+
+	/**
+	 *
+	 * Call from your notification handler to convert $_POST data to an object containing invoice data
+	 *
+	 * @param boolean $apiKey
+	 * @return mixed $json
+	 * @throws Exception $e
+	 *
+	 */
+	protected function _verifyNotification() {
+		try {
+			$this->_log('Notification received: ' . date("m.d.y H:i:s"));
+
+			$post = file_get_contents("php://input");
+			if(!$post) {
+				$this->_log('Error: No post data');
+				return false;
+			}
+
+			$json = json_decode($post, true);
+			$this->_log('-- Data: ' . $post);
+			$this->_log($json);
+
+			if(is_string($json))
+				return false;
+
+			if(!array_key_exists('posData', $json)) {
+				$this->_log('Error: No posData');
+				return false;
+			}
+
+			$json['posData'] = json_decode($json['posData'], true);
+			if(empty($json['posData']) || !is_array($json['posData'])) {
+				$this->_log('Error: Empty posData');
+				return false;
+			}
+
+			return $json;
+		}
+		catch (Exception $e) {
+			if($this->aBpOptions['useLogging'])
+				$this->_log('Error: ' . $e->getMessage());
+
+			return false;
+		}
+	}
+
+	/**
+	 *
+	 * Call from your notification handler to verify posData
+	 *
+	 * @param array $aPosData
+	 * @return boolean
+	 *
+	 */
+	protected function _verifyPosData($aPosData) {
+		if(!$this->aBpOptions['verifyPos']) {
+			if(empty($aPosData['d']) || !is_array($aPosData['d'])) {
+				$this->_log('Error: Payment data cannot be found.');
+				return false;
+			}
+
+			return $aPosData['d'];
+		}
+
+		if($this->_hash(serialize($aPosData['d']), $this->aBpOptions['apiKey']) != $aPosData['h']) {
+			$this->_log('Error: Authentication failed (bad posData hash).');
+			return false;
+		}
+
+		return $aPosData['d'];
+	}
+
+	protected function _verifyAmount($aPosData, $fAmount) {
+    	$iPendingId = (int)$aPosData['pnd'];
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+        if(empty($aPending) || !is_array($aPending) || $fAmount != (float)$aPending['amount'])
+            return false;	        	
+
+        return true;
+	}
+
+	/**
+	 * 
+	 * Decodes JSON response and returns
+	 * associative array.
+	 * 
+	 * @param string $response
+	 * @return array $arrResponse
+	 * @throws Exception $e
+	 * 
+	 */
+	protected function _decodeResponse($response) {
+		try {
+			if (empty($response) || !(is_string($response)))
+				return array('error' => 'BxPmtBitPay::_decodeResponse expects a string parameter.');
+
+			return json_decode($response, true);
+		} 
+		catch (Exception $e) {
+			if($this->aBpOptions['useLogging'])
+				$this->_log('Error: ' . $e->getMessage());
+
+			return array('error' => $e->getMessage());
+		}
+	}
+
+	/**
+	 *
+	 * Generates a base64 encoded keyed hash.
+	 *
+	 * @param string $data, string $key
+	 * @return string $hmac
+	 * @throws Exception $e
+	 *
+	 */
+	protected function _hash($data, $key) {
+		try {
+			$hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE));
+			return strtr($hmac, array('+' => '-', '/' => '_', '=' => ''));
+		}
+		catch (Exception $e) {
+			if($this->aBpOptions['useLogging'])
+				$this->_log('Error: ' . $e->getMessage());
+
+			return 'Error: ' . $e->getMessage();
+		}
+	}
+
+	/**
+	 *
+	 * Handles post/get to BitPay via curl.
+	 *
+	 * @param string $url, string $apiKey, boolean $post
+	 * @return mixed $response
+	 * @throws Exception $e
+	 *
+	 */
+	protected function _curl($url, $apiKey, $post = false) {
+		if(!isset($url) || trim($url) == '' || !isset($apiKey) || trim($apiKey) == '') {
+			// Invalid parameter specified
+			if($this->aBpOptions['useLogging'])
+				$this->_log('Error: You must supply non-empty url and apiKey parameters.');
+
+	    	return array('error' => 'You must supply non-empty url and apiKey parameters.');
+		}
+
+		try {
+			$curl = curl_init();
+			$length = 0;
+
+			if ($post) {
+				curl_setopt($curl, CURLOPT_POST, 1);
+				curl_setopt($curl, CURLOPT_POSTFIELDS, $post);
+				$length = strlen($post);
+			}
+
+			$uname = base64_encode($apiKey);
+
+			if($uname) {
+				$header = array(
+					'Content-Type: application/json',
+					'Content-Length: ' . $length,
+					'Authorization: Basic ' . $uname,
+				);
+
+				curl_setopt($curl, CURLOPT_URL, $url);
+				curl_setopt($curl, CURLOPT_PORT, 443);
+				curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
+				curl_setopt($curl, CURLOPT_TIMEOUT, 10);
+				curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC ) ;
+				curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); // verify certificate
+				curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2); // check existence of CN and verify that it matches hostname
+				curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
+				curl_setopt($curl, CURLOPT_FORBID_REUSE, 1);
+				curl_setopt($curl, CURLOPT_FRESH_CONNECT, 1);
+
+				$responseString = curl_exec($curl);
+
+				if($responseString == false) {
+					$response = array('error' => curl_error($curl));
+					if($this->aBpOptions['useLogging'])
+						$this->_log('Error: ' . curl_error($curl));
+				} 
+				else {
+					$response = json_decode($responseString, true);
+					if (!$response) {
+						$response = array('error' => 'invalid json: '.$responseString);
+						if($this->aBpOptions['useLogging'])
+							$this->_log('Error - Invalid JSON: ' . $responseString);
+					}
+				}
+
+				curl_close($curl);
+				return $response;
+			} 
+			else {
+				curl_close($curl);
+	
+				if($this->aBpOptions['useLogging'])
+					$this->_log('Invalid data found in apiKey value passed to Bitpay::curl method. (Failed: base64_encode(apikey))');
+
+				return array('error' => 'Invalid data found in apiKey value passed to Bitpay::curl method. (Failed: base64_encode(apikey))');
+			}
+		}
+		catch (Exception $e) {
+			@curl_close($curl);
+			if($this->aBpOptions['useLogging'])
+			$this->_log('Error: ' . $e->getMessage());
+			return array('error' => $e->getMessage());
+		}
+	}
+
+	/**
+	 *
+	 * Writes $contents to a log file specified in the bp_options file or, if missing,
+	 * defaults to a standard filename of 'bplog.txt'.
+	 *
+	 * @param mixed $contents
+	 * @return
+	 * @throws Exception $e 
+	 *
+	 */
+	protected function _log($contents) {
+		try {
+			if(isset($this->aBpOptions['logFile']) && $this->aBpOptions['logFile'] != '') {
+				$file = $this->aBpOptions['logFile'];
+	    	} 
+	    	else {
+				// Fallback to using a default logfile name in case the variable is
+				// missing or not set.
+				$file = dirname(__FILE__) . '/bplog.txt';
+			}
+
+			file_put_contents($file, date('m-d H:i:s').": ", FILE_APPEND);
+
+			if (is_array($contents))
+				$contents = var_export($contents, true);	
+			else if (is_object($contents))
+				$contents = json_encode($contents);
+
+			file_put_contents($file, $contents."\n", FILE_APPEND);
+	  	}
+	  	catch (Exception $e) {
+			echo 'Error: ' . $e->getMessage();
+	  	}
+	}
+}
+
+/** @} */

+ 269 - 0
modules/boonex/payment/classes/BxPaymentProviderPayPal.php

@@ -0,0 +1,269 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+define('PP_MODE_LIVE', 1);
+define('PP_MODE_TEST', 2);
+
+define('PP_PRC_TYPE_DIRECT', 1);
+define('PP_PRC_TYPE_PDT', 2);
+define('PP_PRC_TYPE_IPN', 3);
+
+define('PP_CNT_TYPE_SSL', 1);
+define('PP_CNT_TYPE_HTML', 2);
+
+class BxPaymentProviderPayPal extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
+{
+    protected $_sDataReturnUrl;
+
+    function __construct($aConfig)
+    {
+    	$this->MODULE = 'bx_payment';
+
+        parent::__construct($aConfig);
+
+        $this->_bRedirectOnResult = false;
+        $this->_sDataReturnUrl = $this->_oModule->_oConfig->getUrl('return_data') . $this->_sName . '/';
+    }
+
+    public function initializeCheckout($iPendingId, $aCartInfo, $bRecurring = false, $iRecurringDays = 0)
+    {
+        $iMode = (int)$this->getOption('mode');
+        $sActionURL = $iMode == PP_MODE_LIVE ? 'https://www.paypal.com/cgi-bin/webscr' : 'https://www.sandbox.paypal.com/cgi-bin/webscr';
+
+        if($bRecurring)
+            $aFormData = array(
+                'cmd' => '_xclick-subscriptions',
+                'a3' => sprintf("%.2f", (float)$aCartInfo['items_price']),
+                'p3' => $iRecurringDays,
+                't3' => 'D',
+                'src' => '1', // repeat billings unles member cancels subscription
+                'sra' => '1' // reattempt on failure
+            );
+        else
+            $aFormData = array(
+                'cmd' => '_xclick',
+                'amount' => sprintf( "%.2f", (float)$aCartInfo['items_price'])
+            );
+
+        $aFormData = array_merge($aFormData, array(
+            'business' => $iMode == PP_MODE_LIVE ? $this->getOption('business') : $this->getOption('sandbox'),
+            'bn' => 'Boonex_SP',
+            'item_name' => _t($this->_sLangsPrefix . 'txt_payment_to', $aCartInfo['vendor_name']),
+            'item_number' => $iPendingId,
+            'currency_code' => $aCartInfo['vendor_currency_code'],
+            'no_note' => '1',
+            'no_shipping' => '1',
+            'custom' => md5($aCartInfo['vendor_id'] . $iPendingId)
+        ));
+
+        foreach($aCartInfo['items'] as $aItem)
+            $aFormData['item_name'] .= ' ' . $aItem['title'] . ',';
+		$aFormData['item_name'] = trim($aFormData['item_name'], ', ');
+
+        switch($this->getOption('prc_type')) {
+            case PP_PRC_TYPE_PDT:
+            case PP_PRC_TYPE_DIRECT:
+                $aFormData = array_merge($aFormData, array(
+                    'return' => $this->_sDataReturnUrl . $aCartInfo['vendor_id'],
+                    'rm' => '2'
+                ));
+                break;
+            case PP_PRC_TYPE_IPN:
+                $aFormData = array_merge($aFormData, array(
+                    'return' => $this->_oConfig->getUrl('return'),
+                    'notify_url' => $this->_sDataReturnUrl . $aCartInfo['vendor_id'],
+                    'rm' => '1'
+                ));
+                break;
+        }
+
+        $this->_oModule->_oTemplate->displayPageCodeRedirect($sActionURL, $aFormData);
+        exit;
+    }
+
+    public function finalizeCheckout(&$aData)
+    {
+        if($aData['txn_type'] != 'web_accept' && !isset($aData['tx']))
+        	return array('code' => 1, 'message' => $this->_sLangsPrefix . 'pp_err_no_data_given');
+
+		return $this->_registerCheckout($aData);
+    }
+
+	public function checkoutFinished() {}
+
+    /**
+     *
+     * @param $aData - data from payment provider.
+     * @param $bSubscription - Is not needed. May be used in the future for subscriptions.
+     * @param $iPendingId - Is not needed. May be used in the future for subscriptions.
+     * @return array with results.
+     */
+    protected function _registerCheckout(&$aData, $bSubscription = false, $iPendingId = 0)
+    {
+        if(empty($this->_aOptions) && isset($aData['item_number']))
+            $this->_aOptions = $this->getOptionsByPending($aData['item_number']);
+
+        if(empty($this->_aOptions))
+            return array('code' => 2, 'message' => $this->_sLangsPrefix . 'pp_err_no_vendor_given');
+
+        $iPrcType = (int)$this->getOption('prc_type');
+        if(($iPrcType == PP_PRC_TYPE_IPN || $iPrcType == PP_PRC_TYPE_DIRECT) && (!isset($aData['item_number']) || !isset($aData['txn_id'])))
+            return array('code' => 1, 'message' => $this->_sLangsPrefix . 'pp_err_no_data_given');
+        else if($iPrcType == PP_PRC_TYPE_PDT && !isset($aData['tx']))
+            return array('code' => 1, 'message' => $this->_sLangsPrefix . 'pp_err_no_data_given');
+
+        $aResult = $this->_validateCheckout($aData);
+
+        if(!$bSubscription || empty($iPendingId))
+            $iPendingId = (int)$aData['item_number'];
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+        if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
+            return array('code' => 3, 'message' => $this->_sLangsPrefix . 'pp_err_already_processed');
+
+        //--- Update pending transaction ---//
+        $this->_oModule->_oDb->updateOrderPending($iPendingId, array(
+            'order' => $aData['txn_id'],
+            'error_code' => $aResult['code'],
+            'error_msg' => _t($aResult['message'])
+        ));
+
+        $sBuyerFirstName = bx_process_input($aData['first_name']);
+        $sBuyerLastName = bx_process_input($aData['last_name']);
+        $sBuyerEmail = bx_process_input($aData['payer_email']);
+
+        $aResult['pending_id'] = $iPendingId;
+        $aResult['payer_name'] = _t($this->_sLangsPrefix . 'txt_buyer_name_mask', $sBuyerFirstName, $sBuyerLastName); 
+		$aResult['payer_email'] = $sBuyerEmail;
+        return $aResult;
+    }
+
+    protected function _validateCheckout(&$aData)
+    {
+        $iMode = (int)$this->getOption('mode');
+        if($iMode == PP_MODE_LIVE) {
+           $sBusiness = $this->getOption('business');
+           $sConnectionUrl = 'www.paypal.com';
+        } else {
+            $sBusiness = $this->getOption('sandbox');
+            $sConnectionUrl = 'www.sandbox.paypal.com';
+        }
+
+        $iPrcType = $this->getOption('prc_type');
+        if($iPrcType == PP_PRC_TYPE_DIRECT || $iPrcType == PP_PRC_TYPE_IPN) {
+            if($aData['payment_status'] != 'Completed' )
+                return array('code' => 4, 'message' => $this->_sLangsPrefix . 'pp_err_not_completed');
+
+            if($aData['business'] != $sBusiness)
+                return array('code' => 5, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_business');
+
+            $sRequest = 'cmd=_notify-validate';
+            foreach($aData as $sKey => $sValue) {
+                if(in_array($sKey, array('cmd')))
+                    continue;
+
+                $sRequest .= '&' . urlencode($sKey) . '=' . urlencode(bx_process_pass($sValue));
+            }
+
+            $aResponse = $this->_readValidationData($sConnectionUrl, $sRequest);
+            if((int)$aResponse['code'] !== 0)
+               return $aResponse;
+
+            array_walk($aResponse['content'], create_function('&$arg', "\$arg = trim(\$arg);"));
+            if(strcmp($aResponse['content'][0], "INVALID") == 0)
+                return array('code' => 7, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_transaction');
+            else if(strcmp($aResponse['content'][0], "VERIFIED") != 0)
+                return array('code' => 8, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_verification_status');
+        } else if($iPrcType == PP_PRC_TYPE_PDT) {
+            $sRequest = "cmd=_notify-synch&tx=" . $aData['tx'] . "&at=" . $this->getOption('token');
+            $aResponse = $this->_readValidationData($sConnectionUrl, $sRequest);
+
+            if((int)$aResponse['code'] !== 0)
+               return $aResponse;
+
+            if(strcmp($aResponse['content'][0], "FAIL") == 0)
+                return array('code' => 7, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_transaction');
+            else if(strcmp($aResponse['content'][0], "SUCCESS") != 0)
+                return array('code' => 8, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_verification_status');
+
+            $aKeys = array();
+            foreach($aResponse['content'] as $sLine) {
+                list($sKey, $sValue) = explode("=", $sLine);
+                $aKeys[urldecode($sKey)] = urldecode($sValue);
+            }
+
+            $aData = array_merge($aData, $aKeys);
+
+            if($aData['payment_status'] != 'Completed' )
+                return array('code' => 4, 'message' => $this->_sLangsPrefix . 'pp_err_not_completed');
+
+            if($aData['business'] != $sBusiness)
+                return array('code' => 5, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_business');
+        }
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $aData['item_number']));
+        $aVendor = $this->_oModule->getVendorInfo($aPending['seller_id']);
+        $fAmount = (float)$this->_getReceivedAmount($aVendor['currency_code'], $aData);
+        if($fAmount != (float)$aPending['amount'])
+            return array('code' => 9, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_amount');
+
+        if($aData['custom'] != md5($aPending['seller_id'] . $aPending['id']))
+            return array('code' => 10, 'message' => $this->_sLangsPrefix . 'pp_err_wrong_custom_data');
+
+        return array('code' => BX_PAYMENT_RESULT_SUCCESS, 'message' => $this->_sLangsPrefix . 'pp_msg_verified');
+    }
+
+    protected function _readValidationData($sConnectionUrl, $sRequest)
+    {
+        $sHeader = "POST /cgi-bin/webscr HTTP/1.0\r\n";
+        $sHeader .= "Host: " . $sConnectionUrl . "\r\n";
+        $sHeader .= "Content-Type: application/x-www-form-urlencoded\r\n";
+        $sHeader .= "Content-Length: " . strlen($sRequest) . "\r\n";
+        $sHeader .= "Connection: close\r\n\r\n";
+
+        $iErrCode = 0;
+        $sErrMessage = "";
+        if($this->getOption('cnt_type') == PP_CNT_TYPE_SSL)
+            $rSocket = fsockopen("ssl://" . $sConnectionUrl, 443, $iErrCode, $sErrMessage, 60);
+        else
+            $rSocket = fsockopen("tcp://" . $sConnectionUrl, 80, $iErrCode, $sErrMessage, 60);
+
+        if(!$rSocket)
+            return array('code' => 6, 'message' => $this->_sLangsPrefix . 'err_cannot_validate');
+
+        fputs($rSocket, $sHeader);
+        fputs($rSocket, $sRequest);
+
+        $sResponse = '';
+        while(!feof($rSocket))
+            $sResponse .= fread($rSocket, 1024);
+        fclose($rSocket);
+
+        list($sResponseHeader, $sResponseContent) = explode("\r\n\r\n", $sResponse);
+
+        return array('code' => 0, 'content' => explode("\n", $sResponseContent));
+    }
+    protected function _getReceivedAmount($sCurrencyCode, &$aResultData)
+    {
+        $fAmount = 0.00;
+
+        if($aResultData['mc_currency'] == $sCurrencyCode && isset($aResultData['payment_gross']) && !empty($aResultData['payment_gross']))
+            $fAmount = (float)$aResultData['payment_gross'];
+        else if($aResultData['mc_currency'] == $sCurrencyCode && isset($aResultData['mc_gross']) && !empty($aResultData['mc_gross']))
+            $fAmount = (float)$aResultData['mc_gross'];
+        else if($aResultData['settle_currency'] == $sCurrencyCode && isset($aResultData['settle_amount']) && !empty($aResultData['settle_amount']))
+            $fAmount = (float)$aResultData['settle_amount'];
+
+        return $fAmount;
+    }
+}
+
+/** @} */

+ 48 - 0
modules/boonex/payment/classes/BxPaymentResponse.php

@@ -0,0 +1,48 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Timeline Timeline
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentResponse extends BxDolAlertsResponse
+{
+	protected $MODULE;
+    protected $_oModule;
+
+	public function __construct()
+    {
+    	$this->MODULE = 'bx_payment';
+
+        parent::__construct();
+
+        $this->_oModule = BxDolModule::getInstance($this->MODULE);
+    }
+
+	/**
+     * Overwtire the method of parent class.
+     *
+     * @param BxDolAlerts $oAlert an instance of alert.
+     */
+    public function response($oAlert)
+    {
+    	if($oAlert->sUnit != 'profile' || !in_array($oAlert->sAction, array('join', 'delete')))
+    		return;
+
+		switch($oAlert->sAction) {
+			case 'join':
+				$this->_oModule->onProfileJoin($oAlert->iObject);
+				break;
+
+			case 'delete':
+				$this->_oModule->onProfileDelete($oAlert->iObject);
+				break;
+		}
+    }
+}
+
+/** @} */

+ 317 - 0
modules/boonex/payment/classes/BxPaymentTemplate.php

@@ -0,0 +1,317 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentTemplate extends BxBaseModPaymentTemplate
+{
+	protected $_sLangsPrefix;
+
+    function __construct(&$oConfig, &$oDb)
+    {
+        parent::__construct($oConfig, $oDb);
+
+        $this->_sLangsPrefix = $this->_oConfig->getPrefix('langs');
+    }
+
+    public function addJsCssOrders()
+    {
+    	$this->addJs(array(
+    		'jquery-ui/jquery.ui.widget.min.js',
+    		'jquery-ui/jquery.ui.menu.min.js', 
+    		'jquery-ui/jquery.ui.autocomplete.min.js', 
+    		'jquery.form.min.js', 
+    		'jquery.anim.js', 
+    		'jquery.webForms.js', 
+    		'orders.js'
+    	));
+    	$this->addCss(array('orders.css'));
+    }
+
+    public function addJsCssCart()
+    {
+    	$this->addJsTranslation(array('_bx_payment_err_nothing_selected'));
+    	$this->addJs(array('jquery.anim.js', 'cart.js'));
+    	$this->addCss(array('orders.css', 'cart.css'));
+    }
+
+	public function displayCartJs($bWrapped = true)
+    {
+        $this->addJsCssCart();
+        return $this->displayJsCode('cart');
+    }
+
+    public function displayAddToCartJs($iVendorId, $iModuleId, $iItemId, $iItemCount, $bNeedRedirect = false, $bWrapped = true)
+    {
+        $sJsCode = $this->displayCartJs($bWrapped);
+        $sJsMethod = $this->parseHtmlByName('add_to_cart_js.html', array(
+            'js_object' => $this->_oConfig->getJsObject('cart'),
+            'vendor_id' => $iVendorId,
+            'module_id' => $iModuleId,
+            'item_id' => $iItemId,
+            'item_count' => $iItemCount,
+            'need_redirect' => (int)$bNeedRedirect
+        ));
+
+        return array($sJsCode, $sJsMethod);
+    }
+
+    public function displayAddToCartLink($iVendorId, $iModuleId, $iItemId, $iItemCount, $bNeedRedirect = false)
+    {
+        $this->addJsCssCart();
+        return $this->parseHtmlByName('add_to_cart.html', array(
+        	'js_object' => $this->_oConfig->getJsObject('cart'),
+        	'js_content' => $this->displayJsCode('cart'),
+        	'txt_add_to_cart' => _t($this->_sLangsPrefix . 'txt_add_to_cart'),
+            'vendor_id' => $iVendorId,
+            'module_id' => $iModuleId,
+            'item_id' => $iItemId,
+            'item_count' => $iItemCount,
+            'need_redirect' => (int)$bNeedRedirect
+        ));
+    }
+
+	public function displayBlockCart($aCartInfo, $iVendorId = BX_PAYMENT_EMPTY_ID)
+    {
+    	if(empty($aCartInfo))
+    		return MsgBox(_t($this->_sLangsPrefix . 'msg_no_results'));
+
+        $sJsObject = $this->_oConfig->getJsObject('cart');
+
+        if($iVendorId != BX_PAYMENT_EMPTY_ID)
+            $aCartInfo = array($aCartInfo);
+
+        $aVendors = array();
+        foreach($aCartInfo as $aVendor) {
+            //--- Get Providers ---//
+            $aProviders = array();
+            $aVendorProviders = $this->_oDb->getVendorInfoProviders($aVendor['vendor_id']);
+            foreach($aVendorProviders as $aProvider)
+                $aProviders[] = array(
+                    'name' => $aProvider['name'],
+                    'caption' => _t($this->_sLangsPrefix . 'txt_cart_' . $aProvider['name']),
+                    'checked' => empty($aProviders) ? 'checked="checked"' : ''
+                );
+
+            //--- Get Items ---//
+            $aItems = array();
+            foreach($aVendor['items'] as $aItem)
+                $aItems[] = array(
+                    'vendor_id' => $aVendor['vendor_id'],
+                    'vendor_currency_code' => $aVendor['vendor_currency_code'],
+                    'module_id' => $aItem['module_id'],
+                    'item_id' => $aItem['id'],
+                    'item_title' => $aItem['title'],
+                    'item_url' => $aItem['url'],
+                    'item_quantity' => $aItem['quantity'],
+                	'bx_if:show_price_paid' => array(
+                		'condition' => (int)$aItem['price'] != 0,
+                		'content' => array(
+                			'item_price' => $aItem['quantity'] * $aItem['price'],
+                			'vendor_currency_code' => $aVendor['vendor_currency_code'],
+                		)
+                	),
+                	'bx_if:show_price_free' => array(
+                		'condition' => (int)$aItem['price'] == 0,
+                		'content' => array()
+                	),
+                    'js_object' => $sJsObject
+                );
+
+            //--- Get Control Panel ---//
+            $aButtons = array(
+                'bx-payment-checkout' => _t($this->_sLangsPrefix . 'form_cart_input_do_checkout'),
+                'bx-payment-delete' => _t($this->_sLangsPrefix . 'form_cart_input_do_delete')
+            );
+            $oSearchResult = new BxTemplSearchResult();
+            $sControlPanel = $oSearchResult->showAdminActionsPanel('items_from_' . $aVendor['vendor_id'], $aButtons, 'items', true, true);
+
+            //--- Get General ---//
+            $bVendorLink = !empty($aVendor['vendor_link']);
+            $sTxtShoppingCart = _t($this->_sLangsPrefix . 'txt_shopping_cart', $aVendor['vendor_name']);
+
+            $aVendors[] = array(
+                'vendor_id' => $aVendor['vendor_id'],
+                'bx_if:show_link' => array(
+                    'condition' => $bVendorLink,
+                    'content' => array(
+            			'txt_shopping_cart' => $sTxtShoppingCart,
+                        'vendor_url' => $aVendor['vendor_link'],
+                        'vendor_currency_code' => $aVendor['vendor_currency_code'],
+                        'items_count' => $aVendor['items_count'],
+                        'items_price' => $aVendor['items_price']
+                    )
+                ),
+                'bx_if:show_text' => array(
+                    'condition' => !$bVendorLink,
+                    'content' => array(
+                		'txt_shopping_cart' => $sTxtShoppingCart,
+                        'vendor_currency_code' => $aVendor['vendor_currency_code'],
+                        'items_count' => $aVendor['items_count'],
+                        'items_price' => $aVendor['items_price']
+                    )
+                ),
+                'vendor_icon' => $aVendor['vendor_icon'],
+                'bx_repeat:providers' => $aProviders,
+                'bx_repeat:items' => $aItems,
+                'js_object' => $sJsObject,
+                'process_url' => BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'cart_submit/',
+                'control_panel' => $sControlPanel
+            );
+        }
+
+        $this->addJsCssCart();
+        return $this->parseHtmlByName('cart.html', array(
+        	'js_content' => $this->displayJsCode('cart'),
+        	'bx_repeat:vendors' => $aVendors
+        ));
+    }
+
+    public function displayBlockHistory($iClientId, $iSellerId)
+    {
+    	$oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_history'));
+        if(!$oGrid || empty($iClientId))
+            return MsgBox(_t($this->_sLangsPrefix . 'msg_no_results'));
+
+		$oGrid->addQueryParam('client_id', $iClientId);
+		if(!empty($iSellerId))
+			$oGrid->addQueryParam('seller_id', $iSellerId);
+
+		$this->addJsCssOrders();
+        return $this->displayJsCode('history') . $oGrid->getCode();
+    }
+
+	public function displayBlockOrders($sType, $iSellerId)
+    {
+    	$oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getObject('grid_' . $sType));
+        if(!$oGrid || empty($iSellerId))
+            return MsgBox(_t($this->_sLangsPrefix . 'msg_no_results'));
+
+		$oGrid->addQueryParam('seller_id', $iSellerId);
+
+		$this->addJsCssOrders();
+        return $this->displayJsCode($sType) . $oGrid->getCode();
+    }
+
+	public function displayOrder($sType, $iId)
+    {
+        $sMethodName = 'getOrder' . bx_gen_method_name($sType);
+        $aOrder = $this->_oDb->$sMethodName(array('type' => 'id', 'id' => $iId));
+
+        $oModule = $this->_getModule();
+        $aSeller = $oModule->getVendorInfo((int)$aOrder['seller_id']);
+        $aClient = $oModule->getProfileInfo((int)$aOrder['client_id']);
+
+        $aResult = array(
+        	'txt_client' => _t($this->_sLangsPrefix . 'txt_client'),
+        	'txt_seller' => _t($this->_sLangsPrefix . 'txt_seller'),
+        	'txt_order' => _t($this->_sLangsPrefix . 'txt_order'),
+        	'txt_processed_with' => _t($this->_sLangsPrefix . 'txt_processed_with'),
+        	'txt_message' => _t($this->_sLangsPrefix . 'txt_message'),
+        	'txt_date' => _t($this->_sLangsPrefix . 'txt_date'),
+        	'txt_products' => _t($this->_sLangsPrefix . 'txt_products'),
+            'client_name' => $aClient['name'],
+            'client_url' => $aClient['link'],
+            'bx_if:show_link' => array(
+                'condition' => !empty($aSeller['link']),
+                'content' => array(
+                    'seller_name' => $aSeller['name'],
+                    'seller_url' => $aSeller['link'],
+                )
+            ),
+            'bx_if:show_text' => array(
+                'condition' => empty($aSeller['link']),
+                'content' => array(
+                    'seller_name' => $aSeller['name']
+                )
+            ),
+            'order' => $aOrder['order'],
+            'provider' => $aOrder['provider'],
+            'error' => $aOrder['error_msg'],
+            'date' => bx_time_js($aOrder['date']),
+            'bx_repeat:items' => array()
+        );
+
+        if($sType == BX_PAYMENT_ORDERS_TYPE_PENDING)
+            $aItems = $this->_oConfig->descriptorsM2A($aOrder['items']);
+        else
+            $aItems = $this->_oConfig->descriptorsM2A($this->_oConfig->descriptorA2S(array($aOrder['seller_id'], $aOrder['module_id'], $aOrder['item_id'], $aOrder['item_count'])));
+
+        foreach($aItems as $aItem) {
+            $aInfo = $oModule->callGetCartItem((int)$aItem['module_id'], array($aItem['item_id']));
+            if(!empty($aInfo) && is_array($aInfo))
+	            $aResult['bx_repeat:items'][] = array(
+	                'bx_if:link' => array(
+	                    'condition' => !empty($aInfo['url']),
+	                    'content' => array(
+	                        'title' => $aInfo['title'],
+	                        'url' => $aInfo['url']
+	                    )
+	                ),
+	                'bx_if:text' => array(
+	                    'condition' => empty($aInfo['url']),
+	                    'content' => array(
+	                        'title' => $aInfo['title'],
+	                    )
+	                ),
+	                'quantity' => $aItem['item_count'],
+	                'price' => $aInfo['price'],
+	                'currency_code' => $aSeller['currency_code']
+	            );
+        }
+
+        return $this->parseHtmlByName('order_' . $sType . '.html', $aResult);
+    }
+
+    public function displayItems($aItems = array())
+    {
+        $aTmplVarsItems = array();
+
+        foreach($aItems as $aItem) {
+            $aTmplVarsItems[] = array(
+                'id' => $aItem['id'],
+                'price' => $aItem['price'],
+                'bx_if:link' => array(
+                    'condition' => !empty($aItem['url']),
+                    'content' => array(
+                        'url' => $aItem['url'],
+                        'title' => $aItem['title']
+                    )
+                ),
+                'bx_if:text' => array(
+                    'condition' => empty($aItem['url']),
+                    'content' => array(
+                        'title' => $aItem['title']
+                    )
+                ),
+            );
+        }
+
+        if(empty($aTmplVarsItems))
+        	$aTmplVarsItems = MsgBox(_t($this->_sLangsPrefix . 'msg_no_results'));
+
+        return $this->parseHtmlByName('items.html', array(
+        	'html_id' => $this->_oConfig->getHtmlIds('processed', 'order_processed_items'),
+        	'bx_repeat:items' => $aTmplVarsItems
+        ));
+    }
+
+	protected function _getModule()
+    {
+        $sName = $this->_oConfig->getName();
+        return BxDolModule::getInstance($sName);
+    }
+
+    protected function _isSubscription($aOrder)
+    {
+    	return '';
+    }
+}
+
+/** @} */

+ 70 - 0
modules/boonex/payment/install/config.php

@@ -0,0 +1,70 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+	'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_payment',
+    'title' => 'Payment',
+    'note' => 'Payment module.',
+    'version' => '8.0.0.DEV',
+    'vendor' => 'Boonex',
+    'help_url' => 'http://feed.boonex.com/?section={module_name}',
+
+    'compatible_with' => array(
+        '8.0.x'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/payment/',
+    'home_uri' => 'payment',
+
+    'db_prefix' => 'bx_payment_',
+    'class_prefix' => 'BxPayment',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Payment',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+	'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 30 - 0
modules/boonex/payment/install/installer.php

@@ -0,0 +1,30 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+class BxPaymentInstaller extends BxDolStudioInstaller
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+
+	function enable($aParams)
+    {
+        $aResult = parent::enable($aParams);
+
+        if($aResult['result'])
+            BxDolService::call($this->_aConfig['name'], 'update_dependent_modules');
+
+        return $aResult;
+    }
+}
+
+/** @} */

+ 289 - 0
modules/boonex/payment/install/langs/en.xml

@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string name="_bx_payment"><![CDATA[Payment]]></string>
+
+	<!-- OPTIONS -->
+	<string name="_bx_payment_option_default_currency_code"><![CDATA[Currency code]]></string>
+	<string name="_bx_payment_option_err_default_currency_code"><![CDATA[Currency code cannot be empty.]]></string>
+	<string name="_bx_payment_option_default_currency_sign"><![CDATA[Currency sign]]></string>
+	<string name="_bx_payment_option_err_default_currency_sign"><![CDATA[Currency sign cannot be empty.]]></string>	
+	<string name="_bx_payment_option_site_admin"><![CDATA[Site administrator]]></string>
+
+	<!-- PAGES and BLOCKS -->
+	<string name="_bx_payment_page_title_sys_cart"><![CDATA[Shopping Cart]]></string>
+	<string name="_bx_payment_page_title_cart"><![CDATA[Shopping Cart]]></string>
+	<string name="_bx_payment_page_block_title_cart"><![CDATA[Shopping Cart]]></string>
+
+	<string name="_bx_payment_page_title_sys_history"><![CDATA[Shopping History]]></string>
+	<string name="_bx_payment_page_title_history"><![CDATA[Shopping History]]></string>
+	<string name="_bx_payment_page_block_title_history"><![CDATA[Shopping History]]></string>
+
+	<string name="_bx_payment_page_title_sys_orders"><![CDATA[Orders]]></string>
+	<string name="_bx_payment_page_title_orders"><![CDATA[Orders]]></string>
+	<string name="_bx_payment_page_block_title_orders"><![CDATA[Orders]]></string>
+
+	<string name="_bx_payment_page_title_sys_details"><![CDATA[Payment Details]]></string>
+	<string name="_bx_payment_page_title_details"><![CDATA[Payment Details]]></string>
+	<string name="_bx_payment_page_block_title_details"><![CDATA[Payment Details]]></string>
+
+	<string name="_bx_payment_page_block_title_orders_processed"><![CDATA[Processed Orders]]></string>
+	<string name="_bx_payment_page_block_title_orders_pending"><![CDATA[Pending Orders]]></string>
+
+	<string name="_bx_payment_page_title_response"><![CDATA[Payment Response]]></string>
+	<string name="_bx_payment_page_title_error"><![CDATA[Payment Error]]></string>
+
+	<string name="_bx_payment_page_title_sys_join"><![CDATA[Join]]></string>
+	<string name="_bx_payment_page_title_join"><![CDATA[Join]]></string>
+	<string name="_bx_payment_page_block_title_join"><![CDATA[Join]]></string>
+
+	<!--
+	<string name="_bx_payment_page_title_sys_"><![CDATA[]]></string>
+	<string name="_bx_payment_page_title_"><![CDATA[]]></string>
+	<string name="_bx_payment_page_block_title_"><![CDATA[]]></string>
+	-->
+
+	<!-- MENUS -->
+	<string name="_bx_payment_menu_title_orders_submenu"><![CDATA[Orders Submenu]]></string>
+    <string name="_bx_payment_menu_set_title_orders_submenu"><![CDATA[Orders Submenu]]></string>
+    <string name="_bx_payment_menu_item_title_system_orders_processed"><![CDATA[Processed Orders]]></string>
+    <string name="_bx_payment_menu_item_title_orders_processed"><![CDATA[Processed]]></string>
+    <string name="_bx_payment_menu_item_title_system_orders_pending"><![CDATA[Pending Orders]]></string>
+    <string name="_bx_payment_menu_item_title_orders_pending"><![CDATA[Pending]]></string>
+    <string name="_bx_payment_menu_item_title_system_details"><![CDATA[Payment Details]]></string>
+    <string name="_bx_payment_menu_item_title_details"><![CDATA[Settings]]></string>
+
+	<string name="_bx_payment_menu_title_cart_submenu"><![CDATA[Cart Submenu]]></string>
+    <string name="_bx_payment_menu_set_title_cart_submenu"><![CDATA[Cart Submenu]]></string>
+    <string name="_bx_payment_menu_item_title_system_cart"><![CDATA[Cart]]></string>
+    <string name="_bx_payment_menu_item_title_cart"><![CDATA[Cart]]></string>
+    <string name="_bx_payment_menu_item_title_system_cart_history"><![CDATA[Cart History]]></string>
+    <string name="_bx_payment_menu_item_title_cart_history"><![CDATA[Cart History]]></string>
+
+	<!--
+	<string name="_bx_payment_menu_title_"><![CDATA[]]></string>
+    <string name="_bx_payment_menu_set_title_"><![CDATA[]]></string>
+    <string name="_bx_payment_menu_item_title_system_"><![CDATA[]]></string>
+    <string name="_bx_payment_menu_item_title_"><![CDATA[]]></string>
+    -->
+
+	<!-- FORMS -->
+	<string name="_bx_payment_form_cart_input_do_delete"><![CDATA[Delete]]></string>
+	<string name="_bx_payment_form_cart_input_do_checkout"><![CDATA[Checkout]]></string>
+	<string name="_bx_payment_form_details_input_do_submit"><![CDATA[Save]]></string>
+
+	<!-- GRIDS -->
+	<string name="_bx_payment_grid_orders_field_client_id"><![CDATA[Client]]></string>
+	<string name="_bx_payment_grid_orders_field_seller_id"><![CDATA[Seller]]></string>
+	<string name="_bx_payment_grid_orders_field_transaction"><![CDATA[Transaction]]></string>
+	<string name="_bx_payment_grid_orders_field_license"><![CDATA[License]]></string>
+	<string name="_bx_payment_grid_orders_field_amount"><![CDATA[Amount]]></string>
+	<string name="_bx_payment_grid_orders_field_date"><![CDATA[Date]]></string>
+
+	<string name="_bx_payment_grid_orders_action_add"><![CDATA[Manual Order]]></string>
+	<string name="_bx_payment_grid_orders_action_cancel"><![CDATA[Cancel]]></string>
+	<string name="_bx_payment_grid_orders_action_process"><![CDATA[Process]]></string>
+	<string name="_bx_payment_grid_orders_action_view_order"><![CDATA[View]]></string>
+
+	<string name="_bx_payment_grid_orders_popup_order_history_view"><![CDATA[Order]]></string>
+	<string name="_bx_payment_grid_orders_popup_order_pending_view"><![CDATA[Pending Order]]></string>
+	<string name="_bx_payment_grid_orders_popup_order_pending_process"><![CDATA[Process Pending Order]]></string>
+	<string name="_bx_payment_grid_orders_popup_order_processed_view"><![CDATA[Processed Order]]></string>
+	<string name="_bx_payment_grid_orders_popup_order_processed_add"><![CDATA[Manual Order]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+
+
+	<!-- FORMS -->
+	<string name="_bx_payment_form_pendings_form"><![CDATA[Pendings Form]]></string>
+	<string name="_bx_payment_form_processed_form"><![CDATA[Processed Form]]></string>
+	<string name="_bx_payment_form_pendings_display_process"><![CDATA[Process Pending Display]]></string>
+	<string name="_bx_payment_form_processed_display_add"><![CDATA[Add Processed Display]]></string>
+	<string name="_bx_payment_form_pendings_input_id_sys"><![CDATA[ID]]></string>
+	<string name="_bx_payment_form_pendings_input_seller_id_sys"><![CDATA[Seller ID]]></string>
+	<string name="_bx_payment_form_pendings_input_order_sys"><![CDATA[Order]]></string>
+	<string name="_bx_payment_form_pendings_input_order"><![CDATA[Order]]></string>
+	<string name="_bx_payment_form_pendings_input_order_err"><![CDATA[You need to fill in order number here.]]></string>
+	<string name="_bx_payment_form_pendings_input_process_sys"><![CDATA[Process]]></string>
+	<string name="_bx_payment_form_pendings_input_process"><![CDATA[Process]]></string>
+	<string name="_bx_payment_form_pendings_input_cancel_sys"><![CDATA[Cancel]]></string>
+	<string name="_bx_payment_form_pendings_input_cancel"><![CDATA[Cancel]]></string>
+	<string name="_bx_payment_form_processed_input_seller_id_sys"><![CDATA[Seller ID]]></string>
+	<string name="_bx_payment_form_processed_input_client_id_sys"><![CDATA[Client ID]]></string>
+	<string name="_bx_payment_form_processed_input_client_sys"><![CDATA[Client]]></string>
+	<string name="_bx_payment_form_processed_input_client"><![CDATA[Client]]></string>
+	<string name="_bx_payment_form_processed_input_client_err"><![CDATA[You need to enter a name of an existed member here.]]></string>
+	<string name="_bx_payment_form_processed_input_order_sys"><![CDATA[Order]]></string>
+	<string name="_bx_payment_form_processed_input_order"><![CDATA[Order]]></string>
+	<string name="_bx_payment_form_processed_input_order_err"><![CDATA[You need to fill in order number here.]]></string>
+	<string name="_bx_payment_form_processed_input_module_id_sys"><![CDATA[Module]]></string>
+	<string name="_bx_payment_form_processed_input_module_id"><![CDATA[Module]]></string>
+	<string name="_bx_payment_form_processed_input_module_id_err"><![CDATA[You need to select a module here.]]></string>
+	<string name="_bx_payment_form_processed_input_items_sys"><![CDATA[Items]]></string>
+	<string name="_bx_payment_form_processed_input_items"><![CDATA[Items]]></string>
+	<string name="_bx_payment_form_processed_input_items_err"><![CDATA[You need to select at least one item from the list here.]]></string>
+	<string name="_bx_payment_form_processed_input_add"><![CDATA[Add]]></string>
+	<string name="_bx_payment_form_processed_input_add_sys"><![CDATA[Add]]></string>
+	<string name="_bx_payment_form_processed_input_cancel"><![CDATA[Cancel]]></string>
+	<string name="_bx_payment_form_processed_input_cancel_sys"><![CDATA[Cancel]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+
+	<!-- ACL -->
+	<string name="_bx_payment_acl_action_purchase"><![CDATA[Purchase]]></string>
+	<string name="_bx_payment_acl_action_sell"><![CDATA[Sell]]></string>
+
+	<!-- EMAIL TEMPLATES -->
+	<string name="_bx_payment_et_txt_name_paid_need_join"><![CDATA[Paid and need to join.]]></string>
+	<string name="_bx_payment_et_txt_subject_paid_need_join"><![CDATA[Payment was accepted.]]></string>
+	<string name="_bx_payment_et_txt_body_paid_need_join"><![CDATA[<bx_include_auto:_email_header.html />
+<p><b>Dear {RealName}</b>,</p>
+<p>Your payment was accepted. If you did not fill in the join form yet, then you may do it using the following link.</p>
+<p><a href="{JoinLink}">Join Now</a></p>
+<bx_include_auto:_email_footer.html />]]></string>
+
+	<!-- MESSAGES, WARNINGS and ERRORS -->
+	<string name="_bx_payment_msg_no_results"><![CDATA[Empty]]></string>
+	<string name="_bx_payment_msg_redirecting"><![CDATA[Redirecting... Please wait.]]></string>
+	<string name="_bx_payment_msg_successfully_added"><![CDATA[The item was successfully added to your shopping cart.]]></string>
+	<string name="_bx_payment_msg_successfully_processed_free"><![CDATA[Free item was successfully processed]]></string>
+	<string name="_bx_payment_err_access_db"><![CDATA[Database access error.]]></string>
+	<string name="_bx_payment_err_cannot_validate"><![CDATA[Can't connect to remote host for validation.]]></string>
+	<string name="_bx_payment_err_empty_items"><![CDATA[There are no items selected.]]></string>
+	<string name="_bx_payment_err_empty_order"><![CDATA[The order is empty.]]></string>
+	<string name="_bx_payment_err_inactive_vendor"><![CDATA[The vendor status is not active.]]></string>
+	<string name="_bx_payment_err_incorrect_provider"><![CDATA[Incorrect or unavailable payment provider.]]></string>
+	<string name="_bx_payment_err_not_accept_payments"><![CDATA[The vendor does not accept payments.]]></string>
+	<string name="_bx_payment_err_nothing_selected"><![CDATA[You need to select at least one item.]]></string>
+	<string name="_bx_payment_err_purchase_from_yourself"><![CDATA[You cannot purchase your own item. It's just a bad luck thing, you know.]]></string>
+	<string name="_bx_payment_err_required_login"><![CDATA[You need to login.]]></string>
+	<string name="_bx_payment_err_unknown_vendor"><![CDATA[Unknown vendor.]]></string>
+	<string name="_bx_payment_err_wrong_data"><![CDATA[Incorrect input data.]]></string>
+	<string name="_bx_payment_err_not_found_pending"><![CDATA[Pending order was not found.]]></string>
+	<string name="_bx_payment_err_cannot_perform"><![CDATA[Cannot perform the action. Please report.]]></string>
+	<string name="_bx_payment_err_wrong_quantity"><![CDATA[Incorrect quantity specified.]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+
+
+	<!-- Other -->
+	<string name="_bx_payment_txt_shopping_cart"><![CDATA[{0}'s cart]]></string>
+	<string name="_bx_payment_txt_payment_to"><![CDATA[Payment to {0} for:]]></string>
+	<string name="_bx_payment_txt_client"><![CDATA[Client]]></string>
+	<string name="_bx_payment_txt_seller"><![CDATA[Seller]]></string>
+	<string name="_bx_payment_txt_order"><![CDATA[Order ID]]></string>
+	<string name="_bx_payment_txt_processed_with"><![CDATA[Processed with]]></string>
+	<string name="_bx_payment_txt_message"><![CDATA[Result]]></string>
+	<string name="_bx_payment_txt_date"><![CDATA[Date]]></string>
+	<string name="_bx_payment_txt_products"><![CDATA[Products]]></string>
+	
+	
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+	<string name=""><![CDATA[]]></string>
+
+	<!-- PayPal payment provider -->
+	<string name="_bx_payment_pp_cpt"><![CDATA[PayPal]]></string>
+	<string name="_bx_payment_pp_dsc"><![CDATA[PayPal payment provider]]></string>
+	<string name="_bx_payment_pp_active_cpt"><![CDATA[Active]]></string>
+	<string name="_bx_payment_pp_active_dsc"><![CDATA[Activate(Deactivate) provider]]></string>
+	<string name="_bx_payment_pp_mode_cpt"><![CDATA[Mode]]></string>
+	<string name="_bx_payment_pp_mode_dsc"><![CDATA[Mode to process payments]]></string>
+	<string name="_bx_payment_pp_mode_live"><![CDATA[live]]></string>
+	<string name="_bx_payment_pp_mode_test"><![CDATA[test]]></string>
+	<string name="_bx_payment_pp_business_cpt"><![CDATA[Business]]></string>
+	<string name="_bx_payment_pp_business_dsc"><![CDATA[Business email address]]></string>
+	<string name="_bx_payment_pp_prc_type_cpt"><![CDATA[Process type]]></string>
+	<string name="_bx_payment_pp_prc_type_dsc"><![CDATA[The type in which data is to be processed. In IPN mode use URL like the following http://www.your-site.com/your-path/m/payment/act_finalize_checkout/paypal as notify URL in your PayPal account]]></string>
+	<string name="_bx_payment_pp_prc_type_direct"><![CDATA[direct]]></string>
+	<string name="_bx_payment_pp_prc_type_pdt"><![CDATA[PDT]]></string>
+	<string name="_bx_payment_pp_prc_type_ipn"><![CDATA[IPN]]></string>
+	<string name="_bx_payment_pp_cnt_type_cpt"><![CDATA[Connection type]]></string>
+	<string name="_bx_payment_pp_cnt_type_dsc"><![CDATA[The type of connection to be used to transfer data]]></string>
+	<string name="_bx_payment_pp_cnt_type_ssl"><![CDATA[SSL]]></string>
+	<string name="_bx_payment_pp_cnt_type_http"><![CDATA[HTTP]]></string>
+	<string name="_bx_payment_pp_token_cpt"><![CDATA[Identity token]]></string>
+	<string name="_bx_payment_pp_token_dsc"><![CDATA[Identity token]]></string>
+	<string name="_bx_payment_pp_sandbox_cpt"><![CDATA[Sandbox]]></string>
+	<string name="_bx_payment_pp_sandbox_dsc"><![CDATA[Sandbox email address]]></string>
+	<string name="_bx_payment_pp_return_url_cpt"><![CDATA[Data return URL]]></string>
+	<string name="_bx_payment_pp_err_no_data_given"><![CDATA[No data given.]]></string>
+	<string name="_bx_payment_pp_err_no_vendor_given"><![CDATA[Unknown vendor.]]></string>
+	<string name="_bx_payment_pp_err_not_completed"><![CDATA[Payment is not completed.]]></string>
+	<string name="_bx_payment_pp_err_wrong_business"><![CDATA[Wrong receiver email.]]></string>
+	<string name="_bx_payment_pp_err_wrong_transaction"><![CDATA[Transaction verification failed.]]></string>
+	<string name="_bx_payment_pp_err_wrong_verification_status"><![CDATA[No verification status received.]]></string>
+	<string name="_bx_payment_pp_err_wrong_amount"><![CDATA[Wrong amount.]]></string>
+	<string name="_bx_payment_pp_err_wrong_custom_data"><![CDATA[Incorrect custom data.]]></string>
+	<string name="_bx_payment_pp_err_already_processed"><![CDATA[This payment has already processed.]]></string>
+	<string name="_bx_payment_pp_msg_verified"><![CDATA[Payment was successfully accepted.]]></string>
+	<string name="_bx_payment_txt_cart_paypal"><![CDATA[PayPal]]></string>
+
+	<!-- 2Checkout payment provider -->
+	<string name="_bx_payment_2co_cpt"><![CDATA[2Checkout]]></string>
+	<string name="_bx_payment_2co_dsc"><![CDATA[2Checkout payment provider]]></string>
+	<string name="_bx_payment_2co_active_cpt"><![CDATA[Active]]></string>
+	<string name="_bx_payment_2co_active_dsc"><![CDATA[Activate(Deactivate) provider]]></string>
+	<string name="_bx_payment_2co_mode_cpt"><![CDATA[Mode]]></string>
+	<string name="_bx_payment_2co_mode_dsc"><![CDATA[Mode to process payments]]></string>
+	<string name="_bx_payment_2co_mode_live"><![CDATA[live]]></string>
+	<string name="_bx_payment_2co_mode_test"><![CDATA[test]]></string>
+	<string name="_bx_payment_2co_account_id_cpt"><![CDATA[Account number]]></string>
+	<string name="_bx_payment_2co_account_id_dsc"><![CDATA[Your 2Checkout vendor account number]]></string>
+	<string name="_bx_payment_2co_payment_method_cpt"><![CDATA[Payment Method]]></string>
+	<string name="_bx_payment_2co_payment_method_dsc"><![CDATA[This will set the default selection on the payment method step during the checkout process]]></string>
+	<string name="_bx_payment_2co_payment_method_cc"><![CDATA[Credit Card(CC)]]></string>
+	<string name="_bx_payment_2co_payment_method_ppi"><![CDATA[PayPal(PPI)]]></string>
+	<string name="_bx_payment_2co_secret_word_cpt"><![CDATA[Secret Word]]></string>
+	<string name="_bx_payment_2co_secret_word_dsc"><![CDATA[Secret word is used to calculate a check hash]]></string>
+	<string name="_bx_payment_2co_return_url_cpt"><![CDATA[Data return URL]]></string>
+	<string name="_bx_payment_2co_err_no_vendor_given"><![CDATA[Unknown vendor]]></string>
+	<string name="_bx_payment_2co_err_no_data_given"><![CDATA[No data given.]]></string>
+	<string name="_bx_payment_2co_err_wrong_key"><![CDATA[Check key is incorrect.]]></string>
+	<string name="_bx_payment_2co_err_wrong_payment"><![CDATA[Wrong payment amount.]]></string>
+	<string name="_bx_payment_2co_err_already_processed"><![CDATA[This payment has already processed]]></string>
+	<string name="_bx_payment_2co_msg_verified"><![CDATA[Successfully verified.]]></string>
+	<string name="_bx_payment_txt_cart_2checkout"><![CDATA[2Checkout]]></string>
+
+	<!-- BitPay payment provider -->
+	<string name="_bx_payment_bp_cpt"><![CDATA[BitPay]]></string>
+	<string name="_bx_payment_bp_dsc"><![CDATA[BitPay payment provider]]></string>
+	<string name="_bx_payment_bp_active_cpt"><![CDATA[Active]]></string>
+	<string name="_bx_payment_bp_active_dsc"><![CDATA[Activate(Deactivate) provider]]></string>
+	<string name="_bx_payment_bp_active_err"><![CDATA[You should have SSL certificate (HTTPS connection) to activate and use BitPay on your site.]]></string>
+	<string name="_bx_payment_bp_api_key_cpt"><![CDATA[API Key]]></string>
+	<string name="_bx_payment_bp_api_key_dsc"><![CDATA[Your merchant BitPay API Access key]]></string>
+	<string name="_bx_payment_bp_transaction_speed_cpt"><![CDATA[Transaction speed]]></string>
+	<string name="_bx_payment_bp_transaction_speed_dsc"><![CDATA[Determines when an invoice is confirmed]]></string>
+	<string name="_bx_payment_bp_transaction_speed_high"><![CDATA[High]]></string>
+	<string name="_bx_payment_bp_transaction_speed_medium"><![CDATA[Medium]]></string>
+	<string name="_bx_payment_bp_transaction_speed_low"><![CDATA[Low]]></string>
+	<string name="_bx_payment_bp_full_notifications_cpt"><![CDATA[Enable full notifications]]></string>
+	<string name="_bx_payment_bp_full_notifications_dsc"><![CDATA[When enabled notifications will be sent on every status change, otherwise when an invoice is confirmed only.]]></string>
+	<string name="_bx_payment_bp_notification_email_cpt"><![CDATA[Notification Email]]></string>
+	<string name="_bx_payment_bp_notification_email_dsc"><![CDATA[Email where invoice update notifications will be sent. Leave empty to disable.]]></string>
+	<string name="_bx_payment_bp_err_posdata_exceed_limit"><![CDATA[The value of posData exceeds limit. Contact with administrator.]]></string>
+	<string name="_bx_payment_bp_err_no_data_given"><![CDATA[No data given.]]></string>
+	<string name="_bx_payment_bp_err_no_vendor_given"><![CDATA[Unknown vendor.]]></string>
+	<string name="_bx_payment_bp_err_incorrect_data"><![CDATA[Incorrect order data.]]></string>
+	<string name="_bx_payment_bp_err_wrong_amount"><![CDATA[Wrong payment amount.]]></string>
+	<string name="_bx_payment_bp_err_already_processed"><![CDATA[This payment has already processed.]]></string>
+	<string name="_bx_payment_bp_err_no_confirmation_given"><![CDATA[Payment is waiting for full confirmation from the Bitcoin network.]]></string>
+	<string name="_bx_payment_bp_msg_checkout_finished"><![CDATA[A payment has been confirmed. Once the transaction is fully confirmed on the Bitcoin network, the payment will be processed automatically here. Then the purchased product(s) will be displayed in shopping cart history.<br /><b>Note.</b> If it doesn't happen for some reason, you need to contact with product\'s vendor and provide him with your payment's details. He'll be able to process payment manually if the payment was received.]]></string>
+	<string name="_bx_payment_txt_cart_bitpay"><![CDATA[BitPay]]></string>
+</resources>

+ 39 - 0
modules/boonex/payment/install/sql/disable.sql

@@ -0,0 +1,39 @@
+SET @sName = 'bx_payment';
+
+
+-- OPTIONS
+SET @iTypeId = (SELECT `ID` FROM `sys_options_types` WHERE `name` = @sName LIMIT 1);
+SET @iCategId = (SELECT `ID` FROM `sys_options_categories` WHERE `type_id` = @iTypeId LIMIT 1);
+DELETE FROM `sys_options` WHERE `category_id` = @iCategId;
+DELETE FROM `sys_options_categories` WHERE `type_id` = @iTypeId;
+DELETE FROM `sys_options_types` WHERE `id` = @iTypeId;
+
+
+-- PAGES & BLOCKS
+DELETE FROM `sys_objects_page` WHERE `module` = @sName;
+DELETE FROM `sys_pages_blocks` WHERE `module` = @sName;
+
+
+-- MENU
+DELETE FROM `sys_objects_menu` WHERE `module` = @sName;
+DELETE FROM `sys_menu_sets` WHERE `module` = @sName;
+DELETE FROM `sys_menu_items` WHERE `module` = @sName;
+
+
+-- ALERTS
+SET @iHandlerId = (SELECT `id` FROM `sys_alerts_handlers` WHERE `name` = @sName LIMIT 1);
+DELETE FROM `sys_alerts` WHERE `handler_id` = @iHandlerId;
+DELETE FROM `sys_alerts_handlers` WHERE `id` = @iHandlerId LIMIT 1;
+
+
+-- ACL
+DELETE `sys_acl_actions`, `sys_acl_matrix` FROM `sys_acl_actions`, `sys_acl_matrix` WHERE `sys_acl_matrix`.`IDAction` = `sys_acl_actions`.`ID` AND `sys_acl_actions`.`Module` = @sName;
+DELETE FROM `sys_acl_actions` WHERE `Module` = @sName;
+
+
+-- EMAIL TEMPLATES
+DELETE FROM `sys_email_templates` WHERE `Module` = @sName;
+
+
+-- PAYMENTS
+DELETE FROM `sys_objects_payments` WHERE `object` = @sName;

+ 115 - 0
modules/boonex/payment/install/sql/enable.sql

@@ -0,0 +1,115 @@
+SET @sName = 'bx_payment';
+
+
+-- OPTIONS
+SET @iTypeOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_options_types` WHERE `group` = 'modules');
+INSERT INTO `sys_options_types`(`group`, `name`, `caption`, `icon`, `order`) VALUES 
+('modules', @sName, '_bx_payment', 'bx_payment@modules/boonex/payment/|std-mi.png', @iTypeOrder + 1);
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, @sName, '_bx_payment', 1);
+SET @iCategoryId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_params`, `check_error`, `extra`, `order`) VALUES
+('bx_payment_default_currency_code', 'USD', @iCategoryId, '_bx_payment_option_default_currency_code', 'select', 'Avail', '', '_bx_payment_option_err_default_currency_code', 'AUD,CAD,EUR,GBP,USD,YEN', 0),
+('bx_payment_default_currency_sign', '&#36;', @iCategoryId, '_bx_payment_option_default_currency_sign', 'digit', 'Avail', '', '_bx_payment_option_err_default_currency_sign', '', 1),
+('bx_payment_site_admin', '', @iCategoryId, '_bx_payment_option_site_admin', 'select', '', '', '', 'a:2:{s:6:"module";s:10:"bx_payment";s:6:"method";s:22:"get_options_site_admin";}', 2);
+
+
+-- PAGES & BLOCKS
+INSERT INTO `sys_objects_page`(`object`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `uri`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES
+('bx_payment_join', '_bx_payment_page_title_sys_join', '_bx_payment_page_title_join', @sName, 5, 2147483647, 1, 'payment-join', 'page.php?i=payment-join', '', '', '', 0, 1, 0, 'BxPaymentPageJoin', 'modules/boonex/payment/classes/BxPaymentPageJoin.php'),
+('bx_payment_cart', '_bx_payment_page_title_sys_cart', '_bx_payment_page_title_cart', @sName, 5, 2147483647, 1, 'payment-cart', 'page.php?i=payment-cart', '', '', '', 0, 1, 0, 'BxPaymentPageCart', 'modules/boonex/payment/classes/BxPaymentPageCart.php'),
+('bx_payment_history', '_bx_payment_page_title_sys_history', '_bx_payment_page_title_history', @sName, 5, 2147483647, 1, 'payment-history', 'page.php?i=payment-history', '', '', '', 0, 1, 0, 'BxPaymentPageHistory', 'modules/boonex/payment/classes/BxPaymentPageHistory.php'),
+('bx_payment_orders', '_bx_payment_page_title_sys_orders', '_bx_payment_page_title_orders', @sName, 5, 2147483647, 1, 'payment-orders', 'page.php?i=payment-orders', '', '', '', 0, 1, 0, 'BxPaymentPageOrders', 'modules/boonex/payment/classes/BxPaymentPageOrders.php'),
+('bx_payment_details', '_bx_payment_page_title_sys_details', '_bx_payment_page_title_details', @sName, 5, 2147483647, 1, 'payment-details', 'page.php?i=payment-details', '', '', '', 0, 1, 0, 'BxPaymentPageDetails', 'modules/boonex/payment/classes/BxPaymentPageDetails.php');
+
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_payment_join', 1, @sName, '_bx_payment_page_block_title_join', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:10:"bx_payment";s:6:"method";s:14:"get_block_join";s:6:"params";a:0:{}s:5:"class";s:4:"Join";}', 0, 0, 1),
+
+('bx_payment_cart', 1, @sName, '_bx_payment_page_block_title_cart', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:10:"bx_payment";s:6:"method";s:14:"get_block_cart";s:6:"params";a:0:{}s:5:"class";s:4:"Cart";}', 0, 0, 1),
+
+('bx_payment_history', 1, @sName, '_bx_payment_page_block_title_history', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:10:"bx_payment";s:6:"method";s:22:"get_block_cart_history";s:6:"params";a:0:{}s:5:"class";s:4:"Cart";}', 0, 0, 1),
+
+('bx_payment_orders', 1, @sName, '_bx_payment_page_block_title_orders', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:10:"bx_payment";s:6:"method";s:16:"get_block_orders";s:6:"params";a:0:{}s:5:"class";s:6:"Orders";}', 0, 0, 1),
+
+('bx_payment_details', 1, @sName, '_bx_payment_page_block_title_details', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:10:"bx_payment";s:6:"method";s:17:"get_block_details";s:6:"params";a:0:{}s:5:"class";s:7:"Details";}', 0, 0, 1);
+
+
+-- MENUS
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_payment_menu_cart_submenu', '_bx_payment_menu_title_cart_submenu', 'bx_payment_menu_cart_submenu', 'bx_payment', 6, 0, 1, '', '');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_payment_menu_cart_submenu', 'bx_payment', '_bx_payment_menu_set_title_cart_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES 
+('bx_payment_menu_cart_submenu', 'bx_payment', 'cart', '_bx_payment_menu_item_title_system_cart', '_bx_payment_menu_item_title_cart', 'page.php?i=payment-cart', '', '_self', '', '', '', 2147483647, 1, 0, 1, 1),
+('bx_payment_menu_cart_submenu', 'bx_payment', 'cart-history', '_bx_payment_menu_item_title_system_cart_history', '_bx_payment_menu_item_title_cart_history', 'page.php?i=payment-history', '', '_self', '', '', '', 2147483647, 1, 0, 1, 2);
+
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_payment_menu_orders_submenu', '_bx_payment_menu_title_orders_submenu', 'bx_payment_menu_orders_submenu', 'bx_payment', 6, 0, 1, '', '');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_payment_menu_orders_submenu', 'bx_payment', '_bx_payment_menu_set_title_orders_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `editable`, `order`) VALUES 
+('bx_payment_menu_orders_submenu', 'bx_payment', 'orders-processed', '_bx_payment_menu_item_title_system_orders_processed', '_bx_payment_menu_item_title_orders_processed', 'page.php?i=payment-orders&type=processed', '', '_self', '', '', '', 2147483647, 1, 0, 1, 1),
+('bx_payment_menu_orders_submenu', 'bx_payment', 'orders-pending', '_bx_payment_menu_item_title_system_orders_pending', '_bx_payment_menu_item_title_orders_pending', 'page.php?i=payment-orders&type=pending', '', '_self', '', '', '', 2147483647, 1, 0, 1, 2),
+('bx_payment_menu_orders_submenu', 'bx_payment', 'details', '_bx_payment_menu_item_title_system_details', '_bx_payment_menu_item_title_details', 'page.php?i=payment-details', '', '_self', '', '', '', 2147483647, 1, 0, 1, 3);
+
+
+-- ALERTS
+INSERT INTO `sys_alerts_handlers`(`name`, `class`, `file`, `service_call`) VALUES 
+(@sName, 'BxPaymentResponse', 'modules/boonex/payment/classes/BxPaymentResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('profile', 'join', @iHandler),
+('profile', 'delete', @iHandler);
+
+
+-- ACL
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+(@sName, 'purchase', NULL, '_bx_payment_acl_action_purchase', '', 1, 3);
+SET @iIdActionPurchase = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+(@sName, 'sell', NULL, '_bx_payment_acl_action_sell', '', 1, 3);
+SET @iIdActionSell = LAST_INSERT_ID();
+
+SET @iUnauthenticated = 1;
+SET @iAccount = 2;
+SET @iStandard = 3;
+SET @iUnconfirmed = 4;
+SET @iPending = 5;
+SET @iSuspended = 6;
+SET @iModerator = 7;
+SET @iAdministrator = 8;
+SET @iPremium = 9;
+
+INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
+
+-- purchase
+(@iStandard, @iIdActionPurchase),
+(@iModerator, @iIdActionPurchase),
+(@iAdministrator, @iIdActionPurchase),
+(@iPremium, @iIdActionPurchase),
+
+-- sell
+(@iStandard, @iIdActionSell),
+(@iModerator, @iIdActionSell),
+(@iAdministrator, @iIdActionSell),
+(@iPremium, @iIdActionSell);
+
+
+-- EMAIL TEMPLATES
+INSERT INTO `sys_email_templates` (`Module`, `NameSystem`, `Name`, `Subject`, `Body`) VALUES 
+(@sName, '_bx_payment_et_txt_name_paid_need_join', 'bx_payment_paid_need_join', '_bx_payment_et_txt_subject_paid_need_join', '_bx_payment_et_txt_body_paid_need_join');
+
+
+-- PAYMENTS
+INSERT INTO `sys_objects_payments` (`object`, `title`, `uri`) VALUES
+(@sName, '_bx_payment', 'payment');

+ 227 - 0
modules/boonex/payment/install/sql/install.sql

@@ -0,0 +1,227 @@
+SET @sName = 'bx_payment';
+
+
+-- TABLES
+CREATE TABLE IF NOT EXISTS `bx_payment_providers` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(64) NOT NULL default '',
+  `caption` varchar(128) NOT NULL default '',
+  `description` text NOT NULL default '',
+  `option_prefix` varchar(32) NOT NULL default '',
+  `for_visitor` tinyint(4) NOT NULL default '0',
+  `class_name` varchar(128) NOT NULL default '',
+  `class_file` varchar(255) NOT NULL  default '',
+  PRIMARY KEY(`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `bx_payment_providers_options` (
+  `id` int(11) NOT NULL auto_increment,
+  `provider_id` varchar(64) NOT NULL default '',
+  `name` varchar(64) NOT NULL default '',
+  `type` varchar(64) NOT NULL default 'text',
+  `caption` varchar(255) NOT NULL default '',
+  `description` text NOT NULL default '',
+  `extra` varchar(255) NOT NULL default '',
+  `check_type` varchar(64) NOT NULL default '',
+  `check_params` varchar(128) NOT NULL default '',
+  `check_error` varchar(128) NOT NULL default '',
+  `order` tinyint(4) NOT NULL default '0',
+  PRIMARY KEY(`id`),
+  UNIQUE KEY `name`(`name`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `bx_payment_user_values` (
+  `user_id` int(11) NOT NULL default '0',
+  `option_id` int(11) NOT NULL default '0',  
+  `value` varchar(255) NOT NULL default '',
+  UNIQUE KEY `value`(`user_id`, `option_id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `bx_payment_cart` (
+  `client_id` int(11) NOT NULL default '0',
+  `items` text NOT NULL default '',
+  PRIMARY KEY(`client_id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `bx_payment_transactions` (
+  `id` int(11) NOT NULL auto_increment,
+  `pending_id` int(11) NOT NULL default '0',
+  `order_id` varchar(16) NOT NULL default '',
+  `client_id` int(11) NOT NULL default '0',
+  `seller_id` int(11) NOT NULL default '0',
+  `module_id` int(11) NOT NULL default '0',  
+  `item_id` int(11) NOT NULL default '0',
+  `item_count` int(11) NOT NULL default '0',
+  `amount` float NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY(`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `bx_payment_transactions_pending` (
+  `id` int(10) unsigned NOT NULL auto_increment,
+  `client_id` int(11) NOT NULL default '0',
+  `seller_id` int(11) NOT NULL default '0',
+  `items` text NOT NULL default '',
+  `amount` float NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `error_code` varchar(16) NOT NULL default '',
+  `error_msg` varchar(255) NOT NULL default '',
+  `provider` varchar(32) NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  `processed` tinyint(4) NOT NULL default '0',
+  PRIMARY KEY(`id`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS `bx_payment_modules` (
+  `id` int(11) NOT NULL auto_increment,
+  `name` varchar(32) NOT NULL default '',
+  PRIMARY KEY(`id`),
+  UNIQUE KEY `uri`(`name`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+
+-- PayPal payment provider
+INSERT INTO `bx_payment_providers`(`name`, `caption`, `description`, `option_prefix`, `for_visitor`, `class_name`) VALUES
+('paypal', '_bx_payment_pp_cpt', '_bx_payment_pp_dsc', 'pp_', 1, 'BxPaymentProviderPayPal');
+SET @iProviderId = LAST_INSERT_ID();
+
+INSERT INTO `bx_payment_providers_options`(`provider_id`, `name`, `type`, `caption`, `description`, `extra`, `check_type`, `check_params`, `check_error`, `order`) VALUES
+(@iProviderId, 'pp_active', 'checkbox', '_bx_payment_pp_active_cpt', '_bx_payment_pp_active_dsc', '', '', '', '', 1),
+(@iProviderId, 'pp_mode', 'select', '_bx_payment_pp_mode_cpt', '_bx_payment_pp_mode_dsc', '1|_bx_payment_pp_mode_live,2|_bx_payment_pp_mode_test', '', '', '', 2),
+(@iProviderId, 'pp_business', 'text', '_bx_payment_pp_business_cpt', '_bx_payment_pp_business_dsc', '', '', '', '', 3),
+(@iProviderId, 'pp_prc_type', 'select', '_bx_payment_pp_prc_type_cpt', '_bx_payment_pp_prc_type_dsc', '1|_bx_payment_pp_prc_type_direct,2|_bx_payment_pp_prc_type_pdt,3|_bx_payment_pp_prc_type_ipn', '', '', '', 4),
+(@iProviderId, 'pp_cnt_type', 'select', '_bx_payment_pp_cnt_type_cpt', '_bx_payment_pp_cnt_type_dsc', '1|_bx_payment_pp_cnt_type_ssl,2|_bx_payment_pp_cnt_type_http', '', '', '', 5),
+(@iProviderId, 'pp_token', 'text', '_bx_payment_pp_token_cpt', '_bx_payment_pp_token_dsc', '', '', '', '', 6),
+(@iProviderId, 'pp_sandbox', 'text', '_bx_payment_pp_sandbox_cpt', '_bx_payment_pp_sandbox_dsc', '', '', '', '', 7),
+(@iProviderId, 'pp_return_url', 'value', '_bx_payment_pp_return_url_cpt', '', '', '', '', '', 8);
+
+
+-- 2Checkout payment provider
+INSERT INTO `bx_payment_providers`(`name`, `caption`, `description`, `option_prefix`, `for_visitor`, `class_name`) VALUES
+('2checkout', '_bx_payment_2co_cpt', '_bx_payment_2co_dsc', '2co_', 1, 'BxPaymentProvider2Checkout');
+SET @iProviderId = LAST_INSERT_ID();
+
+INSERT INTO `bx_payment_providers_options`(`provider_id`, `name`, `type`, `caption`, `description`, `extra`, `check_type`, `check_params`, `check_error`, `order`) VALUES
+(@iProviderId, '2co_active', 'checkbox', '_bx_payment_2co_active_cpt', '_bx_payment_2co_active_dsc', '', '', '', '', 1),
+(@iProviderId, '2co_mode', 'select', '_bx_payment_2co_mode_cpt', '_bx_payment_2co_mode_dsc', '1|_bx_payment_2co_mode_live,2|_bx_payment_2co_mode_test', '', '', '', 2),
+(@iProviderId, '2co_account_id', 'text', '_bx_payment_2co_account_id_cpt', '_bx_payment_2co_account_id_dsc', '', '', '', '', 3),
+(@iProviderId, '2co_payment_method', 'select', '_bx_payment_2co_payment_method_cpt', '_bx_payment_2co_payment_method_dsc', 'CC|_bx_payment_2co_payment_method_cc,PPI|_bx_payment_2co_payment_method_ppi', '', '', '', 4),
+(@iProviderId, '2co_secret_word', 'text', '_bx_payment_2co_secret_word_cpt', '_bx_payment_2co_secret_word_dsc', '', '', '', '', 5),
+(@iProviderId, '2co_return_url', 'value', '_bx_payment_2co_return_url_cpt', '', '', '', '', '', 6);
+
+
+-- BitPay payment provider
+INSERT INTO `bx_payment_providers`(`name`, `caption`, `description`, `option_prefix`, `for_visitor`, `class_name`) VALUES
+('bitpay', '_bx_payment_bp_cpt', '_bx_payment_bp_dsc', 'bp_', 0, 'BxPaymentProviderBitPay');
+SET @iProviderId = LAST_INSERT_ID();
+
+INSERT INTO `bx_payment_providers_options`(`provider_id`, `name`, `type`, `caption`, `description`, `extra`, `check_type`, `check_params`, `check_error`, `order`) VALUES
+(@iProviderId, 'bp_active', 'checkbox', '_bx_payment_bp_active_cpt', '_bx_payment_bp_active_dsc', '', 'https', '', '_bx_payment_bp_active_err', 1),
+(@iProviderId, 'bp_api_key', 'text', '_bx_payment_bp_api_key_cpt', '_bx_payment_bp_api_key_dsc', '', '', '', '', 2),
+(@iProviderId, 'bp_transaction_speed', 'select', '_bx_payment_bp_transaction_speed_cpt', '_bx_payment_bp_transaction_speed_dsc', 'high|_bx_payment_bp_transaction_speed_high,medium|_bx_payment_bp_transaction_speed_medium,low|_bx_payment_bp_transaction_speed_low', '', '', '', 3),
+(@iProviderId, 'bp_full_notifications', 'checkbox', '_bx_payment_bp_full_notifications_cpt', '_bx_payment_bp_full_notifications_dsc', '', '', '', '', 4),
+(@iProviderId, 'bp_notification_email', 'text', '_bx_payment_bp_notification_email_cpt', '_bx_payment_bp_notification_email_dsc', '', '', '', '', 5);
+
+
+-- GRIDS
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `override_class_name`, `override_class_file`) VALUES
+('bx_payment_grid_orders_history', 'Sql', 'SELECT `tt`.`id` AS `id`, `tt`.`seller_id` AS `seller_id`, `ttp`.`order` AS `transaction`, `tt`.`order_id` AS `license`, `tt`.`amount` AS `amount`, `tt`.`date` AS `date` FROM `bx_payment_transactions` AS `tt` LEFT JOIN `bx_payment_transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id` WHERE 1 ', 'bx_payment_transactions', 'id', 'date', '', '', 100, NULL, 'start', '', 'ttp`.`order,tt`.`order_id,tt`.`amount,tt`.`date', '', 'auto', '', '', 'BxPaymentGridHistory', 'modules/boonex/payment/classes/BxPaymentGridHistory.php'),
+('bx_payment_grid_orders_processed', 'Sql', 'SELECT `tt`.`id` AS `id`, `tt`.`client_id` AS `client_id`, `tt`.`seller_id` AS `seller_id`, `tt`.`module_id` AS `module_id`, `tt`.`item_id` AS `item_id`, `tt`.`item_count` AS `item_count`, `ttp`.`order` AS `transaction`, `ttp`.`error_msg` AS `error_msg`, `ttp`.`provider` AS `provider`, `tt`.`order_id` AS `license`, `tt`.`amount` AS `amount`, `tt`.`date` AS `date` FROM `bx_payment_transactions` AS `tt` LEFT JOIN `bx_payment_transactions_pending` AS `ttp` ON `tt`.`pending_id`=`ttp`.`id` WHERE 1 ', 'bx_payment_transactions', 'id', 'date', '', '', 100, NULL, 'start', '', 'ttp`.`order,tt`.`order_id,tt`.`amount,tt`.`date', '', 'auto', '', '', 'BxPaymentGridProcessed', 'modules/boonex/payment/classes/BxPaymentGridProcessed.php'),
+('bx_payment_grid_orders_pending', 'Sql', 'SELECT `tt`.`id` AS `id`, `tt`.`client_id` AS `client_id`, `tt`.`seller_id` AS `seller_id`, `tt`.`items` AS `items`, `tt`.`amount` AS `amount`, `tt`.`order` AS `transaction`, `tt`.`error_msg` AS `error_msg`, `tt`.`provider` AS `provider`, `tt`.`date` AS `date` FROM `bx_payment_transactions_pending` AS `tt` WHERE 1 AND (ISNULL(`tt`.`order`) OR (NOT ISNULL(`tt`.`order`) AND `tt`.`error_code`<>''0'')) ', 'bx_payment_transactions_pending', 'id', 'date', '', '', 100, NULL, 'start', '', 'tt`.`order,tt`.`amount,tt`.`date', '', 'auto', '', '', 'BxPaymentGridPending', 'modules/boonex/payment/classes/BxPaymentGridPending.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_payment_grid_orders_history', 'seller_id', '_bx_payment_grid_orders_field_seller_id', '24%', 0, '20', '', 1),
+('bx_payment_grid_orders_history', 'transaction', '_bx_payment_grid_orders_field_transaction', '22%', 0, '18', '', 2),
+('bx_payment_grid_orders_history', 'license', '_bx_payment_grid_orders_field_license', '22%', 0, '18', '', 3),
+('bx_payment_grid_orders_history', 'amount', '_bx_payment_grid_orders_field_amount', '10%', 1, '10', '', 4),
+('bx_payment_grid_orders_history', 'date', '_bx_payment_grid_orders_field_date', '10%', 0, '10', '', 5),
+('bx_payment_grid_orders_history', 'actions', '', '12%', 0, '', '', 6),
+
+('bx_payment_grid_orders_processed', 'checkbox', '', '2%', 0, '', '', 1),
+('bx_payment_grid_orders_processed', 'client_id', '_bx_payment_grid_orders_field_client_id', '22%', 0, '18', '', 2),
+('bx_payment_grid_orders_processed', 'transaction', '_bx_payment_grid_orders_field_transaction', '20%', 0, '18', '', 3),
+('bx_payment_grid_orders_processed', 'license', '_bx_payment_grid_orders_field_license', '20%', 0, '18', '', 4),
+('bx_payment_grid_orders_processed', 'amount', '_bx_payment_grid_orders_field_amount', '10%', 1, '10', '', 5),
+('bx_payment_grid_orders_processed', 'date', '_bx_payment_grid_orders_field_date', '10%', 0, '10', '', 6),
+('bx_payment_grid_orders_processed', 'actions', '', '16%', 0, '', '', 7),
+
+('bx_payment_grid_orders_pending', 'checkbox', '', '2%', 0, '', '', 1),
+('bx_payment_grid_orders_pending', 'client_id', '_bx_payment_grid_orders_field_client_id', '25%', 0, '25', '', 2),
+('bx_payment_grid_orders_pending', 'transaction', '_bx_payment_grid_orders_field_transaction', '25%', 0, '25', '', 3),
+('bx_payment_grid_orders_pending', 'amount', '_bx_payment_grid_orders_field_amount', '14%', 1, '14', '', 4),
+('bx_payment_grid_orders_pending', 'date', '_bx_payment_grid_orders_field_date', '14%', 0, '14', '', 5),
+('bx_payment_grid_orders_pending', 'actions', '', '20%', 0, '', '', 6);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_payment_grid_orders_history', 'single', 'view_order', '_bx_payment_grid_orders_action_view_order', 'ellipsis-h', 1, 0, 1),
+
+('bx_payment_grid_orders_processed', 'independent', 'add', '_bx_payment_grid_orders_action_add', '', 0, 0, 1),
+('bx_payment_grid_orders_processed', 'bulk', 'cancel', '_bx_payment_grid_orders_action_cancel', '', 0, 1, 1),
+('bx_payment_grid_orders_processed', 'single', 'view_order', '_bx_payment_grid_orders_action_view_order', 'ellipsis-h', 1, 0, 1),
+('bx_payment_grid_orders_processed', 'single', 'cancel', '_bx_payment_grid_orders_action_cancel', 'times', 1, 1, 2),
+
+('bx_payment_grid_orders_pending', 'bulk', 'cancel', '_bx_payment_grid_orders_action_cancel', '', 0, 1, 1),
+('bx_payment_grid_orders_pending', 'single', 'view_order', '_bx_payment_grid_orders_action_view_order', 'ellipsis-h', 1, 0, 1),
+('bx_payment_grid_orders_pending', 'single', 'process', '_bx_payment_grid_orders_action_process', 'refresh', 1, 0, 2),
+('bx_payment_grid_orders_pending', 'single', 'cancel', '_bx_payment_grid_orders_action_cancel', 'times', 1, 1, 3);
+
+
+-- FORMS
+INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_payment_form_pendings', @sName, '_bx_payment_form_pendings_form', '', '', 'do_submit', 'bx_payment_transactions_pending', 'id', '', '', '', 0, 1, 'BxPaymentFormView', 'modules/boonex/payment/classes/BxPaymentFormView.php'),
+('bx_payment_form_processed', @sName, '_bx_payment_form_processed_form', '', '', 'do_submit', 'bx_payment_transactions', 'id', '', '', '', 0, 1, 'BxPaymentFormView', 'modules/boonex/payment/classes/BxPaymentFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_payment_form_pendings_process', @sName, 'bx_payment_form_pendings', '_bx_payment_form_pendings_display_process', 0),
+('bx_payment_form_processed_add', @sName, 'bx_payment_form_processed', '_bx_payment_form_processed_display_add', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_payment_form_pendings', @sName, 'id', '0', '', 0, 'hidden', '_bx_payment_form_pendings_input_id_sys', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_payment_form_pendings', @sName, 'seller_id', '0', '', 0, 'hidden', '_bx_payment_form_pendings_input_seller_id_sys', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_payment_form_pendings', @sName, 'order', '', '', 0, 'text', '_bx_payment_form_pendings_input_order_sys', '_bx_payment_form_pendings_input_order', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_payment_form_pendings_input_order_err', 'Xss', '', 0, 0),
+('bx_payment_form_pendings', @sName, 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_payment_form_pendings', @sName, 'do_submit', '_bx_payment_form_pendings_input_process', '', 0, 'submit', '_bx_payment_form_pendings_input_process_sys', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_payment_form_pendings', @sName, 'do_cancel', '_bx_payment_form_pendings_input_cancel', '', 0, 'button', '_bx_payment_form_pendings_input_cancel_sys', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0),
+
+('bx_payment_form_processed', @sName, 'id', '0', '', 0, 'hidden', '_bx_payment_form_processed_input_id_sys', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_payment_form_processed', @sName, 'seller_id', '0', '', 0, 'hidden', '_bx_payment_form_processed_input_seller_id_sys', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_payment_form_processed', @sName, 'client_id', '0', '', 0, 'hidden', '_bx_payment_form_processed_input_client_id_sys', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_payment_form_processed', @sName, 'client', '0', '', 0, 'text', '_bx_payment_form_processed_input_client_sys', '_bx_payment_form_processed_input_client', '', 0, 0, 0, '', '', '', 'Avail', '', '_bx_payment_form_processed_input_client_err', 'Xss', '', 0, 0),
+('bx_payment_form_processed', @sName, 'order', '0', '', 0, 'text', '_bx_payment_form_processed_input_order_sys', '_bx_payment_form_processed_input_order', '', 0, 0, 0, '', '', '', 'Avail', '', '_bx_payment_form_processed_input_order_err', 'Xss', '', 0, 0),
+('bx_payment_form_processed', @sName, 'module_id', '0', '', 0, 'select', '_bx_payment_form_processed_input_module_id_sys', '_bx_payment_form_processed_input_module_id', '', 0, 0, 0, '', '', '', 'Avail', '', '_bx_payment_form_processed_input_module_id_err', 'Int', '', 0, 0),
+('bx_payment_form_processed', @sName, 'items', '0', '', 0, 'custom', '_bx_payment_form_processed_input_items_sys', '_bx_payment_form_processed_input_items', '', 0, 0, 0, '', '', '', '', '', '_bx_payment_form_processed_input_items_err', '', '', 0, 0),
+('bx_payment_form_processed', @sName, 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_payment_form_processed', @sName, 'do_submit', '_bx_payment_form_processed_input_add', '', 0, 'submit', '_bx_payment_form_processed_input_add_sys', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_payment_form_processed', @sName, 'do_cancel', '_bx_payment_form_processed_input_cancel', '', 0, 'button', '_bx_payment_form_processed_input_cancel_sys', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_payment_form_pendings_process', 'id', 2147483647, 1, 1),
+('bx_payment_form_pendings_process', 'seller_id', 2147483647, 1, 2),
+('bx_payment_form_pendings_process', 'order', 2147483647, 1, 3),
+('bx_payment_form_pendings_process', 'controls', 2147483647, 1, 4),
+('bx_payment_form_pendings_process', 'do_submit', 2147483647, 1, 5),
+('bx_payment_form_pendings_process', 'do_cancel', 2147483647, 1, 6),
+
+('bx_payment_form_processed_add', 'id', 2147483647, 0, 1),
+('bx_payment_form_processed_add', 'seller_id', 2147483647, 1, 2),
+('bx_payment_form_processed_add', 'client_id', 2147483647, 1, 3),
+('bx_payment_form_processed_add', 'client', 2147483647, 1, 4),
+('bx_payment_form_processed_add', 'order', 2147483647, 1, 5),
+('bx_payment_form_processed_add', 'module_id', 2147483647, 1, 6),
+('bx_payment_form_processed_add', 'items', 2147483647, 1, 7),
+('bx_payment_form_processed_add', 'controls', 2147483647, 1, 8),
+('bx_payment_form_processed_add', 'do_submit', 2147483647, 1, 9),
+('bx_payment_form_processed_add', 'do_cancel', 2147483647, 1, 10);
+
+
+-- STUDIO PAGE & WIDGET
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, @sName, '_bx_payment', '_bx_payment', 'bx_payment@modules/boonex/payment/|std-pi.png');
+SET @iPageId = LAST_INSERT_ID();
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT MAX(`order`) FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, @sName, '{url_studio}module.php?name=bx_payment', '', 'bx_payment@modules/boonex/payment/|std-wi.png', '_bx_payment', '', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_actions";s:6:"params";a:0:{}s:5:"class";s:18:"TemplStudioModules";}');
+INSERT INTO `sys_std_pages_widgets` (`page_id`, `widget_id`, `order`) VALUES
+(@iParentPageId, LAST_INSERT_ID(), IF(ISNULL(@iParentPageOrder), 1, @iParentPageOrder + 1));

+ 30 - 0
modules/boonex/payment/install/sql/uninstall.sql

@@ -0,0 +1,30 @@
+SET @sName = 'bx_payment';
+
+
+-- TABLES
+DROP TABLE IF EXISTS `bx_payment_providers`;
+DROP TABLE IF EXISTS `bx_payment_providers_options`;
+DROP TABLE IF EXISTS `bx_payment_user_values`;
+DROP TABLE IF EXISTS `bx_payment_cart`;
+DROP TABLE IF EXISTS `bx_payment_transactions`;
+DROP TABLE IF EXISTS `bx_payment_transactions_pending`;
+DROP TABLE IF EXISTS `bx_payment_modules`;
+
+
+-- GRIDS
+DELETE FROM `sys_objects_grid` WHERE `object` IN ('bx_payment_grid_orders_history', 'bx_payment_grid_orders_processed', 'bx_payment_grid_orders_pending');
+DELETE FROM `sys_grid_fields` WHERE `object` IN ('bx_payment_grid_orders_history', 'bx_payment_grid_orders_processed', 'bx_payment_grid_orders_pending');
+DELETE FROM `sys_grid_actions` WHERE `object` IN ('bx_payment_grid_orders_history', 'bx_payment_grid_orders_processed', 'bx_payment_grid_orders_pending');
+
+
+-- FORMS
+DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN  (SELECT `display_name` FROM `sys_form_displays` WHERE `module`=@sName);
+DELETE FROM `sys_form_inputs` WHERE `module`=@sName;
+DELETE FROM `sys_form_displays` WHERE `module`=@sName;
+DELETE FROM `sys_objects_form` WHERE `module`=@sName;
+
+
+-- STUDIO PAGE & WIDGET
+DELETE FROM `tp`, `tw`, `tpw`
+USING `sys_std_pages` AS `tp`, `sys_std_widgets` AS `tw`, `sys_std_pages_widgets` AS `tpw`
+WHERE `tp`.`id` = `tw`.`page_id` AND `tw`.`id` = `tpw`.`widget_id` AND `tp`.`name` = @sName;

+ 112 - 0
modules/boonex/payment/js/cart.js

@@ -0,0 +1,112 @@
+function BxPaymentCart(oOptions) {
+	this.init(oOptions);
+}
+
+BxPaymentCart.prototype.init = function(oOptions) {
+	if($.isEmptyObject(oOptions))
+		return;
+
+	this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oPmtCart' : oOptions.sObjName;
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+
+    this._sErrNothingSelected = '_bx_payment_err_nothing_selected';
+};
+
+BxPaymentCart.prototype.addToCart = function(iVendorId, iModuleId, iItemId, iItemCount, iNeedRedirect) {
+    var oDate = new Date();
+    var $this = this;
+
+    if(!(iNeedRedirect = parseInt(iNeedRedirect)))
+    	iNeedRedirect = 0;
+
+    $.post(
+        this._sActionsUrl + 'add_to_cart/' + iVendorId + '/' + iModuleId + '/' + iItemId + '/' + iItemCount + '/',
+        {
+            _t:oDate.getTime()
+        },
+        function(oData){
+        	alert(oData.message);
+
+            if(oData.code == 0) {
+            	/*
+            	 * TODO: Display counter somewhere if it's needed.
+            	 * 
+            	$('#bx-menu-object-sys_account_notifications').html(oData.total_quantity);
+                $('#bx-payment-tbar-content').replaceWith(oData.content);
+                */
+
+            	if(iNeedRedirect == 1)
+            		window.location.href = $this._sActionsUrl + 'cart';
+            }
+        },
+        'json'
+    );
+};
+
+/**
+ * Isn't used yet.
+ */
+BxPaymentCart.prototype.deleteFromCart = function(iVendorId, iModuleId, iItemId) {
+    var oDate = new Date();
+    var $this = this;
+
+    $.post(
+        this._sActionsUrl + 'delete_from_cart/' + iVendorId + '/' + iModuleId + '/' + iItemId,
+        {
+            _t:oDate.getTime()
+        },
+        function(oData) {
+            alert(oData.message);
+
+            if(oData.code == 0) {
+            	/*
+            	 * TODO: Hide item if it's needed.
+            	 * 
+                $('#item-' + iVendorId + '-' + iModuleId + '-' + iItemId).bx_anim('hide', $this._sAnimationEffect, $this._iAnimationSpeed, function() {
+                        $(this).remove();
+                });
+                 */
+            }
+        },
+        'json'
+    );
+};
+
+/**
+ * Isn't used yet.
+ */
+BxPaymentCart.prototype.emptyCart = function(iVendorId) {
+    var oDate = new Date();
+    var $this = this;    
+
+    $.post(
+        this._sActionsUrl + 'empty_cart/' + iVendorId,
+        {
+            _t:oDate.getTime()
+        },
+        function(oData){
+            if(oData.code == 0) {
+            	/*
+            	 * TODO: Hide vendor from vendors's list if it's needed.
+            	 */
+            }
+            alert(oData.message);
+        },
+        'json'
+    );
+};
+
+BxPaymentCart.prototype.toggle = function(oElement) {
+	$(oElement).find('.sys-icon').toggleClass('caret-down').toggleClass('caret-right');
+    $(oElement).parents('.bx-payment-box:first').find('.bx-payment-box-cnt').bx_anim('toggle', 'slide', this._iAnimationSpeed);
+};
+
+BxPaymentCart.prototype.onSubmit = function(oForm) {
+    if($(oForm).find(":checkbox[name='items[]']:checked").length > 0)
+    	return true;
+
+    alert(_t(this._sErrNothingSelected));
+	return false;
+};

+ 56 - 0
modules/boonex/payment/js/orders.js

@@ -0,0 +1,56 @@
+function BxPaymentOrders(oOptions) {
+	this.init(oOptions);
+}
+
+BxPaymentOrders.prototype.init = function(oOptions) {
+	if($.isEmptyObject(oOptions))
+		return;
+
+	this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oPaymentOrders' : oOptions.sObjName;
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._iTimeout = (oOptions.iTimeout == undefined ? 5 : oOptions.iTimeout) * 1000;
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+};
+
+/*--- Manual Order function ---*/
+BxPaymentOrders.prototype.paOnShowPopup = function(oSelect) {
+	var $this = this;
+
+	$('#' + this._aHtmlIds['order_processed_client']).autocomplete({
+        source: this._sActionsUrl + 'get_clients/',
+        select: function(oEvent, oUi) {
+        	$('#' + $this._aHtmlIds['order_processed_client_id']).val(oUi.item.value);
+            $(this).val(oUi.item.label);
+
+            oEvent.preventDefault();
+        }
+    });
+};
+
+BxPaymentOrders.prototype.paOnSelectModule = function(oSelect) {
+    var oDate = new Date();
+
+    var oPopup = $('#' + this._aHtmlIds['order_processed_add']); 
+    var oItems = $('#' + this._aHtmlIds['order_processed_items']);
+
+    oPopup.bx_loading(true);
+
+    $.get(
+        this._sActionsUrl + 'get_items/' + parseInt($(oSelect).val()) + '/',
+        {
+            _t:oDate.getTime()
+        },
+        function(oData) {
+        	oPopup.bx_loading(false);
+
+        	if(oData.message != undefined)
+        		alert(oData.message);
+
+            if(oData.data != undefined)
+            	oItems.replaceWith(oData.data);
+        },
+        'json'
+    );
+};

+ 18 - 0
modules/boonex/payment/request.php

@@ -0,0 +1,18 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+bx_import('BxDolLanguages');
+
+BxDolRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

+ 4 - 0
modules/boonex/payment/template/add_to_cart.html

@@ -0,0 +1,4 @@
+<button class="bx-btn bx-btn-img bx-payment-add-to-cart bx-def-margin-sec-left-auto" onclick="javascript:__js_object__.addToCart(__vendor_id__, __module_id__, __item_id__, __item_count__, __need_redirect__)">
+    <i class="sys-icon shopping-cart"></i>__txt_add_to_cart__
+</button>
+__js_content__

+ 1 - 0
modules/boonex/payment/template/add_to_cart_js.html

@@ -0,0 +1 @@
+__js_object__.addToCart(__vendor_id__, __module_id__, __item_id__, __item_count__, __need_redirect__)

+ 43 - 0
modules/boonex/payment/template/cart.html

@@ -0,0 +1,43 @@
+<div class="bx-payment-box-wrp bx-clearfix">
+    <bx_repeat:vendors>
+        <div class="bx-payment-box bx-def-border bx-def-margin-top-auto">
+            <div class="bx-payment-box-cpt bx-def-padding-sec-left">
+                <div class="bx-payment-box-collapse">
+                	<a href="javascript:void(0)" onclick="javascript:__js_object__.toggle(this)">
+                		<i class="sys-icon caret-down"></i>
+                	</a>
+                </div>
+                <bx_if:show_link>
+                	<a href="__vendor_url__">__txt_shopping_cart__</a> (__items_count__) - __items_price__ __vendor_currency_code__
+                </bx_if:show_link>
+                <bx_if:show_text>__txt_shopping_cart__ (__items_count__) - __items_price__ __vendor_currency_code__</bx_if:show_text>
+            </div>
+            <div class="bx-payment-box-cnt bx-def-border-top">
+                <form id="items_from___vendor_id__" name="items_from___vendor_id__" method="POST" action="__process_url__" onsubmit="javascript:return __js_object__.onSubmit(this)">
+                    <input type="hidden" name="vendor_id" value="__vendor_id__" />
+                    <div class="bx-payment-vendor-icon">
+                    	<img src="__vendor_icon__" />
+                    </div>
+                    <div class="bx-payment-providers">
+                        <bx_repeat:providers>
+                            <div class="bx-payment-provider">
+                                <input type="radio" name="provider" value="__name__" __checked__ />
+                                <b>__caption__</b>
+                            </div>
+                        </bx_repeat:providers>
+                    </div>
+                    <div class="bx-payment-items bx-def-padding-sec">
+                        <bx_repeat:items>
+                            <div class="bx-payment-item">
+                                <input type="checkbox" name="items[]" value="__vendor_id_____module_id_____item_id_____item_quantity__" checked="checked" />
+                                <a href="__item_url__">__item_title__</a> (__item_quantity__) - <bx_if:show_price_paid>__item_price__ __vendor_currency_code__</bx_if:show_price_paid><bx_if:show_price_free><bx_text:_payment_txt_free /></bx_if:show_price_free>
+                            </div>
+                        </bx_repeat:items>
+                    </div>
+                    __control_panel__
+                </form>
+            </div>
+        </div>
+    </bx_repeat:vendors>
+</div>
+__js_content__

+ 60 - 0
modules/boonex/payment/template/css/cart.css

@@ -0,0 +1,60 @@
+/*--- Box Styles ---*/
+div.bx-payment-box-wrp {}
+div.bx-payment-box {
+    position:relative;
+    background-color: #f2f2ff;
+}
+div.bx-payment-box-cpt {
+    height: 28px;
+
+    background-color: #ececec;
+
+    line-height: 28px;
+}
+div.bx-payment-box-collapse {
+	position: absolute;
+
+	top: 5px;
+    right: 5px;
+    width: 16px;
+    height: 16px;
+
+	line-height: 16px;
+	text-align: center;
+}
+div.bx-payment-box-collapse a {
+	text-decoration: none;
+}
+div.bx-payment-box-cnt {
+    position: relative;
+    background-color: #f2f2ff;    
+}
+
+/*--- Cart Styles ---*/
+div.bx-payment-vendor-icon {
+    top: 10px;
+    left: 10px;
+    width: 32px;
+    height: 32px;
+
+    position: absolute;
+}
+div.bx-payment-providers {
+    margin: 10px 0 0 52px;
+    min-height: 32px;
+}
+div.bx-payment-provider {
+    padding: 2px 0px;
+}
+div.bx-payment-provider input {
+    margin: 0px;
+    padding: 0px;
+}
+
+div.bx-payment-item {
+    padding: 2px 0px;
+}
+div.bx-payment-item input {
+    margin: 0px;
+    padding: 0px;
+}

+ 68 - 0
modules/boonex/payment/template/css/orders.css

@@ -0,0 +1,68 @@
+iframe.hidden {
+    width: 0px;
+    height: 0px;
+    display: none;
+}
+
+/*--- 'Manual Order' and 'More' windows ---*/
+.ui-autocomplete.ui-front {
+	z-index: 1001;
+}
+div.bx-payment-pa-items {
+	max-height: 300px;
+	overflow-x: hidden;
+	overflow-y: auto;
+}
+div.bx-payment-pa-item {
+	width: 100%;
+	white-space: nowrap;
+}
+div.bx-payment-pa-item-chb,
+div.bx-payment-pa-item-title,
+div.bx-payment-pa-item-quantity {
+    position: relative;
+    display: inline-block;
+	vertical-align: middle;
+}
+div.bx-payment-pa-item-chb {
+    width: 4%;
+}
+div.bx-payment-pa-item-chb input {
+    margin: 0px;
+    padding: 0px;
+}
+div.bx-payment-pa-item-title {
+    width: 80%;
+}
+div.bx-payment-pa-item-title-cnt {
+	TEXT-OVERFLOW: ellipsis;
+    -o-text-overflow: ellipsis;
+    white-space: nowrap;
+	overflow: hidden;
+}
+div.bx-payment-pa-item-quantity {
+    width: 16%;
+}
+
+/*--- View Order ---*/
+div.bx-payment-view-order {
+    border-top-width: 0px;
+}
+div.bx-payment-vo-row {
+	white-space: nowrap;
+}
+div.bx-payment-vo-caption,
+div.bx-payment-vo-content,
+div.bx-payment-vo-items {
+    position: relative;
+    display: inline-block;
+	vertical-align: top;
+
+    overflow: hidden;
+}
+div.bx-payment-vo-caption {
+    width: 30%;
+}
+div.bx-payment-vo-content {
+    width: 70%;
+}

BIN
modules/boonex/payment/template/images/icons/std-mi.png


BIN
modules/boonex/payment/template/images/icons/std-pi.png


BIN
modules/boonex/payment/template/images/icons/std-si.png


BIN
modules/boonex/payment/template/images/icons/std-wi.png


+ 23 - 0
modules/boonex/payment/template/items.html

@@ -0,0 +1,23 @@
+<div id="__html_id__" class="bx-payment-pa-items">
+	<div class="bx-payment-pa-items-cnt">
+		<bx_repeat:items>
+		    <div class="bx-payment-pa-item bx-def-margin-thd-top-auto bx-clearfix">
+		        <div class="bx-payment-pa-item-chb">
+		            <input type="checkbox" name="items[]" value="__id__" />
+		            <input type="hidden" name="item-price-__id__" value="__price__" />
+		        </div><div class="bx-payment-pa-item-title">
+		        	<div class="bx-payment-pa-item-title-cnt bx-def-padding-sec-leftright">
+			            <bx_if:link>
+			                <a href="__url__" target="_blank" title="__title__">__title__</a>
+			            </bx_if:link>
+			            <bx_if:text>__title__</bx_if:text>
+					</div>
+		        </div><div class="bx-payment-pa-item-quantity">
+		        	<div class="bx-form-input-wrapper bx-form-input-wrapper-text">
+	                    <input type="text" class="bx-def-font-inputs bx-form-input-text" name="item-quantity-__id__" value="1" />
+	                </div>
+		        </div>
+		    </div>
+		</bx_repeat:items>
+	</div>
+</div>

+ 31 - 0
modules/boonex/payment/template/order_history.html

@@ -0,0 +1,31 @@
+<div class="bx-payment-view-order-popup bx-def-color-bg-block">
+    <div class="bx-payment-view-order-popup-cnt">
+		<div class="bx-payment-view-order bx-def-border bx-def-color-bg-box">
+		    <div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+		        <div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_seller__</div><div class="bx-payment-vo-content">
+					<bx_if:show_link>
+		            	<a href="__seller_url__" target="_blank">__seller_name__</a>
+		            </bx_if:show_link>
+		            <bx_if:show_text>__seller_name__</bx_if:show_text>
+		        </div>
+		    </div>
+		    <div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+		        <div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_order__</div><div class="bx-payment-vo-content">__order__</div>
+		    </div>
+		    <div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+		        <div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_date__</div><div class="bx-payment-vo-content">__date__</div>
+		    </div>
+		    <div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+		        <div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_products__</div><div class="bx-payment-vo-items">
+		            <bx_repeat:items>
+		                <bx_if:link>
+		                    <a href="__url__" target="_blank">__title__</a>
+		                </bx_if:link>
+		                <bx_if:text>__title__</bx_if:text>
+		                (__quantity__) - __price__ __currency_code__<br />
+		            </bx_repeat:items>
+		        </div>
+		    </div> 
+		</div>
+	</div>
+</div>

+ 1 - 0
modules/boonex/payment/template/order_pending.html

@@ -0,0 +1 @@
+<bx_include_auto:order_processed.html />

+ 19 - 0
modules/boonex/payment/template/order_pending_process.html

@@ -0,0 +1,19 @@
+<div class="bx-payment-pending-process bx-def-color-bg-block">
+    <div class="bx-payment-pending-process-cnt">
+        __form__
+        <script type="text/javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function (formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function (data) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        glGrids.__object__.processJson(data, '__action__');
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 32 - 0
modules/boonex/payment/template/order_processed.html

@@ -0,0 +1,32 @@
+<div class="bx-payment-view-order-popup bx-def-color-bg-block">
+    <div class="bx-payment-view-order-popup-cnt">
+		<div class="bx-payment-view-order  bx-def-border bx-def-color-bg-box">
+			<div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+				<div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_client__</div><div class="bx-payment-vo-content">
+					<a href="__client_url__" target="_blank">__client_name__</a>            
+				</div>
+			</div>
+			<div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+				<div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_order__</div><div class="bx-payment-vo-content">__order__</div>
+			</div>
+			<div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+				<div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_processed_with__</div><div class="bx-payment-vo-content">__provider__</div>
+			</div>
+			<div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+				<div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_message__</div><div class="bx-payment-vo-content">__error__</div>
+			</div>
+			<div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+				<div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_date__</div><div class="bx-payment-vo-content">__date__</div>
+			</div>
+			<div class="bx-payment-vo-row bx-def-padding-thd-topbottom bx-def-border-top bx-clearfix">
+				<div class="bx-payment-vo-caption bx-def-padding-thd-left bx-def-box-sizing">__txt_products__</div><div class="bx-payment-vo-items">
+					<bx_repeat:items>
+						<bx_if:link><a href="__url__" target="_blank">__title__</a></bx_if:link>
+						<bx_if:text>__title__</bx_if:text>
+						(__quantity__) - __price__ __currency_code__<br />
+					</bx_repeat:items>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>

+ 21 - 0
modules/boonex/payment/template/order_processed_add.html

@@ -0,0 +1,21 @@
+<div class="bx-payment-processed-add bx-def-color-bg-block">
+    <div class="bx-payment-processed-add-cnt">
+        __form__
+        <script type="text/javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function (formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function (data) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        glGrids.__object__.processJson(data, '__action__');
+                    }
+                });
+
+                __js_object__.paOnShowPopup();
+            });
+        </script>
+    </div>
+</div>

+ 1 - 0
modules/boonex/payment/template/profile_link.html

@@ -0,0 +1 @@
+<a href="__href__" title="__title__">__content__</a>

+ 9 - 0
modules/boonex/payment/template/redirect.html

@@ -0,0 +1,9 @@
+__content__
+<form name="bx-payment-redirect" action="__action__" method="__method__">
+	<bx_repeat:items>
+		<input type="hidden" name="__name__" value="__value__" />
+	</bx_repeat:items>
+</form>
+<script type="text/javascript">
+	document.forms['bx-payment-redirect'].submit();
+</script>