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

Ticket #3709 - Payment: Integrate One-Time payments in the latest Chargebee integration.

AntonLV 4 лет назад
Родитель
Сommit
c24cb9c46c

+ 6 - 0
modules/base/payment/classes/BxBaseModPaymentCart.php

@@ -237,6 +237,12 @@ class BxBaseModPaymentCart extends BxDol
         return isset($aContent[$iSellerId]) && is_array($aContent[$iSellerId]) ? $aContent[$iSellerId] : array();
     }  
 
+    public function getCartItems($iUserId, $iSellerId = 0)
+    {
+        $aContent = $this->_parseByVendor($iUserId);
+        return $iSellerId != 0 && isset($aContent[$iSellerId]) && is_array($aContent[$iSellerId]) ? $aContent[$iSellerId] : [];
+    }  
+
     protected function _parseByVendor($iUserId)
     {                            
         $aCart = $this->_oModule->_oDb->getCartContent($iUserId);

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

@@ -711,6 +711,28 @@ class BxPaymentModule extends BxBaseModPaymentModule
         exit;
     }
 
+    public function actionInitializeCheckoutJson($sType)
+    {
+    	if(!$this->isLogged())
+            return echoJson(['code' => 1, 'message' => _t($this->_sLangsPrefix . 'err_required_login')]);
+
+        if(bx_get('seller_id') !== false && bx_get('provider') !== false && bx_get('items') !== false) {
+            $iSellerId = bx_process_input(bx_get('seller_id'), BX_DATA_INT);
+            $sProvider = bx_process_input(bx_get('provider'));
+            $aItems = bx_process_input(bx_get('items'));
+
+            $mixedResult = $this->serviceInitializeCheckout(BX_PAYMENT_TYPE_SINGLE, $iSellerId, $sProvider, $aItems);
+            if($mixedResult === false)
+                return echoJson(['code' => 2, 'message' => _t($this->_sLangsPrefix . 'err_cannot_perform')]);
+            if(is_string($mixedResult))
+                return echoJson(['code' => 3, 'message' => $mixedResult]);
+            else if(is_array($mixedResult))
+                return echoJson($mixedResult);
+        }
+
+        return echoJson(['code' => 0, 'redirect' => $this->_oConfig->getUrl('URL_CART')]);
+    }
+
     /**
      * @page service Service Calls
      * @section bx_payment Payment

+ 77 - 8
modules/boonex/payment/classes/BxPaymentProviderChargebee.php

@@ -17,6 +17,7 @@ use ChargeBee\ChargeBee\Models\Addon;
 use ChargeBee\ChargeBee\Models\Customer;
 use ChargeBee\ChargeBee\Models\HostedPage;
 use ChargeBee\ChargeBee\Models\Subscription;
+use ChargeBee\ChargeBee\Models\Invoice;
 
 class BxPaymentProviderChargebee extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
 {
@@ -57,7 +58,7 @@ class BxPaymentProviderChargebee extends BxBaseModPaymentProvider implements iBx
         $aClient = $this->_oModule->getProfileInfo();
         $aVendor = $this->_oModule->getProfileInfo($aCartInfo['vendor_id']);
 
-        $oPage = $this->createHostedPage($aItem, $aClient, $aVendor, $iPendingId);
+        $oPage = $this->createHostedPageRecurring($aItem, $aClient, $aVendor, $iPendingId);
         if($oPage === false)
             return $this->_sLangsPrefix . 'err_cannot_perform';
 
@@ -120,21 +121,65 @@ class BxPaymentProviderChargebee extends BxBaseModPaymentProvider implements iBx
         return $this->deleteSubscription($sSubscriptionId);
     }
 
-    public function createHostedPage($aItem, $aClient, $aVendor = array(), $iPendingId = 0)
+    public function createHostedPageSingle($aItem, $aClient, $aVendor = array(), $iPendingId = 0)
     {
         $oPage = false;
 
         try {
-            $aPage = array(
+            $aPage = [
                 'embed' => false, //--- Note. 'embed' should be disabled to allow payments via PayPal
-                'subscription' => array(
+                'charges' => [
+                    [
+                        'amount' => $aItem['amount'], 
+                        'description' => $aItem['description'],
+                    ]
+                ],
+                'customer' => [
+                    'email' => $aClient['email'],
+                    'firstName' => $aClient['name']
+                ],
+            ];
+
+            if(!empty($aItem['addons']) && is_array($aItem['addons']))
+                $aPage['addons'] = $aItem['addons'];
+
+            if(!empty($aVendor) && is_array($aVendor) && !empty($aVendor))
+                $aPage['redirectUrl'] = bx_append_url_params($this->getReturnDataUrl($aVendor['id']), array(
+                    'pending_id' => $iPendingId
+                ));
+
+            Environment::configure($this->_getSite(), $this->_getApiKey());
+            $oResult = HostedPage::checkoutOneTime($aPage);
+
+            $oPage = $oResult->hostedPage();
+        }
+        catch (Exception $oException) {
+            $iError = $oException->getCode();
+            $sError = $oException->getMessage();
+
+            $this->log('Create Hosted Page Error: ' . $sError . '(' . $iError . ')');
+
+            return false;
+        }
+
+        return $oPage;
+    }
+
+    public function createHostedPageRecurring($aItem, $aClient, $aVendor = array(), $iPendingId = 0)
+    {
+        $oPage = false;
+
+        try {
+            $aPage = [
+                'embed' => false, //--- Note. 'embed' should be disabled to allow payments via PayPal
+                'subscription' => [
                     'planId' => $aItem['name']
-                ),
-                'customer' => array(
+                ],
+                'customer' => [
                     'email' => $aClient['email'],
                     'firstName' => $aClient['name']
-                ), 
-            );
+                ], 
+            ];
 
             if(!empty($aItem['addons']) && is_array($aItem['addons']))
                 $aPage['addons'] = $aItem['addons'];
@@ -251,6 +296,30 @@ class BxPaymentProviderChargebee extends BxBaseModPaymentProvider implements iBx
         return true;
     }
 
+    public function retrieveInvoice($sInvoiceId)
+    {
+        $oInvoice = null;
+
+        try {
+            Environment::configure($this->_getSite(), $this->_getApiKey());
+            $oResult = Invoice::retrieve($sInvoiceId);
+
+            $oInvoice = $oResult->invoice();
+            if($oInvoice->id != $sInvoiceId)
+                return false;
+        }
+        catch (Exception $oException) {
+            $iError = $oException->getCode();
+            $sError = $oException->getMessage();
+
+            $this->log('Retrieve Invoice Error: ' . $sError . '(' . $iError . ')');
+
+            return false;
+        }
+
+        return $oInvoice;
+    }
+
     public function retrieveAddons($sStatus = 'active', $iLimit = 0)
     {
         $aAddons = array();

+ 137 - 40
modules/boonex/payment/classes/BxPaymentProviderChargebeeV3.php

@@ -32,7 +32,44 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
         );
     }
 
-    public function actionGetHostedPage($iClientId, $iVendorId, $sItemName)
+    public function actionGetHostedPageSingle($iClientId, $iVendorId)
+    {
+        $this->initOptionsByVendor($iVendorId);
+
+        $aCartInfo = $this->_oModule->getObjectCart()->getInfo(BX_PAYMENT_TYPE_SINGLE, $iClientId, $iVendorId);
+
+        $aItem = [
+            'amount' => 100 * $aCartInfo['items_price'], 
+            'description' => _t($this->_sLangsPrefix . 'txt_payment_to', $aCartInfo['vendor_name'])
+        ];
+
+        $mixedItemAddons = bx_process_input(bx_get('addons'));
+        if(!empty($mixedItemAddons)) {
+            $aItemAddons = is_array($mixedItemAddons) ? $mixedItemAddons : $this->_oModule->_oConfig->s2a($mixedItemAddons);
+
+            foreach($aItemAddons as $sItemAddon)
+                if(!isset($aItem['addons'][$sItemAddon]))
+                    $aItem['addons'][$sItemAddon] = array(
+                        'id' => $sItemAddon,
+                        'quantity' => 1
+                    );
+                else 
+                    $aItem['addons'][$sItemAddon]['quantity'] += 1;
+
+            $aItem['addons'] = array_values($aItem['addons']);
+        }
+
+        $aClient = $this->_oModule->getProfileInfo($iClientId);
+
+        $oPage = $this->createHostedPageSingle($aItem, $aClient);
+        if($oPage === false)
+            return echoJson(array());
+
+        header('Content-type: text/html; charset=utf-8');
+        echo $oPage->toJson();
+    }
+
+    public function actionGetHostedPageRecurring($iClientId, $iVendorId, $sItemName)
     {
         $this->initOptionsByVendor($iVendorId);
 
@@ -55,7 +92,7 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
         }
         $aClient = $this->_oModule->getProfileInfo($iClientId);
 
-        $oPage = $this->createHostedPage($aItem, $aClient);
+        $oPage = $this->createHostedPageRecurring($aItem, $aClient);
         if($oPage === false)
             return echoJson(array());
 
@@ -125,22 +162,38 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
         if(!empty($aPending['order']) || !empty($aPending['error_code']) || !empty($aPending['error_msg']) || (int)$aPending['processed'] != 0)
             return $this->_sLangsPrefix . 'err_already_processed';
 
-        if($aPending['type'] != BX_PAYMENT_TYPE_RECURRING) 
-            return $this->_sLangsPrefix . 'err_wrong_data';
+        $aResult = [];
+        switch($aPending['type']) {
+            case BX_PAYMENT_TYPE_SINGLE:
+                $oInvoice = $oPage->content()->invoice();
+
+                $aResult = [
+                    'code' => 0,
+                    'eval' => $this->_oModule->_oConfig->getJsObject('cart') . '.onCartCheckout(oData);',
+                    'link' => $this->getReturnDataUrl($aVendor['id'], [
+                        'order_id' => $oInvoice->id,
+                        'customer_id' => $oInvoice->customerId,
+                        'pending_id' => $aPending['id'],
+                        'redirect' => $sRedirect
+                    ])
+                ];
+                break;
 
-        $oCustomer = $oPage->content()->customer();
-        $oSubscription = $oPage->content()->subscription();
+            case BX_PAYMENT_TYPE_RECURRING:
+                $aResult = [
+                    'code' => 0,
+                    'eval' => $this->_oModule->_oConfig->getJsObject('cart') . '.onSubscribeSubmit(oData);',
+                    'redirect' => $this->getReturnDataUrl($aVendor['id'], [
+                        'order_id' => $oPage->content()->subscription()->id,
+                        'customer_id' => $oPage->content()->customer()->id,
+                        'pending_id' => $aPending['id'],
+                        'redirect' => $sRedirect
+                    ])
+                ];
+                break;
+        }
 
-        return array(
-            'code' => 0,
-            'eval' => $this->_oModule->_oConfig->getJsObject('cart') . '.onSubscribeSubmit(oData);',
-            'redirect' => $this->getReturnDataUrl($aVendor['id'], array(
-                'order_id' => $oSubscription->id,
-                'customer_id' => $oCustomer->id,
-                'pending_id' => $aPending['id'],
-                'redirect' => $sRedirect
-            ))
-        );
+        return $aResult;
     }
 
     public function finalizeCheckout(&$aData)
@@ -157,33 +210,56 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
         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');
 
-        if($aPending['type'] != BX_PAYMENT_TYPE_RECURRING) 
-            return array('code' => 1, 'message' => $this->_sLangsPrefix . 'err_wrong_data');
-
         $oCustomer = $this->retrieveCustomer($sCustomerId);
-        $oSubscription = $this->retrieveSubscription($sOrderId);
-        if($oCustomer === false || $oSubscription === false)
+        if($oCustomer === false)
             return array('code' => 4, 'message' => $this->_sLangsPrefix . 'err_cannot_perform');
 
-        $aResult = array(
+        $sOrder = '';
+        $aResult = [
             'code' => BX_PAYMENT_RESULT_SUCCESS,
-            'message' => $this->_sLangsPrefix . 'cbee_msg_subscribed',
+            'message' => '',
             'pending_id' => $iPendingId,
-            'customer_id' => $oCustomer->id,
-            'subscription_id' => $oSubscription->id,
             'client_name' => _t($this->_sLangsPrefix . 'txt_buyer_name_mask', $oCustomer->firstName, $oCustomer->lastName),
             'client_email' => $oCustomer->email,
             'paid' => false,
-            'trial' => $oSubscription->status == 'in_trial',
             'redirect' => $sRedirect
-        );
+        ];
+
+        switch($aPending['type']) {
+            case BX_PAYMENT_TYPE_SINGLE:
+                $oInvoice = $this->retrieveInvoice($sOrderId);
+                if($oInvoice === false || empty($oInvoice->linkedPayments))
+                    return ['code' => 4, 'message' => $this->_sLangsPrefix . 'err_cannot_perform'];
+
+                $sOrder = $oInvoice->linkedPayments[0]->txnId;
+                $aResult = array_merge($aResult, [
+                    'message' => $this->_sLangsPrefix . 'cbee_msg_charged',
+                    'paid' => $oInvoice->status == 'paid',
+                ]);
+                break;
+
+            case BX_PAYMENT_TYPE_RECURRING:
+                $oSubscription = $this->retrieveSubscription($sOrderId);
+                if($oSubscription === false)
+                    return ['code' => 4, 'message' => $this->_sLangsPrefix . 'err_cannot_perform'];
+
+                $sOrder = $oSubscription->id;
+                $aResult = array_merge($aResult, [
+                    'message' => $this->_sLangsPrefix . 'cbee_msg_subscribed',
+                    'customer_id' => $oCustomer->id,
+                    'subscription_id' => $oSubscription->id,
+                    'trial' => $oSubscription->status == 'in_trial',
+                ]);
+
+                break;
+        }
 
         //--- Update pending transaction ---//
-        $this->_oModule->_oDb->updateOrderPending($iPendingId, array(
-            'order' => $oSubscription->id,
+        $this->_oModule->_oDb->updateOrderPending($iPendingId, [
+            'order' => $sOrder,
             'error_code' => $aResult['code'],
             'error_msg' => _t($aResult['message'])
-        ));
+        ]);
 
         return $aResult;
     }
@@ -228,17 +304,40 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
     	), $aParams));
     }
 
-    /**
-     * Single time payments aren't available with Chargebee
-     */
-    public function getButtonSingle($iClientId, $iVendorId, $aParams = array())
+    public function getButtonSingle($iClientId, $iVendorId, $aParams = [])
     {
-        return '';
+        $oCart = $this->_oModule->getObjectCart();
+        $aCartInfo = $oCart->getInfo(BX_PAYMENT_TYPE_SINGLE, $iClientId, (int)$iVendorId);
+        if(empty($aCartInfo) || !is_array($aCartInfo))
+            return '';
+
+        $aItems = [];
+        $aCartItems = $oCart->getCartItems($iClientId, $iVendorId);
+        foreach($aCartItems as $aCartItem)
+            $aItems[] = $this->_oModule->_oConfig->descriptorA2S($aCartItem);
+
+    	return $this->_getButton(BX_PAYMENT_TYPE_SINGLE, $iClientId, $iVendorId, array_merge($aParams, [
+            'iSellerId' => $iVendorId,
+            'aItems' => $aItems
+    	]));
     }
 
-    public function getButtonSingleJs($iClientId, $iVendorId, $aParams = array())
+    public function getButtonSingleJs($iClientId, $iVendorId, $aParams = [])
     {
-        return array();
+        $oCart = $this->_oModule->getObjectCart();
+        $aCartInfo = $oCart->getInfo(BX_PAYMENT_TYPE_SINGLE, $iClientId, (int)$iVendorId);
+        if(empty($aCartInfo) || !is_array($aCartInfo))
+            return '';
+
+        $aItems = [];
+        $aCartItems = $oCart->getCartItems($iClientId, $iVendorId);
+        foreach($aCartItems as $aCartItem)
+            $aItems[] = $this->_oModule->_oConfig->descriptorA2S($aCartItem);
+
+    	return $this->_getButtonJs(BX_PAYMENT_TYPE_SINGLE, $iClientId, $iVendorId, array_merge($aParams, [
+            'iSellerId' => $iVendorId,
+            'aItems' => $aItems
+    	]));
     }
 
     public function getButtonRecurring($iClientId, $iVendorId, $aParams = array())
@@ -278,9 +377,7 @@ class BxPaymentProviderChargebeeV3 extends BxPaymentProviderChargebee
         $sJsObject = $this->getJsObject($aParams);
         switch($sType) {
             case BX_PAYMENT_TYPE_SINGLE:
-                /**
-                 * Single time payments aren't available with Chargebee.
-                 */
+                $sJsMethod = $sJsObject . '.checkout(this)';
                 break;
 
             case BX_PAYMENT_TYPE_RECURRING:

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

@@ -588,6 +588,7 @@
         <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_charged"><![CDATA[Payment was successfully processed.]]></string>
 	<string name="_bx_payment_cbee_msg_subscribed"><![CDATA[Thank you! Subscription created. Please, check your email for further instructions.]]></string>
 	<string name="_bx_payment_cbee_txt_checkout_with_single"><![CDATA[Checkout with {0}]]></string>
     <string name="_bx_payment_cbee_txt_checkout_with_recurring"><![CDATA[Checkout with {0}]]></string>

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

@@ -260,7 +260,7 @@ INSERT INTO `bx_payment_providers_options`(`provider_id`, `name`, `type`, `capti
 
 -- 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
-('chargebee_v3', '_bx_payment_cbee_v3_cpt', '_bx_payment_cbee_v3_dsc', 'cbee_v3_', 1, 0, 1, 1, 41, 'BxPaymentProviderChargebeeV3');
+('chargebee_v3', '_bx_payment_cbee_v3_cpt', '_bx_payment_cbee_v3_dsc', 'cbee_v3_', 1, 1, 1, 1, 41, 'BxPaymentProviderChargebeeV3');
 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

+ 64 - 1
modules/boonex/payment/js/chargebee_v3.js

@@ -24,6 +24,11 @@ BxPaymentProviderChargebeeV3.prototype.init = function(oOptions) {
     this._sObjNameCart = oOptions.sObjNameCart == undefined ? '' : oOptions.sObjNameCart;
     this._iClientId = oOptions.iClientId == undefined ? 0 : oOptions.iClientId;
     this._iSellerId = oOptions.iSellerId == undefined ? 0 : oOptions.iSellerId;
+
+    //-- For Single payments only.
+    this._aItems = oOptions.aItems == undefined ? {} : oOptions.aItems;
+
+    //-- For Recurring payments only.
     this._iModuleId  = oOptions.iModuleId == undefined ? 0 : oOptions.iModuleId;
     this._iItemId  = oOptions.iItemId == undefined ? 0 : oOptions.iItemId;
     this._sItemName = oOptions.sItemName == undefined ? '' : oOptions.sItemName;
@@ -38,6 +43,64 @@ BxPaymentProviderChargebeeV3.prototype.init = function(oOptions) {
     this._rHandler = Chargebee.getInstance();
 };
 
+BxPaymentProviderChargebeeV3.prototype.checkout = function(oLink) {
+    return this.checkoutWithAddons(oLink);
+};
+
+BxPaymentProviderChargebeeV3.prototype.checkoutWithAddons = function(oLink, mixedAddons) {
+    var $this = this;
+    var oDate = new Date();
+
+    oLink = jQuery(oLink);
+    if(oLink.hasClass('bx-btn-disabled'))
+        return;
+
+    oLink.addClass('bx-btn-disabled');
+
+    this._rHandler.openCheckout({
+        hostedPage: function() {
+            var oParams = {
+                url: $this._sActionsUrl + 'call/' + $this._sProvider + '/get_hosted_page_single/' + $this._iClientId + '/' + $this._iSellerId + '/',
+                dataType: 'json'
+            };
+            if(mixedAddons != undefined)
+                oParams.data = {
+                    addons: mixedAddons
+                };
+
+            return $.post(oParams);
+        },
+        success: function(sHostedPageId) {
+            $this.loadingInPopup(oLink, true);
+
+            var oParams = {
+                seller_id: $this._iSellerId,
+                provider: $this._sProvider,
+                items: $this._aItems,
+                page_id: sHostedPageId,
+                _t: oDate.getTime()
+            };
+
+            $.post(
+                $this._sActionsUrl + 'initialize_checkout_json/',
+                oParams,
+                function(oData){
+                    $this.loadingInPopup(oLink, true);
+
+                    processJsonData(oData);
+                },
+                'json'
+            );
+        },
+        close: function() {
+            oLink.removeClass('bx-btn-disabled');
+        }
+    });
+
+    return false;
+};
+
+
 BxPaymentProviderChargebeeV3.prototype.subscribe = function(oLink) {
     return this.subscribeWithAddons(oLink);
 };
@@ -55,7 +118,7 @@ BxPaymentProviderChargebeeV3.prototype.subscribeWithAddons = function(oLink, mix
     this._rHandler.openCheckout({
         hostedPage: function() {
             return $.post({
-                url: $this._sActionsUrl + 'call/' + $this._sProvider + '/get_hosted_page/' + $this._iClientId + '/' + $this._iSellerId + '/' + $this._sItemName + '/',
+                url: $this._sActionsUrl + 'call/' + $this._sProvider + '/get_hosted_page_recurring/' + $this._iClientId + '/' + $this._iSellerId + '/' + $this._sItemName + '/',
                 data: {
                     addons: mixedAddons != undefined ? mixedAddons : $this._sItemAddons
                 }, 

+ 2 - 0
modules/boonex/payment/template/cbee_v3_button_single.html

@@ -0,0 +1,2 @@
+<a class="bx-btn bx-def-margin-top-auto" href="__link__" onclick="javascript: return __js_object__.checkout(this)">__caption__</a>
+__js_code__

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

@@ -590,6 +590,7 @@
     <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_charged"><![CDATA[Платеж успешно принят.]]></string>
     <string name="_bx_payment_cbee_msg_subscribed"><![CDATA[Спасибо! Подписка была создана. Пожалуйста, проверьте ваш емэйл для получения дальнейших инструкций.]]></string>
     <string name="_bx_payment_cbee_txt_checkout_with_single"><![CDATA[Оплатить через {0}]]></string>
     <string name="_bx_payment_cbee_txt_checkout_with_recurring"><![CDATA[Оплатить через {0}]]></string>