63 lines
2.6 KiB
PHP
63 lines
2.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
interface OtpProvider
|
|
{
|
|
public function send(string $phoneE164, string $code): void;
|
|
}
|
|
|
|
/** Minimal HTTP JSON adapter. Map request fields to the selected vendor contract only after confirmation. */
|
|
final class ConfiguredHttpOtpProvider implements OtpProvider
|
|
{
|
|
public function send(string $phoneE164, string $code): void
|
|
{
|
|
AppConfig::loadEnvironment();
|
|
if (getenv('OTP_ENABLED') !== 'true') {
|
|
throw new RuntimeException('OTP provider is disabled');
|
|
}
|
|
if (AppConfig::required('OTP_PROVIDER') !== 'generic_json') {
|
|
throw new RuntimeException('Configure a supported OTP provider adapter before enabling delivery');
|
|
}
|
|
$url = AppConfig::required('OTP_API_URL');
|
|
$apiKey = AppConfig::required('OTP_API_KEY');
|
|
if (!filter_var($url, FILTER_VALIDATE_URL) || parse_url($url, PHP_URL_SCHEME) !== 'https') {
|
|
throw new RuntimeException('OTP endpoint must use HTTPS');
|
|
}
|
|
if (!function_exists('curl_init')) {
|
|
throw new RuntimeException('The cURL extension is required for OTP delivery');
|
|
}
|
|
|
|
$sender = getenv('OTP_SENDER_ID') ?: '';
|
|
$message = getenv('OTP_MESSAGE_TEMPLATE') ?: 'رمز التحقق الخاص بك هو {code}';
|
|
if (strpos($message, '{code}') === false) {
|
|
throw new RuntimeException('OTP message template must include {code}');
|
|
}
|
|
$payload = json_encode([
|
|
'to' => $phoneE164,
|
|
'sender' => $sender,
|
|
'message' => str_replace('{code}', $code, $message),
|
|
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
if ($payload === false) {
|
|
throw new RuntimeException('Unable to encode OTP request');
|
|
}
|
|
$handle = curl_init($url);
|
|
curl_setopt_array($handle, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => $payload,
|
|
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . $apiKey],
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_CONNECTTIMEOUT => 5,
|
|
CURLOPT_TIMEOUT => 12,
|
|
CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
|
|
]);
|
|
$response = curl_exec($handle);
|
|
$status = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
|
|
$error = curl_error($handle);
|
|
curl_close($handle);
|
|
if ($response === false || $status < 200 || $status >= 300) {
|
|
error_log('OTP delivery failed; HTTP ' . $status . '; transport error: ' . $error);
|
|
throw new RuntimeException('OTP provider rejected the request');
|
|
}
|
|
}
|
|
}
|