Skip to content

Commit 93e1dc1

Browse files
Merge pull request #21 from marventhieme/feature/encoding-detection
Encoding detection, support for non-latin characters in SMS
2 parents 2deeb96 + 7dfccb6 commit 93e1dc1

File tree

9 files changed

+248
-82
lines changed

9 files changed

+248
-82
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.idea/
12
/vendor
23
build
34
composer.phar

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22

33
All notable changes to `CMSMS` will be documented in this file
44

5+
## [4.0.0] - 2025-02-??
6+
#### Changed
7+
- Moved from XML to JSON for the request body
8+
- Changed CM endpoint
9+
#### Added
10+
- Added config value for encoding detection type
11+
- Two events for success and failure: `SMSSentSuccessfullyEvent` and `SMSSendingFailedEvent`
12+
#### Removed
13+
- Removed `tariff` support
14+
15+
## [3.3.0] - 2024-03-22
16+
#### Added
17+
- Laravel 11 support
18+
19+
## [3.3.0] - 2024-03-22
20+
#### Changed
21+
- Update CM endpoint by @marventhieme
22+
523
## [3.2.0] - 2023-03-29
624
#### Changed
725
- Added support for Laravel 10.0 (#17) by @charleskoko

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,14 @@ public function routeNotificationForCmsms()
102102
- `body('')`: Accepts a string value for the message body.
103103
- `originator('')`: Accepts a string value between 1 and 11 characters, used as the message sender name.
104104
- `reference('')`: Accepts a string value for your message reference. This information will be returned in a status report so you can match the message and it's status. Restrictions: 1 - 32 alphanumeric characters. Reference will not work for demo accounts.
105-
- `tariff()`: Accepts a integer value for your message tariff. The unit is eurocent. Requires the `originator` to be set to a specific value. Contact CM for this tariff value. CM also must enable this feature for your contract manually.
105+
- `encodingDetectionType('')`: Read about encoding detection here: https://developers.cm.com/messaging/docs/sms#auto-detect-encoding
106106
- `multipart($minimum, $maximum)`: Accepts a 0 to 8 integer range which allows multipart messages. See the [documentation from CM](https://dashboard.onlinesmsgateway.com/docs#send-a-message-multipart) for more information.
107107

108+
### Available events
109+
- `SMSSentSuccessfullyEvent`: This event will be fired after the message was sent. The event will contain the payload we have sent to CM.
110+
- `SMSSendingFailedEvent`: This event will be fired if the message was not sent. The event will contain the response body we received from CM.
111+
112+
108113
## Changelog
109114

110115
Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.

src/CmsmsClient.php

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,102 @@
55
namespace NotificationChannels\Cmsms;
66

77
use GuzzleHttp\Client as GuzzleClient;
8+
use GuzzleHttp\Exception\GuzzleException;
89
use Illuminate\Support\Arr;
10+
use NotificationChannels\Cmsms\Events\SMSSendingFailedEvent;
11+
use NotificationChannels\Cmsms\Events\SMSSentSuccessfullyEvent;
912
use NotificationChannels\Cmsms\Exceptions\CouldNotSendNotification;
10-
use SimpleXMLElement;
13+
use NotificationChannels\Cmsms\Exceptions\InvalidMessage;
1114

1215
class CmsmsClient
1316
{
14-
public const GATEWAY_URL = 'https://gw.messaging.cm.com/gateway.ashx';
17+
public const GATEWAY_URL = 'https://gw.cmtelecom.com/v1.0/message';
1518

1619
public function __construct(
1720
protected GuzzleClient $client,
1821
protected string $productToken,
1922
) {
2023
}
2124

25+
/**
26+
* @throws InvalidMessage
27+
* @throws GuzzleException
28+
* @throws CouldNotSendNotification
29+
*/
2230
public function send(CmsmsMessage $message, string $recipient): void
2331
{
24-
if (is_null(Arr::get($message->toXmlArray(), 'FROM'))) {
32+
if (empty($message->getOriginator())) {
2533
$message->originator(config('services.cmsms.originator'));
2634
}
2735

36+
$payload = $this->buildMessageJson($message, $recipient);
37+
2838
$response = $this->client->request('POST', static::GATEWAY_URL, [
29-
'body' => $this->buildMessageXml($message, $recipient),
39+
'body' => $payload,
3040
'headers' => [
31-
'Content-Type' => 'application/xml',
41+
'accept' => 'application/json',
42+
'content-type' => 'application/json',
3243
],
3344
]);
3445

35-
// API returns an empty string on success
36-
// On failure, only the error string is passed
46+
/**
47+
* If error code is 0, the message was sent successfully.
48+
*/
3749
$body = $response->getBody()->getContents();
38-
if (! empty($body)) {
50+
$errorCode = Arr::get(json_decode($body, true), 'errorCode');
51+
if ((int) $errorCode !== 0) {
52+
SMSSendingFailedEvent::dispatch($body);
53+
3954
throw CouldNotSendNotification::serviceRespondedWithAnError($body);
4055
}
56+
57+
SMSSentSuccessfullyEvent::dispatch($payload);
4158
}
4259

43-
public function buildMessageXml(CmsmsMessage $message, string $recipient): string
60+
/**
61+
* See: https://developers.cm.com/messaging/reference/messages_sendmessage-1
62+
*/
63+
public function buildMessageJson(CmsmsMessage $message, string $recipient): string
4464
{
45-
$xml = new SimpleXMLElement('<MESSAGES/>');
46-
47-
$xml->addChild('AUTHENTICATION')
48-
->addChild('PRODUCTTOKEN', $this->productToken);
65+
$body = [];
66+
$body['content'] = $message->getBody();
67+
if (strtoupper($message->getEncodingDetectionType()) === 'AUTO') {
68+
$body['type'] = 'AUTO';
69+
}
4970

50-
if ($tariff = $message->getTariff()) {
51-
$xml->addChild('TARIFF', (string) $tariff);
71+
$minimumNumberOfMessageParts = [];
72+
if ($message->getMinimumNumberOfMessageParts() !== null) {
73+
$minimumNumberOfMessageParts['minimumNumberOfMessageParts'] = $message->getMinimumNumberOfMessageParts();
74+
}
75+
$maximumNumberOfMessageParts = [];
76+
if ($message->getMaximumNumberOfMessageParts() !== null) {
77+
$maximumNumberOfMessageParts['maximumNumberOfMessageParts'] = $message->getMaximumNumberOfMessageParts();
5278
}
5379

54-
$msg = $xml->addChild('MSG');
55-
foreach ($message->toXmlArray() as $name => $value) {
56-
$msg->addChild($name, (string) $value);
80+
$reference = [];
81+
if ($message->getReference() !== null) {
82+
$reference['reference'] = $message->getReference();
5783
}
58-
$msg->addChild('TO', $recipient);
5984

60-
return $xml->asXML();
85+
$json = [
86+
'messages' => [
87+
'authentication' => [
88+
'productToken' => $this->productToken,
89+
],
90+
'msg' => [[
91+
'body' => $body,
92+
'to' => [[
93+
'number' => $recipient,
94+
]],
95+
'dcs' => strtoupper($message->getEncodingDetectionType()) === 'AUTO' ? '0' : $message->getEncodingDetectionType(),
96+
'from' => $message->getOriginator(),
97+
...$minimumNumberOfMessageParts,
98+
...$maximumNumberOfMessageParts,
99+
...$reference,
100+
]],
101+
],
102+
];
103+
104+
return json_encode($json);
61105
}
62106
}

src/CmsmsMessage.php

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class CmsmsMessage
1212

1313
protected string $reference = '';
1414

15-
protected int $tariff = 0;
15+
protected string $encodingDetectionType = 'AUTO';
1616

1717
protected ?int $minimumNumberOfMessageParts = null;
1818

@@ -31,6 +31,11 @@ public function body(string $body): self
3131
return $this;
3232
}
3333

34+
public function getBody(): string
35+
{
36+
return $this->body;
37+
}
38+
3439
public function originator(string|int $originator): self
3540
{
3641
if (empty($originator) || strlen($originator) > 11) {
@@ -42,6 +47,11 @@ public function originator(string|int $originator): self
4247
return $this;
4348
}
4449

50+
public function getOriginator(): string
51+
{
52+
return $this->originator;
53+
}
54+
4555
public function reference(string $reference): self
4656
{
4757
if (empty($reference) || strlen($reference) > 32 || ! ctype_alnum($reference)) {
@@ -53,16 +63,9 @@ public function reference(string $reference): self
5363
return $this;
5464
}
5565

56-
public function tariff(int $tariff): self
57-
{
58-
$this->tariff = $tariff;
59-
60-
return $this;
61-
}
62-
63-
public function getTariff(): int
66+
public function getReference(): string
6467
{
65-
return $this->tariff;
68+
return $this->reference;
6669
}
6770

6871
public function multipart(int $minimum, int $maximum): self
@@ -77,15 +80,26 @@ public function multipart(int $minimum, int $maximum): self
7780
return $this;
7881
}
7982

80-
public function toXmlArray(): array
83+
public function getMinimumNumberOfMessageParts(): ?int
84+
{
85+
return $this->minimumNumberOfMessageParts;
86+
}
87+
88+
public function getMaximumNumberOfMessageParts(): ?int
89+
{
90+
return $this->maximumNumberOfMessageParts;
91+
}
92+
93+
public function encodingDetectionType(string|int $encodingDetectionType): self
94+
{
95+
$this->encodingDetectionType = (string) $encodingDetectionType;
96+
97+
return $this;
98+
}
99+
100+
public function getEncodingDetectionType(): string
81101
{
82-
return array_filter([
83-
'BODY' => $this->body,
84-
'FROM' => $this->originator,
85-
'REFERENCE' => $this->reference,
86-
'MINIMUMNUMBEROFMESSAGEPARTS' => $this->minimumNumberOfMessageParts,
87-
'MAXIMUMNUMBEROFMESSAGEPARTS' => $this->maximumNumberOfMessageParts,
88-
]);
102+
return $this->encodingDetectionType;
89103
}
90104

91105
public static function create(string $body = ''): self
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace NotificationChannels\Cmsms\Events;
4+
5+
use Illuminate\Foundation\Events\Dispatchable;
6+
7+
class SMSSendingFailedEvent
8+
{
9+
use Dispatchable;
10+
11+
public function __construct(public string $response)
12+
{
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace NotificationChannels\Cmsms\Events;
4+
5+
use Illuminate\Foundation\Events\Dispatchable;
6+
7+
class SMSSentSuccessfullyEvent
8+
{
9+
use Dispatchable;
10+
11+
public function __construct(public string $payload)
12+
{
13+
}
14+
}

0 commit comments

Comments
 (0)