Fixed Stripe + User upgrade bug

Aayush

Well-known member
Affected version
2.1.10 Patch 2
PHP version - 7.2.24-0ubuntu0.18.04.6

Class - XF\Payment\Stripe
Function - prepareCost($cost, $currency)

Bug:-
Code:
return intval($cost);

Should be:-
Code:
return intval(strval($cost));

Due to how floating points are represented in PHP, this causes the number to be rounded lower which causes Stripe return value(1 cent lower) and the value stored in database for user upgrade to not match and the user is not upgraded.

DragonByte User Upgrade Coupons was also being used on the site that modified the price which could've caused it but from debugging this particular issue, we found the $purchase->cost to be 4.98 that was being sent and we fixed it by making the code change mentioned above.

$4.98 was being converted to $4.97 for more reference. Let me know if you need more info to help you resolve this.
 
DragonByte User Upgrade Coupons was also being used on the site that modified the price which could've caused it but from debugging this particular issue, we found the $purchase->cost to be 4.98 that was being sent and we fixed it by making the code change mentioned above.
That would explain an old support ticket @ DBTech from January 2020. For reference, here's how User Upgrade Coupons modifies the price:
PHP:
$purchaseObject->cost = $coupon->getDiscountedCost($userUpgrade, $purchaseObject->cost);
PHP:
    public function getDiscountedCost(UserUpgrade $userUpgrade, float $cost): float
    {
        if (!$discount = $this->getApplicableDiscount($userUpgrade))
        {
            return $cost;
        }
      
        switch ($this->coupon_type)
        {
            case 'percent':
                $cost *= (1 - ($discount / 100));
                break;
          
            case 'value':
                $cost -= $discount;
                break;
        }
      
        return max(0, $cost);
    }

PHP:
    public function getApplicableDiscount(UserUpgrade $userUpgrade): float
    {
        if (!count($this->user_upgrade_discounts))
        {
            return $this->coupon_type == 'percent' ? $this->coupon_percent : $this->coupon_value;
        }
      
        foreach ($this->user_upgrade_discounts as $discount)
        {
            if ($discount['user_upgrade_id'] == $userUpgrade->user_upgrade_id)
            {
                return $discount['upgrade_value'] != 0.00 ? $discount['upgrade_value'] : $this->getBaseDiscount($userUpgrade, false);
            }
        }
      
        return 0.00;
    }

PHP:
    public function getBaseDiscount(UserUpgrade $userUpgrade, bool $forDisplay = true, \XF\Language $language = null)
    {
        if ($forDisplay)
        {
            if ($this->coupon_type == 'percent')
            {
                return $this->coupon_percent . '%';
            }
           
            /** @var \XF\Data\Currency $currencyData */
            $currencyData = $this->app()->data('XF:Currency');
            return $currencyData->languageFormat($this->coupon_value, $userUpgrade->cost_currency, $language);
        }
       
        return $this->coupon_type == 'percent' ? $this->coupon_percent : $this->coupon_value;
    }

If there are any changes I can make on my end that avoids this issue, please do let me know.
 
Can the fix be incorporated in your addon?
My add-on does not touch the Stripe payment processor so I don't think so. There are no issues reported with any other payment processor, so I am loathe to make any changes unless advised to do so by the XenForo developers.
 
That is why I wanted to bring it to everyone's attention and find a way to incorporate this change into a long term working solution for everyone.
 
It's likely that is only happening, when combined with the third party add-on. I offer upgrades with stripe on my forum (using just what is built in), and have never had one fail.
 
Yeah, that's maybe why. But this fails only for specific pricing with Stripe. So there are instances when a combination of the addon discount and a different user upgrade pricing doesn't fail. In those cases, the user is successfully upgraded automatically. However, PayPal works regardless. We use crypto currency as well, which works fine!

Maybe the reason of failure could be that we need to send the price in cents to Stripe and the multiplication combined with the way floats are handled in PHP is the reason for this problem.
 
If there's discounting involved, it could be a rounding issue. I'm sure they'll work it out.

My solution to discounting was to provide multiple user upgrade packages, with different prices and contract lengths.
 
I noticed the Fixed tag. Can someone update in which version will the fix for this be released?

9.95 is also one such number.
 
Top Bottom