Adding a Custom SMS Gateway
This guide walks through implementing and registering a custom SMS gateway in Shesha.
Overview
Shesha resolves the active SMS gateway at runtime by reading the SmsGateway UID from application settings and locating the matching registered implementation. To add a new gateway you need to:
- Create a settings class
- Create a settings accessor interface
- Create a marker interface for the gateway
- Implement the gateway class
- Register everything in an ABP module
Step 1 — Settings class
Create a plain class to hold the gateway's configuration (API keys, host, etc.).
public class MyGatewaySettings
{
public string ApiKey { get; set; }
public string BaseUrl { get; set; }
}
Step 2 — Setting name constants
public static class MyGatewaySettingNames
{
public const string GatewaySettings = "GatewaySettings";
}
Step 3 — Settings accessor interface
Extend ISettingAccessors and declare a typed accessor for the settings class. The [Setting] attribute ties the property to a named setting stored in the database. The optional EditorFormName points to a Shesha configuration form used to edit the settings in the admin UI.
[Category("SMS")]
public interface IMyGatewaySettings : ISettingAccessors
{
[Display(Name = "My Gateway")]
[Setting(MyGatewaySettingNames.GatewaySettings, EditorFormName = "my-gateway-settings")]
ISettingAccessor<MyGatewaySettings> MyGateway { get; }
}
Step 4 — Marker interface
public interface IMyGatewaySmsGateway : IConfigurableSmsGateway<MyGatewaySettings>
{
}
This interface is used when registering with the IoC container (see Step 6).
Step 5 — Gateway implementation
Extend ConfigurableSmsGateway<TSettings> and decorate the class with:
[ClassUid]— a stable GUID that uniquely identifies this gateway. This is stored in theSmsGatewaysetting to select which gateway is active.[Display(Name = "...")]— the human-readable name shown in the admin UI.
[ClassUid("your-unique-guid-here")]
[Display(Name = "My Gateway")]
public class MyGatewaySmsGateway : ConfigurableSmsGateway<MyGatewaySettings>, IMyGatewaySmsGateway
{
private readonly IMyGatewaySettings _settings;
public MyGatewaySmsGateway(IMyGatewaySettings settings)
{
_settings = settings;
}
public override async Task<SendStatus> SendSmsAsync(string mobileNumber, string body)
{
var settings = await _settings.MyGateway.GetValueAsync();
// Call your SMS provider's API here
using var httpClient = new HttpClient();
// ... build request, send, handle response ...
return SendStatus.Success();
// or: return SendStatus.Failed("reason");
}
public override async Task<MyGatewaySettings> GetTypedSettingsAsync()
{
return await _settings.MyGateway.GetValueAsync();
}
public override async Task SetTypedSettingsAsync(MyGatewaySettings settings)
{
await _settings.MyGateway.SetValueAsync(settings);
}
}
SendStatus
SendSmsAsync must return a SendStatus:
| Method | When to use |
|---|---|
SendStatus.Success() | Message accepted by the provider |
SendStatus.Failed("reason") | Provider rejected or an error occurred |
Step 6 — ABP module
Create an ABP module that registers the settings accessor (with defaults) and the gateway.
[DependsOn(typeof(SheshaFrameworkModule), typeof(SheshaApplicationModule), typeof(AbpAspNetCoreModule))]
public class MyGatewayModule : SheshaModule
{
public const string ModuleName = "MyCompany.MyGateway";
public override SheshaModuleInfo ModuleInfo => new SheshaModuleInfo(ModuleName)
{
FriendlyName = "My SMS Gateway",
Publisher = "MyCompany",
};
public override void PreInitialize()
{
Configuration.Modules.AbpAspNetCore().CreateControllersForAppServices(
this.GetType().Assembly,
moduleName: "MyGateway",
useConventionalHttpVerbs: true);
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
// Register settings with defaults
IocManager.RegisterSettingAccessor<IMyGatewaySettings>(s =>
{
s.MyGateway.WithDefaultValue(new MyGatewaySettings
{
BaseUrl = "https://api.myprovider.com"
});
});
// Register the gateway — Forward<> is required so ISmsGateway resolves correctly
IocManager.IocContainer.Register(
Component.For<IMyGatewaySmsGateway>()
.Forward<MyGatewaySmsGateway>()
.ImplementedBy<MyGatewaySmsGateway>()
.LifestyleTransient()
);
}
}
Why
.Forward<>()? Shesha discovers gateways by scanning allISmsGatewayregistrations. Using.Forward<>()on the concrete type ensures the same instance registration is resolvable as bothIMyGatewaySmsGatewayandISmsGateway.
Step 7 — Add the module to your host application
In your host application's module class, add a [DependsOn] reference:
[DependsOn(
typeof(SheshaApplicationModule),
typeof(MyGatewayModule), // <-- add this
// ... other modules
)]
public class MyApplicationModule : AbpModule
{
// ...
}
Selecting the gateway
Once deployed, navigate to Admin → Settings → SMS and select your gateway from the SMS Gateway dropdown. The dropdown is populated by scanning all registered ISmsGateway implementations, matching them by their [ClassUid].
The IsSmsEnabled flag must also be enabled for any messages to be sent.