支付相关
This commit is contained in:
@@ -6,41 +6,75 @@ import { PaymentApi } from "./PaymentApi";
|
||||
import proto from 'db://assets/Scripts/proto/proto.pb.js';
|
||||
|
||||
export interface PollOptions {
|
||||
maxDurationMs: number; // 轮询总时长(例如 2分钟)
|
||||
startIntervalMs: number; // 初始间隔(例如 2s)
|
||||
maxIntervalMs: number; // 最大间隔(例如 10s)
|
||||
jitter?: boolean; // 抖动
|
||||
maxDurationMs: number; // 最大轮询时长(超过就自动退出)(例如 2分钟)
|
||||
startIntervalMs: number; // 第一次轮询等待的间隔(例如 2s)
|
||||
maxIntervalMs: number; // 最大允许的间隔,防止指数增长过大(例如 10s)
|
||||
jitter?: boolean; // 是否启用抖动(让等待时间带点随机性,避免所有客户端同一时间请求服务器)
|
||||
}
|
||||
|
||||
export class PaymentPoller {
|
||||
private cancelled = false;
|
||||
private currentPollCount = 0; // 轮询次数
|
||||
|
||||
// 取消轮询
|
||||
cancel() { this.cancelled = true; }
|
||||
cancel() {
|
||||
this.cancelled = true;
|
||||
}
|
||||
|
||||
// 轮询订单
|
||||
async poll(orderId: string, opt: PollOptions, onTick?: (d: proto.cs.ICSQueryOrderRes) => void): Promise<proto.cs.ICSQueryOrderRes | null> {
|
||||
// 获取当前轮询次数
|
||||
public getCurrentPollCount(): number {
|
||||
return this.currentPollCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 轮询订单
|
||||
* @param orderId 要查询的订单 ID
|
||||
* @param opt 轮询配置(间隔、最大时长、是否抖动)
|
||||
* @param onTick 可选回调函数,每次轮询到结果时调用
|
||||
* @returns 最终查询到的订单结果 ICSQueryOrderRes,或者 null(超时/被取消)
|
||||
*/
|
||||
async poll(orderId: string, opt: PollOptions, onTick?: (d: proto.cs.ICSQueryOrderRes) => void): Promise<proto.cs.ICSQueryOrderRes> {
|
||||
const t0 = Date.now();
|
||||
let interval = opt.startIntervalMs;
|
||||
while (!this.cancelled) {
|
||||
// 增加轮询次数
|
||||
this.currentPollCount++;
|
||||
// 查询
|
||||
let reqData = {orderId: orderId};
|
||||
console.log("第 ", this.currentPollCount, " 次轮询订单的请求数据:", reqData);
|
||||
const r = await PaymentApi.I.queryOrder(reqData);
|
||||
console.log("第 ", this.currentPollCount, " 次轮询订单的响应数据:", r);
|
||||
if (r.code === proto.cs.EnmRetCode.SUCCESS && r.data) {
|
||||
// 回调
|
||||
onTick?.(r.data);
|
||||
// 订单状态,-1:失败,0:成功,1:处理中
|
||||
if ([-1, 0].indexOf(r.data.status) !== -1) {
|
||||
return r.data;
|
||||
}
|
||||
}
|
||||
// 退出条件
|
||||
if (Date.now() - t0 > opt.maxDurationMs) return null;
|
||||
// 轮询超时
|
||||
if (Date.now() - t0 > opt.maxDurationMs) {
|
||||
let resultData: proto.cs.ICSQueryOrderRes = {
|
||||
status: 92, // 自定义的状态,代表客户端轮询超时
|
||||
retCode: 0,
|
||||
balance: 0,
|
||||
vipExpire: 0,
|
||||
}
|
||||
return resultData;
|
||||
}
|
||||
// 等待
|
||||
await this.sleep(this.jitter(interval, opt));
|
||||
// 每次轮询后,interval 会按照 1.5 倍增长,直到达到最大间隔 maxIntervalMs,避免在短时间内重复请求
|
||||
interval = Math.min(interval * 1.5, opt.maxIntervalMs);
|
||||
}
|
||||
return null;
|
||||
// 至此,代表取消了轮询订单
|
||||
let resultData: proto.cs.ICSQueryOrderRes = {
|
||||
status: 90, // 自定义的状态,代表取消
|
||||
retCode: 0,
|
||||
balance: 0,
|
||||
vipExpire: 0,
|
||||
}
|
||||
return resultData;
|
||||
}
|
||||
|
||||
// 用来暂停一定的时间(ms 毫秒),并让轮询进入等待状态
|
||||
|
||||
@@ -32,12 +32,12 @@ export class PaymentService {
|
||||
}
|
||||
|
||||
/** 发起支付:创建订单→打开URL→回到App后开始轮询 */
|
||||
async startPayment(req: proto.cs.ICSCreateOrderReq, open: OpenStrategy = "system"): Promise<proto.cs.ICSQueryOrderRes | null> {
|
||||
async startPayment(req: proto.cs.ICSCreateOrderReq, open: OpenStrategy = "system"): Promise<proto.cs.ICSQueryOrderRes> {
|
||||
// 下单
|
||||
const create = await PaymentApi.I.createOrder(req);
|
||||
console.log("创建订单的响应数据:", create);
|
||||
if (create.code !== proto.cs.EnmRetCode.SUCCESS || !create.data) {
|
||||
return { status: -1, retCode: -1 };
|
||||
return { status: -1, retCode: -1, balance: 0, vipExpire: 0};
|
||||
}
|
||||
const { orderId, payUrl } = create.data;
|
||||
this.bus.emit(PaymentEvents.Started, { orderId });
|
||||
@@ -108,6 +108,7 @@ export class PaymentService {
|
||||
return new Promise<void>((resolve) => {
|
||||
let finished = false;
|
||||
const finish = () => {
|
||||
console.log("应用回到 finish");
|
||||
if (finished) return;
|
||||
finished = true;
|
||||
// 安全移除监听
|
||||
@@ -117,6 +118,7 @@ export class PaymentService {
|
||||
|
||||
const onShow = () => {
|
||||
// 应用回到前台
|
||||
console.log("应用回到了前台");
|
||||
finish();
|
||||
};
|
||||
|
||||
|
||||
@@ -22,13 +22,15 @@ export function mapQueryOrderStatus(
|
||||
case 1: return "PENDING"; // 处理中
|
||||
case 0: return "SUCCESS"; // 成功
|
||||
case -1: return "FAILED"; // 失败
|
||||
case 90: return "CANCELED"; // 取消
|
||||
case 92: return "TIMEOUT"; // 轮询超时
|
||||
default: return "CREATED"; // 未知/初始,按“已创建”兜底
|
||||
}
|
||||
}
|
||||
|
||||
/** 是否为终态(无需再轮询) */
|
||||
export function isTerminalStatus(s: OrderStatus): boolean {
|
||||
return s === "SUCCESS" || s === "FAILED" || s === "CANCELED" || s === "EXPIRED";
|
||||
return s === "SUCCESS" || s === "FAILED" || s === "CANCELED" || s === "EXPIRED" || s === "TIMEOUT";
|
||||
}
|
||||
|
||||
/** 是否应继续轮询查询订单 */
|
||||
|
||||
@@ -9,6 +9,7 @@ export type OrderStatus =
|
||||
| "SUCCESS" // 成功
|
||||
| "FAILED" // 失败
|
||||
| "CANCELED" // 取消
|
||||
| "TIMEOUT" // 轮询超时
|
||||
| "EXPIRED"; // 过期
|
||||
|
||||
// 订单数据的数据结构
|
||||
|
||||
@@ -124,7 +124,7 @@ export class PurchasePanel extends li_BaseView {
|
||||
const isRechargeId = shopData.isRechargeId(id);
|
||||
if (isRechargeId) {
|
||||
// 需要外部支付进行购买
|
||||
this.startPayment(id);
|
||||
// this.startPayment(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,10 +186,19 @@ export class PurchasePanel extends li_BaseView {
|
||||
const reqData = {
|
||||
goodId
|
||||
};
|
||||
// let res = await PaymentService.I.startPayment(reqData);
|
||||
// if (res) {
|
||||
|
||||
// }
|
||||
let res = await PaymentService.I.startPayment(reqData);
|
||||
console.log("支付完成的响应数据:", res);
|
||||
if (res) {
|
||||
let status = res.status;
|
||||
if (status === -1) {
|
||||
console.log("支付失败!!!,错误码:", res.retCode);
|
||||
} else if (status === 0) {
|
||||
console.log("支付成功!!!");
|
||||
const walletData = DataManager.I.getDataById<WalletData>(DataId.Wallet);
|
||||
walletData.balance = Number(res.balance);
|
||||
walletData.vipExpire = Number(res.vipExpire);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
|
||||
Vendored
+17
-5
@@ -5802,11 +5802,23 @@ static getTypeUrl(typeUrlPrefix?: string): string;
|
||||
/** EnmRetCode enum. */
|
||||
enum EnmRetCode {
|
||||
SUCCESS = 0,
|
||||
GenJWTFailed = 5010,
|
||||
InvalidArg = 5013,
|
||||
MySQL = 5027,
|
||||
TokenExpire = 5046,
|
||||
TokenInvalid = 5047
|
||||
GenJWTFailed = 5000,
|
||||
Insufficient = 5001,
|
||||
InvalidArg = 5002,
|
||||
InvalidSign = 5003,
|
||||
JsonFormat = 5004,
|
||||
MySQL = 5005,
|
||||
NotRegist = 5006,
|
||||
NotResConfig = 5007,
|
||||
Rate_Limited = 5008,
|
||||
Redis = 5009,
|
||||
Request_Too_More = 5010,
|
||||
RMGOrder_Code = 5011,
|
||||
RMGOrder_Http = 5012,
|
||||
RMGOrder_MaxQuery = 5013,
|
||||
RMGOrder_NotFound = 5014,
|
||||
TokenExpire = 5015,
|
||||
TokenInvalid = 5016
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13854,20 +13854,44 @@ $root.cs = (function() {
|
||||
* @name cs.EnmRetCode
|
||||
* @enum {number}
|
||||
* @property {number} SUCCESS=0 SUCCESS value
|
||||
* @property {number} GenJWTFailed=5010 GenJWTFailed value
|
||||
* @property {number} InvalidArg=5013 InvalidArg value
|
||||
* @property {number} MySQL=5027 MySQL value
|
||||
* @property {number} TokenExpire=5046 TokenExpire value
|
||||
* @property {number} TokenInvalid=5047 TokenInvalid value
|
||||
* @property {number} GenJWTFailed=5000 GenJWTFailed value
|
||||
* @property {number} Insufficient=5001 Insufficient value
|
||||
* @property {number} InvalidArg=5002 InvalidArg value
|
||||
* @property {number} InvalidSign=5003 InvalidSign value
|
||||
* @property {number} JsonFormat=5004 JsonFormat value
|
||||
* @property {number} MySQL=5005 MySQL value
|
||||
* @property {number} NotRegist=5006 NotRegist value
|
||||
* @property {number} NotResConfig=5007 NotResConfig value
|
||||
* @property {number} Rate_Limited=5008 Rate_Limited value
|
||||
* @property {number} Redis=5009 Redis value
|
||||
* @property {number} Request_Too_More=5010 Request_Too_More value
|
||||
* @property {number} RMGOrder_Code=5011 RMGOrder_Code value
|
||||
* @property {number} RMGOrder_Http=5012 RMGOrder_Http value
|
||||
* @property {number} RMGOrder_MaxQuery=5013 RMGOrder_MaxQuery value
|
||||
* @property {number} RMGOrder_NotFound=5014 RMGOrder_NotFound value
|
||||
* @property {number} TokenExpire=5015 TokenExpire value
|
||||
* @property {number} TokenInvalid=5016 TokenInvalid value
|
||||
*/
|
||||
cs.EnmRetCode = (function() {
|
||||
var valuesById = {}, values = Object.create(valuesById);
|
||||
values[valuesById[0] = "SUCCESS"] = 0;
|
||||
values[valuesById[5010] = "GenJWTFailed"] = 5010;
|
||||
values[valuesById[5013] = "InvalidArg"] = 5013;
|
||||
values[valuesById[5027] = "MySQL"] = 5027;
|
||||
values[valuesById[5046] = "TokenExpire"] = 5046;
|
||||
values[valuesById[5047] = "TokenInvalid"] = 5047;
|
||||
values[valuesById[5000] = "GenJWTFailed"] = 5000;
|
||||
values[valuesById[5001] = "Insufficient"] = 5001;
|
||||
values[valuesById[5002] = "InvalidArg"] = 5002;
|
||||
values[valuesById[5003] = "InvalidSign"] = 5003;
|
||||
values[valuesById[5004] = "JsonFormat"] = 5004;
|
||||
values[valuesById[5005] = "MySQL"] = 5005;
|
||||
values[valuesById[5006] = "NotRegist"] = 5006;
|
||||
values[valuesById[5007] = "NotResConfig"] = 5007;
|
||||
values[valuesById[5008] = "Rate_Limited"] = 5008;
|
||||
values[valuesById[5009] = "Redis"] = 5009;
|
||||
values[valuesById[5010] = "Request_Too_More"] = 5010;
|
||||
values[valuesById[5011] = "RMGOrder_Code"] = 5011;
|
||||
values[valuesById[5012] = "RMGOrder_Http"] = 5012;
|
||||
values[valuesById[5013] = "RMGOrder_MaxQuery"] = 5013;
|
||||
values[valuesById[5014] = "RMGOrder_NotFound"] = 5014;
|
||||
values[valuesById[5015] = "TokenExpire"] = 5015;
|
||||
values[valuesById[5016] = "TokenInvalid"] = 5016;
|
||||
return values;
|
||||
})();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user