student 1 day ago
parent
commit
b63afe6800

+ 66 - 0
website/Areas/LotteryV2/Controllers/HomeController.cs

@@ -713,6 +713,66 @@ namespace LotteryWebApp.Areas.LotteryV2.Controllers
             }
         }
 
+        [HttpPost]
+        public IActionResult PrepareTicketPayment([FromBody] LotteryV2PaymentRequest paymentRequest)
+        {
+            try
+            {
+                if (paymentRequest?.ticketData == null || paymentRequest.ticketData.ticket == null || !paymentRequest.ticketData.ticket.Any())
+                {
+                    return Json(new { responseCode = Code.FAILURE, responseMessage = Lang.ticket_invalid });
+                }
+
+                if (paymentRequest.paymentSource != Constants.BASIC_WALLET_TICKET &&
+                    paymentRequest.paymentSource != Constants.NATCASH_WALLET_TICKET)
+                {
+                    return Json(new { responseCode = Code.FAILURE, responseMessage = Lang.error_happened });
+                }
+
+                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" });
+                }
+
+                ConfirmTicketDataRequest request = paymentRequest.ticketData;
+                request.token = token;
+                request.msisdn = msisdn;
+                request.language = CultureInfo.CurrentCulture.Name.StartsWith("en") ? "0" : "1";
+                request.requestId = Guid.NewGuid().ToString();
+
+                log.Info($"[PrepareTicketPayment] Confirm ticket request: {JsonConvert.SerializeObject(request)}");
+                ConfirmTicketDataResponse response = api.ConfirmTicketDataApi(configuration, request);
+                log.Info($"[PrepareTicketPayment] Confirm ticket response: {JsonConvert.SerializeObject(response)}");
+
+                if (response.responseCode != Code.SUCCESS || paymentRequest.paymentSource != Constants.NATCASH_WALLET_TICKET)
+                {
+                    return Json(response);
+                }
+
+                string totalMoney = response.totalMoney;
+                string param = "transactionId=" + response.transId + "&requestId=" + response.requestId + "&money=" + totalMoney;
+                string privateUrl = CreatePrivateURL(configuration, param, "seconds", "0", "0", GetParameter("rsaPolicy"));
+
+                return Json(new
+                {
+                    responseCode = response.responseCode,
+                    responseMessage = response.responseMessage,
+                    transId = response.transId,
+                    requestId = response.requestId,
+                    totalMoney = response.totalMoney,
+                    paymentSource = paymentRequest.paymentSource,
+                    redirectUrl = GetParameter(Constants.SUB_DOMAIN) + "/BuyTicket/BackToApp?" + privateUrl
+                });
+            }
+            catch (Exception ex)
+            {
+                log.Error("[PrepareTicketPayment] EXCEPTION: ", ex);
+                return Json(new { responseCode = Code.ERROR, responseMessage = Lang.error_happened });
+            }
+        }
+
         [HttpPost]
         public IActionResult ConfirmOTP([FromBody] ConfirmOTPRequest request)
         {
@@ -1057,4 +1117,10 @@ namespace LotteryWebApp.Areas.LotteryV2.Controllers
             return Redirect(GetParameter(Constants.SUB_DOMAIN) + "/Account/Login");
         }
     }
+
+    public class LotteryV2PaymentRequest
+    {
+        public string paymentSource { get; set; }
+        public ConfirmTicketDataRequest ticketData { get; set; }
+    }
 }

+ 85 - 43
website/Areas/LotteryV2/Views/Home/BuyTicket.cshtml

@@ -379,6 +379,28 @@ else
     </div>
 </div>
 
+<!-- Payment source selection -->
+<div id="paymentSourceModal" class="fixed inset-0 bg-black/70 backdrop-blur-sm z-[190] hidden items-center justify-center p-4 font-bricolage">
+    <div class="bg-white w-full max-w-[350px] rounded-[28px] overflow-hidden shadow-2xl animate__animated animate__zoomIn animate__faster">
+        <div class="bg-[#EE0033] px-5 py-4 text-white text-center relative">
+            <h2 class="text-[19px] font-black">@Lang.v2_choose_account</h2>
+            <button type="button" onclick="hidePaymentSourceModal()" class="absolute right-3 top-3 w-9 h-9 rounded-full bg-white/15 flex items-center justify-center">
+                <i class="fa-solid fa-xmark"></i>
+            </button>
+        </div>
+        <div class="p-5 grid gap-3">
+            <button type="button" data-payment-source="@Constants.BASIC_WALLET_TICKET" onclick="selectPaymentSource('@Constants.BASIC_WALLET_TICKET', this)" class="payment-source-button w-full border-2 border-[#EE0033] rounded-2xl px-4 py-4 flex items-center gap-4 text-left active:scale-[0.98] transition-all">
+                <span class="w-11 h-11 rounded-full bg-[#EE0033]/10 text-[#EE0033] flex items-center justify-center"><i class="fa-solid fa-mobile-screen-button text-xl"></i></span>
+                <span class="font-black text-[16px] text-gray-800">@Lang.basic_account</span>
+            </button>
+            <button type="button" data-payment-source="@Constants.NATCASH_WALLET_TICKET" onclick="selectPaymentSource('@Constants.NATCASH_WALLET_TICKET', this)" class="payment-source-button w-full border-2 border-[#0A9800] rounded-2xl px-4 py-4 flex items-center gap-4 text-left active:scale-[0.98] transition-all">
+                <span class="w-11 h-11 rounded-full bg-[#0A9800]/10 text-[#0A9800] flex items-center justify-center"><i class="fa-solid fa-wallet text-xl"></i></span>
+                <span class="font-black text-[16px] text-gray-800">@Lang.NatCash</span>
+            </button>
+        </div>
+    </div>
+</div>
+
     <!-- OTP Verification Modal (V2 Design Overhaul - Matching Figma) -->
     <div id="otpModal" class="fixed inset-0 bg-black/80 backdrop-blur-sm z-[200] hidden items-center justify-center p-4">
         
@@ -824,6 +846,8 @@ else
 
         // --- Order Summary Modal Logic ---
         // --- Payment Preparation & Order Summary Logic ---
+        let pendingPaymentRequest = null;
+
         function preparePayment(event) {
             const isBSMode = @(Model.termType == Constants.PIC10_BIGSMALL_CODE || Model.termType == Constants.PIC10_ODDEVEN_CODE ? "true" : "false");
             const tickets = [];
@@ -865,52 +889,18 @@ else
                 }
             }
 
-            const requestData = {
+            pendingPaymentRequest = {
                 gameId: "@Model.termType",
                 ticket: tickets
             };
 
-            const btn = event ? event.currentTarget : null;
-            const originalText = btn ? btn.innerHTML : "@Lang.v2_payment";
-            if (btn) {
-                btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Loading...';
-                btn.disabled = true;
-            }
-
-
-            $.ajax({
-                url: subDomain + '@Url.Action("ConfirmTicketData", "Home")',
-                type: 'POST',
-                contentType: 'application/json',
-                data: JSON.stringify(requestData),
-                success: function(data) {
-
-                    if (btn) {
-                        btn.innerHTML = originalText;
-                        btn.disabled = false;
-                    }
-                    if (data.responseCode === "0") {
-                        showOrderSummary(data, tickets);
-                    } else {
-                        showNotification(data.responseMessage || "Confirmation failed", data.responseCode);
-                    }
-                },
-                error: function(err) {
-
-                    if (btn) {
-                        btn.innerHTML = originalText;
-                        btn.disabled = false;
-                    }
-
-                    showNotification("Network error occurred.", "warning");
-                }
-            });
+            showOrderSummary(null, tickets);
         }
 
         let currentTransId = null;
 
         function showOrderSummary(apiData, localTickets) {
-            currentTransId = apiData.transId;
+            currentTransId = apiData ? apiData.transId : null;
             const summaryModal = $("#orderSummaryModal");
             const summaryList = $("#summaryTicketList");
             summaryList.empty();
@@ -957,7 +947,9 @@ else
             });
 
             $("#summaryTotalCount").text(displayTickets.length);
-            const totalMoney = apiData.totalMoneyPayment || apiData.totalMoney || "0";
+            const totalMoney = apiData
+                ? (apiData.totalMoneyPayment || apiData.totalMoney || "0")
+                : displayTickets.reduce((sum, ticket) => sum + (parseInt(ticket.money, 10) || 0), 0);
             $("#summaryTotalAmount").text(formatMoneyV2(totalMoney));
 
             summaryModal.removeClass("hidden").addClass("flex");
@@ -968,24 +960,74 @@ else
         }
 
         function confirmCheckout(btn) {
+            if (!pendingPaymentRequest) {
+                showNotification("@Lang.v2_ticket_not_valid", "warning");
+                return;
+            }
+
+            $("#paymentSourceModal").removeClass("hidden").addClass("flex");
+        }
+
+        function hidePaymentSourceModal() {
+            $("#paymentSourceModal").removeClass("flex").addClass("hidden");
+        }
+
+        function selectPaymentSource(paymentSource, btn) {
             const originalText = $(btn).html();
-            $(btn).prop('disabled', true).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i>Processing...');
+            const sourceButtons = $(".payment-source-button");
+            sourceButtons.prop('disabled', true);
+            $(btn).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i>Processing...');
+
+            $.ajax({
+                url: subDomain + '@Url.Action("PrepareTicketPayment", "Home")',
+                type: 'POST',
+                contentType: 'application/json',
+                data: JSON.stringify({
+                    paymentSource: paymentSource,
+                    ticketData: pendingPaymentRequest
+                }),
+                success: function(data) {
+                    sourceButtons.prop('disabled', false);
+                    $(btn).html(originalText);
+                    if (data.responseCode !== "0") {
+                        showNotification(data.responseMessage || "Confirmation failed", data.responseCode);
+                        return;
+                    }
+
+                    currentTransId = data.transId;
 
-            // Trigger SendOTP API
+                    if (paymentSource === '@Constants.NATCASH_WALLET_TICKET') {
+                        if (data.redirectUrl) {
+                            window.location.href = data.redirectUrl;
+                        } else {
+                            showNotification(data.responseMessage || "Unable to open wallet payment.", "warning");
+                        }
+                        return;
+                    }
+
+                    sendOtpAfterTicketConfirmation(btn);
+                },
+                error: function() {
+                    sourceButtons.prop('disabled', false);
+                    $(btn).html(originalText);
+                    showNotification("Network error occurred.", "warning");
+                }
+            });
+        }
+
+        function sendOtpAfterTicketConfirmation(btn) {
             $.ajax({
                 url: subDomain + '@Url.Action("SendOTP", "Home")',
                 type: 'POST',
                 success: function(data) {
-                    $(btn).prop('disabled', false).html(originalText);
-                    
                     if (data.responseCode === "0") {
+                        hidePaymentSourceModal();
                         showOtpModal();
                     } else {
                         showNotification(data.responseMessage || "Failed to send OTP", data.responseCode);
                     }
                 },
                 error: function() {
-                    $(btn).prop('disabled', false).html(originalText);
                     showNotification("Network error occurred.", "warning");
                 }
             });

+ 65 - 0
website/Areas/Millions/Controllers/HomeController.cs

@@ -1011,6 +1011,65 @@ namespace LotteryWebApp.Areas.Millions.Controllers
             }
         }
 
+        [HttpPost]
+        public IActionResult PrepareTicketPayment([FromBody] MillionsPaymentRequest paymentRequest)
+        {
+            try
+            {
+                if (paymentRequest?.ticketData == null || paymentRequest.ticketData.ticket == null || !paymentRequest.ticketData.ticket.Any())
+                {
+                    return Json(new { responseCode = Code.FAILURE, responseMessage = Lang.ticket_invalid });
+                }
+
+                if (paymentRequest.paymentSource != Constants.BASIC_WALLET_TICKET &&
+                    paymentRequest.paymentSource != Constants.NATCASH_WALLET_TICKET)
+                {
+                    return Json(new { responseCode = Code.FAILURE, responseMessage = Lang.error_happened });
+                }
+
+                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" });
+                }
+
+                ConfirmTicketDataRequest request = paymentRequest.ticketData;
+                request.token = token;
+                request.msisdn = msisdn;
+                request.language = CultureInfo.CurrentCulture.Name.StartsWith("en") ? "0" : "1";
+                request.requestId = Guid.NewGuid().ToString();
+
+                log.Info($"[Millions.PrepareTicketPayment] Confirm ticket request: {JsonConvert.SerializeObject(request)}");
+                ConfirmTicketDataResponse response = api.ConfirmTicketDataApi(configuration, request);
+                log.Info($"[Millions.PrepareTicketPayment] Confirm ticket response: {JsonConvert.SerializeObject(response)}");
+
+                if (response.responseCode != Code.SUCCESS || paymentRequest.paymentSource != Constants.NATCASH_WALLET_TICKET)
+                {
+                    return Json(response);
+                }
+
+                string param = "transactionId=" + response.transId + "&requestId=" + response.requestId + "&money=" + response.totalMoney;
+                string privateUrl = CreatePrivateURL(configuration, param, "seconds", "0", "0", GetParameter("rsaPolicy"));
+
+                return Json(new
+                {
+                    responseCode = response.responseCode,
+                    responseMessage = response.responseMessage,
+                    transId = response.transId,
+                    requestId = response.requestId,
+                    totalMoney = response.totalMoney,
+                    paymentSource = paymentRequest.paymentSource,
+                    redirectUrl = GetParameter(Constants.SUB_DOMAIN) + "/BuyTicket/BackToApp?" + privateUrl
+                });
+            }
+            catch (Exception ex)
+            {
+                log.Error("[Millions.PrepareTicketPayment] EXCEPTION: ", ex);
+                return Json(new { responseCode = Code.ERROR, responseMessage = Lang.error_happened });
+            }
+        }
+
         [HttpPost]
         public IActionResult ConfirmOTP([FromBody] ConfirmOTPRequest request)
         {
@@ -1324,5 +1383,11 @@ namespace LotteryWebApp.Areas.Millions.Controllers
             return Redirect(GetParameter(Constants.SUB_DOMAIN) + "/Account/Login");
         }
     }
+
+    public class MillionsPaymentRequest
+    {
+        public string paymentSource { get; set; }
+        public ConfirmTicketDataRequest ticketData { get; set; }
+    }
 }
 

+ 85 - 43
website/Areas/Millions/Views/Home/BuyTicket.cshtml

@@ -396,6 +396,28 @@ else
     </div>
 </div>
 
+<!-- Payment source selection -->
+<div id="paymentSourceModal" class="fixed inset-0 bg-black/70 backdrop-blur-sm z-[190] hidden items-center justify-center p-4 font-bricolage">
+    <div class="bg-white w-full max-w-[350px] rounded-[28px] overflow-hidden shadow-2xl animate__animated animate__zoomIn animate__faster">
+        <div class="bg-[#0062FF] px-5 py-4 text-white text-center relative">
+            <h2 class="text-[19px] font-black">@Lang.millions_choose_account</h2>
+            <button type="button" onclick="hidePaymentSourceModal()" class="absolute right-3 top-3 w-9 h-9 rounded-full bg-white/15 flex items-center justify-center">
+                <i class="fa-solid fa-xmark"></i>
+            </button>
+        </div>
+        <div class="p-5 grid gap-3">
+            <button type="button" onclick="selectPaymentSource('@Constants.BASIC_WALLET_TICKET', this)" class="payment-source-button w-full border-2 border-[#0062FF] rounded-2xl px-4 py-4 flex items-center gap-4 text-left active:scale-[0.98] transition-all">
+                <span class="w-11 h-11 rounded-full bg-[#0062FF]/10 text-[#0062FF] flex items-center justify-center"><i class="fa-solid fa-mobile-screen-button text-xl"></i></span>
+                <span class="font-black text-[16px] text-gray-800">@Lang.basic_account</span>
+            </button>
+            <button type="button" onclick="selectPaymentSource('@Constants.NATCASH_WALLET_TICKET', this)" class="payment-source-button w-full border-2 border-[#0A9800] rounded-2xl px-4 py-4 flex items-center gap-4 text-left active:scale-[0.98] transition-all">
+                <span class="w-11 h-11 rounded-full bg-[#0A9800]/10 text-[#0A9800] flex items-center justify-center"><i class="fa-solid fa-wallet text-xl"></i></span>
+                <span class="font-black text-[16px] text-gray-800">@Lang.NatCash</span>
+            </button>
+        </div>
+    </div>
+</div>
+
     <!-- OTP Verification Modal (V2 Design Overhaul - Matching Figma) -->
     <div id="otpModal" class="fixed inset-0 bg-black/80 backdrop-blur-sm z-[200] hidden items-center justify-center p-4">
         
@@ -960,6 +982,8 @@ else
 
         // --- Order Summary Modal Logic ---
         // --- Payment Preparation & Order Summary Logic ---
+        let pendingPaymentRequest = null;
+
         function preparePayment(event) {
             const isBSMode = @(Model.termType == Constants.PIC10_BIGSMALL_CODE || Model.termType == Constants.PIC10_ODDEVEN_CODE ? "true" : "false");
             const tickets = [];
@@ -1001,52 +1025,18 @@ else
                 }
             }
 
-            const requestData = {
+            pendingPaymentRequest = {
                 gameId: "@Model.termType",
                 ticket: tickets
             };
 
-            const btn = event ? event.currentTarget : null;
-            const originalText = btn ? btn.innerHTML : "@Lang.millions_payment";
-            if (btn) {
-                btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Loading...';
-                btn.disabled = true;
-            }
-
-
-            $.ajax({
-                url: subDomain + '@Url.Action("ConfirmTicketData", "Home")',
-                type: 'POST',
-                contentType: 'application/json',
-                data: JSON.stringify(requestData),
-                success: function(data) {
-
-                    if (btn) {
-                        btn.innerHTML = originalText;
-                        btn.disabled = false;
-                    }
-                    if (data.responseCode === "0") {
-                        showOrderSummary(data, tickets);
-                    } else {
-                        showNotification(data.responseMessage || "Confirmation failed", data.responseCode);
-                    }
-                },
-                error: function(err) {
-
-                    if (btn) {
-                        btn.innerHTML = originalText;
-                        btn.disabled = false;
-                    }
-
-                    showNotification("Network error occurred.", "warning");
-                }
-            });
+            showOrderSummary(null, tickets);
         }
 
         let currentTransId = null;
 
         function showOrderSummary(apiData, localTickets) {
-            currentTransId = apiData.transId;
+            currentTransId = apiData ? apiData.transId : null;
             const summaryModal = $("#orderSummaryModal");
             const summaryList = $("#summaryTicketList");
             summaryList.empty();
@@ -1099,7 +1089,9 @@ else
             });
 
             $("#summaryTotalCount").text(displayTickets.length);
-            const totalMoney = apiData.totalMoneyPayment || apiData.totalMoney || "0";
+            const totalMoney = apiData
+                ? (apiData.totalMoneyPayment || apiData.totalMoney || "0")
+                : displayTickets.reduce((sum, ticket) => sum + (parseInt(ticket.money, 10) || 0), 0);
             $("#summaryTotalAmount").text(formatMoneyV2(totalMoney));
 
             summaryModal.removeClass("hidden").addClass("flex");
@@ -1110,24 +1102,74 @@ else
         }
 
         function confirmCheckout(btn) {
+            if (!pendingPaymentRequest) {
+                showNotification("@Lang.millions_ticket_not_valid", "warning");
+                return;
+            }
+
+            $("#paymentSourceModal").removeClass("hidden").addClass("flex");
+        }
+
+        function hidePaymentSourceModal() {
+            $("#paymentSourceModal").removeClass("flex").addClass("hidden");
+        }
+
+        function selectPaymentSource(paymentSource, btn) {
             const originalText = $(btn).html();
-            $(btn).prop('disabled', true).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i>Processing...');
+            const sourceButtons = $(".payment-source-button");
+            sourceButtons.prop('disabled', true);
+            $(btn).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i>Processing...');
 
-            // Trigger SendOTP API
+            $.ajax({
+                url: subDomain + '@Url.Action("PrepareTicketPayment", "Home")',
+                type: 'POST',
+                contentType: 'application/json',
+                data: JSON.stringify({
+                    paymentSource: paymentSource,
+                    ticketData: pendingPaymentRequest
+                }),
+                success: function(data) {
+                    sourceButtons.prop('disabled', false);
+                    $(btn).html(originalText);
+                    if (data.responseCode !== "0") {
+                        showNotification(data.responseMessage || "Confirmation failed", data.responseCode);
+                        return;
+                    }
+
+                    currentTransId = data.transId;
+
+                    if (paymentSource === '@Constants.NATCASH_WALLET_TICKET') {
+                        if (data.redirectUrl) {
+                            window.location.href = data.redirectUrl;
+                        } else {
+                            showNotification(data.responseMessage || "Unable to open wallet payment.", "warning");
+                        }
+                        return;
+                    }
+
+                    sendOtpAfterTicketConfirmation();
+                },
+                error: function() {
+                    sourceButtons.prop('disabled', false);
+                    $(btn).html(originalText);
+                    showNotification("Network error occurred.", "warning");
+                }
+            });
+        }
+
+        function sendOtpAfterTicketConfirmation() {
             $.ajax({
                 url: subDomain + '@Url.Action("SendOTP", "Home")',
                 type: 'POST',
                 success: function(data) {
-                    $(btn).prop('disabled', false).html(originalText);
-                    
                     if (data.responseCode === "0") {
+                        hidePaymentSourceModal();
                         showOtpModal();
                     } else {
                         showNotification(data.responseMessage || "Failed to send OTP", data.responseCode);
                     }
                 },
                 error: function() {
-                    $(btn).prop('disabled', false).html(originalText);
                     showNotification("Network error occurred.", "warning");
                 }
             });

+ 8 - 0
website/Controllers/HomeController.cs

@@ -165,6 +165,10 @@ namespace LotteryWebApp.Controllers
                                     userStatusRequest
                                 );
                                 HttpContext.Session.SetComplexData("userStatus", userStatusGet);
+
+                                return Redirect(
+                                    GetParameter(Constants.SUB_DOMAIN) + "/Account/ChooseApp"
+                                );
                             }
                             else
                             {
@@ -280,6 +284,10 @@ namespace LotteryWebApp.Controllers
                                 userStatusRequest
                             );
                             HttpContext.Session.SetComplexData("userStatus", userStatusGet);
+
+                            return Redirect(
+                                GetParameter(Constants.SUB_DOMAIN) + "/Account/ChooseApp"
+                            );
                         }
                         else
                         {

+ 1 - 1
website/Properties/PublishProfiles/FolderProfile1.pubxml

@@ -8,7 +8,7 @@
     <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
     <LastUsedPlatform>Any CPU</LastUsedPlatform>
     <PublishProvider>FileSystem</PublishProvider>
-    <PublishUrl>E:\Ex_publish\LotoNatcomV2</PublishUrl>
+    <PublishUrl>E:\Ex_publish\NatCashLotoNatcomV2</PublishUrl>
     <WebPublishMethod>FileSystem</WebPublishMethod>
     <_TargetId>Folder</_TargetId>
     <SiteUrlToLaunchAfterPublish />