student před 3 týdny
rodič
revize
d679232061

+ 134 - 1
website/Areas/LotteryV2/Controllers/HomeController.cs

@@ -703,7 +703,7 @@ namespace LotteryWebApp.Areas.LotteryV2.Controllers
         }
 
         [HttpPost]
-        public IActionResult ConfirmBuyingTicket([FromBody] ConfirmBuyingTicketRequest request)
+        public IActionResult ConfirmBuyingTicketV2([FromBody] ConfirmBuyingTicketRequest request)
         {
             try
             {
@@ -711,6 +711,23 @@ namespace LotteryWebApp.Areas.LotteryV2.Controllers
                 var msisdn = HttpContext.Session.GetComplexData<string>("msisdn");
                 if (string.IsNullOrEmpty(token)) return Json(new { responseCode = Code.SESSION_EXPIRED, responseMessage = "Session expired" });
 
+                // Step 1: Verify OTP first
+                ConfirmOTPRequest otpRequest = new ConfirmOTPRequest
+                {
+                    otp = request.paymentCode,
+                    msisdn = msisdn,
+                    token = token,
+                    language = CultureInfo.CurrentCulture.Name.StartsWith("en") ? "0" : "1",
+                    channel = configuration.GetSection("channel").Value
+                };
+                ConfirmOTPResponse otpResponse = api.ConfirmOTPApi(configuration, otpRequest);
+
+                if (otpResponse.responseCode != Code.SUCCESS)
+                {
+                    return Json(new { responseCode = otpResponse.responseCode, responseMessage = otpResponse.responseMessage });
+                }
+
+                // Step 2: Proceed to Confirm buying
                 request.token = token;
                 request.msisdn = msisdn;
                 request.requestId = Guid.NewGuid().ToString();
@@ -718,6 +735,10 @@ namespace LotteryWebApp.Areas.LotteryV2.Controllers
                 request.channel = configuration.GetSection("channel").Value;
 
                 ConfirmBuyingTicketResponse response = api.ConfirmBuyingTicketApi(configuration, request);
+                if (response.responseCode == Code.SUCCESS)
+                {
+                    UpdateUserStatus(msisdn, token);
+                }
                 return Json(response);
             }
             catch (Exception ex)
@@ -727,6 +748,41 @@ namespace LotteryWebApp.Areas.LotteryV2.Controllers
             }
         }
 
+        [HttpPost]
+        public IActionResult ConfirmBuyingTicket([FromBody] ConfirmBuyingTicketRequest request)
+        {
+            try
+            {
+                var token = HttpContext.Session.GetComplexData<string>("token");
+                var msisdn = HttpContext.Session.GetComplexData<string>("msisdn");
+                if (string.IsNullOrEmpty(token)) return Json(new { responseCode = Code.SESSION_EXPIRED, responseMessage = "Session expired" });
+
+                request.token = token;
+                request.msisdn = msisdn;
+                request.requestId = Guid.NewGuid().ToString();
+                request.language = CultureInfo.CurrentCulture.Name.StartsWith("en") ? "0" : "1";
+                request.channel = configuration.GetSection("channel").Value;
+
+                ConfirmBuyingTicketResponse response = api.ConfirmBuyingTicketApi(configuration, request);
+                if (response.responseCode == Code.SUCCESS)
+                {
+                    UpdateUserStatus(msisdn, token);
+                }
+                return Json(new { 
+                    responseCode = response.responseCode, 
+                    responseMessage = response.responseMessage,
+                    transId = response.transId,
+                    orderId = response.orderId,
+                    userStatus = HttpContext.Session.GetComplexData<UserStatus>("userStatus")
+                });
+            }
+            catch (Exception ex)
+            {
+                log.Error(ex);
+                return Json(new { responseCode = Code.ERROR, responseMessage = ex.Message });
+            }
+        }
+
         public IActionResult BuyTicket(string termType)
         {
             try
@@ -788,6 +844,83 @@ namespace LotteryWebApp.Areas.LotteryV2.Controllers
             return View(model);
         }
 
+        [HttpPost]
+        public IActionResult ConfirmTransfer(string otp, string phone, string amount)
+        {
+            try
+            {
+                var token = HttpContext.Session.GetComplexData<string>("token");
+                var msisdn = HttpContext.Session.GetComplexData<string>("msisdn");
+                if (string.IsNullOrEmpty(token)) return Json(new { status = Code.SESSION_EXPIRED, message = "Session expired" });
+
+                // Step 1: Verify OTP
+                ConfirmOTPRequest otpRequest = new ConfirmOTPRequest
+                {
+                    otp = otp,
+                    msisdn = msisdn,
+                    token = token,
+                    language = CultureInfo.CurrentCulture.Name.StartsWith("en") ? "0" : "1",
+                    channel = configuration.GetSection("channel").Value
+                };
+                ConfirmOTPResponse otpResponse = api.ConfirmOTPApi(configuration, otpRequest);
+
+                if (otpResponse.responseCode != Code.SUCCESS)
+                {
+                    return Json(new { status = otpResponse.responseCode, message = otpResponse.responseMessage });
+                }
+
+                // Step 2: If OTP success, call Transfer Money Api
+                TransferMoneyRequest xferRequest = new TransferMoneyRequest
+                {
+                    msisdn = msisdn,
+                    msisdnReceive = phone,
+                    money = amount,
+                    otp = otp,
+                    token = token,
+                    channelPayment = Constants.BASIC_WALLET_TRANSFER,
+                    language = CultureInfo.CurrentCulture.Name.StartsWith("en") ? "0" : "1",
+                    channel = configuration.GetSection("channel").Value
+                };
+
+                TransferMoneyResponse xferResponse = api.TransferMoneyApi(configuration, xferRequest);
+
+                if (xferResponse.responseCode == Code.SUCCESS)
+                {
+                    UpdateUserStatus(msisdn, token);
+                }
+
+                return Json(new { 
+                    status = xferResponse.responseCode, 
+                    message = xferResponse.responseMessage,
+                    paymentCode = xferResponse.paymentCode,
+                    responseCode = xferResponse.responseCode,
+                    userStatus = HttpContext.Session.GetComplexData<UserStatus>("userStatus")
+                });
+            }
+            catch (Exception ex)
+            {
+                log.Error(ex);
+                return Json(new { status = Code.ERROR, message = ex.Message });
+            }
+        }
+
+        private void UpdateUserStatus(string msisdn, string token)
+        {
+            try
+            {
+                UserStatusRequest userStatusRequest = new UserStatusRequest { users = msisdn, token = token };
+                UserStatus userStatusGet = api.GetUserStatusApi(configuration, userStatusRequest);
+                if (userStatusGet != null)
+                {
+                    HttpContext.Session.SetComplexData("userStatus", userStatusGet);
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("UpdateUserStatus Error: " + ex.Message);
+            }
+        }
+
         public IActionResult Logout()
         {
             ClearCache();

+ 31 - 44
website/Areas/LotteryV2/Views/Home/BuyTicket.cshtml

@@ -400,10 +400,13 @@ else
                         }
                     </div>
         
+                    <!-- OTP Error Message -->
+                    <p id="otpError" class="text-[#EE0033] font-bold text-[14px] text-center mb-1 hidden animate__animated animate__shakeX"></p>
+
                     <!-- Timer & Resend -->
                     <div class="flex flex-col items-center gap-1 mt-0">
                         <span id="otpTimer" class="text-[15px] font-black text-[#EE0033] font-bricolage">60s</span>
-                        <button id="resendOtpBtn" onclick="sendOtpRequest()" disabled class="text-[14px] font-black text-[#0A9800] underline decoration-solid underline-offset-[3px] transition-all hover:text-[#087a00] disabled:opacity-40 disabled:no-underline font-bricolage">@Lang.v2_request_new_otp</button>
+                        <button id="resendOtpBtn" onclick="sendOtpRequest()" class="hidden text-[14px] font-black text-[#0A9800] underline decoration-solid underline-offset-[3px] transition-all hover:text-[#087a00] disabled:opacity-40 disabled:no-underline font-bricolage">@Lang.v2_request_new_otp</button>
                     </div>
         
                     <!-- Action Buttons: Cancel / Confirm -->
@@ -735,18 +738,14 @@ else
                 btn.disabled = true;
             }
 
-            // Gọi ConfirmTicketData API (giống nhau cho tất cả loại game)
-            // Pick 10: code = "21,26,40,47,48,52,67,69,71,74"
-            // Big/Small: code = "B" hoặc "S"
-            // Odd/Even: code = "O" hoặc "E"
-            console.log("[DEBUG] ConfirmTicketData request:", JSON.stringify(requestData));
+
             $.ajax({
                 url: subDomain + '@Url.Action("ConfirmTicketData", "Home")',
                 type: 'POST',
                 contentType: 'application/json',
                 data: JSON.stringify(requestData),
                 success: function(data) {
-                    console.log("[DEBUG] ConfirmTicketData response:", JSON.stringify(data));
+
                     if (btn) {
                         btn.innerHTML = originalText;
                         btn.disabled = false;
@@ -758,12 +757,12 @@ else
                     }
                 },
                 error: function(err) {
-                    console.error("[DEBUG] ConfirmTicketData AJAX Error:", err);
+
                     if (btn) {
                         btn.innerHTML = originalText;
                         btn.disabled = false;
                     }
-                    console.error("AJAX Error:", err);
+
                     showNotification("Network error occurred.", "warning");
                 }
             });
@@ -858,6 +857,7 @@ else
             hideOrderSummary();
             $("#otpModal").removeClass("hidden").addClass("flex");
             $("#otpInputs input").val("");
+            $("#otpError").addClass("hidden").text("");
             $("#otp1").focus();
             
             // Map game type to readable name & image
@@ -906,7 +906,8 @@ else
 
         function startOtpTimer(seconds) {
             let timeLeft = seconds;
-            $("#resendOtpBtn").prop("disabled", true);
+            $("#resendOtpBtn").addClass("hidden");
+            $("#otpTimer").removeClass("hidden").text(timeLeft + "s");
             clearInterval(otpInterval);
             
             otpInterval = setInterval(() => {
@@ -914,8 +915,8 @@ else
                 $("#otpTimer").text(timeLeft + "s");
                 if (timeLeft <= 0) {
                     clearInterval(otpInterval);
-                    $("#resendOtpBtn").prop("disabled", false);
-                    $("#otpTimer").text("");
+                    $("#otpTimer").addClass("hidden");
+                    $("#resendOtpBtn").removeClass("hidden");
                 }
             }, 1000);
         }
@@ -924,6 +925,8 @@ else
             if (el.value.length >= 1) {
                 if (nextId) document.getElementById(nextId).focus();
             }
+            // Clear error when typing
+            $("#otpError").addClass("hidden").text("");
         }
 
         function sendOtpRequest() {
@@ -991,46 +994,30 @@ else
                 paymentCode: otpCode
             };
 
-            // STEP 1: Confirm OTP API
+            // Call the unified backend action that verifies OTP and then buys the ticket
             $.ajax({
-                url: subDomain + '@Url.Action("ConfirmOTP", "Home")',
+                url: subDomain + '@Url.Action("ConfirmBuyingTicketV2", "Home")',
                 type: 'POST',
                 contentType: 'application/json',
-                data: JSON.stringify({ otp: otpCode }),
-                success: function(otpResp) {
-                    // Check if OTP validation succeeds
-                    if (otpResp.responseCode === "0" || otpResp.responseCode === 200 || otpResp.responseCode === "0000") {
-                        
-                        // STEP 2: Confirm Buying Ticket API
-                        $.ajax({
-                            url: subDomain + '@Url.Action("ConfirmBuyingTicket", "Home")',
-                            type: 'POST',
-                            contentType: 'application/json',
-                            data: JSON.stringify(finalData),
-                            success: function(buyResp) {
-                                $(btn).prop('disabled', false).html(originalText);
-                                
-                                if (buyResp.responseCode === "0" || buyResp.responseCode === "0000") {
-                                    // Open Figma styled success modal!
-                                    showReceiptSuccess(buyResp.transId || currentTransId, $("#summaryTotalAmount").text(), "@(Model.userStatus?.msisdn ?? "-")");
-                                } else {
-                                    showNotification(buyResp.responseMessage || "Payment failed", "warning");
-                                }
-                            },
-                            error: function() {
-                                $(btn).prop('disabled', false).html(originalText);
-                                showNotification("Network error occurred during payment completion.", "warning");
-                            }
-                        });
-
+                data: JSON.stringify(finalData),
+                success: function(res) {
+                    $(btn).prop('disabled', false).html(originalText);
+                    
+                    if (res.responseCode === "0" || res.responseCode === "0000") {
+                        // Success: Show receipt Modal
+                        showReceiptSuccess(res.transId || currentTransId, $("#summaryTotalAmount").text(), "@(Model.userStatus?.msisdn ?? "-")");
                     } else {
-                        $(btn).prop('disabled', false).html(originalText);
-                        showNotification(otpResp.responseMessage || "Invalid OTP code", "warning");
+                        // showNotification(res.responseMessage || "Payment failed", "warning");
+                        // As requested: show error on OTP modal instead of whole page notification
+                        $("#otpError").text(res.responseMessage || "Invalid OTP").removeClass("hidden");
+                        // Clear inputs
+                        $("#otpInputs input").val("");
+                        $("#otp1").focus();
                     }
                 },
                 error: function() {
                     $(btn).prop('disabled', false).html(originalText);
-                    showNotification("Network error while validating OTP.", "warning");
+                    showNotification("Network error occurred during payment completion.", "warning");
                 }
             });
         }

+ 5 - 6
website/Areas/LotteryV2/Views/Home/GameHome.cshtml

@@ -19,8 +19,8 @@
             <div>
                 <div class="font-bold text-sm tracking-wide opacity-90">@(Model?.profile?.fullName ?? "User")</div>
                 <div class="flex items-center gap-1 mt-0.5">
-                    <span class="text-3xl font-black text-[#FBF3A7]" style="text-shadow: 0 1px 2px rgba(0,0,0,0.2)">@(Model?.userStatus?.cash_coin ?? "0")</span>
-                    <span class="text-xs font-bold text-[#FBF3A7] uppercase mt-1">htg</span>
+                    <span class="text-3xl font-black text-[#FBF3A7]" style="text-shadow: 0 1px 2px rgba(0,0,0,0.2)">@(Model?.userStatus?.bet_coin ?? "0")</span>
+                    <span class="text-xs font-bold text-[#FBF3A7] uppercase mt-1">@Lang.v2_htg</span>
                 </div>
             </div>
         </div>
@@ -88,7 +88,7 @@
                 </div>
                 <div class="col-span-4 flex flex-col items-center text-center">
                     <div class="text-[12px] uppercase font-bold text-white/80">@Lang.jackpot_prize</div>
-                    <div class="text-xl font-extrabold text-[#FBF3A7] tracking-wider">100.000 <span class="text-[10px] font-bold">HTG</span></div>
+                    <div class="text-xl font-extrabold text-[#FBF3A7] tracking-wider">100.000 <span class="text-[10px] font-bold">@Lang.v2_htg</span></div>
                     <div class="text-[10px] mt-1 flex gap-1 items-center">@Lang.next_round <span class="font-bold text-yellow-300">05:00</span></div>
                 </div>
                 <div class="col-span-3 flex flex-col justify-end items-center h-full relative">
@@ -110,7 +110,7 @@
                 </div>
                 <div class="col-span-4 flex flex-col items-center text-center">
                     <div class="text-[12px] uppercase font-bold text-white/80">@Lang.jackpot_prize</div>
-                    <div class="text-xl font-extrabold text-[#FBF3A7] tracking-wider">100 <span class="text-[10px] font-bold">HTG</span></div>
+                    <div class="text-xl font-extrabold text-[#FBF3A7] tracking-wider">100 <span class="text-[10px] font-bold">@Lang.v2_htg</span></div>
                     <div class="text-[10px] mt-1 flex gap-1 items-center">@Lang.next_round <span class="font-bold text-yellow-300">05:00</span></div>
                 </div>
                 <div class="col-span-3 flex flex-col justify-end items-center h-full relative">
@@ -133,7 +133,7 @@
                 </div>
                 <div class="col-span-4 flex flex-col items-center text-center">
                     <div class="text-[12px] uppercase font-bold text-white/80">@Lang.jackpot_prize</div>
-                    <div class="text-xl font-extrabold text-[#FBF3A7] tracking-wider">200 <span class="text-[10px] font-bold">HTG</span></div>
+                    <div class="text-xl font-extrabold text-[#FBF3A7] tracking-wider">200 <span class="text-[10px] font-bold">@Lang.v2_htg</span></div>
                     <div class="text-[10px] mt-1 flex gap-1 items-center">@Lang.next_round <span class="font-bold text-yellow-300">05:00</span></div>
                 </div>
                 <div class="col-span-3 flex flex-col justify-end items-center h-full relative">
@@ -239,7 +239,6 @@
                     isCheckingTerm = false;
                 },
                 error: function (err) {
-                    console.error("AJAX Error:", err);
                     showNotification("@Lang.error_happened");
                     isCheckingTerm = false;
                 }

+ 0 - 1
website/Areas/LotteryV2/Views/Home/History.cshtml

@@ -118,7 +118,6 @@
                     updatePaginationUI();
                 })
                 .catch(error => {
-                    console.error('Error fetching history:', error);
                     container.style.opacity = "1";
                 });
         }

+ 5 - 5
website/Areas/LotteryV2/Views/Home/Index.cshtml

@@ -41,7 +41,7 @@
                     <!-- Left: Module and Ball component -->
                     <div class="col-span-3 flex flex-col items-start justify-center pl-2">
                         <div class="relative">
-                            <div class="text-[13px] font-black italic tracking-wider text-white">Basic</div>
+                            <div class="text-[13px] font-black italic tracking-wider text-white">@Lang.Basic</div>
                             <div class="text-[14px] font-black italic tracking-wider text-yellow-300 -mt-1">Pick</div>
                             <!-- Yellow number style ball -->
                             <div class="absolute left-10 top-2 w-7 h-7 bg-gradient-to-br from-yellow-300 to-yellow-500 rounded-full border border-white flex items-center justify-center shadow-md">
@@ -52,15 +52,15 @@
 
                     <!-- Center: Dynamic text layer aligned for Translation -->
                     <div class="col-span-4 flex flex-col items-center justify-center text-center">
-                        <div class="text-[10px] uppercase font-bold text-gray-200 tracking-wider">Jackpot Prize</div>
-                        <div class="text-2xl font-black text-yellow-400 mt-0.5" style="text-shadow: 0 2px 4px rgba(0,0,0,0.3)">100.000<span class="text-xs font-normal text-white"> HTG</span></div>
-                        <div class="text-[9px] text-gray-200 mt-1 flex gap-1">Next round <span class="font-bold text-white">05:00</span></div>
+                        <div class="text-[10px] uppercase font-bold text-gray-200 tracking-wider">@Lang.jackpot_prize</div>
+                        <div class="text-2xl font-black text-yellow-400 mt-0.5" style="text-shadow: 0 2px 4px rgba(0,0,0,0.3)">100.000<span class="text-xs font-normal text-white"> @Lang.v2_htg</span></div>
+                        <div class="text-[9px] text-gray-200 mt-1 flex gap-1">@Lang.next_round <span class="font-bold text-white">05:00</span></div>
                     </div>
 
                     <!-- Right: Illustration from image overlay & button -->
                     <div class="col-span-3 flex flex-col items-center justify-end h-full pb-2 relative">
                         <img src="/LotteryV2/img/gold.png" alt="Chest" class="absolute -top-1 right-2 w-14 h-14 object-contain animate__animated animate__pulse animate__infinite animate__slow">
-                        <button class="bg-gradient-to-b from-yellow-300 to-yellow-400 text-red-700 font-extrabold px-3 py-1.5 rounded-xl text-[10px] shadow-md hover:from-yellow-200 hover:to-yellow-300" onclick="changeGame()" id="MEGA_LOTO_ID" value="@Constants.MEGA_LOTO">Play now</button>
+                        <button class="bg-gradient-to-b from-yellow-300 to-yellow-400 text-red-700 font-extrabold px-3 py-1.5 rounded-xl text-[10px] shadow-md hover:from-yellow-200 hover:to-yellow-300" onclick="changeGame()" id="MEGA_LOTO_ID" value="@Constants.MEGA_LOTO">@Lang.play</button>
                     </div>
                 </div>
             </div>

+ 3 - 1
website/Areas/LotteryV2/Views/Home/More.cshtml

@@ -102,6 +102,8 @@
             .then(response => {
                 location.reload();
             })
-            .catch(error => console.error('Error changing language:', error));
+            .catch(error => {
+                location.reload();
+            });
     }
 </script>

+ 0 - 1
website/Areas/LotteryV2/Views/Home/Profile.cshtml

@@ -207,7 +207,6 @@
             error: function (error) {
                 $btn.prop('disabled', false).html(originalText);
                 showFailureModal("An error occurred during network request");
-                console.log(error);
             }
         });
     }

+ 0 - 1
website/Areas/LotteryV2/Views/Home/Results.cshtml

@@ -154,7 +154,6 @@
                     btn.innerHTML = '<i class="fas fa-search"></i>';
                 })
                 .catch(error => {
-                    console.error('Error fetching results:', error);
                     container.style.opacity = "1";
                     btn.disabled = false;
                     btn.innerHTML = '<i class="fas fa-search"></i>';

+ 1 - 1
website/Areas/LotteryV2/Views/Home/Rule.cshtml

@@ -361,7 +361,7 @@
         </div>
         <ul class="bullet-list mb-4">
             <li>@Lang.rule_system_draws_20_01_80</li>
-            <li>For this game, the system will count how many of the 20 drawn numbers are:
+            <li>@Lang.v2_rule_drawn_count_desc
                 <ul class="list-disc pl-5 mt-1 text-[#212121]">
                     <li>@Lang.rule_odd_numbers</li>
                     <li>@Lang.rule_even_numbers</li>

+ 206 - 38
website/Areas/LotteryV2/Views/Home/TransferWinMoney.cshtml

@@ -34,7 +34,7 @@
             <div class="relative z-10">
                 <p class="text-white/80 font-bold text-[14px] uppercase tracking-wider">@Lang.v2_account_name</p>
                 <div class="flex items-end gap-2 mt-2">
-                    <span class="text-white font-[900] text-[36px] leading-[0.9]">@String.Format("{0:N0}", double.TryParse(Model?.userStatus?.cash_coin, out var cc) ? cc : 0)</span>
+                    <span id="currentBetCoin" class="text-white font-[900] text-[36px] leading-[0.9]">@String.Format("{0:N0}", double.TryParse(Model?.userStatus?.bet_coin, out var cc) ? cc : 0)</span>
                     <span class="text-[#FBF3A7] font-[900] text-[16px] uppercase pb-1">htg</span>
                 </div>
             </div>
@@ -43,7 +43,7 @@
                 <div class="w-10 h-10 bg-white/20 backdrop-blur-md rounded-full flex items-center justify-center border border-white/30">
                     <img src="/LotteryV2/img/transfer_wallet_icon.png" alt="Wallet" class="w-5 h-5 object-contain" />
                 </div>
-                <span class="text-white/90 font-bold text-[14px]">Available Balance</span>
+                <!-- <span class="text-white/90 font-bold text-[14px]">Available Balance</span> -->
             </div>
         </div>
 
@@ -130,8 +130,64 @@
         </div>
     </div>
 
-    <!-- Failure Modal Overlay (Standardized Height as per user request) -->
-    <div id="failureModal" class="fixed inset-0 z-[110] flex items-center justify-center hidden px-6 translate-y-0" style="background: rgba(0,0,0,0.5);">
+    <!-- Success Modal Overlay -->
+    <div id="successModal" class="fixed inset-0 z-[110] flex items-center justify-center hidden px-6" style="background: rgba(0,0,0,0.7);">
+        <div class="w-full max-w-[370px] bg-white rounded-[32px] overflow-hidden flex flex-col items-center animate__animated animate__zoomIn animate__faster shadow-2xl relative">
+            
+            <!-- Confetti Background (Uses existing if available, else decorative fallback) -->
+            <img src="/LotteryV2/img/modal/otp_glitter.png" class="absolute top-0 left-0 w-full opacity-40 pointer-events-none" />
+
+            <!-- Content Container -->
+            <div class="relative z-10 w-full flex flex-col items-center pt-10 pb-8 px-6">
+                
+                <!-- Success Icon Group -->
+                <div class="relative w-[120px] h-[120px] mb-4">
+                    <div class="absolute inset-0 bg-[#F5F5F5] rounded-full scale-110"></div>
+                    <img src="/LotteryV2/img/modal/otp_success.png" class="relative z-20 w-full h-full object-contain drop-shadow-[0_0_8px_rgba(240,201,63,0.8)]" />
+                    <!-- Sparkles (Using existing SVG icons if possible) -->
+                    <img src="/LotteryV2/img/modal/otp_sparkle_1.svg" class="absolute -bottom-2 -left-2 w-8 z-30 drop-shadow-[0_0_4px_rgba(240,201,63,1)]" />
+                    <img src="/LotteryV2/img/modal/otp_sparkle_2.svg" class="absolute top-10 -right-6 w-10 z-30 drop-shadow-[0_0_4px_rgba(240,201,63,1)] opacity-70" />
+                </div>
+
+                <h2 class="text-[#0A9800] font-[800] text-[32px] mb-1 tracking-tight">@Lang.success</h2>
+                <p class="text-[#000000] font-[700] text-[20px] text-center px-4 mb-6 leading-tight">
+                    @Lang.v2_payment_successfully
+                </p>
+
+                <div class="w-full border-t border-dashed border-gray-200 pt-5 flex flex-col gap-3 mb-5">
+                    <!-- Info Rows -->
+                    <div class="flex justify-between items-center text-[15px]">
+                        <span class="text-[#534A4A] font-bold">@Lang.amount_transfered</span>
+                        <span id="successAmount" class="text-black font-black text-right">5.000 HTG</span>
+                    </div>
+                    <div class="flex justify-between items-center text-[15px]">
+                        <span class="text-[#534A4A] font-bold">@Lang.sender</span>
+                        <span id="successSender" class="text-black font-black text-right">50940236545</span>
+                    </div>
+                    <div class="flex justify-between items-center text-[15px]">
+                        <span class="text-[#534A4A] font-bold">@Lang.receiver</span>
+                        <span id="successReceiver" class="text-black font-black text-right">50940236545</span>
+                    </div>
+                    <div class="flex justify-between items-center text-[15px]">
+                        <span class="text-[#534A4A] font-bold">@Lang.fee</span>
+                        <span class="text-black font-black text-right">0 HTG</span>
+                    </div>
+                </div>
+
+                <div class="w-full border-t border-dashed border-gray-200 pt-4 flex justify-between items-center mb-8">
+                    <span class="text-[#534A4A] font-bold text-[15px]">@Lang.time</span>
+                    <span id="successTime" class="text-[#534A4A] font-medium text-[14px] text-right">08/01/2022 - 10:00:20</span>
+                </div>
+
+                <button onclick="window.location.href='/LotteryV2/Home/GameHome'" class="w-full bg-[#EE0033] text-white font-[800] text-[20px] py-[14px] rounded-[14px] shadow-lg active:scale-95 transition-all">
+                    @Lang.back_to_homepage
+                </button>
+            </div>
+        </div>
+    </div>
+
+    <!-- Failure Modal Overlay -->
+    <div id="failureModal" class="fixed inset-0 z-[110] flex items-center justify-center hidden px-6" style="background: rgba(0,0,0,0.5);">
         <div class="w-full max-w-[343px] min-h-[420px] bg-white rounded-[20px] overflow-hidden flex flex-col items-center p-8 animate__animated animate__zoomIn animate__faster shadow-2xl">
             <div class="w-full flex flex-col items-center mb-6 mt-4">
                 <div class="relative w-full flex items-center justify-center">
@@ -153,27 +209,69 @@
 
 
     <!-- OTP Modal Overlay -->
-    <div id="otpModal" class="fixed inset-0 z-[120] flex items-center justify-center hidden px-6 translate-y-0" style="background: rgba(0,0,0,0.5);">
-        <div class="w-full max-w-[343px] min-h-[420px] bg-white rounded-[20px] overflow-hidden flex flex-col items-center p-6 animate__animated animate__zoomIn animate__faster shadow-2xl relative">
-            <!-- Decorative Header Circle -->
-            <div class="absolute -top-12 left-1/2 -translate-x-1/2 w-[400px] h-[80px] bg-[#EE0033] rounded-full opacity-10"></div>
-            
-            <h3 class="text-[#534A4A] font-black text-[22px] mt-4 mb-2">@Lang.v2_confirm_transaction</h3>
-            <p class="text-gray-500 font-bold text-[14px] text-center px-4 mb-6">@Lang.v2_enter_otp_proceed</p>
+    <div id="otpModal" class="fixed inset-0 z-[120] flex items-center justify-center hidden px-6" style="background: rgba(0,0,0,0.7);">
+        <!-- Glitter/Sparkles Overlay -->
+        <img src="/LotteryV2/img/modal/otp_glitter.png" class="absolute pointer-events-none opacity-60 w-full max-w-[500px]" />
+
+        <div class="w-full max-w-[360px] bg-white rounded-[32px] overflow-visible flex flex-col items-center animate__animated animate__zoomIn animate__faster relative border-4 border-[#FFCD1D]/10" style="box-shadow: 0 0 25px rgba(229, 107, 35, 0.6);">
             
-            <!-- OTP Inputs -->
-            <div class="flex gap-3 mb-4" id="otpInputs">
-                <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" class="otp-digit w-12 h-14 bg-gray-100 border-2 border-transparent focus:border-[#EE0033] rounded-xl text-center text-[24px] font-black outline-none transition-all" />
-                <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" class="otp-digit w-12 h-14 bg-gray-100 border-2 border-transparent focus:border-[#EE0033] rounded-xl text-center text-[24px] font-black outline-none transition-all" />
-                <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" class="otp-digit w-12 h-14 bg-gray-100 border-2 border-transparent focus:border-[#EE0033] rounded-xl text-center text-[24px] font-black outline-none transition-all" />
-                <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" class="otp-digit w-12 h-14 bg-gray-100 border-2 border-transparent focus:border-[#EE0033] rounded-xl text-center text-[24px] font-black outline-none transition-all" />
+            <!-- Gold Coins on Top -->
+            <img src="/LotteryV2/img/modal/otp_coins.png" class="absolute -top-12 -right-2 w-32 z-30 pointer-events-none drop-shadow-lg" />
+
+            <!-- Red Header -->
+            <div class="w-full bg-[#EE0033] pt-5 pb-4 px-6 rounded-t-[32px] flex justify-center border-b-4 border-white/20">
+                <h3 class="text-white font-[900] text-[22px] tracking-tight text-center">@Lang.v2_confirm_transaction</h3>
             </div>
-            
-            <span id="otpTimer" class="text-[#EE0033] font-black text-[16px] mb-8">60s</span>
-            
-            <div class="w-full flex gap-3 mt-auto">
-                <button onclick="closeOtpModal()" class="flex-1 bg-gray-200 text-[#534A4A] font-black py-3 rounded-xl active:scale-95 transition-all text-[18px]">@Lang.cancel</button>s
-                <button id="btnConfirmTransfer" class="flex-1 bg-[#0A9800] text-white font-black py-3 rounded-xl active:scale-95 transition-all text-[18px]">@Lang.confirm</button>
+
+            <div class="relative z-10 w-full flex flex-col items-center p-6 py-6 pb-8">
+                
+                <!-- Shield Icon -->
+                <div class="flex flex-col items-center gap-1 mb-2">
+                    <img src="/LotteryV2/img/modal/otp_shield.png" class="w-[90px] h-auto object-contain drop-shadow-md" />
+                    <span class="text-[#534A4A] font-bold text-[14px]">@Lang.v2_convert</span>
+                </div>
+
+                <!-- Conversion Display Area -->
+                <div class="flex items-center justify-center gap-2 mb-4">
+                    <span id="displayAmountCoins" class="text-[#EE0033] font-[900] text-[24px]">100</span>
+                    <span class="text-black font-[900] text-[24px]">@Lang.v2_coins</span>
+                    <i class="fa-solid fa-arrow-right text-gray-800 text-[20px] mx-1"></i>
+                    <span id="displayAmountHTG" class="text-[#EE0033] font-[900] text-[24px]">100</span>
+                    <span class="text-black font-[900] text-[24px]">@Lang.v2_htg</span>
+                </div>
+
+                <p class="text-[#534A4A] font-bold text-[14px] text-center px-4 mb-6 leading-tight">@Lang.v2_enter_otp_proceed</p>
+                
+                <!-- OTP Inputs Area -->
+                <div class="flex gap-2 mb-4" id="otpInputs">
+                    <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
+                           class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
+                    <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
+                           class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
+                    <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
+                           class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
+                    <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
+                           class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
+                    <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
+                           class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
+                    <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
+                           class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
+                </div>
+                
+                <!-- OTP Error Message -->
+                <p id="otpError" class="text-[#EE0033] font-bold text-[14px] text-center mb-4 hidden animate__animated animate__shakeX"></p>
+
+                <!-- Timer & Resend Display -->
+                <div class="flex flex-col items-center justify-center mb-6 gap-2">
+                    <span id="otpTimer" class="text-[#EE0033] font-[900] text-[18px]">60s</span>
+                    <button id="resendOtpBtn" onclick="sendOtpRequest()" class="hidden text-[15px] font-black text-[#0A9800] underline decoration-solid underline-offset-[4px] hover:text-[#087a00] transition-all font-bricolage">@Lang.v2_request_new_otp</button>
+                </div>
+                
+                <!-- Action Buttons -->
+                <div class="w-full flex gap-4">
+                    <button onclick="closeOtpModal()" class="flex-1 bg-[#FF0000] text-white font-[900] py-2.5 rounded-[16px] active:scale-95 transition-all text-[13px] shadow-lg">@Lang.cancel</button>
+                    <button id="btnConfirmTransfer" class="flex-1 bg-[#0A9800] text-white font-[900] py-2.5 rounded-[16px] active:scale-95 transition-all text-[13px] shadow-lg">@Lang.confirm</button>
+                </div>
             </div>
         </div>
     </div>
@@ -232,7 +330,7 @@
                 success: function(res) {
                     $btn.prop('disabled', false).html(originalText);
                     isProcessing = false;
-                    if(res.status == "0" || res.status == 0) {
+                    if(res.responseCode == "0" || res.responseCode == 0) {
                         openOtpModal();
                     } else {
                         showFailureModal(res.message || "Failed to send OTP");
@@ -249,8 +347,15 @@
         function openOtpModal() {
             // Clear previous OTP values
             $(".otp-digit").val("");
+            
+            // Set display amounts
+            const amt = $("#transferAmount").val();
+            const formattedAmt = String(amt).replace(/\B(?=(\d{3})+(?!\d))/g, ".");
+            $("#displayAmountCoins").text(formattedAmt);
+            $("#displayAmountHTG").text(formattedAmt);
+
             $("#otpModal").removeClass("hidden").addClass("flex");
-            $("#btnConfirmTransfer").prop('disabled', false).html('Confirm');
+            $("#btnConfirmTransfer").prop('disabled', false).html('@Lang.confirm').removeClass('opacity-50');
             resetTimer();
             $(".otp-digit").first().focus();
         }
@@ -264,43 +369,83 @@
         function handleOtpInput(el) {
             // Allow only single digit
             el.value = el.value.replace(/[^0-9]/g, '').slice(0, 1);
+            
             if (el.value.length === 1) {
-                $(el).next('.otp-digit').focus();
+                const next = $(el).next('.otp-digit');
+                if (next.length) {
+                    next.focus();
+                }
             }
+
+            // Clear error when typing
+            $("#otpError").addClass("hidden").text("");
         }
 
         // OTP backspace: move to previous input
         function handleOtpKeydown(e, el) {
-            if (e.key === 'Backspace' && el.value.length === 0) {
-                $(el).prev('.otp-digit').focus();
+            if (e.key === 'Backspace') {
+                if (el.value.length === 0) {
+                    const prev = $(el).prev('.otp-digit');
+                    if (prev.length) {
+                        prev.focus();
+                    }
+                }
             }
         }
 
         function resetTimer() {
             let seconds = 60;
             clearInterval(timerInterval);
-            $("#otpTimer").text("60s");
-            $("#btnConfirmTransfer").prop('disabled', false);
+            $("#otpTimer").removeClass("hidden").text("60s");
+            $("#resendOtpBtn").addClass("hidden");
+            $("#btnConfirmTransfer").prop('disabled', false).removeClass('opacity-50');
+            
             timerInterval = setInterval(() => {
                 seconds--;
                 $("#otpTimer").text(seconds + "s");
                 if (seconds <= 0) {
                     clearInterval(timerInterval);
-                    $("#otpTimer").text("Expired");
+                    $("#otpTimer").addClass("hidden");
+                    $("#resendOtpBtn").removeClass("hidden");
                     // Disable confirm when timer expired
                     $("#btnConfirmTransfer").prop('disabled', true).addClass('opacity-50');
                 }
             }, 1000);
         }
 
+        function sendOtpRequest() {
+            const $btn = $("#resendOtpBtn");
+            const originalText = $btn.text();
+            $btn.prop('disabled', true).text("...");
+
+            $.ajax({
+                url: subDomain + "/LotteryV2/Home/SendOTP",
+                type: 'POST',
+                success: function(data) {
+                    $btn.prop('disabled', false).text(originalText);
+                    if (data.responseCode === "0") {
+                        resetTimer();
+                        $("#otpError").addClass("hidden").text("");
+                        $(".otp-digit").val("").first().focus();
+                    } else {
+                        $("#otpError").text(data.responseMessage || "Failed to resend OTP").removeClass("hidden");
+                    }
+                },
+                error: function() {
+                    $btn.prop('disabled', false).text(originalText);
+                    $("#otpError").text("Network error occurred.").removeClass("hidden");
+                }
+            });
+        }
+
         $("#btnConfirmTransfer").on("click", function() {
             if (isProcessing) return; // Prevent double-click
 
             let otp = "";
             $(".otp-digit").each(function() { otp += $(this).val(); });
             
-            if(otp.length < 4) {
-                showFailureModal("Please enter 4-digit OTP");
+            if(otp.length < 6) {
+                showFailureModal("Please enter 6-digit OTP");
                 return;
             }
 
@@ -310,7 +455,7 @@
             const originalText = $btn.html();
             $btn.prop('disabled', true).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i> ...');
             
-            // Call transfer confirm API
+            // Call the unified backend action that verifies OTP and then transfers
             $.ajax({
                 url: subDomain + "/LotteryV2/Home/ConfirmTransfer",
                 type: "POST",
@@ -322,12 +467,35 @@
                 success: function(res) {
                     $btn.prop('disabled', false).html(originalText);
                     isProcessing = false;
-                    if(res.status == "0" || res.status == 0) {
+
+                    if(res.responseCode == "0" || res.responseCode == 0) {
                         closeOtpModal();
-                        // Redirect to success or show success modal
-                        window.location.href = subDomain + "/LotteryV2/Home/GameHome";
+                        
+                        // Update balance in UI immediately
+                        if(res.userStatus && res.userStatus.bet_coin) {
+                           let balanceVal = parseFloat(res.userStatus.bet_coin);
+                           $("#currentBetCoin").text(balanceVal.toLocaleString());
+                        }
+
+                        // Populate Success Modal Fields
+                        const transferAmt = $("#transferAmount").val();
+                        $("#successAmount").text(parseFloat(transferAmt).toLocaleString() + " HTG");
+                        $("#successSender").text("@(Model?.userStatus?.msisdn ?? "0")");
+                        $("#successReceiver").text($("#receiverPhone").val());
+                        
+                        const now = new Date();
+                        const pad = (n) => n.toString().padStart(2, '0');
+                        const formattedTime = `${pad(now.getDate())}/${pad(now.getMonth() + 1)}/${now.getFullYear()} - ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
+                        $("#successTime").text(formattedTime);
+
+                        $("#successModal").removeClass("hidden").addClass("flex");
                     } else {
-                        showFailureModal(res.message || "Transfer failed");
+                        // showFailureModal(res.message || "Transfer failed");
+                        // As requested: show error on OTP modal instead of failing whole transaction modal
+                        $("#otpError").text(res.message || "Invalid OTP").removeClass("hidden");
+                        // Clear inputs
+                        $(".otp-digit").val("");
+                        $(".otp-digit").first().focus();
                     }
                 },
                 error: function() {

+ 36 - 0
website/Languages/Lang.Designer.cs

@@ -3284,6 +3284,15 @@ namespace LotteryWebApp.Languages {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Coins.
+        /// </summary>
+        public static string v2_coins {
+            get {
+                return ResourceManager.GetString("v2_coins", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Confirmer.
         /// </summary>
@@ -3320,6 +3329,15 @@ namespace LotteryWebApp.Languages {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Convert.
+        /// </summary>
+        public static string v2_convert {
+            get {
+                return ResourceManager.GetString("v2_convert", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Dat ak Lè.
         /// </summary>
@@ -3401,6 +3419,15 @@ namespace LotteryWebApp.Languages {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to HTG.
+        /// </summary>
+        public static string v2_htg {
+            get {
+                return ResourceManager.GetString("v2_htg", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to You do not have enough win money..
         /// </summary>
@@ -3500,6 +3527,15 @@ namespace LotteryWebApp.Languages {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to For this game, the system will count how many of the 20 drawn numbers are:.
+        /// </summary>
+        public static string v2_rule_drawn_count_desc {
+            get {
+                return ResourceManager.GetString("v2_rule_drawn_count_desc", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to Save.
         /// </summary>

+ 12 - 0
website/Languages/Lang.fr.resx

@@ -1346,4 +1346,16 @@ We’ll help you create an account in a few easy steps.</value>
   <data name="Account" xml:space="preserve">
     <value>Account</value>
   </data>
+  <data name="v2_convert" xml:space="preserve">
+    <value>Convert</value>
+  </data>
+  <data name="v2_coins" xml:space="preserve">
+    <value>Coins</value>
+  </data>
+  <data name="v2_htg" xml:space="preserve">
+    <value>HTG</value>
+  </data>
+  <data name="v2_rule_drawn_count_desc" xml:space="preserve">
+    <value>For this game, the system will count how many of the 20 drawn numbers are:</value>
+  </data>
 </root>

+ 10 - 0
website/Languages/Lang.resx

@@ -1346,4 +1346,14 @@ We’ll help you create an account in a few easy steps.</value>
   <data name="Account" xml:space="preserve">
     <value>Account</value>
   </data>
+  <data name="v2_convert" xml:space="preserve">
+    <value>Convert</value>
+  </data>
+  <data name="v2_coins" xml:space="preserve">
+    <value>Coins</value>
+  </data>
+  <data name="v2_htg" xml:space="preserve">
+    <value>HTG</value>
+  </data>
+  <data name="v2_rule_drawn_count_desc" xml:space="preserve"><value>For this game, the system will count how many of the 20 drawn numbers are:</value></data>
 </root>

+ 4 - 3
website/Properties/PublishProfiles/FolderProfile.pubxml

@@ -10,11 +10,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
     <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
     <LastUsedPlatform>Any CPU</LastUsedPlatform>
     <PublishProvider>FileSystem</PublishProvider>
-    <PublishUrl>D:\Common\Publish</PublishUrl>
+    <PublishUrl>E:\Ex_publish\LotoNatcomV2</PublishUrl>
     <WebPublishMethod>FileSystem</WebPublishMethod>
     <SiteUrlToLaunchAfterPublish />
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
     <ProjectGuid>847e0916-075b-4836-93bc-919c422fa9fe</ProjectGuid>
-    <SelfContained>false</SelfContained>
+    <SelfContained>true</SelfContained>
+    <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
   </PropertyGroup>
 </Project>

+ 267 - 57
website/Views/Account/ChooseApp.cshtml

@@ -2,86 +2,296 @@
     ViewData["Title"] = "Choose App";
     Layout = "~/Views/Shared/_NothingLayout.cshtml";
 }
-
+@using LotteryWebApp.Languages;
 <style>
+    /* Override the layout backgrounds */
     body {
-        /* Background same as login or custom */
-        background: linear-gradient(135deg, #1A1A2E 0%, #16213E 100%) !important;
-        color: white;
+        background: #2D0506 !important;
+        background-image: none !important;
+        margin: 0 !important;
+        padding: 0 !important;
     }
-    .choose-wrapper {
-        display: flex;
-        flex-direction: column;
-        justify-content: center;
-        align-items: center;
-        min-height: 100vh;
-        padding: 20px;
+
+    .all-screen {
+        max-width: 393px !important;
+        background: #2D0506 !important;
+        position: relative !important;
+        overflow: hidden !important;
+        height: 700px !important;
     }
-    .logo-header {
-        margin-bottom: 40px;
-        text-align: center;
+
+    .float-button-navigator-back-to-app {
+        display: none !important;
     }
-    .logo-header img {
-        max-width: 150px;
+
+    /* ========== CHOOSE APP CUSTOM STYLES ========== */
+    .ca-wrap {
+        position: relative;
+        width: 100%;
+        height: 100%;
+    }
+
+    /* Hero background - covers the ENTIRE container */
+    .ca-hero {
+        position: absolute;
+        top: 0;
+        left: -20%;
+        width: 140%;
+        height: 100%;
+        z-index: 0;
     }
-    .grid-container {
-        display: grid;
-        grid-template-columns: repeat(2, 1fr);
-        gap: 20px;
+    .ca-hero img {
         width: 100%;
-        max-width: 400px;
-    }
-    .module-card {
-        background: rgba(255, 255, 255, 0.1);
-        backdrop-filter: blur(10px);
-        border: 1px solid rgba(255, 255, 255, 0.2);
-        border-radius: 16px;
-        padding: 25px 15px;
+        height: 100%;
+        object-fit: cover;
+        object-position: center top;
+    }
+    /* Gradient fades the image into the dark background color */
+    .ca-hero-overlay {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: linear-gradient(180deg,
+            rgba(45, 5, 6, 0) 0%,
+            rgba(45, 5, 6, 0) 45%,
+            rgba(45, 5, 6, 0.6) 58%,
+            rgba(45, 5, 6, 0.9) 68%,
+            rgba(45, 5, 6, 1) 78%);
+    }
+
+    /* Logo */
+    .ca-logo-wrap {
+        position: absolute;
+        top: 10px;
+        left: 0;
+        right: 0;
+        z-index: 5;
         text-align: center;
-        transition: transform 0.2s, background 0.2s;
-        cursor: pointer;
+    }
+    .ca-logo-wrap img {
+        width: 275px;
+        height: auto;
+        filter: drop-shadow(0 6px 16px rgba(0,0,0,0.6));
+        display: inline-block;
+    }
+
+    /* Cards pinned to bottom */
+    .ca-cards-section {
+        position: absolute;
+        bottom: 20px;
+        left: 20px;
+        right: 20px;
+        z-index: 10;
         display: flex;
         flex-direction: column;
+        gap: 12px;
+    }
+
+    /* Card base */
+    .ca-game-card {
+        position: relative;
+        width: 100%;
+        height: 88px;
+        border-radius: 12px;
+        overflow: hidden;
+        display: flex;
         align-items: center;
+        justify-content: space-between;
+        padding: 0 12px;
         text-decoration: none !important;
-        color: white !important;
+        border: 2px solid rgba(255, 255, 255, 0.15);
+        box-sizing: border-box;
+        cursor: pointer;
     }
-    .module-card:hover {
-        transform: translateY(-5px);
-        background: rgba(255, 255, 255, 0.15);
-        border-color: #FF8603;
+    .ca-game-card:hover { opacity: 0.95; }
+    .ca-game-card:active { transform: scale(0.98); }
+
+    .ca-card-blue { background: #211ABF; }
+    .ca-card-orange { background: linear-gradient(90deg, #FF921E 0%, #FF1E1E 100%); }
+
+    /* Translucent circles (decorative) */
+    .ca-deco-circle {
+        position: absolute;
+        border-radius: 50%;
+        background: rgba(255, 255, 255, 0.07);
+        pointer-events: none;
     }
-    .module-icon {
-        font-size: 35px;
-        margin-bottom: 15px;
+
+    /* Left: game name + balls */
+    .ca-left {
+        display: flex;
+        align-items: center;
+        gap: 6px;
+        z-index: 2;
+        flex-shrink: 0;
     }
-    .module-title {
-        font-size: 14px;
-        font-weight: 600;
+    .ca-game-name {
+        font-family: 'Montserrat', sans-serif !important;
+        font-weight: 800 !important;
+        font-size: 22px;
+        color: #fff;
+        line-height: 1.1;
+        text-shadow: 0 2px 4px rgba(0,0,0,0.3);
     }
-    .module-sub {
-        font-size: 11px;
-        color: #ccc;
-        margin-top: 5px;
+    .ca-game-lotto {
+        font-family: 'Montserrat', sans-serif !important;
+        font-weight: 800 !important;
+        font-style: italic;
+        font-size: 30px;
+        line-height: 0.95;
+        color: #fff;
+        text-shadow: 0 2px 4px rgba(0,0,0,0.3);
+    }
+
+    /* Lottery balls */
+    .ca-ball {
+        width: 56px;
+        height: 56px;
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-family: 'Bricolage Grotesque', sans-serif !important;
+        font-weight: 800 !important;
+        color: #000;
+        position: relative;
+        box-shadow: 3px 3px 9px rgba(0,0,0,0.45);
+    }
+    .ca-ball-sm {
+        width: 44px;
+        height: 44px;
+    }
+    .ca-ball-md {
+        width: 52px;
+        height: 52px;
+    }
+    .ca-ball::after {
+        content: "";
+        position: absolute;
+        top: 12%;
+        left: 20%;
+        width: 50%;
+        height: 30%;
+        background: linear-gradient(180deg, rgba(255,255,255,0.65) 0%, rgba(255,255,255,0.05) 100%);
+        border-radius: 50%;
+    }
+
+    /* Right: asset image + button */
+    .ca-right {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        height: 100%;
+        z-index: 2;
+        flex-shrink: 0;
+        position: relative;
+        padding-top: 10px;
+    }
+    .ca-asset-img {
+        object-fit: contain;
+        filter: drop-shadow(0 0 4px rgba(255,255,255,0.7));
+        margin-bottom: -8px;
+        position: relative;
+        z-index: 3;
+    }
+
+    /* Play now button */
+    .ca-play-btn {
+        background: linear-gradient(44deg, #EFAD19 0%, #FFF385 100%);
+        border: 2px solid rgba(255, 255, 255, 0.4);
+        border-radius: 12px;
+        box-shadow: 0px 3px 8px rgba(0,0,0,0.25);
+        padding: 5px 30px;
+        font-family: 'Bricolage Grotesque', sans-serif !important;
+        font-weight: 800 !important;
+        font-size: 12px;
+        color: #000 !important;
+        text-decoration: none !important;
+        text-transform: uppercase;
+        white-space: nowrap;
+        line-height: 1.3;
+    }
+
+    /* Small decorative dots */
+    .ca-dot {
+        position: absolute;
+        border-radius: 50%;
+        pointer-events: none;
     }
 </style>
 
-<div class="choose-wrapper animate__animated animate__fadeIn">
-    <div class="logo-header">
-        <img src="~/img/Group 83968 (1).png" />
-        <h5 class="mt-3">Chào mừng bạn trở lại!</h5>
-        <p class="text-muted small">Hãy chọn ứng dụng bạn muốn trải nghiệm</p>
+<link href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:wght@800&family=Montserrat:ital,wght@0,800;1,800&display=swap" rel="stylesheet">
+
+<div class="ca-wrap">
+
+    <!-- Full background image -->
+    <div class="ca-hero">
+        <img src="~/LotteryV2/img/ChooseApp/main_bg.png" alt="" />
+        <div class="ca-hero-overlay"></div>
+    </div>
+
+    <!-- Logo -->
+    <div class="ca-logo-wrap">
+        <img src="~/LotteryV2/img/ChooseApp/logo.png" alt="NATCOM LOTO" />
     </div>
 
-    <div class="grid-container">
-        <!-- Website Cũ -->
-        <a href="/Home" class="module-card">
-            <div class="module-title">Website Gốc</div>
+    <!-- Game Cards -->
+    <div class="ca-cards-section">
+
+        <!-- BoLèt Lotto -->
+        <a href="/Home" class="ca-game-card ca-card-blue">
+            <div class="ca-deco-circle" style="width:150px;height:150px;right:-35px;top:-35px;"></div>
+            <div class="ca-deco-circle" style="width:150px;height:150px;right:-15px;top:-15px;"></div>
+            <div class="ca-deco-circle" style="width:150px;height:150px;left:-65px;bottom:-85px;"></div>
+
+            <div class="ca-dot" style="width:10px;height:10px;background:#CAF7FF;left:55%;top:28%;"></div>
+            <div class="ca-dot" style="width:14px;height:14px;background:rgba(255,255,255,0.7);left:50%;top:0;"></div>
+            <div class="ca-dot" style="width:6px;height:6px;background:rgba(255,255,255,0.6);left:46%;top:2px;"></div>
+
+            <div class="ca-left">
+                <div>
+                    <div class="ca-game-name">BòLèt</div>
+                    <div class="ca-game-lotto">Lotto</div>
+                </div>
+                <div class="ca-ball" style="background:#6868FF;font-size:26px;margin-left:4px;">20</div>
+            </div>
+
+            <div class="ca-right">
+                <img src="~/LotteryV2/img/ChooseApp/money_stack.png" class="ca-asset-img" style="width:65px;height:40px;" alt="" />
+                <span class="ca-play-btn">@Lang.play</span>
+            </div>
         </a>
 
-        <!-- LotteryV2 -->
-        <a href="/LotteryV2/Home" class="module-card">
-            <div class="module-title">LotteryV2</div>
+        <!-- MEGA Lotto -->
+        <a href="/LotteryV2/Home" class="ca-game-card ca-card-orange">
+            <div class="ca-deco-circle" style="width:150px;height:150px;right:-35px;top:-35px;"></div>
+            <div class="ca-deco-circle" style="width:150px;height:150px;right:-15px;top:-15px;"></div>
+            <div class="ca-deco-circle" style="width:150px;height:150px;left:-65px;bottom:-85px;"></div>
+
+            <div class="ca-dot" style="width:10px;height:10px;background:#FFD43D;left:48%;top:16px;"></div>
+            <div class="ca-dot" style="width:14px;height:14px;background:rgba(255,255,255,0.7);left:54%;bottom:8px;"></div>
+            <div class="ca-dot" style="width:6px;height:6px;background:rgba(255,255,255,0.6);left:46%;top:12px;"></div>
+
+            <div class="ca-left">
+                <div>
+                    <div class="ca-game-name">MEGA</div>
+                    <div class="ca-game-lotto">Lotto</div>
+                </div>
+                <!-- Overlapping balls: 88 behind, 66 in front -->
+                <div style="position:relative;width:70px;height:68px;margin-left:2px;">
+                    <div class="ca-ball ca-ball-sm" style="background:#FFD43D;font-size:18px;position:absolute;left:0;top:0;z-index:1;">88</div>
+                    <div class="ca-ball ca-ball-md" style="background:#52A34D;font-size:22px;position:absolute;left:18px;top:14px;z-index:2;">66</div>
+                </div>
+            </div>
+
+            <div class="ca-right">
+                <img src="~/LotteryV2/img/ChooseApp/gold_bag.png" class="ca-asset-img" style="width:50px;height:50px;" alt="" />
+                <span class="ca-play-btn">@Lang.play</span>
+            </div>
         </a>
+
     </div>
 </div>

binární
website/wwwroot/LotteryV2/img/ChooseApp/asset_ball.png


binární
website/wwwroot/LotteryV2/img/ChooseApp/gold_bag.png


binární
website/wwwroot/LotteryV2/img/ChooseApp/logo.png


binární
website/wwwroot/LotteryV2/img/ChooseApp/main_bg.png


binární
website/wwwroot/LotteryV2/img/ChooseApp/money_stack.png