tekkenking / swissecho
A multigateway laravel SMS notification channel package
Requires
- php: ^8.1
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^11.5.50|^12.5.22
- dev-master
- 0.7.3
- 0.7.2
- 0.7.1
- 0.7.0
- 0.6.3-beta
- 0.6.2-alpha
- 0.6.1-alpha
- 0.5.1
- 0.5.0
- 0.4.0
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.4
- 0.2.3
- 0.2.2
- 0.2.1
- 0.2.0
- 0.1.14
- 0.1.13
- 0.1.12
- 0.1.11
- 0.1.10
- 0.1.9
- 0.1.8
- 0.1.7
- 0.1.6
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.0.1
- dev-copilot/fix-swissecho-channel-registration
- dev-copilot/fix-composer-dependency-conflict
- dev-copilot/update-php-laravel-support
- dev-copilot/add-custom-sms-gateway-docs
- dev-dev
- dev-wirepick
This package is auto-updated.
Last update: 2026-04-19 00:32:47 UTC
README
What is Swissecho?
Swissecho is a Laravel package that provides a unified, fluent API for sending messages across multiple channels and multiple gateway providers. Instead of writing separate integration code for each provider, you configure them all in one place and switch between them with a single method call.
Supported Channels (Routes)
| Channel | Description | Supported Gateways |
|---|---|---|
| SMS | Traditional text messages | Termii, RouteMobile, SmsBroadcast (AU), TNZ (NZ), NigerianBulkSMS, Montnets, Wirepick |
| Voice | Voice OTP / voice calls | Termii, Textng.xyz |
| WhatsApp messaging | KudiSMS | |
| Slack | Slack notifications | Built-in Slack route |
Key Features
- ๐ Multi-gateway โ Switch SMS providers per-request or per-country
- ๐ Geo-routing (Places) โ Automatically route messages to the correct gateway based on the recipient's country
- ๐งช Mock mode โ In development, messages are logged to file or sent to email instead of hitting live APIs
- ๐ Laravel Notification integration โ Use it as a standard Laravel notification channel
- โก Direct sending โ Send messages without creating a Notification class
- ๐ฃ Events โ An
AfterSendevent is dispatched after every message, giving you full insight into requests and responses - ๐ช Webhooks โ Built-in webhook handling for provider callbacks (e.g., delivery reports)
Requirements
| Dependency | Supported Versions |
|---|---|
| PHP | ^8.1 |
| Laravel | 11.x, 12.x, 13.x |
Installation
composer require tekkenking/swissecho
The package auto-discovers itself via Laravel's package auto-discovery โ no manual registration needed.
Configuration
Environment Variables
Add these to your .env file. Only configure the gateways you plan to use:
# โโ Core Settings โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ SWISSECHO_ENABLED=false # Set to true for live/production sending SWISSECHO_SENDER=MyApp # Default sender name SWISSECHO_FAKE=log # Mock mode: "log" (writes to file) or "mail" (sends email) SWISSECHO_FAKE_MAIL=admin@example.com # Email for mock mode when SWISSECHO_FAKE=mail SWISSECHO_ROUTE=sms # Default route/channel: sms, voice, whatsapp, slack # โโ Termii (SMS & Voice) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ TERMII_API_KEY=your_api_key TERMII_SENDER_ID=YourSender TERMII_URL=https://api.ng.termii.com/api/sms/send # โโ RouteMobile โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ ROUTEMOBILE_USERNAME=your_username ROUTEMOBILE_PASSWORD=your_password ROUTEMOBILE_SENDER_ID=YourSender ROUTEMOBILE_URL=https://api.routemobile.com/... # โโ SmsBroadcast (Australia) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ SMSBRC_DOTCOM_DOT_AU_USERNAME=your_username SMSBRC_DOTCOM_DOT_AU_PASSWORD=your_password SMSBRC_DOTCOM_DOT_AU_URL=https://api.smsbroadcast.com.au/... # โโ TNZ (New Zealand) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ TNZ_API_KEY=your_api_key TNZ_URL=https://api.tnz.co.nz/... # โโ NigerianBulkSMS โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ NIGERIANBULKSMS_USERNAME=your_username NIGERIANBULKSM_PASSWORD=your_password NIGERIANBULKSMS_URL=https://portal.nigeriabulksms.com/api/ # โโ Montnets โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ MONTNETS_SMS_URL=your_url MONTNETS_SMS_USERNAME=your_username MONTNETS_SMS_PASSWORD=your_password # โโ Wirepick โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ WIREPICK_SMS_URL=your_url WIREPICK_SMS_CLIENT=your_client WIREPICK_SMS_PASSWORD=your_password WIREPICK_SMS_AFFLIATE=your_affliate # โโ Textng.xyz (Voice) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ TEXTNGXYZ_API_KEY=your_api_key # โโ KudiSMS (WhatsApp) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ KUDISMS_API_KEY=your_api_key KUDISMS_URL=your_url
The Config File
You can publish and customize the full config at config/swissecho.php. The most important sections are:
| Key | Purpose |
|---|---|
live |
true = send real messages; false = mock mode |
sender |
Default sender ID/name |
fake |
Mock strategy: "log" or "mail" |
route |
Default channel: sms, voice, whatsapp, or slack |
routes_options |
Per-channel gateway definitions and geo-routing rules |
Geo-Routing with places
Each route (SMS, voice, WhatsApp) has a places map that automatically picks the right gateway based on the recipient's country:
'sms' => [ 'gateway_options' => [ /* ... */ ], 'places' => [ 'nga' => [ // Nigeria 'gateway' => 'nigerianbulksms', 'phonecode' => '234' ], 'gha' => [ // Ghana 'gateway' => 'wirepick', 'phonecode' => '233' ], 'aus' => [ // Australia 'gateway' => 'smsbroadcast', 'phonecode' => '61' ], 'nzl' => [ // New Zealand 'gateway' => 'tnz', 'phonecode' => '64' ], ] ],
The phone code is automatically prepended to phone numbers (stripping leading 0 or +).
๐ Adding a Custom SMS Gateway
Swissecho is designed to be easily extensible. If you need a gateway that isn't built in, you can wire up your own in three steps โ no need to touch the package source at all.
Step 1: Create Your Gateway Class
Create a folder anywhere in your project (e.g., app/Sms/Gateways/MyProvider/) and add a class inside it. The class must:
- Extend
Tekkenking\Swissecho\Routes\Sms\Gateways\BaseGateway - Implement two methods:
init()andsend()
<?php namespace App\Sms\Gateways\MyProvider; use Tekkenking\Swissecho\Routes\Sms\Gateways\BaseGateway; class MyProvider extends BaseGateway { /** * Build and return the request payload that will be passed to send(). * * Available properties (populated by BaseGateway from the message builder): * $this->to โ array of recipient phone numbers * $this->sender โ sender ID / name * $this->body โ the SMS message text * $this->config โ your gateway's config block from config/swissecho.php * * @return mixed Any value you like โ it will be passed directly to send(). * Typically a URL string, an array, or a Guzzle request object. */ public function init(): mixed { // Example: build a query-string URL $url = $this->config['url']; $url .= '?api_key=' . $this->config['auth']['api_key']; $url .= '&from=' . $this->sender; $url .= '&to=' . implode(',', $this->to); $url .= '&message=' . urlencode($this->body); return $url; } /** * Receive the value returned by init(), make the HTTP call, * and return a configured cURL handle. * * @param mixed $data Whatever init() returned. * @return \CurlHandle|bool A ready-to-execute cURL handle. * Swissecho calls curl_exec() on it automatically. */ public function send($data): \CurlHandle|bool { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $data); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // โ ๏ธ Only disable in local/testing environments; keep true in production return $ch; } }
How it works under the hood: after
send()returns the cURL handle, Swissecho'sSwissechoGatewayTrait::execCurl()callscurl_exec(), collects the response, formats it, and fires theAfterSendevent.
Step 2: Register the Gateway in the Config
Open config/swissecho.php and add your gateway to the routes_options.sms.gateway_options array:
use App\Sms\Gateways\MyProvider\MyProvider; // โ import your class 'routes_options' => [ 'sms' => [ 'gateway_options' => [ // ... existing gateways ... 'myprovider' => [ // โ the key becomes the gateway "name" 'class' => MyProvider::class, 'url' => env('MYPROVIDER_URL'), 'auth' => [ 'api_key' => env('MYPROVIDER_API_KEY'), ], // add any other keys your gateway needs โ they will all be // available inside your class as $this->config['...'] ], ], ], ],
Add the corresponding values to your .env:
MYPROVIDER_URL=https://api.myprovider.com/send MYPROVIDER_API_KEY=your_api_key_here
Step 3: Use Your Gateway
That's it โ your gateway is now a first-class citizen in Swissecho. Use it exactly like any built-in gateway:
// Direct / fluent sending swissecho()->gateway('myprovider')->quick('2348012345678', 'Hello!'); // Or inside a route callback swissecho()->route('sms', function ($ms) { return $ms->to('2348012345678') ->content('Your OTP is 9988') ->gateway('myprovider'); })->go(); // Or inside a Laravel Notification public function toSms($notifiable): SwissechoMessage { return (new SwissechoMessage()) ->line('Your order has shipped!') ->gateway('myprovider'); }
You can also map it to a country in places for automatic geo-routing:
'places' => [ 'zaf' => [ // South Africa, for example 'gateway' => 'myprovider', 'phonecode' => '27', ], ],
BaseGateway Quick Reference
| Member | Type | Description |
|---|---|---|
$this->to |
array |
Recipient phone numbers |
$this->sender |
string |
Sender ID / name |
$this->body |
string |
The message text |
$this->config |
array |
Your gateway's full config block from swissecho.php |
init(): mixed |
abstract method | Build the request payload; return value is passed to send() |
| `send($data): \CurlHandle | bool` | abstract method |
Usage
Swissecho can be used in two ways: directly (without a Notification class), or through Laravel's notification system.
Access Methods
You have three ways to get a Swissecho instance:
// 1. Global helper function swissecho() // 2. Laravel Facade Swissecho:: // 3. From the container app('swissecho')
A) Direct Sending (Without Notification Classes)
Quick Send โ One Liner
The simplest way to send a message. Uses the default route and default gateway from config:
swissecho()->quick('2348012345678', 'Your OTP code is 1234');
Quick Send with a Specific Gateway
swissecho()->gateway('vonage')->quick('2348012345678', 'Your OTP code is 1234');
Fluent Builder โ Full Control
swissecho()->route('sms', function ($ms) { return $ms->to('2348012345678, 2348098765432') // comma-separated recipients ->content('Your order has been shipped!') ->line('Track it at https://example.com/track'); // appends a new line // ->gateway('routemobile') // override gateway inside callback // ->sender('MyBrand') // override sender inside callback }) ->to('2348011111111') // optional: additional/fallback recipient ->sender('MyApp') // optional: override sender ->gateway('termii') // optional: override gateway ->go(); // ๐ sends the message
Property-Based Sending
You can also set properties directly on the Swissecho instance:
$sw = swissecho(); $sw->gateway('termii'); $sw->to = '2348012345678'; $sw->sender = 'MyApp'; $sw->message = 'The world is a beautiful place created by GOD'; $sw->go();
Sending via WhatsApp
swissecho()->route('whatsapp', function ($ms) { return $ms->to('2348012345678') ->content('Hello from WhatsApp!'); })->go();
Sending via Slack
swissecho()->message('Hello team!') ->to('CHANNEL_ID') ->route('slack') ->go();
Sending via Voice Call
swissecho()->route('voice', function ($ms) { return $ms->to('2348012345678') ->content('Your OTP is 5 6 7 8'); })->gateway('termii')->go();
B) Laravel Notification Channel Integration
Swissecho integrates with Laravel's built-in notification system. Create a notification class and define a toSms (or toVoice, toWhatsapp, toSlack) method:
Step 1: Create the Notification
<?php namespace App\Notifications; use Illuminate\Notifications\Notification; use Tekkenking\Swissecho\SwissechoMessage; class OrderShipped extends Notification { /** * The delivery channels. */ public function via($notifiable): array { return ['swissecho']; } /** * (Optional) Tell Swissecho which routes to use. * If omitted, defaults to ['sms', 'slack', 'whatsapp']. */ public function swissechoRoutes($notifiable): array { return ['sms']; } /** * Build the SMS message. * Method name follows the pattern: to{Route} โ toSms, toVoice, toWhatsapp, toSlack */ public function toSms($notifiable): SwissechoMessage { return (new SwissechoMessage()) ->line('Hi ' . $notifiable->name . '!') ->line('Your order has been shipped.') ->sender('MyStore'); } }
Step 2: Make Your User Model "Notifiable"
Swissecho pulls the phone number from the notifiable model. Implement one of these:
class User extends Authenticatable { use Notifiable; /** * Option A: Have a `phone` attribute on the model (e.g. column in DB). * Swissecho checks $notifiable->phone automatically. */ /** * Option B: Define this method for custom logic. */ public function routeNotificationPhone(): string { return $this->mobile_number; } /** * (Optional) Tell Swissecho the recipient's country for geo-routing. * Return a 3-letter ISO code matching a key in config places. */ public function routeNotificationPlace(): string { return 'nga'; // Nigeria } }
Step 3: Send the Notification
$user->notify(new OrderShipped());
SwissechoMessage API Reference
The SwissechoMessage class is the message builder used in callbacks and notification methods:
| Method | Description | Example |
|---|---|---|
->line($text) |
Appends a line of text to the message body. Multiple calls add new lines. | ->line('Hello') |
->content($text) |
Alias for line(). |
->content('Hello') |
->to($recipient) |
Sets the recipient(s). Accepts a string or comma-separated list. | ->to('234801..., 234809...') |
->sender($name) |
Sets the sender ID (max 10 characters). | ->sender('MyApp') |
->from($name) |
Alias for sender(). |
->from('MyApp') |
->gateway($name) |
Overrides the gateway for this message. | ->gateway('termii') |
->place($code) |
Sets the country/place code (e.g., 'nga'). Overrides auto-detection. | ->place('nga') |
->phonecode($code) |
Manually sets the phone country code (e.g., '234'). | ->phonecode('234') |
->identifier($id) |
Attaches an identifier (e.g., user ID) to the message for tracking. | ->identifier($user->id) |
->route($name) |
Sets the route/channel on the message itself. | ->route('sms') |
Mock Mode (Development & Testing)
When SWISSECHO_ENABLED=false (the default), no real API calls are made. Instead, messages are captured by the mock system.
Mock via Log (Default)
Messages are written to storage/logs/swissecho_mock.log:
SWISSECHO_FAKE=log
The log includes: sender, recipient, message body, route, gateway, gateway class, country, and phone code.
Mock via Email
Messages are emailed to the configured address:
SWISSECHO_FAKE=mail SWISSECHO_FAKE_MAIL=developer@example.com
Events
After every message send (including mock sends), Swissecho dispatches the AfterSend event:
Tekkenking\Swissecho\Events\AfterSend
Event Properties
| Property | Type | Description |
|---|---|---|
$insightPayload |
array |
Contains request (the payload sent to the gateway) and response (raw gateway response) |
$formattedResponse |
array |
Structured response with status, partner_response, from, to, body, route, gateway, identifier, timestamp |
$identifier |
mixed |
The identifier attached to the message (e.g., user ID) |
Listening to the Event
// app/Providers/EventServiceProvider.php protected $listen = [ \Tekkenking\Swissecho\Events\AfterSend::class => [ \App\Listeners\LogSmsDelivery::class, ], ];
<?php // app/Listeners/LogSmsDelivery.php namespace App\Listeners; use Tekkenking\Swissecho\Events\AfterSend; class LogSmsDelivery { public function handle(AfterSend $event): void { // $event->formattedResponse['status'] โ true/false // $event->formattedResponse['to'] โ recipient array // $event->formattedResponse['gateway'] โ which gateway was used // $event->insightPayload['request'] โ raw request payload // $event->insightPayload['response'] โ raw response from provider logger()->info('SMS sent', $event->formattedResponse); } }
Webhooks
Swissecho includes a built-in webhook handler for receiving delivery reports or callbacks from gateway providers. The webhook system validates a secret key and routes the request to the appropriate gateway handler.
Configure webhooks per gateway in config/swissecho.php:
'{$gateway}' => [ // ... 'webhook' => [ 'secret' => env('TERMI_WEBHOOK_SECRET', 'your-secret-here'), 'handle' => 'webhook' // method name on the gateway class ] ],
Helper Functions
Swissecho provides global helper functions for phone number manipulation:
| Function | Description |
|---|---|
swissecho() |
Returns the Swissecho singleton instance |
addCountryCodeToPhoneNumber($phone, $code) |
Prepends a country code (e.g., '234') to a phone number, stripping leading 0 or + |
removeCountryCodeFromPhoneNumber($phone, $code) |
Strips a country code prefix from a phone number |
convertPhoneNumberToArray($phone) |
Splits a comma-separated phone string into an array |
Examples
addCountryCodeToPhoneNumber('08012345678', '234'); // Returns: "2348012345678" removeCountryCodeFromPhoneNumber('2348012345678', '234'); // Returns: "8012345678" convertPhoneNumberToArray('2348012345678, 2348098765432'); // Returns: ["2348012345678", "2348098765432"]
License
This package is open-sourced software licensed under the MIT license.