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

Php Client Laravel Package

smsapi.pl/php-client

Official PHP client for SMSAPI.pl for sending SMS, MMS and VMS messages, managing contacts, groups, templates and sender names, with bulk sending, delivery reports and easy Laravel integration.

View on GitHub
Deep Wiki
Context7

Getting Started

What you're working with. smsapi/php-client is a plain PHP SDK for the SMSAPI gateway (smsapi.pl — predominantly Polish/CEE market: SMS, MMS, VMS, contacts, sender names, blacklists, templates). It is not a Laravel package: no service provider, no facade, no config file, no auto-discovery, no Sms::fake(). You get a thin, well-typed wrapper around the HTTP API and you own everything above it. With 0 dependents and ~66 stars, treat it as a vendor SDK you wrap, not a framework citizen.

Minimal steps.

  1. composer require smsapi/php-client
  2. Read the README and the tests/ directory — the tests are the most accurate documentation of bag/feature construction and of the exact payloads sent to the gateway.
  3. Get credentials before writing code: in the SMSAPI panel create an API client (OAuth), issue a bearer token, and note the scopes you grant (SMS, contacts, sender names are separate scopes). Register/activate the sender name (from) there too — you cannot invent one at runtime.
  4. Wrap the client in a small SmsapiService bound as a singleton in AppServiceProvider, reading config from config/smsapi.php (which in turn reads env() — never call env() outside config files).

Sketch of the bootstrap (verify exact factory names against the installed version's README; the SmsapiClient / Feature / Bag shape is stable, the constructor sugar is not):

// config/smsapi.php
return [
    'token'  => env('SMSAPI_TOKEN'),
    'sender' => env('SMSAPI_SENDER', 'MyApp'),
    'format' => env('SMSAPI_FORMAT', 'hybrid'), // sms | unicode | hybrid
    'timeout' => 5,
];

// AppServiceProvider::register()
$this->app->singleton(SmsapiClient::class, fn () => SmsapiClient::withBearerToken(config('smsapi.token')));
$this->app->bind(SmsTransport::class, SmsapiTransport::class);

First use case that pays for itself: transactional SMS — OTP on password reset, order-confirmation ping. That's one recipient, one message, and it forces you to solve the three things that actually matter: phone normalization, sender name, and character encoding.

Implementation Patterns

Do not call the client from controllers. Define a SmsTransport interface (send(string $to, string $body, array $opts = []): string) and put the SDK call behind it. The payoff is a FakeSmsTransport for tests and the ability to swap providers later — with an NOASSERTION licence and a single maintainer, a cheap escape hatch is prudent.

Use Laravel notifications, not raw service calls. Create an SmsChannel (send($notifiable, Notification $n)) and have notifiables expose routeNotificationForSmsapi(). Then:

  • class OrderShipped extends Notification implements ShouldQueue — always queue. An HTTP round-trip to the gateway inside web is a request you'll regret at peak load.
  • On-demand: Notification::route('smsapi', $phone)->notify(...) for non-User recipients (invited users, leads).
  • Keep the notification class responsible for content, the transport responsible for delivery and error mapping.

Bulk sends. The SDK exposes a bulk bag for one-message-many-recipients and a personalised variant. Chunk lists (a few thousand per call, not the full table) and set your own idx per message so delivery reports are correlatable. For personalisation, pass per-recipient params rather than looping send() — one HTTP call beats 5,000 and avoids hammering rate limits.

Persistence and delivery reports. Store an sms_messages row (idx, to, status, segments, cost, provider_response) before dispatching. Configure the callback URL in the panel and add a route:

  • The callback is unsigned — protect it with a shared secret query param or IP allowlist, and make the handler idempotent (updateOrCreate on idx) because retries happen.
  • Map provider status codes to a small local enum so your UI doesn't render provider jargon.

Scheduled/campaign traffic. A queued job with ->delay() plus RateLimiter/Bus::batch() gives you pacing and per-batch failure reporting. The SDK has no throttling of its own.

Testing. Point the container at a fake in TestCase::setUp(). For integration-level tests, the SDK rides on Guzzle, so you can inject a mock handler with canned JSON responses and assert the outgoing payload — worth doing once to lock down encoding and from behaviour.

Multi-account. Marketing traffic and transactional traffic usually live on separate SMSAPI subaccounts (different sender names, separate limits and billing). Bind two singletons with distinct config keys rather than one global client.

Gotchas and Tips

  • Auth drift. Older docs show username + password-hash basic auth; the current recommended path is an OAuth bearer token with explicit scopes and IP restrictions on the API client. If you copy an old snippet and get 401s, this is why — and an IP-bound token will fail silently from a queue worker on a different egress IP.
  • Sender name is a server-side fact. An unregistered or deactivated from returns a per-message error, not a client exception. Validate it once at boot (or in a health check) instead of discovering it in production.
  • Phone format. SMSAPI wants digits with country code (e.g. 48XXXXXXXXX), not +48 … and not local 500 600 700. Normalise and validate in a form request or a mutator (propaganistas/laravel-phone is a good fit) and store canonical numbers — a rejected number costs you a failed OTP and a support ticket.
  • Encoding is the cost multiplier. sms = GSM-7 (160 chars), unicode = UCS-2 (70 chars/segment), hybrid = auto-detect per message. One emoji or a stray ą flips a message to UCS-2 and triples the segment count. Choose deliberately: hybrid for safety, sms + normalize (strip diacritics) when cost matters and legibility survives without Polish characters.
  • Errors are layered. You'll see transport/host exceptions, auth exceptions, and per-message errors returned inside a successful response — especially in bulk. Catching a top-level exception is not enough; iterate the response and log failures with their idx. Classify transient (5xx, timeout, 429) vs permanent (invalid number, no funds) before deciding to retry, and let Laravel's $this->release($backoff) or Retry middleware handle the transient case.
  • No idempotency built in. A queued job retried after a timeout can double-send and double-bill. Send with your own dedupe key in the message params and short-circuit on repeat.
  • Set timeouts. The default Guzzle timeout can pin PHP-FPM workers behind a slow gateway. Set connect timeout ~2s, total ~5s in the client construction, and make the failure mode "log and let the queue retry", not "hang".
  • config:cache traps. Any env() call inside your transport class resolves to null after php artisan config:cache. Keep the env reads strictly in config/smsapi.php.
  • Don't log PII. Full phone numbers and message bodies are personal data under RODO/GDPR. Log masked numbers (48*****6700) and the idx, never the body — OTPs in logs are a breach waiting to happen.
  • Callback security has no signature. Anyone who learns the URL can forge a "delivered" status. Use an unguessable path segment or a secret query parameter compared with hash_equals.
  • Extension points worth knowing. The SDK accepts a custom proxy/HTTP client — that's your hook for Guzzle middleware to add structured logging, metrics, and correlation IDs. On the feature side, Bag objects are mutable builders; build a fresh one per send rather than reusing a mutated bag across a loop, which is a classic source of "everyone got the first person's name".
  • Version and maintenance. Releases are regular, but the project is single-maintainer and not Laravel-aware. Pin the major version in composer.json, and keep the SDK call confined to one class so a breaking change or an abandoned release is a one-file fix.
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