From f7865660cf6d4311ca085f1b20ad92e79eccc47b Mon Sep 17 00:00:00 2001 From: Daniele Alessandra Date: Fri, 22 Aug 2025 17:32:41 +0200 Subject: [PATCH 1/5] Added checkout customization parameters # Conflicts: # start.php --- includes/class-freemius.php | 100 ++++++++++++++++++ .../managers/class-fs-checkout-manager.php | 5 + start.php | 2 +- 3 files changed, 106 insertions(+), 1 deletion(-) diff --git a/includes/class-freemius.php b/includes/class-freemius.php index 79439c8e..d43b6932 100755 --- a/includes/class-freemius.php +++ b/includes/class-freemius.php @@ -365,6 +365,15 @@ class Freemius extends Freemius_Abstract { */ private $_is_bundle_license_auto_activation_enabled = false; + /** + * Developer-defined config overrides for checkout. + * + * @var array + */ + protected $_checkout_config = []; + + /** + #region Uninstall Reasons IDs const REASON_NO_LONGER_NEEDED = 1; @@ -5181,6 +5190,10 @@ private function parse_settings( &$plugin_info ) { ) ); } + if ( isset( $plugin_info['checkout'] ) && is_array( $plugin_info['checkout'] ) ) { + $this->_checkout_config = $this->validate_checkout_config( $plugin_info['checkout'] ); + } + $plugin = ( $this->_plugin instanceof FS_Plugin ) ? $this->_plugin : new FS_Plugin(); @@ -5322,6 +5335,93 @@ private function parse_settings( &$plugin_info ) { ); } + /** + * Validates and filters developer-provided checkout config. + * + * @param array $config + * + * @return array + */ + protected function validate_checkout_config($config) + { + $schema = [ + 'cart' => [ + 'always_show_renewals_amount' => 'bool', + 'annual_discount' => 'bool', + 'billing_cycle' => ['string', 'int'], + 'bundle_discount' => 'float', + 'maximize_discounts' => 'bool', + 'multisite_discount' => ['bool', 'string'], // string expected to be "auto" + 'show_inline_currency_selector' => 'bool', + 'show_monthly' => 'bool', + ], + 'appearance' => [ + 'form_position' => 'string', + 'is_bundle_collapsed' => 'bool', + 'layout' => 'string', + 'refund_policy_position' => 'string', + 'show_refund_badge' => 'bool', + 'show_reviews' => 'bool', + 'show_upsells' => 'bool', + 'title' => 'string', + ], + ]; + + $result = []; + + foreach ($schema as $section => $fields) + { + if (isset($config[$section]) && is_array($config[$section])) + { + foreach ($fields as $key => $expected_type) + { + if (array_key_exists($key, $config[$section])) + { + $value = $config[$section][$key]; + $types = is_array($expected_type) ? $expected_type : [$expected_type]; + $valid = false; + + foreach ($types as $type) + { + switch ($type) + { + case 'bool': + if (is_bool($value)) + $valid = true; + break; + case 'string': + if (is_string($value)) + $valid = true; + break; + case 'int': + if (is_int($value)) + $valid = true; + break; + case 'float': + if (is_float($value) || is_int($value)) + $valid = true; + break; + } + } + + if ($valid) + $result[$key] = $value; + } + } + } + } + + return $result; + } + + /** + * @return array + */ + public function get_checkout_config() + { + return $this->_checkout_config; + } + /** * @param string[] $options * @param string $key diff --git a/includes/managers/class-fs-checkout-manager.php b/includes/managers/class-fs-checkout-manager.php index 75ad9d63..e642a852 100644 --- a/includes/managers/class-fs-checkout-manager.php +++ b/includes/managers/class-fs-checkout-manager.php @@ -153,6 +153,11 @@ public function get_query_params( Freemius $fs, $plugin_id, $plan_id, $licenses ( $fs->is_theme() && current_user_can( 'install_themes' ) ) ); + // Add developer-defined checkout overrides directly to context. + foreach ( $fs->get_checkout_config() as $key => $value ) { + $context_params[ $key ] = $value; + } + return array_merge( $context_params, $_GET, array( // Current plugin version. 'plugin_version' => $fs->get_plugin_version(), diff --git a/start.php b/start.php index 7615ebaa..17f14695 100644 --- a/start.php +++ b/start.php @@ -15,7 +15,7 @@ * * @var string */ - $this_sdk_version = '2.12.2.3'; + $this_sdk_version = '2.12.1.3'; #region SDK Selection Logic -------------------------------------------------------------------- From f97701fb8c1f607ea249fad9b5f575b9db648261 Mon Sep 17 00:00:00 2001 From: Daniele Alessandra Date: Mon, 25 Aug 2025 20:01:12 +0200 Subject: [PATCH 2/5] Flat checkout properties --- includes/class-freemius.php | 116 +++++++++++++++++------------------- 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/includes/class-freemius.php b/includes/class-freemius.php index d43b6932..4229da06 100755 --- a/includes/class-freemius.php +++ b/includes/class-freemius.php @@ -5190,9 +5190,8 @@ private function parse_settings( &$plugin_info ) { ) ); } - if ( isset( $plugin_info['checkout'] ) && is_array( $plugin_info['checkout'] ) ) { - $this->_checkout_config = $this->validate_checkout_config( $plugin_info['checkout'] ); - } + // Extracts, validate and save checkout-specific settings. + $this->_checkout_config = $this->validate_checkout_config( $plugin_info ); $plugin = ( $this->_plugin instanceof FS_Plugin ) ? $this->_plugin : @@ -5344,70 +5343,65 @@ private function parse_settings( &$plugin_info ) { */ protected function validate_checkout_config($config) { - $schema = [ - 'cart' => [ - 'always_show_renewals_amount' => 'bool', - 'annual_discount' => 'bool', - 'billing_cycle' => ['string', 'int'], - 'bundle_discount' => 'float', - 'maximize_discounts' => 'bool', - 'multisite_discount' => ['bool', 'string'], // string expected to be "auto" - 'show_inline_currency_selector' => 'bool', - 'show_monthly' => 'bool', - ], - 'appearance' => [ - 'form_position' => 'string', - 'is_bundle_collapsed' => 'bool', - 'layout' => 'string', - 'refund_policy_position' => 'string', - 'show_refund_badge' => 'bool', - 'show_reviews' => 'bool', - 'show_upsells' => 'bool', - 'title' => 'string', - ], - ]; - - $result = []; - - foreach ($schema as $section => $fields) + $schema = array( + // currency + 'currency' => 'string', + 'default_currency' => 'string', + // cart + 'always_show_renewals_amount' => 'bool', + 'annual_discount' => 'bool', + 'billing_cycle' => ['string', 'int'], + 'bundle_discount' => 'float', + 'maximize_discounts' => 'bool', + 'multisite_discount' => ['bool', 'string'], // string expected to be "auto" + 'show_inline_currency_selector' => 'bool', + 'show_monthly' => 'bool', + // appearance + 'form_position' => 'string', + 'is_bundle_collapsed' => 'bool', + 'layout' => 'string', + 'refund_policy_position' => 'string', + 'show_refund_badge' => 'bool', + 'show_reviews' => 'bool', + 'show_upsells' => 'bool', + 'title' => 'string', + ); + + $result = array(); + + foreach ($schema as $key => $expected_type) { - if (isset($config[$section]) && is_array($config[$section])) + if (array_key_exists($key, $config)) { - foreach ($fields as $key => $expected_type) + $value = $config[$key]; + $types = is_array($expected_type) ? $expected_type : [$expected_type]; + $valid = false; + + foreach ($types as $type) { - if (array_key_exists($key, $config[$section])) + switch ($type) { - $value = $config[$section][$key]; - $types = is_array($expected_type) ? $expected_type : [$expected_type]; - $valid = false; - - foreach ($types as $type) - { - switch ($type) - { - case 'bool': - if (is_bool($value)) - $valid = true; - break; - case 'string': - if (is_string($value)) - $valid = true; - break; - case 'int': - if (is_int($value)) - $valid = true; - break; - case 'float': - if (is_float($value) || is_int($value)) - $valid = true; - break; - } - } - - if ($valid) - $result[$key] = $value; + case 'bool': + if (is_bool($value)) + $valid = true; + break; + case 'string': + if (is_string($value)) + $valid = true; + break; + case 'int': + if (is_int($value)) + $valid = true; + break; + case 'float': + if (is_float($value) || is_int($value)) + $valid = true; + break; } } + + if ($valid) + $result[$key] = $value; } } From c96efc6081e2b1b325433b6e2ed4d24df0ef8e91 Mon Sep 17 00:00:00 2001 From: Daniele Alessandra Date: Wed, 22 Oct 2025 17:39:45 +0200 Subject: [PATCH 3/5] feat(checkout): allow customizing checkout query params via filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a namespaced filter to let developers add/modify query params sent to Freemius Checkout. • Add fs_apply_filter( $fs->get_unique_affix(), 'checkout_query_params', ... ) --- includes/class-freemius.php | 94 ------------------- .../managers/class-fs-checkout-manager.php | 37 ++++++-- start.php | 2 +- 3 files changed, 32 insertions(+), 101 deletions(-) diff --git a/includes/class-freemius.php b/includes/class-freemius.php index 4229da06..79439c8e 100755 --- a/includes/class-freemius.php +++ b/includes/class-freemius.php @@ -365,15 +365,6 @@ class Freemius extends Freemius_Abstract { */ private $_is_bundle_license_auto_activation_enabled = false; - /** - * Developer-defined config overrides for checkout. - * - * @var array - */ - protected $_checkout_config = []; - - /** - #region Uninstall Reasons IDs const REASON_NO_LONGER_NEEDED = 1; @@ -5190,9 +5181,6 @@ private function parse_settings( &$plugin_info ) { ) ); } - // Extracts, validate and save checkout-specific settings. - $this->_checkout_config = $this->validate_checkout_config( $plugin_info ); - $plugin = ( $this->_plugin instanceof FS_Plugin ) ? $this->_plugin : new FS_Plugin(); @@ -5334,88 +5322,6 @@ private function parse_settings( &$plugin_info ) { ); } - /** - * Validates and filters developer-provided checkout config. - * - * @param array $config - * - * @return array - */ - protected function validate_checkout_config($config) - { - $schema = array( - // currency - 'currency' => 'string', - 'default_currency' => 'string', - // cart - 'always_show_renewals_amount' => 'bool', - 'annual_discount' => 'bool', - 'billing_cycle' => ['string', 'int'], - 'bundle_discount' => 'float', - 'maximize_discounts' => 'bool', - 'multisite_discount' => ['bool', 'string'], // string expected to be "auto" - 'show_inline_currency_selector' => 'bool', - 'show_monthly' => 'bool', - // appearance - 'form_position' => 'string', - 'is_bundle_collapsed' => 'bool', - 'layout' => 'string', - 'refund_policy_position' => 'string', - 'show_refund_badge' => 'bool', - 'show_reviews' => 'bool', - 'show_upsells' => 'bool', - 'title' => 'string', - ); - - $result = array(); - - foreach ($schema as $key => $expected_type) - { - if (array_key_exists($key, $config)) - { - $value = $config[$key]; - $types = is_array($expected_type) ? $expected_type : [$expected_type]; - $valid = false; - - foreach ($types as $type) - { - switch ($type) - { - case 'bool': - if (is_bool($value)) - $valid = true; - break; - case 'string': - if (is_string($value)) - $valid = true; - break; - case 'int': - if (is_int($value)) - $valid = true; - break; - case 'float': - if (is_float($value) || is_int($value)) - $valid = true; - break; - } - } - - if ($valid) - $result[$key] = $value; - } - } - - return $result; - } - - /** - * @return array - */ - public function get_checkout_config() - { - return $this->_checkout_config; - } - /** * @param string[] $options * @param string $key diff --git a/includes/managers/class-fs-checkout-manager.php b/includes/managers/class-fs-checkout-manager.php index e642a852..07988096 100644 --- a/includes/managers/class-fs-checkout-manager.php +++ b/includes/managers/class-fs-checkout-manager.php @@ -153,12 +153,37 @@ public function get_query_params( Freemius $fs, $plugin_id, $plan_id, $licenses ( $fs->is_theme() && current_user_can( 'install_themes' ) ) ); - // Add developer-defined checkout overrides directly to context. - foreach ( $fs->get_checkout_config() as $key => $value ) { - $context_params[ $key ] = $value; - } - - return array_merge( $context_params, $_GET, array( + /** + * Allow developers to customize the checkout query params before final validation, + * so custom keys can be included and known keys can be overridden. + * We then validate the merged params and re-attach any unknown custom keys that + * validation intentionally ignores, preserving developer-provided extras while + * keeping core keys safe. + * + * Usage example (in a plugin/theme): + * + * add_filter( 'fs_checkout_query_params_' . fs()->get_unique_affix(), function( $params ) { + * // Add or modify query params passed to the Freemius Checkout. + * $params['coupon'] = 'WELCOME10'; + * $params['utm_source'] = 'my-plugin'; + * return $params; + * }, 10, 5 ); + * + * @since 2.12.1.3 + * + * @param array $context_params The params prepared by the SDK before validation. + * @param Freemius $fs The Freemius instance of the calling module. + * @param int|mixed $plugin_id The target plugin/add-on ID for the checkout context. + * @param int|mixed $plan_id The selected plan ID (if any). + * @param int|mixed $licenses The requested number of licenses (if provided). + */ + $filtered_params = fs_apply_filter( + $fs->get_unique_affix(), + 'checkout_query_params', + $context_params + ); + + return array_merge( $filtered_params, $_GET, array( // Current plugin version. 'plugin_version' => $fs->get_plugin_version(), 'sdk_version' => WP_FS__SDK_VERSION, diff --git a/start.php b/start.php index 17f14695..7bad8e1c 100644 --- a/start.php +++ b/start.php @@ -15,7 +15,7 @@ * * @var string */ - $this_sdk_version = '2.12.1.3'; + $this_sdk_version = '2.12.2.4'; #region SDK Selection Logic -------------------------------------------------------------------- From bc8f28f7d3058fae8355491d70e1823ee633c598 Mon Sep 17 00:00:00 2001 From: Daniele Alessandra Date: Thu, 6 Nov 2025 16:28:38 +0100 Subject: [PATCH 4/5] Add allowlist for checkout query parameters in FS_Checkout_Manager Introduced the private $_allowed_custom_params property to define all supported custom query parameters for the checkout. After applying `fs_apply_filter()`, the function now filters `$filtered_params` using `array_intersect_key()` to remove any unsupported keys before merging. This prevents external filters from injecting unexpected parameters into the checkout query. --- .../managers/class-fs-checkout-manager.php | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/includes/managers/class-fs-checkout-manager.php b/includes/managers/class-fs-checkout-manager.php index 07988096..d5a92e71 100644 --- a/includes/managers/class-fs-checkout-manager.php +++ b/includes/managers/class-fs-checkout-manager.php @@ -12,7 +12,35 @@ class FS_Checkout_Manager { - # region Singleton + /** + * Allowlist of query parameters for checkout. + */ + private $_allowed_custom_params = array( + // currency + 'currency' => 'string', + 'default_currency' => 'string', + // cart + 'always_show_renewals_amount' => 'bool', + 'annual_discount' => 'bool', + 'billing_cycle' => ['string', 'int'], + 'bundle_discount' => 'float', + 'maximize_discounts' => 'bool', + 'multisite_discount' => ['bool', 'string'], // string expected to be "auto" + 'show_inline_currency_selector' => 'bool', + 'show_monthly' => 'bool', + // appearance + 'form_position' => 'string', + 'is_bundle_collapsed' => 'bool', + 'layout' => 'string', + 'refund_policy_position' => 'string', + 'show_refund_badge' => 'bool', + 'show_reviews' => 'bool', + 'show_upsells' => 'bool', + 'title' => 'string', + ); + + + # region Singleton /** * @var FS_Checkout_Manager @@ -169,7 +197,7 @@ public function get_query_params( Freemius $fs, $plugin_id, $plan_id, $licenses * return $params; * }, 10, 5 ); * - * @since 2.12.1.3 + * @since 2.12.2.4 * * @param array $context_params The params prepared by the SDK before validation. * @param Freemius $fs The Freemius instance of the calling module. @@ -183,7 +211,10 @@ public function get_query_params( Freemius $fs, $plugin_id, $plan_id, $licenses $context_params ); - return array_merge( $filtered_params, $_GET, array( + // Allowlist only allowed query params. + $filtered_params = array_intersect_key($filtered_params, $this->_allowed_custom_params); + + return array_merge( $context_params, $filtered_params, $_GET, array( // Current plugin version. 'plugin_version' => $fs->get_plugin_version(), 'sdk_version' => WP_FS__SDK_VERSION, @@ -269,4 +300,4 @@ public function get_pending_activation_url( Freemius $fs, $plugin_id ) { private function get_checkout_redirect_nonce_action( Freemius $fs ) { return $fs->get_unique_affix() . '_checkout_redirect'; } - } \ No newline at end of file + } From 7c6d8255e680358bb53ea31f78baccdc31d1dc26 Mon Sep 17 00:00:00 2001 From: Daniele Alessandra Date: Mon, 10 Nov 2025 10:52:21 +0100 Subject: [PATCH 5/5] Changed filter name to checkout/parameters, code clean and small fixes --- .../managers/class-fs-checkout-manager.php | 67 ++++++------------- start.php | 1 + 2 files changed, 21 insertions(+), 47 deletions(-) diff --git a/includes/managers/class-fs-checkout-manager.php b/includes/managers/class-fs-checkout-manager.php index d5a92e71..359bc25e 100644 --- a/includes/managers/class-fs-checkout-manager.php +++ b/includes/managers/class-fs-checkout-manager.php @@ -17,26 +17,27 @@ class FS_Checkout_Manager { */ private $_allowed_custom_params = array( // currency - 'currency' => 'string', - 'default_currency' => 'string', + 'currency' => true, + 'default_currency' => true, // cart - 'always_show_renewals_amount' => 'bool', - 'annual_discount' => 'bool', - 'billing_cycle' => ['string', 'int'], - 'bundle_discount' => 'float', - 'maximize_discounts' => 'bool', - 'multisite_discount' => ['bool', 'string'], // string expected to be "auto" - 'show_inline_currency_selector' => 'bool', - 'show_monthly' => 'bool', + 'always_show_renewals_amount' => true, + 'annual_discount' => true, + 'billing_cycle' => true, + 'billing_cycle_selector' => true, + 'bundle_discount' => true, + 'maximize_discounts' => true, + 'multisite_discount' => true, + 'show_inline_currency_selector' => true, + 'show_monthly' => true, // appearance - 'form_position' => 'string', - 'is_bundle_collapsed' => 'bool', - 'layout' => 'string', - 'refund_policy_position' => 'string', - 'show_refund_badge' => 'bool', - 'show_reviews' => 'bool', - 'show_upsells' => 'bool', - 'title' => 'string', + 'form_position' => true, + 'is_bundle_collapsed' => true, + 'layout' => true, + 'refund_policy_position' => true, + 'show_refund_badge' => true, + 'show_reviews' => true, + 'show_upsells' => true, + 'title' => true, ); @@ -181,35 +182,7 @@ public function get_query_params( Freemius $fs, $plugin_id, $plan_id, $licenses ( $fs->is_theme() && current_user_can( 'install_themes' ) ) ); - /** - * Allow developers to customize the checkout query params before final validation, - * so custom keys can be included and known keys can be overridden. - * We then validate the merged params and re-attach any unknown custom keys that - * validation intentionally ignores, preserving developer-provided extras while - * keeping core keys safe. - * - * Usage example (in a plugin/theme): - * - * add_filter( 'fs_checkout_query_params_' . fs()->get_unique_affix(), function( $params ) { - * // Add or modify query params passed to the Freemius Checkout. - * $params['coupon'] = 'WELCOME10'; - * $params['utm_source'] = 'my-plugin'; - * return $params; - * }, 10, 5 ); - * - * @since 2.12.2.4 - * - * @param array $context_params The params prepared by the SDK before validation. - * @param Freemius $fs The Freemius instance of the calling module. - * @param int|mixed $plugin_id The target plugin/add-on ID for the checkout context. - * @param int|mixed $plan_id The selected plan ID (if any). - * @param int|mixed $licenses The requested number of licenses (if provided). - */ - $filtered_params = fs_apply_filter( - $fs->get_unique_affix(), - 'checkout_query_params', - $context_params - ); + $filtered_params = $fs->apply_filters('checkout/parameters', $context_params); // Allowlist only allowed query params. $filtered_params = array_intersect_key($filtered_params, $this->_allowed_custom_params); diff --git a/start.php b/start.php index 7bad8e1c..e47e0fa4 100644 --- a/start.php +++ b/start.php @@ -446,6 +446,7 @@ function_exists( 'wp_is_json_request' ) && * fs_plugin_icon_{plugin_slug} * fs_show_trial_{plugin_slug} * fs_is_pricing_page_visible_{plugin_slug} + * fs_checkout/parameters_{plugin_slug} * * -------------------------------------------------------- *