Skip to content

Instantly share code, notes, and snippets.

@chikaibeneme
Created February 27, 2025 04:18
Show Gist options
  • Select an option

  • Save chikaibeneme/7b092e0602736c73dd7991bc5c88548a to your computer and use it in GitHub Desktop.

Select an option

Save chikaibeneme/7b092e0602736c73dd7991bc5c88548a to your computer and use it in GitHub Desktop.
* Patch the PayPal Client request() method to capture arguments once * and avoid re-capturing them on retry.
<?php
/**
* Patch the PayPal Client request() method to capture arguments once
* and avoid re-capturing them on retry.
*
* Requires the runkit or runkit7 extension.
*/
add_action( 'plugins_loaded', function() {
if ( ! function_exists( 'runkit_method_redefine' ) ) {
error_log( 'Runkit extension is not available; PayPal Client patch was not applied.' );
return;
}
// Redefine the "request" method of the Client class.
runkit_method_redefine(
'TEC\Tickets\Commerce\Gateways\PayPal\Client',
'request',
'$method, $url, array $query_args = array(), array $request_arguments = array(), $raw = false, $retries = 0',
<<<'EOD'
// Capture the arguments the first time this method is called.
$arguments = array($method, $url, $query_args, $request_arguments, $raw, $retries);
$method = strtoupper($method);
// If $url is not a full URL, append query args.
$url = 0 !== strpos($url, 'https://')
? $this->get_api_url($url, $query_args)
: add_query_arg($query_args, $url);
$default_arguments = [
'headers' => [
'Accept' => 'application/json',
'Authorization' => sprintf('Bearer %1$s', $this->get_access_token()),
'Content-Type' => 'application/json',
]
];
// For non-GET requests, ensure a body exists.
if ('GET' !== $method) {
$default_arguments['body'] = [];
}
foreach ($default_arguments as $key => $default_argument) {
$request_arguments[$key] = array_merge($default_argument, \Tribe__Utils__Array::get($request_arguments, $key, []));
}
if ('GET' !== $method) {
$content_type = \Tribe__Utils__Array::get($request_arguments, ['headers', 'Content-Type']);
if (empty($content_type)) {
$content_type = \Tribe__Utils__Array::get($request_arguments, ['headers', 'content-type']);
}
// If body is present and content type is JSON, encode it.
if (!empty($request_arguments['body']) && 'application/json' === strtolower($content_type)) {
$request_arguments['body'] = wp_json_encode($request_arguments['body']);
}
}
// Execute the request.
if ('GET' === $method) {
$response = wp_remote_get($url, $request_arguments);
} elseif ('POST' === $method) {
$response = wp_remote_post($url, $request_arguments);
} else {
$request_arguments['method'] = $method;
$response = wp_remote_request($url, $request_arguments);
}
if (is_wp_error($response)) {
tribe('logger')->log_error(sprintf(
'[%s] PayPal "%s" request error: %s',
$method,
$url,
$response->get_error_message()
), 'tickets-commerce-paypal');
return $response;
}
if (true === $raw) {
return $response;
}
$response_code = wp_remote_retrieve_response_code($response);
// Store the debug header if available.
$this->set_debug_header(null);
if (!empty($response['headers']['Paypal-Debug-Id'])) {
$this->set_debug_header($response['headers']['Paypal-Debug-Id']);
}
// On 401 error (and not for token generation), retry once.
if (
401 === $response_code &&
2 >= $retries &&
false === strpos($url, 'v1/oauth2/token')
) {
$merchant = tribe(Merchant::class);
$token_data = $this->get_access_token_from_client_credentials($merchant->get_client_id(), $merchant->get_client_secret());
$saved = $merchant->save_access_token_data($token_data);
if ($saved) {
// Use the previously captured $arguments,
// update the retry count and re-call the method.
array_pop($arguments);
$arguments[] = $retries + 1;
return call_user_func_array([$this, 'request'], $arguments);
}
}
// Decode the response body.
$response_body = wp_remote_retrieve_body($response);
$response_body = @json_decode($response_body, true);
if (empty($response_body)) {
return $response;
}
if (!is_array($response_body)) {
tribe('logger')->log_error(sprintf('[%s] Unexpected PayPal %s response', $url, $method), 'tickets-commerce-paypal');
return new \WP_Error('tec-tickets-commerce-gateway-paypal-client-unexpected', null, [
'method' => $method,
'url' => $url,
'query_args' => $query_args,
'request_arguments' => $request_arguments,
'response' => $response,
]);
}
return $response_body;
EOD
);
} );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment