MatchMaster/assets/action_bundle/script/NewbieGift.ts
2026-09-11 14:36:13 +08:00

261 lines
10 KiB
TypeScript

import JiaZai from "../../Script/JiaZai";
import LoadingCatAnimation from "../../Script/LoadingCatAnimation";
import Utils from "../../Script/module/Pay/Utils";
import { MiniGameSdk } from "../../Script/Sdk/MiniGameSdk";
import { applyStarterPackStatus, starterPackRemaining, starterPackTimeText } from "../../Script/module/Config/StarterPack";
const { ccclass, property } = cc._decorator;
declare const wx: any;
@ccclass
export default class NewbieGift extends cc.Component {
// Retain the unused serialized properties in the existing prefab.
@property(cc.Node) monthCardTime: cc.Node = null;
@property(cc.Node) monthCardBtn: cc.Node = null;
@property(cc.Node) monthCardBtn2: cc.Node = null;
btn_Touch = true;
private paying = false;
private polling = false;
private onShowListener: () => void = null;
private pendingOrder = "";
private confirmedOrder = "";
init(first: boolean) {
this.node.opacity = 255;
this.closeConfirmBox();
if (!this.paying) this.closeLoad();
const title = cc.find("ConfirmBox/title", this.node);
if (title) title.getComponent(cc.Label).string = "幸运礼包充值";
// The old Spine/propBg nodes stay disabled. Preserve the authored layout.
this.unschedule(this.refreshCountdown);
this.schedule(this.refreshCountdown, 1);
this.refreshCountdown();
if (typeof wx !== "undefined" && !this.onShowListener) {
this.onShowListener = () => this.onShow();
wx.onShow(this.onShowListener);
}
this.onShow();
cc.fx.GameTool.recordStarterPackShown();
}
refreshCountdown() {
if (!cc.isValid(this)) return;
const seconds = starterPackRemaining();
const label = cc.find("timeContainer/time", this.node).getComponent(cc.Label);
label.string = seconds > 0 ? starterPackTimeText(seconds) : "已结束";
this.btn_Touch = seconds > 0 && !this.paying;
cc.find("btnContainer/btn", this.node).getComponent(cc.Button).interactable = this.btn_Touch;
}
onShow() {
// wx.onShow and the payment callback may arrive together. Only one query
// chain owns the order; refreshing activity data is not reward delivery.
if (this.polling) return;
Utils.getStarter_pack(res => {
if (res && res.code === 1) applyStarterPackStatus(res.data);
this.refreshCountdown();
});
const order = this.pendingOrder || cc.fx.GameConfig.GM_INFO.iosStarterOrder;
if (!order) return;
this.polling = true;
this.paying = true;
this.openLoad();
// Retry only the delivery acknowledgement after payment was confirmed.
if (this.confirmedOrder === order) {
this.finishOrder(order);
return;
}
const directPayment = !!this.pendingOrder;
const version = cc.fx.GameTool.getWechatGameVersion();
this.logPayment("开始查单", order, {
channel: directPayment ? "直购" : "iOS客服", version,
server: version === "开发版" || version === "体验版" ? Utils.testHttpip : Utils.httpip,
});
const done = (data) => {
if (directPayment && data?.code === 1 && data.data?.pay_state === 2) {
this.confirmedOrder = order;
this.finishOrder(order);
} else if (!directPayment && data?.code === 1 && data.data?.payment_name === "starter_pack") {
// The original customer-service API already completes the order.
this.completeOrder(order);
} else if ((directPayment && data?.code === 1 && data.data?.pay_state === 1)
|| (!directPayment && data?.code === 0 && data.msg === "已经获取到奖励")) {
// A completed legacy order is not a new grant; clear stale UI state.
this.completeOrder(order, false);
} else {
this.logPayment("等待确认,保留订单供重试", order, {
code: data?.code, pay_state: data?.data?.pay_state, msg: data?.msg || data?.message,
});
this.polling = false;
this.paying = false;
this.closeLoad();
this.refreshCountdown();
// Keep the order for retry, including after the offer expires.
this.openConfirmBox();
}
};
if (this.pendingOrder) Utils.getPayInfo(done, order, {
timeoutMs: 30000,
onProgress: res => this.logPayment("查单返回", order, {
code: res?.code, pay_state: res?.data?.pay_state, msg: res?.msg || res?.message,
}),
});
else Utils.getIosPayInfo(order, done);
}
againGet() {
this.closeConfirmBox();
this.onShow();
}
buyProduct(event?: cc.Event, customData?: string) {
if (this.pendingOrder || cc.fx.GameConfig.GM_INFO.iosStarterOrder) {
this.onShow();
return;
}
this.refreshCountdown();
if (!this.btn_Touch) return;
this.paying = true;
this.refreshCountdown();
this.openLoad();
// Fetch the server deadline; purchase eligibility is enforced by this UI.
Utils.getStarter_pack(res => {
if (!res || res.code !== 1) {
this.failPayment("礼包状态获取失败,请重试");
return;
}
applyStarterPackStatus(res.data);
if (starterPackRemaining() <= 0) {
this.failPayment("礼包已结束或已购买");
return;
}
let systemType = "Android";
if (typeof wx !== "undefined" && wx.getSystemInfoSync().platform === "ios") systemType = "ios";
if (systemType === "ios" && !cc.fx.GameConfig.GM_INFO.iosCanPay) {
this.iosOldPay();
return;
}
Utils.buyProp("starter_pack", 1, 300, systemType, "starter_pack", result => {
if (!result || result.err) {
if (result && result.errCode === 16) {
this.iosOldPay();
return;
}
this.failPayment("充值失败");
return;
}
if (!result.outTradeNo) {
this.failPayment("支付订单号缺失,请重进游戏检查补单");
return;
}
this.pendingOrder = result.outTradeNo;
this.logPayment("微信支付回调成功,等待服务端确认", this.pendingOrder);
this.onShow();
});
});
}
private finishOrder(order: string) {
this.logPayment("支付已确认,开始领取确认", order);
this.paying = true;
this.openLoad();
Utils.setPayInfo(res => {
this.logPayment("领取确认返回", order, { code: res?.code, msg: res?.msg || res?.message });
this.polling = false;
if (!res || res.code !== 1) {
this.paying = false;
this.openConfirmBox();
this.refreshCountdown();
return;
}
this.completeOrder(order);
}, order);
}
private completeOrder(order: string, grant = true) {
if (grant) {
this.logPayment("开始前端发奖", order);
// The legacy acknowledgement returns data: "ok", not order metadata.
cc.fx.GameTool.shopBuy("starter_pack", false, { outTradeNo: order });
this.logPayment("前端发奖调用完成", order);
MiniGameSdk.API.yinli_Pay(300, order, "购买金币道具:starter_pack");
MiniGameSdk.API.showToast("充值成功");
}
this.pendingOrder = "";
this.confirmedOrder = "";
cc.fx.GameConfig.GM_INFO.iosStarterOrder = "";
cc.fx.GameConfig.GM_INFO.starterPackPurchased = true;
this.polling = false;
this.paying = false;
this.buyStarter_pack();
}
private logPayment(stage: string, order: string, details: any = {}) {
console.log("[新手礼包支付] " + stage, { orderSuffix: String(order).slice(-6), ...details });
}
buyStarter_pack() {
const canvas = cc.find("Canvas");
const home = canvas && canvas.getComponent(JiaZai);
if (home) {
home.newbieGift = false;
home.startStarter_pack();
home.updateCoin();
}
const scene = canvas && canvas.getComponent("SceneManager");
if (scene) scene.refreshJungleRewardDisplay();
this.closeLoad();
if (cc.isValid(this)) this.closeStarter_pack();
}
private failPayment(message: string) {
this.paying = false;
this.closeLoad();
this.refreshCountdown();
MiniGameSdk.API.showToast(message);
cc.fx.GameTool.shushu_Track("payment_fail", {
outTradeNo: this.pendingOrder || cc.fx.GameConfig.GM_INFO.iosStarterOrder || "",
pay_amount: 300, payment_name: "starter_pack", payment_num: 1,
fail_reason: message,
});
}
iosOldPay() {
Utils.GoKEFu({ price: 300, payment_name: "starter_pack", payment_count: 1 }, result => {
this.paying = false;
this.closeLoad();
this.refreshCountdown();
if (result !== "success") {
cc.fx.GameConfig.GM_INFO.iosStarterOrder = "";
MiniGameSdk.API.showToast("未能打开支付,请重试");
}
});
}
closeStarter_pack() {
this.node.active = false;
this.node.emit("starter-pack-closed");
}
openLoad() {
if (cc.isValid(this)) LoadingCatAnimation.play(this.node.getChildByName("Loading"));
}
closeLoad() {
if (cc.isValid(this)) LoadingCatAnimation.stop(this.node.getChildByName("Loading"));
}
openConfirmBox() {
this.closeLoad();
if (cc.isValid(this)) this.node.getChildByName("ConfirmBox").active = true;
}
closeConfirmBox() {
if (cc.isValid(this)) this.node.getChildByName("ConfirmBox").active = false;
}
onDisable() {
this.unschedule(this.refreshCountdown);
if (typeof wx !== "undefined" && this.onShowListener) wx.offShow(this.onShowListener);
this.onShowListener = null;
}
onDestroy() {
this.onDisable();
}
}