From ecbd97d37b23d65058b77424fb0a0ebdcd699022 Mon Sep 17 00:00:00 2001 From: Bryan Elliott Date: Mon, 21 Oct 2024 22:47:29 -0400 Subject: [PATCH] Update & refine logic to determine if products is expired/expiring. --- .../packages/my-jetpack/_inc/constants.ts | 2 +- .../my-jetpack/src/class-products.php | 2 +- .../my-jetpack/src/products/class-product.php | 80 ++++++++++++++----- .../my-jetpack/src/products/class-stats.php | 21 +++++ .../src/products/class-videopress.php | 44 +++------- 5 files changed, 96 insertions(+), 53 deletions(-) diff --git a/projects/packages/my-jetpack/_inc/constants.ts b/projects/packages/my-jetpack/_inc/constants.ts index fa8036077304e..a6c1ae3416407 100644 --- a/projects/packages/my-jetpack/_inc/constants.ts +++ b/projects/packages/my-jetpack/_inc/constants.ts @@ -36,6 +36,6 @@ export const PRODUCT_STATUSES = { NEEDS_FIRST_SITE_CONNECTION: 'needs_first_site_connection', USER_CONNECTION_ERROR: 'user_connection_error', CAN_UPGRADE: 'can_upgrade', - EXPIRING_SOON: 'expiring_soon', + EXPIRING_SOON: 'expiring', EXPIRED: 'expired', }; diff --git a/projects/packages/my-jetpack/src/class-products.php b/projects/packages/my-jetpack/src/class-products.php index c60bce7fefeae..a5be1d9d4ad9a 100644 --- a/projects/packages/my-jetpack/src/class-products.php +++ b/projects/packages/my-jetpack/src/class-products.php @@ -20,7 +20,7 @@ class Products { const STATUS_USER_CONNECTION_ERROR = 'user_connection_error'; const STATUS_ACTIVE = 'active'; const STATUS_CAN_UPGRADE = 'can_upgrade'; - const STATUS_EXPIRING_SOON = 'expiring_soon'; + const STATUS_EXPIRING_SOON = 'expiring'; const STATUS_EXPIRED = 'expired'; const STATUS_INACTIVE = 'inactive'; const STATUS_MODULE_DISABLED = 'module_disabled'; diff --git a/projects/packages/my-jetpack/src/products/class-product.php b/projects/packages/my-jetpack/src/products/class-product.php index d51e0e024c93b..4394b09a1dd6b 100644 --- a/projects/packages/my-jetpack/src/products/class-product.php +++ b/projects/packages/my-jetpack/src/products/class-product.php @@ -100,6 +100,13 @@ abstract class Product { */ public static $requires_plan = false; + /** + * The feature slug that identifies the highest paid plan + * + * @var string + */ + public static $feature_identifying_paid_plan = ''; + /** * Get the plugin slug * @@ -189,7 +196,7 @@ public static function get_info() { * * @return WP_Error|array */ - private static function get_site_features_from_wpcom() { + public static function get_site_features_from_wpcom() { static $features = null; if ( $features !== null ) { @@ -206,7 +213,11 @@ private static function get_site_features_from_wpcom() { $body = wp_remote_retrieve_body( $response ); $feature_return = json_decode( $body ); - $features = $feature_return->active; + + $features = array( + 'active' => $feature_return->active, + 'available' => $feature_return->available, + ); return $features; } @@ -228,7 +239,7 @@ public static function does_site_have_feature( $feature ) { return false; } - return in_array( $feature, $features, true ); + return in_array( $feature, $features['active'], true ); } /** @@ -407,32 +418,61 @@ public static function has_any_plan_for_product() { return static::has_paid_plan_for_product() || static::has_free_plan_for_product(); } + /** + * Get the product-slugs of the paid plans for this product (not including bundles). + * + * @return array + */ + public static function get_paid_plan_product_slugs() { + return array(); + } + + /** + * Get the product-slugs of the paid bundles/plans that this product/module is included in + * + * @return array + */ + public static function get_paid_bundles_that_include_product() { + $features = static::get_site_features_from_wpcom(); + if ( is_wp_error( $features ) ) { + return array(); + } + $idendifying_feature = static::$feature_identifying_paid_plan; + if ( empty( $features['available'] ) || empty( $idendifying_feature ) ) { + return array(); + } + $paid_bundles = $features['available']->$idendifying_feature; + + return $paid_bundles; + } + /** * Gets the paid plan's expiry status, or null if: no paid plan, or not expired, or not expiring soon. * - * @return string + * @return string|null */ public static function get_paid_plan_expiration_status() { - if ( ! static::has_paid_plan_for_product() ) { - return null; - } - $product_slug = static::get_wpcom_product_slug(); + $paid_plans = array_merge( + static::get_paid_plan_product_slugs(), + static::get_paid_bundles_that_include_product() + ); + $purchases_data = Wpcom_Products::get_site_current_purchases(); if ( is_wp_error( $purchases_data ) ) { return null; } + if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) { foreach ( $purchases_data as $purchase ) { - if ( strpos( $purchase->product_slug, $product_slug ) !== false ) { - // Check if expired or expiring soon - $now = time(); - $expiry_date = strtotime( $purchase->expiry_date ); - $expiring_soon = strtotime( $purchase->expiry_date . ' -30 days' ); - if ( $now > $expiring_soon && $now < $expiry_date ) { - return Products::STATUS_EXPIRING_SOON; - } - if ( $now > $expiry_date ) { - return Products::STATUS_EXPIRED; + foreach ( $paid_plans as $plan ) { + if ( strpos( $purchase->product_slug, $plan ) !== false ) { + // Check if expired or expiring soon + if ( $purchase->expiry_status === Products::STATUS_EXPIRING_SOON ) { + return Products::STATUS_EXPIRING_SOON; + } + if ( $purchase->expiry_status === Products::STATUS_EXPIRED ) { + return Products::STATUS_EXPIRED; + } } } } @@ -539,10 +579,10 @@ public static function get_status() { } } elseif ( static::$requires_user_connection && ! ( new Connection_Manager() )->has_connected_owner() ) { $status = Products::STATUS_USER_CONNECTION_ERROR; - } elseif ( static::is_upgradable() ) { - $status = Products::STATUS_CAN_UPGRADE; } elseif ( static::has_paid_plan_for_product() && in_array( static::get_paid_plan_expiration_status(), Products::$expiring_or_expired_module_statuses, true ) ) { $status = static::get_paid_plan_expiration_status(); + } elseif ( static::is_upgradable() ) { + $status = Products::STATUS_CAN_UPGRADE; } // Check specifically for inactive modules, which will prevent a product from being active } elseif ( static::$module_name && ! static::is_module_active() ) { diff --git a/projects/packages/my-jetpack/src/products/class-stats.php b/projects/packages/my-jetpack/src/products/class-stats.php index 6f1118cf6f4c2..163e5935c5bf2 100644 --- a/projects/packages/my-jetpack/src/products/class-stats.php +++ b/projects/packages/my-jetpack/src/products/class-stats.php @@ -67,6 +67,13 @@ class Stats extends Module_Product { */ public static $has_free_offering = true; + /** + * The feature slug that identifies the paid plan + * + * @var string + */ + public static $feature_identifying_paid_plan = 'stats-paid'; + /** * Get the product name * @@ -237,6 +244,20 @@ public static function has_paid_plan_for_product() { return false; } + /** + * Get the product-slugs of the paid plans for this product (not including bundles) + * + * @return array + */ + public static function get_paid_plan_product_slugs() { + return array( + 'jetpack_stats_yearly', + 'jetpack_stats_monthly', + 'jetpack_stats_bi_yearly', + 'jetpack_stats_pwyw_yearly', + ); + } + /** * Returns a productType parameter for an upgrade URL, determining whether * to show the PWYW upgrade interstitial or commercial upgrade interstitial. diff --git a/projects/packages/my-jetpack/src/products/class-videopress.php b/projects/packages/my-jetpack/src/products/class-videopress.php index aa6e17bb849dd..c837cf5c52225 100644 --- a/projects/packages/my-jetpack/src/products/class-videopress.php +++ b/projects/packages/my-jetpack/src/products/class-videopress.php @@ -8,7 +8,6 @@ namespace Automattic\Jetpack\My_Jetpack\Products; use Automattic\Jetpack\My_Jetpack\Hybrid_Product; -use Automattic\Jetpack\My_Jetpack\Products; use Automattic\Jetpack\My_Jetpack\Wpcom_Products; /** @@ -69,6 +68,13 @@ class Videopress extends Hybrid_Product { */ public static $has_free_offering = true; + /** + * The feature slug that identifies the paid plan + * + * @var string + */ + public static $feature_identifying_paid_plan = 'videopress-1tb-storage'; + /** * Get the product name * @@ -203,39 +209,15 @@ public static function has_paid_plan_for_product() { } /** - * Gets the paid plan's expiry status, or null if: no paid plan, or not expired, or not expiring soon. + * Get the product-slugs of the paid plans for this product (not including bundles) * - * @return boolean + * @return array */ - public static function get_paid_plan_expiration_status() { - $plans_with_videopress = array( + public static function get_paid_plan_product_slugs() { + return array( 'jetpack_videopress', - 'jetpack_complete', - 'jetpack_business', - 'jetpack_premium', + 'jetpack_videopress_monthly', + 'jetpack_videopress_bi_yearly', ); - $purchases_data = Wpcom_Products::get_site_current_purchases(); - if ( is_wp_error( $purchases_data ) ) { - return null; - } - if ( is_array( $purchases_data ) && ! empty( $purchases_data ) ) { - foreach ( $purchases_data as $purchase ) { - foreach ( $plans_with_videopress as $plan ) { - if ( strpos( $purchase->product_slug, $plan ) !== false ) { - // Check if expired or expiring soon - $now = time(); - $expiry_date = strtotime( $purchase->expiry_date ); - $expiring_soon = strtotime( $purchase->expiry_date . ' -30 days' ); - if ( $now > $expiring_soon && $now < $expiry_date ) { - return Products::STATUS_EXPIRING_SOON; - } - if ( $now > $expiry_date ) { - return Products::STATUS_EXPIRED; - } - } - } - } - } - return null; } }