Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lara Termii Laravel Package

zeevx/lara-termii

LaraTermii integrates the Termii messaging API into Laravel, making it easy to send SMS and OTP tokens, manage sender IDs and channels, and add Termii-powered messaging to Laravel notifications and applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimum viable setup:

  1. composer require the package, then publish its config (php artisan vendor:publish --tag=termii-config or the tag listed in the README) so config/termii.php exists.
  2. Populate env: TERMII_API_KEY, sender ID (approved alphanumeric, e.g. TERMII), and the base URL — the eSIM client reuses the same key and base URL, so you configure them once.
  3. Resolve the client through the container/facade: $termii = app(LaraTermii::class); (or the LaraTermii facade alias). Never new it up — the sub-clients are wired in the service provider.

Where to look first:

  • The README for the messaging/OTP quickstart — that is 90% of day-one usage.
  • src/Concerns/Engage for the Termii Engage (messaging) methods you actually have. After 1.3.0 the method list lives in traits, not on LaraTermii, so IDE autocompletion on the root class can look thin.
  • src/Concerns/Sotel for the nine eSIM endpoints.
  • TermiiException for the exception vocabulary, including the new esimAuthenticationFailed().

First use case — an SMS OTP:

$termii->sendMessage(to: '2348012345678', from: config('termii.sender_id'), sms: 'Your code is 123456');

Then verify the number format, the sender ID approval status, and that you are hitting the correct base URL before building anything else.

Implementation Patterns

Resolve sub-clients, don't reach through the root class. The 1.3.0 split into Concerns\Engage and Concerns\Sotel signals the intended shape: $termii->esim()->dataPlans() for eSIM, the Engage-facing methods for messaging. Treat the root object as a factory and inject the sub-client into the class that needs it — that keeps your own code stable if the package reorganises again.

OTP flow. Send with the messaging API, persist a hashed code plus an expiry and attempt counter, and verify server-side. Do not use the send call's return as proof of delivery — queue it, log the message ID, and let a webhook or a status poll confirm.

eSIM journey. The nine endpoints map cleanly onto a provisioning flow:

$countries  = $termii->esim()->countries();
$plans      = $termii->esim()->dataPlans();
$esim       = $termii->esim()->createEsim(...);
$order      = $termii->esim()->purchasePlan(...); // non-idempotent
$qr         = $termii->esim()->qrCode($esim->id);
// later: profile(), usage(), planStatus(), esims()

Wrap createEsim() and purchasePlan() in a database transaction plus a local record keyed by your own idempotency reference, and run them from a queued job with tries = 1 (or a failed() handler that flags for manual reconciliation). Everything read-only — countries(), dataPlans(), usage(), planStatus() — is safe to cache and safe to retry.

Queue and rate-limit everything outbound. A ShouldQueue job per send, with backoff() and a retryUntil() window, keeps a bulk campaign from burning your Termii rate limit and from blocking a request cycle.

Testing. It is an HTTP-client wrapper, so Http::fake() in feature tests is enough — assert the outgoing URL and payload, and assert your job handles a TermiiException path. Add one test that fakes esimAuthenticationFailed() so a token-exchange regression fails loudly rather than silently in production.

Error handling. Catch TermiiException at the boundary of each job/controller and branch: esimAuthenticationFailed() is a configuration/credential problem and should alert, not retry; messaging failures are usually per-recipient and belong in a per-row result, not a thrown exception that kills a batch.

Gotchas and Tips

  • sendMessageWithNumber() is gone. It was added in 1.2.0 and removed in 1.3.0 because Termii's live v4 gateway 404s on sms/number/send. Grep your codebase before upgrading — the failure mode was an exception at runtime, not a compile error, so it hides behind untested branches. Fall back to the supported Engage send path or a raw Http::withToken(...) call if you genuinely need that endpoint, and pin the version if you cannot migrate yet.
  • The trait refactor is internal, but not invisible. The public API is unchanged, so normal calls are safe. What breaks is anything structural: extending LaraTermii, method_exists() checks against the concrete class, cached reflection lists, or a decorator that proxies __call. If you do any of that, run your suite before shipping.
  • eSIM auth is a second auth scheme. The eSIM client performs a bearer-token exchange automatically, using the same API key. A failed exchange throws esimAuthenticationFailed(). If messaging works and eSIM does not, the cause is almost always API-key scope or an eSIM/Sotel product not being enabled on the account — not your sender ID, and not the base URL.
  • Check token caching behaviour under load. Because the exchange is automatic, a burst of parallel eSIM jobs can each trigger one. If you see intermittent auth failures only at high concurrency, stagger the jobs or pre-warm the call before fanning out.
  • purchasePlan() is not idempotent. A timeout does not mean the purchase failed. Record your intent locally first, and reconcile with planStatus()/esims() rather than blindly retrying — the same warning applies to createEsim().
  • Sender ID and DND are the usual messaging culprits. Unapproved sender IDs get silently rewritten or rejected, and Nigerian DND numbers need the appropriate route. Verify a single real handset before you debug code.
  • Config caching. After changing TERMII_* env values, php artisan config:clear — a stale cached config is the most common "it works locally" report with this package.
  • Debugging. Log the outbound payload and the TermiiException body at the job boundary; Termii's error strings are specific enough to distinguish auth, sender, and balance problems without a support ticket.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource