diff --git a/src/Utopia/Messaging/Adapter/SMS/Vonage.php b/src/Utopia/Messaging/Adapter/SMS/VonageLegacy.php similarity index 96% rename from src/Utopia/Messaging/Adapter/SMS/Vonage.php rename to src/Utopia/Messaging/Adapter/SMS/VonageLegacy.php index d7644386..5e75618f 100644 --- a/src/Utopia/Messaging/Adapter/SMS/Vonage.php +++ b/src/Utopia/Messaging/Adapter/SMS/VonageLegacy.php @@ -9,9 +9,9 @@ use Utopia\Messaging\Messages\SMS; use Utopia\Messaging\Response; -class Vonage extends SMSAdapter +class VonageLegacy extends SMSAdapter { - protected const NAME = 'Vonage'; + protected const NAME = 'Vonage Legacy'; /** * @param string $apiKey Vonage API Key diff --git a/src/Utopia/Messaging/Adapter/SMS/VonageMessages.php b/src/Utopia/Messaging/Adapter/SMS/VonageMessages.php new file mode 100644 index 00000000..c02e940c --- /dev/null +++ b/src/Utopia/Messaging/Adapter/SMS/VonageMessages.php @@ -0,0 +1,110 @@ +apiKey}:{$this->apiSecret}"); + } + + /** + * @return array + */ + protected function getRequestHeaders(): array + { + return [ + "Authorization: {$this->getAuthorizationHeader()}", + 'Content-Type: application/json', + 'Accept: application/json', + 'User-Agent: Utopia Messaging', + ]; + } + + // Get adapter name. + public function getName(): string + { + return static::NAME; + } + + // Get max messages per request. + public function getMaxMessagesPerRequest(): int + { + return 1; + } + + protected function process(SMSMessage $message): array + { + $to = \ltrim($message->getTo()[0], '+'); + $from = $this->from ?? $message->getFrom(); + $from = $from !== null ? \ltrim($from, '+') : null; + + $response = new Response($this->getType()); + + if (empty($from)) { + $response->addResult($message->getTo()[0], 'The "from" field is required for the Vonage Messages API.'); + return $response->toArray(); + } + + $result = $this->request( + method: 'POST', + url: $this->getApiEndpoint(), + headers: $this->getRequestHeaders(), + body: [ + 'message_type' => 'text', + 'to' => $to, + 'from' => $from, + 'text' => $message->getContent(), + 'channel' => 'sms', + ], + ); + + if ($result['statusCode'] === 202) { + $response->setDeliveredTo(1); + $response->addResult($message->getTo()[0]); + } else { + $errorMessage = "Error {$result['statusCode']}"; + + if (\is_array($result['response'])) { + if (isset($result['response']['detail'])) { + $errorMessage = $result['response']['detail']; + } elseif (isset($result['response']['title'])) { + $errorMessage = $result['response']['title']; + } + } elseif (!empty($result['error'])) { + $errorMessage = $result['error']; + } elseif (\is_string($result['response']) && !empty($result['response'])) { + $errorMessage = "Error {$result['statusCode']}: " . \mb_strimwidth(\strip_tags($result['response']), 0, 100, '...'); + } + + $response->addResult($message->getTo()[0], $errorMessage); + } + + return $response->toArray(); + } +} diff --git a/tests/Messaging/Adapter/SMS/VonageTest.php b/tests/Messaging/Adapter/SMS/VonageLegacyTest.php similarity index 82% rename from tests/Messaging/Adapter/SMS/VonageTest.php rename to tests/Messaging/Adapter/SMS/VonageLegacyTest.php index 67027be9..0da5dde9 100644 --- a/tests/Messaging/Adapter/SMS/VonageTest.php +++ b/tests/Messaging/Adapter/SMS/VonageLegacyTest.php @@ -2,9 +2,10 @@ namespace Utopia\Tests\Adapter\SMS; +use Utopia\Messaging\Adapter\SMS\VonageLegacy; use Utopia\Tests\Adapter\Base; -class VonageTest extends Base +class VonageLegacyTest extends Base { /** * @throws \Exception @@ -17,7 +18,7 @@ public function testSendSMS(): void $apiKey = \getenv('VONAGE_API_KEY'); $apiSecret = \getenv('VONAGE_API_SECRET'); - $sender = new Vonage($apiKey, $apiSecret); + $sender = new VonageLegacy($apiKey, $apiSecret); $message = new SMS( to: [\getenv('VONAGE_TO')], diff --git a/tests/Messaging/Adapter/SMS/VonageMessagesTest.php b/tests/Messaging/Adapter/SMS/VonageMessagesTest.php new file mode 100644 index 00000000..6711ed2f --- /dev/null +++ b/tests/Messaging/Adapter/SMS/VonageMessagesTest.php @@ -0,0 +1,65 @@ +markTestSkipped('Vonage Messages credentials or recipient are not available.'); + } + + $sender = new VonageMessages( + apiKey: $apiKey, + apiSecret: $apiSecret, + from: \getenv('VONAGE_FROM') ?: 'Vonage', + ); + + $message = new SMS( + to: [$to], + content: 'Test Content', + ); + + $response = $sender->send($message); + + $this->assertResponse($response); + } + + // Tests sending an SMS where the 'from' number is provided by the message object. + public function testSendSMSWithFallbackFrom(): void + { + $apiKey = \getenv('VONAGE_API_KEY'); + $apiSecret = \getenv('VONAGE_API_SECRET'); + $to = \getenv('VONAGE_TO'); + $from = \getenv('VONAGE_FROM') ?: null; + + if (!$apiKey || !$apiSecret || !$to || !$from) { + $this->markTestSkipped('Vonage Messages credentials or sender/recipient are not available.'); + } + + $sender = new VonageMessages( + apiKey: $apiKey, + apiSecret: $apiSecret, + ); + + $message = new SMS( + to: [$to], + content: 'Test Content', + from: $from, + ); + + $response = $sender->send($message); + + $this->assertResponse($response); + } +}