Просмотр исходного кода

Ticket #2298 - Related changes in Payments.

AntonLV 7 лет назад
Родитель
Сommit
643c038a44

+ 53 - 26
inc/classes/BxDolPayments.php

@@ -131,36 +131,54 @@ class BxDolPayments extends BxDolFactory implements iBxDolSingleton
     public function getOrdersUrl()
     {
     	if(!BxDolRequest::serviceExists($this->_sActive, 'get_orders_url', 'Orders'))
-    		return '';
+            return '';
 
     	return BxDolService::call($this->_sActive, 'get_orders_url', array(), 'Orders');
     }
 
     public function getOrdersCount($sType)
     {
-    	if(!BxDolRequest::serviceExists($this->_sActive, 'get_orders_count', 'Orders'))
-    		return array();
+        if(!BxDolRequest::serviceExists($this->_sActive, 'get_orders_count', 'Orders'))
+            return array();
 
-		$aSrvParams = array($sType);
-		return BxDolService::call($this->_sActive, 'get_orders_count', $aSrvParams, 'Orders');
+        $aSrvParams = array($sType);
+        return BxDolService::call($this->_sActive, 'get_orders_count', $aSrvParams, 'Orders');
     }
 
     public function getOrdersInfo($aConditions)
     {
-    	if(!BxDolRequest::serviceExists($this->_sActive, 'get_orders_info', 'Orders'))
-    		return array();
+        if(!BxDolRequest::serviceExists($this->_sActive, 'get_orders_info', 'Orders'))
+            return array();
 
-		$aSrvParams = array($aConditions);
-		return BxDolService::call($this->_sActive, 'get_orders_info', $aSrvParams, 'Orders');
+        $aSrvParams = array($aConditions);
+        return BxDolService::call($this->_sActive, 'get_orders_info', $aSrvParams, 'Orders');
     }
 
     public function getPendingOrdersInfo($aConditions)
     {
-    	if(!BxDolRequest::serviceExists($this->_sActive, 'get_pending_orders_info', 'Orders'))
-    		return array();
+        if(!BxDolRequest::serviceExists($this->_sActive, 'get_pending_orders_info', 'Orders'))
+            return array();
+
+        $aSrvParams = array($aConditions);
+        return BxDolService::call($this->_sActive, 'get_pending_orders_info', $aSrvParams, 'Orders');
+    }
+
+    public function getSubscriptionOrdersInfo($aConditions)
+    {
+        if(!BxDolRequest::serviceExists($this->_sActive, 'get_subscription_orders_info', 'Subscriptions'))
+            return array();
+
+        $aSrvParams = array($aConditions);
+        return BxDolService::call($this->_sActive, 'get_subscription_orders_info', $aSrvParams, 'Subscriptions');
+    }
+
+    public function getSubscriptionsInfo($aConditions, $bCheckInProvider = false)
+    {
+        if(!BxDolRequest::serviceExists($this->_sActive, 'get_subscriptions_info', 'Subscriptions'))
+            return array();
 
-		$aSrvParams = array($aConditions);
-		return BxDolService::call($this->_sActive, 'get_pending_orders_info', $aSrvParams, 'Orders');
+        $aSrvParams = array($aConditions, $bCheckInProvider);
+        return BxDolService::call($this->_sActive, 'get_subscriptions_info', $aSrvParams, 'Subscriptions');
     }
 
     public function getCartUrl($iVendor = 0)
@@ -270,32 +288,41 @@ class BxDolPayments extends BxDolFactory implements iBxDolSingleton
      */
     public function getSubscribeLink($iVendorId, $sVendorProvider, $mixedModuleId, $iItemId, $iItemCount = 1, $sRedirect = '', $aCustom = array())
     {
-		if(!BxDolRequest::serviceExists($this->_sActive, 'get_subscribe_link', 'Subscriptions'))
-			return '';
+        if(!BxDolRequest::serviceExists($this->_sActive, 'get_subscribe_link', 'Subscriptions'))
+            return '';
 
-		$aSrvParams = array($iVendorId, $sVendorProvider, $mixedModuleId, $iItemId, $iItemCount, $sRedirect, $aCustom);
-		return BxDolService::call($this->_sActive, 'get_subscribe_link', $aSrvParams, 'Subscriptions');
+        $aSrvParams = array($iVendorId, $sVendorProvider, $mixedModuleId, $iItemId, $iItemCount, $sRedirect, $aCustom);
+        return BxDolService::call($this->_sActive, 'get_subscribe_link', $aSrvParams, 'Subscriptions');
+    }
+
+    public function sendSubscriptionExpirationLetters($iPendingId, $sOrderId)
+    {
+        if(!BxDolRequest::serviceExists($this->_sActive, 'send_subscription_expiration_letters', 'Subscriptions'))
+            return '';
+
+        $aSrvParams = array($iPendingId, $sOrderId);
+        return BxDolService::call($this->_sActive, 'send_subscription_expiration_letters', $aSrvParams, 'Subscriptions');
     }
 
     public function initializeCheckout($iVendorId, $sProvider, $aItems = array())
     {
-    	if(!BxDolRequest::serviceExists($this->_sActive, 'initialize_checkout'))
-			return '';
+        if(!BxDolRequest::serviceExists($this->_sActive, 'initialize_checkout'))
+            return '';
 
-		$aSrvParams = array($iVendorId, $sProvider, $aItems);
-		return BxDolService::call($this->_sActive, 'initialize_checkout', $aSrvParams);
+        $aSrvParams = array($iVendorId, $sProvider, $aItems);
+        return BxDolService::call($this->_sActive, 'initialize_checkout', $aSrvParams);
     }
 
     /**
      * Isn't used anywhere for now. Is needed when license 'prolong' is requested from license keeping module like Market and ACL.  
      */
-	public function prolongSubscription($sOrderId)
+    public function prolongSubscription($sOrderId)
     {
-    	if(!BxDolRequest::serviceExists($this->_sActive, 'prolong_subscription'))
-			return '';
+        if(!BxDolRequest::serviceExists($this->_sActive, 'prolong_subscription'))
+            return '';
 
-		$aSrvParams = array($sOrderId);
-		return BxDolService::call($this->_sActive, 'prolong_subscription', $aSrvParams);
+        $aSrvParams = array($sOrderId);
+        return BxDolService::call($this->_sActive, 'prolong_subscription', $aSrvParams);
     }
 
     public function generateLicense()

+ 23 - 10
modules/base/payment/classes/BxBaseModPaymentProvider.php

@@ -11,17 +11,17 @@
 
 interface iBxBaseModPaymentProvider
 {
-	public function initializeCheckout($iPendingId, $aCartInfo);
+    public function initializeCheckout($iPendingId, $aCartInfo);
     public function finalizeCheckout(&$aData);
     public function finalizedCheckout();
 }
 
 class BxBaseModPaymentProvider extends BxDol
 {
-	protected $MODULE;
-	protected $_oModule;
+    protected $MODULE;
+    protected $_oModule;
 
-	protected $_sLangsPrefix;
+    protected $_sLangsPrefix;
 
     protected $_iId;
     protected $_sName;
@@ -32,6 +32,12 @@ class BxBaseModPaymentProvider extends BxDol
     protected $_bRedirectOnResult;
     protected $_sLogFile;
 
+    /**
+     * Is needed to translate status received from Payment Provider 
+     * to internal one. 
+     */
+    protected $_aSbsStatuses;
+
     function __construct($aConfig)
     {
         parent::__construct();
@@ -50,9 +56,11 @@ class BxBaseModPaymentProvider extends BxDol
 
         $this->_sLogFile = BX_DIRECTORY_PATH_LOGS . 'bx_pp_' . $this->_sName . '.log';
 
+        $this->_aSbsStatuses = array();
+
         $this->_aOptions = array();
         if(!empty($aConfig['options']) && is_array($aConfig['options']))
-        	$this->setOptions($aConfig['options']);
+            $this->setOptions($aConfig['options']);
     }
 
     public function setOptions($aOptions)
@@ -101,24 +109,29 @@ class BxBaseModPaymentProvider extends BxDol
 		return bx_append_url_params($sUrl, $aParams);
     }
 
-	public function getNotifyUrl($iVendorId, $aParams = array())
+    public function getNotifyUrl($iVendorId, $aParams = array())
     {
     	$sUrl = $this->_oModule->_oConfig->getUrl('URL_NOTIFY', array(), $this->_bUseSsl) . $this->_sName . '/' . $iVendorId;
-		return bx_append_url_params($sUrl, $aParams);
+        return bx_append_url_params($sUrl, $aParams);
     }
 
     /**
      * TODO: Check whether the method is needed or not. 
      * Is used on success only.
      */
-	public function needRedirect()
+    public function needRedirect()
     {
         return $this->_bRedirectOnResult;
     }
 
-	public function addJsCss() {}
+    public function addJsCss() {}
+
+    public function finalizedCheckout() {}
 
-	public function finalizedCheckout() {}
+    public function getSubscription($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        return array();
+    }
 
     protected function getOptionsByPending($iPendingId)
     {

+ 15 - 4
modules/boonex/payment/classes/BxPaymentDb.php

@@ -415,20 +415,31 @@ class BxPaymentDb extends BxBaseModPaymentDb
     {
     	$aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
 
-    	$sWhereClause = $sLimitClause = '';
+        $sSelectClause = "`ts`.*";
+    	$sJoinClause = $sWhereClause = $sLimitClause = '';
         switch($aParams['type']) {
             case 'pending_id':
                 $aMethod['name'] = 'getRow';
             	$aMethod['params'][1] = array(
-                	'pending_id' => $aParams['pending_id']
+                    'pending_id' => $aParams['pending_id']
                 );
 
-                $sWhereClause = " AND `pending_id`=:pending_id";
+                $sWhereClause = " AND `ts`.`pending_id`=:pending_id";
                 $sLimitClause = " LIMIT 1";
                 break;
+
+            case 'mixed':
+                $sWhereClause = " AND " . $this->arrayToSQL($aParams['conditions'], ' AND ');
+                break;
+
+            case 'mixed_ext':
+                $sSelectClause .= ", `ttp`.`client_id`, `ttp`.`seller_id`, `ttp`.`type`, `ttp`.`provider`, `ttp`.`order`";
+                $sJoinClause = "LEFT JOIN `" . $this->_sPrefix . "transactions_pending` AS `ttp` ON `ts`.`pending_id`=`ttp`.`id`";
+                $sWhereClause = " AND " . $this->arrayToSQL($aParams['conditions'], ' AND ');
+                break;
         }
 
-        $aMethod['params'][0] = "SELECT * FROM `" . $this->_sPrefix . "subscriptions` WHERE 1 " . $sWhereClause . $sLimitClause;
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $this->_sPrefix . "subscriptions` AS `ts` " . $sJoinClause . " WHERE 1 " . $sWhereClause . $sLimitClause;
         return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
     }
 

+ 21 - 13
modules/boonex/payment/classes/BxPaymentModule.php

@@ -18,6 +18,14 @@ define('BX_PAYMENT_EMPTY_ID', 0);
 
 define('BX_PAYMENT_RESULT_SUCCESS', 0);
 
+define('BX_PAYMENT_SBS_STATUS_SCHEDULED', 'scheduled');
+define('BX_PAYMENT_SBS_STATUS_TRIAL', 'trial');
+define('BX_PAYMENT_SBS_STATUS_ACTIVE', 'active');
+define('BX_PAYMENT_SBS_STATUS_UNPAID', 'unpaid');
+define('BX_PAYMENT_SBS_STATUS_PAUSED', 'paused');
+define('BX_PAYMENT_SBS_STATUS_CANCELED', 'canceled');
+define('BX_PAYMENT_SBS_STATUS_UNKNOWN', 'unknown');
+
 /**
  * Payment module by BoonEx
  *
@@ -445,22 +453,22 @@ class BxPaymentModule extends BxBaseModPaymentModule
         if($mixedResult !== true)
             return echoJson(array('code' => 2, 'message' => $mixedResult));
 
-		$aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($this->_oConfig->getPrefix('general') . 'cancelation_request', array(
-			'sibscription_id' => $aSubscription['subscription_id'],
-			'sibscription_customer' => $aSubscription['customer_id'],
-		    'sibscription_date' => bx_time_js($aSubscription['date'], BX_FORMAT_DATE, true)
-		), 0, (int)$aPending['client_id']);
+        $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($this->_oConfig->getPrefix('general') . 'cancelation_request', array(
+            'sibscription_id' => $aSubscription['subscription_id'],
+            'sibscription_customer' => $aSubscription['customer_id'],
+            'sibscription_date' => bx_time_js($aSubscription['date'], BX_FORMAT_DATE, true)
+        ), 0, (int)$aPending['client_id']);
 
-		$sEmail = '';
-		$oProvider = $this->getObjectProvider($aPending['provider'], $aPending['seller_id']);
-		if($oProvider !== false && $oProvider->isActive())
-		    $sEmail = $oProvider->getOption('cancellation_email');
+        $sEmail = '';
+        $oProvider = $this->getObjectProvider($aPending['provider'], $aPending['seller_id']);
+        if($oProvider !== false && $oProvider->isActive())
+            $sEmail = $oProvider->getOption('cancellation_email');
 
-		if(empty($sEmail))
-		    $sEmail = $oRecipient->getAccountObject()->getEmail();
+        if(empty($sEmail))
+            $sEmail = $oRecipient->getAccountObject()->getEmail();
 
-		if(!sendMail($sEmail, $aTemplate['Subject'], $aTemplate['Body'], 0, array(), BX_EMAIL_SYSTEM))
-		    return echoJson($aResult);
+        if(!sendMail($sEmail, $aTemplate['Subject'], $aTemplate['Body'], 0, array(), BX_EMAIL_SYSTEM))
+            return echoJson($aResult);
 
         echoJson(array('code' => 0, 'message' => _t('_bx_payment_msg_cancelation_request_sent')));
     }

+ 57 - 28
modules/boonex/payment/classes/BxPaymentProviderChargebee.php

@@ -16,14 +16,23 @@ define('CBEE_MODE_TEST', 2);
 
 class BxPaymentProviderChargebee extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
 {
-	protected $_iMode;
-	protected $_bCheckAmount;
+    protected $_iMode;
+    protected $_bCheckAmount;
 
     function __construct($aConfig)
     {
     	$this->MODULE = 'bx_payment';
 
-        parent::__construct($aConfig);        
+        parent::__construct($aConfig);   
+
+        $this->_aSbsStatuses = array(
+            'future' => BX_PAYMENT_SBS_STATUS_SCHEDULED, 
+            'in_trial' => BX_PAYMENT_SBS_STATUS_TRIAL, 
+            'active' => BX_PAYMENT_SBS_STATUS_ACTIVE, 
+            'non_renewing' => BX_PAYMENT_SBS_STATUS_ACTIVE, 
+            'paused' => BX_PAYMENT_SBS_STATUS_PAUSED,
+            'canceled' => BX_PAYMENT_SBS_STATUS_CANCELED,
+        );
     }
 
     public function setOptions($aOptions)
@@ -216,38 +225,58 @@ class BxPaymentProviderChargebee extends BxBaseModPaymentProvider implements iBx
 	}
 
     public function retrieveCustomer($sCustomerId)
-	{
-		$oCustomer = null;
+    {
+        $oCustomer = null;
 
-		try {
-			ChargeBee_Environment::configure($this->_getSite(), $this->_getApiKey());
-			$oResult = ChargeBee_Customer::retrieve($sCustomerId);
+        try {
+            ChargeBee_Environment::configure($this->_getSite(), $this->_getApiKey());
+            $oResult = ChargeBee_Customer::retrieve($sCustomerId);
 
-			$oCustomer = $oResult->customer();
-			if($oCustomer->id != $sCustomerId)
-				return false;
-		}
-		catch (Exception $oException) {
-			$iError = $oException->getCode();
-			$sError = $oException->getMessage();
+            $oCustomer = $oResult->customer();
+            if($oCustomer->id != $sCustomerId)
+                    return false;
+        }
+        catch (Exception $oException) {
+            $iError = $oException->getCode();
+            $sError = $oException->getMessage();
 
-			$this->log('Retrieve Customer Error: ' . $sError . '(' . $iError . ')');
+            $this->log('Retrieve Customer Error: ' . $sError . '(' . $iError . ')');
 
-			return false;
-		}
+            return false;
+        }
 
-		return $oCustomer;
-	}
+        return $oCustomer;
+    }
 
-	protected function _getSite()
-	{
-		return $this->_iMode == CBEE_MODE_LIVE ? $this->getOption('live_site') : $this->getOption('test_site');
-	}
+    public function getSubscription($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        $oSubscription = $this->retrieveSubscription($sSubscriptionId);
+        if($oSubscription === false)
+            return array();
+
+        $sStatus = $oSubscription->status;
+        $sStatus = isset($this->_aSbsStatuses[$sStatus]) ? $this->_aSbsStatuses[$sStatus] : BX_PAYMENT_SBS_STATUS_UNKNOWN;
+
+        return array(
+            'status' => $sStatus,
+            'created' => $oSubscription->createdAt,
+            'started' => $oSubscription->startedAt,
+            'trial_start' => $oSubscription->trialStart,
+            'trial_end' => $oSubscription->trialEnd,
+            'cperiod_start' => $oSubscription->currentTermStart,
+            'cperiod_end' => $oSubscription->currentTermEnd,
+        );
+    }
 
-	protected function _getApiKey()
-	{
-		return $this->_iMode == CBEE_MODE_LIVE ? $this->getOption('live_api_key') : $this->getOption('test_api_key');
-	}
+    protected function _getSite()
+    {
+        return $this->_iMode == CBEE_MODE_LIVE ? $this->getOption('live_site') : $this->getOption('test_site');
+    }
+
+    protected function _getApiKey()
+    {
+        return $this->_iMode == CBEE_MODE_LIVE ? $this->getOption('live_api_key') : $this->getOption('test_api_key');
+    }
 
 	protected function _processEvent()
 	{

+ 64 - 31
modules/boonex/payment/classes/BxPaymentProviderStripe.php

@@ -16,20 +16,20 @@ define('STRP_MODE_TEST', 2);
 
 class BxPaymentProviderStripe extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
 {
-	protected $_aIncludeJs;
-	protected $_aIncludeCss;
+    protected $_aIncludeJs;
+    protected $_aIncludeCss;
 
-	protected $_sFormDetails;
-	protected $_sFormDisplayDetailsEdit;
+    protected $_sFormDetails;
+    protected $_sFormDisplayDetailsEdit;
 
     protected $_sFormCard;
-	protected $_sFormDisplayCardAdd;
+    protected $_sFormDisplayCardAdd;
 
-	protected $_iMode;
-	protected $_bCheckAmount;
-	protected $_bProrate;
+    protected $_iMode;
+    protected $_bCheckAmount;
+    protected $_bProrate;
 
-	protected $_oCustomer;
+    protected $_oCustomer;
 
 
     function __construct($aConfig)
@@ -38,6 +38,16 @@ class BxPaymentProviderStripe extends BxBaseModPaymentProvider implements iBxBas
 
         parent::__construct($aConfig);
 
+        $this->_aSbsStatuses = array(
+            'incomplete' => BX_PAYMENT_SBS_STATUS_UNPAID, 
+            'incomplete_expired' => BX_PAYMENT_SBS_STATUS_UNPAID, 
+            'trialing' => BX_PAYMENT_SBS_STATUS_TRIAL, 
+            'active' => BX_PAYMENT_SBS_STATUS_ACTIVE, 
+            'past_due' => BX_PAYMENT_SBS_STATUS_UNPAID,
+            'unpaid' => BX_PAYMENT_SBS_STATUS_UNPAID,
+            'canceled' => BX_PAYMENT_SBS_STATUS_CANCELED,
+        );
+
         $this->_sFormDetails = 'bx_payment_form_strp_details';
         $this->_sFormDisplayDetailsEdit = 'bx_payment_form_strp_details_edit';
 
@@ -47,12 +57,12 @@ class BxPaymentProviderStripe extends BxBaseModPaymentProvider implements iBxBas
         $this->_bProrate = false;
 
         $this->_aIncludeJs = array(
-        	'https://checkout.stripe.com/checkout.js',
-        	'main.js',
-        	'stripe.js'
+            'https://checkout.stripe.com/checkout.js',
+            'main.js',
+            'stripe.js'
         );
         $this->_aIncludeCss = array(
-        	'stripe.css'
+            'stripe.css'
         );
 
         $this->_oCustomer = null;
@@ -385,7 +395,7 @@ class BxPaymentProviderStripe extends BxBaseModPaymentProvider implements iBxBas
         }
 
         return $this->_oModule->_oTemplate->parseHtmlByName('strp_billing_change_recuring.html', array(
-        	'object' => $this->_oModule->_oConfig->getJsObject('subscription'),
+            'object' => $this->_oModule->_oConfig->getJsObject('subscription'),
             'form' => $oForm->getCode(),
             'form_id' => $oForm->aFormAttrs['id'],
         ));
@@ -403,32 +413,55 @@ class BxPaymentProviderStripe extends BxBaseModPaymentProvider implements iBxBas
     public function getCheckoutParamsSingle($aParams, &$oGrid)
     {
     	if(bx_get('token') !== false)
-    		$aParams['token'] = bx_process_input(bx_get('token'));
+            $aParams['token'] = bx_process_input(bx_get('token'));
 
     	return $aParams;
     }
 
-	protected function _getPublicKey()
-	{
-		return $this->_iMode == STRP_MODE_LIVE ? $this->getOption('live_pub_key') : $this->getOption('test_pub_key');
-	}
+    public function getSubscription($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        $oSubscription = $this->_retrieveSubscription($sCustomerId, $sSubscriptionId);
+        if($oSubscription === false)
+            return array();
 
-	protected function _getSecretKey()
-	{
-		return $this->_iMode == STRP_MODE_LIVE ? $this->getOption('live_sec_key') : $this->getOption('test_sec_key');
-	}
+        $aSubscription = $oSubscription->jsonSerialize();
+        if(empty($aSubscription) || !is_array($aSubscription))
+            return array();
+
+        $sStatus = isset($this->_aSbsStatuses[$aSubscription['status']]) ? $this->_aSbsStatuses[$aSubscription['status']] : BX_PAYMENT_SBS_STATUS_UNKNOWN;
+
+        return array(
+            'status' => $sStatus,
+            'created' => $aSubscription['created'],
+            'started' => !empty($aSubscription['start']) ? $aSubscription['start'] : 0,
+            'trial_start' => !empty($aSubscription['trial_start']) ? $aSubscription['trial_start'] : 0,
+            'trial_end' => !empty($aSubscription['trial_end']) ? $aSubscription['trial_end'] : 0,
+            'cperiod_start' => !empty($aSubscription['current_period_start']) ? $aSubscription['current_period_start'] : 0,
+            'cperiod_end' => !empty($aSubscription['current_period_end']) ? $aSubscription['current_period_end'] : 0,
+        );
+    }
+
+    protected function _getPublicKey()
+    {
+        return $this->_iMode == STRP_MODE_LIVE ? $this->getOption('live_pub_key') : $this->getOption('test_pub_key');
+    }
+
+    protected function _getSecretKey()
+    {
+        return $this->_iMode == STRP_MODE_LIVE ? $this->getOption('live_sec_key') : $this->getOption('test_sec_key');
+    }
 
     protected function _createToken($aCard)
     {
-		try {
-			$oToken = Stripe\Token::create(array('card' => $aCard));
-		}
-		catch (Stripe\Error\Base $oException) {
-			return $this->_processException('Create Token Error: ', $oException);
-		}
+        try {
+            $oToken = Stripe\Token::create(array('card' => $aCard));
+        }
+        catch (Stripe\Error\Base $oException) {
+            return $this->_processException('Create Token Error: ', $oException);
+        }
 
-		return $oToken->jsonSerialize();
-	}
+        return $oToken->jsonSerialize();
+    }
 
 	protected function _createCustomer($sType, $sToken, $aClient)
 	{

+ 154 - 2
modules/boonex/payment/classes/BxPaymentSubscriptions.php

@@ -88,7 +88,88 @@ class BxPaymentSubscriptions extends BxBaseModPaymentSubscriptions
         return $this->_getBlock('history');
     }
 
-	/**
+    /**
+     * @page service Service Calls
+     * @section bx_payment Payment
+     * @subsection bx_payment-other Other
+     * @subsubsection bx_payment-get_subscription_orders_info get_subscription_orders_info
+     * 
+     * @code bx_srv('bx_payment', 'get_subscription_orders_info', [...], 'Subscriptions'); @endcode
+     * 
+     * Get subscription transaction(s) which meets all requirements.
+     *
+     * @param $aConditions an array of pears('key' => 'value'). The most useful keys are the following:
+     * a. client_id - client's ID (integer)
+     * b. seller_id - seller's ID (integer)
+     * c. type - transaction type: single or recurring (string)
+     * d. amount - transaction amount (float)
+     * e. order - order ID received from payment provider (string)
+     * f. provider - payment provider name (string)
+     * g. date - the date when the payment was established(UNIXTIME STAMP)
+     * h. processed - whether the payment was processed or not (integer, 0 or 1)
+     * @return an array of transactions. Each transaction has full info(client ID, seller ID, external transaction ID, date and so on)
+     * 
+     * @see BxPaymentSubscriptions::serviceGetSubscriptionOrdersInfo
+     */
+    /** 
+     * @ref bx_payment-get_subscription_orders_info "get_subscription_orders_info"
+     */
+    public function serviceGetSubscriptionOrdersInfo($aConditions)
+    {
+        if(empty($aConditions) || !is_array($aConditions))
+            return array();
+
+        return $this->_oModule->_oDb->getOrderSubscription(array('type' => 'mixed', 'conditions' => $aConditions));
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_payment Payment
+     * @subsection bx_payment-other Other
+     * @subsubsection bx_payment-get_subscriptions_info get_subscriptions_info
+     * 
+     * @code bx_srv('bx_payment', 'get_subscriptions_info', [...], 'Subscriptions'); @endcode
+     * 
+     * Get subscription(s) which meets all requirements.
+     *
+     * @param $aConditions an array of pears('key' => 'value'). The most useful keys are the following:
+     * a. pending_id - pending transaction ID (integer)
+     * b. customer_id - customer ID from payment provider (string)
+     * c. subscription_id - subscription ID from payment provider (string)
+     * d. paid - flag determining whether the subscription paid or not (integer)
+     * e. date - the date when the subscription was established(UNIXTIME STAMP)
+     * @param $bCheckInProvider boolean value determining whether the subscription should be checked in associated payment provider.
+     * @return an array of subscriptions. Each subscription has full info(pending ID, customer ID, subscription ID and so on)
+     * 
+     * @see BxPaymentSubscriptions::serviceGetSubscriptionsInfo
+     */
+    /** 
+     * @ref bx_payment-get_subscriptions_info "get_subscriptions_info"
+     */
+    public function serviceGetSubscriptionsInfo($aConditions, $bCheckInProvider = false)
+    {
+        if(empty($aConditions) || !is_array($aConditions))
+            return array();
+
+        $aSubscriptions = $this->_oModule->_oDb->getSubscription(array('type' => 'mixed_ext', 'conditions' => $aConditions));
+        if(empty($aSubscriptions) || !is_array($aSubscriptions) || !$bCheckInProvider)
+            return $aSubscriptions;
+
+        foreach($aSubscriptions as $iKey => $aSubscription) {
+            if(empty($aSubscription['provider']) || $aSubscription['provider'] == 'manual')
+                continue;
+
+            $oProvider = $this->_oModule->getObjectProvider($aSubscription['provider'], $aSubscription['seller_id']);
+            if(!$oProvider)
+                continue;
+
+            $aSubscriptions[$iKey]['data'] = $oProvider->getSubscription($aSubscription['pending_id'], $aSubscription['customer_id'], $aSubscription['subscription_id']);
+        }
+
+        return $aSubscriptions;
+    }
+
+    /**
      * @page service Service Calls
      * @section bx_payment Payment
      * @subsection bx_payment-purchase_processing Purchase Processing
@@ -111,7 +192,7 @@ class BxPaymentSubscriptions extends BxBaseModPaymentSubscriptions
     /** 
      * @ref bx_payment-subscribe "subscribe"
      */
-	public function serviceSubscribe($iSellerId, $sSellerProvider, $iModuleId, $iItemId, $iItemCount, $sRedirect = '', $aCustom = array())
+    public function serviceSubscribe($iSellerId, $sSellerProvider, $iModuleId, $iItemId, $iItemCount, $sRedirect = '', $aCustom = array())
     {
     	$CNF = &$this->_oModule->_oConfig->CNF;
 
@@ -147,6 +228,77 @@ class BxPaymentSubscriptions extends BxBaseModPaymentSubscriptions
 		return $mixedResult;
     }
 
+    /**
+     * @page service Service Calls
+     * @section bx_payment Payment
+     * @subsection bx_payment-purchase_processing Purchase Processing
+     * @subsubsection bx_payment-subscribe subscribe
+     * 
+     * @code bx_srv('bx_payment', 'subscribe', [...], 'Subscriptions'); @endcode
+     * 
+     * Initialize subscription for specified item.
+     *
+     * @param $iSellerId integer value with seller ID.
+     * @param $sSellerProvider string value with a name of payment provider to be used for processing. Empty value means that payment provider selector should be shown.
+     * @param $iModuleId integer value with module ID.
+     * @param $iItemId integer value with item ID.
+     * @param $iItemCount integer value with a number of items for purchasing. It's equal to 1 in case of subscription.
+     * @param $sRedirect (optional) string value with redirect URL, if it's needed. 
+     * @return an array with special format which describes the result of operation.
+     * 
+     * @see BxPaymentSubscriptions::serviceSubscribe
+     */
+    /** 
+     * @ref bx_payment-subscribe "subscribe"
+     */
+    public function serviceSendSubscriptionExpirationLetters($iPendingId, $sOrderId)
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+        $sPrefix = $this->_oModule->_oConfig->getPrefix('general');
+
+        $aSubscription = $this->serviceGetSubscriptionsInfo(array('subscription_id' => $sOrderId));
+        if(empty($aSubscription) || !is_array($aSubscription))
+            return;
+
+        $aSubscription = array_shift($aSubscription);
+
+        $oSeller = BxDolProfile::getInstanceMagic((int)$aSubscription['seller_id']);
+        $oClient = BxDolProfile::getInstanceMagic((int)$aSubscription['client_id']);
+
+        $aEtParams = array(
+            'sibscription_id' => $aSubscription['subscription_id'],
+            'sibscription_customer' => $aSubscription['customer_id'],
+            'sibscription_date' => bx_time_js($aSubscription['date'], BX_FORMAT_DATE, true)
+        );
+
+        /**
+         * Notify seller.
+         */
+        if($oSeller !== false) {
+            $sEmail = '';
+            $oProvider = $this->_oModule->getObjectProvider($aSubscription['provider'], $aSubscription['seller_id']);
+            if($oProvider !== false && $oProvider->isActive())
+                $sEmail = $oProvider->getOption('expiration_email');
+
+            if(empty($sEmail))
+                $sEmail = $oSeller->getAccountObject()->getEmail();
+
+            $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($sPrefix . 'expiration_notification_seller', $aEtParams, 0, (int)$aSubscription['client_id']);
+
+            sendMail($sEmail, $aTemplate['Subject'], $aTemplate['Body'], 0, array(), BX_EMAIL_SYSTEM, 'html', false, array(), true);
+        }
+
+        /**
+         * Notify client.
+         */
+        if($oClient !== false) {
+            $sEmail = $oClient->getAccountObject()->getEmail();
+            $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($sPrefix . 'expiration_notification_client', $aEtParams, 0, (int)$aSubscription['seller_id']);
+
+            sendMail($sEmail, $aTemplate['Subject'], $aTemplate['Body'], 0, array(), BX_EMAIL_SYSTEM, 'html', false, array(), true);
+        }
+    }
+
     public function cancel($iPendingId)
     {
     	$aSubscription = $this->_oModule->_oDb->getSubscription(array('type' => 'pending_id', 'pending_id' => $iPendingId));

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

@@ -205,6 +205,24 @@
 <p>Hello,</p>
 <p>A user with {profile_name} profile name requested cancellation for a subscription.</p>
 <p>Subscription info:<br />ID: {sibscription_id}<br />Customer: {sibscription_customer}<br />Date: {sibscription_date}</p>
+{email_footer}]]></string>
+
+    <string name="_bx_payment_et_txt_name_expiration_notification_seller"><![CDATA[Subscription expiration notification (for seller).]]></string>
+    <string name="_bx_payment_et_txt_subject_expiration_notification_seller"><![CDATA[Subscription expiration notification.]]></string>
+    <string name="_bx_payment_et_txt_body_expiration_notification_seller"><![CDATA[{email_header}
+<p>Hello,</p>
+<p>A subscription purchased by '{profile_name}' expired.</p>
+<p>Subscription info:<br />ID: {sibscription_id}<br />Customer: {sibscription_customer}<br />Date: {sibscription_date}</p>
+<p><b>Note</b>. An associated data will be removed shortly.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_payment_et_txt_name_expiration_notification_client"><![CDATA[Subscription expiration notification (for client).]]></string>
+    <string name="_bx_payment_et_txt_subject_expiration_notification_client"><![CDATA[Subscription expiration notification.]]></string>
+    <string name="_bx_payment_et_txt_body_expiration_notification_client"><![CDATA[{email_header}
+<p>Hello,</p>
+<p>Your subscription purchased from '{profile_name}' expired.</p>
+<p>Subscription info:<br />ID: {sibscription_id}<br />Customer: {sibscription_customer}<br />Date: {sibscription_date}</p>
+<p><b>Note</b>. An associated data will be removed shortly.</p>
 {email_footer}]]></string>
 
 	<!-- MESSAGES, WARNINGS and ERRORS -->
@@ -370,6 +388,8 @@
 	<string name="_bx_payment_cbee_check_amount_dsc"><![CDATA[Enable amount checking for Chargebee Webhooks (recomended). However checking should be disabled when discounts and coupons are used.]]></string>
 	<string name="_bx_payment_cbee_ssl_cpt"><![CDATA[Enable SSL]]></string>
 	<string name="_bx_payment_cbee_ssl_dsc"><![CDATA[Enable SSL connection to communicate with Chargebee. Note. URLs below will be updated after saving.]]></string>
+        <string name="_bx_payment_cbee_cancellation_email_cpt"><![CDATA[Cancellation Email]]></string>
+        <string name="_bx_payment_cbee_expiration_email_cpt"><![CDATA[Expiration Email]]></string>
 	<string name="_bx_payment_cbee_return_data_url_cpt"><![CDATA[Data return URL]]></string>
 	<string name="_bx_payment_cbee_notify_url_cpt"><![CDATA[Webhooks URL]]></string>
 	<string name="_bx_payment_cbee_msg_subscribed"><![CDATA[Thank you! Subscription created. Please, check your email for further instructions.]]></string>
@@ -462,6 +482,7 @@
 	<string name="_bx_payment_strp_ssl_dsc"><![CDATA[Enable SSL connection to communicate with Stripe. Note. URLs below will be updated after saving.]]></string>
 	<string name="_bx_payment_strp_notify_url_cpt"><![CDATA[Webhooks URL]]></string>
 	<string name="_bx_payment_strp_cancellation_email_cpt"><![CDATA[Cancellation Email]]></string>
+        <string name="_bx_payment_strp_expiration_email_cpt"><![CDATA[Expiration Email]]></string>
 	<string name="_bx_payment_strp_msg_charged"><![CDATA[Payment was successfully processed.]]></string>
 	<string name="_bx_payment_strp_msg_subscribed"><![CDATA[Thank you! Subscription created. Please, check your email for further instructions.]]></string>
 	<string name="_bx_payment_strp_msg_details_changed"><![CDATA[Subscription details were changed.]]></string>

+ 3 - 1
modules/boonex/payment/install/sql/enable.sql

@@ -154,7 +154,9 @@ INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
 -- 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'),
-(@sName, '_bx_payment_et_txt_name_cancelation_request', 'bx_payment_cancelation_request', '_bx_payment_et_txt_subject_cancelation_request', '_bx_payment_et_txt_body_cancelation_request');
+(@sName, '_bx_payment_et_txt_name_cancelation_request', 'bx_payment_cancelation_request', '_bx_payment_et_txt_subject_cancelation_request', '_bx_payment_et_txt_body_cancelation_request'),
+(@sName, '_bx_payment_et_txt_name_expiration_notification_seller', 'bx_payment_expiration_notification_seller', '_bx_payment_et_txt_subject_expiration_notification_seller', '_bx_payment_et_txt_body_expiration_notification_seller'),
+(@sName, '_bx_payment_et_txt_name_expiration_notification_client', 'bx_payment_expiration_notification_client', '_bx_payment_et_txt_subject_expiration_notification_client', '_bx_payment_et_txt_body_expiration_notification_client');
 
 
 -- PAYMENTS

+ 10 - 5
modules/boonex/payment/install/sql/install.sql

@@ -169,8 +169,10 @@ INSERT INTO `bx_payment_providers_options`(`provider_id`, `name`, `type`, `capti
 (@iProviderId, 'cbee_test_api_key', 'text', '_bx_payment_cbee_test_api_key_cpt', '_bx_payment_cbee_test_api_key_dsc', '', '', '', '', 7),
 (@iProviderId, 'cbee_check_amount', 'checkbox', '_bx_payment_cbee_check_amount_cpt', '_bx_payment_cbee_check_amount_dsc', '', '', '', '', 8),
 (@iProviderId, 'cbee_ssl', 'checkbox', '_bx_payment_cbee_ssl_cpt', '_bx_payment_cbee_ssl_dsc', '', '', '', '', 9),
-(@iProviderId, 'cbee_return_data_url', 'value', '_bx_payment_cbee_return_data_url_cpt', '', '', '', '', '', 10),
-(@iProviderId, 'cbee_notify_url', 'value', '_bx_payment_cbee_notify_url_cpt', '', '', '', '', '', 11);
+(@iProviderId, 'cbee_cancellation_email', 'text', '_bx_payment_cbee_cancellation_email_cpt', '', '', 'EmailOrEmpty', '', '_sys_form_account_input_email_error', 10),
+(@iProviderId, 'cbee_expiration_email', 'text', '_bx_payment_cbee_expiration_email_cpt', '', '', 'EmailOrEmpty', '', '_sys_form_account_input_email_error', 11),
+(@iProviderId, 'cbee_return_data_url', 'value', '_bx_payment_cbee_return_data_url_cpt', '', '', '', '', '', 12),
+(@iProviderId, 'cbee_notify_url', 'value', '_bx_payment_cbee_notify_url_cpt', '', '', '', '', '', 13);
 
 -- Chargebee V3 payment provider
 INSERT INTO `bx_payment_providers`(`name`, `caption`, `description`, `option_prefix`, `for_visitor`, `for_single`, `for_recurring`, `active`, `order`, `class_name`) VALUES
@@ -187,8 +189,10 @@ INSERT INTO `bx_payment_providers_options`(`provider_id`, `name`, `type`, `capti
 (@iProviderId, 'cbee_v3_test_api_key', 'text', '_bx_payment_cbee_test_api_key_cpt', '_bx_payment_cbee_test_api_key_dsc', '', '', '', '', 7),
 (@iProviderId, 'cbee_v3_check_amount', 'checkbox', '_bx_payment_cbee_check_amount_cpt', '_bx_payment_cbee_check_amount_dsc', '', '', '', '', 8),
 (@iProviderId, 'cbee_v3_ssl', 'checkbox', '_bx_payment_cbee_ssl_cpt', '_bx_payment_cbee_ssl_dsc', '', '', '', '', 9),
-(@iProviderId, 'cbee_v3_return_data_url', 'value', '_bx_payment_cbee_return_data_url_cpt', '', '', '', '', '', 10),
-(@iProviderId, 'cbee_v3_notify_url', 'value', '_bx_payment_cbee_notify_url_cpt', '', '', '', '', '', 11);
+(@iProviderId, 'cbee_v3_cancellation_email', 'text', '_bx_payment_cbee_cancellation_email_cpt', '', '', 'EmailOrEmpty', '', '_sys_form_account_input_email_error', 10),
+(@iProviderId, 'cbee_v3_expiration_email', 'text', '_bx_payment_cbee_expiration_email_cpt', '', '', 'EmailOrEmpty', '', '_sys_form_account_input_email_error', 11),
+(@iProviderId, 'cbee_v3_return_data_url', 'value', '_bx_payment_cbee_return_data_url_cpt', '', '', '', '', '', 12),
+(@iProviderId, 'cbee_v3_notify_url', 'value', '_bx_payment_cbee_notify_url_cpt', '', '', '', '', '', 13);
 
 -- Recurly payment provider
 INSERT INTO `bx_payment_providers`(`name`, `caption`, `description`, `option_prefix`, `for_visitor`, `for_single`, `for_recurring`, `active`, `order`, `class_name`) VALUES
@@ -221,7 +225,8 @@ INSERT INTO `bx_payment_providers_options`(`provider_id`, `name`, `type`, `capti
 (@iProviderId, 'strp_check_amount', 'checkbox', '_bx_payment_strp_check_amount_cpt', '_bx_payment_strp_check_amount_dsc', '', '', '', '', 8),
 (@iProviderId, 'strp_ssl', 'checkbox', '_bx_payment_strp_ssl_cpt', '_bx_payment_strp_ssl_dsc', '', '', '', '', 9),
 (@iProviderId, 'strp_cancellation_email', 'text', '_bx_payment_strp_cancellation_email_cpt', '', '', 'EmailOrEmpty', '', '_sys_form_account_input_email_error', 10),
-(@iProviderId, 'strp_notify_url', 'value', '_bx_payment_strp_notify_url_cpt', '', '', '', '', '', 11);
+(@iProviderId, 'strp_expiration_email', 'text', '_bx_payment_strp_expiration_email_cpt', '', '', 'EmailOrEmpty', '', '_sys_form_account_input_email_error', 11),
+(@iProviderId, 'strp_notify_url', 'value', '_bx_payment_strp_notify_url_cpt', '', '', '', '', '', 12);
 
 
 -- GRIDS

+ 21 - 0
modules/boonex/russian/data/langs/bx_payment/ru.xml

@@ -205,6 +205,24 @@
 <p>Привет,</p>
 <p>Пользователь с именем профиля {profile_name} отправил запрос на отмену подписки.</p>
 <p>Информация о подписке:<br />ID: {sibscription_id}<br />Клиент: {sibscription_customer}<br />Дата: {sibscription_date}</p>
+{email_footer}]]></string>
+
+    <string name="_bx_payment_et_txt_name_expiration_notification_seller"><![CDATA[Оповещение об истечении срока подписки (для  продавца).]]></string>
+    <string name="_bx_payment_et_txt_subject_expiration_notification_seller"><![CDATA[Оповещение об истечении срока подписки.]]></string>
+    <string name="_bx_payment_et_txt_body_expiration_notification_seller"><![CDATA[{email_header}
+<p>Привет,</p>
+<p>Подписка, купленная пользователем '{profile_name}', закончилась.</p>
+<p>Информация о подписке:<br />ID: {sibscription_id}<br />Клиент: {sibscription_customer}<br />Дата: {sibscription_date}</p>
+<p><b>Внимание</b>. Данные, связанные с подпиской, будут удалены в ближайшее время.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_payment_et_txt_name_expiration_notification_client"><![CDATA[Оповещение об истечении срока подписки (для клиента).]]></string>
+    <string name="_bx_payment_et_txt_subject_expiration_notification_client"><![CDATA[Оповещение об истечении срока подписки.]]></string>
+    <string name="_bx_payment_et_txt_body_expiration_notification_client"><![CDATA[{email_header}
+<p>Привет,</p>
+<p>Ваша подписка, купленная у '{profile_name}', закончилась.</p>
+<p>Информация о подписке:<br />ID: {sibscription_id}<br />Клиент: {sibscription_customer}<br />Дата: {sibscription_date}</p>
+<p><b>Внимание</b>. Данные, связанные с подпиской, будут удалены в ближайшее время.</p>
 {email_footer}]]></string>
 
 	<!-- MESSAGES, WARNINGS and ERRORS -->
@@ -370,6 +388,8 @@
 	<string name="_bx_payment_cbee_check_amount_dsc"><![CDATA[Активировать проверку суммы для Chargebee Webhooks (рекомендуется). Однако проверка должна быть отключена когда используются скидки и купоны.]]></string>
 	<string name="_bx_payment_cbee_ssl_cpt"><![CDATA[Активировать SSL]]></string>
 	<string name="_bx_payment_cbee_ssl_dsc"><![CDATA[Активировать SSL соединение для связи с Chargebee. Внимание. Нижеприведенные ссылки будут обновлены после сохранения.]]></string>
+        <string name="_bx_payment_cbee_cancellation_email_cpt"><![CDATA[Email для запросов отмены подписки]]></string>
+        <string name="_bx_payment_cbee_expiration_email_cpt"><![CDATA[Email для предупреждений о заканчивающихся подписках]]></string>
 	<string name="_bx_payment_cbee_return_data_url_cpt"><![CDATA[URL для возврата данных]]></string>
 	<string name="_bx_payment_cbee_notify_url_cpt"><![CDATA[URL для получения webhook-ов]]></string>
 	<string name="_bx_payment_cbee_msg_subscribed"><![CDATA[Спасибо! Подписка была создана. Пожалуйста, проверьте ваш емэйл для получения дальнейших инструкций.]]></string>
@@ -462,6 +482,7 @@
 	<string name="_bx_payment_strp_ssl_dsc"><![CDATA[Активировать SSL соединение для связи с Stripe. Внимание. Нижеприведенная Webhooks ссылка будет обновлена после сохранения.]]></string>
 	<string name="_bx_payment_strp_notify_url_cpt"><![CDATA[URL для получения webhook-ов]]></string>
 	<string name="_bx_payment_strp_cancellation_email_cpt"><![CDATA[Email для запросов отмены подписки]]></string>
+        <string name="_bx_payment_strp_expiration_email_cpt"><![CDATA[Email для предупреждений о заканчивающихся подписках]]></string>
 	<string name="_bx_payment_strp_msg_charged"><![CDATA[Платеж успешно принят.]]></string>
 	<string name="_bx_payment_strp_msg_subscribed"><![CDATA[Спасибо! Подписка была создана. Пожалуйста, проверьте ваш емэйл для получения дальнейших инструкций.]]></string>
 	<string name="_bx_payment_strp_msg_details_changed"><![CDATA[Детали подписки были изменены.]]></string>