Download the PHP package uzbek/laravel-sms without Composer
On this page you can find all versions of the php package uzbek/laravel-sms. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download uzbek/laravel-sms
More information about uzbek/laravel-sms
Files in uzbek/laravel-sms
Package laravel-sms
Short Description Unified SMS sending for Uzbek providers — Eskiz, PlayMobile, TextUp and Sayqal behind a single driver contract.
License MIT
Homepage https://github.com/professor93/laravel-sms-uzbekistan
Informations about the package laravel-sms
Laravel SMS Uzbekistan
One API over Uzbekistan's SMS providers — Eskiz, PlayMobile (smsxabar), TextUp, Sayqal — plus your own drivers. Fallback, queueing, OTP, webhooks, templates, notifications channel, testing fake. Everything beyond plain sending is opt-in: unused features cost nothing and change nothing.
Requirements
- PHP 8.2+, Laravel 11+
- Credentials for at least one provider
Install
Keep the published config/sms.php complete — Laravel merges package config top-level only; a partial file drops whole sections.
Quick start
Sending
Bulk
Native batch endpoints are used where they exist (Eskiz, PlayMobile); others loop per message.
Results
SentMessage fields: provider, phone, text, status (enum), successful, providerMessageId, errorMessage, raw, fallbackFrom (primary provider when a fallback delivered), debug. Methods:
Queued and scheduled sends
Fallback resolves at queue time; options and runtime credentials travel with the job. Inside the job a provider failure becomes the usual failed SentMessage + events — queue retries stay for infrastructure errors. Sync-only (throw LogicException when queued): debug(), useFallback() with a Closure.
Duplicate protection
At-most-once per key: reserved in cache before transport; a double click or requeued job inside the TTL is skipped (failed result, "duplicate" message, no fallback). Cache down → sends proceed with a warning.
OTP
Codes are hashed at rest, single-use, attempt-limited, resend-throttled. template = raw string | templates.list name | translation key. Config: sms.otp (length 6, ttl 300, max_attempts 5, resend_cooldown 60, template). Cache down → no code is sent (it could never be verified).
Notifications channel
Recipient: routeNotificationForSms() on the notifiable, SmsMessage->to(), or on-demand Notification::route('sms', $phone). Missing route/toSms() = logged skip, never a throw.
Templates
enforce=true (matches Eskiz/TextUp moderation reality): builder text matching no template throws TemplateViolationException before transport — single, bulk, and queue paths. :placeholders match any value.
TextUp moderation: the text must exactly match an approved template (placeholders filled, trailing newline included) or TextUp rejects it. Approve templates in the cabinet first —
enforcehelps you catch violations before the provider does.
Fallback provider
Per-provider default: 'fallback' => env('ESKIZ_FALLBACK') — applied by the fluent builders, overridable per message. One level deep, result carries fallbackFrom. Bulk fallback retries only failed messages and needs the primary driver to declare SupportsBulkFallback (per-message senders like Sayqal); batch-optimistic drivers log a warning instead (silenced by SMS_SILENT).
Resilience (all opt-in, per provider)
Defaults keep the old behavior: no transient retry (401 refresh-and-retry always works), breaker off, one batch request. Breaker state lives in the cache; cache down = breaker stays closed. Success closes the failure counter; prefix rejections and fake mode don't count.
Restricting recipients by prefix
Per provider, digit-normalized (+998 33 == 99833). A prohibited number fails before any HTTP with the usual pipeline: failed SentMessage, SmsSent event, log row; the rest of a bulk continues.
Dynamic rules (database, admin panel)
Resolved once per send call — instant rule changes; cache inside the class. Neither list required; blocklist wins; merged with the static config. Source failure = warning + static lists only, so keep legal blocks static.
Webhooks
Routes register at POST {SMS_WEBHOOK_PATH}/{provider} when SMS_WEBHOOK_ENABLED=true. Outside web/CSRF (providers POST server-to-server); middleware from sms.webhook.middleware. Every verified webhook dispatches DeliveryStatusUpdated per report (updates sms_logs when database logging is on); without a handler it is also written to the log.
PlayMobile — register https://your-app.uz/sms/webhooks/playmobile?token=<PLAYMOBILE_WEBHOOK_SECRET> in the cabinet. Empty secret = token check skipped; set = enforced. Optional allowed_ips pinning — exact IPs and CIDR ranges, IPv4 + IPv6 (['185.8.212.47', '10.0.0.0/8', '2001:db8::/32']). Bad token/IP → 403.
Eskiz — set ESKIZ_CALLBACK_ENABLED=true and every send carries callback_url: the explicit ESKIZ_CALLBACK_URL, or the package webhook URL (needs SMS_WEBHOOK_ENABLED=true), with ?token= appended when ESKIZ_WEBHOOK_SECRET is set. Same token/IP guards on intake.
TextUp, Sayqal — no webhook intake; poll checkStatus() / refreshStatus(), or accept their POSTs with a custom handler.
Custom handlers — per provider:
Additive: events and DB logging keep working. A handler also unlocks the endpoint for drivers that can't parse webhooks ($reports empty, raw request yours, handler owns security). Wrong class → loud 500.
Balance, health, stats, prune
Missing table / missing retention = friendly message, exit 0.
Named providers, runtime credentials
Provider names are arbitrary keys — two Eskiz accounts are two entries sharing 'driver' => 'eskiz'. The name flows through sms_logs.provider, webhooks, and fallback references.
Tokens cache per credential set — parallel accounts never collide.
Custom drivers
Required: doSend() + resolveAuthenticator() (reuse ApiKey/Bearer/Basic/SignedToken/LoginToken authenticators). Optional capability contracts — add what the provider supports: ChecksDeliveryStatus, HandlesWebhooks (+ VerifiesWebhookSecurity trait), ChecksBalance, ChecksHealth, SupportsBulkFallback (marker: per-message bulk, safe to retry individually). Native batch → override doSendMany() through finalizeBulk(). Events, logging, retries, breaker, prefixes, chunking, fake mode, builders — inherited.
Capability detection
Config overrides from the database
File config stays the base; a source supplies only changed keys (rotated credentials, enabled toggles):
Precedence: file < source < runtime credentials. Failing source = warning + file config. Any ProviderConfigOverrides implementation works (Redis, API, ...).
Fake mode (env-driven)
Sends short-circuit before auth — zero provider traffic — while events, logging, prefixes, and fallback stay real. fake-<ulid> ids, raw = ['fake' => true].
Testing your app
Builders, helper, named providers, facades, notifications, OTP — all route through the fake.
Debug mode
Code-only (no config flag), sync-only. Bulk gets the whole-window trace per message.
Events
SmsSent($message)— exactly once per message, success or failure. Note: fires beforefallbackFrom/debugare stamped.DeliveryStatusUpdated(provider, providerMessageId, status, raw)— webhooks,updateStatus(),refreshStatus().LowBalanceDetected(provider, amount, threshold).
Logging
- Database (
SMS_LOG_DATABASE): every send →sms_logs(provider, phone, text, status, segments, cost, error, payload); webhooks andupdateStatus()update the row. - Debug log (
SMS_LOG_DEBUG): structured entry per send toSMS_LOG_CHANNEL, credentials redacted.
Full .env reference
Config-only (no env): drivers, prefix_rules, config_overrides(+_ttl), templates.list, webhook.middleware, per-provider retry, circuit_breaker, bulk, price_per_segment, health_check, webhook_handler, allowed_ips, http_options (raw Guzzle — Uzbek providers usually need a whitelisted static IP, so proxy is first-class), otp block, logging.prune_after_days.
Deployment and cache notes
Eskiz/TextUp tokens cache with single-flight refresh: on 401 one process re-logs-in under an atomic lock, the rest adopt the fresh token.
SMS_CACHE_STORE(empty = app default) also holds breaker/OTP/dedupe state. Locks work onfile,database,array,redis,memcached,dynamodb.- Multi-server / Vapor: use a shared store (
redis,database,dynamodb). Per-container caches still work — each container just logs in independently. - No lock support → refresh degrades to lock-less re-login; duplicate logins are harmless.
SMS_CACHE_PREFIXnamespaces every key (sms:eskiz:token,sms:eskiz:breaker,sms:otp:*,sms:dedupe:*).
Package tests
License
MIT. See LICENSE.md.
All versions of laravel-sms with dependencies
illuminate/contracts Version ^12.0|^13.0
illuminate/database Version ^12.0|^13.0
illuminate/events Version ^12.0|^13.0
illuminate/http Version ^12.0|^13.0
illuminate/support Version ^12.0|^13.0
spatie/laravel-data Version ^4.0
symfony/uid Version ^7.0|^8.0