Skip to content

Commit 2af52bc

Browse files
feat: add Vonage Messages API adapter
1 parent fcb4c3c commit 2af52bc

3 files changed

Lines changed: 172 additions & 0 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
namespace Utopia\Messaging\Adapter\SMS;
4+
5+
use Utopia\Messaging\Adapter\SMS as SMSAdapter;
6+
use Utopia\Messaging\Adapter\VonageMessagesBase;
7+
use Utopia\Messaging\Messages\SMS as SMSMessage;
8+
use Utopia\Messaging\Response;
9+
10+
/**
11+
* Vonage Messages API SMS Adapter.
12+
*
13+
* Uses the newer Vonage Messages API (V1) instead of the older SMS API.
14+
* The Messages API is cheaper and supports multiple message types.
15+
*
16+
* Reference: https://developer.vonage.com/en/api/messages
17+
*/
18+
class VonageMessages extends SMSAdapter
19+
{
20+
use VonageMessagesBase;
21+
22+
protected const NAME = 'Vonage Messages';
23+
24+
/**
25+
* @param string $apiKey Vonage API Key
26+
* @param string $apiSecret Vonage API Secret
27+
*/
28+
public function __construct(
29+
/** @phpstan-ignore property.onlyWritten */
30+
private string $apiKey,
31+
/** @phpstan-ignore property.onlyWritten */
32+
private string $apiSecret,
33+
private ?string $from = null
34+
) {
35+
}
36+
37+
public function getName(): string
38+
{
39+
return static::NAME;
40+
}
41+
42+
public function getMaxMessagesPerRequest(): int
43+
{
44+
return 1;
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
protected function process(SMSMessage $message): array
51+
{
52+
$to = \ltrim($message->getTo()[0], '+');
53+
$from = $this->from ?? $message->getFrom();
54+
55+
$response = new Response($this->getType());
56+
57+
$result = $this->request(
58+
method: 'POST',
59+
url: $this->getApiEndpoint(),
60+
headers: $this->getRequestHeaders(),
61+
body: [
62+
'message_type' => 'text',
63+
'to' => $to,
64+
'from' => $from,
65+
'text' => $message->getContent(),
66+
'channel' => 'sms',
67+
],
68+
);
69+
70+
if ($result['statusCode'] === 202) {
71+
$response->setDeliveredTo(1);
72+
$response->addResult($message->getTo()[0]);
73+
} else {
74+
$errorMessage = 'Unknown error';
75+
if (isset($result['response']['detail'])) {
76+
$errorMessage = $result['response']['detail'];
77+
} elseif (isset($result['response']['title'])) {
78+
$errorMessage = $result['response']['title'];
79+
} elseif (!empty($result['error'])) {
80+
$errorMessage = $result['error'];
81+
}
82+
83+
$response->addResult($message->getTo()[0], $errorMessage);
84+
}
85+
86+
return $response->toArray();
87+
}
88+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
namespace Utopia\Messaging\Adapter;
4+
5+
/**
6+
* Trait for Vonage Messages API adapters.
7+
*
8+
* Provides common functionality for adapters using the Vonage Messages API (V1).
9+
* This trait can be used by different message type adapters (SMS, Chat, etc.).
10+
*
11+
* Required properties (from extending class):
12+
* - $apiKey: string
13+
* - $apiSecret: string
14+
*
15+
* Reference: https://developer.vonage.com/en/api/messages
16+
*/
17+
trait VonageMessagesBase
18+
{
19+
/**
20+
* Get the API endpoint for the Vonage Messages API.
21+
*/
22+
protected function getApiEndpoint(): string
23+
{
24+
return 'https://api.nexmo.com/v1/messages';
25+
}
26+
27+
protected function getAuthorizationHeader(): string
28+
{
29+
return 'Basic ' . \base64_encode("{$this->apiKey}:{$this->apiSecret}");
30+
}
31+
32+
/**
33+
* Build the request headers for the Messages API.
34+
*
35+
* @return array<string>
36+
*/
37+
protected function getRequestHeaders(): array
38+
{
39+
return [
40+
"Authorization: {$this->getAuthorizationHeader()}",
41+
'Content-Type: application/json',
42+
'Accept: application/json',
43+
];
44+
}
45+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Utopia\Tests\Adapter\SMS;
4+
5+
use Utopia\Messaging\Adapter\SMS\VonageMessages;
6+
use Utopia\Messaging\Messages\SMS;
7+
use Utopia\Tests\Adapter\Base;
8+
9+
class VonageMessagesTest extends Base
10+
{
11+
/**
12+
* @throws \Exception
13+
*/
14+
public function testSendSMS(): void
15+
{
16+
$apiKey = \getenv('VONAGE_API_KEY');
17+
$apiSecret = \getenv('VONAGE_API_SECRET');
18+
19+
if (!$apiKey || !$apiSecret) {
20+
$this->markTestSkipped('Vonage Messages credentials are not available.');
21+
}
22+
23+
$sender = new VonageMessages(
24+
apiKey: $apiKey,
25+
apiSecret: $apiSecret,
26+
from: \getenv('VONAGE_FROM') ?: 'Vonage',
27+
);
28+
29+
$message = new SMS(
30+
to: [\getenv('VONAGE_TO')],
31+
content: 'Test Content',
32+
from: \getenv('VONAGE_FROM')
33+
);
34+
35+
$response = $sender->send($message);
36+
37+
$this->assertResponse($response);
38+
}
39+
}

0 commit comments

Comments
 (0)