MatchMaster/assets/action_bundle/script/NewbieGift.ts
2026-09-30 21:49:05 +08:00

312 lines
14 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, starterPackReward } 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 = "";
private orderContext: any = null;
private offer: any = null;
init(first: boolean) {
this.node.opacity = 255;
this.offer = cc.fx.GameConfig.GM_INFO.starterPackOffer;
this.refreshOffer();
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();
}
private refreshOffer() {
const price = Number(this.offer?.priceFen) || 300;
for (const value of [3, 6, 12, 18, 30]) cc.find("btnContainer/" + value, this.node).active = value * 100 === price;
const reward = starterPackReward(this.offer?.productId);
const bundle = cc.assetManager.getBundle("action_bundle");
bundle.load("img/newbieGift/newbieGiftFont", cc.SpriteAtlas, (error, atlas: cc.SpriteAtlas) => {
if (error || !cc.isValid(this)) return;
for (const [path, amount] of [["daoju1/chuiziCount", reward.hammer], ["daoju2/chuiziCount", reward.freeze], ["daoju3/chuiziCount", reward.magic_wand]]) {
cc.find(String(path), this.node).getComponent(cc.Sprite).spriteFrame = atlas.getSpriteFrame(String(amount));
}
const minutes = String(reward.infinite_health / 60);
const first = cc.find("tiliContainer/tiliCount1", this.node), second = cc.find("tiliContainer/tiliCount2", this.node);
first.getComponent(cc.Sprite).spriteFrame = atlas.getSpriteFrame(minutes[minutes.length - 2]);
second.getComponent(cc.Sprite).spriteFrame = atlas.getSpriteFrame(minutes[minutes.length - 1]);
let hundreds = first.parent.getChildByName("tiliHundreds");
if (minutes.length === 3 && !hundreds) {
hundreds = cc.instantiate(first);
hundreds.name = "tiliHundreds";
hundreds.x = first.x - (second.x - first.x);
first.parent.addChild(hundreds);
}
if (hundreds) {
hundreds.active = minutes.length === 3;
hundreds.getComponent(cc.Sprite).spriteFrame = atlas.getSpriteFrame(minutes[0]);
}
});
cc.assetManager.getBundle("publicUI").load("font", cc.SpriteAtlas, (error, atlas: cc.SpriteAtlas) => {
if (error || !cc.isValid(this)) return;
const digits = String(reward.coin);
cc.find("goldContainer", this.node).children.filter(node => node.name === "New Sprite").forEach((node, index) => {
node.getComponent(cc.Sprite).spriteFrame = atlas.getSpriteFrame("scoin_" + digits[index]);
});
});
}
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 (data?.data && /^starter_pack(?:_[2-5])?$/.test(data.data.itemid || data.data.payment_name || "")) {
this.orderContext = data.data;
}
if (directPayment && data?.code === 1 && data.data?.pay_state === 2) {
this.confirmedOrder = order;
this.finishOrder(order);
} else if (!directPayment && data?.code === 1 && /^starter_pack(?:_[2-5])?$/.test(data.data?.payment_name)) {
// 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;
}
const productId = this.offer?.productId || "starter_pack";
const price = this.offer?.priceFen || 300;
Utils.buyProp(productId, 1, price, systemType, productId, 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();
}, undefined, this.offer?.round);
});
}
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;
}
if (res.starterPackOffer) this.orderContext = res;
this.completeOrder(order);
}, order);
}
private completeOrder(order: string, grant = true) {
if (grant) {
this.logPayment("开始前端发奖", order);
// The legacy acknowledgement returns data: "ok", not order metadata.
const productId = this.orderContext?.itemid || this.orderContext?.payment_name || "starter_pack";
cc.fx.GameTool.shopBuy(productId, false, { ...this.orderContext, outTradeNo: order });
this.logPayment("前端发奖调用完成", order);
MiniGameSdk.API.yinli_Pay(this.orderContext?.starterPackOffer?.priceFen || 300, order, "购买金币道具:" + productId);
MiniGameSdk.API.showToast("充值成功");
}
this.pendingOrder = "";
this.confirmedOrder = "";
cc.fx.GameConfig.GM_INFO.iosStarterOrder = "";
const info = cc.fx.GameConfig.GM_INFO;
if ((Number(this.orderContext?.starterPackBuyRound) || 1) === (info.starterPackRound || 1)) info.starterPackPurchased = true;
this.orderContext = null;
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();
const shopNode = canvas && canvas.getChildByName("shop");
const shop = shopNode && shopNode.activeInHierarchy && shopNode.getComponent("shop");
if (shop) shop.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: this.offer?.priceFen || 300, payment_name: this.offer?.productId || "starter_pack", payment_num: 1,
fail_reason: message,
});
}
iosOldPay() {
Utils.GoKEFu({ price: this.offer?.priceFen || 300, payment_name: this.offer?.productId || "starter_pack", payment_count: 1,
starterPackRound: this.offer?.round }, 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();
}
}