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();
|
||||
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
|
||||
{
|
||||
await _smsService.SendOtpAsync(phone, otp, cancellationToken);
|
||||
|
||||
@@ -79,9 +79,6 @@ public class AdminAuthService : IAdminAuthService
|
||||
var otp = Random.Shared.Next(100000, 999999).ToString();
|
||||
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
|
||||
{
|
||||
await _smsService.SendOtpAsync(phone, otp, cancellationToken);
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Text.Json.Serialization;
|
||||
using Meezi.Core.Interfaces;
|
||||
using Meezi.Infrastructure.Services.Platform;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Meezi.Infrastructure.ExternalServices;
|
||||
@@ -26,17 +27,20 @@ public class KavenegarSmsService : ISmsService
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly IPlatformRuntimeConfig _platform;
|
||||
private readonly IHostEnvironment _environment;
|
||||
private readonly ILogger<KavenegarSmsService> _logger;
|
||||
|
||||
public KavenegarSmsService(
|
||||
HttpClient httpClient,
|
||||
IConfiguration configuration,
|
||||
IPlatformRuntimeConfig platform,
|
||||
IHostEnvironment environment,
|
||||
ILogger<KavenegarSmsService> logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_configuration = configuration;
|
||||
_platform = platform;
|
||||
_environment = environment;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -44,6 +48,12 @@ public class KavenegarSmsService : ISmsService
|
||||
|
||||
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);
|
||||
if (string.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
@@ -54,7 +64,7 @@ public class KavenegarSmsService : ISmsService
|
||||
var url = $"{BaseUrl}/{apiKey}/verify/lookup.json";
|
||||
var content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||
{
|
||||
["receptor"] = phone,
|
||||
["receptor"] = NormalizePhone(phone),
|
||||
["token"] = otp,
|
||||
["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
|
||||
{
|
||||
400 => "Missing or invalid parameters",
|
||||
@@ -189,18 +207,26 @@ public class KavenegarSmsService : ISmsService
|
||||
403 => "Invalid API key",
|
||||
404 => "Method not found",
|
||||
405 => "Wrong HTTP method",
|
||||
406 => "Recipient is on the blacklist or number is deactivated",
|
||||
411 => "Invalid recipient number",
|
||||
412 => "Invalid sender number",
|
||||
413 => "Message empty or too long",
|
||||
414 => "Too many recipients",
|
||||
415 => "Server error on Kavenegar side",
|
||||
416 => "Recipient is invalid, blacklisted, or deactivated",
|
||||
417 => "Invalid scheduled date",
|
||||
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",
|
||||
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",
|
||||
428 => "Voice call requires numeric token",
|
||||
431 => "SMS sending is disabled on this account",
|
||||
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)
|
||||
@@ -208,7 +234,7 @@ public class KavenegarSmsService : ISmsService
|
||||
var enabled = await _platform.GetAsync(DbKeyEnabled, ct);
|
||||
// If explicitly disabled in DB, short-circuit
|
||||
if (enabled is "false")
|
||||
return (null, string.Empty, "meeziotp");
|
||||
return (null, string.Empty, "verify");
|
||||
|
||||
var apiKey = await _platform.GetAsync(DbKeyApiKey, ct);
|
||||
if (string.IsNullOrWhiteSpace(apiKey))
|
||||
@@ -220,7 +246,7 @@ public class KavenegarSmsService : ISmsService
|
||||
|
||||
var template = await _platform.GetAsync(DbKeyOtpTemplate, ct);
|
||||
if (string.IsNullOrWhiteSpace(template))
|
||||
template = _configuration["Kavenegar:OtpTemplate"] ?? "meeziotp";
|
||||
template = _configuration["Kavenegar:OtpTemplate"] ?? "verify";
|
||||
|
||||
return (apiKey, sender, template);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user