diff --git a/.editorconfig b/.editorconfig
index 9d52482..a2a4e25 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -24,7 +24,7 @@ dotnet_sort_system_directives_first = true
dotnet_separate_import_directive_groups = false
# this. preferences
-dotnet_style_qualification_for_field = true
+dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c23cb6..4f7aca9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2.8.0] - 2024-01-08
+### Added
+- Implementing OTP Request and verify
+
## [2.7.0] - 2023-06-26
### Added
- Whatsapp Multi-Product Template Messages
diff --git a/CM.Text.Tests/CM.Text.Tests.csproj b/CM.Text.Tests/CM.Text.Tests.csproj
index 218649b..76e45a1 100644
--- a/CM.Text.Tests/CM.Text.Tests.csproj
+++ b/CM.Text.Tests/CM.Text.Tests.csproj
@@ -9,11 +9,14 @@
-
-
-
-
-
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/CM.Text/BusinessMessaging/CarouselBuilder.cs b/CM.Text/BusinessMessaging/CarouselBuilder.cs
index 5c43d3c..5431e1f 100644
--- a/CM.Text/BusinessMessaging/CarouselBuilder.cs
+++ b/CM.Text/BusinessMessaging/CarouselBuilder.cs
@@ -19,7 +19,7 @@ public class CarouselBuilder
///
public CarouselBuilder(CarouselCardWidth carouselCardWidth)
{
- this._carouselCardWidth = carouselCardWidth;
+ _carouselCardWidth = carouselCardWidth;
}
///
@@ -29,7 +29,7 @@ public CarouselBuilder(CarouselCardWidth carouselCardWidth)
///
public CarouselBuilder AddCard(RichCard card)
{
- this._cards.Add(card);
+ _cards.Add(card);
return this;
}
@@ -41,7 +41,7 @@ public CarouselMessage Build()
{
return new CarouselMessage
{
- Carousel = new Carousel {Cards = this._cards.ToArray(), CarouselCardWidth = this._carouselCardWidth}
+ Carousel = new Carousel {Cards = _cards.ToArray(), CarouselCardWidth = _carouselCardWidth}
};
}
}
diff --git a/CM.Text/BusinessMessaging/MessageBuilder.cs b/CM.Text/BusinessMessaging/MessageBuilder.cs
index e3b6839..9a96f17 100644
--- a/CM.Text/BusinessMessaging/MessageBuilder.cs
+++ b/CM.Text/BusinessMessaging/MessageBuilder.cs
@@ -24,7 +24,7 @@ public class MessageBuilder
///
public MessageBuilder(string messageText, string from, params string[] to)
{
- this._message = new Message
+ _message = new Message
{
Body = new Body
{
@@ -44,8 +44,8 @@ public MessageBuilder(string messageText, string from, params string[] to)
///
public Message Build()
{
- this._message.RichContent = this._richContent;
- return this._message;
+ _message.RichContent = _richContent;
+ return _message;
}
///
@@ -59,7 +59,7 @@ public Message Build()
///
public MessageBuilder WithAllowedChannels(params Channel[] channels)
{
- this._message.AllowedChannels = channels;
+ _message.AllowedChannels = channels;
return this;
}
@@ -70,7 +70,7 @@ public MessageBuilder WithAllowedChannels(params Channel[] channels)
///
public MessageBuilder WithReference(string reference)
{
- this._message.Reference = reference;
+ _message.Reference = reference;
return this;
}
@@ -97,7 +97,7 @@ public MessageBuilder WithReference(string reference)
///
public MessageBuilder WithValidityPeriod(string period)
{
- this._message.Validity = period;
+ _message.Validity = period;
return this;
}
@@ -110,10 +110,10 @@ public MessageBuilder WithValidityPeriod(string period)
///
public MessageBuilder WithRichMessage(IRichMessage richMessage)
{
- if (this._richContent == null)
- this._richContent = new RichContent();
+ if (_richContent == null)
+ _richContent = new RichContent();
- this._richContent.AddConversationPart(richMessage);
+ _richContent.AddConversationPart(richMessage);
return this;
}
@@ -125,10 +125,10 @@ public MessageBuilder WithRichMessage(IRichMessage richMessage)
///
public MessageBuilder WithSuggestions(params SuggestionBase[] suggestions)
{
- if (this._richContent == null)
- this._richContent = new RichContent();
+ if (_richContent == null)
+ _richContent = new RichContent();
- this._richContent.Suggestions = suggestions;
+ _richContent.Suggestions = suggestions;
return this;
}
@@ -138,7 +138,7 @@ public MessageBuilder WithSuggestions(params SuggestionBase[] suggestions)
///
public MessageBuilder WitHybridAppKey(Guid appKey)
{
- this._message.HybridAppKey = appKey;
+ _message.HybridAppKey = appKey;
return this;
}
@@ -150,10 +150,10 @@ public MessageBuilder WitHybridAppKey(Guid appKey)
///
public MessageBuilder WithTemplate(TemplateMessage template)
{
- if (this._richContent == null)
- this._richContent = new RichContent();
+ if (_richContent == null)
+ _richContent = new RichContent();
- this._richContent.AddConversationPart(template);
+ _richContent.AddConversationPart(template);
return this;
}
@@ -164,10 +164,10 @@ public MessageBuilder WithTemplate(TemplateMessage template)
///
public MessageBuilder WithInteractive(WhatsAppInteractiveMessage interactive)
{
- if (this._richContent == null)
- this._richContent = new RichContent();
+ if (_richContent == null)
+ _richContent = new RichContent();
- this._richContent.AddConversationPart(interactive);
+ _richContent.AddConversationPart(interactive);
return this;
}
@@ -178,10 +178,10 @@ public MessageBuilder WithInteractive(WhatsAppInteractiveMessage interactive)
///
public MessageBuilder WithApplePay(ApplePayRequest applePayRequest)
{
- if (this._richContent == null)
- this._richContent = new RichContent();
+ if (_richContent == null)
+ _richContent = new RichContent();
- this._richContent.AddConversationPart(applePayRequest);
+ _richContent.AddConversationPart(applePayRequest);
return this;
}
}
diff --git a/CM.Text/BusinessMessaging/Model/MultiChannel/MediaContent.cs b/CM.Text/BusinessMessaging/Model/MultiChannel/MediaContent.cs
index 8a43784..599c54d 100644
--- a/CM.Text/BusinessMessaging/Model/MultiChannel/MediaContent.cs
+++ b/CM.Text/BusinessMessaging/Model/MultiChannel/MediaContent.cs
@@ -24,9 +24,9 @@ public MediaContent()
///
public MediaContent(string mediaName, string mediaUri, string mimeType)
{
- this.MediaName = mediaName;
- this.MediaUri = mediaUri;
- this.MimeType = mimeType;
+ MediaName = mediaName;
+ MediaUri = mediaUri;
+ MimeType = mimeType;
}
///
diff --git a/CM.Text/BusinessMessaging/Model/MultiChannel/MediaMessage.cs b/CM.Text/BusinessMessaging/Model/MultiChannel/MediaMessage.cs
index 6a14e04..b4aed6d 100644
--- a/CM.Text/BusinessMessaging/Model/MultiChannel/MediaMessage.cs
+++ b/CM.Text/BusinessMessaging/Model/MultiChannel/MediaMessage.cs
@@ -25,7 +25,7 @@ public MediaMessage()
///
public MediaMessage(string mediaName, string mediaUri, string mimeType)
{
- this.Media = new MediaContent(mediaName, mediaUri, mimeType);
+ Media = new MediaContent(mediaName, mediaUri, mimeType);
}
///
diff --git a/CM.Text/BusinessMessaging/Model/MultiChannel/RichContent.cs b/CM.Text/BusinessMessaging/Model/MultiChannel/RichContent.cs
index 3018429..01b6ff9 100644
--- a/CM.Text/BusinessMessaging/Model/MultiChannel/RichContent.cs
+++ b/CM.Text/BusinessMessaging/Model/MultiChannel/RichContent.cs
@@ -16,8 +16,8 @@ public class RichContent
///
public RichContent()
{
- this.Conversation = null;
- this.Suggestions = null;
+ Conversation = null;
+ Suggestions = null;
}
///
@@ -38,14 +38,14 @@ public RichContent()
///
public void AddConversationPart(IRichMessage part)
{
- if (this.Conversation == null)
- this.Conversation = new[] {part};
+ if (Conversation == null)
+ Conversation = new[] {part};
else
{
- var newArr = this.Conversation;
- Array.Resize(ref newArr, this.Conversation.Length + 1);
+ var newArr = Conversation;
+ Array.Resize(ref newArr, Conversation.Length + 1);
newArr[newArr.Length - 1] = part;
- this.Conversation = newArr;
+ Conversation = newArr;
}
}
@@ -55,14 +55,14 @@ public void AddConversationPart(IRichMessage part)
///
public void AddSuggestion(SuggestionBase suggestion)
{
- if (this.Suggestions == null)
- this.Suggestions = new[] {suggestion};
+ if (Suggestions == null)
+ Suggestions = new[] {suggestion};
else
{
- var newArr = this.Suggestions;
- Array.Resize(ref newArr, this.Suggestions.Length + 1);
+ var newArr = Suggestions;
+ Array.Resize(ref newArr, Suggestions.Length + 1);
newArr[newArr.Length - 1] = suggestion;
- this.Suggestions = newArr;
+ Suggestions = newArr;
}
}
}
diff --git a/CM.Text/BusinessMessaging/Model/MultiChannel/TextMessage.cs b/CM.Text/BusinessMessaging/Model/MultiChannel/TextMessage.cs
index 2490384..5287a68 100644
--- a/CM.Text/BusinessMessaging/Model/MultiChannel/TextMessage.cs
+++ b/CM.Text/BusinessMessaging/Model/MultiChannel/TextMessage.cs
@@ -23,7 +23,7 @@ public TextMessage()
///
public TextMessage(string text)
{
- this.Text = text;
+ Text = text;
}
///
diff --git a/CM.Text/CM.Text.csproj b/CM.Text/CM.Text.csproj
index 91933e3..ce4b5ac 100644
--- a/CM.Text/CM.Text.csproj
+++ b/CM.Text/CM.Text.csproj
@@ -13,12 +13,12 @@
LICENSE
icon.png
$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/../CHANGELOG.md"))
- 2.7.0
+ 2.8.0
https://github.com/cmdotcom/text-sdk-dotnet
en
true
- 2.7.0
- 2.7.0
+ 2.8.0
+ 2.8.0
True
@@ -63,7 +63,7 @@
-
+
diff --git a/CM.Text/Common/Constant.cs b/CM.Text/Common/Constant.cs
index 14e53d2..4800299 100644
--- a/CM.Text/Common/Constant.cs
+++ b/CM.Text/Common/Constant.cs
@@ -5,6 +5,10 @@ internal static class Constant
internal static readonly string TextSdkReference = $"text-sdk-dotnet-{typeof(TextClient).Assembly.GetName().Version}";
internal const string BusinessMessagingGatewayJsonEndpoint = "https://gw.cmtelecom.com/v1.0/message";
+
+ internal const string OtpRequestEndpoint = "https://api.cm.com/otp/v2/otp";
+ internal const string OtpVerifyEndpointFormatter = "https://api.cm.com/otp/v2/otp/{0}/verify";
+
internal static readonly string BusinessMessagingGatewayMediaTypeJson = "application/json";
internal static readonly string BusinessMessagingBodyTypeAuto = "AUTO";
internal static readonly int BusinessMessagingMessagePartsMinDefault = 1;
diff --git a/CM.Text/Identity/OtpRequest.cs b/CM.Text/Identity/OtpRequest.cs
new file mode 100644
index 0000000..14c8b05
--- /dev/null
+++ b/CM.Text/Identity/OtpRequest.cs
@@ -0,0 +1,83 @@
+using System.Text.Json.Serialization;
+using JetBrains.Annotations;
+
+namespace CM.Text.Identity
+{
+ ///
+ /// A request to send an OTP towards an end-user.
+ ///
+ [PublicAPI]
+ public class OtpRequest
+ {
+ ///
+ /// Required: This is the sender name.
+ /// The maximum length is 11 alphanumerical characters or 16 digits. Example: 'MyCompany'
+ ///
+ [JsonPropertyName("from")]
+ public string From { get; set; }
+
+ ///
+ /// Required: The destination mobile numbers.
+ /// This value should be in international format.
+ /// A single mobile number per request. Example: '00447911123456'
+ ///
+ [JsonPropertyName("to")]
+ public string To { get; set; }
+
+ ///
+ /// The length of the code (min 4, max 10). default: 5.
+ ///
+ [JsonPropertyName("digits")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public int? Digits { get; set; }
+
+ ///
+ /// The expiry in seconds (min 10, max 3600). default: 60 seconds.
+ ///
+ [JsonPropertyName("expiry")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public int? Expiry { get; set; }
+
+ ///
+ /// The channel to send the code.
+ /// Supported values: auto, sms, push, whatsapp, voice, email.
+ /// Channel auto is only available with a SOLiD subscription.
+ ///
+ [JsonPropertyName("channel")]
+ public string Channel { get; set; } = "sms";
+
+ ///
+ /// The locale, for WhatsApp supported values: en, nl, fr, de, it, es.
+ /// Default: en
+ ///
+ /// For Voice: the spoken language in the voice call,
+ /// supported values: de-DE, en-AU, en-GB, en-IN, en-US, es-ES, fr-CA, fr-FR, it-IT, ja-JP, nl-NL
+ /// Default: en-GB.
+ ///
+ /// For Email: The locale for the email template.
+ ///
+ [JsonPropertyName("locale")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [CanBeNull]
+ public string Locale { get; set; }
+
+ ///
+ /// The app key, when is 'push'
+ ///
+ [JsonPropertyName("pushAppKey")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [CanBeNull]
+ public string PushAppKey { get; set; }
+
+ ///
+ /// For WhatsApp, set a custom message. You can use the placeholder {code}, this will be replaced by the actual code.
+ /// Example: Your code is: {code}. This is only used as a fallback in case the message could not be delivered via WhatsApp.
+ ///
+ /// For email, Set a custom message to be used in the email message. Do not include the {code} placeholder.
+ ///
+ [JsonPropertyName("message")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [CanBeNull]
+ public string Message { get; set; }
+ }
+}
diff --git a/CM.Text/Identity/OtpRequestBuilder.cs b/CM.Text/Identity/OtpRequestBuilder.cs
new file mode 100644
index 0000000..b15a16d
--- /dev/null
+++ b/CM.Text/Identity/OtpRequestBuilder.cs
@@ -0,0 +1,105 @@
+
+using JetBrains.Annotations;
+
+namespace CM.Text.Identity
+{
+ ///
+ /// Builder class to construct messages
+ ///
+ [PublicAPI]
+ public class OtpRequestBuilder
+ {
+ private readonly OtpRequest _otpRequest;
+
+ ///
+ /// Creates a new OtpRequestBuilder
+ ///
+ ///
+ ///
+ public OtpRequestBuilder(string from, string to)
+ {
+ _otpRequest = new OtpRequest { From = from, To = to };
+ }
+
+ ///
+ /// Constructs the request.
+ ///
+ ///
+ public OtpRequest Build()
+ {
+ return _otpRequest;
+ }
+
+ ///
+ /// Set the channel
+ ///
+ public OtpRequestBuilder WithChannel(string channel)
+ {
+ _otpRequest.Channel = channel;
+ return this;
+ }
+
+ ///
+ /// Sets The length of the code (min 4, max 10). default: 5.
+ ///
+ ///
+ ///
+ public OtpRequestBuilder WithDigits(int digits)
+ {
+ _otpRequest.Digits = digits;
+ return this;
+ }
+
+ ///
+ /// The expiry in seconds (min 10, max 3600). default: 60 seconds.
+ ///
+ public OtpRequestBuilder WithExpiry(int expiryInSeconds)
+ {
+ _otpRequest.Expiry = expiryInSeconds;
+ return this;
+ }
+
+ ///
+ /// The locale, for WhatsApp supported values: en, nl, fr, de, it, es.
+ /// Default: en
+ ///
+ /// For Voice: the spoken language in the voice call,
+ /// supported values: de-DE, en-AU, en-GB, en-IN, en-US, es-ES, fr-CA, fr-FR, it-IT, ja-JP, nl-NL
+ /// Default: en-GB.
+ ///
+ /// For Email: The locale for the email template.
+ ///
+ ///
+ ///
+ public OtpRequestBuilder WithLocale(string locale)
+ {
+ _otpRequest.Locale = locale;
+ return this;
+ }
+
+ ///
+ /// The app key, when the channel is 'push'
+ ///
+ ///
+ ///
+ public OtpRequestBuilder WithPushAppKey(string pushAppKey)
+ {
+ _otpRequest.PushAppKey = pushAppKey;
+ return this;
+ }
+
+ ///
+ /// For WhatsApp, set a custom message. You can use the placeholder {code}, this will be replaced by the actual code.
+ /// Example: Your code is: {code}. This is only used as a fallback in case the message could not be delivered via WhatsApp.
+ ///
+ /// For email, Set a custom message to be used in the email message. Do not include the {code} placeholder.
+ ///
+ ///
+ ///
+ public OtpRequestBuilder WithMessage(string message)
+ {
+ _otpRequest.Message = message;
+ return this;
+ }
+ }
+}
diff --git a/CM.Text/Identity/OtpResult.cs b/CM.Text/Identity/OtpResult.cs
new file mode 100644
index 0000000..966408d
--- /dev/null
+++ b/CM.Text/Identity/OtpResult.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Text.Json.Serialization;
+
+namespace CM.Text.Identity
+{
+ ///
+ /// The result of an OTP request.
+ ///
+ public class OtpResult
+ {
+ ///
+ /// The identifier of the OTP.
+ ///
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+ ///
+ /// The channel used to send the code.
+ ///
+ [JsonPropertyName("channel")]
+ public string Channel { get; set; }
+ ///
+ /// Indicates if the code was valid.
+ ///
+ [JsonPropertyName("verified")]
+ public bool Verified { get; set; }
+ ///
+ /// The date the OTP was created.
+ ///
+ [JsonPropertyName("createdAt")]
+ public DateTime CreatedAt { get; set; }
+ ///
+ /// The date the OTP will expire.
+ ///
+ [JsonPropertyName("expiresAt")]
+ public DateTime ExpiresAt { get; set; }
+ }
+}
diff --git a/CM.Text/TextClient.cs b/CM.Text/TextClient.cs
index a3afb47..d7d549c 100644
--- a/CM.Text/TextClient.cs
+++ b/CM.Text/TextClient.cs
@@ -3,11 +3,13 @@
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Text;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using CM.Text.BusinessMessaging;
using CM.Text.BusinessMessaging.Model;
using CM.Text.Common;
+using CM.Text.Identity;
using CM.Text.Interfaces;
using JetBrains.Annotations;
@@ -54,9 +56,9 @@ public TextClient(Guid apiKey, [CanBeNull] HttpClient httpClient): this(apiKey,
[PublicAPI]
public TextClient(Guid apiKey, [CanBeNull] HttpClient httpClient, [CanBeNull] Uri endPointOverride)
{
- this._apiKey = apiKey;
- this._httpClient = httpClient ?? ClientSingletonLazy.Value;
- this._endPointOverride = endPointOverride;
+ _apiKey = apiKey;
+ _httpClient = httpClient ?? ClientSingletonLazy.Value;
+ _endPointOverride = endPointOverride;
}
///
@@ -70,16 +72,16 @@ public async Task SendMessageAsync(
{
using (var request = new HttpRequestMessage(
HttpMethod.Post,
- this._endPointOverride ?? new Uri(Constant.BusinessMessagingGatewayJsonEndpoint)
+ _endPointOverride ?? new Uri(Constant.BusinessMessagingGatewayJsonEndpoint)
))
{
request.Content = new StringContent(
- BusinessMessagingApi.GetHttpPostBody(this._apiKey, messageText, from, to, reference),
+ BusinessMessagingApi.GetHttpPostBody(_apiKey, messageText, from, to, reference),
Encoding.UTF8,
Constant.BusinessMessagingGatewayMediaTypeJson
);
- using (var requestResult = await this._httpClient.SendAsync(request, cancellationToken)
+ using (var requestResult = await _httpClient.SendAsync(request, cancellationToken)
.ConfigureAwait(false))
{
cancellationToken.ThrowIfCancellationRequested();
@@ -105,16 +107,16 @@ public async Task SendMessageAsync(
{
using (var request = new HttpRequestMessage(
HttpMethod.Post,
- this._endPointOverride ?? new Uri(Constant.BusinessMessagingGatewayJsonEndpoint)
+ _endPointOverride ?? new Uri(Constant.BusinessMessagingGatewayJsonEndpoint)
))
{
request.Content = new StringContent(
- BusinessMessagingApi.GetHttpPostBody(this._apiKey, message),
+ BusinessMessagingApi.GetHttpPostBody(_apiKey, message),
Encoding.UTF8,
Constant.BusinessMessagingGatewayMediaTypeJson
);
- using (var requestResult = await this._httpClient.SendAsync(request, cancellationToken)
+ using (var requestResult = await _httpClient.SendAsync(request, cancellationToken)
.ConfigureAwait(false))
{
cancellationToken.ThrowIfCancellationRequested();
@@ -126,5 +128,75 @@ await requestResult.Content.ReadAsStringAsync()
}
}
}
+
+ ///
+ /// Sends an One Time Password asynchronously.
+ ///
+ /// The otp to send.
+ /// The cancellation token.
+ ///
+ [PublicAPI]
+ public async Task SendOtpAsync(
+ OtpRequest otpRequest,
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ using (var request = new HttpRequestMessage(
+ HttpMethod.Post,
+ _endPointOverride ?? new Uri(Constant.OtpRequestEndpoint)
+ ))
+ {
+ request.Content = new StringContent(
+ JsonSerializer.Serialize(otpRequest),
+ Encoding.UTF8,
+ Constant.BusinessMessagingGatewayMediaTypeJson
+ );
+
+ return await SendOtpApiRequestAsync(request, cancellationToken);
+ }
+ }
+
+ ///
+ /// Checks an One Time Password asynchronously.
+ ///
+ /// id of the OTP to check.
+ /// The code the end user used
+ /// The cancellation token.
+ ///
+ [PublicAPI]
+ public async Task VerifyOtpAsync(
+ string id,
+ string code,
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ using (var request = new HttpRequestMessage(
+ HttpMethod.Post,
+ _endPointOverride ?? new Uri(string.Format(Constant.OtpVerifyEndpointFormatter, id))
+ ))
+ {
+ request.Content = new StringContent(
+ JsonSerializer.Serialize(new { code = code } ),
+ Encoding.UTF8,
+ Constant.BusinessMessagingGatewayMediaTypeJson
+ );
+
+ return await SendOtpApiRequestAsync(request, cancellationToken);
+ }
+ }
+
+ private async Task SendOtpApiRequestAsync(HttpRequestMessage request,
+ CancellationToken cancellationToken)
+ {
+ request.Headers.Add("X-CM-ProductToken", _apiKey.ToString());
+ using (var requestResult = await _httpClient.SendAsync(request, cancellationToken)
+ .ConfigureAwait(false))
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ return JsonSerializer.Deserialize(
+ await requestResult.Content.ReadAsStringAsync()
+ .ConfigureAwait(false)
+ );
+ }
+ }
}
}
diff --git a/CM.Text/TextClientFactory.cs b/CM.Text/TextClientFactory.cs
index 028c429..82ec0d8 100644
--- a/CM.Text/TextClientFactory.cs
+++ b/CM.Text/TextClientFactory.cs
@@ -35,14 +35,14 @@ public class TextClientFactory : ITextClientFactory
/// (Optional) The end point to use, instead of the default "https://gw.cmtelecom.com/v1.0/message".
public TextClientFactory(HttpClient httpClient, Uri endPointOverride = null)
{
- this._httpClient = httpClient;
- this._endPointOverride = endPointOverride;
+ _httpClient = httpClient;
+ _endPointOverride = endPointOverride;
}
///
public ITextClient GetClient(Guid productToken)
{
- return new TextClient(productToken, this._httpClient, this._endPointOverride);
+ return new TextClient(productToken, _httpClient, _endPointOverride);
}
}
}
diff --git a/CM.Text/[JetBrains.Annotations]/JetBrains.Annotations.cs b/CM.Text/[JetBrains.Annotations]/JetBrains.Annotations.cs
index 9b9858e..90159d9 100644
--- a/CM.Text/[JetBrains.Annotations]/JetBrains.Annotations.cs
+++ b/CM.Text/[JetBrains.Annotations]/JetBrains.Annotations.cs
@@ -1272,4 +1272,4 @@ internal sealed class RazorWriteMethodParameterAttribute : Attribute
internal sealed class NoReorder : Attribute
{
}
-}
\ No newline at end of file
+}
diff --git a/README.md b/README.md
index 65a5772..98e61b3 100644
--- a/README.md
+++ b/README.md
@@ -415,3 +415,20 @@ var client = new TextClient(apiKey);
var message = builder.Build();
var result = await client.SendMessageAsync(message);
```
+
+## Using the OTP API
+Send a simple OTP code
+```cs
+ var client = new TextClient(new Guid(ConfigurationManager.AppSettings["ApiKey"]));
+ var otpBuilder = new OtpRequestBuilder("Sender_name", "Recipient_PhoneNumber");
+ otpBuilder.WithMessage("Your otp code is {code}.");
+ var result = await textClient.SendOtpAsync(otpBuilder.Build());
+```
+
+Verify the response code
+```cs
+ var verifyResult = client.VerifyOtp("OTP-ID", "code");
+ bool isValid = verifyResult.Verified;
+```
+
+For more advanced scenarios see also https://developers.cm.com/identity/docs/one-time-password-create
\ No newline at end of file