????
| Current Path : /home/arabianr/public_html/wp-content/plugins/simplybook/app/Services/ |
| Current File : //home/arabianr/public_html/wp-content/plugins/simplybook/app/Services/CreateAccountService.php |
<?php
namespace SimplyBook\Services;
use SimplyBook\Traits\HasLogging;
use SimplyBook\Exceptions\ApiException;
use SimplyBook\Support\Helpers\Storages\EnvironmentConfig;
class CreateAccountService
{
use HasLogging;
private const SIMPLYBOOK_API_VERSION = 'v2';
private const INSTALLATION_ID_OPTION = '_simplybook_installation_id';
private const EMPTY_INSTALLATION_ID = 'unknown';
// API Endpoints
private const ENDPOINT_COMPANY = 'simplybook/company';
protected EnvironmentConfig $env;
public function __construct(EnvironmentConfig $env)
{
$this->env = $env;
}
/**
* Register a new company.
*
* @param string $captchaToken reCAPTCHA Enterprise token (not score).
* @throws ApiException
*/
public function registerCompany(
string $companyLogin,
string $email,
string $password,
bool $marketingConsent,
string $callbackUrl,
string $captchaToken = '',
?int $category = null,
string $userAgent = ''
): array {
// Sanitize inputs
$sanitizedCompanyLogin = sanitize_text_field($companyLogin);
$requestBody = [
'company_login' => $sanitizedCompanyLogin,
'email' => sanitize_email($email),
'callback_url' => esc_url_raw($callbackUrl),
'password' => sanitize_text_field($password),
'retype_password' => sanitize_text_field($password),
'journey_type' => 'wp_plugin',
'marketing_consent' => $marketingConsent,
];
if (is_int($category) && $category > 0) {
$requestBody['category'] = $category;
}
return $this->request('POST', self::ENDPOINT_COMPANY, $requestBody, $sanitizedCompanyLogin, $captchaToken, $userAgent);
}
/**
* Make a request.
* @throws ApiException
*/
private function request(string $method, string $endpoint, array $body = [], string $companyLogin = '', string $captchaToken = '', string $userAgent = ''): array
{
$url = $this->buildUrl($endpoint);
$headers = $this->buildRequestHeaders($companyLogin, $captchaToken);
if (!empty($userAgent)) {
$headers['User-Agent'] = sanitize_text_field($userAgent);
}
$args = [
'method' => $method,
'headers' => $headers,
'timeout' => 15,
'sslverify' => true,
'body' => wp_json_encode($body),
];
$response = wp_safe_remote_request($url, $args);
if (is_wp_error($response)) {
throw (new ApiException(
__('Failed to connect.', 'simplybook')
))->setData([
'error' => sanitize_text_field($response->get_error_message()),
]);
}
return $this->parseResponse($response);
}
/**
* Build request headers including optional company login and captcha token.
*/
private function buildRequestHeaders(string $companyLogin = '', string $captchaToken = ''): array
{
$headers = array_merge($this->getRspalHeaders(), [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]);
if (!empty($companyLogin)) {
$headers['X-Company-Login'] = sanitize_text_field($companyLogin);
}
if (!empty($captchaToken)) {
$headers['RSPAL-RecaptchaV3Token'] = sanitize_text_field($captchaToken);
}
return $headers;
}
/**
* Parse the response and handle errors.
* @throws ApiException
*/
private function parseResponse(array $response): array
{
$responseCode = wp_remote_retrieve_response_code($response);
$responseBodyRaw = wp_remote_retrieve_body($response);
$responseBody = json_decode($responseBodyRaw, true);
if (!is_array($responseBody)) {
throw new ApiException(__('Invalid response.', 'simplybook'));
}
if (isset($responseBody['rspal-error'])) {
$this->handleRspalError($responseBody['rspal-error'], $responseCode);
}
return [
'code' => (int) $responseCode,
'body' => $responseBody,
];
}
/**
* Handle rspal-error responses.
*
* @param array|string $rspalError The error data from the rspal-error response.
* @throws ApiException
*/
private function handleRspalError($rspalError, int $responseCode): void
{
$errorMessage = is_array($rspalError)
? wp_json_encode($rspalError)
: sanitize_text_field($rspalError);
throw (new ApiException(
$errorMessage ?: __('Account registration failed. Please try again.', 'simplybook')
))->setData([
'error' => $errorMessage,
]);
}
/**
* Get the Installation ID or 'unknown' if not set.
*/
public function getInstallationId(): string
{
return get_option(self::INSTALLATION_ID_OPTION, self::EMPTY_INSTALLATION_ID);
}
/**
* Request the creation of a new installation ID
* @param bool $override Whether to override existing ID or not
* @throws ApiException
*/
public function createInstallationId(string $userAgent, bool $override = true): void
{
$existingId = $this->getInstallationId();
if (($existingId !== self::EMPTY_INSTALLATION_ID) && !$override) {
return;
}
$headers = array_merge([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'User-Agent' => $userAgent,
], $this->getRspalHeaders());
$response = wp_remote_post($this->env->getUrl('simplybook.rsp_auth_url') . '/installation/create', [
'headers' => $headers,
'timeout' => 15,
'sslverify' => true,
'body' => json_encode([]),
]);
if (is_wp_error($response)) {
throw new ApiException('Could not create Installation ID. WP Error: ' . $response->get_error_message());
}
$responseCode = wp_remote_retrieve_response_code($response);
$responseBody = json_decode(wp_remote_retrieve_body($response), true);
if ($responseCode !== 200 && $responseCode !== 201) {
throw new ApiException('Could not create Installation ID. Invalid response code: ' . $responseCode);
}
$installationId = isset($responseBody['uuid']) ? sanitize_text_field($responseBody['uuid']) : '';
if (empty($installationId)) {
throw new ApiException('Could not create Installation ID. Installation ID not found in response.');
}
update_option(self::INSTALLATION_ID_OPTION, $installationId, false);
}
/**
* Build the URL.
*/
private function buildUrl(string $endpoint): string
{
$endpoint = preg_replace('/[^a-zA-Z0-9\/_-]/', '', $endpoint);
return $this->env->getUrl('simplybook.rsp_auth_url') . '/' . self::SIMPLYBOOK_API_VERSION . '/' . ltrim($endpoint, '/');
}
/**
* Build the RSPAL headers.
*/
private function getRspalHeaders(): array
{
$installationId = $this->getInstallationId();
$headers = [
'RSPAL-PluginName' => $this->env->getString('plugin.name'),
'RSPAL-PluginVersion' => $this->env->getString('plugin.version'),
'RSPAL-PluginPath' => $this->getPluginRelativePath(),
'RSPAL-Origin' => trailingslashit(site_url()),
'RSPAL-InstallationId' => $installationId,
];
$headers['RSPAL-Signature'] = $this->getInstallationSignature($headers, $installationId);
return $headers;
}
/**
* Generate the installation signature.
*/
private function getInstallationSignature(array $format, string $id): string
{
return hash_hmac('sha256', json_encode($format), $id);
}
/**
* Get the plugin path relative to the WordPress root directory.
*/
private function getPluginRelativePath(): string
{
$pluginFullPath = wp_normalize_path(realpath($this->env->getString('plugin.path')));
$wpRoot = wp_normalize_path(realpath(ABSPATH));
return str_replace($wpRoot, '', $pluginFullPath);
}
}