59 lines
2.0 KiB
PHP
59 lines
2.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
/**
|
|
* Chooses which channel delivers an OTP.
|
|
*
|
|
* OTP_PROVIDER=nabeh -> Nabeh gateway
|
|
* OTP_PROVIDER=whatsapp -> legacy self-hosted WhatsApp bots
|
|
*
|
|
* Callers pass the code, not a rendered message: Nabeh composes its own message
|
|
* around the code, while the WhatsApp bots need finished text. Keeping that
|
|
* difference here means call sites do not care which channel is active.
|
|
*
|
|
* With OTP_FALLBACK_ENABLED on, a failure is retried on the other channel
|
|
* before the login is refused - losing OTP delivery locks out every user.
|
|
*/
|
|
class OtpSender
|
|
{
|
|
public static function sendOtp(string $phone, string $otp): array
|
|
{
|
|
$provider = strtolower(trim((string)env('OTP_PROVIDER', 'whatsapp')));
|
|
$fallback = strtolower((string)env('OTP_FALLBACK_ENABLED', 'true')) === 'true';
|
|
|
|
$result = self::dispatch($provider, $phone, $otp);
|
|
$result['provider'] = $provider;
|
|
|
|
if ($result['success'] || !$fallback) {
|
|
return $result;
|
|
}
|
|
|
|
$alternate = $provider === 'nabeh' ? 'whatsapp' : 'nabeh';
|
|
error_log(sprintf(
|
|
'[OTP] primary channel "%s" failed, falling back to "%s" | error=%s',
|
|
$provider,
|
|
$alternate,
|
|
$result['error'] ?? substr((string)($result['raw_response'] ?? ''), 0, 200)
|
|
));
|
|
|
|
$fallbackResult = self::dispatch($alternate, $phone, $otp);
|
|
$fallbackResult['provider'] = $alternate;
|
|
$fallbackResult['primary_error'] = $result['error'] ?? null;
|
|
|
|
return $fallbackResult;
|
|
}
|
|
|
|
private static function dispatch(string $channel, string $phone, string $otp): array
|
|
{
|
|
if ($channel === 'nabeh') {
|
|
return (new NabehOtpService())->sendOtp($phone, $otp);
|
|
}
|
|
|
|
$appName = (string)env('NABEH_APP_NAME', 'مُصادَق');
|
|
$message = "رمز التحقق لتطبيق {$appName}:\n*{$otp}*\n\nصالح لمدة 5 دقائق.";
|
|
|
|
return (new WhatsAppProxyService())->sendMessage($phone, $message);
|
|
}
|
|
}
|