fix: 金字塔活动未上报payment事件
This commit is contained in:
@@ -488,6 +488,8 @@ async function getIosOrder(openid, ctx) {
|
||||
let list = [];
|
||||
if (res.data.length > 0) {
|
||||
for (let i = 0; i < res.data.length; i++) {
|
||||
// 金字塔使用专用支付确认及订单级去重埋点,不走旧通用补单/普通 payment 上报。
|
||||
if (res.data[i].pyramid || res.data[i].activityType === "pyramid") continue;
|
||||
// 原生黄金矿工订单不走旧商户查单,也不能被三小时清单逻辑删除。
|
||||
if (res.data[i].goldMiner || String(res.data[i].itemid || "").startsWith("gold_miner")) continue;
|
||||
let ret = await getOrder(res.data[i].outTradeNo);
|
||||
|
||||
@@ -136,3 +136,12 @@
|
||||
前端领取成功后同步绝对库存、关闭补发弹窗、播放战令奖励动画。领取失败保留弹窗供重试;关闭弹窗不入账、不播放奖励动画、不消耗服务端清单,下次首页/前台检查仍可领取。动画只展示,不修改库存。
|
||||
|
||||
部署须同步更新 pyramid/api、客户端及金字塔分包,旧客户端的查询即入账协议不再使用。现有每分钟 jobs 触发器不变,无需新增云函数或集合。
|
||||
|
||||
|
||||
### 2026-10-13 payment 埋点补齐
|
||||
|
||||
金字塔在统一 fulfill 流程确认 paymentStatus=succeeded 且 firstVerifiedAt 有效后上报 payment,覆盖原生支付、iOS 客服支付、查询恢复和 jobs 发货恢复。按服务器订单填写金额(分)、商品、数量和已验证支付时间;沿用现有用户/AB 属性与 Android、ios、ios_old 渠道口径。测试环境订单进入测试数数项目。未支付订单不报;已付款但次数过期的订单仍报。
|
||||
|
||||
使用项目已有 trackFirst,firstCheckId=outTradeNo,重复请求由数数按订单去重;不新增云函数或集合。SDK 异常不阻断发货,后续回调/查单可再次尝试;没有新增独立的埋点补偿定时任务。首次事件可能延迟入库,不能以查单调用次数统计支付笔数。没有 distinctId 时记录警告并等待后续确认重试。
|
||||
|
||||
部署更新 pyramid/api 与 login。login 的旧商户补单跳过金字塔,避免通用普通 payment 上报与专用去重事件混用;金字塔仍由专用支付回调、客户端支付恢复及 jobs 处理。无需更新前端。本次不批量回填历史订单。
|
||||
|
||||
@@ -454,9 +454,47 @@ export async function createOrder(body: any, platform: "and" | "ios") {
|
||||
};
|
||||
} catch (error) { return failure(error); }
|
||||
}
|
||||
// All verified payment channels converge here. The analytics first-event key is
|
||||
// the order ID, so callback/query/job retries cannot count the same payment twice.
|
||||
async function reportPayment(order: any) {
|
||||
try {
|
||||
const user = await collection("users").findOne({ _id: order.pyramid.uid });
|
||||
if (!user?.distinctId || user.openid !== order.openid) {
|
||||
console.warn("[PyramidPayment] payment 上报缺少匹配的用户标识", order.outTradeNo.slice(-6));
|
||||
return;
|
||||
}
|
||||
let abTests = user.abTests;
|
||||
if (typeof abTests === "string") { try { abTests = JSON.parse(abTests); } catch { abTests = null; } }
|
||||
const group = (key: string) => ({ A: 1, B: 2, C: 3, D: 4, E: 5 }[String(abTests?.[key]?.group || "").toUpperCase()] || 0);
|
||||
const ThinkingData = require('thinkingdata-node');
|
||||
const appId = order.paymentAppEnv === "test" || user.isDebug === "true"
|
||||
? '95993f9ab6f1402a87abe5147827e5e0' : '40e3d5c5f2af49a4a074205564ce5dbb';
|
||||
const sdk = ThinkingData.initWithBatchMode(appId, 'https://data.nika4fun.com/sync_data', { dryRun: false, deviceId: '123456789' });
|
||||
try {
|
||||
sdk.trackFirst({
|
||||
accountId: order.openid, distinctId: user.distinctId, event: 'payment', firstCheckId: order.outTradeNo,
|
||||
time: new Date(order.pyramid.firstVerifiedAt + 8 * 3600000),
|
||||
properties: {
|
||||
order_id: order.outTradeNo, pay_amount: order.goodsPrice, payment_name: order.itemid, payment_num: order.itemCount,
|
||||
payment_type: order.pyramid.paymentChannel === "legacy_ios" ? "ios_old" : order.type === "ios" ? "ios" : "Android",
|
||||
is_on_table: Number(user.addLevel) >= 5, current_level: user.levelAmount, current_health: user.healthAmount,
|
||||
tmp_coin: user.coinAmount, version: "1.6", user_id: user._id, pay_user: user.pay_user, register_time: user.register_time,
|
||||
abTest_layer_one: group("layer_1"), abTest_layer_two: group("layer_2"), abTest_layer_three: group("layer_3")
|
||||
},
|
||||
callback(error: any) {
|
||||
if (error) console.error("[PyramidPayment] payment 上报失败,后续支付确认可重试", order.outTradeNo.slice(-6));
|
||||
}
|
||||
});
|
||||
} finally { sdk.close(); }
|
||||
} catch {
|
||||
// Analytics failure must never block payment acknowledgement or draw credits.
|
||||
console.error("[PyramidPayment] payment 上报失败,后续支付确认可重试", order.outTradeNo.slice(-6));
|
||||
}
|
||||
}
|
||||
export async function fulfill(orderId: string) {
|
||||
const order = await orders().findOne({ outTradeNo: orderId }), g = order?.pyramid;
|
||||
if (!g || g.paymentStatus !== "succeeded" || !g.firstVerifiedAt) fail("PAYMENT_PENDING");
|
||||
await reportPayment(order);
|
||||
let saved;
|
||||
try {
|
||||
saved = await mutate(g.uid, g.periodId, (p, now) => {
|
||||
@@ -661,9 +699,11 @@ export async function settleExpiredPeriod(uid: string, periodId: string) {
|
||||
export async function readSettlement(uid: string) {
|
||||
const rows = await collection(TABLES.players).find({ uid, endAt: { $lte: Date.now() }, "settlementNotice.readAt": null })
|
||||
.sort({ endAt: 1 }).toArray();
|
||||
return { status: rows.some((p: any) => p.settlementPreparedAt == null) ? "settling" : "ready",
|
||||
return {
|
||||
status: rows.some((p: any) => p.settlementPreparedAt == null) ? "settling" : "ready",
|
||||
notices: rows.filter((p: any) => p.settlementNotice && p.settlementPreparedAt != null).map((p: any) => clone(p.settlementNotice)),
|
||||
serverTime: Date.now() };
|
||||
serverTime: Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
/** Only an explicit claim deposits this player's notice. Retries return the same receipt. */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user