Merge branch 'main' of http://47.107.44.202:3000/xionglijia/18xchat
This commit is contained in:
@@ -33,11 +33,10 @@ import { NetTimeData } from "../../chat18x/data/NetTimeData";
|
||||
import ConfigManager from "../../chat18x/manager/ConfigManager";
|
||||
import { AppConfig } from "db://assets/Scripts/chat18x/config/env/appConfig";
|
||||
import DeviceIdService from "db://assets/Scripts/chat18x/foundation/identity/DeviceIdService";
|
||||
import { AuthService } from "db://assets/Scripts/chat18x/network/services/AuthService";
|
||||
import { ApiCode } from "db://assets/Scripts/chat18x/network/client/types";
|
||||
|
||||
import MachineInfoService from "db://assets/Scripts/chat18x/foundation/identity/MachineInfoService";
|
||||
|
||||
import { AuthService } from "db://assets/Scripts/chat18x/network/services/AuthService";
|
||||
import { PlayerDataService } from "db://assets/Scripts/chat18x/network/services/PlayerDataService";
|
||||
import { ApiCode } from "db://assets/Scripts/chat18x/network/client/types";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -295,9 +294,26 @@ export class PreloadUI extends Component {
|
||||
netTimeData.timezoneOffset = resData.TimezoneOffset;
|
||||
|
||||
this.loginFinish = true;
|
||||
|
||||
// this.reqPlayerData();
|
||||
};
|
||||
}
|
||||
|
||||
// 请求玩家数据
|
||||
private async reqPlayerData() {
|
||||
const reqData = {
|
||||
GameName: ConfigManager.tables.TbGlobalConfig.GameName,
|
||||
IsAll: true,
|
||||
DataFlagList: [],
|
||||
};
|
||||
console.log("玩家数据请求数据:", reqData);
|
||||
let res = await PlayerDataService.I.reqQueryPlayerData(reqData);
|
||||
console.log("玩家数据响应数据:", res);
|
||||
if (res && res.code === ApiCode.OK) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
enterGame() {
|
||||
//let islogin = HttpUnit.IsLogin();
|
||||
//console.log("进入游戏,登录状态:", islogin);
|
||||
|
||||
@@ -13,12 +13,12 @@ export class AuthService {
|
||||
|
||||
// 登录
|
||||
public async login(req: proto.ProtoMsg.ILoginReq): Promise<ApiResponse<proto.ProtoMsg.ILoginRsp>> {
|
||||
let EP_LOGIN: Endpoint<proto.ProtoMsg.ILoginReq, proto.ProtoMsg.ILoginRsp> = {
|
||||
let epData: Endpoint<proto.ProtoMsg.ILoginReq, proto.ProtoMsg.ILoginRsp> = {
|
||||
path: "login",
|
||||
method: "POST",
|
||||
codec: "json",
|
||||
needsAuth: false
|
||||
};
|
||||
return this.api.call(EP_LOGIN, req);
|
||||
};
|
||||
return this.api.call(epData, req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { ApiClient } from "../client/ApiClient";
|
||||
import type { Endpoint } from "../client/endpoints";
|
||||
import type { ApiResponse } from "../client/types";
|
||||
import proto from 'db://assets/Scripts/proto/proto.pb.js';
|
||||
|
||||
export class PlayerDataService {
|
||||
private static _I: PlayerDataService | null = null;
|
||||
public static get I(): PlayerDataService {
|
||||
if (!PlayerDataService._I) PlayerDataService._I = new PlayerDataService();
|
||||
return PlayerDataService._I;
|
||||
}
|
||||
private constructor(private api = ApiClient.I) {}
|
||||
|
||||
// 拉取玩家数据
|
||||
public async reqQueryPlayerData(req: proto.ProtoMsg.IQueryPlayerDataReq): Promise<ApiResponse<proto.ProtoMsg.IQueryPlayerDataRsp>> {
|
||||
let epData: Endpoint<proto.ProtoMsg.IQueryPlayerDataReq, proto.ProtoMsg.IQueryPlayerDataRsp> = {
|
||||
path: "playerdata/query_player_data",
|
||||
method: "POST",
|
||||
codec: "json",
|
||||
needsAuth: true
|
||||
};
|
||||
return this.api.call(epData, req);
|
||||
}
|
||||
|
||||
// 保存玩家数据
|
||||
public async reqSavePlayerData(req: proto.ProtoMsg.ISavePlayerDataReq): Promise<ApiResponse<proto.ProtoMsg.ISavePlayerDataRsp>> {
|
||||
let epData: Endpoint<proto.ProtoMsg.ISavePlayerDataReq, proto.ProtoMsg.ISavePlayerDataRsp> = {
|
||||
path: "playerdata/save_player_data",
|
||||
method: "POST",
|
||||
codec: "json",
|
||||
needsAuth: true
|
||||
};
|
||||
return this.api.call(epData, req);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e651acda-1f16-4ceb-88e4-be892c13a248",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "3da5f7b4-77ad-4f88-959b-624499248ae0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 和服务器交互的API接口
|
||||
*
|
||||
*/
|
||||
import { ApiClient } from "../network/client/ApiClient";
|
||||
import type { Endpoint } from "../network/client/endpoints";
|
||||
import type { ApiResponse } from "../network/client/types";
|
||||
import { OrderStatus, CreateOrderReq, CreateOrderData, QueryOrderData } from "./types";
|
||||
import proto from 'db://assets/Scripts/proto/proto.pb.js';
|
||||
|
||||
export class PaymentApi {
|
||||
private static _I: PaymentApi | null = null;
|
||||
static get I() { return this._I ?? (this._I = new PaymentApi()); }
|
||||
private constructor(private api = ApiClient.I) {}
|
||||
|
||||
// 下单
|
||||
createOrder(req: CreateOrderReq): Promise<ApiResponse<CreateOrderData>> {
|
||||
let epData: Endpoint<CreateOrderReq, { orderId: string; payUrl: string; expireAt?: number; }> = {
|
||||
path: "pay/create",
|
||||
method: "POST",
|
||||
codec: "json",
|
||||
needsAuth: true,
|
||||
};
|
||||
return this.api.call(epData, req);
|
||||
}
|
||||
|
||||
// 查询订单
|
||||
queryOrder(orderId: string): Promise<ApiResponse<QueryOrderData>> {
|
||||
let epData: Endpoint<{ orderId: string }, { orderId: string; status: OrderStatus; paidAt?: number; failureReason?: string; }> = {
|
||||
path: "pay/query",
|
||||
method: "POST",
|
||||
codec: "json",
|
||||
needsAuth: true,
|
||||
};
|
||||
return this.api.call(epData, { orderId });
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
cancelOrder(orderId: string): Promise<ApiResponse<{ ok: boolean }>> {
|
||||
let epData: Endpoint<{ orderId: string }, { ok: boolean; }> = {
|
||||
path: "pay/cancel",
|
||||
method: "POST",
|
||||
codec: "json",
|
||||
needsAuth: true,
|
||||
};
|
||||
return this.api.call(epData, { orderId });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c4e3bad2-2588-427a-ba4f-9d839205c37d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 打开支付URL(系统浏览器 / 内嵌 WebView / 小游戏 API),可插拔
|
||||
*
|
||||
*/
|
||||
|
||||
import { sys } from "cc";
|
||||
|
||||
export type OpenStrategy = "system" | "inapp" | "minigame";
|
||||
|
||||
export interface IPaymentOpener {
|
||||
open(url: string): Promise<void>;
|
||||
canReturnSignal(): boolean; // 是否能“自动感知回到App”(inapp/minigame一般可以)
|
||||
}
|
||||
|
||||
/** 系统浏览器:使用 sys.openURL。无法自动知道用户何时完成,靠“回到前台”事件触发轮询 */
|
||||
class SystemBrowserOpener implements IPaymentOpener {
|
||||
async open(url: string) { sys.openURL(url); }
|
||||
canReturnSignal() { return false; }
|
||||
}
|
||||
|
||||
// 如需内嵌 WebView,可封装一个组件并在 open 里显示;此处给空壳
|
||||
class InAppWebViewOpener implements IPaymentOpener {
|
||||
async open(url: string) {
|
||||
// TODO: 实现你的 WebView 弹窗并加载 url;可拦截 redirect_uri 关闭弹窗
|
||||
sys.openURL(url); // 临时:用系统浏览器兜底
|
||||
}
|
||||
canReturnSignal() { return true; } // 拦截回跳时可立即开始轮询
|
||||
}
|
||||
|
||||
// 小游戏:使用平台API打开/内嵌webview,回跳后触发事件
|
||||
class MiniGameOpener implements IPaymentOpener {
|
||||
async open(url: string) {
|
||||
const g: any = globalThis as any;
|
||||
if (g.tt?.openAwemeUserProfile) {
|
||||
// 示例:按实际平台API替换
|
||||
g.tt.openSchema({ schema: url });
|
||||
} else if (g.wx?.openUrl) {
|
||||
g.wx.openUrl({ url });
|
||||
} else {
|
||||
sys.openURL(url);
|
||||
}
|
||||
}
|
||||
canReturnSignal() { return true; }
|
||||
}
|
||||
|
||||
export class PaymentOpenerFactory {
|
||||
static create(strategy: OpenStrategy): IPaymentOpener {
|
||||
switch (strategy) {
|
||||
case "inapp": return new InAppWebViewOpener();
|
||||
case "minigame": return new MiniGameOpener();
|
||||
default: return new SystemBrowserOpener();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "d1bfe39e-0c3f-4c88-ac5d-510ee01c9121",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 轮询器(指数退避+超时+可取消)
|
||||
*
|
||||
*/
|
||||
|
||||
import { PaymentApi } from "./PaymentApi";
|
||||
import type { QueryOrderData } from "./types";
|
||||
import { ApiCode } from "../network/client/types";
|
||||
|
||||
export interface PollOptions {
|
||||
maxDurationMs: number; // 轮询总时长(例如 2分钟)
|
||||
startIntervalMs: number; // 初始间隔(例如 2s)
|
||||
maxIntervalMs: number; // 最大间隔(例如 10s)
|
||||
jitter?: boolean; // 抖动
|
||||
}
|
||||
|
||||
export class PaymentPoller {
|
||||
private cancelled = false;
|
||||
|
||||
// 取消轮询
|
||||
cancel() { this.cancelled = true; }
|
||||
|
||||
// 轮询订单
|
||||
async poll(orderId: string, opt: PollOptions, onTick?: (d: QueryOrderData) => void): Promise<QueryOrderData | null> {
|
||||
const t0 = Date.now();
|
||||
let interval = opt.startIntervalMs;
|
||||
while (!this.cancelled) {
|
||||
// 查询
|
||||
const r = await PaymentApi.I.queryOrder(orderId);
|
||||
if (r.code === ApiCode.OK && r.data) {
|
||||
// 回调
|
||||
onTick?.(r.data);
|
||||
if (["SUCCESS", "FAILED", "CANCELED", "EXPIRED"].indexOf(r.data.status) !== -1) {
|
||||
return r.data;
|
||||
}
|
||||
}
|
||||
// 退出条件
|
||||
if (Date.now() - t0 > opt.maxDurationMs) return null;
|
||||
// 等待
|
||||
await this.sleep(this.jitter(interval, opt));
|
||||
// 每次轮询后,interval 会按照 1.5 倍增长,直到达到最大间隔 maxIntervalMs,避免在短时间内重复请求
|
||||
interval = Math.min(interval * 1.5, opt.maxIntervalMs);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 用来暂停一定的时间(ms 毫秒),并让轮询进入等待状态
|
||||
private sleep(ms: number) { return new Promise(res => setTimeout(res, ms)); }
|
||||
|
||||
// 用于实现抖动(随机化轮询间隔)
|
||||
private jitter(base: number, opt: PollOptions) {
|
||||
if (!opt.jitter) return base;
|
||||
const delta = Math.min(500, Math.max(100, base * 0.1));
|
||||
return base + (Math.random() * 2 - 1) * delta;
|
||||
}
|
||||
}
|
||||
export default PaymentPoller;
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a5127d19-3cee-4a5f-9ff9-d602dbb48f09",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* 支付对外的相关接口
|
||||
*
|
||||
*/
|
||||
import { EventTarget, game, Game } from "cc";
|
||||
import { PaymentApi } from "./PaymentApi";
|
||||
import { PaymentOpenerFactory, type OpenStrategy } from "./PaymentOpener";
|
||||
import { PendingOrderRepo } from "./PendingOrderRepo";
|
||||
import PaymentPoller from "./PaymentPoller";
|
||||
import type { CreateOrderReq, PaymentResult, PendingOrder, OrderStatus } from "./types";
|
||||
import { ApiCode } from "../network/client/types";
|
||||
|
||||
export const PaymentEvents = {
|
||||
Started: "pay-started", // { orderId }
|
||||
UrlOpened: "pay-url-opened",
|
||||
PollTick: "pay-poll-tick", // { orderId, status }
|
||||
Finished: "pay-finished", // PaymentResult
|
||||
};
|
||||
|
||||
export class PaymentService {
|
||||
private static _I: PaymentService | null = null;
|
||||
static get I() { return this._I ?? (this._I = new PaymentService()); }
|
||||
private constructor() {}
|
||||
|
||||
private bus = new EventTarget();
|
||||
|
||||
on(event: string, cb: (...args: any[]) => void, target?: any) {
|
||||
this.bus.on(event, cb, target);
|
||||
}
|
||||
off(event: string, cb: (...args: any[]) => void, target?: any) {
|
||||
this.bus.off(event, cb, target);
|
||||
}
|
||||
|
||||
/** 发起支付:创建订单→打开URL→回到App后开始轮询 */
|
||||
async startPayment(req: CreateOrderReq, open: OpenStrategy = "system"): Promise<PaymentResult> {
|
||||
// 下单
|
||||
const create = await PaymentApi.I.createOrder(req);
|
||||
if (create.code !== ApiCode.OK || !create.data) {
|
||||
return { orderId: "", status: "FAILED", message: create.msg || "create order failed" };
|
||||
}
|
||||
const { orderId, payUrl, expireAt } = create.data;
|
||||
this.bus.emit(PaymentEvents.Started, { orderId });
|
||||
|
||||
// 存为未决订单(用于异常恢复)
|
||||
PendingOrderRepo.instance.add({ orderId, channel: req.channel, createdAt: Date.now(), expireAt, productId: req.productId });
|
||||
|
||||
// 打开支付页
|
||||
await PaymentOpenerFactory.create(open).open(payUrl);
|
||||
this.bus.emit(PaymentEvents.UrlOpened, { orderId });
|
||||
|
||||
// 等待回到前台(系统浏览器策略下)
|
||||
// - 如果是 inapp/minigame,可在 WebView/回跳时立即开始轮询;
|
||||
// - 这里给一个通用的“等待前台”方法(可用 Cocos 的 onShow/onHide 自行接入)
|
||||
await this.waitAppResumeIfNeeded(open);
|
||||
|
||||
// 轮询
|
||||
const poller = new PaymentPoller();
|
||||
const data = await poller.poll(orderId, {
|
||||
maxDurationMs: 2 * 60 * 1000,
|
||||
startIntervalMs: 2000,
|
||||
maxIntervalMs: 10000,
|
||||
jitter: true,
|
||||
}, (d) => this.bus.emit(PaymentEvents.PollTick, { orderId, status: d.status }));
|
||||
|
||||
// 产出结果
|
||||
let res: PaymentResult;
|
||||
if (!data) {
|
||||
res = { orderId, status: "EXPIRED", message: "poll timeout" };
|
||||
} else if (data.status === "SUCCESS") {
|
||||
res = { orderId, status: "SUCCESS" };
|
||||
} else {
|
||||
res = { orderId, status: data.status as OrderStatus, message: data.failureReason };
|
||||
}
|
||||
|
||||
PendingOrderRepo.instance.remove(orderId);
|
||||
this.bus.emit(PaymentEvents.Finished, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** App 回到前台后恢复未完成订单的轮询(在 onShow 时调用) */
|
||||
async resumePending(): Promise<void> {
|
||||
const now = Date.now();
|
||||
const list = PendingOrderRepo.instance.all();
|
||||
|
||||
// 去重:同 orderId 只保留 createdAt 最新的一条
|
||||
const uniq = new Map<string, PendingOrder>();
|
||||
for (const o of list) {
|
||||
const ex = uniq.get(o.orderId);
|
||||
if (!ex || (o.createdAt ?? 0) > (ex.createdAt ?? 0)) {
|
||||
uniq.set(o.orderId, o);
|
||||
}
|
||||
}
|
||||
|
||||
for (const o of uniq.values()) {
|
||||
// 只处理未过期
|
||||
if (this.isOrderExpired(o, now)) {
|
||||
PendingOrderRepo.instance.remove(o.orderId);
|
||||
this.bus.emit(PaymentEvents.Finished, {
|
||||
orderId: o.orderId,
|
||||
status: "EXPIRED" as OrderStatus,
|
||||
message: "order expired",
|
||||
} as PaymentResult);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 轮询最新且未过期的订单
|
||||
const r = await this.startPollingOnly(o.orderId);
|
||||
if (r && r.status !== "PENDING" && r.status !== "CREATED") {
|
||||
PendingOrderRepo.instance.remove(o.orderId);
|
||||
this.bus.emit(PaymentEvents.Finished, {
|
||||
orderId: o.orderId,
|
||||
status: r.status as OrderStatus,
|
||||
message: r.failureReason,
|
||||
} as PaymentResult);
|
||||
}
|
||||
// r === null(轮询超时)时保留在仓库,等待下一次 resume 再查
|
||||
}
|
||||
}
|
||||
|
||||
/** 判断订单是否过期:有 expireAt 且 now >= expireAt 才认为过期 */
|
||||
private isOrderExpired(o: PendingOrder, now = Date.now()): boolean {
|
||||
return typeof o.expireAt === "number" && now >= o.expireAt;
|
||||
}
|
||||
|
||||
/** 仅轮询(用于 resumePending 或手动刷新) */
|
||||
private async startPollingOnly(orderId: string) {
|
||||
const poller = new PaymentPoller();
|
||||
return poller.poll(orderId, {
|
||||
maxDurationMs: 2 * 60 * 1000,
|
||||
startIntervalMs: 2000,
|
||||
maxIntervalMs: 8000,
|
||||
jitter: true,
|
||||
});
|
||||
}
|
||||
|
||||
/** 等待“回到前台”的占位实现:实际请在 App 生命周期里调用 resumePending() */
|
||||
private waitAppResumeIfNeeded(open: OpenStrategy): Promise<void> {
|
||||
if (open !== "system") {
|
||||
// 内嵌 WebView / 小游戏平台通常可以在回调里直接开始轮询
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
let finished = false;
|
||||
const finish = () => {
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
// 安全移除监听
|
||||
game.off(Game.EVENT_SHOW, onShow, this);
|
||||
resolve();
|
||||
};
|
||||
|
||||
const onShow = () => {
|
||||
// 应用回到前台
|
||||
finish();
|
||||
};
|
||||
|
||||
// 监听一次“回到前台”
|
||||
game.on(Game.EVENT_SHOW, onShow, this);
|
||||
|
||||
// 兜底:某些环境(桌面浏览器)可能不会触发隐藏/显示事件,避免一直卡住
|
||||
const FALLBACK_MS = 15000; // 按需调整
|
||||
setTimeout(finish, FALLBACK_MS);
|
||||
});
|
||||
}
|
||||
}
|
||||
export default PaymentService;
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "4474d3e2-964e-49b9-9793-7edab2f9d007",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* 本地持久化未决订单,断线/重启后可恢复
|
||||
*
|
||||
*/
|
||||
|
||||
import { sys } from "cc";
|
||||
import type { PendingOrder } from "./types";
|
||||
|
||||
export class PendingOrderRepo {
|
||||
private static _instance: PendingOrderRepo | null = null; // 私有静态实例
|
||||
private static KEY = "pay:pending_orders"; // 本地存储的 key
|
||||
private cache: PendingOrder[] | null = null; // 本地缓存
|
||||
|
||||
// 私有构造函数,禁止外部直接实例化
|
||||
private constructor() {}
|
||||
|
||||
// 获取唯一实例
|
||||
public static get instance(): PendingOrderRepo {
|
||||
if (!PendingOrderRepo._instance) {
|
||||
PendingOrderRepo._instance = new PendingOrderRepo();
|
||||
}
|
||||
return PendingOrderRepo._instance;
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
private load(): PendingOrder[] {
|
||||
if (this.cache) return this.cache; // 如果有缓存,直接返回
|
||||
const raw = sys.localStorage.getItem(PendingOrderRepo.KEY); // 从 localStorage 获取数据
|
||||
if (!raw) return (this.cache = []); // 如果没有数据,返回空数组
|
||||
try { return (this.cache = JSON.parse(raw) || []); } catch { return (this.cache = []); } // 解析失败时返回空数组
|
||||
}
|
||||
|
||||
// 保存数据
|
||||
private save(list: PendingOrder[]) {
|
||||
this.cache = list; // 更新缓存
|
||||
sys.localStorage.setItem(PendingOrderRepo.KEY, JSON.stringify(list)); // 存储到 localStorage
|
||||
}
|
||||
|
||||
// 添加未决订单
|
||||
add(o: PendingOrder) {
|
||||
const list = this.load();
|
||||
list.unshift(o); // 将新订单添加到数组前面
|
||||
this.save(list.slice(0, 10)); // 保存最多 10 条订单
|
||||
}
|
||||
|
||||
// 删除指定订单
|
||||
remove(orderId: string) {
|
||||
const list = this.load().filter(x => x.orderId !== orderId); // 过滤掉指定订单
|
||||
this.save(list); // 更新数据
|
||||
}
|
||||
|
||||
// 获取所有未决订单
|
||||
all(): PendingOrder[] {
|
||||
return this.load(); // 返回加载的数据
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "5165ac21-6ad4-444b-8575-47a851305a31",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 支付相关的类型定义
|
||||
*
|
||||
*/
|
||||
|
||||
export type PaymentChannel = "alipay" | "wxpay" | "stripe" | "paypal" | "url";
|
||||
|
||||
export type OrderStatus =
|
||||
| "CREATED" // 已创建,待支付
|
||||
| "PENDING" // 服务器处理中
|
||||
| "SUCCESS"
|
||||
| "FAILED"
|
||||
| "CANCELED"
|
||||
| "EXPIRED";
|
||||
|
||||
// 下单的请求数据
|
||||
export interface CreateOrderReq {
|
||||
productId: string;
|
||||
amount: number; // 分/元按后端定义
|
||||
currency: string; // "CNY" / "USD" ...
|
||||
channel: PaymentChannel; // “alipay/wxpay/stripe/url”等
|
||||
extra?: Record<string, any>; // 透传(区服、活动、角色信息等)
|
||||
}
|
||||
|
||||
export interface CreateOrderData {
|
||||
orderId: string;
|
||||
payUrl: string; // 这次集成的核心
|
||||
expireAt?: number; // ms
|
||||
}
|
||||
|
||||
// 查询订单的响应数据
|
||||
export interface QueryOrderData {
|
||||
orderId: string;
|
||||
status: OrderStatus;
|
||||
paidAt?: number; // ms
|
||||
failureReason?: string;
|
||||
}
|
||||
|
||||
export interface PaymentResult {
|
||||
orderId: string;
|
||||
status: OrderStatus; // SUCCESS / FAILED / CANCELED / EXPIRED
|
||||
message?: string; // 失败原因/提示
|
||||
}
|
||||
|
||||
export interface PendingOrder {
|
||||
orderId: string;
|
||||
channel: PaymentChannel;
|
||||
createdAt: number;
|
||||
expireAt?: number;
|
||||
productId?: string;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "5d3e814f-7ee9-463f-a568-c5f721f73498",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user