@@ -13,20 +13,23 @@ A Laravel package for seamless integration with the OTPIQ SMS service API. Send
1313## ✨ Features
1414
1515- ** Multi-Channel Messaging** : Send messages via SMS, WhatsApp, or Telegram.
16- - ** Verification Codes** : Easily send OTPs and verification codes.
16+ - ** Verification Codes** : Send OTP verification codes easily .
1717- ** Custom Messages** : Send personalized messages with approved sender IDs.
18- - ** Delivery Tracking** : Track the status of sent messages in real-time .
18+ - ** Delivery Tracking** : Track the status of sent messages with detailed channel flow .
1919- ** Credit Management** : Monitor your remaining credits and usage.
2020- ** Sender ID Management** : Retrieve and manage your approved sender IDs.
21- - ** Laravel 10+ Support** : Fully compatible with Laravel 10 and above.
22- - ** PHP 8.1+ Support** : Built for modern PHP applications.
21+ - ** Webhooks Support** : Real-time notifications for message status updates.
22+ - ** Error Handling** : Comprehensive exception handling.
23+ - ** Laravel 10-12 Support** : Fully compatible with Laravel 10, 11, and 12.
24+ - ** PHP 8.1-8.4 Support** : Built for modern PHP applications.
2325
2426---
2527
2628## 🛠️ Requirements
2729
28- - PHP 8.1 or higher
29- - Laravel 10 or higher
30+ - PHP 8.1, 8.2, 8.3, or 8.4
31+ - Laravel 10, 11, or 12
32+ - Guzzle HTTP 7.0+
3033
3134---
3235
@@ -72,32 +75,35 @@ return [
7275use Rstacode\Otpiq\Facades\Otpiq;
7376
7477$response = Otpiq::sendSms([
75- 'phoneNumber' => '9647501234567 ',
78+ 'phoneNumber' => '964750123456 ',
7679 'smsType' => 'verification',
7780 'verificationCode' => '123456',
78- 'provider' => 'auto ' // Optional (default: auto )
81+ 'provider' => 'whatsapp-telegram-sms ' // Optional (recommended )
7982]);
8083
8184// Response:
8285// [
8386// 'message' => 'SMS task created successfully',
84- // 'smsId' => 'sms-1234567890',
85- // 'remainingCredit' => 920,
86- // 'provider' => 'telegram',
87- // 'status' => 'pending'
87+ // 'smsId' => 'sms-1234567890abcdef123456',
88+ // 'remainingCredit' => 14800,
89+ // 'cost' => 200,
90+ // 'canCover' => true,
91+ // 'paymentType' => 'prepaid'
8892// ]
8993```
9094
9195### Send Custom Message
9296
9397``` php
9498$response = Otpiq::sendSms([
95- 'phoneNumber' => '9647501234567 ',
99+ 'phoneNumber' => '964750123456 ',
96100 'smsType' => 'custom',
97101 'customMessage' => 'Special offer! 20% discount today!',
98- 'senderId' => 'MyStore ',
102+ 'senderId' => 'OTPIQ ',
99103 'provider' => 'sms' // Required for custom messages
100104]);
105+
106+ // Response: Same as verification code response above
101107```
102108
103109### Track SMS Status
@@ -107,10 +113,23 @@ $status = Otpiq::trackSms('sms-1234567890');
107113
108114// Response:
109115// [
110- // 'status' => 'delivered',
111- // 'phoneNumber' => '9647501234567',
112- // 'smsId' => 'sms-1234567890',
113- // 'cost' => 80
116+ // 'smsId' => 'sms-1234567890abcdef123456',
117+ // 'phoneNumber' => '964750123456',
118+ // 'status' => 'sent',
119+ // 'cost' => 200,
120+ // 'isFinalStatus' => false,
121+ // 'lastChannel' => 'whatsapp',
122+ // 'channelFlow' => [
123+ // [
124+ // 'channel' => 'whatsapp',
125+ // 'tried' => true,
126+ // 'success' => true
127+ // ],
128+ // [
129+ // 'channel' => 'sms',
130+ // 'tried' => false
131+ // ]
132+ // ]
114133// ]
115134```
116135
@@ -119,9 +138,15 @@ $status = Otpiq::trackSms('sms-1234567890');
119138``` php
120139$projectInfo = Otpiq::getProjectInfo();
121140
141+ // Response:
142+ // [
143+ // 'projectName' => 'My SMS Project',
144+ // 'credit' => 15000
145+ // ]
146+
122147// Access project info:
123- echo $projectInfo['projectName']; // "My Project"
124- echo $projectInfo['credit']; // 1000
148+ echo $projectInfo['projectName']; // "My SMS Project"
149+ echo $projectInfo['credit']; // 15000
125150```
126151
127152### Get Sender IDs
@@ -131,12 +156,18 @@ $senderIds = Otpiq::getSenderIds();
131156
132157// Response:
133158// [
134- // 'senderIds' => [
159+ // 'success' => true,
160+ // 'data' => [
135161// [
136- // 'id ' => 'sender-123 ',
137- // 'senderId' => 'MyBrand ',
162+ // '_id ' => '507f1f77bcf86cd799439011 ',
163+ // 'senderId' => 'OTPIQ ',
138164// 'status' => 'accepted',
139- // 'createdAt' => '2024-01-01T00:00:00.000Z'
165+ // 'pricePerSms' => [
166+ // 'korekTelecom' => 80,
167+ // 'asiaCell' => 80,
168+ // 'zainIraq' => 80,
169+ // 'others' => 100
170+ // ]
140171// ]
141172// ]
142173// ]
@@ -173,40 +204,226 @@ try {
173204
174205## 🔌 Available Providers
175206
176- When sending messages, you can specify the provider :
207+ OTPIQ offers 6 provider options including smart fallback routes :
177208
178- - ` auto ` (recommended): System automatically chooses the best available provider.
179- - ` sms ` : Send via SMS.
180- - ` whatsapp ` : Send via WhatsApp.
181- - ` telegram ` : Send via Telegram.
209+ - ` whatsapp-telegram-sms ` (recommended): Try WhatsApp → Telegram → SMS (maximum delivery success)
210+ - ` whatsapp-sms ` : Try WhatsApp first, fallback to SMS
211+ - ` telegram-sms ` : Try Telegram first, fallback to SMS
212+ - ` sms ` : SMS only
213+ - ` whatsapp ` : WhatsApp only
214+ - ` telegram ` : Telegram only
182215
183- ** Note** : When ` smsType ` is ` custom ` , the provider is automatically set to ` sms ` .
216+ ** Note** : For custom messages , the provider is typically set to ` sms ` since sender IDs are mainly supported via SMS .
184217
185218---
186219
187- ## 🧪 Testing
220+ ## 🔗 Webhooks
221+
222+ OTPIQ provides real-time delivery status notifications via webhooks. When you configure webhooks, you'll receive instant updates about message delivery status directly to your server.
223+
224+ ### How to Configure Webhooks
225+
226+ Include a ` deliveryReport ` object in your SMS request:
227+
228+ ``` php
229+ $response = Otpiq::sendSms([
230+ 'phoneNumber' => '964750123456',
231+ 'smsType' => 'verification',
232+ 'verificationCode' => '123456',
233+ 'deliveryReport' => [
234+ 'webhookUrl' => 'https://your-app.com/webhooks/sms-status',
235+ 'deliveryReportType' => 'all', // 'all' or 'final'
236+ 'webhookSecret' => 'your_secret_123' // Optional
237+ ]
238+ ]);
239+ ```
240+
241+ ### Webhook Configuration Fields
242+
243+ | Field | Type | Required | Description |
244+ | -------------------- | ------ | -------- | -------------------------------------------------------- |
245+ | ` webhookUrl ` | string | Yes | HTTPS URL where status updates are sent |
246+ | ` deliveryReportType ` | string | No | ` "all" ` for all updates, ` "final" ` for final status only |
247+ | ` webhookSecret ` | string | No | Secret key for webhook authentication |
248+
249+ ### Webhook Payload Structure
250+
251+ Each webhook request contains a JSON payload with these fields:
252+
253+ ** Required Fields:**
254+
255+ - ` smsId ` : Unique message identifier
256+ - ` deliveryReportType ` : Your configured report type
257+ - ` isFinal ` : Whether this is the final status
258+ - ` channel ` : Messaging channel (sms, whatsapp, telegram)
259+ - ` status ` : Delivery status (sent, delivered, failed)
260+
261+ ** Optional Fields:**
262+
263+ - ` reason ` : Failure reason (only when status is 'failed')
264+ - ` senderId ` : Sender ID used (only for SMS with custom sender IDs)
265+
266+ ### Delivery Status Flow
267+
268+ ** SMS Messages:**
269+
270+ - ` sent ` → Message accepted by carrier
271+ - ` delivered ` → Message confirmed delivered to recipient
272+ - ` failed ` → Message could not be delivered
273+
274+ ** WhatsApp Messages:**
275+
276+ - ` sent ` → Message sent to WhatsApp servers
277+ - ` delivered ` → Message delivered to recipient's device
278+ - ` failed ` → Message could not be sent or delivered
279+
280+ ** Telegram Messages:**
281+
282+ - ` sent ` → Message sent to Telegram servers
283+ - ` delivered ` → Message delivered to recipient
284+ - ` failed ` → Message could not be sent
285+
286+ ### Webhook Examples
287+
288+ #### Example 1: SMS with Custom Sender ID
188289
189- Run the test suite:
290+ ** Request: **
190291
191- ``` bash
192- composer test
292+ ``` php
293+ $response = Otpiq::sendSms([
294+ 'phoneNumber' => '964750123456',
295+ 'smsType' => 'custom',
296+ 'customMessage' => 'Your order has been confirmed!',
297+ 'senderId' => 'OTPIQ',
298+ 'deliveryReport' => [
299+ 'webhookUrl' => 'https://your-app.com/webhooks/sms-status',
300+ 'deliveryReportType' => 'all',
301+ 'webhookSecret' => 'your_secret_123'
302+ ]
303+ ]);
304+ ```
305+
306+ ** Webhook Payloads Received:**
307+
308+ Sent Status:
309+
310+ ``` json
311+ {
312+ "smsId" : " sms_1234567890abcdef" ,
313+ "deliveryReportType" : " all" ,
314+ "isFinal" : false ,
315+ "channel" : " sms" ,
316+ "status" : " sent" ,
317+ "senderId" : " OTPIQ"
318+ }
319+ ```
320+
321+ Delivered Status:
322+
323+ ``` json
324+ {
325+ "smsId" : " sms_1234567890abcdef" ,
326+ "deliveryReportType" : " all" ,
327+ "isFinal" : true ,
328+ "channel" : " sms" ,
329+ "status" : " delivered" ,
330+ "senderId" : " OTPIQ"
331+ }
332+ ```
333+
334+ #### Example 2: WhatsApp with Final-Only Reports
335+
336+ ** Request:**
337+
338+ ``` php
339+ $response = Otpiq::sendSms([
340+ 'phoneNumber' => '964750123456',
341+ 'smsType' => 'verification',
342+ 'verificationCode' => '123456',
343+ 'provider' => 'whatsapp',
344+ 'deliveryReport' => [
345+ 'webhookUrl' => 'https://your-app.com/webhooks/whatsapp-status',
346+ 'deliveryReportType' => 'final'
347+ ]
348+ ]);
193349```
194350
195- Run specific tests:
351+ ** Webhook Payload (Final Status Only): **
196352
197- ``` bash
198- ./vendor/bin/phpunit tests/Unit/OtpiqServiceTest.php
353+ ``` json
354+ {
355+ "smsId" : " sms_1234567890abcdef" ,
356+ "deliveryReportType" : " final" ,
357+ "isFinal" : true ,
358+ "channel" : " whatsapp" ,
359+ "status" : " delivered"
360+ }
199361```
200362
201- Mock API responses in tests:
363+ #### Example 3: Failed Message
364+
365+ ** Request:**
202366
203367``` php
204- Otpiq::fake([
205- 'info' => ['projectName' => 'Test Project', 'credit' => 5000],
206- 'sms' => ['smsId' => 'test-123', 'status' => 'queued']
368+ $response = Otpiq::sendSms([
369+ 'phoneNumber' => '964750123456',
370+ 'smsType' => 'custom',
371+ 'customMessage' => 'Your order has been confirmed!',
372+ 'senderId' => 'OTPIQ',
373+ 'deliveryReport' => [
374+ 'webhookUrl' => 'https://your-app.com/webhooks/sms-status',
375+ 'deliveryReportType' => 'final'
376+ ]
207377]);
208378```
209379
380+ ** Webhook Payload (Failure):**
381+
382+ ``` json
383+ {
384+ "smsId" : " sms_abcdef1234567890" ,
385+ "deliveryReportType" : " final" ,
386+ "isFinal" : true ,
387+ "channel" : " sms" ,
388+ "status" : " failed" ,
389+ "reason" : " Carrier rejected the message" ,
390+ "senderId" : " OTPIQ"
391+ }
392+ ```
393+
394+ ### Laravel Event Integration
395+
396+ For handling webhook events in Laravel:
397+
398+ ``` php
399+ // In routes/web.php
400+ Route::post('/webhooks/sms-status', function (Request $request) {
401+ $payload = $request->all();
402+
403+ // Verify webhook signature (for security)
404+ // $signature = $request->header('X-OTPIQ-Signature');
405+
406+ switch ($payload['status']) {
407+ case 'delivered':
408+ // Handle delivered event
409+ Log::info("SMS {$payload['smsId']} delivered via {$payload['channel']}");
410+ break;
411+
412+ case 'failed':
413+ // Handle failed event
414+ Log::error("SMS {$payload['smsId']} failed: {$payload['reason']}");
415+ break;
416+
417+ case 'sent':
418+ // Handle sent event
419+ Log::info("SMS {$payload['smsId']} sent via {$payload['channel']}");
420+ break;
421+ }
422+
423+ return response()->json(['status' => 'ok']);
424+ });
425+ ```
426+
210427---
211428
212429## 📜 License
0 commit comments