='); } /** Declare HPOS and Cart/Checkout blocks compatibility; the plugin touches neither orders nor checkout. */ function chargebacks911_declare_compatibility(): void { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', CHARGEBACKS911_PLUGIN_FILE, true); \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('cart_checkout_blocks', CHARGEBACKS911_PLUGIN_FILE, true); } /** The one requirement message, shared by the refused-activation notice and the WooCommerce-missing notice. */ function chargebacks911_woocommerce_requirement_message(): string { return sprintf( /* translators: 1: this plugin's name, 2: "WooCommerce", 3: minimum WooCommerce version */ __('%1$s requires %2$s %3$s or newer to be installed and active.', 'chargebacks911'), chargebacks911_setting('appname'), chargebacks911_setting('marketplace_name'), CHARGEBACKS911_MIN_WC_VERSION, ); } /** Finish a refused activation, and show the requirement notice while WooCommerce 8.4+ is missing. */ function chargebacks911_require_woocommerce(): void { $refused = (bool) get_transient('chargebacks911_activation_refused'); if ($refused) { delete_transient('chargebacks911_activation_refused'); deactivate_plugins(plugin_basename(CHARGEBACKS911_PLUGIN_FILE), true); unset($_GET['activate']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended } if ($refused || ! chargebacks911_woocommerce_ready()) { add_action('admin_notices', 'chargebacks911_woocommerce_missing_notice'); } } /** Explain the blocked extension state without exposing internal errors. */ function chargebacks911_woocommerce_missing_notice(): void { chargebacks911_render_template('notice', [ 'message' => chargebacks911_woocommerce_requirement_message(), ]); } /** Register one WooCommerce submenu entry. */ function chargebacks911_register_admin_menu(): void { add_submenu_page( 'woocommerce', chargebacks911_setting('menu_name'), chargebacks911_setting('menu_name'), 'manage_woocommerce', 'chargebacks911', 'chargebacks911_render_page' ); } /** The Open button's target in a new tab: WooCommerce's approval first, then the app through a freshly signed link. */ function chargebacks911_open(): void { if (! current_user_can('manage_woocommerce') || ! chargebacks911_backend_configured() || chargebacks911_shop() === '') { wp_die(esc_html__('Sorry, you are not allowed to open this app.', 'chargebacks911'), 403); } check_admin_referer('chargebacks911_open'); if (chargebacks911_signing_secret() !== '') { $url = chargebacks911_connector_url(); } elseif (sanitize_text_field(wp_unslash($_GET['success'] ?? '')) === '0') { $url = admin_url('admin.php?page=chargebacks911&success=0'); } else { $url = chargebacks911_authorize_url(); } add_filter('allowed_redirect_hosts', 'chargebacks911_allowed_redirect_hosts'); wp_safe_redirect($url); exit; } /** The SaaS address that starts WooCommerce's app authorization for this store. */ function chargebacks911_authorize_url(): string { return rtrim(chargebacks911_setting('app_url'), '/').'/marketplace/auth?'.http_build_query([ 'marketplace' => 'woocommerce', 'app' => chargebacks911_setting('app'), 'shop' => chargebacks911_shop(), 'host' => chargebacks911_host(), 'nonce' => wp_create_nonce('chargebacks911_open'), ], '', '&', PHP_QUERY_RFC3986); } /** * Let wp_safe_redirect() reach the app's host. * * @param array $hosts * @return array */ function chargebacks911_allowed_redirect_hosts(array $hosts): array { $hosts[] = (string) wp_parse_url(chargebacks911_setting('app_url'), PHP_URL_HOST); return $hosts; } /** Build wc-auth's own URLs in their query form, which works under every permalink setting. */ function chargebacks911_wc_auth_endpoint_url(string $url, string $endpoint, string $value, string $permalink): string { if ($endpoint !== 'wc-auth/v1') { return $url; } return add_query_arg(['wc-auth-version' => '1', 'wc-auth-route' => $value], $permalink); } /** Like Shopify's host, the admin URL the app returns to: base64url of admin_url(). */ function chargebacks911_host(): string { return rtrim(strtr(base64_encode(admin_url()), '+/', '-_'), '='); } /** The single-use, Shopify-style signed launch URL for the logged-in admin. */ function chargebacks911_connector_url(): string { $user = wp_get_current_user(); return rtrim(chargebacks911_setting('app_url'), '/').'/connector?'.http_build_query(chargebacks911_signed([ 'platform' => 'woocommerce', 'app' => chargebacks911_setting('app'), 'shop' => chargebacks911_shop(), 'host' => chargebacks911_host(), 'locale' => strtolower(explode('_', get_user_locale())[0]), 'timestamp' => (string) time(), 'nonce' => wp_generate_uuid4(), 'user_id' => (string) $user->ID, 'email' => (string) $user->user_email, 'first_name' => (string) $user->first_name, 'last_name' => (string) $user->last_name, ]), '', '&', PHP_QUERY_RFC3986); } /** Render the plugin page, or the notice that explains why the app cannot open. */ function chargebacks911_render_page(): void { if (! current_user_can('manage_woocommerce')) { return; } if (! chargebacks911_backend_configured()) { chargebacks911_render_template('notice', [ 'message' => __('This plugin package is incomplete. Download it again or contact support.', 'chargebacks911'), ]); return; } if (chargebacks911_shop() === '') { chargebacks911_render_template('notice', [ 'message' => sprintf( /* translators: 1: this plugin's name, 2: "WooCommerce" */ __('%1$s requires %2$s 8.4 or newer. Update it, then reactivate this plugin.', 'chargebacks911'), chargebacks911_setting('appname'), chargebacks911_setting('marketplace_name'), ), ]); return; } if (chargebacks911_signing_secret() === '' && sanitize_text_field(wp_unslash($_GET['success'] ?? '')) === '0') { // phpcs:ignore WordPress.Security.NonceVerification.Recommended chargebacks911_render_template('notice', [ 'message' => __('Access was not approved. Open the app again to approve it.', 'chargebacks911'), ]); } $appUrl = rtrim(chargebacks911_setting('app_url'), '/'); chargebacks911_render_template('admin-page', [ 'appname' => chargebacks911_setting('appname'), 'open_url' => wp_nonce_url(admin_url('admin-post.php?action=chargebacks911_open'), 'chargebacks911_open'), 'terms_url' => $appUrl.'/terms-of-use', 'privacy_url' => $appUrl.'/privacy-policy', ]); } /** * Include one file from templates/; it reads its values from $args. * * @param array $args */ function chargebacks911_render_template(string $name, array $args = []): void { include CHARGEBACKS911_PLUGIN_DIR.'templates/'.$name.'.php'; }