File Manager

Path: /home/liffinac1/domains/bank.stellerpay.ca/public_html/app/Services/

Viewing File: SmsService.php

<?php

namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use App\Models\User;

class SmsService
{
    protected $settings;

    public function __construct()
    {
        $this->settings = DB::table('sms_settings')->first();
    }

    /**
     * Send SMS to a user's phone number
     */
    public function sendSms($userId, $message, $eventType = 'general')
    {
        if (!$this->settings || $this->settings->sms_enabled != 'yes') {
            return ['success' => false, 'message' => 'SMS service not enabled'];
        }

        // Get user phone
        $user = User::find($userId);
        if (!$user || empty($user->phone)) {
            return ['success' => false, 'message' => 'User phone number not found'];
        }

        // Check if event is enabled
        if (!$this->isEventEnabled($eventType)) {
            return ['success' => false, 'message' => 'SMS not enabled for this event type'];
        }

        // Check rate limiting
        if ($this->settings->sms_rate_limiting_enabled && !$this->checkRateLimit($userId)) {
            return ['success' => false, 'message' => 'Rate limit exceeded'];
        }

        // Try to send SMS
        $result = $this->sendViaPrimaryProvider($user->phone, $message);

        // If primary fails, try fallback 1
        if (!$result['success'] && !empty($this->settings->sms_fallback_provider_1)) {
            $result = $this->sendViaProvider($this->settings->sms_fallback_provider_1, $user->phone, $message);
        }

        // If fallback 1 fails, try fallback 2
        if (!$result['success'] && !empty($this->settings->sms_fallback_provider_2)) {
            $result = $this->sendViaProvider($this->settings->sms_fallback_provider_2, $user->phone, $message);
        }

        // Log SMS attempt
        $this->logSms($userId, $user->phone, $message, $eventType, $result);

        return $result;
    }

    /**
     * Send SMS to a specific phone number (without user lookup)
     */
    public function sendToPhone($phoneNumber, $message, $eventType = 'general')
    {
        if (!$this->settings || $this->settings->sms_enabled != 'yes') {
            return ['success' => false, 'message' => 'SMS service not enabled'];
        }

        $result = $this->sendViaPrimaryProvider($phoneNumber, $message);

        if (!$result['success'] && !empty($this->settings->sms_fallback_provider_1)) {
            $result = $this->sendViaProvider($this->settings->sms_fallback_provider_1, $phoneNumber, $message);
        }

        if (!$result['success'] && !empty($this->settings->sms_fallback_provider_2)) {
            $result = $this->sendViaProvider($this->settings->sms_fallback_provider_2, $phoneNumber, $message);
        }

        DB::table('sms_logs')->insert([
            'phone_number' => $phoneNumber,
            'message' => $message,
            'provider' => $this->settings->sms_primary_provider,
            'event_type' => $eventType,
            'status' => $result['success'] ? 'success' : 'failed',
            'response' => json_encode($result),
            'estimated_cost' => $result['cost'] ?? 0,
            'message_id' => $result['message_id'] ?? null,
            'sent_at' => $result['success'] ? now() : null,
            'created_at' => now(),
            'updated_at' => now(),
        ]);

        return $result;
    }

    protected function sendViaPrimaryProvider($phone, $message)
    {
        return $this->sendViaProvider($this->settings->sms_primary_provider, $phone, $message);
    }

    protected function sendViaProvider($provider, $phone, $message)
    {
        try {
            switch ($provider) {
                case 'twilio':
                    return $this->sendViaTwilio($phone, $message);
                case 'msg91':
                    return $this->sendViaMSG91($phone, $message);
                case 'plivo':
                    return $this->sendViaPlivo($phone, $message);
                case 'vonage':
                    return $this->sendViaVonage($phone, $message);
                case 'africastalking':
                    return $this->sendViaAfricasTalking($phone, $message);
                case 'termii':
                    return $this->sendViaTermii($phone, $message);
                default:
                    return ['success' => false, 'error' => 'Invalid provider'];
            }
        } catch (\Exception $e) {
            Log::error("SMS Error ({$provider}): " . $e->getMessage());
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    protected function sendViaTwilio($phone, $message)
    {
        $url = "https://api.twilio.com/2010-04-01/Accounts/{$this->settings->sms_twilio_sid}/Messages.json";
        
        $response = Http::asForm()
            ->withBasicAuth($this->settings->sms_twilio_sid, $this->settings->sms_twilio_token)
            ->post($url, [
                'From' => $this->settings->sms_twilio_from,
                'To' => $phone,
                'Body' => $message,
            ]);
        
        if ($response->successful()) {
            $data = $response->json();
            return [
                'success' => true,
                'message_id' => $data['sid'] ?? null,
                'cost' => isset($data['price']) ? abs((float)$data['price']) : 0.01,
            ];
        }
        
        return ['success' => false, 'error' => $response->body()];
    }

    protected function sendViaMSG91($phone, $message)
    {
        $url = 'https://api.msg91.com/api/v5/flow/';
        
        $response = Http::withHeaders([
            'authkey' => $this->settings->sms_msg91_authkey,
            'Content-Type' => 'application/json',
        ])->post($url, [
            'sender' => $this->settings->sms_msg91_sender_id,
            'route' => $this->settings->sms_msg91_route ?? '4',
            'country' => '91',
            'sms' => [[
                'message' => $message,
                'to' => [$phone]
            ]]
        ]);
        
        if ($response->successful()) {
            $data = $response->json();
            return [
                'success' => true,
                'message_id' => $data['request_id'] ?? null,
                'cost' => 0.01,
            ];
        }
        
        return ['success' => false, 'error' => $response->body()];
    }

    protected function sendViaPlivo($phone, $message)
    {
        $url = "https://api.plivo.com/v1/Account/{$this->settings->sms_plivo_auth_id}/Message/";
        
        $response = Http::withBasicAuth($this->settings->sms_plivo_auth_id, $this->settings->sms_plivo_auth_token)
            ->asJson()
            ->post($url, [
                'src' => $this->settings->sms_plivo_from,
                'dst' => $phone,
                'text' => $message,
            ]);
        
        if ($response->successful()) {
            $data = $response->json();
            return [
                'success' => true,
                'message_id' => $data['message_uuid'][0] ?? null,
                'cost' => 0.01,
            ];
        }
        
        return ['success' => false, 'error' => $response->body()];
    }

    protected function sendViaVonage($phone, $message)
    {
        $url = 'https://rest.nexmo.com/sms/json';
        
        $response = Http::post($url, [
            'api_key' => $this->settings->sms_vonage_key,
            'api_secret' => $this->settings->sms_vonage_secret,
            'from' => $this->settings->sms_vonage_from,
            'to' => $phone,
            'text' => $message,
        ]);
        
        if ($response->successful()) {
            $data = $response->json();
            if (isset($data['messages'][0]['status']) && $data['messages'][0]['status'] == '0') {
                return [
                    'success' => true,
                    'message_id' => $data['messages'][0]['message-id'] ?? null,
                    'cost' => 0.01,
                ];
            }
            return ['success' => false, 'error' => $data['messages'][0]['error-text'] ?? 'Unknown error'];
        }
        
        return ['success' => false, 'error' => $response->body()];
    }

    protected function sendViaAfricasTalking($phone, $message)
    {
        $url = 'https://api.africastalking.com/version1/messaging';
        
        $response = Http::withHeaders([
            'apiKey' => $this->settings->sms_africastalking_api_key,
            'Content-Type' => 'application/x-www-form-urlencoded',
            'Accept' => 'application/json',
        ])->asForm()->post($url, [
            'username' => $this->settings->sms_africastalking_username,
            'to' => $phone,
            'message' => $message,
            'from' => $this->settings->sms_africastalking_from,
        ]);
        
        if ($response->successful()) {
            $data = $response->json();
            if (isset($data['SMSMessageData']['Recipients'][0]['status']) && 
                $data['SMSMessageData']['Recipients'][0]['status'] === 'Success') {
                $cost = $data['SMSMessageData']['Recipients'][0]['cost'] ?? '0';
                return [
                    'success' => true,
                    'message_id' => $data['SMSMessageData']['Recipients'][0]['messageId'] ?? null,
                    'cost' => (float)preg_replace('/[^0-9.]/', '', $cost),
                ];
            }
            return ['success' => false, 'error' => $data['SMSMessageData']['Message'] ?? 'Unknown error'];
        }
        
        return ['success' => false, 'error' => $response->body()];
    }

    protected function sendViaTermii($phone, $message)
    {
        $url = 'https://api.ng.termii.com/api/sms/send';
        
        $response = Http::asJson()->post($url, [
            'api_key' => $this->settings->sms_termii_api_key,
            'to' => $phone,
            'from' => $this->settings->sms_termii_sender_id,
            'sms' => $message,
            'type' => 'plain',
            'channel' => $this->settings->sms_termii_channel ?? 'generic',
        ]);
        
        if ($response->successful()) {
            $data = $response->json();
            if (isset($data['message_id'])) {
                return [
                    'success' => true,
                    'message_id' => $data['message_id'],
                    'cost' => 0.01,
                ];
            }
            return ['success' => false, 'error' => $data['message'] ?? 'Unknown error'];
        }
        
        return ['success' => false, 'error' => $response->body()];
    }

    protected function isEventEnabled($eventType)
    {
        $eventMap = [
            'registration' => 'sms_on_registration',
            'deposit' => 'sms_on_deposit',
            'withdrawal' => 'sms_on_withdrawal',
            'transfer' => 'sms_on_transfer',
            'login' => 'sms_on_login',
            'password_change' => 'sms_on_password_change',
        ];

        if (!isset($eventMap[$eventType])) {
            return true; // Allow general/test messages
        }

        return (bool) $this->settings->{$eventMap[$eventType]};
    }

    protected function checkRateLimit($userId)
    {
        $window = $this->settings->sms_rate_limit_window ?? 60; // minutes
        $limit = $this->settings->sms_rate_limit_per_user ?? 10;

        $count = DB::table('sms_logs')
            ->where('user_id', $userId)
            ->where('created_at', '>=', now()->subMinutes($window))
            ->count();

        return $count < $limit;
    }

    protected function logSms($userId, $phone, $message, $eventType, $result)
    {
        DB::table('sms_logs')->insert([
            'user_id' => $userId,
            'phone_number' => $phone,
            'message' => $message,
            'provider' => $this->settings->sms_primary_provider,
            'event_type' => $eventType,
            'status' => $result['success'] ? 'success' : 'failed',
            'response' => json_encode($result),
            'estimated_cost' => $result['cost'] ?? 0,
            'message_id' => $result['message_id'] ?? null,
            'sent_at' => $result['success'] ? now() : null,
            'created_at' => now(),
            'updated_at' => now(),
        ]);
    }
}