register_admin_menu(); } ); add_action( 'admin_init', function() { $this->handle_actions(); } ); } /** * Store response from an API request. * * @var string */ protected $response = ''; /** * Store response from the integration status API request. * * @var string */ protected $integration_status_response = []; /** * Add menu entries */ protected function register_admin_menu() { if ( apply_filters( 'woocommerce_gla_enable_connection_test', false ) ) { add_menu_page( 'Connection Test', 'Connection Test', 'manage_woocommerce', 'connection-test-admin-page', function () { $this->render_admin_page(); } ); } else { add_submenu_page( '', 'Connection Test', 'Connection Test', 'manage_woocommerce', 'connection-test-admin-page', function () { $this->render_admin_page(); } ); } } /** * Render the admin page. */ protected function render_admin_page() { /** @var OptionsInterface $options */ $options = $this->container->get( OptionsInterface::class ); /** @var Manager $manager */ $manager = $this->container->get( Manager::class ); $blog_token = $manager->get_tokens()->get_access_token(); $user_token = $manager->get_tokens()->get_access_token( get_current_user_id() ); $user_data = $manager->get_connected_user_data( get_current_user_id() ); $url = admin_url( 'admin.php?page=connection-test-admin-page' ); if ( ! empty( $_GET['google-mc'] ) && 'connected' === $_GET['google-mc'] ) { $this->response .= 'Google Account connected successfully.'; } if ( ! empty( $_GET['google-manager'] ) && 'connected' === $_GET['google-manager'] ) { $this->response .= 'Successfully connected a Google Manager account.'; } if ( ! empty( $_GET['google'] ) && 'failed' === $_GET['google'] ) { $this->response .= 'Failed to connect to Google.'; } ?>

Connection Test

Google for WooCommerce connection testing page used for debugging purposes. Debug responses are output at the top of the page.


response ) ) { ?>

Response

response ); ?>

WooCommerce Connect Server


WordPress.com

get( OptionsInterface::JETPACK_CONNECTED ) ) { ?>

Google Account


Merchant Center

More Merchant Center

For single-step development testing, not used for normal account setup flow.



Google Ads


Terms of Service


Product Sync


container->get( OptionsInterface::class ); $wp_api_status = $options->get( OptionsInterface::WPCOM_REST_API_STATUS ); $notification_service = new NotificationsService( $this->container->get( MerchantCenterService::class ), $this->container->get( AccountService::class ) ); $notification_service->set_options_object( $options ); ?>

Partner API Pull Integration

integration_status_response['site'] ) || isset( $this->integration_status_response['errors'] ) ) { ?>
container->get( Manager::class ); if ( 'connect' === $_GET['action'] && check_admin_referer( 'connect' ) ) { // Register the site to WPCOM. if ( $manager->is_connected() ) { $result = $manager->reconnect(); } else { $result = $manager->register(); } if ( is_wp_error( $result ) ) { $this->response .= $result->get_error_message(); return; } // Get an authorization URL which will redirect back to our page. $redirect = admin_url( 'admin.php?page=connection-test-admin-page' ); $auth_url = $manager->get_authorization_url( null, $redirect ); // Payments flow allows redirect back to the site without showing plans. $auth_url = add_query_arg( [ 'from' => 'google-listings-and-ads' ], $auth_url ); // Using wp_redirect intentionally because we're redirecting outside. wp_redirect( $auth_url ); // phpcs:ignore WordPress.Security.SafeRedirect exit; } if ( 'disconnect' === $_GET['action'] && check_admin_referer( 'disconnect' ) ) { $manager->remove_connection(); $plugin = $manager->get_plugin(); if ( $plugin && ! $plugin->is_only() ) { $connected_plugins = $manager->get_connected_plugins(); $this->response = 'Cannot disconnect WordPress.com connection as there are other plugins using it: '; $this->response .= implode( ', ', array_keys( $connected_plugins ) ) . "\n"; $this->response .= 'Please disconnect the connection using the Jetpack plugin.'; return; } else { $redirect = admin_url( 'admin.php?page=connection-test-admin-page' ); wp_safe_redirect( $redirect ); exit; } } if ( 'wp-status' === $_GET['action'] && check_admin_referer( 'wp-status' ) ) { $request = new Request( 'GET', '/wc/gla/jetpack/connected' ); $this->send_rest_request( $request ); /** @var OptionsInterface $options */ $options = $this->container->get( OptionsInterface::class ); $this->response .= "\n\n" . 'Saved Connection option = ' . ( $options->get( OptionsInterface::JETPACK_CONNECTED ) ? 'connected' : 'disconnected' ); $this->response .= "\n\n" . 'Connected plugins: ' . implode( ', ', array_column( $manager->get_connected_plugins(), 'name' ) ) . "\n"; } if ( 'wcs-test' === $_GET['action'] && check_admin_referer( 'wcs-test' ) ) { $url = $this->get_connect_server_url(); $this->response = 'GET ' . $url . "\n"; $response = wp_remote_get( $url ); if ( is_wp_error( $response ) ) { $this->response .= $response->get_error_message(); return; } $this->response .= wp_remote_retrieve_body( $response ); } if ( 'partner-notification' === $_GET['action'] && check_admin_referer( 'partner-notification' ) ) { if ( ! isset( $_GET['topic'] ) ) { $this->response .= "\n Topic is required."; return; } $item = $_GET['item_id'] ?? null; $topic = $_GET['topic']; $mc = $this->container->get( MerchantCenterService::class ); /** @var OptionsInterface $options */ $options = $this->container->get( OptionsInterface::class ); $service = new NotificationsService( $mc, $this->container->get( AccountService::class ) ); $service->set_options_object( $options ); if ( $service->notify( $topic, $item ) ) { $this->response .= "\n Notification success. Item: " . $item . " - Topic: " . $topic; } else { $this->response .= "\n Notification failed. Item: " . $item . " - Topic: " . $topic; } return; } if ( 'partner-integration-status' === $_GET['action'] && check_admin_referer( 'partner-integration-status' ) ) { $integration_status_args = [ 'method' => 'GET', 'timeout' => 30, 'url' => 'https://public-api.wordpress.com/wpcom/v2/sites/' . Jetpack_Options::get_option( 'id' ) . '/wc/partners/google/remote-site-status', 'user_id' => get_current_user_id(), ]; $integration_remote_request_response = Client::remote_request( $integration_status_args, null ); if ( is_wp_error( $integration_remote_request_response ) ) { $this->response .= $integration_remote_request_response->get_error_message(); } else { $this->integration_status_response = json_decode( wp_remote_retrieve_body( $integration_remote_request_response ), true ) ?? []; // If the merchant isn't connected to the Google App, it's not necessary to display an error indicating that the partner token isn't associated. if ( ! $this->integration_status_response['is_partner_token_healthy'] && isset( $this->integration_status_response['errors'] ['rest_api_partner_token']['error_code'] ) && $this->integration_status_response['errors'] ['rest_api_partner_token']['error_code'] === 'wpcom_partner_token_not_associated' ) { unset( $this->integration_status_response['errors'] ['rest_api_partner_token'] ); } if ( json_last_error() || ! isset( $this->integration_status_response['site'] ) ) { $this->response .= wp_remote_retrieve_body( $integration_remote_request_response ); } } } if ( 'disconnect-wp-api' === $_GET['action'] && check_admin_referer( 'disconnect-wp-api' ) ) { $request = new Request( 'DELETE', '/wc/gla/rest-api/authorize' ); $this->send_rest_request( $request ); } if ( 'wcs-auth-test' === $_GET['action'] && check_admin_referer( 'wcs-auth-test' ) ) { $url = trailingslashit( $this->get_connect_server_url() ) . 'connection/test'; $args = [ 'headers' => [ 'Authorization' => $this->get_auth_header() ], ]; $this->response = 'GET ' . $url . "\n" . var_export( $args, true ) . "\n"; $response = wp_remote_get( $url, $args ); if ( is_wp_error( $response ) ) { $this->response .= $response->get_error_message(); return; } $this->response .= wp_remote_retrieve_body( $response ); } if ( 'wcs-google-manager' === $_GET['action'] && check_admin_referer( 'wcs-google-manager' ) ) { if ( empty( $_GET['manager_id'] ) ) { $this->response .= 'Manager ID must be set'; return; } $id = absint( $_GET['manager_id'] ); $url = trailingslashit( $this->get_connect_server_url() ) . 'google/connection/google-manager'; $args = [ 'headers' => [ 'Authorization' => $this->get_auth_header() ], 'body' => [ 'returnUrl' => admin_url( 'admin.php?page=connection-test-admin-page' ), 'managerId' => $id, 'countries' => 'US,CA', ], ]; $this->response = 'POST ' . $url . "\n" . var_export( $args, true ) . "\n"; $response = wp_remote_post( $url, $args ); if ( is_wp_error( $response ) ) { $this->response .= $response->get_error_message(); return; } $this->response .= wp_remote_retrieve_body( $response ); $json = json_decode( wp_remote_retrieve_body( $response ), true ); if ( $json && isset( $json['oauthUrl'] ) ) { wp_redirect( $json['oauthUrl'] ); // phpcs:ignore WordPress.Security.SafeRedirect exit; } } if ( 'wcs-google-ads-setup' === $_GET['action'] && check_admin_referer( 'wcs-google-ads-setup' ) ) { $request = new Request( 'POST', '/wc/gla/ads/accounts' ); if ( is_numeric( $_GET['ads_id'] ?? false ) ) { $request->set_body_params( [ 'id' => absint( $_GET['ads_id'] ) ] ); } $this->send_rest_request( $request ); } if ( 'wcs-google-ads-check' === $_GET['action'] && check_admin_referer( 'wcs-google-ads-check' ) ) { $request = new Request( 'GET', '/wc/gla/ads/connection' ); $this->send_rest_request( $request ); } if ( 'wcs-google-ads-disconnect' === $_GET['action'] && check_admin_referer( 'wcs-google-ads-disconnect' ) ) { $request = new Request( 'DELETE', '/wc/gla/ads/connection' ); $this->send_rest_request( $request ); } if ( 'wcs-google-mc' === $_GET['action'] && check_admin_referer( 'wcs-google-mc' ) ) { /** @var Connection $connection */ $connection = $this->container->get( Connection::class ); $redirect_url = $connection->connect( admin_url( 'admin.php?page=connection-test-admin-page' ) ); if ( ! empty( $redirect_url ) ) { wp_redirect( $redirect_url ); // phpcs:ignore WordPress.Security.SafeRedirect exit; } } if ( 'wcs-google-mc-disconnect' === $_GET['action'] && check_admin_referer( 'wcs-google-mc-disconnect' ) ) { /** @var Connection $connection */ $connection = $this->container->get( Connection::class ); $response = $connection->disconnect(); $this->response .= $response; } if ( 'wcs-google-sv-link' === $_GET['action'] && check_admin_referer( 'wcs-google-sv-link' ) ) { try { if ( $this->container->get( Middleware::class )->link_merchant_to_mca() ) { $this->response .= "Linked merchant to MCA\n"; } } catch ( \Exception $e ) { $this->response .= $e->getMessage(); } } if ( 'wcs-google-mc-setup' === $_GET['action'] && check_admin_referer( 'wcs-google-mc-setup' ) ) { add_filter( 'woocommerce_gla_site_url', function( $url ) { return isset( $_GET['site_url'] ) ? esc_url_raw( $_GET['site_url'] ) : $url; } ); $request = new Request( 'POST', '/wc/gla/mc/accounts' ); if ( is_numeric( $_GET['account_id'] ?? false ) ) { $request->set_body_params( [ 'id' => $_GET['account_id'] ] ); } $this->send_rest_request( $request ); } if ( 'wcs-google-mc-claim-overwrite' === $_GET['action'] && check_admin_referer( 'wcs-google-mc-claim-overwrite' ) ) { $request = new Request( 'POST', '/wc/gla/mc/accounts/claim-overwrite' ); if ( is_numeric( $_GET['account_id'] ?? false ) ) { $request->set_body_params( [ 'id' => $_GET['account_id'] ] ); } $this->send_rest_request( $request ); } if ( 'wcs-google-mc-switch-url' === $_GET['action'] && check_admin_referer( 'wcs-google-mc-switch-url' ) ) { $request = new Request( 'POST', '/wc/gla/mc/accounts/switch-url' ); if ( is_numeric( $_GET['account_id'] ?? false ) ) { $request->set_body_params( [ 'id' => $_GET['account_id'] ] ); } $this->send_rest_request( $request ); } if ( 'clear-mc-status-cache' === $_GET['action'] && check_admin_referer( 'clear-mc-status-cache' ) ) { $this->container->get( MerchantStatuses::class )->clear_cache(); $this->response .= 'Merchant Center statuses transient successfully deleted.'; } if ( 'wcs-google-accounts-check' === $_GET['action'] && check_admin_referer( 'wcs-google-accounts-check' ) ) { $request = new Request( 'GET', '/wc/gla/mc/connection' ); $this->send_rest_request( $request ); } if ( 'wcs-google-accounts-delete' === $_GET['action'] && check_admin_referer( 'wcs-google-accounts-delete' ) ) { $request = new Request( 'DELETE', '/wc/gla/mc/connection' ); $this->send_rest_request( $request ); } if ( 'wcs-google-accounts-claim' === $_GET['action'] && check_admin_referer( 'wcs-google-accounts-claim' ) ) { add_filter( 'woocommerce_gla_site_url', function ( $url ) { return isset( $_GET['site_url'] ) ? esc_url_raw( $_GET['site_url'] ) : $url; } ); try { $this->container->get( Merchant::class )->claimwebsite(); $this->response .= 'Website claimed'; } catch ( \Exception $e ) { $this->response .= 'Error: ' . $e->getMessage(); } } if ( 'wcs-google-mc-status' === $_GET['action'] && check_admin_referer( 'wcs-google-mc-status' ) ) { $url = trailingslashit( $this->get_connect_server_url() ) . 'google/connection/google-mc'; $args = [ 'headers' => [ 'Authorization' => $this->get_auth_header() ], 'method' => 'GET', ]; $this->response = 'GET ' . $url . "\n" . var_export( $args, true ) . "\n"; $response = wp_remote_get( $url, $args ); if ( is_wp_error( $response ) ) { $this->response .= $response->get_error_message(); return; } $this->response .= wp_remote_retrieve_body( $response ); } if ( 'wcs-google-mc-id' === $_GET['action'] && check_admin_referer( 'wcs-google-mc-id' ) ) { try { $this->response = 'Proxied request > get merchant ID' . "\n"; foreach ( $this->container->get( Middleware::class )->get_merchant_accounts() as $account ) { $this->response .= sprintf( "Merchant ID: %s%s\n", $account['id'], $account['subaccount'] ? ' (IS a subaccount)' : '' ); $_GET['merchant_id'] = $account['id']; } } catch ( \Exception $e ) { $this->response .= $e->getMessage(); } } if ( 'wcs-google-mc-proxy' === $_GET['action'] && check_admin_referer( 'wcs-google-mc-proxy' ) ) { /** @var Merchant $merchant */ $merchant = $this->container->get( Merchant::class ); /** @var OptionsInterface $options */ $options = $this->container->get( OptionsInterface::class ); if ( empty( $options->get_merchant_id() ) ) { $this->response .= 'Please enter a Merchant ID'; return; } $this->response = "Proxied request > get products for merchant {$options->get_merchant_id()}\n"; $products = $merchant->get_products(); if ( empty( $products ) ) { $this->response .= 'No products found'; } foreach ( $products as $product ) { $this->response .= "{$product->getId()} {$product->getTitle()}\n"; } } if ( 'wcs-ads-customers-lib' === $_GET['action'] && check_admin_referer( 'wcs-ads-customers-lib' ) ) { try { $accounts = $this->container->get( Ads::class )->get_ads_accounts(); $this->response .= 'Total accounts: ' . count( $accounts ) . "\n"; foreach ( $accounts as $account ) { $this->response .= sprintf( "%d : %s\n", $account['id'], $account['name'] ); $_GET['customer_id'] = $account['id']; } } catch ( \Exception $e ) { $this->response .= 'Error: ' . $e->getMessage(); } } if ( 'wcs-ads-campaign-lib' === $_GET['action'] && check_admin_referer( 'wcs-ads-campaign-lib' ) ) { try { /** @var AdsCampaign $ads_campaign */ $ads_campaign = $this->container->get( AdsCampaign::class ); /** @var OptionsInterface $options */ $options = $this->container->get( OptionsInterface::class ); $this->response = "Proxied request > get ad campaigns {$options->get_ads_id()}\n"; $campaigns = $ads_campaign->get_campaigns(); if ( empty( $campaigns ) ) { $this->response .= 'No campaigns found'; } else { $this->response .= 'Total campaigns: ' . count( $campaigns ) . "\n"; foreach ( $campaigns as $campaign ) { $this->response .= print_r( $campaign, true ) . "\n"; } } } catch ( \Exception $e ) { $this->response .= 'Error: ' . $e->getMessage(); } } if ( 'wcs-accept-tos' === $_GET['action'] && check_admin_referer( 'wcs-accept-tos' ) ) { $result = $this->container->get( Middleware::class )->mark_tos_accepted( 'google-mc', 'john.doe@example.com' ); $this->response .= sprintf( 'Attempting to accept Tos. Successful? %s
Response body: %s', $result->accepted() ? 'Yes' : 'No', $result->message() ); } if ( 'wcs-check-tos' === $_GET['action'] && check_admin_referer( 'wcs-check-tos' ) ) { $accepted = $this->container->get( Middleware::class )->check_tos_accepted( 'google-mc' ); $this->response .= sprintf( 'Tos Accepted? %s
Response body: %s', $accepted->accepted() ? 'Yes' : 'No', $accepted->message() ); } if ( 'wcs-sync-product' === $_GET['action'] && check_admin_referer( 'wcs-sync-product' ) ) { if ( empty( $_GET['product_id'] ) ) { $this->response .= 'Please enter a Product ID'; return; } $id = absint( $_GET['product_id'] ); $product = wc_get_product( $id ); if ( $product instanceof \WC_Product ) { if ( empty( $_GET['async'] ) ) { /** @var ProductSyncer $product_syncer */ $product_syncer = $this->container->get( ProductSyncer::class ); try { $result = $product_syncer->update( [ $product ] ); $this->response .= sprintf( '%s products successfully submitted to Google.', count( $result->get_products() ) ) . "\n"; if ( ! empty( $result->get_errors() ) ) { $this->response .= sprintf( 'There were %s errors:', count( $result->get_errors() ) ) . "\n"; foreach ( $result->get_errors() as $invalid_product ) { $this->response .= sprintf( "%s:\n%s", $invalid_product->get_wc_product_id(), implode( "\n", $invalid_product->get_errors() ) ) . "\n"; } } } catch ( ProductSyncerException $exception ) { $this->response = 'Error submitting product to Google: ' . $exception->getMessage(); } } else { // schedule a job /** @var UpdateProducts $update_job */ $update_job = $this->container->get( JobRepository::class )->get( UpdateProducts::class ); $update_job->schedule( [ [ $product->get_id() ] ] ); $this->response = 'Successfully scheduled a job to sync the product ' . $product->get_id(); } } else { $this->response = 'Invalid product ID provided: ' . $id; } } if ( 'wcs-sync-all-products' === $_GET['action'] && check_admin_referer( 'wcs-sync-all-products' ) ) { if ( empty( $_GET['async'] ) ) { /** @var ProductSyncer $product_syncer */ $product_syncer = $this->container->get( ProductSyncer::class ); /** @var ProductRepository $product_repository */ $product_repository = $this->container->get( ProductRepository::class ); try { $products = $product_repository->find_sync_ready_products()->get(); $result = $product_syncer->update( $products ); $this->response .= sprintf( '%s products successfully submitted to Google.', count( $result->get_products() ) ) . "\n"; if ( ! empty( $result->get_errors() ) ) { $this->response .= sprintf( 'There were %s errors:', count( $result->get_errors() ) ) . "\n"; foreach ( $result->get_errors() as $invalid_product ) { $this->response .= sprintf( "%s:\n%s", $invalid_product->get_wc_product_id(), implode( "\n", $invalid_product->get_errors() ) ) . "\n"; } } } catch ( ProductSyncerException $exception ) { $this->response = 'Error submitting products to Google: ' . $exception->getMessage(); } } else { // schedule a job /** @var UpdateAllProducts $update_job */ $update_job = $this->container->get( JobRepository::class )->get( UpdateAllProducts::class ); $update_job->schedule(); $this->response = 'Successfully scheduled a job to sync all products!'; } } if ( 'wcs-delete-synced-products' === $_GET['action'] && check_admin_referer( 'wcs-delete-synced-products' ) ) { if ( empty( $_GET['async'] ) ) { /** @var ProductSyncer $product_syncer */ $product_syncer = $this->container->get( ProductSyncer::class ); /** @var ProductRepository $product_repository */ $product_repository = $this->container->get( ProductRepository::class ); try { $products = $product_repository->find_synced_products(); $result = $product_syncer->delete( $products ); $this->response .= sprintf( '%s synced products deleted from Google.', count( $result->get_products() ) ) . "\n"; if ( ! empty( $result->get_errors() ) ) { $this->response .= sprintf( 'There were %s errors:', count( $result->get_errors() ) ) . "\n"; foreach ( $result->get_errors() as $invalid_product ) { $this->response .= sprintf( "%s:\n%s", $invalid_product->get_wc_product_id(), implode( "\n", $invalid_product->get_errors() ) ) . "\n"; } } } catch ( ProductSyncerException $exception ) { $this->response = 'Error deleting products from Google: ' . $exception->getMessage(); } } else { // schedule a job /** @var DeleteAllProducts $delete_job */ $delete_job = $this->container->get( JobRepository::class )->get( DeleteAllProducts::class ); $delete_job->schedule(); $this->response = 'Successfully scheduled a job to delete all synced products!'; } } if ( 'wcs-cleanup-products' === $_GET['action'] && check_admin_referer( 'wcs-cleanup-products' ) ) { if ( empty( $_GET['async'] ) ) { /** @var ProductSyncer $product_syncer */ $product_syncer = $this->container->get( ProductSyncer::class ); /** @var ProductRepository $product_repository */ $product_repository = $this->container->get( ProductRepository::class ); /** @var BatchProductHelper $batch_product_helper */ $batch_product_helper = $this->container->get( BatchProductHelper::class ); try { $products = $product_repository->find_synced_products(); $stale_entries = $batch_product_helper->generate_stale_products_request_entries( $products ); $result = $product_syncer->delete_by_batch_requests( $stale_entries ); $this->response .= sprintf( '%s products cleaned up.', count( $result->get_products() ) ) . "\n"; if ( ! empty( $result->get_errors() ) ) { $this->response .= sprintf( 'There were %s errors:', count( $result->get_errors() ) ) . "\n"; foreach ( $result->get_errors() as $invalid_product ) { $this->response .= sprintf( "%s:\n%s", $invalid_product->get_wc_product_id(), implode( "\n", $invalid_product->get_errors() ) ) . "\n"; } } } catch ( ProductSyncerException $exception ) { $this->response = 'Error cleaning up products: ' . $exception->getMessage(); } } else { // schedule a job /** @var CleanupProductsJob $delete_job */ $delete_job = $this->container->get( JobRepository::class )->get( CleanupProductsJob::class ); $delete_job->schedule(); $this->response = 'Successfully scheduled a job to cleanup all products!'; } } if ( 'migrate-gtin' === $_GET['action'] && check_admin_referer( 'migrate-gtin' ) ) { /** @var MigrateGTIN $job */ $job = $this->container->get( JobRepository::class )->get( MigrateGTIN::class ); $job->schedule(); $this->response = 'Successfully scheduled a job to migrate GTIN'; } } /** * Retrieve an authorization header containing a Jetpack token. * * @return string Authorization header. */ private function get_auth_header(): string { /** @var Manager $manager */ $manager = $this->container->get( Manager::class ); $token = $manager->get_tokens()->get_access_token(); [ $token_key, $token_secret ] = explode( '.', $token->secret ); $token_key = sprintf( '%s:%d:%d', $token_key, defined( 'JETPACK__API_VERSION' ) ? JETPACK__API_VERSION : 1, $token->external_user_id ); $time_diff = (int) Jetpack_Options::get_option( 'time_diff' ); $timestamp = time() + $time_diff; $nonce = wp_generate_password( 10, false ); $normalized_request_string = join( "\n", [ $token_key, $timestamp, $nonce ] ) . "\n"; $signature = base64_encode( hash_hmac( 'sha1', $normalized_request_string, $token_secret, true ) ); $auth = [ 'token' => $token_key, 'timestamp' => $timestamp, 'nonce' => $nonce, 'signature' => $signature, ]; $header_pieces = []; foreach ( $auth as $key => $value ) { $header_pieces[] = sprintf( '%s="%s"', $key, $value ); } return 'X_JP_Auth ' . join( ' ', $header_pieces ); } /** * Send a REST API request and add the response to our buffer. */ private function send_rest_request( Request $request ) { $response = rest_do_request( $request ); $server = rest_get_server(); $data = $server->response_to_data( $response, false ); $json = wp_json_encode( $data, JSON_PRETTY_PRINT ); $this->response .= 'Request: ' . $request->get_method() . ' ' . $request->get_route() . PHP_EOL; $this->response .= 'Status: ' . $response->get_status() . PHP_EOL; $this->response .= 'Response: ' . $json; return $data; } }