fix(prod): payment/tax gateways never fake success outside Development
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m7s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 51s
CI/CD / Deploy · all services (push) Successful in 1m31s

Production-readiness audit fixes — every mock fallback is now gated on
IsDevelopment; in production these paths fail loudly instead:

- ZarinPal/Tara/SnappPay init: missing credentials returned a MOCK
  payment URL whose callback verified as paid — a café could activate a
  paid plan without paying. Now: "Payment gateway is not configured."
- Tara/SnappPay verify: a forged MOCK-* trace/token on the callback was
  accepted as a verified payment in any environment. Now rejected
  outside Development.
- Taraz (سامانه مودیان): returned a fake MOCK-TARAZ tracking code as if
  invoices reached the tax authority. Now returns an honest error (the
  real integration is not built yet).
- Admin integrations: NextPay/Vandar removed — they were listed but have
  no gateway implementation (selecting them silently used ZarinPal).
- docker-compose: ASPNETCORE_ENVIRONMENT default flipped Development →
  Production so a missing env var can never run prod in dev mode.

86 tests pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-12 10:16:01 +03:30
parent 00649d0248
commit 9765491f6f
7 changed files with 94 additions and 24 deletions
@@ -1,17 +1,29 @@
using Meezi.Core.Interfaces;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Meezi.Infrastructure.ExternalServices;
/// <summary>
/// Taraz (سامانه مودیان) invoice submission. The real API integration is not
/// built yet, so this service NEVER fakes a tracking code outside development —
/// a merchant must not believe invoices reached the tax authority when they
/// did not.
/// </summary>
public class TarazTaxService : ITarazTaxService
{
private readonly IConfiguration _configuration;
private readonly IHostEnvironment _environment;
private readonly ILogger<TarazTaxService> _logger;
public TarazTaxService(IConfiguration configuration, ILogger<TarazTaxService> logger)
public TarazTaxService(
IConfiguration configuration,
IHostEnvironment environment,
ILogger<TarazTaxService> logger)
{
_configuration = configuration;
_environment = environment;
_logger = logger;
}
@@ -20,20 +32,33 @@ public class TarazTaxService : ITarazTaxService
DateTime dateUtc,
CancellationToken cancellationToken = default)
{
var username = _configuration["Taraz:Username"];
if (string.IsNullOrWhiteSpace(username))
if (_environment.IsDevelopment())
{
_logger.LogInformation(
"Taraz not configured — skip submit for cafe {CafeId} date {Date}",
_logger.LogWarning(
"[DEV TARAZ] Pretend-submitted invoices for cafe {CafeId} date {Date}",
cafeId,
dateUtc.Date);
return Task.FromResult(new TarazSubmitResult(
true,
"MOCK-TARAZ",
"Taraz API not configured; submission logged only."));
"DEV-TARAZ",
"Development stub — nothing was sent to the tax authority."));
}
_logger.LogInformation("Taraz submit queued for cafe {CafeId} on {Date}", cafeId, dateUtc.Date);
return Task.FromResult(new TarazSubmitResult(true, null, "Submission queued."));
var username = _configuration["Taraz:Username"];
if (string.IsNullOrWhiteSpace(username))
{
return Task.FromResult(new TarazSubmitResult(
false,
null,
"سرویس مودیان هنوز پیکربندی نشده است. لطفاً با پشتیبانی تماس بگیرید."));
}
// Credentials exist but the actual Taraz API call is not implemented yet.
// Be honest with the merchant instead of returning a fake tracking code.
_logger.LogWarning("Taraz submit requested for cafe {CafeId} but the integration is not implemented", cafeId);
return Task.FromResult(new TarazSubmitResult(
false,
null,
"اتصال مستقیم به سامانه مودیان هنوز در دست توسعه است."));
}
}