@@ -213,7 +215,8 @@ export function WalletScreen() {
{isCreditPlayer ? (
{t("wallet.creditNoTransferHint", {
- defaultValue: "信用盘账号由代理授信,无需主站转入转出;额度调整请联系代理。",
+ defaultValue:
+ "由代理授信,无需主站转入转出;中奖不即时派彩,盈亏在账期统一结算,额度调整请联系代理。",
})}
) : (
diff --git a/src/i18n/locales/en/entry.json b/src/i18n/locales/en/entry.json
index a33e677..27005d2 100644
--- a/src/i18n/locales/en/entry.json
+++ b/src/i18n/locales/en/entry.json
@@ -49,6 +49,16 @@
"footer": {
"secure": "Secure. Trusted. Authorized access."
},
+ "login": {
+ "title": "Agent player login",
+ "hint": "Sign in with the account your agent created for you. Main-site users should enter from the main site.",
+ "username": "Username",
+ "password": "Password",
+ "submit": "Sign in",
+ "missingFields": "Please enter username and password",
+ "failed": "Sign-in failed",
+ "backEntry": "Back to entry"
+ },
"errors": {
"noToken": "No authorization token found",
"noTokenDetail": "Please return to the main site and try again.",
diff --git a/src/i18n/locales/en/player.json b/src/i18n/locales/en/player.json
index 2e2f1b5..ae9b547 100644
--- a/src/i18n/locales/en/player.json
+++ b/src/i18n/locales/en/player.json
@@ -24,7 +24,8 @@
"results": "Results",
"orders": "My Bets",
"rules": "Rules",
- "wallet": "Wallet"
+ "wallet": "Wallet",
+ "credit": "Credit"
},
"notifications": {
"title": "Pending reconciliation",
@@ -346,10 +347,12 @@
"wallet": {
"title": "Wallet",
"subtitle": "Balance and transfer records",
+ "creditTitle": "Credit",
+ "creditSubtitle": "Credit usage and billing-period activity",
"balance": "Wallet Balance",
"creditAvailable": "Available credit",
"creditSummary": "Limit {{limit}} · Used {{used}}",
- "creditNoTransferHint": "Credit accounts are funded by your agent; no main-site transfers. Contact your agent to change your limit.",
+ "creditNoTransferHint": "Credit accounts are funded by your agent; no main-site transfers. Wins are not paid out instantly—profit and loss are settled in billing periods. Contact your agent to change your limit.",
"available": "Available {{amount}}",
"transferIn": "Transfer In",
"transferOut": "Transfer Out",
@@ -358,6 +361,8 @@
"logs": "Logs",
"logsTitle": "Wallet Logs",
"logsSubtitle": "Transfer and betting records",
+ "creditLogsTitle": "Credit activity",
+ "creditLogsSubtitle": "Holds, releases, and billing-period payments",
"typeFilter": "Type Filter",
"transferInTitle": "Transfer In",
"transferOutTitle": "Transfer Out",
@@ -392,12 +397,14 @@
"flowsTitle": "Wallet logs",
"creditFlowsTitle": "Credit activity",
"playerChannel": {
- "credit": "Credit-line player (agent credit ledger)",
+ "credit": "Credit line: credit holds, releases, and billing payments (not wallet payouts)",
"wallet": "Main-site wallet player (transfers & wallet ledger)"
},
"creditAvailableAfter": "Available credit after",
+ "creditPaymentRecordOnly": "Billing payment record only; not counted in available credit",
"totalRecords": "{{total}} records",
"emptyLogs": "No wallet logs",
+ "emptyCreditLogs": "No credit activity",
"noMoreLogs": "No more transactions",
"balanceAfter": "Balance after",
"wsBalanceUpdated": "Balance {{change}} ({{reason}})",
@@ -420,10 +427,12 @@
},
"creditFlow": {
"all": "All",
- "bet": "Bet hold",
- "prize": "Payout",
- "refund": "Credit release",
- "reversal": "Reversal"
+ "bet": "Credit hold",
+ "credit_release": "Credit release",
+ "bill_settlement": "Period payment",
+ "win_credit": "Win release",
+ "refund": "Period confirm release",
+ "reversal": "Refund release"
},
"txnStatus": {
"posted": "Success",
@@ -498,6 +507,7 @@
"winAmount": "Win amount {{amount}}",
"jackpotAmount": "Jackpot {{amount}}",
"payoutTotal": "Payout total {{amount}}",
+ "creditWinTotal": "Win total {{amount}}",
"matchLose": "Match result: not won",
"matchResult": "Match result",
"drawPendingMatch": "Draw numbers are not published yet. Winning status cannot be determined.",
@@ -528,9 +538,11 @@
"next": "Next ›",
"drawTime": "Draw time: {{time}}",
"myPayout": "My payout for this issue",
+ "creditMyWin": "My wins for this issue",
"regular": "Regular: {{amount}}",
"jackpot": "Jackpot: {{amount}}",
"hitPending": "Your ticket has hit a result cell in this issue. The amount summary will show after payout is completed.",
+ "creditHitPending": "Your ticket matched this issue. Amounts will show after settlement; profit and loss are handled in billing periods.",
"hitHint": "If you win, numbers matched by your tickets are highlighted in gold.",
"viewMyWinning": "View my winning status",
"jackpotLabel": "Jackpot",
@@ -645,6 +657,10 @@
"partial_failed": "Partially failed",
"pending_payout": "Won, pending payout",
"settled_win": "Paid",
+ "credit": {
+ "pending_payout": "Won, pending settlement",
+ "settled_win": "Won"
+ },
"settled_lose": "Not won",
"failed": "Failed",
"refunded": "Refunded",
diff --git a/src/i18n/locales/ne/entry.json b/src/i18n/locales/ne/entry.json
index e7062c6..8ef85e1 100644
--- a/src/i18n/locales/ne/entry.json
+++ b/src/i18n/locales/ne/entry.json
@@ -49,6 +49,16 @@
"footer": {
"secure": "सुरक्षित। विश्वसनीय। अधिकृत पहुँच।"
},
+ "login": {
+ "title": "एजेन्ट खेलाडी लगइन",
+ "hint": "एजेन्टले दिएको खाताबाट लगइन गर्नुहोस्। मुख्य साइट प्रयोगकर्ताहरू मुख्य साइटबाट प्रवेश गर्नुहोस्।",
+ "username": "प्रयोगकर्ता नाम",
+ "password": "पासवर्ड",
+ "submit": "लगइन",
+ "missingFields": "कृपया प्रयोगकर्ता नाम र पासवर्ड भर्नुहोस्",
+ "failed": "लगइन असफल",
+ "backEntry": "प्रवेशमा फर्कनुहोस्"
+ },
"errors": {
"noToken": "कुनै प्राधिकरण टोकन फेला परेन",
"noTokenDetail": "कृपया मुख्य साइटमा फर्कनुहोस् र फेरि प्रयास गर्नुहोस्।",
diff --git a/src/i18n/locales/ne/player.json b/src/i18n/locales/ne/player.json
index ce2141f..b51c25a 100644
--- a/src/i18n/locales/ne/player.json
+++ b/src/i18n/locales/ne/player.json
@@ -24,7 +24,8 @@
"results": "नतिजा",
"orders": "मेरा बेट",
"rules": "नियम",
- "wallet": "वालेट"
+ "wallet": "वालेट",
+ "credit": "क्रेडिट"
},
"notifications": {
"title": "मिलान बाँकी सूचना",
@@ -387,6 +388,30 @@
"pendingDescription": "यी ट्रान्सफरहरू मुख्य साइटबाट अन्तिम पुष्टि भएका छैनन्। लामो समयसम्म नआए support सम्पर्क गर्नुहोस्।",
"pendingStatus": "प्रक्रिया हुँदैछ",
"flowsTitle": "वालेट लग",
+ "creditTitle": "क्रेडिट",
+ "creditSubtitle": "क्रेडिट प्रयोग र बिलिङ अवधि लेनदेन",
+ "creditAvailable": "उपलब्ध क्रेडिट",
+ "creditSummary": "सीमा {{limit}} · प्रयोग {{used}}",
+ "creditNoTransferHint": "क्रेडिट खाता एजेन्टले दिन्छ; मुख्य साइट ट्रान्सफर छैन। जित तुरुन्त भुक्तानी हुँदैन—नाफा/नोक्सान बिलिङ अवधिमा मिल्छ।",
+ "creditFlowsTitle": "क्रेडिट लेनदेन",
+ "creditLogsTitle": "क्रेडिट लेनदेन",
+ "creditLogsSubtitle": "ओगट, मुक्ति र बिलिङ भुक्तानी",
+ "playerChannel": {
+ "credit": "क्रेडिट लाइन: ओगट, मुक्ति र बिलिङ भुक्तानी (वालेट भुक्तानी होइन)",
+ "wallet": "मुख्य साइट वालेट खेलाडी"
+ },
+ "creditAvailableAfter": "पछि उपलब्ध क्रेडिट",
+ "creditPaymentRecordOnly": "बिलिङ भुक्तानी रेकर्ड मात्र; उपलब्ध क्रेडिटमा गणना हुँदैन",
+ "emptyCreditLogs": "क्रेडिट लेनदेन छैन",
+ "creditFlow": {
+ "all": "सबै",
+ "bet": "क्रेडिट ओगट",
+ "credit_release": "मुक्ति",
+ "bill_settlement": "अवधि भुक्तानी",
+ "win_credit": "जित मुक्ति",
+ "refund": "अवधि पुष्टि मुक्ति",
+ "reversal": "फिर्ता मुक्ति"
+ },
"totalRecords": "{{total}} रेकर्ड",
"emptyLogs": "वालेट लग छैन",
"noMoreLogs": "थप लेनदेन छैन",
@@ -482,6 +507,7 @@
"winAmount": "जित रकम {{amount}}",
"jackpotAmount": "Jackpot {{amount}}",
"payoutTotal": "कुल भुक्तानी {{amount}}",
+ "creditWinTotal": "जित कुल {{amount}}",
"matchLose": "मिलान नतिजा: जितेन",
"matchResult": "मिलान नतिजा",
"drawPendingMatch": "यस इश्यूका ड्र नम्बर प्रकाशित भएका छैनन्। जित-नजित अझै निर्धारण गर्न मिल्दैन।",
@@ -512,9 +538,11 @@
"next": "अर्को ›",
"drawTime": "ड्र समय: {{time}}",
"myPayout": "यस इश्यूमा मेरो भुक्तानी",
+ "creditMyWin": "यस इश्यूमा मेरो जित",
"regular": "सामान्य: {{amount}}",
"jackpot": "Jackpot: {{amount}}",
"hitPending": "तपाईंको टिकटले यस इश्यूको नतिजा सेल हिट गरेको छ। भुक्तानी पूरा भएपछि रकम देखिनेछ।",
+ "creditHitPending": "तपाईंको टिकट मिल्यो। मिलान पछि रकम देखिनेछ; नाफा/नोक्सान बिलिङ अवधिमा मिल्छ।",
"hitHint": "तपाईं जित्नुभयो भने, तपाईंका टिकटसँग मिलेका नम्बरहरू सुनौलो रंगमा देखिन्छन्।",
"viewMyWinning": "मेरो जित स्थिति हेर्नुहोस्",
"jackpotLabel": "Jackpot",
@@ -629,6 +657,10 @@
"partial_failed": "आंशिक असफल",
"pending_payout": "जितेको, भुक्तानी बाँकी",
"settled_win": "भुक्तानी भयो",
+ "credit": {
+ "pending_payout": "जितेको, मिलान बाँकी",
+ "settled_win": "जितेको"
+ },
"settled_lose": "जितेन",
"failed": "असफल",
"refunded": "फिर्ता",
diff --git a/src/i18n/locales/zh/entry.json b/src/i18n/locales/zh/entry.json
index db12045..2007881 100644
--- a/src/i18n/locales/zh/entry.json
+++ b/src/i18n/locales/zh/entry.json
@@ -3,7 +3,7 @@
"backgroundAlt": "页头背景"
},
"loading": {
- "title": "正在进入彩票大厅(前端测试标记)",
+ "title": "正在进入彩票大厅",
"progress": "进度"
},
"steps": {
diff --git a/src/i18n/locales/zh/player.json b/src/i18n/locales/zh/player.json
index 48ce8d4..ce91182 100644
--- a/src/i18n/locales/zh/player.json
+++ b/src/i18n/locales/zh/player.json
@@ -24,7 +24,8 @@
"results": "开奖结果",
"orders": "我的注单",
"rules": "规则",
- "wallet": "钱包"
+ "wallet": "钱包",
+ "credit": "信用"
},
"notifications": {
"title": "待对账提醒",
@@ -346,10 +347,12 @@
"wallet": {
"title": "钱包",
"subtitle": "余额与划转记录",
+ "creditTitle": "信用",
+ "creditSubtitle": "额度占用与账期流水",
"balance": "钱包余额",
"creditAvailable": "可用信用",
"creditSummary": "授信 {{limit}} · 已用 {{used}}",
- "creditNoTransferHint": "信用盘账号由代理授信,无需主站转入转出;额度调整请联系代理。",
+ "creditNoTransferHint": "由代理授信,无需主站转入转出;中奖不即时派彩,盈亏在账期统一结算,额度调整请联系代理。",
"available": "可用 {{amount}}",
"transferIn": "转入",
"transferOut": "转出",
@@ -358,6 +361,8 @@
"logs": "流水",
"logsTitle": "钱包流水",
"logsSubtitle": "划转与下注记录",
+ "creditLogsTitle": "信用流水",
+ "creditLogsSubtitle": "占用、释额与账期收付",
"typeFilter": "类型筛选",
"transferInTitle": "转入资金",
"transferOutTitle": "转出资金",
@@ -392,12 +397,14 @@
"flowsTitle": "资金流水",
"creditFlowsTitle": "信用流水",
"playerChannel": {
- "credit": "信用盘玩家(代理授信,流水来自信用账本)",
+ "credit": "信用盘:展示额度占用、释额与账期收付(非钱包派彩)",
"wallet": "主站钱包玩家(划转与钱包余额流水)"
},
"creditAvailableAfter": "变更后可用信用",
+ "creditPaymentRecordOnly": "账期收付记账,不计入可用信用",
"totalRecords": "共 {{total}} 条记录",
"emptyLogs": "暂无流水",
+ "emptyCreditLogs": "暂无信用流水",
"noMoreLogs": "没有更多流水",
"balanceAfter": "变更后余额",
"wsBalanceUpdated": "余额 {{change}}({{reason}})",
@@ -420,10 +427,12 @@
},
"creditFlow": {
"all": "全部",
- "bet": "下注占用",
- "prize": "派彩",
- "refund": "释放额度",
- "reversal": "冲正"
+ "bet": "占用额度",
+ "credit_release": "释额",
+ "bill_settlement": "账期收付",
+ "win_credit": "中奖释额",
+ "refund": "账期确认释额",
+ "reversal": "退本释额"
},
"txnStatus": {
"posted": "成功",
@@ -498,6 +507,7 @@
"winAmount": "中奖金额 {{amount}}",
"jackpotAmount": "Jackpot {{amount}}",
"payoutTotal": "派彩合计 {{amount}}",
+ "creditWinTotal": "中奖合计 {{amount}}",
"matchLose": "匹配结果:未中奖",
"matchResult": "匹配结果",
"drawPendingMatch": "本期开奖号码尚未发布,暂不能判断是否中奖。",
@@ -528,9 +538,11 @@
"next": "下一期 ›",
"drawTime": "开奖时间: {{time}}",
"myPayout": "本期我的派彩",
+ "creditMyWin": "本期我的中奖",
"regular": "常规:{{amount}}",
"jackpot": "Jackpot:{{amount}}",
"hitPending": "您的注单已命中本期开奖号码中的格子;派彩完成后将显示金额汇总。",
+ "creditHitPending": "您的注单已命中本期开奖号码;结算完成后将显示金额,盈亏在账期统一处理。",
"hitHint": "如果您中奖,与注单匹配的号码将以金色高亮显示。",
"viewMyWinning": "查看我的中奖情况",
"jackpotLabel": "Jackpot",
@@ -645,6 +657,10 @@
"partial_failed": "部分失败",
"pending_payout": "已中奖待派彩",
"settled_win": "已派彩",
+ "credit": {
+ "pending_payout": "已中奖待结算",
+ "settled_win": "已中奖"
+ },
"settled_lose": "未中奖",
"failed": "失败",
"refunded": "已退款",
diff --git a/src/lib/lottery-http.ts b/src/lib/lottery-http.ts
index ee5194b..985a6d4 100644
--- a/src/lib/lottery-http.ts
+++ b/src/lib/lottery-http.ts
@@ -1,9 +1,11 @@
import axios, {
isAxiosError,
+ type AxiosError,
type AxiosRequestConfig,
type AxiosResponse,
} from "axios";
+import { isInIframe } from "@/components/iframe-bridge";
import { withPlayerAuthHeader } from "@/lib/lottery-auth";
import { usePlayerSessionStore } from "@/stores/player-session-store";
import { withLotteryLocaleHeaders } from "@/lib/lottery-locale";
@@ -14,6 +16,7 @@ import {
import { isApiEnvelope } from "@/types/api/envelope";
import { useErrorStore } from "@/stores/error-store";
import { resolveLotteryApiV1Base } from "@/lib/lottery-api-base";
+import i18n from "@/i18n";
/**
* **第一层**:`baseURL` 对齐 Laravel `api/v1`;各 `api/*.ts` 只写业务 path(如 `/currencies`)。
@@ -24,6 +27,34 @@ export const lotteryHttp = axios.create({
headers: { Accept: "application/json" },
});
+/** 凭据类 401(非 token 失效),不应触发「会话过期」跳转 */
+const PLAYER_CREDENTIAL_BIZ_CODES = new Set([8006]);
+
+function isPlayerAuthLoginRequest(config: AxiosRequestConfig | undefined): boolean {
+ if (!config || (config.method ?? "get").toLowerCase() !== "post") {
+ return false;
+ }
+ return `${config.url ?? ""}`.includes("/player/auth/login");
+}
+
+/** 是否应将 401 视为已登录态失效并跳转入口(见 {@link EntryGate} `session=expired`) */
+function shouldRedirectPlayerSessionExpired(error: AxiosError): boolean {
+ if (isPlayerAuthLoginRequest(error.config)) {
+ return false;
+ }
+
+ if (typeof window !== "undefined" && window.location.pathname === "/login") {
+ return false;
+ }
+
+ const body = error.response?.data;
+ if (isApiEnvelope(body) && PLAYER_CREDENTIAL_BIZ_CODES.has(body.code)) {
+ return false;
+ }
+
+ return true;
+}
+
/** 站内接口 401:清本地会话并回入口,与 {@link EntryGate} `session=expired` 衔接 */
/** 500 错误:更新全局服务器错误状态 */
lotteryHttp.interceptors.response.use(
@@ -32,20 +63,28 @@ lotteryHttp.interceptors.response.use(
if (isAxiosError(error) && typeof window !== "undefined") {
const status = error.response?.status;
- // 401: 会话过期,清除令牌并重定向
+ // 401: 已登录态 token 失效 → 清会话并回入口;登录凭据错误等由页面自行提示
if (status === 401) {
- usePlayerSessionStore.getState().clearBearerToken();
- const onEntry = window.location.pathname === "/";
- const alreadyExpired = window.location.search.includes("session=expired");
- if (!onEntry || !alreadyExpired) {
- window.location.replace("/?session=expired");
+ const redirectSessionExpired = shouldRedirectPlayerSessionExpired(error);
+ if (redirectSessionExpired || window.location.pathname === "/login") {
+ usePlayerSessionStore.getState().clearBearerToken();
+ }
+ if (redirectSessionExpired) {
+ const pathname = window.location.pathname;
+ const alreadyExpired = window.location.search.includes("session=expired");
+ const expiredTarget = isInIframe() ? "/?session=expired" : "/login?session=expired";
+ const onExpiredPage =
+ (pathname === "/" || pathname === "/login") && alreadyExpired;
+ if (!onExpiredPage) {
+ window.location.replace(expiredTarget);
+ }
}
}
// 500/502/503: 服务器错误,更新全局错误状态
if (status === 500 || status === 502 || status === 503) {
const setServerError = useErrorStore.getState().setServerError;
- let message = "服务器暂时不可用,请稍后重试";
+ let message = i18n.t("serverError.serverMessage", { ns: "player" });
// 尝试从响应中获取更详细的错误信息
const responseData = error.response?.data;
diff --git a/src/lib/player-funding-mode.ts b/src/lib/player-funding-mode.ts
new file mode 100644
index 0000000..6c457c6
--- /dev/null
+++ b/src/lib/player-funding-mode.ts
@@ -0,0 +1,10 @@
+/** 信用盘玩家(代理授信);与主站钱包资金盘区分。 */
+export function isCreditFundingPlayer(
+ source?: { funding_mode?: string | null; credit_line_mode?: boolean | null } | null,
+): boolean {
+ if (!source) {
+ return false;
+ }
+
+ return source.credit_line_mode === true || source.funding_mode === "credit";
+}
diff --git a/src/types/api/wallet-logs.ts b/src/types/api/wallet-logs.ts
index 7370c67..c1e3601 100644
--- a/src/types/api/wallet-logs.ts
+++ b/src/types/api/wallet-logs.ts
@@ -12,7 +12,9 @@ export type WalletLogItem = {
amount_abs: number;
direction: "in" | "out";
currency_code: string;
- balance_after: number;
+ balance_after: number | null;
+ /** 信用盘:false 表示账期收付记账,不改变可用信用 */
+ affects_available_credit?: boolean;
ref_id: string | null;
idempotent_key: string | null;
external_ref_no: string | null;