Jake Vanderwerf
2026-09-18 a6413da67a4579866f0fd0323ddb23a7b6b9654a
inc/integrations/services/GoogleMyBusiness.php
@@ -8,7 +8,6 @@
use JVBase\integrations\OAuthCredentials;
use JVBase\integrations\OAuthURLs;
use JVBase\integrations\Requests;
use JVBase\integrations\SyncTo;
use JVBase\integrations\SyncToPost;
use JVBase\integrations\UserConnection;
use WP_Error;
@@ -57,6 +56,8 @@
         );
            $client_id->setRequired();
            $client_id->setHint('From your Google Cloud Console OAuth credentials.');
            $client_id->makeSetupField();
            $this->updateField('client_id', $client_id);
         $secret = $this->addField(
            'client_secret',
@@ -66,6 +67,8 @@
         );
            $secret->setSubType('password');
            $secret->setRequired();
            $secret->makeSetupField();
            $this->updateField('client_secret', $client_id);
         if ($this->getAccessToken()) {
            $accs = $this->addField('accounts', 'Accounts', 'select', [$this, 'testUserCanConnect']);
@@ -102,6 +105,25 @@
            'Add redirect URI: <code>'.esc_html($this->getRedirectUri()).'</code>',
            'Use the "Authorize" button below to complete OAuth flow'
         ];
         $this->instructions = [
            'Go to <a href="https://console.cloud.google.com/apis/credentials" target="_blank">Google Cloud Console</a>',
            'Create OAuth 2.0 Client ID credentials',
            'Enable the following APIs in your project:',
            [
               '<a href="https://console.cloud.google.com/apis/library/mybusinessaccountmanagement.googleapis.com" target="_blank">My Business Account Management API</a>',
               '<a href="https://console.cloud.google.com/apis/library/mybusinessbusinessinformation.googleapis.com" target="_blank">My Business Business Information API</a>',
               '<a href="https://console.cloud.google.com/apis/library/mybusiness.googleapis.com" target="_blank">Google My Business API</a> (legacy v4, required for reviews)',
            ],
            'On the OAuth consent screen, add these scopes:',
            [
               '<code>https://www.googleapis.com/auth/business.manage</code>',
               '<code>https://www.googleapis.com/auth/userinfo.email</code>',
               '<code>https://www.googleapis.com/auth/userinfo.profile</code>',
            ],
            'Add redirect URI: <code>'.esc_html($this->getRedirectUri()).'</code>',
            'Use the "Authorize" button below to complete OAuth flow'
         ];
      }
      protected function defineOAuth():void
      {
@@ -120,11 +142,13 @@
      }
      protected function defineActions():void
      {
         $this->addAction(
            'Update Location',
            [$this, 'handleUpdateLocation'],
            'update_location',
         );
         if ($this->hasOAuthCredentials($this->userID)) {
            $this->addAction(
               'Update Location',
               [$this, 'handleUpdateLocation'],
               'update_location',
            );
         }
      }
         public function handleUpdateLocation(array $data):array
         {
@@ -153,7 +177,7 @@
      ];
   }
   protected function handleResponse(\WP_Error|array $response, string $method, string $endpoint): array
   protected function handleResponse(WP_Error|array $response, string $method, string $endpoint): array
   {
      if (is_wp_error($response) && $response->get_error_message() === 'rate_limit_exceeded') {
         return $this->response(false, 'Rate limit exceeded');
@@ -170,12 +194,81 @@
   protected function refreshAccessToken(OAuthCredentials $credentials): array
   {
      if (empty($this->getClientID()) || empty($this->getClientSecret()) || empty($credentials->refresh_token)) {
         $this->logError('refreshAccessToken', 'Missing refresh token');
         return $this->response(false, 'Missing client ID, client Secret, or refresh token');
      }
      $response = wp_remote_post(
         $this->oauth->getToken(),
         [
            'timeout'   => 15,
            'body'   => [
               'grant_type'   => 'refresh_token',
               'refresh_token'   => $credentials->refresh_token,
               'client_id'    =>$this->getClientID(),
               'client_secret'   =>$this->getClientSecret()
            ],
            'headers'   => $this->getOAuthRequestHeaders()
         ]
      );
      if (is_wp_error($response)) {
         $this->logError(
            'refreshAccessToken',
            'Failed to refresh GMB Token',
            [
               'error'  => $response->get_error_message()
            ]
         );
         return $this->response(false, 'Failed to refresh GMB Token');
      }
      $data = json_decode(wp_remote_retrieve_body($response), true);
      if (empty($data['access_token'])) {
         $this->logError('refreshAccessToken', 'No access token in response');
         return $this->response(false, 'No access token in response');
      }
      $credentials = [
         'access_token' => $data['access_token'],
         'expires_at'   => time() + (int)($data['expires_in']??3600),
         // Google usually omits refresh_token on refresh — keep the old one
         'refresh_token' => $body['refresh_token'] ?? $credentials->refresh_token,
      ];
      $this->saveCredentials($credentials);
      return $this->response(true, 'Successfully refreshed token');
   }
   /*********************************************************
    * UTILITY
   *********************************************************/
   protected function getClientID():string|false
   {
      $switch = false;
      if ($this->userID !== null) {
         $switch = $this->userID;
         $this->switchUser(null);
      }
      $ID =  $this->loadCredentials()['client_id']??false;
      if ($switch) {
         $this->switchUser($switch);
      }
      return $ID;
   }
   protected function getClientSecret():string|false
   {
      $switch = false;
      if ($this->userID !== null) {
         $switch = $this->userID;
         $this->switchUser(null);
      }
      $ID = $this->loadCredentials()['client_secret']??false;
      if ($switch) {
         $this->switchUser($switch);
      }
      return $ID;
   }
   protected function getAccountID():string|false
   {
      return $this->loadCredentials()['account_id']??false;