woocommerce / woocommerce-gateway-stripe

The official Stripe Payment Gateway for WooCommerce
https://wordpress.org/plugins/woocommerce-gateway-stripe/
231 stars 202 forks source link

Fatal error with PRBs and Subscriptions when the subscription's price or sign-up fee is a string #2966

Open a-danae opened 6 months ago

a-danae commented 6 months ago

Describe the bug There's a fatal error when using Payment Request Buttons, like Google Pay or Apple Pay, with subscriptions in some scenarios.

The error comes from this line.

WC_Subscriptions_Product::get_sign_up_fee() may return a string.

``` PHP Fatal error: Uncaught TypeError: Unsupported operand types: string + string in .../wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php:365 Stack trace: #0 .../wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php(416): WC_Stripe_Payment_Request->get_product_price(Object(WC_Product_Subscription_Variation)) #1 .../wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php(806): WC_Stripe_Payment_Request->get_product_data() #2 .../wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php(839): WC_Stripe_Payment_Request->javascript_params() #3 .../wp-includes/class-wp-hook.php(324): WC_Stripe_Payment_Request->scripts('') #4 .../wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters(NULL, Array) #5 .../wp-includes/plugin.php(517): WP_Hook->do_action(Array) #6 .../wp-includes/script-loader.php(2262): do_action('wp_enqueue_scri...') #7 .../wp-includes/class-wp-hook.php(324): wp_enqueue_scripts('') #8 .../wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters(NULL, Array) #9 .../wp-includes/plugin.php(517): WP_Hook->do_action(Array) #10 .../wp-includes/general-template.php(3052): ```

To Reproduce Steps to reproduce the behavior: TBC (I think this happens with PHP 8.1)

Expected behavior No errors must be triggered when using PRBs with subscriptions.

Additional context

Slack thread p1709233934325419-slack-C7U3Y3VMY

Kudratullah commented 4 months ago

I have encountered this error (PHP 8.0) today. Please see the debug.log output below.

Log:

[06-May-2024 05:52:21 UTC] PHP Fatal error:  Uncaught TypeError: Unsupported operand types: string + int in /home/public_html/wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php:365
Stack trace:
#0 /home/public_html/wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php(416): WC_Stripe_Payment_Request->get_product_price()
#1 /home/public_html/wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php(808): WC_Stripe_Payment_Request->get_product_data()
#2 /home/public_html/wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php(841): WC_Stripe_Payment_Request->javascript_params()
#3 /home/public_html/wp-includes/class-wp-hook.php(324): WC_Stripe_Payment_Request->scripts()
#4 /home/public_html/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters()
#5 /home/public_html/wp-includes/plugin.php(517): WP_Hook->do_action()
#6 /home/public_html/wp-includes/script-loader.php(2265): do_action()
#7 /home/public_html/wp-includes/class-wp-hook.php(324): wp_enqueue_scripts()
#8 /home/public_html/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters()
#9 /home/public_html/wp-includes/plugin.php(517): WP_Hook->do_action()
#10 /home/public_html/wp-includes/general-template.php(3050): do_action()
#11 /home/public_html/wp-content/themes/uptimemonster/header.php(26): wp_head()
#12 /home/public_html/wp-includes/template.php(810): require_once('...')
#13 /home/public_html/wp-includes/template.php(745): load_template()
#14 /home/public_html/wp-includes/general-template.php(48): locate_template()
#15 /home/public_html/wp-content/themes/uptimemonster/single.php(19): get_header()
#16 /home/public_html/wp-includes/template-loader.php(106): include('...')
#17 /home/public_html/wp-blog-header.php(19): require_once('...')
#18 /home/public_html/index.php(17): require('...')
#19 {main}
  thrown in /home/public_html/wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php on line 365

PHP Version:

PHP 8.0.25 (cli) (built: Oct 28 2022 18:02:51) ( NTS )
Copyright (c) The PHP Group
Zend Engine v4.0.25, Copyright (c) Zend Technologies
    with Zend OPcache v8.0.25, Copyright (c), by Zend Technologies

WordPress & Plugin:

WordPress: 6.5.2
WooCommerce: 8.8.3
WooCommerce Stripe Payment Gateway: 8.2.0
WooCommerce Subscriptions: 5.4.0

Code in question:

https://github.com/woocommerce/woocommerce-gateway-stripe/blob/87c3bd0f76d851ea2b31c612dc836bebd78ab629/includes/payment-methods/class-wc-stripe-payment-request.php#L353-L370

After digging into the code it seems both WC_Product::get_price()and WC_Subscriptions_Product::get_sign_up_fee() can return mixed value (string, int or float) as they get data from post meta and WooCommerce doesn't convert them from string.

Casting both output as float seems resolves the issue in my server.

public function get_product_price( $product ) {
    $product_price = $product->get_price();
    // Add subscription sign-up fees to product price.
    if ( in_array( $product->get_type(), [ 'subscription', 'subscription_variation' ] ) && class_exists( 'WC_Subscriptions_Product' ) ) {
        $product_price = (float) $product->get_price() + (float) WC_Subscriptions_Product::get_sign_up_fee( $product );
    }

    return $product_price;
}
OmarFPG commented 1 day ago

8753128-zen

SusanBradley commented 1 day ago

The issue was in the file: ‘/wp-content/plugins/woocommerce-gateway-stripe/includes/payment-methods/class-wc-stripe-payment-request.php’ line 365, where I had to add (int) casting to both values:

broken:

$product_price = $product->get_price() + WC_Subscriptions_Product::get_sign_up_fee( $product );

fixed:

$product_price = (int)$product->get_price() + (int)WC_Subscriptions_Product::get_sign_up_fee( $product );