Przeglądaj źródła

Ticket #2992 - Subscriptions in Credits module.

AntonLV 5 lat temu
rodzic
commit
50fe388a7e

+ 6 - 2
modules/boonex/credits/classes/BxCreditsConfig.php

@@ -100,12 +100,14 @@ class BxCreditsConfig extends BxBaseModGeneralConfig
         $this->_aJsClasses = array_merge($this->_aJsClasses, array(
             'studio' => 'BxCreditsStudio',
             'checkout' => 'BxCreditsCheckout',
+            'subscribe' => 'BxCreditsSubscribe',
             'withdraw' => 'BxCreditsWithdraw',
         ));
 
         $this->_aJsObjects = array_merge($this->_aJsObjects, array(
             'studio' => 'oBxCreditsStudio',
             'checkout' => 'oBxCreditsCheckout',
+            'subscribe' => 'oBxCreditsSubscribe',
             'withdraw' => 'oBxCreditsWithdraw',
         ));
 
@@ -120,6 +122,8 @@ class BxCreditsConfig extends BxBaseModGeneralConfig
             'withdraw_field_rate' => $sPrefix . '-wff-rate',
             'withdraw_field_amount' => $sPrefix . '-wff-amount',
             'withdraw_field_result' => $sPrefix . '-wff-result',
+
+            'subscribe_popup' =>  $sPrefix . '-subscribe-popup',
         );
 
         $this->_aPrefixes = array(
@@ -216,9 +220,9 @@ class BxCreditsConfig extends BxBaseModGeneralConfig
         return BxDolSession::getInstance()->setValue($this->_sCheckoutSessionKey, $aData);
     }
 
-    public function getOrder()
+    public function getOrder($iLength = 16)
     {
-        return genRndPwd(16, false);
+        return genRndPwd($iLength, false);
     }
 
     /**

+ 85 - 0
modules/boonex/credits/classes/BxCreditsModule.php

@@ -94,6 +94,47 @@ class BxCreditsModule extends BxBaseModGeneralModule
         ));
     }
 
+    public function actionSubscribe()
+    {
+        $iBuyerId = bx_get_logged_profile_id();
+        if(!$iBuyerId)
+            return echoJson(array('code' => 1, 'msg' => _t('_bx_credits_err_unknown_buyer')));
+
+        $aData = $this->_oConfig->getCheckoutData();
+        if(empty($aData) || !is_array($aData))
+            return echoJson(array('code' => 2, 'msg' => _t('_bx_credits_err_incorrect_data')));
+
+        $iSellerId = (int)$aData['seller'];
+        $fAmount = (float)$aData['amount'];
+
+        $fBalance = (float)$this->_oDb->getProfile(array('type' => 'balance', 'id' => $iBuyerId));
+        if($fAmount > $fBalance)
+            return echoJson(array('code' => 3, 'msg' => _t('_bx_credits_err_low_balance')));
+
+        $sUnique = $this->_oConfig->getOrder(9);
+        $sCustomer = 'bx_cus_' . $sUnique;
+        $sSubscription = 'bx_sub_' . $sUnique;
+
+        bx_alert($this->getName(), 'subscribe', 0, false, array(
+            'seller' => $iSellerId,
+            'buyer' => $iBuyerId,
+            'amount' => $fAmount, 
+            'trial' => $aData['trial'],
+            'customer' => $sCustomer,
+            'subscription' => $sSubscription
+        ));
+
+        return echoJson(array(
+            'code' => 0,
+            'redirect' => bx_append_url_params($aData['return_data_url'], array(
+                'cs' => $sCustomer, 
+                'sb' => $sSubscription,
+                'tr' => $aData['trial'],
+                'c' => $aData['custom']
+            ))
+        ));
+    }
+
     public function serviceGetSafeServices()
     {
         return array (
@@ -172,6 +213,35 @@ class BxCreditsModule extends BxBaseModGeneralModule
         return $this->_oTemplate->getBlockCheckout($oBuyer, $oSeller, $aData);
     }
 
+    public function serviceGetPopupSubscribe($aData)
+    {
+        $oBuyer = BxDolProfile::getInstance();
+        if(!$oBuyer)
+            return array('msg' => _t('_bx_credits_err_unknown_buyer'));
+
+        $iSeller = (int)$aData['seller'];
+        $oSeller = BxDolProfile::getInstance($iSeller);
+        if(!$oSeller)
+            return array('msg' => _t('_bx_credits_err_unknown_seller'));
+
+        $fAmountM = (float)$aData['amount'];
+        $fAmountC = $this->_oConfig->convertM2C($fAmountM);
+
+        $aData = array_merge($aData, array(
+            'amountm' => $fAmountM,
+            'amountc' => $fAmountC
+        ));
+
+        $this->_oConfig->setCheckoutData(array(
+            'seller' => $iSeller,
+            'amount' => $fAmountC,
+            'trial' => (int)$aData['trial'] > 0,
+            'custom' => bx_process_input($aData['custom']),
+            'return_data_url' => bx_process_input($aData['return_data_url']),
+        ));
+        return $this->_oTemplate->getPopupSubscribe($oBuyer, $oSeller, $aData);
+    }
+
     public function serviceGetMenuItemAddonAmount()
     {
         $iProfileId = bx_get_logged_profile_id();
@@ -593,6 +663,21 @@ class BxCreditsModule extends BxBaseModGeneralModule
 
         return $this->updateProfileBalance($iFirstPid, $iSecondPid, $fAmount, $sOrder, $sInfo);
     }
+    
+    public function serviceMakePayment($iBuyerPid, $fAmount, $iSellerPid, $sOrder = '')
+    {
+        $sInfo = '_bx_credits_txt_history_info_checkout';
+
+        if(!$this->updateProfileBalance($iBuyerPid, $iSellerPid, -$fAmount, $sOrder, $sInfo))
+            return false;
+
+        if(!$this->updateProfileBalance($iSellerPid, $iBuyerPid, $fAmount, $sOrder, $sInfo)) {
+            $this->updateProfileBalance($iBuyerPid, 0, $fAmount, $sOrder, '_bx_credits_txt_history_info_refund');
+            return false;
+        }
+
+        return true;
+    }
 
     /**
      * Delete all content by profile 

+ 50 - 0
modules/boonex/credits/classes/BxCreditsTemplate.php

@@ -135,6 +135,56 @@ class BxCreditsTemplate extends BxBaseModGeneralTemplate
             'js_code' => $oPayment->getCartJs()
         ));
     }
+
+    public function getPopupSubscribe($oBuyer, $oSeller, $aData)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $bDynamic = bx_is_dynamic_request();
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObject('subscribe');
+
+        $fRate = $this->_oConfig->getConversionRateUse();
+        $aCurrency = $this->_oConfig->getCurrency();
+
+        $sCurrencySign = $aCurrency['sign'];
+        if(!empty($aData['currency']['sign']))
+            $sCurrencySign = $aData['currency']['sign'];
+
+        $sPeriod = _t('_bx_credits_txt_subscribe_period_unit_' . $aData['period_unit']);
+        if((int)$aData['period'] > 1);
+            $sPeriod = _t('_bx_credits_txt_subscribe_period_mask', $aData['period'], $sPeriod);
+
+        $sInclude = '';
+        $sInclude .= $this->addJs(array('subscribe.js'), $bDynamic);
+        $sInclude .= $this->addCss(array('main.css', 'subscribe.css'), $bDynamic);
+
+        $sContent = ($bDynamic ? $sInclude : '') . $this->parseHtmlByName('subscribe.html', array(
+            'sp' => $sStylePrefix,
+            'jo' => $sJsObject,
+            'title' => _t('_bx_credits_txt_subscribe_for', $oSeller->getDisplayName()),
+            'item_title' => $aData['title'],
+            'amount' => $sCurrencySign . sprintf("%.2f", (float)($aData['amountm'])),
+            'period' => $sPeriod,
+            'bx_if:show_rate' => array(
+                'condition' => $fRate != 1,
+                'content' => array(
+                    'sp' => $sStylePrefix,
+                    'rate' => $fRate,
+                )
+            ),
+            'bx_if:show_trial' => array(
+                'condition' => (int)$aData['trial'] > 0,
+                'content' => array(
+                    'sp' => $sStylePrefix,
+                    'trial' => _t('_bx_credits_txt_subscribe_trial_mask', $aData['trial'], _t('_bx_credits_txt_subscribe_period_unit_day'))
+                )
+            ),
+            'credits' => $this->getModule()->convertC2S($aData['amountc']),
+            'js_code' => $this->getJsCode('subscribe')
+        ));
+
+        return BxTemplFunctions::getInstance()->transBox($this->_oConfig->getHtmlIds('subscribe_popup'), $sContent);
+    }
 }
 
 /** @} */

+ 14 - 0
modules/boonex/credits/install/langs/en.xml

@@ -211,6 +211,7 @@
     <string name="_bx_credits_txt_history_info_checkout"><![CDATA[Profile to profile checkout]]></string>
     <string name="_bx_credits_txt_history_info_withdraw"><![CDATA[Withdraw by request]]></string>
     <string name="_bx_credits_txt_history_info_service"><![CDATA[Artificially initiated transfer]]></string>
+    <string name="_bx_credits_txt_history_info_refund"><![CDATA[Refund]]></string>
 
     <string name="_bx_credits_msg_empty_author"><![CDATA['Site administrator' is empty. You need to mark some profile as 'Site administrator' in Settings of currently active Payment app.]]></string>
     <string name="_bx_credits_msg_action_performed"><![CDATA[The action was performed successfully.]]></string>
@@ -230,6 +231,19 @@
     <string name="_bx_credits_txt_checkout_rate"><![CDATA[Rate:]]></string>
     <string name="_bx_credits_txt_checkout_qt"><![CDATA[qt.]]></string>
 
+    <!-- Subscribe process -->
+    <string name="_bx_credits_txt_subscribe"><![CDATA[Subscribe]]></string>
+    <string name="_bx_credits_txt_subscribe_for"><![CDATA[Subscribe for:]]></string>    
+    <string name="_bx_credits_txt_subscribe_period_mask"><![CDATA[{0} {1}]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_year"><![CDATA[yr.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_month"><![CDATA[mo.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_week"><![CDATA[wk.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_day"><![CDATA[d.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_hour"><![CDATA[hr.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_minute"><![CDATA[min.]]></string>
+    <string name="_bx_credits_txt_subscribe_trial"><![CDATA[Trial:]]></string>
+    <string name="_bx_credits_txt_subscribe_trial_mask"><![CDATA[{0} {1}]]></string>
+
     <string name="_bx_credits_err_unknown_buyer"><![CDATA[Login required.]]></string>
     <string name="_bx_credits_err_unknown_seller"><![CDATA[Unknown seller.]]></string>
     <string name="_bx_credits_err_incorrect_data"><![CDATA[Incorrect input data. Please report.]]></string>

+ 62 - 0
modules/boonex/credits/js/subscribe.js

@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxCreditsSubscribe(oOptions) {
+    this._sActionsUri = oOptions.sActionUri;
+    this._sActionsUrl = oOptions.sActionUrl;
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxCreditsSubscribe' : oOptions.sObjName;
+    this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'slide' : oOptions.sAnimationEffect;
+    this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+}
+
+BxCreditsSubscribe.prototype.subscribe = function(oElement) {
+    var $this = this;
+    var oParams = this._getDefaultData();
+
+    if(oElement)
+        this.loadingInBox(oElement, true);
+
+    jQuery.get (
+        this._sActionsUrl + 'subscribe',
+        oParams,
+        function(oData) {
+            if(oElement)
+                $this.loadingInBox(oElement, false);
+
+            processJsonData(oData);
+        },
+        'json'
+    );
+};
+
+BxCreditsSubscribe.prototype.loadingInButton = function(e, bShow) {
+    if($(e).length)
+        bx_loading_btn($(e), bShow);
+    else
+        bx_loading($('body'), bShow);	
+};
+
+BxCreditsSubscribe.prototype.loadingInBox = function(e, bShow) {
+    var oParent = $(e).length ? $(e).parents('.bx-def-box:first') : $('body'); 
+    bx_loading(oParent, bShow);
+};
+
+BxCreditsSubscribe.prototype.loadingInBlock = function(e, bShow) {
+    var oParent = $(e).length ? $(e).parents('.bx-db-container:first') : $('body'); 
+    bx_loading(oParent, bShow);
+};
+
+BxCreditsSubscribe.prototype._getDefaultData = function() {
+    var oDate = new Date();
+    return jQuery.extend({}, this._oRequestParams, {_t:oDate.getTime()});
+};
+
+/** @} */

+ 4 - 3
modules/boonex/credits/template/checkout.html

@@ -14,18 +14,19 @@
                 <div class="bx-def-hr bx-def-margin-top"></div>
                 <div class="__sp__-cb-row __sp__-cbr-amount bx-def-margin-top">
                     <div class="__sp__-cbr-caption bx-def-font-grayed"><bx_text:_bx_credits_txt_checkout_price_money /></div>
-                    <div class="__sp__-cbr-value bx-def-font-h3">__amount__</div>
+                    <div class="__sp__-cbr-value bx-def-margin-thd-top bx-def-font-large">__amount__</div>
                 </div>
                 <bx_if:show_rate>
                     <div class="__sp__-cb-row __sp__-cbr-rate bx-def-margin-sec-top">
                         <div class="__sp__-cbr-caption bx-def-font-grayed"><bx_text:_bx_credits_txt_checkout_rate /></div>
-                        <div class="__sp__-cbr-value bx-def-font-large">__rate__</div>
+                        <div class="__sp__-cbr-value bx-def-margin-thd-top bx-def-font-large">__rate__</div>
                     </div>
                 </bx_if:show_rate>
                 <div class="__sp__-cb-row __sp__-cbr-credits bx-def-margin-sec-top">
                     <div class="__sp__-cbr-caption bx-def-font-grayed"><bx_text:_bx_credits_txt_checkout_price_credits /></div>
-                    <div class="__sp__-cbr-value bx-def-font-h3">__credits__</div>
+                    <div class="__sp__-cbr-value bx-def-margin-thd-top bx-def-font-large">__credits__</div>
                 </div>
+                <div class="bx-def-hr bx-def-margin-top"></div>
                 <div class="__sp__-cb-row __sp__-cbr-actions bx-def-margin-top">
                     <a class="bx-btn" href="javascript:void(0)" onclick="javascript:__jo__.checkout(this)"><bx_text:_bx_credits_txt_checkout /></a>
                 </div>

+ 10 - 0
modules/boonex/credits/template/css/checkout.css

@@ -21,6 +21,16 @@
     display: inline-block;
 }
 
+.bx-credits-cb-row {
+    position: relative;
+    display: -webkit-flex;
+    display: flex;
+    -webkit-align-items: center;
+    align-items: center;
+    -webkit-justify-content: space-between;
+    justify-content: space-between;
+}
+
 .bx-credits-cb-item {
     position: relative;
     display: inline-block;

+ 47 - 0
modules/boonex/credits/template/css/subscribe.css

@@ -0,0 +1,47 @@
+/*--- Subscribe popup ---*/
+.bx-credits-subscribe {
+    position: relative;
+}
+.bx-credits-subscribe-cnt {
+    position: relative;
+    display: -webkit-flex;
+    display: flex;
+    -webkit-align-items: center;
+    align-items: center;
+    -webkit-justify-content: center;
+    justify-content: center;
+    width: 100%;
+    height: 100%;
+}
+
+.bx-credits-subscribe-box {
+    position: relative;
+    min-width: 18rem;
+}
+
+.bx-credits-sb-row {
+    position: relative;
+    display: -webkit-flex;
+    display: flex;
+    -webkit-align-items: center;
+    align-items: center;
+    -webkit-justify-content: space-between;
+    justify-content: space-between;
+}
+
+.bx-credits-sbr-actions {
+    text-align: center;
+}
+
+.bx-credits-sbr-actions .bx-btn {
+    display: inline-block;
+    float: none;
+    width: 80%;
+}
+
+.bx-credits-sb-row.bx-credits-sbr-title,
+.bx-credits-sb-row.bx-credits-sbr-item,
+.bx-credits-sb-row.bx-credits-sbr-actions {
+    -webkit-justify-content: center;
+    justify-content: center;
+}

+ 36 - 0
modules/boonex/credits/template/subscribe.html

@@ -0,0 +1,36 @@
+<div class="__sp__-subscribe">
+    <div class="__sp__-subscribe-cnt">
+        <div class="__sp__-subscribe-box">
+            <div class="__sp__-subscribe-box-cnt bx-def-padding">
+                <div class="__sp__-sb-row __sp__-sbr-title bx-def-font-h2">__title__</div>
+                <div class="__sp__-sb-row __sp__-sbr-item bx-def-margin-top">__item_title__</div>
+                <div class="bx-def-hr bx-def-margin-top"></div>
+                <div class="__sp__-sb-row __sp__-sbr-amount bx-def-margin-top">
+                    <div class="__sp__-sbr-caption bx-def-font-grayed"><bx_text:_bx_credits_txt_checkout_price_money /></div>
+                    <div class="__sp__-sbr-value bx-def-margin-thd-top bx-def-font-large">__amount__ / __period__</div>
+                </div>
+                <bx_if:show_rate>
+                    <div class="__sp__-sb-row __sp__-sbr-rate bx-def-margin-sec-top">
+                        <div class="__sp__-sbr-caption bx-def-font-grayed"><bx_text:_bx_credits_txt_checkout_rate /></div>
+                        <div class="__sp__-sbr-value bx-def-margin-thd-top bx-def-font-large">__rate__</div>
+                    </div>
+                </bx_if:show_rate>
+                <div class="__sp__-sb-row __sp__-sbr-credits bx-def-margin-sec-top">
+                    <div class="__sp__-sbr-caption bx-def-font-grayed"><bx_text:_bx_credits_txt_checkout_price_credits /></div>
+                    <div class="__sp__-sbr-value bx-def-margin-thd-top bx-def-font-large">__credits__ / __period__</div>
+                </div>
+                <bx_if:show_trial>
+                    <div class="__sp__-sb-row __sp__-sbr-trial bx-def-margin-sec-top">
+                        <div class="__sp__-sbr-caption bx-def-font-grayed"><bx_text:_bx_credits_txt_subscribe_trial /></div>
+                        <div class="__sp__-sbr-value bx-def-margin-thd-top bx-def-font-large">__trial__</div>
+                    </div>
+                </bx_if:show_trial>
+                <div class="bx-def-hr bx-def-margin-top"></div>
+                <div class="__sp__-sb-row __sp__-sbr-actions bx-def-margin-top">
+                    <a class="bx-btn" href="javascript:void(0)" onclick="javascript:__jo__.subscribe(this)"><bx_text:_bx_credits_txt_subscribe /></a>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+__js_code__

+ 134 - 40
modules/boonex/payment/classes/BxPaymentProviderCredits.php

@@ -27,38 +27,68 @@ class BxPaymentProviderCredits extends BxBaseModPaymentProvider implements iBxBa
         $this->_sModuleCredits = $this->_oModule->_oConfig->CNF['MODULE_CREDITS'];
     }
 
-    public function initializeCheckout($iPendingId, $aCartInfo, $bRecurring = false, $iRecurringDays = 0)
+    public function initializeCheckout($iPendingId, $aCartInfo, $sRedirect = '')
     {
-        if(!BxDolModuleQuery::getInstance()->isEnabledByName($this->_sModuleCredits)) {
-            $this->_oModule->_oTemplate->displayPageCodeError('_bx_payment_cdt_err_not_available');
-            exit;
-        }
+        if(!BxDolModuleQuery::getInstance()->isEnabledByName($this->_sModuleCredits))
+            return $this->_sLangsPrefix . 'cdt_err_not_available';
+
+        $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 $this->_sLangsPrefix . 'err_already_processed';
 
         $iSellerId = (int)$aCartInfo['vendor_id'];
-        $sCheckoutUrl = BxDolService::call($this->_sModuleCredits, 'get_checkout_url');
+        $sCustomData = $this->_constructCustomData($aCartInfo['vendor_id'], $iPendingId);
         $sReturnDataUrl = $this->getReturnDataUrl($iSellerId);
-        
-
-        $aFormData = array(
-            'currency_code' => $aCartInfo['vendor_currency_code'],
-            'currency_sign' => $aCartInfo['vendor_currency_sign'],
-            'seller' => $iSellerId,
-            'amount' => (float)$aCartInfo['items_price'],
-            'items_count' => count($aCartInfo['items']),
-            'custom' => $this->_constructCustomData($aCartInfo['vendor_id'], $iPendingId),
-            'return_data_url' => $sReturnDataUrl,
-        );
-
-        $iIndex = 0;
-        foreach($aCartInfo['items'] as $aItem) {
-            $aFormData['item_title_' . $iIndex] = bx_process_output($aItem['title']);
-            $aFormData['item_quantity_' . $iIndex] = $aItem['quantity'];
-
-            $iIndex += 1;
-        }
 
-        $this->_oModule->_oTemplate->displayPageCodeRedirect($sCheckoutUrl, $aFormData);
-        exit;
+        switch($aPending['type']) {
+            case BX_PAYMENT_TYPE_SINGLE:
+                $aFormData = array(
+                    'currency_code' => $aCartInfo['vendor_currency_code'],
+                    'currency_sign' => $aCartInfo['vendor_currency_sign'],
+                    'seller' => $iSellerId,
+                    'amount' => (float)$aCartInfo['items_price'],
+                    'items_count' => count($aCartInfo['items']),
+                    'custom' => $sCustomData,
+                    'return_data_url' => $sReturnDataUrl,
+                );
+
+                $iIndex = 0;
+                foreach($aCartInfo['items'] as $aItem) {
+                    $aFormData['item_title_' . $iIndex] = bx_process_output($aItem['title']);
+                    $aFormData['item_quantity_' . $iIndex] = $aItem['quantity'];
+
+                    $iIndex += 1;
+                }
+
+                $sCheckoutUrl = bx_srv($this->_sModuleCredits, 'get_checkout_url');
+                $this->_oModule->_oTemplate->displayPageCodeRedirect($sCheckoutUrl, $aFormData);
+                exit;
+
+            case BX_PAYMENT_TYPE_RECURRING:
+                $aItem = array_shift($aCartInfo['items']);
+
+                return array(
+                    'code' => 0,
+                    'popup' => array(
+                        'html' =>bx_srv($this->_sModuleCredits, 'get_popup_subscribe', array(array(
+                            'currency_code' => $aCartInfo['vendor_currency_code'],
+                            'currency_sign' => $aCartInfo['vendor_currency_sign'],
+                            'seller' => $iSellerId,
+                            'title' => bx_process_output($aItem['title']),
+                            'amount' => $aItem['price_recurring'],
+                            'period' => $aItem['period_recurring'],
+                            'period_unit' => $aItem['period_unit_recurring'],
+                            'trial' => $aItem['trial_recurring'],
+                            'custom' => $sCustomData,
+                            'return_data_url' => $sReturnDataUrl,
+                        ))),
+                        'options' => array(
+                            'closeOnOuterClick' => true,
+                            'removeOnClose' => true
+                        )
+                    )
+                );
+        }
     }
 
     public function finalizeCheckout(&$aData)
@@ -72,7 +102,7 @@ class BxPaymentProviderCredits extends BxBaseModPaymentProvider implements iBxBa
 
         if(!empty($aResult['pending_id']))
             $this->_oModule->_oDb->updateOrderPending($aResult['pending_id'], array(
-                'order' => isset($aData['o']) ? $aData['o'] : '',
+                'order' => $aResult['order'],
                 'error_code' => $aResult['code'],
                 'error_msg' => _t($aResult['message'])
             ));
@@ -80,6 +110,48 @@ class BxPaymentProviderCredits extends BxBaseModPaymentProvider implements iBxBa
         return $aResult;
     }
 
+    public function makePayment($mixedPending)
+    {
+        if(!BxDolModuleQuery::getInstance()->isEnabledByName($this->_sModuleCredits))
+            return array('code' => 1, 'message' => $this->_sLangsPrefix . 'cdt_err_not_available');
+
+        if(!is_array($mixedPending))
+            $mixedPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => (int)$mixedPending));
+
+        if(empty($mixedPending) || !is_array($mixedPending))
+            return array('code' => 2, 'message' => $this->_sLangsPrefix . 'err_not_found_pending');
+
+        $iClient = (int)$mixedPending['client_id'];
+        $oClient = BxDolProfile::getInstance($iClient);
+
+        $iSeller = (int)$mixedPending['seller_id'];
+        $oSeller = BxDolProfile::getInstance($iSeller);
+
+        if(!$oClient || !$oSeller)
+            return array('code' => 3, 'message' => $this->_sLangsPrefix . 'err_wrong_data');
+
+        $fAmount = (float)$mixedPending['amount'];
+        if(!bx_srv($this->_sModuleCredits, 'make_payment', array($iClient, $fAmount, $iSeller, $mixedPending['order'])))
+            return array('code' => 4, 'message' => $this->_sLangsPrefix . 'err_cannot_perform');
+
+        return true;        
+    }
+
+    public function cancelRecurring($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        bx_alert($this->MODULE, $this->_sName . '_cancel_subscription', 0, false, array(
+            'pending_id' => $iPendingId,
+            'subscription_id' => $sSubscriptionId,
+        ));
+
+        return true;
+    }
+
+    public function getMenuItemsActionsRecurring($iClientId, $iVendorId, $aParams = array())
+    {
+        return array();
+    }
+
     protected function _finalizeCheckout(&$aData)
     {
         list($iSellerId, $iPendingId) = $this->_deconstructCustomData($aData['c']);
@@ -91,25 +163,47 @@ class BxPaymentProviderCredits extends BxBaseModPaymentProvider implements iBxBa
         if(empty($aPending) || !is_array($aPending))
             return array('code' => 1, 'message' => $this->_sLangsPrefix . 'err_not_found_pending');
 
-        if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
-            return array('code' => 2, 'message' => $this->_sLangsPrefix . 'err_already_processed');
-
         if(empty($this->_aOptions) || !BxDolModuleQuery::getInstance()->isEnabledByName($this->_sModuleCredits))
-            return array('code' => 3, 'message' => $this->_sLangsPrefix . 'err_incorrect_provider', 'pending_id' => $iPendingId);
-
-        $sOrder = $aData['o'];
-        if(!BxDolService::call($this->_sModuleCredits, 'validate_checkout', array((int)$aPending['seller_id'], (int)$aPending['client_id'], (float)$aPending['amount'], $sOrder)))
-            return array('code' => 4, 'message' => $this->_sLangsPrefix . 'cdt_err_wrong_transaction', 'pending_id' => $iPendingId);
+            return array('code' => 2, 'message' => $this->_sLangsPrefix . 'err_incorrect_provider', 'pending_id' => $iPendingId);
+
+        switch($aPending['type']) {
+            case BX_PAYMENT_TYPE_SINGLE:
+                if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
+                    return array('code' => 3, 'message' => $this->_sLangsPrefix . 'err_already_processed');
+
+                $sOrder = $aData['o'];
+                if(!bx_srv($this->_sModuleCredits, 'validate_checkout', array((int)$aPending['seller_id'], (int)$aPending['client_id'], (float)$aPending['amount'], $sOrder)))
+                    return array('code' => 4, 'message' => $this->_sLangsPrefix . 'cdt_err_wrong_transaction', 'pending_id' => $iPendingId);
+
+                $aResult = array(
+                    'message' => $this->_sLangsPrefix . 'cdt_msg_charged',
+                    'order' => !empty($aData['o']) ? $aData['o'] : '',
+                    'paid' => true
+                );
+                break;
+
+            case BX_PAYMENT_TYPE_RECURRING:
+                $sSubscriptionId = bx_process_input($aData['sb']);
+
+                $aResult = array(
+                    'message' => $this->_sLangsPrefix . 'cdt_msg_subscribed',
+                    'customer_id' => bx_process_input($aData['cs']),
+                    'subscription_id' => $sSubscriptionId,
+                    'trial' => (bool)$aData['tr'],
+                    'order' => $sSubscriptionId,
+                );
+                break;
+        }
 
         $oClient = BxDolProfile::getInstance($aPending['client_id']);
-        return array(
+        return array_merge(array(
             'code' => BX_PAYMENT_RESULT_SUCCESS, 
-            'message' => $this->_sLangsPrefix . 'cdt_msg_processed',
+            'message' => '',
             'pending_id' => $iPendingId,
             'client_name' => $oClient->getDisplayName(),
             'client_email' => $oClient->getAccountObject()->getEmail(),
-            'paid' => true
-        );
+            'order' => ''
+        ), $aResult);
     }
 
     protected function _constructCustomData()

+ 14 - 0
modules/boonex/russian/data/langs/bx_credits/ru.xml

@@ -211,6 +211,7 @@
     <string name="_bx_credits_txt_history_info_checkout"><![CDATA[Выплаты между профилями]]></string>
     <string name="_bx_credits_txt_history_info_withdraw"><![CDATA[Снятие по запросу]]></string>
     <string name="_bx_credits_txt_history_info_service"><![CDATA[Искусственно инициированный перевод]]></string>
+    <string name="_bx_credits_txt_history_info_refund"><![CDATA[Возврат средств]]></string>
 
     <string name="_bx_credits_msg_empty_author"><![CDATA['Администратор сайта' не задан. Вам необходимо пометить какой-либо профайл как 'Администратор сайта' в настройках активного на данный момент платежного модуля.]]></string>
     <string name="_bx_credits_msg_action_performed"><![CDATA[Действие было успешно выполнено.]]></string>
@@ -230,6 +231,19 @@
     <string name="_bx_credits_txt_checkout_rate"><![CDATA[Коэффициент:]]></string>
     <string name="_bx_credits_txt_checkout_qt"><![CDATA[шт.]]></string>
 
+    <!-- Subscribe process -->
+    <string name="_bx_credits_txt_subscribe"><![CDATA[Подписаться]]></string>
+    <string name="_bx_credits_txt_subscribe_for"><![CDATA[Подписаться на:]]></string>
+    <string name="_bx_credits_txt_subscribe_period_mask"><![CDATA[{0} {1}]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_year"><![CDATA[гд.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_month"><![CDATA[мес.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_week"><![CDATA[нед.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_day"><![CDATA[дн.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_hour"><![CDATA[ч.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_minute"><![CDATA[мин.]]></string>
+    <string name="_bx_credits_txt_subscribe_trial"><![CDATA[Пробный период:]]></string>
+    <string name="_bx_credits_txt_subscribe_trial_mask"><![CDATA[{0} {1}]]></string>
+
     <string name="_bx_credits_err_unknown_buyer"><![CDATA[Необходимо войти.]]></string>
     <string name="_bx_credits_err_unknown_seller"><![CDATA[Неизвестный продавец.]]></string>
     <string name="_bx_credits_err_incorrect_data"><![CDATA[Неверные входные данные. Пожалуйста, оповестите нас об этой проблеме.]]></string>