PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.64
Timetics – Appointment Booking Calendar & Scheduling v1.0.64
1.0.64 1.0.62 1.0.63 1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 All 65 releases
← All changes | core/integrations/google/client.php +62 -12 1.0.56 → 1.0.64 View file →
@@ -5,8 +5,10 @@
5 5 * @package Timetics
6 6 */
7 7 namespace Timetics\Core\Integrations\Google;
8 8
9 +defined( 'ABSPATH' ) || exit;
10 +
9 11 use Exception;
10 12 use InvalidArgumentException;
11 13
12 14 /**
@@ -93,8 +95,9 @@
93 95 'scope' => urlencode_deep( $this->auth_scope ),
94 96 'redirect_uri' => $this->redirect_uri,
95 97 'response_type' => 'code',
96 98 'access_type' => 'offline',
99 + 'prompt' => 'consent',
97 100 );
98 101
99 102 if ( ! empty( $state ) ) {
100 103 $params['state'] = $state;
@@ -144,9 +147,11 @@
144 147 * Fetch access token
145 148 *
146 149 * @param string $code
147 150 *
148 - * @return void
151 + * @return array Token data on success.
152 + * @throws \InvalidArgumentException When the code is empty.
153 + * @throws \Exception On transport failure or a non-200 token response.
149 154 */
150 155 public function fetch_access_token_with_auth_code( $code ) {
151 156 if ( strlen( $code ) === 0 ) {
152 157 throw new InvalidArgumentException( 'Invalid code' );
@@ -161,16 +166,21 @@
161 166 );
162 167
163 168 $response = wp_remote_post( self::TIMETICS_TOKEN_URI, array( 'body' => $args ) );
164 169
165 - $status_code = wp_remote_retrieve_response_code( $response );
170 + if ( is_wp_error( $response ) ) {
171 + throw new Exception( esc_html( $response->get_error_message() ) );
172 + }
166 173
167 - if ( 200 != $status_code ) {
168 - return false;
174 + $status_code = (int) wp_remote_retrieve_response_code( $response );
175 + $body = wp_remote_retrieve_body( $response );
176 + $data = json_decode( $body, true );
177 +
178 + if ( 200 !== $status_code || empty( $data['access_token'] ) ) {
179 + $message = ! empty( $data['error_description'] ) ? $data['error_description'] : __( 'Failed to obtain Google access token.', 'timetics' );
180 + throw new Exception( esc_html( $message ) );
169 181 }
170 182
171 - $data = json_decode( wp_remote_retrieve_body( $response ), true );
172 -
173 183 return $data;
174 184 }
175 185
176 186 /**
@@ -175,11 +185,21 @@
175 185
176 186 /**
177 187 * Fetch access token by using refresh token
178 188 *
179 - * @return void
189 + * @param string $refresh_token
190 + *
191 + * @return array|\WP_Error Token data on success, or a WP_Error whose error_data carries a boolean `transient` flag.
180 192 */
181 193 public function fetch_access_token_with_refresh_token( $refresh_token ) {
194 + if ( empty( $refresh_token ) ) {
195 + return new \WP_Error(
196 + 'timetics_google_no_refresh_token',
197 + 'No Google refresh token is available for this account.',
198 + array( 'transient' => false )
199 + );
200 + }
201 +
182 202 $args = array(
183 203 'client_id' => $this->client_id,
184 204 'client_secret' => $this->client_secrete,
185 205 'refresh_token' => $refresh_token,
@@ -188,17 +208,47 @@
188 208 );
189 209
190 210 $response = wp_remote_post( self::TIMETICS_TOKEN_URI, array( 'body' => $args ) );
191 211
192 - $status_code = wp_remote_retrieve_response_code( $response );
212 + // Transport-level failure (DNS, timeout, connection refused). Transient:
213 + // the credential is fine, Google was just briefly unreachable.
214 + if ( is_wp_error( $response ) ) {
215 + return new \WP_Error(
216 + 'timetics_google_refresh_http_error',
217 + $response->get_error_message(),
218 + array( 'transient' => true )
219 + );
220 + }
193 221
194 - if ( 200 != $status_code ) {
195 - return false;
222 + $status_code = (int) wp_remote_retrieve_response_code( $response );
223 + $body = wp_remote_retrieve_body( $response );
224 + $data = json_decode( $body, true );
225 +
226 + if ( 200 === $status_code && ! empty( $data['access_token'] ) ) {
227 + return $data;
196 228 }
197 229
198 - $data = json_decode( wp_remote_retrieve_body( $response ), true );
230 + $google_error = isset( $data['error'] ) ? $data['error'] : '';
231 + $transient = ( 'invalid_grant' !== $google_error );
199 232
200 - return $data;
233 + if ( 429 === $status_code || $status_code >= 500 || 0 === $status_code ) {
234 + $transient = true;
235 + }
236 +
237 + if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
238 + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging is guarded by WP_DEBUG checks.
239 + error_log( sprintf( 'Timetics Google token refresh failed (HTTP %d): %s', $status_code, $body ) );
240 + }
241 +
242 + return new \WP_Error(
243 + 'timetics_google_refresh_failed',
244 + ! empty( $data['error_description'] ) ? $data['error_description'] : 'Failed to refresh Google access token.',
245 + [
246 + 'transient' => $transient,
247 + 'status_code' => $status_code,
248 + 'error' => $google_error,
249 + ]
250 + );
201 251 }
202 252
203 253 /**
204 254 * Revoke authorization