javihernandezgil/nexmo-bundle
Symfony bundle that wraps the Nexmo (now Vonage) SMS API in configurable services, making it easy to send text messages from your controllers. It handles client setup through the Symfony service container, so you can start sending SMS with minimal boilerplate.
Read this first: javihernandezgil/nexmo-bundle is a Symfony2 bundle, not a Laravel package. It ships no service provider, no facade, and no config/ publish tag — it only registers a DI service around nexmo/client via Resources/config/services.xml and reads credentials from DependencyInjection/Configuration.php. It was last released in 2016 and the repository is archived. You cannot composer require it into a Laravel app and get anything usable; at best you get a tree of Symfony\Component\* classes that Laravel's container will never boot.
So "getting started" for a Laravel dev means transplanting the idea, not the package:
composer require vonage/client (the successor to nexmo/client; the old namespace Nexmo\Client was renamed to Vonage\Client).Configuration.php did — API key/secret pair, plus optional application ID and private key path for voice/verify — into config/services.php:// config/services.php
'vonage' => [
'key' => env('VONAGE_KEY'),
'secret' => env('VONAGE_SECRET'),
'app_id' => env('VONAGE_APP_ID'),
'private' => env('VONAGE_PRIVATE_KEY_PATH'),
'sms_from' => env('VONAGE_SMS_FROM', 'MyApp'),
],
nexmo.client:$this->app->singleton(Client::class, function ($app) {
$c = $app['config']['services.vonage'];
$credentials = new Credentials($c['key'], $c['secret']);
$client = new Client($credentials);
if ($c['app_id'] && $c['private']) {
$client->setAppId($c['app_id']);
$client->setPrivateKey(Keypair::read($c['private']));
}
return $client;
});
First use case: send one SMS to your own number and assert you get a message-id back — that single round trip validates credentials, sender ID formatting, and outbound connectivity, which is where ~80% of the setup failures land.
Do not inject Vonage\Client into controllers. The bundle's weakness was that it exposed one god-service (nexmo.client) that every consumer reached for. Wrap it:
interface SmsSender { public function send(string $to, string $text, ?string $from = null): string; }
Bind SmsSender to a VonageSmsSender implementation. You get testability, a place to enforce E.164 normalization, and a seam to swap providers later.
Prefer Laravel's built-in notification channel for day-to-day SMS. If your only need is "notify a user," skip the client entirely — Notification::send($user, new OrderShipped($order)) with toVonage() requires no package and no DI wiring. Reach for the raw client only when you need delivery receipts, inbound handling, or Verify/Number Insight APIs.
Queue the send. Network calls inside a request cycle are the classic mistake. Dispatch a SendSms job (ShouldQueue, tries = 3, exponential backoff) so a Nexmo 429 or timeout doesn't 500 your checkout.
Inbound webhooks: register a route outside web middleware (routes/api.php is fine, but disable CSRF and session), validate the payload, and return a bare 200 fast — Nexmo retries on non-2xx and duplicates cost you money.
Map the bundle's config semantics faithfully: the Symfony config tree separated messaging credentials (key/secret) from voice/app credentials (app_id/private_key). Keep that split in your own config array so you don't accidentally require a private key to send an SMS.
Extension point translation: the bundle's compiler pass and service aliasing correspond to Laravel's $this->app->alias() and contextual binding. If you have multiple senders (marketing vs transactional numbers), use when(SmsSender::class)->needs('$from')->give(...) rather than a config switch.
nexmo/client SDK namespaces were renamed. Any tutorial referencing Nexmo\Client from this bundle will fail against a current install.env() outside config files is null after config:cache. The bundle read parameters.yml at container compile time; Laravel's equivalent discipline is: env() only inside config/*.php, always config('services.vonage.key') elsewhere.env, and base_path()-resolve it. Relative paths that work locally silently 401 in production.401 = bad credentials, 402 = account balance too low (looks identical to a bug in your code until you check the dashboard), 429 = throughput cap. Log response->getStatus() and the message-id, and never retry 401/402.+1, 01, and spaced formats all fail with a generic "invalid params" that tells you nothing.hash_equals the computed signature — constant-time, no exceptions.SmsSender in your test container or use Http::fake(). Never let a test suite hit the live API; a stray integration test in CI will burn balance and trip rate limits.nexmo.client service definition with Vonage\Client and keep the service ID, so consumers don't change.How can I help you explore Laravel packages today?