djouamaah/laravel-algeria-sms
A Laravel package for integrating Algerian SMS gateways into your applications, offering a simple way to send text messages and add SMS notifications to your PHP projects.
This is a v1.0.0 first release, so treat the README/config file as the primary documentation — there is no legacy surface area to unlearn.
Install and scaffold:
composer require djouamaah/laravel-algeria-sms
php artisan vendor:publish --tag=algeria-sms-config
php artisan migrate
The migration creates sms_opt_outs (and whatever OTP/log tables the package ships) — run it before anything else or optOut() and OTP verification will fail at runtime rather than at boot.
Look first at config/algeria-sms.php. That is where the default driver, named providers, OTP policy (TTL, max attempts, resend cooldown, hourly cap), quiet-hours window, and locale live.
In local/testing set the driver to log and send your first message:
AlgeriaSms::send('0555 12 34 56', 'Votre commande est arrivée.');
Confirm normalization works by passing the number in several formats (+213555123456, 00213555123456, 0555 12 34 56) — the package accepts all Algerian formats and derives the operator from the prefix (05 Ooredoo, 06 Mobilis, 07 Djezzy).
First real use case: OTP on registration.
AlgeriaSms::otp()->send($phone, 'register');
AlgeriaSms::otp()->verify($phone, $code, 'register');
The context argument ('register', 'login', 'reset') namespaces codes so a login code cannot be replayed against a password reset.
Only after the log driver works end-to-end should you configure a real transport. Local Algerian providers do not publish API contracts, so you either fill in the generic http driver from your provider's contract docs, or register your own driver with AlgeriaSms::extend().
Transport selection. Use log in local/testing, the generic http driver in production against whichever REST provider you have a contract with, and AlgeriaSms::extend() when a provider needs signing, XML, or multi-step auth that the generic driver can't express. Register named providers in config and switch per-environment rather than hardcoding a driver at call sites — it keeps staging on log while production goes out over HTTP.
Custom driver skeleton. Wrap provider specifics in a class implementing the package's driver contract, resolve credentials from config/services.php, map your app's message object to the provider payload, and translate the provider response into the package's success/failure result so MessageSent/MessageFailed fire correctly. Throw (don't silently return) so failed jobs retry.
OTP flows. Always pass a context. Catch the package's throttle exceptions and surface them as validation errors ("please wait N seconds") rather than 500s. Verification should be the only thing that consumes an attempt — don't call verify() speculatively in a loop. Because codes are stored as HMAC, you cannot inspect or recover them; if you need audit trails, log issuance (phone hash + context + timestamp), never the code.
Notifications. Register the algeria-sms channel and implement toAlgeriaSms() on the notification. This is the cleanest path for anything user-facing (order shipped, appointment reminder), because it composes with Notifiable routing and lets you set the phone per-notification.
Validation. Use the AlgerianMobile rule at the edge (form requests) so you never persist a number the sender can't normalize. Normalize once on write and store the canonical form; the operator prefix is then free routing metadata.
Marketing / bulk. Use AlgeriaSms::marketing($phones, SmsMessage::make(...)->gsm()). This path is the only one that automatically queues, batches, skips opted-out numbers, and respects quiet hours (21:00–08:00 Africa/Algiers). It requires a running queue worker. For very large lists, chunk at the call site too, so a single job payload stays small and one bad chunk doesn't poison the batch.
Opt-outs. Wire optOut() to your inbound webhook / customer-support tooling and call it as soon as a recipient asks. isOptedOut() is cheap — check it explicitly for transactional sends if your compliance policy treats opt-out as global rather than marketing-only. Never delete rows from sms_opt_outs; use optIn() so you keep the history.
Events for observability. Listen to MessageSent and MessageFailed to write to your own SMS log table, increment metrics, and alert on failure rate. MessageFailed is where you detect a provider outage before customers do.
Encoding-aware composition. Call ->gsm() on French copy to strip accented characters and keep messages in GSM-7 (160 chars/segment). Use the segment counter before sending to sanity-check cost on Arabic copy.
Testing. AlgeriaSms::fake() plus the usual Queue::fake() / Notification::fake(). Assert on AlgeriaSms::assertSent(...), and separately test the OTP throttle paths with time travel (travel()) rather than sleeping.
http driver is a blank slate. The package deliberately does not ship a provider contract. You must read your provider's docs and map payload, auth headers, and success/failure response shapes yourself. Budget real time for this; a wrong success check means silent non-delivery.APP_KEY rotation invalidates all outstanding OTPs. They are HMAC'd with the app key. Plan key rotation for a low-traffic window, or accept that in-flight verifications fail.array or per-node file store will let users bypass the hourly cap and resend cooldown.optOut(). If you store the raw user-typed string in sms_opt_outs, a later send with a differently formatted number won't match the opt-out and you'll text someone who asked you to stop. Normalize at the boundary, and be aware the opt-out check is keyed on the normalized value.queue:work is running in every environment where marketing is enabled, and monitor failed jobs.MessageFailed as a total failure; consume the events to reconcile.AlgeriaSms::extend() is your seam for anything the generic driver can't do (request signing, token refresh, delivery reports polling). Register it in a service provider, not inline, so config:cache doesn't break resolution.config:cache in deploy scripts, and re-run php artisan migrate on deploy — a cached config referencing missing keys produces confusing "driver not configured" errors at send time rather than boot time.How can I help you explore Laravel packages today?