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 +90 -35 1.0.2 → 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 /**
@@ -86,21 +88,23 @@
86 88 * Get auth url
87 89 *
88 90 * @return string
89 91 */
90 - public function get_auth_url() {
91 - $auth_url = add_query_arg(
92 - [
93 - 'client_id' => $this->client_id,
94 - 'scope' => urlencode_deep( $this->auth_scope ),
95 - 'redirect_uri' => $this->redirect_uri,
96 - 'response_type' => 'code',
97 - 'access_type' => 'offline',
92 + public function get_auth_url( $state = '' ) {
93 + $params = array(
94 + 'client_id' => $this->client_id,
95 + 'scope' => urlencode_deep( $this->auth_scope ),
96 + 'redirect_uri' => $this->redirect_uri,
97 + 'response_type' => 'code',
98 + 'access_type' => 'offline',
99 + 'prompt' => 'consent',
100 + );
98 101
99 - ], self::TIMETICS_AUTH_URL
100 - );
102 + if ( ! empty( $state ) ) {
103 + $params['state'] = $state;
104 + }
101 105
102 - return $auth_url;
106 + return add_query_arg( $params, self::TIMETICS_AUTH_URL );
103 107 }
104 108
105 109 /**
106 110 * Set google authentication configuration
@@ -106,13 +110,13 @@
106 110 * Set google authentication configuration
107 111 *
108 112 * @return void
109 113 */
110 - public function set_auth_config( $args = [] ) {
111 - $defaults = [
114 + public function set_auth_config( $args = array() ) {
115 + $defaults = array(
112 116 'client_id' => '',
113 117 'client_secrete' => '',
114 - ];
118 + );
115 119
116 120 $args = wp_parse_args( $args, $defaults );
117 121
118 122 $this->client_id = $args['client_id'];
@@ -143,9 +147,11 @@
143 147 * Fetch access token
144 148 *
145 149 * @param string $code
146 150 *
147 - * @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.
148 154 */
149 155 public function fetch_access_token_with_auth_code( $code ) {
150 156 if ( strlen( $code ) === 0 ) {
151 157 throw new InvalidArgumentException( 'Invalid code' );
@@ -150,50 +156,99 @@
150 156 if ( strlen( $code ) === 0 ) {
151 157 throw new InvalidArgumentException( 'Invalid code' );
152 158 }
153 159
154 - $args = [
160 + $args = array(
155 161 'client_id' => $this->client_id,
156 162 'client_secret' => $this->client_secrete,
157 163 'code' => $code,
158 164 'redirect_uri' => $this->redirect_uri,
159 165 'grant_type' => 'authorization_code',
160 - ];
166 + );
161 167
162 - $response = wp_remote_post( self::TIMETICS_TOKEN_URI, [ 'body' => $args ] );
168 + $response = wp_remote_post( self::TIMETICS_TOKEN_URI, array( 'body' => $args ) );
163 169
164 - if ( ! is_wp_error( $response ) ) {
165 - $data = json_decode( wp_remote_retrieve_body( $response ), true );
170 + if ( is_wp_error( $response ) ) {
171 + throw new Exception( esc_html( $response->get_error_message() ) );
172 + }
166 173
167 - return $data;
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 ) );
168 181 }
169 182
170 - return $response;
183 + return $data;
171 184 }
172 185
173 186 /**
174 187 * Fetch access token by using refresh token
175 188 *
176 - * @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.
177 192 */
178 193 public function fetch_access_token_with_refresh_token( $refresh_token ) {
179 - $args = [
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 +
202 + $args = array(
180 203 'client_id' => $this->client_id,
181 204 'client_secret' => $this->client_secrete,
182 205 'refresh_token' => $refresh_token,
183 206 'redirect_uri' => $this->redirect_uri,
184 207 'grant_type' => 'refresh_token',
185 - ];
208 + );
186 209
187 - $response = wp_remote_post( self::TIMETICS_TOKEN_URI, [ 'body' => $args ] );
210 + $response = wp_remote_post( self::TIMETICS_TOKEN_URI, array( 'body' => $args ) );
188 211
189 - if ( ! is_wp_error( $response ) ) {
190 - $data = json_decode( wp_remote_retrieve_body( $response ), true );
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 + }
191 221
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'] ) ) {
192 227 return $data;
193 228 }
194 229
195 - return $response;
230 + $google_error = isset( $data['error'] ) ? $data['error'] : '';
231 + $transient = ( 'invalid_grant' !== $google_error );
232 +
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 + );
196 251 }
197 252
198 253 /**
199 254 * Revoke authorization
@@ -203,18 +258,18 @@
203 258 * @return bool
204 259 */
205 260 public function revoke( $token ) {
206 261 $response = wp_remote_post(
207 - self::TIMETICS_REVOKE_URI, [
208 - 'headers' => [
262 + self::TIMETICS_REVOKE_URI, array(
263 + 'headers' => array(
209 264 'content-type' => 'application/x-www-form-urlencoded',
210 - ],
265 + ),
211 266 'body' => build_query(
212 - [
267 + array(
213 268 'token' => $token,
214 - ]
269 + )
215 270 ),
216 - ]
271 + )
217 272 );
218 273
219 274 $status_code = wp_remote_retrieve_response_code( $response );
220 275