142 lines
5.4 KiB
PHP
142 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Core\Request;
|
|
use App\Core\Response;
|
|
use App\Core\Flows\ConversationFlowEngine;
|
|
use App\Services\TTSService;
|
|
use App\Models\WhatsAppSession;
|
|
use App\Models\CompanySubscription;
|
|
use App\Models\CompanySubscriptionUsage;
|
|
|
|
class OTPController extends BaseController
|
|
{
|
|
/**
|
|
* Send OTP verification code via WhatsApp (Text or Voice Note)
|
|
* POST /api/otp/send
|
|
*/
|
|
public function send(Request $request, Response $response): void
|
|
{
|
|
$companyId = $request->company_id;
|
|
$body = $request->getBody();
|
|
|
|
$phone = $body['phone'] ?? '';
|
|
$type = $body['type'] ?? 'text'; // 'text' or 'voice'
|
|
$sessionId = $body['session_id'] ?? null;
|
|
$customCode = $body['code'] ?? null;
|
|
|
|
if (empty($phone)) {
|
|
$response->status(400)->json(['error' => 'Missing required parameter: phone']);
|
|
return;
|
|
}
|
|
|
|
// Clean phone number (remove non-digits except +)
|
|
$phone = preg_replace('/[^\d+]/', '', $phone);
|
|
|
|
// 1. Resolve WhatsApp Session
|
|
$session = null;
|
|
if ($sessionId) {
|
|
$session = WhatsAppSession::findSecure((int)$sessionId);
|
|
if (!$session || (int)$session['company_id'] !== (int)$companyId) {
|
|
$response->status(404)->json(['error' => 'WhatsApp session not found']);
|
|
return;
|
|
}
|
|
} else {
|
|
// Grab the first connected session of the company
|
|
$sessions = WhatsAppSession::findAllByCompany($companyId);
|
|
foreach ($sessions as $s) {
|
|
if ($s['status'] === 'connected') {
|
|
$session = $s;
|
|
break;
|
|
}
|
|
}
|
|
if (!$session && !empty($sessions)) {
|
|
$session = $sessions[0]; // fallback to first session if none is connected
|
|
}
|
|
}
|
|
|
|
if (!$session) {
|
|
$response->status(400)->json(['error' => 'No active WhatsApp sessions configured for this company.']);
|
|
return;
|
|
}
|
|
|
|
if ($session['status'] !== 'connected') {
|
|
$response->status(400)->json(['error' => 'WhatsApp session is not connected. Connect the session first.']);
|
|
return;
|
|
}
|
|
|
|
// 2. Check SaaS subscription quotas
|
|
if ($companyId !== 1) {
|
|
$activeSub = CompanySubscription::findActiveByCompany($companyId);
|
|
if (!$activeSub) {
|
|
$response->status(402)->json(['error' => 'Active subscription plan required.']);
|
|
return;
|
|
}
|
|
|
|
if (!CompanySubscriptionUsage::hasRemainingLimit($companyId, 'request')) {
|
|
$response->status(403)->json(['error' => 'Monthly request quota exceeded. Please upgrade your plan.']);
|
|
return;
|
|
}
|
|
|
|
if ($type === 'voice') {
|
|
if (!CompanySubscriptionUsage::hasRemainingLimit($companyId, 'voice')) {
|
|
$response->status(403)->json(['error' => 'Voice request quota exceeded. Please upgrade your plan.']);
|
|
return;
|
|
}
|
|
|
|
// Starter plan doesn't support Voice Notes
|
|
$features = json_decode($activeSub['features'] ?: '{}', true);
|
|
if (isset($features['voice']) && !$features['voice']) {
|
|
$response->status(403)->json(['error' => 'Voice OTP is not supported in your current subscription plan.']);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Generate verification code
|
|
$code = $customCode ? trim($customCode) : (string)rand(1000, 9999);
|
|
|
|
// 4. Send Message
|
|
try {
|
|
if ($type === 'voice') {
|
|
// Spacing the digits to force slow Arabic pronunciation: e.g. "1 2 3 4"
|
|
$spacedCode = implode(' ', str_split($code));
|
|
$textToRead = "رمز التحقق الخاص بك هو: {$spacedCode}. أكرر، رمز التحقق هو: {$spacedCode}.";
|
|
|
|
$audioBase64 = TTSService::textToSpeechArabic($textToRead);
|
|
if (!$audioBase64) {
|
|
$response->status(500)->json(['error' => 'Failed to generate voice OTP audio.']);
|
|
return;
|
|
}
|
|
|
|
// Send voice note
|
|
ConversationFlowEngine::sendReply($session, $phone, '', null, $audioBase64, 'audio/mp3');
|
|
} else {
|
|
// Send text
|
|
$textMsg = "رمز التحقق الخاص بك لمتجر نابه هو: *{$code}* \n الرجاء عدم مشاركته مع أي شخص.";
|
|
ConversationFlowEngine::sendReply($session, $phone, $textMsg);
|
|
}
|
|
|
|
// Increment usage stats
|
|
if ($companyId !== 1) {
|
|
CompanySubscriptionUsage::incrementUsage($companyId, 'request');
|
|
if ($type === 'voice') {
|
|
CompanySubscriptionUsage::incrementUsage($companyId, 'voice');
|
|
}
|
|
}
|
|
|
|
$response->json([
|
|
'status' => 'success',
|
|
'message' => 'OTP sent successfully',
|
|
'code' => $code,
|
|
'type' => $type
|
|
]);
|
|
|
|
} catch (\Exception $e) {
|
|
error_log("[OTP Controller Error] " . $e->getMessage());
|
|
$response->status(500)->json(['error' => 'Failed to send OTP message: ' . $e->getMessage()]);
|
|
}
|
|
}
|
|
}
|