Explorar el Código

update code mới liên quan bổ sung nút mua lượt
tôi ưu css

student hace 12 horas
padre
commit
81fadf192f
Se han modificado 20 ficheros con 129 adiciones y 64 borrados
  1. 18 5
      KittyFallWs/KittyFallWs/src/com/vas/kittyfallwsvas/wsfw/database/WsProcessUtils.java
  2. 0 4
      Kitty_Fall/Kitty_Fall/Common/CommonConstants.cs
  3. 4 0
      Kitty_Fall/Kitty_Fall/Common/CommonLogic.cs
  4. 1 0
      Kitty_Fall/Kitty_Fall/Common/Http/Apis/Request/UserGameLevelResult.cs
  5. 6 2
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Business/Game/GameBusinessImpl.cs
  6. 26 23
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Business/User/UserBusinessImpl.cs
  7. 3 3
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Views/Shared/_Layout.cshtml
  8. 2 6
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/BaseController.cs
  9. 7 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/GameController.cs
  10. 0 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/HistoryController.cs
  11. 3 2
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/HomeController.cs
  12. 0 5
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Helpers/PackagesCacheHelper.cs
  13. 2 2
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Home/Index.cshtml
  14. 4 4
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Play/Index.cshtml
  15. 5 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/css/all.min.css
  16. 5 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/css/input.css
  17. 11 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/js/all.min.js
  18. 11 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/js/input.js
  19. 19 4
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Woker/Services/GameRewardPayoutWorker.cs
  20. 2 2
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Woker/appsettings.json

+ 18 - 5
KittyFallWs/KittyFallWs/src/com/vas/kittyfallwsvas/wsfw/database/WsProcessUtils.java

@@ -204,13 +204,26 @@ public class WsProcessUtils extends DbProcessorAbstract {
                             + "FROM RESULT_REQUEST_INBOX WHERE EVENT_ID=?");
                             + "FROM RESULT_REQUEST_INBOX WHERE EVENT_ID=?");
                     checkDuplicate.setString(1, event.getEventId());
                     checkDuplicate.setString(1, event.getEventId());
                     duplicateRs = checkDuplicate.executeQuery();
                     duplicateRs = checkDuplicate.executeQuery();
-                    if (duplicateRs.next()
-                            && event.getPayloadHash().equals(duplicateRs.getString(1))) {
-                        logger.info("Skip duplicate resultRequest eventId=" + event.getEventId());
+                    if (duplicateRs.next()) {
+                        String existingPayloadHash = duplicateRs.getString(1);
+                        if (event.getPayloadHash().equals(existingPayloadHash)) {
+                            logger.info("Skip duplicate resultRequest eventId=" + event.getEventId());
+                            return 1;
+                        }
+                        // The original event has already claimed this transaction identity.
+                        // Do not retry or process the conflicting payload because doing so may
+                        // charge/renew the subscriber a second time. The spool source is retained
+                        // in the done archive for reconciliation.
+                        logger.error("Skip EVENT_ID collision with different payload"
+                                + ", eventId=" + event.getEventId()
+                                + ", incomingPayloadHash=" + event.getPayloadHash()
+                                + ", existingPayloadHash=" + existingPayloadHash
+                                + ", sourceFile=" + sourceFile
+                                + ", sourceLine=" + sourceLine);
                         return 1;
                         return 1;
                     }
                     }
-                    throw new SQLException("EVENT_ID collision with different payload: "
-                            + event.getEventId(), ex);
+                    throw new SQLException("Duplicate EVENT_ID was reported but the existing row "
+                            + "could not be found: " + event.getEventId(), ex);
                 }
                 }
                 throw ex;
                 throw ex;
             }
             }

+ 0 - 4
Kitty_Fall/Kitty_Fall/Common/CommonConstants.cs

@@ -199,10 +199,6 @@ namespace Common
 
 
     public static class CommonErrorCode
     public static class CommonErrorCode
     {
     {
-        public const int AddPointSuccess = 0;
-        public const int AddPointFailure = -1;
-        public const int AddPointWaiting = 100;
-
         // http code
         // http code
         public const string WAITING_OTP = "100";
         public const string WAITING_OTP = "100";
         public const int USER_EXISTED = 2;
         public const int USER_EXISTED = 2;

+ 4 - 0
Kitty_Fall/Kitty_Fall/Common/CommonLogic.cs

@@ -20,6 +20,10 @@ namespace Common;
 
 
 public static class CommonLogic
 public static class CommonLogic
 {
 {
+    public const int AddPointSuccess = 0;
+    public const int AddPointFailure = -1;
+    public const int AddPointWaiting = 100;
+
     private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(CommonLogic));
     private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(CommonLogic));
 
 
 
 

+ 1 - 0
Kitty_Fall/Kitty_Fall/Common/Http/Apis/Request/UserGameLevelResult.cs

@@ -27,6 +27,7 @@ namespace Common.Http.Apis.Request
         public string reason { get; set; } = "";
         public string reason { get; set; } = "";
         public decimal totalDataMb { get; set; }
         public decimal totalDataMb { get; set; }
         public decimal totalLoyalty { get; set; }
         public decimal totalLoyalty { get; set; }
+        public int totalTurn { get; set; }
     }
     }
 
 
     public class UserGameLevelResultRes : CommonResponse
     public class UserGameLevelResultRes : CommonResponse

+ 6 - 2
Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Business/Game/GameBusinessImpl.cs

@@ -708,6 +708,7 @@ namespace Kitty_Fall.Apis.Business.Game
                 session.LastActionTime = now;
                 session.LastActionTime = now;
                 session.UpdatedTime = now;
                 session.UpdatedTime = now;
                 await dbContext.SaveChangesAsync();
                 await dbContext.SaveChangesAsync();
+                var totalTurn = await GetAvailableTurnAsync(msisdn, now);
 
 
                 return CommonLogic.BuildResponse(
                 return CommonLogic.BuildResponse(
                     url,
                     url,
@@ -719,7 +720,8 @@ namespace Kitty_Fall.Apis.Business.Game
                         point = "0",
                         point = "0",
                         data = "",
                         data = "",
                         score = session.TotalScore.ToString(CultureInfo.InvariantCulture),
                         score = session.TotalScore.ToString(CultureInfo.InvariantCulture),
-                        transId = request.transId
+                        transId = request.transId,
+                        totalTurn = totalTurn
                     });
                     });
             }
             }
             catch (Exception exception)
             catch (Exception exception)
@@ -783,6 +785,7 @@ namespace Kitty_Fall.Apis.Business.Game
                     session.LastActionTime = now;
                     session.LastActionTime = now;
                     session.UpdatedTime = now;
                     session.UpdatedTime = now;
                     await dbContext.SaveChangesAsync();
                     await dbContext.SaveChangesAsync();
+                    var totalTurn = await GetAvailableTurnAsync(msisdn, now);
 
 
                     return CommonLogic.BuildResponse(
                     return CommonLogic.BuildResponse(
                         url,
                         url,
@@ -796,7 +799,8 @@ namespace Kitty_Fall.Apis.Business.Game
                             score = session.TotalScore.ToString(CultureInfo.InvariantCulture),
                             score = session.TotalScore.ToString(CultureInfo.InvariantCulture),
                             transId = request.transId,
                             transId = request.transId,
                             totalDataMb = session.TotalDataMb,
                             totalDataMb = session.TotalDataMb,
-                            totalLoyalty = session.TotalLoyalty
+                            totalLoyalty = session.TotalLoyalty,
+                            totalTurn = totalTurn
                         });
                         });
                 }
                 }
 
 

+ 26 - 23
Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Business/User/UserBusinessImpl.cs

@@ -467,14 +467,7 @@ namespace Kitty_Fall.Apis.Business.User
                 {
                 {
                     // VAS trả "2" (SUCCESS_FOR_THE_FIRST_TIME_REGISTER) khi user đăng ký lần đầu -> vẫn là thành công.
                     // VAS trả "2" (SUCCESS_FOR_THE_FIRST_TIME_REGISTER) khi user đăng ký lần đầu -> vẫn là thành công.
                     // Đăng ký thẳng (không cần OTP) -> trả kèm danh sách gói để client cập nhật ngay trạng thái thuê bao.
                     // Đăng ký thẳng (không cần OTP) -> trả kèm danh sách gói để client cập nhật ngay trạng thái thuê bao.
-                    List<RegInfo> userPackage = await dbContext
-                        .RegInfos.Where(x => x.Msisdn == msisdn
-                            && x.Status == true
-                            && x.Renew == 1
-                            )
-                        .OrderByDescending(x => x.RegisterTime)
-                        .ThenByDescending(x => x.RegisterId)
-                        .ToListAsync();
+                    List<RegInfo> userPackage = await TryLoadActiveRegistrationsAfterSuccessfulCharge(msisdn);
                     return CommonLogic.BuildResponse(
                     return CommonLogic.BuildResponse(
                         url,
                         url,
                         json,
                         json,
@@ -579,14 +572,7 @@ namespace Kitty_Fall.Apis.Business.User
                 {
                 {
                     // VAS trả "2" (SUCCESS_FOR_THE_FIRST_TIME_REGISTER) khi user đăng ký lần đầu -> vẫn là thành công.
                     // VAS trả "2" (SUCCESS_FOR_THE_FIRST_TIME_REGISTER) khi user đăng ký lần đầu -> vẫn là thành công.
                     // Trả kèm danh sách gói đang dùng để client cập nhật ngay trạng thái thuê bao (không phải gọi lại GetProfileInfo).
                     // Trả kèm danh sách gói đang dùng để client cập nhật ngay trạng thái thuê bao (không phải gọi lại GetProfileInfo).
-                    List<RegInfo> userPackage = await dbContext
-                        .RegInfos.Where(x => x.Msisdn == msisdn
-                            && x.Status == true
-                            && x.Renew == 1
-                         )
-                        .OrderByDescending(x => x.RegisterTime)
-                        .ThenByDescending(x => x.RegisterId)
-                        .ToListAsync();
+                    List<RegInfo> userPackage = await TryLoadActiveRegistrationsAfterSuccessfulCharge(msisdn);
                     return CommonLogic.BuildResponse(
                     return CommonLogic.BuildResponse(
                         url,
                         url,
                         json,
                         json,
@@ -675,13 +661,7 @@ namespace Kitty_Fall.Apis.Business.User
                 if (wsRegisterSubQuickResponse.@return.errorCode == CommonErrorCode.Success
                 if (wsRegisterSubQuickResponse.@return.errorCode == CommonErrorCode.Success
                     || wsRegisterSubQuickResponse.@return.errorCode == CommonErrorCode.SuccessFirstRegister)
                     || wsRegisterSubQuickResponse.@return.errorCode == CommonErrorCode.SuccessFirstRegister)
                 {
                 {
-                    List<RegInfo> userPackage = await dbContext
-                        .RegInfos.Where(x => x.Msisdn == msisdn
-                            && x.Status == true
-                            && x.Renew == 1)
-                        .OrderByDescending(x => x.RegisterTime)
-                        .ThenByDescending(x => x.RegisterId)
-                        .ToListAsync();
+                    List<RegInfo> userPackage = await TryLoadActiveRegistrationsAfterSuccessfulCharge(msisdn);
 
 
                     return CommonLogic.BuildResponse(
                     return CommonLogic.BuildResponse(
                         url,
                         url,
@@ -1475,6 +1455,29 @@ namespace Kitty_Fall.Apis.Business.User
                 new BadRequestObjectResult(new { message = Lang.badRequest })
                 new BadRequestObjectResult(new { message = Lang.badRequest })
             );
             );
         }
         }
+        private async Task<List<RegInfo>> TryLoadActiveRegistrationsAfterSuccessfulCharge(string msisdn)
+        {
+            try
+            {
+                return await dbContext.RegInfos
+                    .Where(x => x.Msisdn == msisdn
+                        && x.Status == true
+                        && x.Renew == 1)
+                    .OrderByDescending(x => x.RegisterTime)
+                    .ThenByDescending(x => x.RegisterId)
+                    .ToListAsync();
+            }
+            catch (Exception exception)
+            {
+                // Charging/registration was already confirmed by VAS. A profile refresh
+                // failure must not turn that completed transaction into a client-visible
+                // failure, which could also encourage a duplicate purchase attempt.
+                log.Warn("VAS registration succeeded, but active package refresh failed. "
+                    + "Returning the confirmed success response without regInfos.", exception);
+                return new List<RegInfo>();
+            }
+        }
+
         private static object GetHttpSendRequest()
         private static object GetHttpSendRequest()
         {
         {
             return GetHttpSendRequest;
             return GetHttpSendRequest;

+ 3 - 3
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Views/Shared/_Layout.cshtml

@@ -84,7 +84,7 @@
                     <div class="bg-admin-active p-1.5 rounded-lg">
                     <div class="bg-admin-active p-1.5 rounded-lg">
                         <i class="fas fa-rocket text-white"></i>
                         <i class="fas fa-rocket text-white"></i>
                     </div>
                     </div>
-                    <span>MYJOB CMS</span>
+                    <span>KITTY_FALL CMS</span>
                 </a>
                 </a>
                 <button type="button" onclick="closeMobileSidebar()" class="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white">
                 <button type="button" onclick="closeMobileSidebar()" class="flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-white">
                     <i class="fas fa-times"></i>
                     <i class="fas fa-times"></i>
@@ -287,7 +287,7 @@
                     <div class="bg-admin-active p-1.5 rounded-lg">
                     <div class="bg-admin-active p-1.5 rounded-lg">
                         <i class="fas fa-rocket text-white"></i>
                         <i class="fas fa-rocket text-white"></i>
                     </div>
                     </div>
-                    <span>MYJOB CMS</span>
+                    <span>KITTY_FALL CMS</span>
                 </a>
                 </a>
                 <ul class="mt-8">
                 <ul class="mt-8">
                     @if (canViewDashboard)
                     @if (canViewDashboard)
@@ -485,7 +485,7 @@
                             <i class="fas fa-bars"></i>
                             <i class="fas fa-bars"></i>
                         </button>
                         </button>
                         <div class="min-w-0">
                         <div class="min-w-0">
-                            <div class="text-xs font-bold uppercase tracking-[0.2em] text-gray-400 md:hidden">MYJOB CMS</div>
+                            <div class="text-xs font-bold uppercase tracking-[0.2em] text-gray-400 md:hidden">KITTY_FALL CMS</div>
                             <div class="truncate text-lg font-bold text-gray-800">@ViewData["Title"]</div>
                             <div class="truncate text-lg font-bold text-gray-800">@ViewData["Title"]</div>
                         </div>
                         </div>
                     </div>
                     </div>

+ 2 - 6
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/BaseController.cs

@@ -1,17 +1,13 @@
 
 
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Rendering;
-using Microsoft.AspNetCore.Mvc.ViewEngines;
-using Microsoft.AspNetCore.Mvc.ViewFeatures;
 using System.Security.Cryptography;
 using System.Security.Cryptography;
-using System.ServiceModel;
 
 
 namespace Kitty_Fall.Website.Controllers;
 namespace Kitty_Fall.Website.Controllers;
 
 
 public class BaseController : Controller
 public class BaseController : Controller
 {
 {
 
 
-    
+
     private string GenerateAuthId()
     private string GenerateAuthId()
     {
     {
 #pragma warning disable SYSLIB0023 // Type or member is obsolete
 #pragma warning disable SYSLIB0023 // Type or member is obsolete
@@ -88,6 +84,6 @@ public class BaseController : Controller
     }
     }
 
 
 
 
-    
+
 
 
 }
 }

+ 7 - 1
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/GameController.cs

@@ -1,4 +1,3 @@
-using Common;
 using Common.Extension;
 using Common.Extension;
 using Common.Http.Apis.Request;
 using Common.Http.Apis.Request;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc;
@@ -25,6 +24,7 @@ namespace Kitty_Fall.Website.Controllers
                 if (apiResult.errorCode == "0" && apiResult.data != null)
                 if (apiResult.errorCode == "0" && apiResult.data != null)
                 {
                 {
                     var maxLevel = await GetTodayMaxLevelAsync(request);
                     var maxLevel = await GetTodayMaxLevelAsync(request);
+                    HttpContext.Session.SetComplexData("totalTurn", (long)apiResult.data.totalTurn);
 
 
                     return Json(new
                     return Json(new
                     {
                     {
@@ -33,6 +33,7 @@ namespace Kitty_Fall.Website.Controllers
                         data = apiResult.data.data,
                         data = apiResult.data.data,
                         score = apiResult.data.score,
                         score = apiResult.data.score,
                         transId = apiResult.data.transId,
                         transId = apiResult.data.transId,
+                        totalTurn = apiResult.data.totalTurn,
                         max_level = maxLevel
                         max_level = maxLevel
                     });
                     });
                 }
                 }
@@ -101,6 +102,10 @@ namespace Kitty_Fall.Website.Controllers
                 log.Info($"ExitGame msisdn={request.msisdn}, transId={request.transId}, level={request.level}, score={request.score}, gameType={request.gameType}, gameOver={request.gameOver}");
                 log.Info($"ExitGame msisdn={request.msisdn}, transId={request.transId}, level={request.level}, score={request.score}, gameType={request.gameType}, gameOver={request.gameOver}");
                 request.gameOver = request.gameOver == 0 ? 1 : request.gameOver;
                 request.gameOver = request.gameOver == 0 ? 1 : request.gameOver;
                 var apiResult = await SendGameApiAsync(request, UrlConstant.GameSaveLevelResultUrl);
                 var apiResult = await SendGameApiAsync(request, UrlConstant.GameSaveLevelResultUrl);
+                if (apiResult.errorCode == "0" && apiResult.data != null)
+                {
+                    HttpContext.Session.SetComplexData("totalTurn", (long)apiResult.data.totalTurn);
+                }
                 var payload = JsonSerializer.Serialize(new
                 var payload = JsonSerializer.Serialize(new
                 {
                 {
                     type = "EXIT_GAME_RESULT",
                     type = "EXIT_GAME_RESULT",
@@ -108,6 +113,7 @@ namespace Kitty_Fall.Website.Controllers
                     score = apiResult.data?.score ?? request.score.ToString(),
                     score = apiResult.data?.score ?? request.score.ToString(),
                     data = apiResult.data?.totalDataMb ?? 0,
                     data = apiResult.data?.totalDataMb ?? 0,
                     loyalty = apiResult.data?.totalLoyalty ?? 0,
                     loyalty = apiResult.data?.totalLoyalty ?? 0,
+                    totalTurn = apiResult.data?.totalTurn,
                     level = request.level,
                     level = request.level,
                     message = apiResult.message ?? ""
                     message = apiResult.message ?? ""
                 });
                 });

+ 0 - 1
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/HistoryController.cs

@@ -2,7 +2,6 @@ using Common;
 using Common.Extension;
 using Common.Extension;
 using Common.Http.Apis.Request;
 using Common.Http.Apis.Request;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using Newtonsoft.Json.Linq;
 
 
 namespace Kitty_Fall.Website.Controllers
 namespace Kitty_Fall.Website.Controllers

+ 3 - 2
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/HomeController.cs

@@ -8,7 +8,6 @@ using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using Newtonsoft.Json.Linq;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Net.Http.Headers;
 using System.Net.Http.Headers;
-using System.Security.Policy;
 using System.Text;
 using System.Text;
 using static DotnetLib.Http.HttpObject;
 using static DotnetLib.Http.HttpObject;
 
 
@@ -76,7 +75,9 @@ namespace Kitty_Fall.Website.Controllers
 
 
                 var allowedTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
                 var allowedTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
                 {
                 {
-                    ["image/jpeg"] = ".jpg", ["image/png"] = ".png", ["image/webp"] = ".webp"
+                    ["image/jpeg"] = ".jpg",
+                    ["image/png"] = ".png",
+                    ["image/webp"] = ".webp"
                 };
                 };
                 if (!allowedTypes.TryGetValue(avatar.ContentType, out var extension))
                 if (!allowedTypes.TryGetValue(avatar.ContentType, out var extension))
                     return BadRequest(new { success = false, message = "Only JPG, PNG or WEBP images are supported." });
                     return BadRequest(new { success = false, message = "Only JPG, PNG or WEBP images are supported." });

+ 0 - 5
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Helpers/PackagesCacheHelper.cs

@@ -1,11 +1,6 @@
-using Common;
 using Common.Extension;
 using Common.Extension;
 using Common.Http.Apis.Request;
 using Common.Http.Apis.Request;
-using Microsoft.AspNetCore.Http;
 using Newtonsoft.Json.Linq;
 using Newtonsoft.Json.Linq;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
 
 
 namespace Kitty_Fall.Website.Helpers
 namespace Kitty_Fall.Website.Helpers
 {
 {

+ 2 - 2
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Home/Index.cshtml

@@ -346,8 +346,8 @@
                 url: "/Login/LogoutBackAction",
                 url: "/Login/LogoutBackAction",
                 type: "POST",
                 type: "POST",
                 complete: function () {
                 complete: function () {
-                    //window.location.href = "mytel://back";
-                    window.location.href = "Login/index";
+                    window.location.href = "mytel://back";
+                    // window.location.href = "Login/index";
                 }
                 }
             });
             });
         }
         }

+ 4 - 4
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Play/Index.cshtml

@@ -26,11 +26,11 @@
                 <img src="~/kitty/assets/kitty-fall/coin.png" alt="">
                 <img src="~/kitty/assets/kitty-fall/coin.png" alt="">
                 <strong>@Model.TotalPoint.ToString("N0")</strong>
                 <strong>@Model.TotalPoint.ToString("N0")</strong>
             </span>
             </span>
-            <span class="play-top-chip">
+            <a class="play-top-chip play-turn-chip" href="@Url.Action("Index", "Home", new { buyturn = true })" aria-label="@Lang.BuyTurn">
                 <img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
                 <img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
-                <strong>@Model.TotalTurn.ToString("N0")</strong>
+                <strong data-play-total-turn>@Model.TotalTurn.ToString("N0")</strong>
                 <b>+</b>
                 <b>+</b>
-            </span>
+            </a>
         </div>
         </div>
         <button class="menu-button" aria-label="@Lang.OpenMenu">
         <button class="menu-button" aria-label="@Lang.OpenMenu">
             <span></span>
             <span></span>
@@ -96,7 +96,7 @@
     </section>
     </section>
 
 
     <div class="play-action-bar">
     <div class="play-action-bar">
-        <a class="play-back-button" href="/">
+        <a class="play-back-button" href="@Url.Action("Index", "Home")">
             <span aria-hidden="true"></span>
             <span aria-hidden="true"></span>
             <strong>@Lang.Back</strong>
             <strong>@Lang.Back</strong>
         </a>
         </a>

+ 5 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/css/all.min.css

@@ -203,6 +203,11 @@ button {
     line-height: 1;
     line-height: 1;
     font-weight: 900;
     font-weight: 900;
     text-shadow: 0 1px 1px rgba(65, 68, 147, 0.2);
     text-shadow: 0 1px 1px rgba(65, 68, 147, 0.2);
+    text-decoration: none;
+}
+
+.play-turn-chip {
+    cursor: pointer;
 }
 }
 
 
 .play-top-chip img {
 .play-top-chip img {

+ 5 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/css/input.css

@@ -203,6 +203,11 @@ button {
     line-height: 1;
     line-height: 1;
     font-weight: 900;
     font-weight: 900;
     text-shadow: 0 1px 1px rgba(65, 68, 147, 0.2);
     text-shadow: 0 1px 1px rgba(65, 68, 147, 0.2);
+    text-decoration: none;
+}
+
+.play-turn-chip {
+    cursor: pointer;
 }
 }
 
 
 .play-top-chip img {
 .play-top-chip img {

+ 11 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/js/all.min.js

@@ -260,6 +260,7 @@ const initKittyFall = () => {
         const gameOverScore = playGameRoot.querySelector("[data-game-over-score]");
         const gameOverScore = playGameRoot.querySelector("[data-game-over-score]");
         const gameOverData = playGameRoot.querySelector("[data-game-over-data]");
         const gameOverData = playGameRoot.querySelector("[data-game-over-data]");
         const gameOverLoyalty = playGameRoot.querySelector("[data-game-over-loyalty]");
         const gameOverLoyalty = playGameRoot.querySelector("[data-game-over-loyalty]");
+        const totalTurnElement = playGameRoot.querySelector("[data-play-total-turn]");
         let selectedLevel = playGameRoot.querySelector("[data-level].is-selected")?.dataset.level
         let selectedLevel = playGameRoot.querySelector("[data-level].is-selected")?.dataset.level
             || levelButtons[0]?.dataset.level
             || levelButtons[0]?.dataset.level
             || "Easy";
             || "Easy";
@@ -311,6 +312,14 @@ const initKittyFall = () => {
 
 
         const formatNumber = (value) => Math.max(0, Math.round(value)).toLocaleString("en-US");
         const formatNumber = (value) => Math.max(0, Math.round(value)).toLocaleString("en-US");
 
 
+        const updateTotalTurn = (value) => {
+            if (value === null || value === undefined || value === "") return;
+            const totalTurn = Number(value);
+            if (!totalTurnElement || !Number.isFinite(totalTurn)) return;
+            totalTurnElement.textContent = formatNumber(totalTurn);
+            playGameRoot.dataset.totalTurn = String(Math.max(0, Math.round(totalTurn)));
+        };
+
         const addLevelReward = (data) => {
         const addLevelReward = (data) => {
             const rewardType = String(data?.rewardType || "").toUpperCase();
             const rewardType = String(data?.rewardType || "").toUpperCase();
             const rewardValue = toNumber(data?.rewardValue);
             const rewardValue = toNumber(data?.rewardValue);
@@ -409,6 +418,7 @@ const initKittyFall = () => {
             }
             }
             if (event.data?.type === "EXIT_GAME_RESULT") {
             if (event.data?.type === "EXIT_GAME_RESULT") {
                 setRunSummary(event.data);
                 setRunSummary(event.data);
+                updateTotalTurn(event.data.totalTurn);
                 closeEmbeddedGame();
                 closeEmbeddedGame();
                 showGameOverSummary();
                 showGameOverSummary();
             }
             }
@@ -453,6 +463,7 @@ const initKittyFall = () => {
                     return;
                     return;
                 }
                 }
 
 
+                updateTotalTurn(result.totalTurn);
                 resetRunRewards();
                 resetRunRewards();
                 gameStarted = true;
                 gameStarted = true;
                 playGameRoot.classList.add("is-game-running");
                 playGameRoot.classList.add("is-game-running");

+ 11 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/js/input.js

@@ -260,6 +260,7 @@ const initKittyFall = () => {
         const gameOverScore = playGameRoot.querySelector("[data-game-over-score]");
         const gameOverScore = playGameRoot.querySelector("[data-game-over-score]");
         const gameOverData = playGameRoot.querySelector("[data-game-over-data]");
         const gameOverData = playGameRoot.querySelector("[data-game-over-data]");
         const gameOverLoyalty = playGameRoot.querySelector("[data-game-over-loyalty]");
         const gameOverLoyalty = playGameRoot.querySelector("[data-game-over-loyalty]");
+        const totalTurnElement = playGameRoot.querySelector("[data-play-total-turn]");
         let selectedLevel = playGameRoot.querySelector("[data-level].is-selected")?.dataset.level
         let selectedLevel = playGameRoot.querySelector("[data-level].is-selected")?.dataset.level
             || levelButtons[0]?.dataset.level
             || levelButtons[0]?.dataset.level
             || "Easy";
             || "Easy";
@@ -311,6 +312,14 @@ const initKittyFall = () => {
 
 
         const formatNumber = (value) => Math.max(0, Math.round(value)).toLocaleString("en-US");
         const formatNumber = (value) => Math.max(0, Math.round(value)).toLocaleString("en-US");
 
 
+        const updateTotalTurn = (value) => {
+            if (value === null || value === undefined || value === "") return;
+            const totalTurn = Number(value);
+            if (!totalTurnElement || !Number.isFinite(totalTurn)) return;
+            totalTurnElement.textContent = formatNumber(totalTurn);
+            playGameRoot.dataset.totalTurn = String(Math.max(0, Math.round(totalTurn)));
+        };
+
         const addLevelReward = (data) => {
         const addLevelReward = (data) => {
             const rewardType = String(data?.rewardType || "").toUpperCase();
             const rewardType = String(data?.rewardType || "").toUpperCase();
             const rewardValue = toNumber(data?.rewardValue);
             const rewardValue = toNumber(data?.rewardValue);
@@ -409,6 +418,7 @@ const initKittyFall = () => {
             }
             }
             if (event.data?.type === "EXIT_GAME_RESULT") {
             if (event.data?.type === "EXIT_GAME_RESULT") {
                 setRunSummary(event.data);
                 setRunSummary(event.data);
+                updateTotalTurn(event.data.totalTurn);
                 closeEmbeddedGame();
                 closeEmbeddedGame();
                 showGameOverSummary();
                 showGameOverSummary();
             }
             }
@@ -453,6 +463,7 @@ const initKittyFall = () => {
                     return;
                     return;
                 }
                 }
 
 
+                updateTotalTurn(result.totalTurn);
                 resetRunRewards();
                 resetRunRewards();
                 gameStarted = true;
                 gameStarted = true;
                 playGameRoot.classList.add("is-game-running");
                 playGameRoot.classList.add("is-game-running");

+ 19 - 4
Kitty_Fall/Kitty_Fall/Kitty_Fall.Woker/Services/GameRewardPayoutWorker.cs

@@ -120,14 +120,23 @@ namespace Kitty_Fall.Woker.Services
                     rewardLog.Msisdn,
                     rewardLog.Msisdn,
                     rewardLog.RewardLogId.ToString(CultureInfo.InvariantCulture),
                     rewardLog.RewardLogId.ToString(CultureInfo.InvariantCulture),
                     GetPackageName(rewardLog));
                     GetPackageName(rewardLog));
-                var isSuccess = addPointRes?.success == true;
+                var responseStatus = addPointRes?.result?.status;
+                var hasResponseCode = int.TryParse(
+                    responseStatus,
+                    NumberStyles.Integer,
+                    CultureInfo.InvariantCulture,
+                    out var responseCode);
+                var isSuccess = addPointRes?.success == true
+                    || (hasResponseCode && responseCode == CommonLogic.AddPointSuccess);
+                var isWaiting = hasResponseCode
+                    && responseCode == CommonLogic.AddPointWaiting;
 
 
                 rewardLog.ResponseTime = DateTime.Now;
                 rewardLog.ResponseTime = DateTime.Now;
                 // DotnetLib tra ma/trang thai cua doi tac trong result.status.
                 // DotnetLib tra ma/trang thai cua doi tac trong result.status.
-                rewardLog.ResponseCode = addPointRes?.result?.status
+                rewardLog.ResponseCode = responseStatus
                     ?? (isSuccess
                     ?? (isSuccess
-                        ? CommonErrorCode.AddPointSuccess.ToString(CultureInfo.InvariantCulture)
-                        : CommonErrorCode.AddPointFailure.ToString(CultureInfo.InvariantCulture));
+                        ? CommonLogic.AddPointSuccess.ToString(CultureInfo.InvariantCulture)
+                        : CommonLogic.AddPointFailure.ToString(CultureInfo.InvariantCulture));
                 rewardLog.ResponseMessage = Truncate(addPointRes?.message, 500);
                 rewardLog.ResponseMessage = Truncate(addPointRes?.message, 500);
                 rewardLog.ExternalTransId = rewardLog.RewardLogId.ToString(CultureInfo.InvariantCulture);
                 rewardLog.ExternalTransId = rewardLog.RewardLogId.ToString(CultureInfo.InvariantCulture);
                 rewardLog.UpdatedTime = DateTime.Now;
                 rewardLog.UpdatedTime = DateTime.Now;
@@ -140,6 +149,12 @@ namespace Kitty_Fall.Woker.Services
                     await SendSuccessMtAsync(dbContext, rewardLog, cancellationToken);
                     await SendSuccessMtAsync(dbContext, rewardLog, cancellationToken);
                     return;
                     return;
                 }
                 }
+                else if (isWaiting)
+                {
+                    // Doi tac da nhan giao dich va dang xu ly. Giu PROCESSING de doi soat,
+                    // khong danh dau FAILED va khong tu dong gui lai giao dich.
+                    rewardLog.Status = StatusProcessing;
+                }
                 else
                 else
                 {
                 {
                     rewardLog.RetryCount++;
                     rewardLog.RetryCount++;

+ 2 - 2
Kitty_Fall/Kitty_Fall/Kitty_Fall.Woker/appsettings.json

@@ -18,8 +18,8 @@
     "IntervalSeconds": 10,
     "IntervalSeconds": 10,
     "BatchSize": 20,
     "BatchSize": 20,
     "ServiceId": "Kitty_Fall_DAYLY",
     "ServiceId": "Kitty_Fall_DAYLY",
-    "DataPackageName": "GAME_DATA",
-    "LoyaltyPackageName": "GAME_LOYALTY",
+    "DataPackageName": "DATA_",
+    "LoyaltyPackageName": "LOYALTY_",
     "DefaultPackageName": "GAME_REWARD"
     "DefaultPackageName": "GAME_REWARD"
   },
   },
   "MonthlyWinner": {
   "MonthlyWinner": {