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

Ticket #2941 - Init and Renew notifications.

AntonLV 5 лет назад
Родитель
Сommit
355f2b04fa

+ 2 - 2
modules/boonex/payment/classes/BxPaymentConfig.php

@@ -241,8 +241,8 @@ class BxPaymentConfig extends BxBaseModPaymentConfig
         else
             $aItems = array();
 
-        foreach($aItems as $sItem) {
-            $aItem = $this->descriptorS2A($sItem);
+        foreach($aItems as $mixedItem) {
+            $aItem = is_array($mixedItem) ? $mixedItem : $this->descriptorS2A($mixedItem);
 
             $aResult = array('vendor_id' => $aItem[0], 'module_id' => $aItem[1], 'item_id' => $aItem[2], 'item_count' => $aItem[3]);
             if(isset($aItem[4]))

+ 103 - 22
modules/boonex/payment/classes/BxPaymentProviderAppleInApp.php

@@ -11,16 +11,26 @@
 
 class BxPaymentProviderAppleInApp extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
 {
-    function __construct($aConfig)
+    protected $_sSecret;
+
+    protected $_sVerifyReceipt;
+    protected $_sVerifyReceiptSandbox;
+
+    public function __construct($aConfig)
     {
     	$this->MODULE = 'bx_payment';
 
         parent::__construct($aConfig);
+
+        $this->_sVerifyReceipt = 'https://buy.itunes.apple.com/verifyReceipt';
+        $this->_sVerifyReceiptSandbox = 'https://sandbox.itunes.apple.com/verifyReceipt';
     }
 
     public function initOptions($aOptions)
     {
     	parent::initOptions($aOptions);
+
+        $this->_sSecret = $this->getOption('secret');
     }
 
     public function initializeCheckout($iPendingId, $aCartInfo, $sRedirect = '')
@@ -38,16 +48,29 @@ class BxPaymentProviderAppleInApp extends BxBaseModPaymentProvider implements iB
         $iResult = $this->_processNotification();
         http_response_code($iResult);
     }
-    
+
+    public function getMenuItemsActionsRecurring($iClientId, $iVendorId, $aParams = array())
+    {
+        if(empty($aParams['id']))
+            return array();
+
+        $sPrefix = 'bx-payment-aina-';
+
+        return array(
+            array('id' => $sPrefix . 'cancel', 'name' => $sPrefix . 'cancel', 'class' => '', 'link' => 'https://apps.apple.com/account/subscriptions', 'target' => '_blank', 'title' => _t('_bx_payment_aina_menu_item_title_cancel'))
+        );
+    }
+
     protected function _processNotification()
     {
-    	$sInput = @file_get_contents("php://input");
+        $sPath = "php://input"; //BX_DIRECTORY_PATH_TMP . "aina_renew.json"; // "aina_init.json";
+    	$sInput = @file_get_contents($sPath);
         $aNotification = json_decode($sInput, true);
         if(empty($aNotification) || !is_array($aNotification)) 
             return 404;
 
         $sType = $aNotification['notification_type'];
-        if(!in_array($sType, array('INITIAL_BUY', 'DID_RENEW', 'REFUND', 'CANCEL')))
+        if(!in_array($sType, array('INITIAL_BUY_CUSTOM', 'DID_RENEW', 'REFUND', 'CANCEL')))
             return 200;
 
         $this->log($aNotification, 'Webhooks: ' . (!empty($sType) ? $sType : ''));
@@ -59,49 +82,107 @@ class BxPaymentProviderAppleInApp extends BxBaseModPaymentProvider implements iB
     	return $this->$sMethod($aNotification) ? 200 : 403;
     }
 
-    protected function _processNotificationInitialBuy($aNotification)
+    protected function _processNotificationInitialBuyCustom($aNotification)
     {
-        $sProduct = $aNotification['auto_renew_product_id'];
+        if(!$this->_verifyNotification(array('rt' => $aNotification['original_data']['transactionReceipt'], 'pt' => $aNotification['product'], 'tx' => $aNotification['transaction'], 'txo' => $aNotification['original_transaction'])))
+            return false;
+
+        $sOrder = $aNotification['transaction'];
+        if(!empty($aNotification['original_transaction']))
+            $sOrder = $aNotification['original_transaction'];
+        $sOrder = bx_process_input($sOrder);
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'order', 'order' => $sOrder));
+        if(!empty($aPending) && is_array($aPending) && (int)$aPending['processed'] == 1)
+            return false;
 
-        //TODO: We need to create 'Pending Transaction' to register this and all future payments.
+        $sModule = bx_process_input($aNotification['module']);
+        $aModule = $this->_oModule->_oDb->getModuleByName($sModule);
+        if(empty($aModule) || !is_array($aModule))
+            return false;
 
-        /*
-        $iPendingId = $this->_oDb->insertOrderPending($this->_iUserId, BX_PAYMENT_TYPE_RECURRING, $this->_sName, array(
-            'author_id' => 0,
-            'module_id' => 0,
-            'id' => 0,
-            'quantity' => 1
+        $iClient = (int)$aNotification['profile_id'];
+
+        $aInfo = $this->_oModule->getObjectCart()->getInfo(BX_PAYMENT_TYPE_RECURRING, $iClient, $this->_iVendor, array(
+            array($this->_iVendor, $aModule['id'], bx_process_input($aNotification['product']), (int)$aNotification['count'])
         ));
-         */
+
+        if(empty($aInfo) || $aInfo['vendor_id'] == BX_PAYMENT_EMPTY_ID || empty($aInfo['items']))
+            return false;
+
+        $iPendingId = $this->_oModule->_oDb->insertOrderPending($iClient, BX_PAYMENT_TYPE_RECURRING, $this->_sName, $aInfo);
+        if(empty($iPendingId))
+            return false;
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+
+        $aSubscription = array(
+            'customer_id' => '', 
+            'subscription_id' => $sOrder,
+            'status' => BX_PAYMENT_SBS_STATUS_ACTIVE
+        );
+
+        if(!$this->_oModule->getObjectSubscriptions()->register($aPending, $aSubscription))
+            return false;
+
+        $this->_oModule->_oDb->updateOrderPending($iPendingId, array(
+            'order' => $sOrder,
+            'error_code' => 0,
+            'error_msg' => _t('_bx_payment_aina_msg_subscribed')
+        ));
+
+        return $this->_oModule->registerPayment($iPendingId);
     }
 
     protected function _processNotificationDidRenew(&$aNotification)
     {
-        $sProduct = $aNotification['auto_renew_product_id'];
+        $aRenew = array_shift($aNotification['unified_receipt']['pending_renewal_info']);
+
+        if(!$this->_verifyNotification(array('rt' => $aNotification['unified_receipt']['latest_receipt'], 'pt' => $aRenew['product_id'], 'txo' => $aRenew['original_transaction_id'])))
+            return false;
 
-        //TODO: We need to get 'PendingId' to register payment.
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'order', 'order' => $aRenew['original_transaction_id']));
+        if(empty($aPending) || !is_array($aPending))
+            return false;
   
-        /*
         if($aPending['type'] == BX_PAYMENT_TYPE_RECURRING)
             $this->_oModule->getObjectSubscriptions()->prolong($aPending);
 
         return $this->_oModule->registerPayment($aPending);
-         */
     }
 
     protected function _processNotificationRefund(&$aNotification)
     {
-        $sProduct = $aNotification['auto_renew_product_id'];
-
         //TODO: We need to get 'PendingId' to process refund.
     }
 
     protected function _processNotificationCancel(&$aNotification)
     {
-        $sProduct = $aNotification['auto_renew_product_id'];
-
         //TODO: We need to get 'PendingId' to cancel the subscription.
     }
+
+    protected function _verifyNotification($aParams)
+    {
+        $aRequest = array(
+            'receipt-data' => $aParams['rt'],
+            'password' => $this->_sSecret
+        );
+
+        $sInput = bx_file_get_contents($this->_sVerifyReceipt, $aRequest, 'post-json');
+        $aInput = json_decode($sInput, true);
+
+        if(isset($aInput['status']) && (int)$aInput['status'] == 21007) {
+            $sInput = bx_file_get_contents($this->_sVerifyReceiptSandbox, $aRequest, 'post-json');
+            $aInput = json_decode($sInput, true);
+        }
+
+        if(!empty($aInput['latest_receipt_info']) && is_array($aInput['latest_receipt_info']))
+            foreach($aInput['latest_receipt_info'] as $aRecipt)
+                if($aRecipt['product_id'] == $aParams['pt'] && ((isset($aParams['txo']) && $aRecipt['original_transaction_id'] == $aParams['txo']) || (isset($aParams['tx']) && $aRecipt['transaction_id'] == $aParams['tx'])))
+                    return true;
+
+        return false;
+    }
 }
 
 /** @} */

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

@@ -593,8 +593,12 @@
     <string name="_bx_payment_aina_active_dsc"><![CDATA[Activate(Deactivate) provider.]]></string>
     <string name="_bx_payment_aina_hidden_cpt"><![CDATA[Hidden]]></string>
     <string name="_bx_payment_aina_hidden_dsc"><![CDATA[Recommended. The provider should be hidden in WEB because it can be used in mobile app only.]]></string>
+    <string name="_bx_payment_aina_secret_cpt"><![CDATA[Shared Secret]]></string>
+    <string name="_bx_payment_aina_secret_dsc"><![CDATA[It's the app-specific unique code to receive receipts for auto-renewable subscriptions.]]></string>
     <string name="_bx_payment_aina_notify_url_cpt"><![CDATA[URL for App Store Server Notifications]]></string>
-    
+    <string name="_bx_payment_aina_menu_item_title_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_payment_aina_msg_charged"><![CDATA[Payment was successfully processed.]]></string>
+    <string name="_bx_payment_aina_msg_subscribed"><![CDATA[Thank you! Subscription created. Please, check your email for further instructions.]]></string>
     <string name="_bx_payment_txt_name_apple_in_app"><![CDATA[Apple In-App]]></string>
     <string name="_bx_payment_txt_cart_apple_in_app"><![CDATA[Apple In-App]]></string>
 </resources>

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

@@ -278,7 +278,8 @@ SET @iProviderId = LAST_INSERT_ID();
 INSERT INTO `bx_payment_providers_options`(`provider_id`, `name`, `type`, `caption`, `description`, `extra`, `check_type`, `check_params`, `check_error`, `order`) VALUES
 (@iProviderId, 'aina_active', 'checkbox', '_bx_payment_aina_active_cpt', '_bx_payment_aina_active_dsc', '', '', '', '', 1),
 (@iProviderId, 'aina_hidden', 'checkbox', '_bx_payment_aina_hidden_cpt', '_bx_payment_aina_hidden_dsc', '', '', '', '', 2),
-(@iProviderId, 'aina_notify_url', 'value', '_bx_payment_aina_notify_url_cpt', '', '', '', '', '', 8);
+(@iProviderId, 'aina_secret', 'text', '_bx_payment_aina_secret_cpt', '_bx_payment_aina_secret_dsc', '', '', '', '', 3),
+(@iProviderId, 'aina_notify_url', 'value', '_bx_payment_aina_notify_url_cpt', '', '', '', '', '', 4);
 
 -- GRIDS
 INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES