laravel-textify

Laravel Textify πŸ“±

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads License

A powerful and enterprise-ready SMS package for Laravel applications supporting 8+ SMS providers including Bangladeshi and international gateways. Built with modern PHP 8.3+ and Laravel 10+ support, featuring queue integration, automatic fallback system, comprehensive activity tracking, and an intuitive fluent API.

Perfect for Laravel developers who need reliable SMS functionality with multiple provider support and enterprise-grade features.

✨ Features

οΏ½ Table of Contents

οΏ½πŸ“‘ Supported SMS Providers

πŸ‡§πŸ‡© Bangladeshi Providers

Provider Features Status Methods
DhorolaSMS GET API, Status tracking, SSL support βœ… Ready send(), getBalance()
BulkSMSBD GET/POST API, Bulk sending, Plain text response βœ… Ready send(), getBalance()
MimSMS Transactional/Promotional, Campaign support βœ… Ready send(), getBalance()
eSMS Enterprise API, Bearer token auth, Cost tracking βœ… Ready send()
REVE SMS Premium gateway, Balance check, Multi-endpoint βœ… Ready send(), getBalance()
Alpha SMS Dual format support, Balance check, SSL/Non-SSL βœ… Ready send(), getBalance()

🌍 International Providers

Provider Features Status Installation Methods
Twilio Global leader, Advanced features, Webhooks βœ… Ready composer require twilio/sdk send(), Advanced APIs
Nexmo (Vonage) International coverage, Client tracking βœ… Ready composer require vonage/client send(), Analytics

πŸ› οΈ Development & Testing Providers

Provider Purpose Features
Log Provider Development testing Logs SMS to Laravel logs
Array Provider Unit testing Stores SMS in memory array

Note: International providers require additional SDK installation for full functionality. Development providers are included for testing purposes.

πŸ“¦ Installation

Install the package via Composer:

composer require devwizardhq/laravel-textify

πŸ”§ Laravel Auto-Discovery

Laravel will automatically register the service provider and facade. No additional configuration required!

πŸ“„ Publish Configuration

Publish the configuration file:

php artisan vendor:publish --provider="DevWizard\Textify\TextifyServiceProvider" --tag="textify-config"

πŸ—„οΈ Optional: Database Activity Tracking

If you want to track SMS activities in your database:

php artisan textify:table
php artisan migrate

πŸ“‹ Requirements

Configuration

Environment Variables

Add these to your .env file based on the providers you want to use:

# Primary Provider Selection
TEXTIFY_PROVIDER=mimsms
TEXTIFY_FALLBACK_PROVIDER=revesms

# ===== BANGLADESHI PROVIDERS =====

# DhorolaSMS Configuration
DHOROLA_API_KEY=your_api_key
DHOROLA_SENDER_ID=your_sender_id
DHOROLA_BASE_URI=https://api.dhorolasms.net
DHOROLA_TIMEOUT=30
DHOROLA_VERIFY_SSL=true

# BulkSMSBD Configuration
BULKSMSBD_API_KEY=your_api_key
BULKSMSBD_SENDER_ID=your_sender_id
BULKSMSBD_BASE_URI=http://bulksmsbd.net
BULKSMSBD_TIMEOUT=30
BULKSMSBD_VERIFY_SSL=false

# MimSMS Configuration
MIMSMS_USERNAME=your_username
MIMSMS_APIKEY=your_api_key
MIMSMS_SENDER_ID=your_sender_id
MIMSMS_TRANSACTION_TYPE=T
MIMSMS_CAMPAIGN_ID=your_campaign_id
MIMSMS_BASE_URI=https://api.mimsms.com
MIMSMS_TIMEOUT=30
MIMSMS_VERIFY_SSL=true

# eSMS Configuration
ESMS_API_TOKEN=your_api_token
ESMS_SENDER_ID=your_sender_id
ESMS_BASE_URI=https://login.esms.com.bd
ESMS_TIMEOUT=30
ESMS_VERIFY_SSL=true

# REVE SMS Configuration
REVESMS_APIKEY=your_api_key
REVESMS_SECRETKEY=your_secret_key
REVESMS_CLIENT_ID=your_client_id
REVESMS_SENDER_ID=your_sender_id
REVESMS_BASE_URI=https://smpp.revesms.com:7790
REVESMS_BALANCE_URI=https://smpp.revesms.com
REVESMS_TIMEOUT=30
REVESMS_VERIFY_SSL=true

# Alpha SMS Configuration
ALPHASMS_API_KEY=your_api_key
ALPHASMS_SENDER_ID=your_sender_id
ALPHASMS_BASE_URI=https://api.sms.net.bd
ALPHASMS_TIMEOUT=30
ALPHASMS_VERIFY_SSL=true

# ===== INTERNATIONAL PROVIDERS =====

# Twilio Configuration
TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_FROM=your_phone_number

# Nexmo (Vonage) Configuration
NEXMO_API_KEY=your_api_key
NEXMO_API_SECRET=your_api_secret
NEXMO_FROM=your_sender_id
NEXMO_CLIENT_REF=your_reference
NEXMO_TIMEOUT=30
NEXMO_VERIFY_SSL=true

# ===== PACKAGE CONFIGURATION =====

# Activity Tracking
TEXTIFY_ACTIVITY_TRACKING_ENABLED=true
TEXTIFY_ACTIVITY_DRIVER=database

# Logging Configuration
TEXTIFY_LOGGING_ENABLED=true
TEXTIFY_LOG_SUCCESSFUL=true
TEXTIFY_LOG_FAILED=true
TEXTIFY_LOG_CHANNEL=stack

# Queue Configuration
TEXTIFY_QUEUE_ENABLED=true
TEXTIFY_QUEUE_CONNECTION=redis
TEXTIFY_QUEUE_NAME=sms

πŸš€ Quick Start

Basic SMS Sending

use DevWizard\Textify\Facades\Textify;

// Send a simple SMS
Textify::send('01712345678', 'Hello, this is a test message!');

// Send using specific driver
Textify::via('revesms')->send('01712345678', 'Hello from REVE SMS!');

Fluent API

// Chain methods for more control
Textify::to('01712345678')
    ->message('Your OTP is: 123456')
    ->via('mimsms')
    ->send();

// Send to multiple recipients
Textify::to(['01712345678', '01887654321'])
    ->message('Bulk SMS message')
    ->send();

Laravel Notifications

// Create and send SMS notifications
$user->notify(new OrderShippedNotification('ORD-123'));

// Or send to any phone number
Notification::route('textify', '01712345678')
    ->notify(new WelcomeNotification());

πŸ”” Laravel Notifications

Laravel Textify provides seamless integration with Laravel’s notification system, allowing you to send SMS notifications just like email or database notifications.

Quick Setup

  1. Add textify to your notification channels:
public function via($notifiable): array
{
    return ['textify']; // or ['mail', 'textify'] for multiple channels
}
  1. Implement the toTextify() method:
public function toTextify($notifiable): TextifyMessage
{
    return TextifyMessage::create('Your order has been shipped!');
}
  1. Configure phone number resolution in your model:
public function routeNotificationForTextify($notification): ?string
{
    return $this->phone_number;
}

Complete Notification Example

<?php

namespace App\Notifications;

use DevWizard\Textify\Notifications\TextifyMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

class OrderShippedNotification extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public readonly string $orderNumber,
        public readonly string $trackingCode
    ) {}

    /**
     * Get the notification's delivery channels.
     */
    public function via($notifiable): array
    {
        return ['textify'];
    }

    /**
     * Get the SMS representation of the notification.
     */
    public function toTextify($notifiable): TextifyMessage
    {
        return TextifyMessage::create(
            message: "🚚 Order #{$this->orderNumber} shipped! Track: {$this->trackingCode}",
            from: 'MyStore',
            driver: 'revesms'
        );
    }

    /**
     * Get the array representation for logging/database
     */
    public function toArray($notifiable): array
    {
        return [
            'order_number' => $this->orderNumber,
            'tracking_code' => $this->trackingCode,
        ];
    }
}

Phone Number Resolution Methods

The notification channel looks for phone numbers in this priority order:

class User extends Authenticatable
{
    use Notifiable;

    // Method 1: Route method (highest priority)
    // Gets notification context, most flexible
    public function routeNotificationForTextify($notification): ?string
    {
        // You can use notification context to determine logic
        if ($notification instanceof UrgentNotification) {
            return $this->emergency_phone ?? $this->phone_number;
        }

        return $this->phone_number;
    }

    // Method 2: Dynamic phone number method
    // Good for custom business logic
    public function getTextifyPhoneNumber(): ?string
    {
        return $this->preferred_sms_number ?? $this->phone_number ?? $this->mobile;
    }

    // Method 3: Automatic attribute detection (lowest priority)
    // Channel automatically checks these attributes:
    // phone_number, phone, mobile, phn, mobile_number, cell
}

Message Format Options

The toTextify() method accepts multiple formats:

// 1. TextifyMessage object (recommended for full control)
public function toTextify($notifiable): TextifyMessage
{
    return TextifyMessage::create(
        message: 'Your order has shipped!',
        from: 'MyStore',
        driver: 'revesms',
        metadata: ['order_id' => $this->orderId]
    );
}

// 2. Simple string (quick and easy)
public function toTextify($notifiable): string
{
    return "Order #{$this->orderNumber} has shipped!";
}

// 3. Array format (flexible structure)
public function toTextify($notifiable): array
{
    return [
        'message' => 'Your order has shipped!',
        'from' => 'MyStore',
        'driver' => 'revesms',
    ];
}

Sending Notifications

use App\Notifications\OrderShippedNotification;
use Illuminate\Support\Facades\Notification;

// Send to a single user
$user->notify(new OrderShippedNotification('ORD-123', 'TRK-456'));

// Send to multiple users
$users = User::where('wants_sms', true)->get();
Notification::send($users, new OrderShippedNotification('ORD-123', 'TRK-456'));

// Send to any phone number without a model
Notification::route('textify', '01712345678')
    ->notify(new WelcomeNotification());

// Queue the notification for background processing
$user->notify(
    (new OrderShippedNotification('ORD-123', 'TRK-456'))
        ->delay(now()->addMinutes(5))
);

Advanced Features

Provider Selection

public function toTextify($notifiable): TextifyMessage
{
    // Use different providers based on user preferences
    $provider = $notifiable->preferred_sms_provider ?? 'revesms';

    return TextifyMessage::create(
        message: 'Your notification',
        driver: $provider
    );
}

Conditional Sending

public function via($notifiable): array
{
    $channels = ['database']; // Always log to database

    // Add SMS for users who opted in
    if ($notifiable->sms_notifications_enabled) {
        $channels[] = 'textify';
    }

    return $channels;
}

Event Integration

use DevWizard\Textify\Events\TextifySent;
use DevWizard\Textify\Events\TextifyFailed;

Event::listen(TextifySent::class, function (TextifySent $event) {
    // Log successful SMS
    Log::info('SMS notification sent', [
        'recipient' => $event->message->getTo(),
        'message_id' => $event->response->getMessageId(),
    ]);
});

Event::listen(TextifyFailed::class, function (TextifyFailed $event) {
    // Handle SMS failures
    Log::error('SMS notification failed', [
        'recipient' => $event->message->getTo(),
        'error' => $event->response->getErrorMessage(),
    ]);
});

πŸ“‹ Provider-Specific Usage

πŸ‡§πŸ‡© Bangladeshi Providers

DhorolaSMS

// Basic usage
Textify::via('dhorola')
    ->to('01712345678')
    ->message('Hello from DhorolaSMS!')
    ->send();

// Check balance
$balance = Textify::via('dhorola')->getBalance();

BulkSMSBD

// Send SMS with custom sender ID
Textify::via('bulksmsbd')
    ->to('01712345678')
    ->from('CustomID')
    ->message('Hello from BulkSMSBD!')
    ->send();

// Check balance
$balance = Textify::via('bulksmsbd')->getBalance();

MimSMS

// Transactional SMS
Textify::via('mimsms')
    ->to('01712345678')
    ->message('Your OTP: 123456')
    ->send();

// The transaction type is configured in .env (T=Transactional, P=Promotional)

eSMS

// Enterprise SMS with cost tracking
$response = Textify::via('esms')
    ->to('01712345678')
    ->message('Enterprise message')
    ->send();

// Access cost information
$cost = $response->getCost();

REVE SMS

// Premium SMS service
Textify::via('revesms')
    ->to('01712345678')
    ->message('Premium SMS via REVE')
    ->send();

// Check account balance
$balance = Textify::via('revesms')->getBalance();
echo "Balance: $balance";

Alpha SMS

// Alpha SMS with SSL support
Textify::via('alphasms')
    ->to('01712345678')
    ->message('Hello from Alpha SMS!')
    ->send();

// Check balance
$balance = Textify::via('alphasms')->getBalance();

🌍 International Providers

Twilio

// Requires: composer require twilio/sdk
Textify::via('twilio')
    ->to('+1234567890')
    ->message('Hello from Twilio!')
    ->send();

Nexmo (Vonage)

// Requires: composer require vonage/client
Textify::via('nexmo')
    ->to('+1234567890')
    ->message('Hello from Vonage!')
    ->send();

πŸ› οΈ Development & Testing

Log Provider (Development)

// Perfect for development - logs to Laravel logs
Textify::via('log')
    ->to('01712345678')
    ->message('This will be logged')
    ->send();

Array Provider (Testing)

// Perfect for unit tests - stores in memory
Textify::via('array')
    ->to('01712345678')
    ->message('This will be stored in array')
    ->send();

// Access sent messages in tests
use DevWizard\Textify\Providers\ArrayProvider;
$messages = ArrayProvider::getMessages();

Unified Send Method

The package provides a powerful unified send method that accepts various input formats:

// Array format with different messages
Textify::send([
    ['to' => '01712345678', 'message' => 'Hello John!'],
    ['to' => '01887654321', 'message' => 'Hello Jane!'],
]);

// Same message to multiple numbers
Textify::send(['01712345678', '01887654321'], 'Same message for all');

// Single SMS
Textify::send('01712345678', 'Single SMS message');

Queue Support

// Send SMS in background
Textify::to('01712345678')
    ->message('Queued message')
    ->queue();

// Queue to specific queue
Textify::to('01712345678')
    ->message('Priority message')
    ->queue('high-priority');

Event Handling

use DevWizard\Textify\Events\TextifySent;
use DevWizard\Textify\Events\TextifyFailed;
use DevWizard\Textify\Events\TextifyJobFailed;
use Illuminate\Support\Facades\Event;

// Listen for SMS events
Event::listen(TextifySent::class, function (TextifySent $event) {
    logger('SMS sent successfully', [
        'to' => $event->message->getTo(),
        'provider' => $event->provider,
    ]);
});

Event::listen(TextifyFailed::class, function (TextifyFailed $event) {
    logger('SMS failed', [
        'to' => $event->message->getTo(),
        'error' => $event->exception?->getMessage() ?? $event->response->getErrorMessage(),
    ]);
});

// Listen for queued job failures
Event::listen(TextifyJobFailed::class, function (TextifyJobFailed $event) {
    logger('SMS job failed', [
        'to' => $event->getRecipient(),
        'provider' => $event->getProvider(),
        'error' => $event->getErrorMessage(),
        'metadata' => $event->getMetadata(),
    ]);

    // You could implement retry logic, alerting, etc.
});

Balance Checking

Many Bangladeshi providers support balance checking:

// REVE SMS - Balance check
$balance = Textify::via('revesms')->getBalance();
echo "Balance: $balance";

// DhorolaSMS - Balance check
$balance = Textify::via('dhorola')->getBalance();

// BulkSMSBD - Simple balance check
$balance = Textify::via('bulksmsbd')->getBalance();

// Alpha SMS - Balance verification
$balance = Textify::via('alphasms')->getBalance();

// MimSMS - Account balance
$balance = Textify::via('mimsms')->getBalance();

πŸ“š API Reference

Core Methods

send(string|array $to, string $message = null): TextifyResponse

Send SMS to one or multiple recipients.

// Single recipient
Textify::send('01712345678', 'Hello World!');

// Multiple recipients with same message
Textify::send(['01712345678', '01887654321'], 'Same message');

// Multiple recipients with different messages
Textify::send([
    ['to' => '01712345678', 'message' => 'Hello John!'],
    ['to' => '01887654321', 'message' => 'Hello Jane!'],
]);

via(string $driver): self

Select specific SMS provider.

Textify::via('revesms')->send('01712345678', 'Hello!');

driver(string $driver): self

Alias for via() method (Laravel Manager pattern compatibility).

Textify::driver('revesms')->send('01712345678', 'Hello!');

to(string|array $recipients): self

Set recipient(s) using fluent API.

Textify::to('01712345678')->message('Hello!')->send();
Textify::to(['01712345678', '01887654321'])->message('Bulk SMS')->send();

message(string $message): self

Set SMS message using fluent API.

Textify::to('01712345678')->message('Your OTP: 123456')->send();

from(string $senderId): self

Set custom sender ID (if supported by provider).

Textify::via('bulksmsbd')
    ->to('01712345678')
    ->from('MyApp')
    ->message('Hello!')
    ->send();

Provider-Specific Methods

getBalance(): float

Check account balance (supported providers: revesms, dhorola, bulksmsbd, alphasms, mimsms).

$balance = Textify::via('revesms')->getBalance();
echo "Current balance: $balance";

Queue Methods

queue(?string $queueName = null): mixed

Send SMS via queue system.

// Send immediately via queue
Textify::to('01712345678')->message('Queued SMS')->queue();

// Send to specific queue
Textify::to('01712345678')
    ->message('Priority SMS')
    ->queue('high-priority');

Response Object

The TextifyResponse object provides access to sending results:

$response = Textify::send('01712345678', 'Hello!');

// Check if SMS was sent successfully
if ($response->isSuccessful()) {
    echo "SMS sent! Message ID: " . $response->getMessageId();
} else {
    echo "Failed: " . $response->getErrorMessage();
}

// Available methods
$response->isSuccessful();        // bool - Check if SMS was sent successfully
$response->isFailed();            // bool - Check if SMS failed
$response->getMessageId();        // string|null - Get provider message ID
$response->getStatus();           // string - Get status message
$response->getCost();             // float|null - Get SMS cost (if supported)
$response->getErrorCode();        // string|null - Get error code
$response->getErrorMessage();     // string|null - Get error message
$response->getRawResponse();      // array - Get raw provider response

Management Methods

via(string $driver): self / driver(string $driver): self

Select SMS provider (both methods are identical).

// Using via()
Textify::via('revesms')->send('01712345678', 'Hello!');

// Using driver() (alias)
Textify::driver('revesms')->send('01712345678', 'Hello!');

fallback(string $driver): self

Set fallback provider for current operation.

Textify::via('revesms')
    ->fallback('dhorola')
    ->send('01712345678', 'Message with fallback');

getProviders(): array

Get list of all registered providers.

$providers = Textify::getProviders();
// Returns: ['dhorola', 'bulksmsbd', 'mimsms', 'esms', 'revesms', 'alphasms', ...]

hasProvider(string $name): bool

Check if a provider is registered.

if (Textify::hasProvider('revesms')) {
    // Provider is available
}

getProvider(string $name): TextifyProviderInterface

Get provider instance directly.

$provider = Textify::getProvider('revesms');
$balance = $provider->getBalance();

reset(): self

Clear prepared data from fluent interface.

Textify::to('01712345678')->message('Test')->reset(); // Clears prepared data

Configuration Methods

Available Providers

πŸ”§ Advanced Usage

Laravel Notifications Integration

πŸ“– For comprehensive notification documentation, see the Laravel Notifications section above.

Laravel Textify provides seamless integration with Laravel’s notification system through the textify channel.

Setting Up Notification Channel

The textify notification channel is automatically registered when you install the package. No additional configuration is required.

Creating SMS Notifications

Create a notification class that uses the textify channel:

<?php

namespace App\Notifications;

use DevWizard\Textify\Notifications\TextifyMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

class OrderShippedNotification extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        public readonly string $orderNumber,
        public readonly string $trackingCode
    ) {}

    /**
     * Get the notification's delivery channels.
     */
    public function via($notifiable): array
    {
        return ['textify']; // Add 'textify' to send SMS
    }

    /**
     * Get the SMS representation of the notification.
     */
    public function toTextify($notifiable): TextifyMessage
    {
        return TextifyMessage::create(
            message: "Your order #{$this->orderNumber} has been shipped! Tracking: {$this->trackingCode}",
            from: 'MyStore',
            driver: 'revesms' // Optional: specify SMS provider
        );
    }
}

Configuring Notifiable Models

Add SMS routing to your User model (or any notifiable model). You have multiple options:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;

    // Option 1: Route method (most specific, gets notification context)
    public function routeNotificationForTextify($notification): ?string
    {
        // You can use notification context to determine the phone number
        return $this->phone_number;
    }

    // Option 2: Dynamic getTextifyPhoneNumber method (custom logic)
    public function getTextifyPhoneNumber(): ?string
    {
        // Custom logic to determine phone number
        // Example: Use different numbers based on user preferences
        return $this->preferred_sms_number ?? $this->phone_number ?? $this->mobile;
    }

    // Option 3: Automatic attribute detection
    // If neither method above is defined, the channel will automatically
    // look for these attributes in this order:
    // phone_number, phone, phn, mobile, cell, mobile_number
}

Priority Order:

  1. routeNotificationForTextify($notification) - highest priority
  2. textifyNumber() - medium priority
  3. Attribute detection - lowest priority

Advanced textifyNumber() Examples:

class User extends Authenticatable
{
    use Notifiable;

    // Example 1: Use different numbers based on notification type
    public function textifyNumber(): ?string
    {
        // Use work phone during business hours, personal phone otherwise
        $now = now();
        if ($now->isWeekday() && $now->hour >= 9 && $now->hour <= 17) {
            return $this->work_phone ?? $this->phone_number;
        }
        return $this->personal_phone ?? $this->phone_number;
    }

    // Example 2: Format phone number dynamically
    public function textifyNumber(): ?string
    {
        $phone = $this->phone_number;

        // Ensure Bangladeshi format
        if ($phone && !str_starts_with($phone, '880')) {
            $phone = '880' . ltrim($phone, '0+');
        }

        return $phone;
    }

    // Example 3: Use notification preferences
    public function textifyNumber(): ?string
    {
        // Check if user has SMS notifications enabled
        if (!$this->sms_notifications_enabled) {
            return null; // Will prevent SMS from being sent
        }

        return $this->preferred_contact_number ?? $this->phone_number;
    }
}

#### Sending Notifications

Send SMS notifications like any other Laravel notification:

```php
use App\Notifications\OrderShippedNotification;

// Send to a single user
$user->notify(new OrderShippedNotification('ORD-123', 'TRK-456'));

// Send to multiple users
Notification::send($users, new OrderShippedNotification('ORD-123', 'TRK-456'));

// Queue the notification for background processing
$user->notify((new OrderShippedNotification('ORD-123', 'TRK-456'))->delay(now()->addMinutes(5)));

Notification Message Formats

The toTextify() method supports multiple return formats:

// 1. TextifyMessage object (recommended)
public function toTextify($notifiable): TextifyMessage
{
    return TextifyMessage::create(
        message: 'Your order has shipped!',
        from: 'MyStore',
        driver: 'revesms'
    );
}

// 2. Simple string
public function toTextify($notifiable): string
{
    return 'Your order has shipped!';
}

// 3. Array format
public function toTextify($notifiable): array
{
    return [
        'message' => 'Your order has shipped!',
        'from' => 'MyStore',
        'driver' => 'revesms',
    ];
}

On-Demand Notifications

Send SMS to any phone number without a model:

use Illuminate\Support\Facades\Notification;

Notification::route('textify', '01712345678')
    ->notify(new OrderShippedNotification('ORD-123', 'TRK-456'));

Event Integration

SMS notifications integrate with Laravel’s event system and Textify’s own events:

use DevWizard\Textify\Events\TextifySent;
use DevWizard\Textify\Events\TextifyFailed;

Event::listen(TextifySent::class, function (TextifySent $event) {
    logger('Notification SMS sent', [
        'recipient' => $event->message->getTo(),
        'provider' => $event->provider,
    ]);
});

Custom Providers

Create your own SMS provider by extending the base provider:

use DevWizard\Textify\Providers\BaseProvider;
use DevWizard\Textify\DTOs\TextifyMessage;
use DevWizard\Textify\DTOs\TextifyResponse;

class CustomSmsProvider extends BaseProvider
{
    protected array $supportedCountries = ['BD', 'US']; // Supported country codes

    public function getName(): string
    {
        return 'custom';
    }

    protected function getRequiredConfigKeys(): array
    {
        return ['api_key', 'sender_id'];
    }

    protected function validateConfig(): void
    {
        $this->ensureConfigKeys();
    }

    protected function getClientConfig(): array
    {
        return [
            'base_uri' => 'https://api.customsms.com',
            'timeout' => 30,
        ];
    }

    protected function sendRequest(TextifyMessage $message): array
    {
        $response = $this->client->post('/send', [
            'json' => [
                'api_key' => $this->config['api_key'],
                'to' => $message->getTo(),
                'from' => $message->getFrom() ?: $this->config['sender_id'],
                'message' => $message->getMessage(),
            ],
        ]);

        return json_decode($response->getBody()->getContents(), true);
    }

    protected function parseResponse(array $response): TextifyResponse
    {
        if ($response['status'] === 'success') {
            return TextifyResponse::success(
                messageId: $response['message_id'],
                status: 'sent',
                cost: $response['cost'] ?? null
            );
        }

        return TextifyResponse::failed(
            errorCode: $response['error_code'],
            errorMessage: $response['error_message']
        );
    }
}

Then register it in your config/textify.php:

'providers' => [
    'custom' => [
        'driver' => 'custom',
        'class' => App\Services\CustomSmsProvider::class,
        'api_key' => env('CUSTOM_SMS_API_KEY'),
        'sender_id' => env('CUSTOM_SMS_SENDER_ID'),
    ],
],

Activity Tracking & Analytics

Track SMS activities with detailed logging:

use DevWizard\Textify\Models\TextifyActivity;

// Activities are automatically tracked when enabled in config
$activities = TextifyActivity::latest()->get();

foreach ($activities as $activity) {
    echo "SMS to {$activity->to}: {$activity->status} at {$activity->created_at}";
}

// Filter by status
$failedSms = TextifyActivity::where('status', 'failed')->get();
$successfulSms = TextifyActivity::where('status', 'sent')->get();

// Filter by provider
$reveSms = TextifyActivity::where('provider', 'revesms')->get();

// Filter by date range
$todaySms = TextifyActivity::whereDate('created_at', today())->get();

Event-Driven Architecture

Listen to SMS lifecycle events:

use DevWizard\Textify\Events\{TextifySending, TextifySent, TextifyFailed, TextifyJobFailed};

// In your EventServiceProvider
protected $listen = [
    TextifySending::class => [
        SendingSmsListener::class,
    ],
    TextifySent::class => [
        SmsSuccessListener::class,
    ],
    TextifyFailed::class => [
        SmsFailureListener::class,
    ],
    TextifyJobFailed::class => [
        QueueJobFailureListener::class,
    ],
];

// Example listeners
class SmsSuccessListener
{
    public function handle(TextifySent $event)
    {
        // Log successful SMS
        logger('SMS sent successfully', [
            'to' => $event->message->getTo(),
            'provider' => $event->provider,
            'message_id' => $event->response->getMessageId(),
            'cost' => $event->response->getCost(),
        ]);

        // Update user notification status
        // Send webhook to external service
        // Update analytics dashboard
    }
}

class QueueJobFailureListener
{
    public function handle(TextifyJobFailed $event)
    {
        // Log job failure with detailed metadata
        logger('SMS queue job failed', $event->getMetadata());

        // Implement retry logic
        if ($this->shouldRetry($event)) {
            // Retry with different provider or after delay
            dispatch(new SendTextifyJob($event->getMessage(), 'fallback-provider'))
                ->delay(now()->addMinutes(5));
        }

        // Send alert to administrators
        // Update monitoring dashboard
    }
}
}

Fallback System

Configure fallback drivers in your config file for maximum reliability:

// config/textify.php
'fallback' => env('TEXTIFY_FALLBACK_PROVIDER', 'revesms'),

// Or in your .env file
TEXTIFY_FALLBACK_PROVIDER=revesms

// Multiple fallbacks can be configured by modifying the config file:
'providers' => [
    // Your primary providers...
],

// Custom fallback logic in your application
$primaryProviders = ['mimsms', 'revesms', 'alphasms'];
$fallbackProviders = ['dhorola', 'bulksmsbd', 'esms'];

foreach ($primaryProviders as $provider) {
    try {
        $response = Textify::via($provider)->send($phone, $message);
        if ($response->isSuccessful()) {
            break;
        }
    } catch (Exception $e) {
        // Try next provider
        continue;
    }
}

When the primary driver fails, the system will automatically try the fallback drivers in order.

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.