1. DJ XtAzY

    DJ XtAzY - Mar 27, 2014 New Member

    Credit
    Point
    I created a product that will cost 1 point for a length of 1 day and recurring payments checked. Let's say this person has tons of points and bought this item and agreed to recurring payments. The product shows up fine in the Purchased tab. But a day later, even though he has enough points, it disappears and expired for no reason. He'll need to subscribe/purchase that product again.

    Checking at the database level, the xf_store_product_purchase_active has a record of it when he made the purchase

    upload_2014-3-27_10-49-54

    but the table will be empty after a day later.
     
    Loading...
  2. DJ XtAzY

    DJ XtAzY - Mar 29, 2014 New Member

    Credit
    Point
    Ok I fixed this myself after some php debugging.

    In file Store\Model\ProductPurchase.php with method processExpiredProductPurchases()
    Code:
    if($isRemove || $purchase['recurring'] || !$this->autoRenewPurchased($purchase))
    {
                    $this->_removeProductChange($purchase);
                    $purchaseRecordIds[] = $purchase['product_purchase_id'];
    }
    
    to

    Code:
    if($isRemove && !($purchase['recurring'] && !$this->autoRenewPurchased($purchase)))
    {
                   $this->_removeProductChange($purchase);
                   $purchaseRecordIds[] = $purchase['product_purchase_id'];
    }
    
    The problem with the original code is that it will always remove that purchase since the OR statement will always true. Next it'll still remove this purchase because $purchase['recurring'] is true. Lastly $this->autoRenewPurchased($purchase) will update the end_date of the purchase's record, but it'll still get removed because of the previous variables, plus the product_purchase_id is the same since the purchase record is simply updated.

    With the second code, the condition table output will be something like this

    T && !(T && !(T)) = F, it's recurring and it auto renew purchase successfully, so don't delete
    T && !(T && !(F)) = T, it's recurring but didn't auto renew purchase successfully (prob not enough money)
    T && !(F && !(T)) = T, it's not recurring so it should delete purchase anyways, $this->autoRenewPurchased($purchase) will never return true anwyas
    T && !(F && !(F)) = T, it's not recurring and $this->autoRenewPurchased($purchase) will always return false.

    Now I searched through the entire Store php files and I haven't found any processExpiredProductPurchases($foo, false) so I believe my fix should be safe.
     
    Last edited: Mar 29, 2014
    Brivium likes this.
    #2
  3. Brivium

    Brivium - Mar 29, 2014 XenForo Services Staff Member

    Credit
    Point
    Thanks for your suggestion. We will fix your issues on next upgrade version.

    Regards,
     
    #3
  4. DJ XtAzY

    DJ XtAzY - Apr 4, 2014 New Member

    Credit
    Point
    Actually I found more bugs. First with my code. It should be this.

    In Store/Model/ProductPurchase.php, function processExpiredProductPurchases(array $purchases, $isRemove = true)
    Code:
    if($isRemove || !($purchase['recurring'] && $this->autoRenewPurchased($purchase)))
    {
           $this->_removeProductChange($purchase);
           $purchaseRecordIds[] = $purchase['product_purchase_id'];
    }
    
    So whenever $isRemove is true, it'll always delete the subscriptions. Which is why I propose a change in the CronEntry

    In Store/CronEntry/Store.php
    Code:
    public static function runProductPurchaseExpiredHandle()
    {
            $productPurchaseModel = XenForo_Model::create('Brivium_Store_Model_ProductPurchase');
            $productPurchaseModel->processExpiredProductPurchases(
                //$productPurchaseModel->getExpiredProductPurchases(), true
                   $productPurchaseModel->getExpiredProductPurchases(), false // don't delete subscription yet
            );
    }
    
    Since $isRemove is false, we now have to go through !($purchase['recurring'] && $this->autoRenewPurchased($purchase)). If !(T), that means the subscription successfully renewed, and deletion won't occur since $isRemove || !(T) are both False. If the second condition is true, remove the subscription since the renewal failed.

    The final change I propose involves with all existing Store Products (or just permission related) addons.

    In StoreProduct/AnyProducts/Model/ProductPurchase.php, function _processProductChange()
    Code:
    //return parent::_processProductChange($user, $product, $productTypeId);
    return parent::_processProductChange($user, $product, $productTypeId, $existingPurchased);
    
    Without passing $existingPurchase, the next execution of _processProductChange for other addons will start with NULL, which means the old changes are lost since it'll now take the user's existing permission and overwrite the xf_store_product_change existing settings.

    Let me know what you think and if those changes work for your test. This definitely solved my renewal and permission problems.
     
    Brivium likes this.
    #4
  5. DJ XtAzY

    DJ XtAzY - Jun 20, 2014 New Member

    Credit
    Point
    Ok still think this bug isnt resolved in 1.2.3

    In this statement
    Code:
    if($isRemove || ($purchase['recurring'] && !$this->autoRenewPurchased($purchase))){
                    $this->_removeProductChange($purchase);
                    $purchaseRecordIds[] = $purchase['product_purchase_id'];
                }
    
    
    Since $isRemove is now set to "false" by default, this won't remove the expired product if the person who bought the product did not choose "Recurring Payments". Therefore $purchase['recurring'] is "false", which will cause:

    ($purchase['recurring'] && !$this->autoRenewPurchased($purchase))
    .................F..................&&..............!(T or F) = F
    to become false. I say my original fix in my previous post works

    Code:
    if($isRemove || !($purchase['recurring'] && $this->autoRenewPurchased($purchase))){
                    $this->_removeProductChange($purchase);
                    $purchaseRecordIds[] = $purchase['product_purchase_id'];
                }
    
     
    Last edited: Jun 20, 2014
    #5