octopush/sms-bundle
octopush/sms-bundle is a Symfony bundle that integrates the Octopush SMS API, letting applications configure credentials and send SMS messages through a simple service. It helps add transactional or notification texting to Symfony projects with minimal setup.
Package reality check first: octopush/sms-bundle is still a Symfony bundle, not a Laravel package. No ServiceProvider, no facade, no publishable config/*.php. 2.0.0 does not change that — it is a client/endpoint change (Octopush API v2), not a framework integration. The pragmatic path remains: require the underlying SDK (octopush/sms) directly and treat this bundle as a reference for how they wrap config + client.
What 2.0.0 actually changes: it moves the bundle onto Octopush API v2. That is a breaking major for a reason — expect different request payloads, different response shapes, and potentially different success codes and auth handling than v1. Any code you wrote against the v1 wrapper needs re-validating end-to-end, not just a version bump in composer.json.
Minimal steps:
composer require octopush/sms-bundle:^2.0. Do not leave a ^1.0-shaped assumption in place — a v1-style wrapper calling a v2 client will fail at runtime, not at install time. Run composer update --dry-run in a scratch branch first; the 2021-era transitive constraints may or may not have been refreshed by this release, and the release note does not promise it.DependencyInjection/Configuration.php. Confirm which config keys survived the major (login/email, api-key, sender, sms_type, recipients limits) — a renamed key fails as a silent auth error, not a config exception.AppServiceProvider::register() and pass it the config array. Credentials go in config/services.php + .env, never a checked-in YAML.Where to look first:
DependencyInjection/Configuration.php — your source of truth for env var names, and the first place a v1→v2 rename will show up.octopush/sms. Check whether 2.0.0 exposes anything new (balance, delivery receipts, batch send) or whether it is a pure transport swap.101, 201, …) may no longer be the success values — re-derive them, don't reuse.Wrap it, don't sprinkle it — and make the wrapper yours. Given a v1→v2 break just shipped, the single highest-value pattern is a one-file boundary you control:
// App\Contracts\SmsSender
interface SmsSender { public function send(string $to, string $message, array $opts = []): array; }
Bind OctopushSender to it in a provider, delegating to the bundle service. When 3.0 lands (or you drop the bundle for octopush/sms), you change one class instead of every call site.
Use it as a Laravel Notification channel. Unchanged as a pattern, and it is the ergonomics the bundle still gives you none of:
class OctopushChannel {
public function __construct(private SmsSender $sms) {}
public function send($notifiable, $notification) {
$message = $notification->toOctopush($notifiable);
return $this->sms->send($notifiable->phone, $message->content);
}
}
OrderShipped implements toOctopush(), models implement routeNotificationForOctopush().
Queue everything. Octopush is a remote HTTP call; a 3-second timeout in checkout is a bad time. ShouldQueue job or a queued listener. On retries, assume the v2 API may have accepted the message before your timeout — persist whatever id v2 returns (ticket/id/number — check the v2 response body, it is not necessarily the v1 field names) and de-dupe against it.
Normalize phone numbers before they hit the API. E.164 with country prefix (+33612345678). Use propaganistas/laravel-phone so invalid numbers fail fast instead of burning credits. v2 may be stricter about format than v1 — validate regardless.
Batch sends for campaigns. The wrapper still exposes single sends. Loop with throttling for per-account rate limits; chunk via LazyCollection or a queue worker with sleep. Log the full response (cost, balance, id) per send so you can reconcile against the dashboard — and re-check which fields v2 actually returns before writing the log columns.
Template the copy outside the code. Still no templating layer: render SMS bodies from resources/sms/*.blade.php or lang/*/sms.php. Keep under ~160 GSM-7 chars; you get no segmentation warning.
2.0.0 is a hard break, not a refresh. The release note is one line — "Use V2.0 of octopush api" — which means the contract change is in the transport, not in the ergonomics. Treat every integration point as suspect: payload keys, response envelope, success code, error message location, auth header. Write one integration test that sends (or mocks) a single message and asserts on the response shape before you touch the rest of the app.
Do not carry over v1 success codes. Your v1 guard if ($response['code'] !== 201) is meaningless against v2 until you confirm the new code. A stale guard either throws on every success or, worse, passes on every failure. Re-derive from the v2 docs, per endpoint, and put the number in config so it is visible.
Config keys may have moved. The bundle reads Symfony-style keys (octopush_sms.login / api_key). A major version is exactly when those get renamed or dropped — if v2 authenticates differently (e.g. api-key only, no login), your OCTOPUSH_EMAIL/OCTOPUSH_LOGIN mapping silently produces an auth failure that reads like a network error. Log the configured login (never the key) at boot in dev to confirm you're feeding it what it expects.
Still no Laravel service auto-discovery. package:discover will not find it; you new the service yourself in a provider. There is still no config/octopush.php to publish — that file does not exist.
Errors are still returned, not thrown. Octopush commonly returns HTTP 200 with a numeric code for both success and failure, and the bundle still does not normalize it:
$response = $this->client->send(...);
if (! in_array($response['code'] ?? null, config('services.octopush.success_codes'), true)) {
Log::warning('octopush.failed', $response);
throw new SmsFailedException($response['message'] ?? 'unknown');
}
Config-drive the code list so a future API bump doesn't mean grepping for magic numbers.
Freshness improved; trust did not. There is now a 2.0.0 rather than a 2021-only artifact, which is a genuine signal the package is maintained. It does not upgrade the risk profile much: still a small Symfony bundle with a thin dependents base. Keep pinning (^2.0), keep the dependency behind your own interface, and keep composer update --dry-run in CI. The honest escalation path is unchanged — fork the ~20-line wrapper into app/Services and require octopush/sms directly. If anything, v2 makes that more attractive: your interface hides the API version.
Sender name (France) is legally constrained. ≤11 alphanumeric characters, no phone-number-lookalike without proof of ownership. Marketing SMS in France must carry an opt-out (STOP) mention — the bundle does not append it, in v2 either.
Phone format traps. Leading 0 French numbers must become +33…. A malformed number is happily forwarded and billed as a rejected message.
International vs. premium vs. low-cost is a cost decision. sms_premium reaches all operators, costs more; sms_low_cost is cheaper but can be throttled or dropped. Confirm the v2 type strings before hard-coding — this is a likely rename point. Make it configurable.
Debugging: Guzzle underneath, in most versions. Decorate the handler (HandlerStack::push(...)) to log request body, status, and timing — never edit vendor code. If you get a generic RuntimeException, drop to octopush/sms and call its client directly: that isolates bundle-vs-SDK, which is now the most likely place a v1→v2 regression is hiding.
Extension point: the wrapper is thin, so forking it into app/Services is cheap and gives you the API version on your own schedule. With a package this small, that remains the honest recommendation for a Laravel codebase.
How can I help you explore Laravel packages today?