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
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:
@@ -22,13 +22,14 @@ public class PlatformIntegrationService : IPlatformIntegrationService
|
||||
public const string KeyKavenegarEnabled = "integrations.kavenegar.enabled";
|
||||
public const string KeyKavenegarSender = "integrations.kavenegar.senderNumber";
|
||||
|
||||
// Only gateways the BillingPaymentOrchestrator actually implements. NextPay
|
||||
// and Vandar were listed here before they had any implementation — enabling
|
||||
// them silently fell back to ZarinPal, which is a trap for the operator.
|
||||
private static readonly (string Id, string NameFa, string Prefix)[] Gateways =
|
||||
[
|
||||
("zarinpal", "زرینپال", "payment.zarinpal"),
|
||||
("tara", "تارا", "payment.tara"),
|
||||
("snapppay", "اسنپپی", "payment.snapppay"),
|
||||
("nextpay", "نکستپی", "payment.nextpay"),
|
||||
("vandar", "وندار", "payment.vandar")
|
||||
("snapppay", "اسنپپی", "payment.snapppay")
|
||||
];
|
||||
|
||||
private readonly AppDbContext _db;
|
||||
@@ -97,11 +98,6 @@ public class PlatformIntegrationService : IPlatformIntegrationService
|
||||
if (!string.IsNullOrWhiteSpace(gw.MerchantId))
|
||||
await UpsertAsync($"{meta.Prefix}.merchantId", gw.MerchantId.Trim(), "payment", "مرچنت زرینپال", ct);
|
||||
}
|
||||
else if (gw.Id is "nextpay" or "vandar")
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(gw.ApiKey) && !IsMaskedPlaceholder(gw.ApiKey))
|
||||
await UpsertAsync($"{meta.Prefix}.apiKey", gw.ApiKey.Trim(), "payment", $"توکن {meta.NameFa}", ct);
|
||||
}
|
||||
|
||||
if (gw.Credentials is not null)
|
||||
await SaveCredentialsAsync(meta.Prefix, gw.Id, gw.Credentials, ct);
|
||||
@@ -211,11 +207,6 @@ public class PlatformIntegrationService : IPlatformIntegrationService
|
||||
merchantId = map.GetValueOrDefault($"{prefix}.merchantId");
|
||||
hasSecret = HasSecret(map, $"{prefix}.merchantId");
|
||||
}
|
||||
else if (id is "nextpay" or "vandar")
|
||||
{
|
||||
apiKey = MaskSecret(map.GetValueOrDefault($"{prefix}.apiKey"));
|
||||
hasSecret = HasSecret(map, $"{prefix}.apiKey");
|
||||
}
|
||||
else if (id == "tara")
|
||||
{
|
||||
credentials = new GatewayCredentialsDto(
|
||||
|
||||
Reference in New Issue
Block a user