Files

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');
}
}
}