/** * 支付对外的相关接口 * */ import { EventTarget, game, Game } from "cc"; import { PaymentApi } from "./PaymentApi"; import { PaymentOpenerFactory, type OpenStrategy } from "./PaymentOpener"; import PaymentPoller from "./PaymentPoller"; import { PaymentFlow } from "./PaymentFlow"; import { CashInrFlow } from "./CashInrFlow"; import { WebUsdFlow } from "./WebUsdFlow"; import { LuffaPayFlow } from "./LuffaPayFlow"; import proto from 'db://assets/Scripts/proto/proto.pb.js'; import { DataManager, DataId } from "db://assets/Scripts/chat18x/data/DataManager"; import { OrderData } from "db://assets/Scripts/chat18x/data/OrderData"; import Utils from "../../Main/Common/Utils"; import { InnerMsgCode } from "../../Main/Config/InnerMsgCode"; import { logger } from "db://assets/Scripts/Main/Common/Logger"; import { PaymentMethod } from './types'; export class PaymentService { private static _I: PaymentService | null = null; static get I() { return this._I ?? (this._I = new PaymentService()); } /** 策略表 */ private flowMap: Map; private constructor() { this.flowMap = new Map(); this.flowMap.set(PaymentMethod.CashPay, new CashInrFlow(this.waitAppResumeIfNeeded.bind(this))); this.flowMap.set(PaymentMethod.Web3, new WebUsdFlow(5000)); this.flowMap.set(PaymentMethod.Luffa, new LuffaPayFlow(5000)); } /** 发起支付:创建订单→打开URL→回到App后开始轮询 */ async startPayment(payMethod: PaymentMethod, req: proto.cs.ICSCreateOrderReq, open: OpenStrategy = "system"): Promise { // 下单 logger.log("创建订单的请求数据:", req); const create = await PaymentApi.I.createOrder(req); logger.log("创建订单的响应数据:", create); if (create.code !== proto.cs.EnmRetCode.SUCCESS || !create.data) { return { status: -1, retCode: -1, balance: 0, vipExpire: 0}; } const { orderId, payUrl } = create.data; // 保存订单数据 const orderData = DataManager.I.getDataById(DataId.Order); orderData.saveOrderData(create.data); // 发出事件,支付订单创建成功 Utils.sendInnerMsg(InnerMsgCode.PayOrderCreateSucc, { orderId: orderId, payType: req.payType,goodId:req.goodId }); // 分发流程 const flow = this.flowMap.get(payMethod); return flow?.run(orderId, payUrl, open) ?? null; } /** App 回到前台后恢复未完成订单的轮询(在 onShow 时调用) */ async resumePending(): Promise { // 所有需要继续轮询查询的订单 id const orderData = DataManager.I.getDataById(DataId.Order); const idList = orderData.getAllShouldPollId(); for (const o of idList) { // 轮询最新且未过期的订单 const r = await this.startPollingOnly(o); if (r && r.status !== 1) { orderData.applyQueryOrder(o, r); } // r === null(轮询超时)时保留在仓库,等待下一次 resume 再查 } } /** 仅轮询(用于 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 { if (open !== "system") { // 内嵌 WebView / 小游戏平台通常可以在回调里直接开始轮询 return Promise.resolve(); } return new Promise((resolve) => { let finished = false; const finish = () => { logger.log("应用回到 finish"); if (finished) return; finished = true; // 安全移除监听 game.off(Game.EVENT_SHOW, onShow, this); resolve(); }; const onShow = () => { // 应用回到前台 logger.log("应用回到了前台"); finish(); }; // 监听一次“回到前台” game.on(Game.EVENT_SHOW, onShow, this); // 兜底:某些环境(桌面浏览器)可能不会触发隐藏/显示事件,避免一直卡住 const FALLBACK_MS = 25000; // 按需调整 // setTimeout(finish, FALLBACK_MS); }); } } export default PaymentService;