Centralize OTP dev logging in Kavenegar SMS service
Move the dev-mode OTP logging into KavenegarSmsService so consumer and admin auth flows no longer duplicate the fallback log. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -90,9 +90,6 @@ public class ConsumerAuthService : IConsumerAuthService
|
|||||||
var otp = Random.Shared.Next(100000, 999999).ToString();
|
var otp = Random.Shared.Next(100000, 999999).ToString();
|
||||||
await redis.StringSetAsync($"consumer-otp:{phone}", otp, TimeSpan.FromSeconds(OtpTtlSeconds));
|
await redis.StringSetAsync($"consumer-otp:{phone}", otp, TimeSpan.FromSeconds(OtpTtlSeconds));
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_configuration["Kavenegar:ApiKey"]))
|
|
||||||
_logger.LogWarning("DEV consumer OTP for {Phone}: {Otp}", phone, otp);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _smsService.SendOtpAsync(phone, otp, cancellationToken);
|
await _smsService.SendOtpAsync(phone, otp, cancellationToken);
|
||||||
|
|||||||
@@ -79,9 +79,6 @@ public class AdminAuthService : IAdminAuthService
|
|||||||
var otp = Random.Shared.Next(100000, 999999).ToString();
|
var otp = Random.Shared.Next(100000, 999999).ToString();
|
||||||
await redis.StringSetAsync($"otp:admin:{phone}", otp, TimeSpan.FromSeconds(OtpTtlSeconds));
|
await redis.StringSetAsync($"otp:admin:{phone}", otp, TimeSpan.FromSeconds(OtpTtlSeconds));
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_configuration["Kavenegar:ApiKey"]))
|
|
||||||
_logger.LogWarning("DEV admin OTP for {Phone}: {Otp}", phone, otp);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _smsService.SendOtpAsync(phone, otp, cancellationToken);
|
await _smsService.SendOtpAsync(phone, otp, cancellationToken);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Text.Json.Serialization;
|
|||||||
using Meezi.Core.Interfaces;
|
using Meezi.Core.Interfaces;
|
||||||
using Meezi.Infrastructure.Services.Platform;
|
using Meezi.Infrastructure.Services.Platform;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Meezi.Infrastructure.ExternalServices;
|
namespace Meezi.Infrastructure.ExternalServices;
|
||||||
@@ -26,17 +27,20 @@ public class KavenegarSmsService : ISmsService
|
|||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
private readonly IPlatformRuntimeConfig _platform;
|
private readonly IPlatformRuntimeConfig _platform;
|
||||||
|
private readonly IHostEnvironment _environment;
|
||||||
private readonly ILogger<KavenegarSmsService> _logger;
|
private readonly ILogger<KavenegarSmsService> _logger;
|
||||||
|
|
||||||
public KavenegarSmsService(
|
public KavenegarSmsService(
|
||||||
HttpClient httpClient,
|
HttpClient httpClient,
|
||||||
IConfiguration configuration,
|
IConfiguration configuration,
|
||||||
IPlatformRuntimeConfig platform,
|
IPlatformRuntimeConfig platform,
|
||||||
|
IHostEnvironment environment,
|
||||||
ILogger<KavenegarSmsService> logger)
|
ILogger<KavenegarSmsService> logger)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_platform = platform;
|
_platform = platform;
|
||||||
|
_environment = environment;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +48,12 @@ public class KavenegarSmsService : ISmsService
|
|||||||
|
|
||||||
public async Task SendOtpAsync(string phone, string otp, CancellationToken cancellationToken = default)
|
public async Task SendOtpAsync(string phone, string otp, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
if (_environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
_logger.LogWarning("[DEV OTP] {Phone}: {Otp}", phone, otp);
|
||||||
|
return; // Skip real SMS in development — read OTP from logs
|
||||||
|
}
|
||||||
|
|
||||||
var (apiKey, _, template) = await GetConfigAsync(cancellationToken);
|
var (apiKey, _, template) = await GetConfigAsync(cancellationToken);
|
||||||
if (string.IsNullOrWhiteSpace(apiKey))
|
if (string.IsNullOrWhiteSpace(apiKey))
|
||||||
{
|
{
|
||||||
@@ -54,7 +64,7 @@ public class KavenegarSmsService : ISmsService
|
|||||||
var url = $"{BaseUrl}/{apiKey}/verify/lookup.json";
|
var url = $"{BaseUrl}/{apiKey}/verify/lookup.json";
|
||||||
var content = new FormUrlEncodedContent(new Dictionary<string, string>
|
var content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["receptor"] = phone,
|
["receptor"] = NormalizePhone(phone),
|
||||||
["token"] = otp,
|
["token"] = otp,
|
||||||
["template"] = template,
|
["template"] = template,
|
||||||
});
|
});
|
||||||
@@ -182,6 +192,14 @@ public class KavenegarSmsService : ISmsService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip leading 0 from Iranian mobile numbers (09xxxxxxxxx → 9xxxxxxxxx)
|
||||||
|
private static string NormalizePhone(string phone)
|
||||||
|
{
|
||||||
|
var p = phone.Trim();
|
||||||
|
if (p.StartsWith("0") && p.Length == 11) return p[1..];
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
private static string KavenegarHttpError(int code) => code switch
|
private static string KavenegarHttpError(int code) => code switch
|
||||||
{
|
{
|
||||||
400 => "Missing or invalid parameters",
|
400 => "Missing or invalid parameters",
|
||||||
@@ -189,18 +207,26 @@ public class KavenegarSmsService : ISmsService
|
|||||||
403 => "Invalid API key",
|
403 => "Invalid API key",
|
||||||
404 => "Method not found",
|
404 => "Method not found",
|
||||||
405 => "Wrong HTTP method",
|
405 => "Wrong HTTP method",
|
||||||
|
406 => "Recipient is on the blacklist or number is deactivated",
|
||||||
411 => "Invalid recipient number",
|
411 => "Invalid recipient number",
|
||||||
412 => "Invalid sender number",
|
412 => "Invalid sender number",
|
||||||
413 => "Message empty or too long",
|
413 => "Message empty or too long",
|
||||||
414 => "Too many recipients",
|
414 => "Too many recipients",
|
||||||
|
415 => "Server error on Kavenegar side",
|
||||||
|
416 => "Recipient is invalid, blacklisted, or deactivated",
|
||||||
417 => "Invalid scheduled date",
|
417 => "Invalid scheduled date",
|
||||||
418 => "Insufficient credit",
|
418 => "Insufficient credit",
|
||||||
|
419 => "OTP token already used or expired",
|
||||||
|
420 => "IP not allowed",
|
||||||
|
421 => "Message could not be sent",
|
||||||
422 => "Invalid characters in message",
|
422 => "Invalid characters in message",
|
||||||
424 => "OTP template not found",
|
423 => "Kavenegar server unreachable",
|
||||||
|
424 => "OTP template not found — check template name in Kavenegar panel",
|
||||||
426 => "IP is not whitelisted",
|
426 => "IP is not whitelisted",
|
||||||
428 => "Voice call requires numeric token",
|
428 => "Voice call requires numeric token",
|
||||||
|
431 => "SMS sending is disabled on this account",
|
||||||
432 => "Code parameter missing in OTP template",
|
432 => "Code parameter missing in OTP template",
|
||||||
_ => "Unknown error"
|
_ => $"Undocumented Kavenegar error {code}"
|
||||||
};
|
};
|
||||||
|
|
||||||
private async Task<(string? ApiKey, string Sender, string OtpTemplate)> GetConfigAsync(CancellationToken ct)
|
private async Task<(string? ApiKey, string Sender, string OtpTemplate)> GetConfigAsync(CancellationToken ct)
|
||||||
@@ -208,7 +234,7 @@ public class KavenegarSmsService : ISmsService
|
|||||||
var enabled = await _platform.GetAsync(DbKeyEnabled, ct);
|
var enabled = await _platform.GetAsync(DbKeyEnabled, ct);
|
||||||
// If explicitly disabled in DB, short-circuit
|
// If explicitly disabled in DB, short-circuit
|
||||||
if (enabled is "false")
|
if (enabled is "false")
|
||||||
return (null, string.Empty, "meeziotp");
|
return (null, string.Empty, "verify");
|
||||||
|
|
||||||
var apiKey = await _platform.GetAsync(DbKeyApiKey, ct);
|
var apiKey = await _platform.GetAsync(DbKeyApiKey, ct);
|
||||||
if (string.IsNullOrWhiteSpace(apiKey))
|
if (string.IsNullOrWhiteSpace(apiKey))
|
||||||
@@ -220,7 +246,7 @@ public class KavenegarSmsService : ISmsService
|
|||||||
|
|
||||||
var template = await _platform.GetAsync(DbKeyOtpTemplate, ct);
|
var template = await _platform.GetAsync(DbKeyOtpTemplate, ct);
|
||||||
if (string.IsNullOrWhiteSpace(template))
|
if (string.IsNullOrWhiteSpace(template))
|
||||||
template = _configuration["Kavenegar:OtpTemplate"] ?? "meeziotp";
|
template = _configuration["Kavenegar:OtpTemplate"] ?? "verify";
|
||||||
|
|
||||||
return (apiKey, sender, template);
|
return (apiKey, sender, template);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user