Pinqponq.Sms
1.0.1
Prefix Reserved
dotnet add package Pinqponq.Sms --version 1.0.1
NuGet\Install-Package Pinqponq.Sms -Version 1.0.1
<PackageReference Include="Pinqponq.Sms" Version="1.0.1" />
<PackageVersion Include="Pinqponq.Sms" Version="1.0.1" />
<PackageReference Include="Pinqponq.Sms" />
paket add Pinqponq.Sms --version 1.0.1
#r "nuget: Pinqponq.Sms, 1.0.1"
#:package Pinqponq.Sms@1.0.1
#addin nuget:?package=Pinqponq.Sms&version=1.0.1
#tool nuget:?package=Pinqponq.Sms&version=1.0.1
Pinqponq.Sms
SMS sending wrapper for NetGSM with a single standard
ISmsSender interface — replacing the divergent ISmsService / IGSMService /
INetGSMService contracts that predate this package. Supports both NetGSM's legacy
GET query API and its REST v2 POST API, with built-in retry for transient HTTP
failures.
Install
dotnet add package Pinqponq.Sms
Requirements
- .NET 8.0, 9.0, or 10.0
- A NetGSM account (
usercode/password) for either the legacy GET API or REST v2
Quick start
using Pinqponq.Sms;
using Pinqponq.Sms.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPinqponqSms(options =>
{
options.ApiUrl = "https://api.netgsm.com.tr/sms/send/get/";
options.UserCode = builder.Configuration["Sms:UserCode"];
options.Password = builder.Configuration["Sms:Password"];
options.MsgHeader = "MYBRAND";
});
var app = builder.Build();
Send a message from anywhere ISmsSender is injected:
public sealed class NotificationService(ISmsSender smsSender)
{
public Task NotifyAsync(string phoneNumber, CancellationToken cancellationToken) =>
smsSender.SendAsync(
new SmsMessage { To = phoneNumber, Text = "Your order has shipped." },
cancellationToken);
}
Configuration
AddPinqponqSms(Action<SmsOptions> configure) registers ISmsSender (as
NetGsmSmsSender) together with a named HttpClient, and validates SmsOptions on
startup via ValidateOnStart().
| Option | Default | Notes |
|---|---|---|
ApiUrl |
null |
For SmsTransport.GetQuery, the legacy send endpoint, e.g. https://api.netgsm.com.tr/sms/send/get/. For SmsTransport.RestV2, an empty value resolves to SmsOptions.DefaultRestV2ApiUrl (https://api.netgsm.com.tr/sms/rest/v2/send). Must be an absolute HTTPS URL when set. |
Transport |
SmsTransport.GetQuery |
See Main types below. |
UserCode |
null |
NetGSM usercode. Required whenever an ApiUrl is in effect (i.e. always for RestV2; for GetQuery, whenever sending isn't a no-op). |
Password |
null |
NetGSM password. Same requirement as UserCode. |
MsgHeader |
null |
Sender header (msgheader) shown to the recipient. Sent as an empty string if not set. |
RetryCount |
3 |
Maximum retry attempts on transient HTTP failures. Must not be negative. |
RetryBaseDelay |
300ms |
Base delay for exponential backoff between retries (with jitter). Must be positive. |
HttpTimeout |
30s |
Timeout applied to the underlying HttpClient. Must be positive. |
AllowNoOp |
false |
Only meaningful for GetQuery: when true, an empty ApiUrl is allowed and SendAsync becomes a no-op. Local development only — see Notes / behavior. Ignored for RestV2. |
Options are validated eagerly at startup by SmsOptionsValidator (registered as an
IValidateOptions<SmsOptions>), so a misconfigured ApiUrl, missing credentials for
the selected transport, or an invalid Transport value fails fast instead of at first
send.
Main types
AddPinqponqSms—IServiceCollectionextension that registersISmsSenderand its options.ISmsSender— the single sending contract:Task SendAsync(SmsMessage message, CancellationToken cancellationToken = default).SmsMessage—To(recipient; non-digit characters are stripped before sending) andText(message body). Bothrequired.SmsOptions— configuration bound/validated as described above.SmsTransport— chooses howNetGsmSmsSendertalks to NetGSM:GetQuery(default,= 0) — legacy NetGSM GET request withusercodeandpasswordon the query string.RestV2(= 1) — NetGSM REST v2POSTwith a JSON body and HTTP Basic Auth (usercode:passwordbase64-encoded in theAuthorizationheader). WhenApiUrlis empty, the request goes to the default HTTPS endpoint.
NetGsmSmsSender— theISmsSenderimplementation. ExposesHttpClientName("Pinqponq.Sms.NetGsm"), the name of theHttpClientregistered byAddPinqponqSms(useful if you want to further customize it viaIHttpClientBuilder, e.g. add a message handler).NetGsmRejectedException— thrown when NetGSM accepts the HTTP call (HTTP 200) but returns a business-error body (NetGSM's response code conventions treat a leading00as success; anything else — e.g. code30— is a rejection). ExposesResponseBodywith the raw provider response.
Notes / behavior
AllowNoOpis a local-only escape hatch. It only applies toSmsTransport.GetQuery: whenApiUrlis empty andAllowNoOpistrue,SendAsyncreturns immediately without making a network call. This exists so a development environment without real NetGSM credentials can still boot withAddPinqponqSmswired up. It is ignored forRestV2— an emptyApiUrlthere always resolves to the default REST endpoint and a real call is made. Do not setAllowNoOp = trueoutside local development.- The GET transport puts your password in the URL.
SmsTransport.GetQuerysendsusercodeandpasswordas query-string parameters. Never log request URLs (or enableHttpClientlogging handlers that do) while using this transport — the credentials would end up in plaintext logs. PreferSmsTransport.RestV2(Basic Auth over HTTPS, credentials in a header, not the URL) for new integrations. - Recipient numbers are normalized.
SmsMessage.Tohas all non-digit characters stripped before the request is built; if nothing is left,SendAsyncthrowsArgumentException. - Retries use Polly with exponential
backoff and jitter, governed by
RetryCount/RetryBaseDelay. TransientHttpRequestExceptions and non-caller-initiatedTaskCanceledExceptions (timeouts) are retried. NetGsmRejectedExceptionis never retried. A business rejection (e.g. bad credentials, insufficient credit, invalid recipient — anything NetGSM reports with a non-00code while still answering HTTP 200) is a permanent failure, not a transient one, so the retry pipeline lets it propagate immediately.- Missing credentials fail fast per-send, not just at startup:
SendAsyncthrowsInvalidOperationExceptionifUserCodeorPasswordis blank at call time (in addition to the startup validation already rejecting most such configurations). - The registered
HttpClient'sTimeoutis read fromSmsOptions.HttpTimeoutat the time the client is configured (viaConfigureHttpClient), so it reflects whateverIOptions<SmsOptions>resolves to at that point.
Related packages
Pinqponq.Identity.Otp— sends one-time passwords over SMS (and email) using this package'sISmsSenderas its SMS delivery channel.
Samples
Try this package in the browser via Pinqponq.Playground — see samples/README.md.
Repository
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Configuration (>= 10.0.10)
- Microsoft.Extensions.DependencyInjection (>= 10.0.10)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Http (>= 10.0.10)
- Microsoft.Extensions.Logging (>= 10.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.10)
- Microsoft.Extensions.Options (>= 10.0.10)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.10)
- Polly.Core (>= 8.4.2)
-
net8.0
- Microsoft.Extensions.Configuration (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Http (>= 8.0.1)
- Microsoft.Extensions.Logging (>= 8.0.1)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.3)
- Microsoft.Extensions.Options (>= 8.0.2)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- Polly.Core (>= 8.4.2)
-
net9.0
- Microsoft.Extensions.Configuration (>= 9.0.18)
- Microsoft.Extensions.DependencyInjection (>= 9.0.18)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.18)
- Microsoft.Extensions.Http (>= 9.0.18)
- Microsoft.Extensions.Logging (>= 9.0.18)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.18)
- Microsoft.Extensions.Options (>= 9.0.18)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.18)
- Polly.Core (>= 8.4.2)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Pinqponq.Sms:
| Package | Downloads |
|---|---|
|
Pinqponq.Identity.Otp
One-time code send/verify flow over email or SMS; channel routing (mail/sms) lives in the package. Storage interface is left to the consumer. |
GitHub repositories
This package is not used by any popular GitHub repositories.