Browse Source

1000 vé free

student 3 tuần trước cách đây
mục cha
commit
96f12792fa

+ 2 - 4
website/Areas/LotteryV2/Controllers/HomeController.cs

@@ -701,10 +701,8 @@ namespace LotteryWebApp.Areas.LotteryV2.Controllers
                 request.language = CultureInfo.CurrentCulture.Name.StartsWith("en") ? "0" : "1";
                 request.requestId = Guid.NewGuid().ToString();
 
-                ConfirmTicketDataResponse response = request.channelPayment == Constants.NATCASH_WALLET_TICKET
-                    ? api.BuyTicketV2Api(configuration, request)
-                    : api.ConfirmTicketDataApi(configuration, request);
-                log.Info($"[ConfirmTicketData] Call ConfirmTicketDataApi Response: {JsonConvert.SerializeObject(response)}");
+                ConfirmTicketDataResponse response = api.BuyTicketV2Api(configuration, request);
+                log.Info($"[ConfirmTicketData] Call BuyTicketV2Api Response: {JsonConvert.SerializeObject(response)}");
 
                 return Json(response);
             }

+ 6 - 7
website/Areas/Millions/Controllers/HomeController.cs

@@ -1059,6 +1059,7 @@ namespace LotteryWebApp.Areas.Millions.Controllers
                 if (isValidPromotionCode)
                 {
                     request.gameId = Constants.Millions_CODE;
+                    request.channelPayment = Constants.BASIC_WALLET_TICKET;
                 }
                 else
                 {
@@ -1069,9 +1070,7 @@ namespace LotteryWebApp.Areas.Millions.Controllers
                 var ticketDebug = request.ticket != null ? string.Join("; ", request.ticket.Select(t => $"code={t.code}, money={t.money}")) : "NULL";
                 log.Info($"[ConfirmTicketData] gameId={request.gameId}, msisdn={request.msisdn}, tickets=[{ticketDebug}]");
 
-                ConfirmTicketDataResponse response = request.channelPayment == Constants.NATCASH_WALLET_TICKET
-                    ? api.BuyTicketV2Api(configuration, request)
-                    : api.ConfirmTicketDataApi(configuration, request);
+                ConfirmTicketDataResponse response = api.BuyTicketV2Api(configuration, request);
 
                 if (isValidPromotionCode && response.responseCode == Code.SUCCESS && !string.IsNullOrWhiteSpace(response.transId))
                 {
@@ -1149,9 +1148,12 @@ namespace LotteryWebApp.Areas.Millions.Controllers
                 if (string.IsNullOrEmpty(token)) return Json(new { responseCode = Code.SESSION_EXPIRED, responseMessage = "Session expired" });
 
                 var isWalletPayment = request.channelPayment == Constants.NATCASH_WALLET_TICKET;
+                var freeTicketTransactionId = HttpContext.Session.GetString(FreeTicketTransactionSessionKey);
+                var isFreeTicketTransaction = !string.IsNullOrWhiteSpace(request.transIdByTicket)
+                    && string.Equals(request.transIdByTicket, freeTicketTransactionId, StringComparison.Ordinal);
 
                 // The main-account flow keeps the existing OTP verification.
-                if (!isWalletPayment)
+                if (!isWalletPayment && !isFreeTicketTransaction)
                 {
                     ConfirmOTPRequest otpRequest = new ConfirmOTPRequest
                     {
@@ -1176,9 +1178,6 @@ namespace LotteryWebApp.Areas.Millions.Controllers
                 request.channel = configuration.GetSection("channel").Value;
 
                 request.gameId = Constants.Millions_CODE;
-                var freeTicketTransactionId = HttpContext.Session.GetString(FreeTicketTransactionSessionKey);
-                var isFreeTicketTransaction = !string.IsNullOrWhiteSpace(request.transIdByTicket)
-                    && string.Equals(request.transIdByTicket, freeTicketTransactionId, StringComparison.Ordinal);
 
                 ConfirmBuyingTicketResponse response = isWalletPayment
                     ? api.ConfirmBuyingTicketV2Api(configuration, request, Constants.WALLET_PAYMENT_CHANNEL, request.walletPin)

+ 30 - 10
website/Areas/Millions/Views/Home/BuyTicket.cshtml

@@ -13,6 +13,8 @@
     var freeTicketPromotionCode = isFreeTicketMode
         ? Context.Session.GetString("Millions.FreeTicket.PromotionCode")
         : null;
+    var hasFreeTicketPromotion = isFreeTicketMode
+        && !string.IsNullOrWhiteSpace(freeTicketPromotionCode);
 }
 
 @section Styles {
@@ -527,6 +529,7 @@ else
 
         const maxBalls = @(Model.termType == Constants.PIC10_BASIC_CODE ? 10 : (Model.termType == Constants.Millions_CODE ? 6 : (Model.termType == Constants.PIC10_BIGSMALL_CODE ? 13 : 15)));
         const isFreeTicketMode = @(isFreeTicketMode ? "true" : "false");
+        const hasFreeTicketPromotion = @(hasFreeTicketPromotion ? "true" : "false");
         const freeTicketPromotionCode = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(freeTicketPromotionCode));
 
         function reindexTickets() {
@@ -952,7 +955,9 @@ else
                     filledTickets++;
                 }
             });
-            const pricePerTicket = @(Model.termType == Constants.PIC10_BASIC_CODE ? "10" : (Model.termType == Constants.Millions_CODE ? "20" : "50"));
+            const pricePerTicket = hasFreeTicketPromotion
+                ? 0
+                : @(Model.termType == Constants.PIC10_BASIC_CODE ? "10" : (Model.termType == Constants.Millions_CODE ? "20" : "50"));
             $("#totalPrice").text(formatMoneyV2(filledTickets * pricePerTicket));
             $("#totalTicketCount").text(filledTickets);
         }
@@ -1055,6 +1060,10 @@ else
             }
 
             pendingTickets = tickets;
+            if (hasFreeTicketPromotion) {
+                selectPaymentChannel('@Constants.BASIC_WALLET_TICKET');
+                return;
+            }
             $("#paymentChannelModal").removeClass("hidden").addClass("flex");
         }
 
@@ -1139,7 +1148,7 @@ else
                         </div>
                         <div class="w-24 shrink-0 bg-white border border-gray-200 rounded-2xl p-2.5 flex items-center justify-center shadow-sm">
                             <div class="flex items-baseline gap-0.5">
-                                <span class="text-[20px] font-black text-black">${formatMoneyV2(t.money)}</span>
+                                <span class="text-[20px] font-black text-black">${formatMoneyV2(hasFreeTicketPromotion ? 0 : t.money)}</span>
                                 <span class="text-[12px] font-black text-[#0062FF]">HTG</span>
                             </div>
                         </div>
@@ -1149,7 +1158,9 @@ else
             });
 
             $("#summaryTotalCount").text(displayTickets.length);
-            const totalMoney = apiData.totalMoneyPayment || apiData.totalMoney || "0";
+            const totalMoney = hasFreeTicketPromotion
+                ? "0"
+                : (apiData.totalMoneyPayment || apiData.totalMoney || "0");
             $("#summaryTotalAmount").text(formatMoneyV2(totalMoney));
 
             summaryModal.removeClass("hidden").addClass("flex");
@@ -1160,6 +1171,12 @@ else
         }
 
         function confirmCheckout(btn) {
+            if (hasFreeTicketPromotion) {
+                hideOrderSummary();
+                finalizePurchase(btn, true);
+                return;
+            }
+
             if (currentChannelPayment === '@Constants.NATCASH_WALLET_TICKET') {
                 showOtpModal(true);
                 return;
@@ -1359,14 +1376,17 @@ else
             $("#receiptSuccessModal").removeClass("hidden").addClass("flex");
         }
 
-        function finalizePurchase(btn) {
+        function finalizePurchase(btn, skipOtpValidation) {
+            const isFreeTicketPurchase = hasFreeTicketPromotion && skipOtpValidation === true;
             const isWalletPayment = currentChannelPayment === '@Constants.NATCASH_WALLET_TICKET';
-            let otpCode = isWalletPayment ? $("#walletPinInput").val().trim() : "";
-            if (!isWalletPayment) {
+            let otpCode = isFreeTicketPurchase
+                ? ""
+                : (isWalletPayment ? $("#walletPinInput").val().trim() : "");
+            if (!isFreeTicketPurchase && !isWalletPayment) {
                 $("#otpInputs input").each(function() { otpCode += $(this).val(); });
             }
             
-            if ((isWalletPayment && otpCode.length === 0) || (!isWalletPayment && otpCode.length < 6)) {
+            if (!isFreeTicketPurchase && ((isWalletPayment && otpCode.length === 0) || (!isWalletPayment && otpCode.length < 6))) {
                 showNotification(isWalletPayment ? '@Html.Raw(Lang.payment_wallet_pin_instruction.Replace("'", "\\'"))' : "Please enter full 6-digit OTP", "warning");
                 return;
             }
@@ -1377,12 +1397,12 @@ else
             const finalData = {
                 transIdByTicket: currentTransId,
                 gameId: isFreeTicketMode ? "@Constants.Millions_CODE" : "@Model.termType",
-                paymentCode: currentChannelPayment === '@Constants.NATCASH_WALLET_TICKET' ? null : otpCode,
-                walletPin: currentChannelPayment === '@Constants.NATCASH_WALLET_TICKET' ? otpCode : null,
+                paymentCode: isFreeTicketPurchase || currentChannelPayment === '@Constants.NATCASH_WALLET_TICKET' ? null : otpCode,
+                walletPin: !isFreeTicketPurchase && currentChannelPayment === '@Constants.NATCASH_WALLET_TICKET' ? otpCode : null,
                 channelPayment: currentChannelPayment
             };
 
-            // Call the unified backend action that verifies OTP and then buys the ticket
+            // Free-ticket transactions skip OTP; paid transactions keep the existing verification flow.
             $.ajax({
                 url: subDomain + '@Url.Action("ConfirmBuyingTicketV2", "Home")',
                 type: 'POST',

+ 6 - 10
website/Areas/Millions/Views/Home/PopupFreeTickit.cshtml

@@ -22,13 +22,14 @@
 	   id="freeTicketClaimButton"
 	   aria-label="Get your free ticket">
 		<img class="free-ticket-popup__image"
-			 src="~/Millions/assets/images/free-ticket-floating-icon.png"
+			 src="~/Millions/assets/images/banner_free_transparent.png"
 			 alt="" aria-hidden="true" />
 		<strong class="free-ticket-popup__ticket-text">
 			@PopupText("millions_free")<br />@PopupText("millions_ticket")
 		</strong>
 		<strong class="free-ticket-popup__button-text" id="freeTicketTitle">
-			@PopupText("millions_free") @PopupText("millions_ticket")
+			<span>@PopupText("free_ticket_banner_cta")</span>
+			<span class="free-ticket-popup__button-highlight">@PopupText("free_ticket_banner_highlight")</span>
 		</strong>
 	</a>
 </aside>
@@ -354,10 +355,9 @@
 
 		function alignFreeTicketIcon() {
 			var millionsFrame = document.querySelector('.main-content, .main-container');
-			var playNowSection = document.getElementById('playNowSection');
 			if (!millionsFrame) {
 				popup.style.left = '';
-				popup.style.right = '10px';
+				popup.style.right = '12px';
 				return;
 			}
 
@@ -365,12 +365,8 @@
 			var popupWidth = popup.getBoundingClientRect().width;
 			popup.style.left = Math.max(8, frameRect.right - popupWidth - 15) + 'px';
 			popup.style.right = 'auto';
-
-			if (playNowSection) {
-				var playRect = playNowSection.getBoundingClientRect();
-				popup.style.top = (playRect.top - 7) + 'px';
-				popup.style.bottom = 'auto';
-			}
+			popup.style.top = '';
+			popup.style.bottom = '';
 		}
 
 		alignFreeTicketIcon();

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

@@ -2147,4 +2147,6 @@ We’ll help you create an account in a few easy steps.</value>
   <data name="free_ticket_error_45_next_primary" xml:space="preserve"><value>Come back tomorrow</value></data>
   <data name="free_ticket_error_45_next_secondary" xml:space="preserve"><value>and try your luck again!</value></data>
   <data name="free_ticket_error_45_footer" xml:space="preserve"><value>Don't miss your chance to win up to 30,000,000 HTG!</value></data>
+  <data name="free_ticket_banner_cta" xml:space="preserve"><value>Tap here to get your</value></data>
+  <data name="free_ticket_banner_highlight" xml:space="preserve"><value>FREE TICKET!</value></data>
 </root>

+ 2 - 0
website/Languages/Lang.resx

@@ -2148,4 +2148,6 @@ Apre yo fin tire 20 nimewo, sistem nan konte konbyen nimewo ki enpe oswa pe: &lt
   <data name="free_ticket_error_45_next_primary" xml:space="preserve"><value>Retounen demen</value></data>
   <data name="free_ticket_error_45_next_secondary" xml:space="preserve"><value>epi eseye chans ou ankò!</value></data>
   <data name="free_ticket_error_45_footer" xml:space="preserve"><value>Pa rate chans ou pou genyen jiska 30,000,000 HTG!</value></data>
+  <data name="free_ticket_banner_cta" xml:space="preserve"><value>Peze la pou jwenn</value></data>
+  <data name="free_ticket_banner_highlight" xml:space="preserve"><value>TIKÈ GRATIS ou!</value></data>
 </root>

BIN
website/wwwroot/Millions/assets/images/banner_free.jpg


BIN
website/wwwroot/Millions/assets/images/banner_free_transparent.png


+ 32 - 26
website/wwwroot/Millions/css/popup-free-ticket.css

@@ -6,10 +6,10 @@
 .free-ticket-popup {
     position: fixed;
     z-index: 90;
-    right: 10px;
-    bottom: calc(88px + env(safe-area-inset-bottom));
-    width: 70px;
-    height: 81px;
+    right: 12px;
+    bottom: calc(82px + env(safe-area-inset-bottom));
+    width: min(380px, calc(100vw - 24px));
+    aspect-ratio: 1552 / 322;
     font-family: Arial, Helvetica, sans-serif;
 }
 
@@ -18,11 +18,11 @@
     right: 0;
     bottom: 0;
     display: block;
-    width: 70px;
-    height: 81px;
+    width: 100%;
+    height: 100%;
     color: inherit;
     cursor: pointer;
-    filter: drop-shadow(0 3px 4px rgba(0, 35, 105, .3));
+    filter: drop-shadow(0 4px 7px rgba(126, 86, 0, .3));
     text-decoration: none;
     transform-origin: center bottom;
     will-change: transform, filter;
@@ -42,36 +42,42 @@
     color: #06429b;
     font-weight: 900;
     text-align: center;
-    text-transform: uppercase;
     text-shadow: 0 1px rgba(255, 255, 255, .35);
     pointer-events: none;
 }
 
 .free-ticket-popup__ticket-text {
-    top: 22px;
-    left: 15px;
-    width: 39px;
-    transform: rotate(3deg);
-    font-size: 8px;
-    line-height: .82;
+    top: 37%;
+    left: 3.2%;
+    width: 21%;
+    transform: rotate(-8deg);
+    font-size: clamp(8px, 2.8vw, 12px);
+    line-height: .86;
+    text-transform: uppercase;
 }
 
 .free-ticket-popup__button-text {
-    top: auto;
-    right: 14px;
-    bottom: 16px;
-    left: 14px;
+    top: 36%;
+    right: 17%;
+    bottom: auto;
+    left: 28%;
     display: flex;
-    height: 9px;
+    height: 30%;
     align-items: center;
     justify-content: center;
+    gap: .2em;
     overflow: hidden;
-    font-size: 5px;
-    line-height: 9px;
+    color: #282828;
+    font-size: clamp(8px, 2.8vw, 11px);
+    line-height: 1.1;
     text-overflow: ellipsis;
     white-space: nowrap;
 }
 
+.free-ticket-popup__button-highlight {
+    color: #075bdc;
+}
+
 .free-ticket-popup__card:hover,
 .free-ticket-popup__card:focus-visible {
     transform: translateY(-3px) scale(1.06);
@@ -715,7 +721,7 @@ body.free-ticket-result-open {
 
 @media (max-width: 359px) {
     .free-ticket-popup {
-        right: 8px;
+        right: 10px;
     }
 
     .free-ticket-error-43__copy--title {
@@ -738,7 +744,7 @@ body.free-ticket-result-open {
 
 @media (prefers-reduced-motion: no-preference) {
     .free-ticket-popup__card {
-        animation: free-ticket-attention 2.6s ease-in-out infinite;
+        animation: free-ticket-attention 1s ease-in-out infinite;
     }
 
     .free-ticket-popup__card:hover,
@@ -753,12 +759,12 @@ body.free-ticket-result-open {
         transform: translateY(0) rotate(0);
         filter: drop-shadow(0 3px 4px rgba(0, 35, 105, .3));
     }
-    73% { transform: translateY(-3px) rotate(-3deg); }
+    73% { transform: translateY(-2px) scale(1.008); }
     78% {
-        transform: translateY(-4px) rotate(3deg) scale(1.04);
+        transform: translateY(-3px) scale(1.015);
         filter: brightness(1.07) drop-shadow(0 0 5px rgba(255, 201, 40, .8));
     }
-    83% { transform: translateY(-3px) rotate(-2deg); }
+    83% { transform: translateY(-2px) scale(1.008); }
     88% { transform: translateY(0) rotate(0); }
 }