feat(plans): menu watermark on Free (removed by paid feature)

Guest QR menu shows a "ساخته‌شده با میزی" watermark under the menu unless the café's
plan has the `watermark_removed` feature (Starter+).

- PublicMenuDto gains ShowWatermark; PublicService computes it from
  IsFeatureEnabledForCafeAsync("watermark_removed") for both slug and branch menus.
- Guest menu renders the watermark footer when showWatermark.
- NoOpPlatformCatalogService test double (all features on) for the PublicService
  ctor; QrMenuTests updated.

86 tests pass; dashboard tsc clean.
This commit is contained in:
soroush.asadi
2026-06-03 02:10:24 +03:30
parent 2487f9e30f
commit 7d06f149d3
6 changed files with 73 additions and 5 deletions
+2 -1
View File
@@ -107,7 +107,8 @@ public record PublicMenuDto(
string CafeName,
string Slug,
CafeThemeDto Theme,
IReadOnlyList<PublicMenuCategoryDto> Categories);
IReadOnlyList<PublicMenuCategoryDto> Categories,
bool ShowWatermark);
public record GuestCreateOrderRequest(
OrderType OrderType,
+12 -3
View File
@@ -53,6 +53,7 @@ public class PublicService : IPublicService
private readonly IBranchIdentityService _identity;
private readonly IAbuseProtectionService _abuse;
private readonly IHttpContextAccessor _http;
private readonly Meezi.Infrastructure.Services.Platform.IPlatformCatalogService _catalog;
public PublicService(
AppDbContext db,
@@ -62,7 +63,8 @@ public class PublicService : IPublicService
IBranchMenuService branchMenu,
IBranchIdentityService identity,
IAbuseProtectionService abuse,
IHttpContextAccessor http)
IHttpContextAccessor http,
Meezi.Infrastructure.Services.Platform.IPlatformCatalogService catalog)
{
_db = db;
_orders = orders;
@@ -72,8 +74,13 @@ public class PublicService : IPublicService
_identity = identity;
_abuse = abuse;
_http = http;
_catalog = catalog;
}
/// <summary>Free menus show a Meezi watermark; the `watermark_removed` feature (paid) hides it.</summary>
private async Task<bool> ShowWatermarkAsync(Cafe cafe, CancellationToken ct) =>
!await _catalog.IsFeatureEnabledForCafeAsync(cafe.Id, cafe.PlanTier, "watermark_removed", ct);
public Task<IReadOnlyList<CafeDiscoverDto>> DiscoverAsync(
DiscoverFilterParams filters,
CancellationToken cancellationToken = default) =>
@@ -190,7 +197,8 @@ public class PublicService : IPublicService
.Where(c => c.Items.Count > 0)
.ToList();
return new PublicMenuDto(cafe.Id, cafe.Name, cafe.Slug, CafeThemeMapping.FromJson(cafe.ThemeJson), grouped);
return new PublicMenuDto(cafe.Id, cafe.Name, cafe.Slug, CafeThemeMapping.FromJson(cafe.ThemeJson), grouped,
await ShowWatermarkAsync(cafe, cancellationToken));
}
public async Task<(GuestOrderPlacedDto? Data, string? ErrorCode, string? ErrorMessage)> PlaceOrderAsync(
@@ -357,7 +365,8 @@ public class PublicService : IPublicService
.OrderBy(c => categoryById.GetValueOrDefault(c.Id)?.SortOrder ?? 0)
.ToList();
return new PublicMenuDto(cafe.Id, cafe.Name, cafe.Slug, CafeThemeMapping.FromJson(cafe.ThemeJson), grouped);
return new PublicMenuDto(cafe.Id, cafe.Name, cafe.Slug, CafeThemeMapping.FromJson(cafe.ThemeJson), grouped,
await ShowWatermarkAsync(cafe, cancellationToken));
}
public async Task<(GuestQrOrderPlacedDto? Data, string? ErrorCode, string? Message)> PlaceBranchGuestOrderAsync(