diff --git a/includes/API.php b/includes/API.php index dac1d55eb..b1068e8a4 100644 --- a/includes/API.php +++ b/includes/API.php @@ -17,7 +17,6 @@ use WooCommerce\Facebook\API\Request; use WooCommerce\Facebook\API\Response; use WooCommerce\Facebook\Events\Event; - use WooCommerce\Facebook\Framework\Api\Base; use WooCommerce\Facebook\Framework\Api\Exception as ApiException; @@ -276,24 +275,19 @@ public function delete_mbe_connection( string $external_business_id): API\FBE\In */ public function get_business_configuration( $external_business_id, $access_token = '', $fields = [] ) { $request = new API\FBE\Configuration\Request( $external_business_id, 'GET' ); - $params = []; - // Use provided access token or fall back to the instance token if ( ! empty( $access_token ) ) { $params['access_token'] = $access_token; } - // Add fields parameter if specified if ( ! empty( $fields ) ) { $params['fields'] = is_array( $fields ) ? implode( ',', $fields ) : $fields; } - // Set parameters if we have any if ( ! empty( $params ) ) { $request->set_params( $params ); } - $this->set_response_handler( API\FBE\Configuration\Read\Response::class ); return $this->perform_request( $request ); } @@ -721,4 +715,59 @@ protected function get_new_request( $args = [] ) { protected function get_plugin() { return facebook_for_woocommerce(); } + + /** + * Repairs the commerce integration connection. + * + * @param string $fbe_external_business_id The external business ID associated with the Facebook Business Extension + * @param string $shop_domain The domain of the WooCommerce site + * @param string $admin_url The admin URL of the WooCommerce site + * @param string $extension_version The version of the Facebook for WooCommerce extension + * + * @return API\Response|API\CommerceIntegration\Repair\Response + * @throws ApiException + */ + public function repair_commerce_integration( string $fbe_external_business_id, string $shop_domain, string $admin_url, string $extension_version ): API\CommerceIntegration\Repair\Response { + $request = new API\CommerceIntegration\Repair\RepairRequest( $fbe_external_business_id, $shop_domain, $admin_url, $extension_version ); + $this->set_response_handler( API\CommerceIntegration\Repair\Response::class ); + return $this->perform_request( $request ); + } + + /** + * Updates the commerce integration configuration. + * + * @param string $commerce_integration_id The ID of the commerce integration to update + * @param string|null $extension_version The version of the Facebook for WooCommerce extension + * @param string|null $admin_url The admin URL of the WooCommerce site + * @param string|null $country_code ISO2 country code + * @param string|null $currency ISO currency code + * @param string|null $platform_store_id The ID of the current website on a multisite setup + * @param string $commerce_partner_seller_platform_type The type of commerce partner platform + * @param string $installation_status The installation status of the integration + * @return API\Response|API\CommerceIntegration\Configuration\Update\Response + * @throws ApiException + */ + public function update_commerce_integration( + string $commerce_integration_id, + ?string $extension_version = null, + ?string $admin_url = null, + ?string $country_code = null, + ?string $currency = null, + ?string $platform_store_id = null, + string $commerce_partner_seller_platform_type = 'SELF_SERVE', + string $installation_status = 'ACCESS_TOKEN_DEPOSITED' + ): API\CommerceIntegration\Configuration\Update\Response { + $request = new API\CommerceIntegration\Configuration\Update\UpdateRequest( + $commerce_integration_id, + $extension_version, + $admin_url, + $country_code, + $currency, + $platform_store_id, + $commerce_partner_seller_platform_type, + $installation_status + ); + $this->set_response_handler(API\CommerceIntegration\Configuration\Update\Response::class); + return $this->perform_request($request); + } } diff --git a/includes/API/CommerceIntegration/Configuration/Request.php b/includes/API/CommerceIntegration/Configuration/Request.php new file mode 100644 index 000000000..5d6c4f73d --- /dev/null +++ b/includes/API/CommerceIntegration/Configuration/Request.php @@ -0,0 +1,23 @@ +success; + } + + /** + * Returns the commerce partner integration ID. + * + * @return string + * @since 3.4.8 + */ + public function get_commerce_partner_integration_id(): string { + return $this->get_id() ?? ''; + } +} diff --git a/includes/API/CommerceIntegration/Configuration/Update/UpdateRequest.php b/includes/API/CommerceIntegration/Configuration/Update/UpdateRequest.php new file mode 100644 index 000000000..5b8aff01c --- /dev/null +++ b/includes/API/CommerceIntegration/Configuration/Update/UpdateRequest.php @@ -0,0 +1,72 @@ + $commerce_partner_seller_platform_type, + 'installation_status' => $installation_status, + ); + + if ( $extension_version ) { + $data['extension_version'] = $extension_version; + } + + if ( $admin_url ) { + $data['admin_url'] = $admin_url; + } + + if ( $country_code ) { + $data['country_code'] = $country_code; + } + + if ( $currency ) { + $data['currency'] = $currency; + } + + if ( $platform_store_id ) { + $data['platform_store_id'] = $platform_store_id; + } + + $this->set_data( $data ); + } +} diff --git a/includes/API/CommerceIntegration/Repair/RepairRequest.php b/includes/API/CommerceIntegration/Repair/RepairRequest.php new file mode 100644 index 000000000..8d27af770 --- /dev/null +++ b/includes/API/CommerceIntegration/Repair/RepairRequest.php @@ -0,0 +1,43 @@ + $fbe_external_business_id, + 'shop_domain' => $shop_domain, + 'admin_url' => $admin_url, + 'extension_version' => $extension_version, + 'commerce_partner_seller_platform_type' => $platform_type, + ); + + $this->set_data( $data ); + } +} diff --git a/includes/API/CommerceIntegration/Repair/Request.php b/includes/API/CommerceIntegration/Repair/Request.php new file mode 100644 index 000000000..4a9ad6286 --- /dev/null +++ b/includes/API/CommerceIntegration/Repair/Request.php @@ -0,0 +1,32 @@ +success; + } + + /** + * Returns the commerce partner integration ID. + * + * @return string + * @since 3.4.8 + */ + public function get_commerce_partner_integration_id(): string { + return $this->get_id() ?? ''; + } +} diff --git a/includes/Handlers/Connection.php b/includes/Handlers/Connection.php index 8bd3b211b..6b32a7053 100644 --- a/includes/Handlers/Connection.php +++ b/includes/Handlers/Connection.php @@ -179,14 +179,112 @@ public function refresh_installation_data() { try { $this->update_installation_data(); - + $this->repair_or_update_commerce_integration_data(); } catch ( ApiException $exception ) { + $this->get_plugin()->log( 'Could not refresh installation data. ' . $exception->getMessage(), null, 'error' ); + } + + } - $this->get_plugin()->log( 'Could not refresh installation data. ' . $exception->getMessage() ); + /** + * Refreshes the client side info and configuration. + * + * @since 3.4.8 + */ + public function repair_or_update_commerce_integration_data() { + // bail if not connected + if ( ! $this->is_connected() ) { + return; } + try { + $commerce_integration_id = $this->get_commerce_partner_integration_id(); + + // If commerce integration ID doesn't exist, call repair endpoint + if ( empty( $commerce_integration_id ) ) { + $response = $this->get_plugin()->get_api()->repair_commerce_integration( + $this->get_external_business_id(), + $this->get_shop_domain(), + admin_url(), + $this->get_plugin()->get_version() + ); + + if ( ! $response->is_successful() ) { + $this->get_plugin()->log( 'Failed to repair commerce integration.', null, 'error' ); + return; + } + + // Store the new commerce integration ID + $new_commerce_integration_id = $response->get_commerce_partner_integration_id(); + if ( empty( $new_commerce_integration_id ) ) { + $this->get_plugin()->log( 'Failed to get commerce partner integration ID from repair response.', null, 'error' ); + return; + } + + $this->update_commerce_partner_integration_id( $new_commerce_integration_id ); + $commerce_integration_id = $new_commerce_integration_id; + $this->get_plugin()->log( 'Successfully repaired commerce integration. New ID: ' . $commerce_integration_id ); + } + + // If we have a commerce integration ID, update the configuration + if ( ! empty( $commerce_integration_id ) ) { + $update_response = $this->get_plugin()->get_api()->update_commerce_integration( + $commerce_integration_id, + $this->get_plugin()->get_version(), // extension_version + admin_url(), // admin_url + $this->get_country_code(), // country_code + $this->get_currency(), // currency + $this->get_platform_store_id(), // platform_store_id + ); + + if ( ! $update_response->is_successful() ) { + $this->get_plugin()->log( 'Failed to update commerce integration configuration.', null, 'error' ); + return; + } + + $this->get_plugin()->log( 'Successfully updated commerce integration configuration.' ); + } + + } catch ( ApiException $exception ) { + $this->get_plugin()->log( 'Could not repair or update commerce integration data. ' . $exception->getMessage(), null, 'error' ); + } + } + + /** + * Gets the shop domain. + * + * @return string + */ + private function get_shop_domain() { + return site_url( '/' ); } + /** + * Gets the country code. + * + * @return string|null + */ + private function get_country_code() { + return WC()->countries->get_base_country(); + } + + /** + * Gets the currency. + * + * @return string|null + */ + private function get_currency() { + return get_woocommerce_currency(); + } + + /** + * Gets the platform store ID. + * + * @return int + */ + private function get_platform_store_id() { + return get_current_blog_id(); + } /** * Retrieves and stores the connected installation data. @@ -800,7 +898,7 @@ public function get_commerce_merchant_settings_id() { /** * Gets Commerce Partner Integration ID value. * - * @since 3.5.0 + * @since 3.4.8 * * @return string */ @@ -1370,4 +1468,15 @@ public function handle_fbe_redirect() { wp_redirect( $redirect_url ); //phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect exit; } + + /** + * Stores the given Commerce Partner Integration ID. + * + * @since 3.4. + * + * @param string $id the ID + */ + public function update_commerce_partner_integration_id( $id ) { + update_option( self::OPTION_COMMERCE_PARTNER_INTEGRATION_ID, $id ); + } } diff --git a/tests/Unit/ApiTest.php b/tests/Unit/ApiTest.php index 2f123ac4d..a84703fd9 100644 --- a/tests/Unit/ApiTest.php +++ b/tests/Unit/ApiTest.php @@ -894,4 +894,110 @@ public function test_create_common_upload_request() { $response = $this->api->create_common_data_feed_upload( $cpi, $data ); $this->assertFalse( $response->has_api_error() ); } + + /** + * Tests repair commerce integration request to Facebook. + * + * @return void + * @throws ApiException In case of network request error. + */ + public function test_repair_commerce_integration_request() { + $fbe_external_business_id = 'test-business-123'; + $shop_domain = 'example.com'; + $admin_url = 'https://example.com/wp-admin'; + $extension_version = '3.0.0'; + + $response = function( $result, $parsed_args, $url ) use ( $fbe_external_business_id ) { + $this->assertEquals( 'POST', $parsed_args['method'] ); + $this->assertEquals( "{$this->endpoint}{$this->version}/commerce_partner_integrations_repair", $url ); + + // Parse the actual request body to compare properties regardless of order + $actual_body = json_decode($parsed_args['body'], true); + $expected_body = [ + 'fbe_external_business_id' => $fbe_external_business_id, + 'shop_domain' => 'example.com', + 'admin_url' => 'https://example.com/wp-admin', + 'extension_version' => '3.0.0', + 'commerce_partner_seller_platform_type' => 'SELF_SERVE_PLATFORM' + ]; + + $this->assertEquals($expected_body, $actual_body); + + return [ + 'body' => '{"success":true}', + 'response' => [ + 'code' => 200, + 'message' => 'OK', + ], + ]; + }; + $this->add_filter_with_safe_teardown( 'pre_http_request', $response, 10, 3 ); + + $response = $this->api->repair_commerce_integration( + $fbe_external_business_id, + $shop_domain, + $admin_url, + $extension_version + ); + + $this->assertTrue( $response->success ); + } + + /** + * Tests update commerce integration request to Facebook. + * + * @return void + * @throws ApiException In case of network request error. + */ + public function test_update_commerce_integration_request() { + $commerce_integration_id = 'test-integration-123'; + $extension_version = '3.0.0'; + $admin_url = 'https://example.com/wp-admin'; + $country_code = 'US'; + $currency = 'USD'; + $platform_store_id = 'store-123'; + $commerce_partner_seller_platform_type = 'SELF_SERVE'; + $installation_status = 'ACCESS_TOKEN_DEPOSITED'; + + $response = function( $result, $parsed_args, $url ) use ( $commerce_integration_id ) { + $this->assertEquals( 'POST', $parsed_args['method'] ); + $this->assertEquals( "{$this->endpoint}{$this->version}/{$commerce_integration_id}", $url ); + + // Parse the actual request body to compare properties regardless of order + $actual_body = json_decode($parsed_args['body'], true); + $expected_body = [ + 'commerce_partner_seller_platform_type' => 'SELF_SERVE', + 'installation_status' => 'ACCESS_TOKEN_DEPOSITED', + 'extension_version' => '3.0.0', + 'admin_url' => 'https://example.com/wp-admin', + 'country_code' => 'US', + 'currency' => 'USD', + 'platform_store_id' => 'store-123' + ]; + + $this->assertEquals($expected_body, $actual_body); + + return [ + 'body' => '{"success":true}', + 'response' => [ + 'code' => 200, + 'message' => 'OK', + ], + ]; + }; + $this->add_filter_with_safe_teardown( 'pre_http_request', $response, 10, 3 ); + + $response = $this->api->update_commerce_integration( + $commerce_integration_id, + $extension_version, + $admin_url, + $country_code, + $currency, + $platform_store_id, + $commerce_partner_seller_platform_type, + $installation_status + ); + + $this->assertTrue( $response->success ); + } }