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

Exabytes Sms Bundle Laravel Package

fd6130/exabytes-sms-bundle

A Symfony bundle for sending SMS through the Exabytes SMS gateway. Integrates the Exabytes API into your Symfony app with simple configuration and service injection, so you can dispatch transactional or notification messages from anywhere in your codebase without writing HTTP client boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and confirm what you actually got.

composer require fd6130/exabytes-sms-bundle

Before writing a single line of app code, open the package's composer.json and check for:

  • extra.laravel.providers / extra.laravel.aliases (auto-discovery) — if absent, you must register the service provider manually in bootstrap/providers.php (Laravel 11+) or config/app.php.
  • require — a symfony/* requirement list means this is a Symfony bundle that happens to be installable in Laravel, not a native Laravel package. The name ...-bundle is a strong hint. Confirm there's a real Illuminate\Support\ServiceProvider subclass before assuming php artisan vendor:publish will work.

2. Publish config and set credentials.

php artisan vendor:publish --provider="Fd6130\ExabytesSms\ExabytesSmsServiceProvider"

(Publish tag and provider FQCN are indicative — grep src/ for publishes( to get the exact tag.) Then in .env:

EXABYTES_API_KEY=
EXABYTES_SENDER_ID=
EXABYTES_API_URL=
EXABYTES_TIMEOUT=10

Confirm the config file path (config/exabytes.php vs config/sms.php) and cache it after changes: php artisan config:clear.

3. First use case: a single OTP to a Malaysian mobile number.

The fastest sanity check is a tinker send, not an integration:

php artisan tinker
>>> app(/* the client class from src/ */::class)->send('60123456789', 'Test OTP 123456');

Watch for: is the number accepted in E.164 (60..., no leading 0, no +)? Is the sender ID registered against the Exabytes account? Does the response object expose a message ID, or just a boolean?

Where to look first, in order: config/ (what's actually configurable), the service provider's register()/boot() (what's bound and whether it's a singleton), the client class (HTTP calls, retry/exception behaviour), then README.md for the expected response shape.

Implementation Patterns

Wrap the package, don't scatter it. Bind it behind your own contract so the vendor can be swapped and so tests don't hit Exabytes:

// app/Contracts/SmsSender.php
interface SmsSender { public function send(string $to, string $message): string; }

// app/Services/ExabytesSender.php
final class ExabytesSender implements SmsSender
{
    public function __construct(private readonly ExabytesClient $client) {}

    public function send(string $to, string $message): string
    {
        $response = $this->client->send($this->normalise($to), $message);

        if (! $response->isSuccess()) {           // shape varies — verify
            throw new SmsFailedException($response->message());
        }

        return $response->messageId();
    }

    private function normalise(string $to): string
    {
        return ltrim(preg_replace('/\D+/', '', $to), '0') |> /* prefix 60 */;
    }
}

Laravel Notification channel. For anything user-facing, expose SMS as a notification channel rather than calling the client from controllers. One toExabytes($notifiable) method returning a message object gives you queueing, throttling, and ShouldQueue for free.

Queue everything, always. HTTP calls to an SMS gateway are the classic source of 30-second checkout requests. Dispatch a job, set $tries = 3 and an explicit backoff(). Do not retry blindly on timeout — a timed-out request may have been delivered; retrying sends a duplicate and double-bills. Retry only on connection failures and 5xx, and prefer an idempotency key if the API supports one.

Bulk sends. Chunk recipients (array_chunk($numbers, 100) or the API's batch limit) and dispatch one job per chunk with Bus::batch(). Add your own rate limiter — the gateway's throughput cap is usually per-second and is documented only in the vendor portal, not in the package.

Delivery receipts / webhooks. If the package ships a webhook handler, register it behind your own route so you control middleware: exclude it from CSRF, verify the signature (or at minimum a shared secret in the query string), return 200 fast, and hand the payload to a job. Persist the provider's message ID at send time — that's the only join key you'll have.

Testing. Point the client at a fake. Either Http::fake() (if the package uses the Laravel HTTP client — check src/ for Illuminate\Support\Facades\Http vs raw Guzzle) or swap the binding:

$this->mock(SmsSender::class, fn ($m) => $m->shouldReceive('send')->once()->andReturn('msg_123'));

Assert on the normalised number — that's where the bugs live.

Multi-tenant. If tenants bring their own Exabytes credentials, you can't rely on the singleton config binding. Either rebuild the client per tenant from a config array, or use Config::set() inside a scoped binding and app()->forgetInstance() after.

Gotchas and Tips

  • "Bundle" is not a Laravel package. This is the single highest-risk item. A Symfony bundle installed into Laravel may autoload fine but expose no service provider, no facade, and no publishable config. Verify before estimating any work; if it's Symfony-only, you're writing a thin Guzzle wrapper yourself and the package is just reference documentation.
  • v1.0.1 shipped with no changelog — only a link to the commit list. Read the diff before upgrading (composer update fd6130/exabytes-sms-bundle --dry-run then inspect the commits). A patch bump is probably safe, but a 1.0.0 → 1.0.1 on a young package often carries silent config-key renames. Pin to ^1.0 and let the lockfile do its job.
  • Sender ID registration is an account-level prerequisite. An unregistered alphanumeric sender ID is frequently accepted by the API and then silently dropped by the carrier. If messages "send successfully" but never arrive, this is almost always the cause — check the Exabytes portal, not the code.
  • Phone number formatting is the #1 runtime bug. Malaysian numbers come in as 012-345 6789, +60123456789, 60123456789, and 123456789. Normalise once, centrally, and reject anything that doesn't match ^60\d{8,10}$ before it reaches the API.
  • HTTP 200 with an error body. Most regional SMS gateways return 200 and put the failure in the payload. Never treat the transport status as success — parse the body and check the status/error field explicitly.
  • Unicode splits your bill. GSM-7 packs 160 chars; a single emoji or curly quote drops you to 70 (UCS-2). Strip or transliterate when composing OTPs, and log segment count so cost surprises are traceable.
  • Don't log the payload. OTP codes and phone numbers are PII. Redact at the log-formatter level rather than trusting yourself to remember.
  • Timeouts are unset by default in many small packages. Confirm EXABYTES_TIMEOUT is actually wired into the Guzzle/HTTP client options; if not, the default is effectively infinite and one hung request ties up a queue worker.
  • Extension points worth knowing: the config array (usually merged shallowly — array_merge won't deep-merge nested arrays), the HTTP client binding (swap it to add middleware for logging/metrics), and the exception class (catch narrowly; a generic \Exception catch here will mask auth failures as delivery failures).
  • Debugging workflow: reproduce in tinker with a real number you own, then move to a queued job, then Http::fake() in the test suite. If the client throws an opaque exception, enable HTTP client logging via Http::globalRequestMiddleware() or a Guzzle Middleware::log() to see the raw request and response.
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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle