├── LICENSE.md ├── README.md ├── composer.json ├── config └── vonage.php └── src ├── Channels └── VonageSmsChannel.php ├── Facades └── Vonage.php ├── Messages └── VonageMessage.php ├── Vonage.php └── VonageChannelServiceProvider.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Taylor Otwell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel Vonage Notification Channel 2 | 3 | Build Status 4 | Total Downloads 5 | Latest Stable Version 6 | License 7 | 8 | ## Official Documentation 9 | 10 | Documentation for Laravel Vonage Notification Channel can be found on the [Laravel website](https://laravel.com/docs/notifications#sms-notifications). 11 | 12 | ## Contributing 13 | 14 | Thank you for considering contributing to Vonage Notification Channel! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). 15 | 16 | ## Code of Conduct 17 | 18 | In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). 19 | 20 | ## Security Vulnerabilities 21 | 22 | Please review [our security policy](https://github.com/laravel/vonage-notification-channel/security/policy) on how to report security vulnerabilities. 23 | 24 | ## License 25 | 26 | Laravel Vonage Notification Channel is open-sourced software licensed under the [MIT license](LICENSE.md). 27 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/vonage-notification-channel", 3 | "description": "Vonage Notification Channel for laravel.", 4 | "keywords": ["laravel", "notifications", "vonage", "nexmo"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Taylor Otwell", 9 | "email": "taylor@laravel.com" 10 | } 11 | ], 12 | "require": { 13 | "php": "^8.0", 14 | "illuminate/notifications": "^8.0|^9.0|^10.0|^11.0|^12.0", 15 | "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", 16 | "vonage/client-core": "^4.0.4" 17 | }, 18 | "require-dev": { 19 | "guzzlehttp/guzzle": "^7.2", 20 | "mockery/mockery": "^1.0", 21 | "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", 22 | "phpstan/phpstan": "^1.10", 23 | "phpunit/phpunit": "^9.0|^10.4|^11.5" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "Illuminate\\Notifications\\": "src/" 28 | } 29 | }, 30 | "autoload-dev": { 31 | "psr-4": { 32 | "Illuminate\\Notifications\\Tests\\": "tests/" 33 | } 34 | }, 35 | "config": { 36 | "sort-packages": true, 37 | "allow-plugins": { 38 | "composer/package-versions-deprecated": true 39 | } 40 | }, 41 | "extra": { 42 | "branch-alias": { 43 | "dev-master": "3.x-dev" 44 | }, 45 | "laravel": { 46 | "providers": [ 47 | "Illuminate\\Notifications\\VonageChannelServiceProvider" 48 | ], 49 | "aliases": { 50 | "Vonage": "Illuminate\\Notifications\\Facades\\Vonage" 51 | } 52 | } 53 | }, 54 | "minimum-stability": "dev", 55 | "prefer-stable": true 56 | } 57 | -------------------------------------------------------------------------------- /config/vonage.php: -------------------------------------------------------------------------------- 1 | env('VONAGE_SMS_FROM'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | API Credentials 21 | |-------------------------------------------------------------------------- 22 | | 23 | | The following configuration options contain your API credentials, which 24 | | may be accessed from your Vonage dashboard. These credentials may be 25 | | used to authenticate with the Vonage API so you may send messages. 26 | | 27 | */ 28 | 29 | 'api_key' => env('VONAGE_KEY'), 30 | 31 | 'api_secret' => env('VONAGE_SECRET'), 32 | 33 | 'application_id' => env('VONAGE_APPLICATION_ID'), 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Signature Secret 38 | |-------------------------------------------------------------------------- 39 | | 40 | | If your application is receiving webhooks from Vonage, you may wish to 41 | | configure a message signature secret so that you can ensure each of 42 | | the inbound webhook calls are actually originating within Vonage. 43 | | 44 | */ 45 | 46 | 'signature_secret' => env('VONAGE_SIGNATURE_SECRET'), 47 | 48 | /* 49 | |-------------------------------------------------------------------------- 50 | | Private Key 51 | |-------------------------------------------------------------------------- 52 | | 53 | | Some of Vonage's recent APIs utilize JWTs for authentication, which also 54 | | require a private key so that they may be signed. You may define your 55 | | application's private key string below via the configuration value. 56 | | 57 | */ 58 | 59 | 'private_key' => env('VONAGE_PRIVATE_KEY'), 60 | 61 | /* 62 | |-------------------------------------------------------------------------- 63 | | Application Identifiers 64 | |-------------------------------------------------------------------------- 65 | | 66 | | Adding an application name and version may assist you in identifying 67 | | problems with your application or when viewing analytics for your 68 | | application's API usage within the dedicated Vonage dashboards. 69 | | 70 | */ 71 | 72 | 'app' => [ 73 | 'name' => env('VONAGE_APP_NAME', 'Laravel'), 74 | 'version' => env('VONAGE_APP_VERSION', '1.1.2'), 75 | ], 76 | 77 | ]; 78 | -------------------------------------------------------------------------------- /src/Channels/VonageSmsChannel.php: -------------------------------------------------------------------------------- 1 | from = $from; 36 | $this->client = $client; 37 | } 38 | 39 | /** 40 | * Send the given notification. 41 | * 42 | * @param mixed $notifiable 43 | * @param \Illuminate\Notifications\Notification $notification 44 | * @return \Vonage\SMS\Collection|null 45 | */ 46 | public function send($notifiable, Notification $notification) 47 | { 48 | if (! $to = $notifiable->routeNotificationFor('vonage', $notification)) { 49 | return; 50 | } 51 | 52 | $message = $notification->toVonage($notifiable); 53 | 54 | if (is_string($message)) { 55 | $message = new VonageMessage($message); 56 | } 57 | 58 | $vonageSms = new SMS( 59 | $to, 60 | $message->from ?: $this->from, 61 | trim($message->content), 62 | $message->type 63 | ); 64 | 65 | $vonageSms->setClientRef($message->clientReference); 66 | 67 | if ($message->statusCallback) { 68 | $vonageSms->setDeliveryReceiptCallback($message->statusCallback); 69 | } 70 | 71 | return ($message->client ?? $this->client)->sms()->send($vonageSms); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Facades/Vonage.php: -------------------------------------------------------------------------------- 1 | content = $content; 58 | } 59 | 60 | /** 61 | * Set the message content. 62 | * 63 | * @param string $content 64 | * @return $this 65 | */ 66 | public function content($content) 67 | { 68 | $this->content = $content; 69 | 70 | return $this; 71 | } 72 | 73 | /** 74 | * Set the phone number the message should be sent from. 75 | * 76 | * @param string $from 77 | * @return $this 78 | */ 79 | public function from($from) 80 | { 81 | $this->from = $from; 82 | 83 | return $this; 84 | } 85 | 86 | /** 87 | * Set the message type. 88 | * 89 | * @return $this 90 | */ 91 | public function unicode() 92 | { 93 | $this->type = 'unicode'; 94 | 95 | return $this; 96 | } 97 | 98 | /** 99 | * Set the client reference (up to 40 characters). 100 | * 101 | * @param string $clientReference 102 | * @return $this 103 | */ 104 | public function clientReference($clientReference) 105 | { 106 | $this->clientReference = $clientReference; 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * Set the webhook callback URL to update the message status. 113 | * 114 | * @param string $callback 115 | * @return $this 116 | */ 117 | public function statusCallback(string $callback) 118 | { 119 | $this->statusCallback = $callback; 120 | 121 | return $this; 122 | } 123 | 124 | /** 125 | * Set the Vonage client instance. 126 | * 127 | * @param \Vonage\Client $client 128 | * @return $this 129 | */ 130 | public function usingClient($client) 131 | { 132 | $this->client = $client; 133 | 134 | return $this; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/Vonage.php: -------------------------------------------------------------------------------- 1 | config = $config; 40 | $this->client = $client; 41 | } 42 | 43 | /** 44 | * Create a new Vonage instance. 45 | * 46 | * @param array $config 47 | * @param \Psr\Http\Client\ClientInterface|null $client 48 | * @return static 49 | */ 50 | public static function make(array $config, ?ClientInterface $client = null) 51 | { 52 | return new static($config, $client); 53 | } 54 | 55 | /** 56 | * Create a new Vonage Client. 57 | * 58 | * @return \Vonage\Client 59 | * 60 | * @throws \RuntimeException 61 | */ 62 | public function client() 63 | { 64 | $privateKeyCredentials = null; 65 | 66 | if ($privateKey = $this->config['private_key'] ?? null) { 67 | if (! $appId = $this->config['application_id'] ?? null) { 68 | throw new RuntimeException('You must provide a vonage.application_id when using a private key.'); 69 | } 70 | 71 | $privateKeyCredentials = new Keypair($this->loadPrivateKey($privateKey), $appId); 72 | } 73 | 74 | $basicCredentials = null; 75 | 76 | if ($apiSecret = $this->config['api_secret'] ?? null) { 77 | $basicCredentials = new Basic($this->config['api_key'], $apiSecret); 78 | } 79 | 80 | $signatureCredentials = null; 81 | 82 | if ($signatureSecret = $this->config['signature_secret'] ?? null) { 83 | $signatureCredentials = new SignatureSecret($this->config['api_key'], $signatureSecret, $this->config['signature_method'] ?? 'md5hash'); 84 | } 85 | 86 | if ($basicCredentials && $signatureCredentials) { 87 | throw new RuntimeException('Provide either vonage.api_secret or vonage.signature_secret, not both.'); 88 | } 89 | 90 | if ($privateKeyCredentials && $basicCredentials) { 91 | $credentials = new Container($privateKeyCredentials, $basicCredentials); 92 | } elseif ($privateKeyCredentials && $signatureCredentials) { 93 | $credentials = new Container($privateKeyCredentials, $signatureCredentials); 94 | } elseif ($privateKeyCredentials) { 95 | $credentials = $privateKeyCredentials; 96 | } elseif ($signatureCredentials) { 97 | $credentials = $signatureCredentials; 98 | } elseif ($basicCredentials) { 99 | $credentials = $basicCredentials; 100 | } else { 101 | $combinations = [ 102 | 'api_key + api_secret', 103 | 'api_key + signature_secret', 104 | 'private_key + application_id', 105 | 'api_key + api_secret + private_key + application_id', 106 | 'api_key + signature_secret + private_key + application_id', 107 | ]; 108 | 109 | throw new RuntimeException( 110 | 'Please provide your Vonage API credentials. Possible combinations: ' 111 | .join(', ', $combinations) 112 | ); 113 | } 114 | 115 | return new Client($credentials, $this->config, $this->client); 116 | } 117 | 118 | /** 119 | * Load the private key contents from the root directory of the application. 120 | * 121 | * @return string 122 | */ 123 | protected function loadPrivateKey($key) 124 | { 125 | if (Str::startsWith($key, '-----BEGIN PRIVATE KEY-----')) { 126 | return $key; 127 | } 128 | 129 | if (! Str::startsWith($key, '/')) { 130 | $key = base_path($key); 131 | } 132 | 133 | return trim(file_get_contents($key)); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/VonageChannelServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__.'/../config/vonage.php', 'vonage'); 21 | 22 | $this->app->singleton(Client::class, function ($app) { 23 | $config = $app['config']['vonage']; 24 | 25 | $httpClient = null; 26 | 27 | if ($httpClient = $config['http_client'] ?? null) { 28 | $httpClient = $app->make($httpClient); 29 | } elseif (! class_exists('GuzzleHttp\Client')) { 30 | throw new RuntimeException( 31 | 'The Vonage client requires a "psr/http-client-implementation" class such as Guzzle.' 32 | ); 33 | } 34 | 35 | return Vonage::make($app['config']['vonage'], $httpClient)->client(); 36 | }); 37 | 38 | $this->app->bind(VonageSmsChannel::class, function ($app) { 39 | return new VonageSmsChannel( 40 | $app->make(Client::class), 41 | $app['config']['vonage.sms_from'] 42 | ); 43 | }); 44 | 45 | Notification::resolved(function (ChannelManager $service) { 46 | $service->extend('vonage', function ($app) { 47 | return $app->make(VonageSmsChannel::class); 48 | }); 49 | }); 50 | } 51 | 52 | /** 53 | * Bootstrap the application services. 54 | * 55 | * @return void 56 | */ 57 | public function boot() 58 | { 59 | if ($this->app->runningInConsole()) { 60 | $this->publishes([ 61 | __DIR__.'/../config/vonage.php' => $this->app->configPath('vonage.php'), 62 | ], 'vonage'); 63 | } 64 | } 65 | } 66 | --------------------------------------------------------------------------------