diff --git a/src/Orbit.Api/Extensions/HttpContextExtensions.cs b/src/Orbit.Api/Extensions/HttpContextExtensions.cs index 6f78693a..24cf2be5 100644 --- a/src/Orbit.Api/Extensions/HttpContextExtensions.cs +++ b/src/Orbit.Api/Extensions/HttpContextExtensions.cs @@ -67,6 +67,11 @@ public static bool IsReadOnlyCredential(this ClaimsPrincipal user) return countryCode; } + var acceptLanguageCountryCode = NormalizeCountryCodeFromAcceptLanguage( + context.Request.Headers.AcceptLanguage.ToString()); + if (acceptLanguageCountryCode is not null) + return acceptLanguageCountryCode; + return null; } @@ -96,6 +101,29 @@ public static bool IsReadOnlyCredential(this ClaimsPrincipal user) : null; } + private static string? NormalizeCountryCodeFromAcceptLanguage(string? acceptLanguage) + { + if (string.IsNullOrWhiteSpace(acceptLanguage)) + return null; + + var languageTags = acceptLanguage.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + foreach (var entry in languageTags) + { + var languageTag = entry.Split(';', 2, StringSplitOptions.TrimEntries)[0] + .Replace('_', '-'); + var segments = languageTag.Split('-', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + if (segments.Length < 2) + continue; + + var countryCode = NormalizeCountryCode(segments[^1]); + if (countryCode is not null) + return countryCode; + } + + return null; + } + private static bool TryNormalizeIpAddress(string? ipAddress, out string? normalizedIpAddress) { normalizedIpAddress = null; diff --git a/tests/Orbit.Infrastructure.Tests/Controllers/SubscriptionControllerTests.cs b/tests/Orbit.Infrastructure.Tests/Controllers/SubscriptionControllerTests.cs index b40f5ec5..6d05d628 100644 --- a/tests/Orbit.Infrastructure.Tests/Controllers/SubscriptionControllerTests.cs +++ b/tests/Orbit.Infrastructure.Tests/Controllers/SubscriptionControllerTests.cs @@ -110,6 +110,23 @@ await _mediator.Received(1).Send( Arg.Any()); } + [Fact] + public async Task CreateCheckout_UsesAcceptLanguageCountryFallback() + { + _mediator.Send(Arg.Any(), Arg.Any()) + .Returns(Result.Success(default(CheckoutResponse)!)); + + _controller.HttpContext.Request.Headers["Accept-Language"] = "pt-BR,pt;q=0.9,en;q=0.8"; + + var request = new SubscriptionController.CreateCheckoutRequest("month"); + + await _controller.CreateCheckout(request, CancellationToken.None); + + await _mediator.Received(1).Send( + Arg.Is(c => c.CountryCode == "BR" && c.IpAddress == "127.0.0.1"), + Arg.Any()); + } + // --- CreatePortal --- [Fact] @@ -241,6 +258,23 @@ await _mediator.Received(1).Send( Arg.Any()); } + [Fact] + public async Task GetPlans_UsesAcceptLanguageCountryFallback() + { + _mediator.Send(Arg.Any(), Arg.Any()) + .Returns(Result.Success(default(PlansResponse)!)); + + _controller.HttpContext.Request.Headers["Accept-Language"] = "pt-BR,pt;q=0.9,en;q=0.8"; + + await _controller.GetPlans(CancellationToken.None); + + await _mediator.Received(1).Send( + Arg.Is(query => + query.CountryCode == "BR" && + query.IpAddress == "127.0.0.1"), + Arg.Any()); + } + // --- ClaimAdReward --- [Fact]