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

Smpp Client Bundle Laravel Package

kronas/smpp-client-bundle

Laravel bundle that wraps an SMPP client, letting PHP applications send SMS through an SMSC using the SMPP protocol. Configure the SMSC host, port, and credentials, then submit messages directly from your Laravel app without handling raw SMPP sockets yourself.

View on GitHub
Deep Wiki
Context7

Getting Started

The first thing to internalize: kronas/smpp-client-bundle is Symfony2 DI glue, not an SMS API. It maps bundle configuration onto the constructor of SM\SMPPTransport from onlinecity/php-smpp and registers the client as a container service. There is no Laravel equivalent of AppKernel, so "getting started" splits into two paths.

Path A — you actually have a Symfony 2.x / PHP 5.x app (the only path where the bundle itself is used):

composer require kronas/smpp-client-bundle
// app/AppKernel.php
new Kronas\SmppClientBundle\KronasSmppClientBundle(),
# app/config/config.yml
kronas_smpp_client:
    host: smpp.provider.example
    port: 2775
    login: my_system_id
    password: my_password
    sender: "MyBrand"

Then find the real service id before writing a line of code — this is the single biggest first-hour time sink, because bundle naming conventions are unpredictable:

php app/console container:debug | grep -i smpp

container:debug <service_id> shows the class and constructor arguments, which is how you confirm whether sender was folded into the transport or exposed separately.

Path B — you are on Laravel (the realistic case): do not install the bundle. Install the underlying, framework-agnostic library and lift the bundle's config shape as your reference:

composer require onlinecity/php-smpp
// config/smpp.php
return [
    'host'     => env('SMPP_HOST'),
    'port'     => (int) env('SMPP_PORT', 2775),
    'login'    => env('SMPP_SYSTEM_ID'),
    'password' => env('SMPP_PASSWORD'),
    'sender'   => env('SMPP_SENDER', 'MyBrand'),
    'timeout'  => 10,
];

Bind a lazily-resolved singleton in a service provider, but treat it as a factory for short-lived sessions inside a daemon, not a global connection — see Gotchas.

First use case: a one-shot Artisan command that binds transceiver, submits a single submit_sm, logs the returned message_id, and closes. Run it against SMPPSim, not production. Do not start by wiring SMS into a controller; you will learn nothing about session behaviour and will learn it painfully in production instead.

Implementation Patterns

1. Put the transport behind your own contract from day one. The whole value of this package is "is SMPP worth it for us?", so the answer must be cheap to reverse.

interface SmsSender {
    /** @return string provider message id for DLR correlation */
    public function send(string $to, string $text, ?string $from = null): string;
}

Bind SmppSender in the container. Later you can swap in a Twilio/Vonage adapter without touching call sites. This also lets you ship a FakeSmsSender for tests.

2. Never send from a web request. SMPP is a stateful TCP bind; PHP-FPM will tear the socket down mid-submit_sm. The production pattern is:

  • POST /notifications → validate → dispatch a job → return 202.
  • A supervised long-lived worker (php artisan sms:daemon under supervisor/systemd) holds the bind and drains the queue.
class SendSmsJob implements ShouldQueue {
    public function handle(SmsSender $sender): void {
        $id = $sender->send($this->to, $this->text);
        SmsMessage::whereKey($this->smsId)->update(['provider_id' => $id, 'status' => 'submitted']);
    }
}

If you use SmsSender as a singleton inside the daemon only, the bind is reused and the queue provides your backpressure. That is the pattern the bundle does not give you and the one you must build.

3. Correlate DLRs explicitly. submit_sm_resp returns a message_id that may be a hex string, a decimal string, or vendor-prefixed garbage. Store it as a string column with an index, never cast it to int. Then a receiver loop handles deliver_sm:

foreach ($client->readSMS() as $pdu) {           // pseudo: your poll loop
    SmsMessage::where('provider_id', $pdu->receiptedMessageId)
        ->update(['status' => $this->mapState($pdu->stat), 'delivered_at' => now()]);
}

Set registered_delivery on submit (0 none, 1 final, 2 failure, 3 both). If you omit it, you get silent messages and no receipts, and you will blame the carrier.

4. Choose encoding deliberately, per message. GSM 7-bit for plain ASCII/Latin text; UCS-2 for anything with emoji or non-GSM characters. data_coding on the PDU must match the body you handed the library, or the handset renders mojibake. Write one helper that returns [encoding, segments] and unit-test it — that helper is where most real-world SMS bugs live.

5. Multipart is your job, not the SMSC's. Long messages become concatenated submit_sm PDUs with a UDH reference number. Budget 153 chars per GSM-7 segment and 67 per UCS-2 segment, and reuse a single UDH reference across the segments of one logical message so handsets reassemble them. Keep the library's sendSMS for single-part and add your own splitter for long text.

6. Inbound (MO) shares the same bind. In transceiver mode, deliver_sm carries both DLRs and inbound replies; your loop must distinguish esm_class and route accordingly. Design the receiver once, with two handlers, rather than bolting on inbound later.

7. Instrument the session, not just the message. Log bind attempts, enquire_link round-trips, reconnect counts, and submit_sm_resp command status. A messaging integration that "works" but reconnects every 90 seconds looks healthy in your app logs and is failing at the SMSC.

Gotchas and Tips

  • Package discovery is the first bug. container:debug | grep -i smpp is non-negotiable; do not guess the service id from the bundle name.
  • PHP/Symfony version wall. The bundle targets PHP 5.4–5.6 and Symfony 2.x. On PHP 8 you will hit removed syntax (each(), create_function(), old constructor styles) and Composer conflicts. If you must use it, vendor and pin the exact commit, and record it as technical debt with an exit date — 4 stars and 0 dependents means bus factor ≈ 1.
  • The bundle is the least valuable part. Its whole contribution is config→constructor mapping. Anything you actually need to fix (reconnect/backoff, throttling, async, DLR state machine) lives in the library or in your code, so plan to own it.
  • Bind type matters. bind_transmitter cannot receive DLRs. If you want receipts or inbound, you need transceiver (or separate tx/rx binds and a second connection). Getting this wrong produces "delivery receipts just don't arrive".
  • Throttling is a normal response, not an exception. ESME_RTHROTTLED (0x58) and ESME_RMSGQFUL mean slow down. Implement a bounded in-flight window (start at 10–20) and exponential backoff; without it you will fill the SMSC queue and lose messages silently.
  • enquire_link keepalive needs a timeout. If the SMSC stops answering, you must detect it and rebind. A socket that is open but dead is the classic silent-failure mode; supervise the worker and health-check the session, not just the process.
  • Sender ID semantics. Alphabetic sender IDs require TON = 5 / NPI = 0; numeric ones use TON = 1. Many aggregators silently rewrite or reject mismatched addressing, and the failure surfaces as "message accepted, never delivered".
  • Never let the client die with the request. No sleep()-based polling in a controller; no SMPP singleton resolved in a web context. If you need a quick path, dispatch() + worker is still the answer.
  • Debug with raw PDUs. Enable the library's logger and dump hex frames when something is off. Comparing your submit_sm against the SMPP 3.4 spec is faster than guessing at encoding or TLV problems.
  • Test against SMPPSim, not a real SMSC. Simulate: successful submit, throttled submit, connection drop mid-bind, DLR with unknown message_id, and multipart reassembly. Those five scenarios are the integration.
  • Extension points you'll actually use: decorate/swap your SmsSender binding, replace the client factory, and add a SmsMessage model for DLR state. Do not patch the vendor bundle in place — fork it into packages/ or replace it outright with the underlying library so upgrades (or the eventual migration to a modern SMPP client or HTTP aggregator) stay possible.
  • Reality check before you invest: if your volume is a few thousand messages a month, a commercial HTTP API with retries, callbacks, and support is cheaper than operating a bind. This package is worth the effort only when per-message margin, sender-ID control, throughput, or raw DLR fidelity are genuinely on the roadmap.
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.
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
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
cgv/localized-resource