133 lines
4.6 KiB
TypeScript
133 lines
4.6 KiB
TypeScript
/**
|
|
* 支付对外的相关接口
|
|
*
|
|
*/
|
|
import { EventTarget, game, Game } from "cc";
|
|
import { PaymentApi } from "./PaymentApi";
|
|
import { PaymentOpenerFactory, type OpenStrategy } from "./PaymentOpener";
|
|
import PaymentPoller from "./PaymentPoller";
|
|
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";
|
|
|
|
export const PaymentEvents = {
|
|
Started: "pay-started", // { orderId }
|
|
UrlOpened: "pay-url-opened",
|
|
PollTick: "pay-poll-tick", // { orderId, status }
|
|
Finished: "pay-finished",
|
|
};
|
|
|
|
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: proto.cs.ICSCreateOrderReq, open: OpenStrategy = "system"): Promise<proto.cs.ICSQueryOrderRes | null> {
|
|
// 下单
|
|
const create = await PaymentApi.I.createOrder(req);
|
|
console.log("创建订单的响应数据:", create);
|
|
if (create.code !== proto.cs.EnmRetCode.SUCCESS || !create.data) {
|
|
return { status: -1, retCode: -1 };
|
|
}
|
|
const { orderId, payUrl } = create.data;
|
|
this.bus.emit(PaymentEvents.Started, { orderId });
|
|
|
|
// 保存订单数据
|
|
const orderData = DataManager.I.getDataById<OrderData>(DataId.Order);
|
|
orderData.saveOrderData(create.data);
|
|
|
|
// 打开支付页
|
|
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 }));
|
|
|
|
// 刷新订单数据
|
|
orderData.applyQueryOrder(orderId, data);
|
|
// 返回结果
|
|
this.bus.emit(PaymentEvents.Finished, data);
|
|
return data;
|
|
}
|
|
|
|
/** App 回到前台后恢复未完成订单的轮询(在 onShow 时调用) */
|
|
async resumePending(): Promise<void> {
|
|
// 所有需要继续轮询查询的订单 id
|
|
const orderData = DataManager.I.getDataById<OrderData>(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);
|
|
this.bus.emit(PaymentEvents.Finished, 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<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;
|