diff --git a/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/BffDataController.cs b/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/BffDataController.cs
deleted file mode 100644
index c81c442f..00000000
--- a/apps/web-client-tpos-net/src/WebClientTpos.Server/Controllers/BffDataController.cs
+++ /dev/null
@@ -1,1830 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Npgsql;
-using Dapper;
-using System.IdentityModel.Tokens.Jwt;
-
-namespace WebClientTpos.Server.Controllers;
-
-// EN: BFF controller with multi-tenant data isolation.
-// All queries are scoped to the current user's merchant.
-// JWT token is parsed manually from Authorization header (no middleware dependency).
-// VI: BFF controller với cách ly dữ liệu multi-tenant.
-// Tất cả queries đều lọc theo merchant của user hiện tại.
-// JWT token được parse thủ công từ header Authorization (không phụ thuộc middleware).
-[ApiController]
-[Route("api/bff")]
-public class BffDataController : ControllerBase
-{
- // EN: DB host configurable via env var (Docker: "postgres", dev: "localhost")
- // VI: DB host cấu hình qua env var (Docker: "postgres", dev: "localhost")
- private static readonly string _dbHost = Environment.GetEnvironmentVariable("BFF_DB_HOST") ?? "localhost";
- private static readonly string _dbPort = Environment.GetEnvironmentVariable("BFF_DB_PORT") ?? "5432";
- private static readonly string _dbUser = Environment.GetEnvironmentVariable("BFF_DB_USER") ?? "goodgo";
- private static readonly string _dbPass = Environment.GetEnvironmentVariable("BFF_DB_PASS") ?? "goodgo_dev_2024";
-
- private static string ConnStr(string db) =>
- $"Host={_dbHost};Port={_dbPort};Database={db};Username={_dbUser};Password={_dbPass}";
-
- // ═══ TENANT RESOLUTION HELPERS ═══
-
- ///
- /// EN: Extract user ID from JWT token in Authorization header.
- /// Parses the token manually without middleware validation.
- /// VI: Trích xuất user ID từ JWT token trong header Authorization.
- /// Parse token thủ công không cần middleware validation.
- ///
- private Guid? GetUserIdFromToken()
- {
- var authHeader = Request.Headers["Authorization"].FirstOrDefault();
- if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
- return null;
-
- var tokenStr = authHeader["Bearer ".Length..].Trim();
- try
- {
- var handler = new JwtSecurityTokenHandler();
- var jwt = handler.ReadJwtToken(tokenStr);
- var sub = jwt.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;
- if (!string.IsNullOrEmpty(sub) && Guid.TryParse(sub, out var userId))
- return userId;
- }
- catch { /* Invalid token — return null */ }
-
- return null;
- }
-
- ///
- /// EN: Extract current user's merchant ID from JWT → merchants table.
- /// VI: Lấy merchant ID của user hiện tại từ JWT → bảng merchants.
- ///
- private async Task GetCurrentMerchantIdAsync()
- {
- var userId = GetUserIdFromToken();
- if (userId == null)
- return null;
-
- await using var conn = new NpgsqlConnection(ConnStr("merchant_service"));
- return await conn.QueryFirstOrDefaultAsync(
- "SELECT id FROM merchants WHERE user_id = @UserId AND is_deleted = false",
- new { UserId = userId });
- }
-
- ///
- /// EN: Get list of shop IDs owned by the current merchant.
- /// VI: Lấy danh sách shop IDs thuộc sở hữu của merchant hiện tại.
- ///
- private async Task> GetMyShopIdsAsync(Guid merchantId)
- {
- await using var conn = new NpgsqlConnection(ConnStr("merchant_service"));
- var ids = await conn.QueryAsync(
- "SELECT id FROM shops WHERE merchant_id = @MerchantId AND is_deleted = false",
- new { MerchantId = merchantId });
- return ids.ToList();
- }
-
- // ═══ SHOP ENDPOINTS ═══
-
- [HttpGet("shops")]
- public async Task GetShops()
- {
- var merchantId = await GetCurrentMerchantIdAsync();
- if (merchantId == null)
- return Ok(Array.Empty