支付层
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
* 支付对外的相关接口
|
* 支付对外的相关接口
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
import { EventTarget } from "cc";
|
import { EventTarget, game, Game } from "cc";
|
||||||
import { PaymentApi } from "./PaymentApi";
|
import { PaymentApi } from "./PaymentApi";
|
||||||
import { PaymentOpenerFactory, type OpenStrategy } from "./PaymentOpener";
|
import { PaymentOpenerFactory, type OpenStrategy } from "./PaymentOpener";
|
||||||
import { PendingOrderRepo } from "./PendingOrderRepo";
|
import { PendingOrderRepo } from "./PendingOrderRepo";
|
||||||
@@ -79,21 +79,49 @@ export class PaymentService {
|
|||||||
|
|
||||||
/** App 回到前台后恢复未完成订单的轮询(在 onShow 时调用) */
|
/** App 回到前台后恢复未完成订单的轮询(在 onShow 时调用) */
|
||||||
async resumePending(): Promise<void> {
|
async resumePending(): Promise<void> {
|
||||||
const all = PendingOrderRepo.instance.all();
|
const now = Date.now();
|
||||||
for (const o of all) {
|
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);
|
const r = await this.startPollingOnly(o.orderId);
|
||||||
if (r && r.status !== "PENDING" && r.status !== "CREATED") {
|
if (r && r.status !== "PENDING" && r.status !== "CREATED") {
|
||||||
PendingOrderRepo.instance.remove(o.orderId);
|
PendingOrderRepo.instance.remove(o.orderId);
|
||||||
this.bus.emit(PaymentEvents.Finished, {
|
this.bus.emit(PaymentEvents.Finished, {
|
||||||
orderId: o.orderId,
|
orderId: o.orderId,
|
||||||
status: r.status as OrderStatus,
|
status: r.status as OrderStatus,
|
||||||
message: r.failureReason
|
message: r.failureReason,
|
||||||
} as PaymentResult);
|
} 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 或手动刷新) */
|
/** 仅轮询(用于 resumePending 或手动刷新) */
|
||||||
private async startPollingOnly(orderId: string) {
|
private async startPollingOnly(orderId: string) {
|
||||||
const poller = new PaymentPoller();
|
const poller = new PaymentPoller();
|
||||||
@@ -106,12 +134,34 @@ export class PaymentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 等待“回到前台”的占位实现:实际请在 App 生命周期里调用 resumePending() */
|
/** 等待“回到前台”的占位实现:实际请在 App 生命周期里调用 resumePending() */
|
||||||
private async waitAppResumeIfNeeded(open: OpenStrategy) {
|
private waitAppResumeIfNeeded(open: OpenStrategy): Promise<void> {
|
||||||
// 如果是系统浏览器,我们通常等“App 回到前台”再开始轮询;
|
if (open !== "system") {
|
||||||
// 这里简单 sleep 1s,实际项目请监听 Cocos 的 onShow/onHide 或平台回调。
|
// 内嵌 WebView / 小游戏平台通常可以在回调里直接开始轮询
|
||||||
if (open === "system") {
|
return Promise.resolve();
|
||||||
await new Promise(res => setTimeout(res, 1000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
export default PaymentService;
|
||||||
|
|||||||
Reference in New Issue
Block a user