@maoyugames/phaser-framework 1.0.12 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,24 +10,50 @@ function requireTT(capability) {
10
10
  if (!hasTT()) throw new PlatformUnsupportedError("tiktok", capability);
11
11
  }
12
12
  var TikTokAuth = class {
13
- // TikTok H5 SDK 实测(2026-05-28 反编译 connect.tiktok-minis.com/game/sdk.js)
14
- // **没有 game.login 也没有 game.getUserInfo**——这两个 API 是抖音(国内)小游戏的协议,
15
- // TikTok 海外走 OAuth code 流程:authorizeOpenContext({get_status_only:false}) 弹授权页拿 code,
16
- // 后端用 code + client_secret 调 open.tiktokapis.com/v2/oauth/token/ + /v2/user/info/ 拿 user info
17
- async login() {
13
+ // 登录用 game.login(静默登录,2026-05-29 核对 SDK + 官方接入指南 §2 确认):它是 SDK 的
14
+ // class method,返回 authResponse.code,后端用此 code /v2/oauth/token/ 换 access_token+open_id。
15
+ // 早先误用 authorizeOpenContext 的 code(非标准 OAuth code),后端换 token 报 invalid_request:
16
+ // malformed,导致登录全废 一切需鉴权功能(购买/排行榜/同步)401。已纠正为 game.login
17
+ // game.login 是 callback 式({success,fail,complete}),这里包成 Promise。
18
+ login() {
18
19
  requireTT("auth.login");
19
- let res = await TTMinis.game.authorizeOpenContext({ get_status_only: true });
20
- if (!res?.is_success || !res?.code) {
21
- res = await TTMinis.game.authorizeOpenContext({ get_status_only: false });
22
- }
23
- if (!res?.is_success || !res?.code) {
24
- const e = res?.error;
25
- throw new Error(`TikTok authorize failed: ${e?.error_msg ?? "unknown"} (code=${e?.error_code ?? "NA"})`);
26
- }
27
- return { code: res.code, raw: res };
20
+ return new Promise((resolve, reject) => {
21
+ let settled = false;
22
+ const timer = setTimeout(() => {
23
+ if (settled) return;
24
+ settled = true;
25
+ reject(new Error("TikTok login timeout (no callback in 10s)"));
26
+ }, 1e4);
27
+ const finish = (fn) => {
28
+ if (settled) return;
29
+ settled = true;
30
+ clearTimeout(timer);
31
+ fn();
32
+ };
33
+ const onResult = (res) => {
34
+ const code = res?.code ?? res?.authResponse?.code ?? "";
35
+ finish(
36
+ () => code ? resolve({ code, raw: res }) : reject(new Error(`TikTok login failed: ${JSON.stringify(res?.error ?? res ?? "no code")}`))
37
+ );
38
+ };
39
+ try {
40
+ TTMinis.game.login({
41
+ success: onResult,
42
+ fail: (err) => finish(() => reject(new Error(`TikTok login fail: ${err?.errMsg ?? JSON.stringify(err)}`)))
43
+ });
44
+ } catch (e) {
45
+ try {
46
+ TTMinis.game.login(
47
+ (r) => onResult(r)
48
+ );
49
+ } catch {
50
+ finish(() => reject(e));
51
+ }
52
+ }
53
+ });
28
54
  }
29
- // TikTok H5 SDK 没有客户端 user info API。nickname/avatar 必须由后端用 access_token 调
30
- // /v2/user/info/ 取(scope=user.info.basic),客户端拿到的 user 已含这些字段。
55
+ // TikTok H5 SDK 没有客户端 user info API。nickname/avatar 由后端用 access_token 调
56
+ // /v2/user/info/ 取(需 explicit authorize 的 user.info.basic scope),客户端拿到的 user 已含。
31
57
  async getProfile() {
32
58
  return null;
33
59
  }
@@ -100,8 +126,48 @@ var TikTokAds = class {
100
126
  onError(e?.message ?? "show rejected");
101
127
  });
102
128
  }
103
- async showInterstitial(_adUnitId) {
104
- return "failed";
129
+ // 插页广告:官方文档 createInterstitialAd 自 SDK 0.3.0;当前反编译版 sdk.js 未见此 API,
130
+ // 真机 SDK 版本可能更高。typeof 检测:SDK 有就用(create → onClose/onError → show),没有降级 failed
131
+ showInterstitial(adUnitId) {
132
+ requireTT("ads.showInterstitial");
133
+ const create = TTMinis.game.createInterstitialAd;
134
+ if (typeof create !== "function") return Promise.resolve("failed");
135
+ return new Promise((resolve) => {
136
+ let settled = false;
137
+ const done = (r) => {
138
+ if (!settled) {
139
+ settled = true;
140
+ resolve(r);
141
+ }
142
+ };
143
+ try {
144
+ const ad = create.call(TTMinis.game, { adUnitId });
145
+ if (!ad || typeof ad.show !== "function") {
146
+ done("failed");
147
+ return;
148
+ }
149
+ try {
150
+ ad.onClose(() => done("closed"));
151
+ } catch {
152
+ }
153
+ if (typeof ad.onError === "function") {
154
+ try {
155
+ ad.onError((res) => {
156
+ console.warn("[ads.showInterstitial] error:", res?.error?.error_msg);
157
+ done("failed");
158
+ });
159
+ } catch {
160
+ }
161
+ }
162
+ void ad.show().catch((e) => {
163
+ console.warn("[ads.showInterstitial] show rejected:", e?.message);
164
+ done("failed");
165
+ });
166
+ } catch (e) {
167
+ console.warn("[ads.showInterstitial] create threw:", e?.message);
168
+ done("failed");
169
+ }
170
+ });
105
171
  }
106
172
  };
107
173
  var TikTokShortcut = class {
@@ -190,27 +256,40 @@ var TikTokMission = class {
190
256
  }
191
257
  };
192
258
  var TikTokPayment = class {
193
- async purchase(productId, extra) {
259
+ // TikTok IAP(2026-05-29 飞书接入指南):后端预下单拿 trade_order_id,前端 game.pay 拉起收银台。
260
+ // purchase 的 productId 参数在 TikTok 下被用作 trade_order_id(业务后端已下单);也兼容从
261
+ // extra.orderId / extra.tradeOrderId 取(ShopModule 传的是 {orderId: trade_order_id})。
262
+ // resolve 仅表示「客户端支付面板流程结束」,真正发货由 webhook + 业务层轮询后端订单状态确认。
263
+ purchase(productId, extra) {
194
264
  requireTT("payment.purchase");
195
- const res = await TTMinis.game.requestPayment({ productId, ...extra ?? {} });
196
- return {
197
- productId: res.productId ?? productId,
198
- transactionId: res.transactionId ?? "",
199
- receipt: res.receipt ?? "",
200
- raw: res
201
- };
265
+ const tradeOrderId = extra?.orderId ?? extra?.tradeOrderId ?? productId;
266
+ return new Promise((resolve, reject) => {
267
+ let settled = false;
268
+ try {
269
+ TTMinis.game.pay({
270
+ trade_order_id: tradeOrderId,
271
+ success: () => {
272
+ if (settled) return;
273
+ settled = true;
274
+ resolve({ productId, transactionId: tradeOrderId, receipt: tradeOrderId, raw: { trade_order_id: tradeOrderId } });
275
+ },
276
+ fail: (err) => {
277
+ if (settled) return;
278
+ settled = true;
279
+ reject(new Error(`TikTok pay failed: ${err?.errMsg ?? "unknown"}`));
280
+ }
281
+ });
282
+ } catch (e) {
283
+ if (!settled) {
284
+ settled = true;
285
+ reject(e);
286
+ }
287
+ }
288
+ });
202
289
  }
290
+ // TikTok H5 SDK 未确认 getUnfinishedPurchases;掉单恢复靠后端订单状态查询 + webhook,这里返空。
203
291
  async restore() {
204
- requireTT("payment.restore");
205
- const fn = TTMinis.game.getUnfinishedPurchases;
206
- if (!fn) return [];
207
- const list = await fn.call(TTMinis.game) ?? [];
208
- return list.map((res) => ({
209
- productId: res.productId,
210
- transactionId: res.transactionId ?? "",
211
- receipt: res.receipt ?? "",
212
- raw: res
213
- }));
292
+ return [];
214
293
  }
215
294
  };
216
295
  var TikTokDevice = class {
@@ -241,12 +320,35 @@ var TikTokDevice = class {
241
320
  const res = await TTMinis.game.getClipboardData();
242
321
  return res?.data ?? "";
243
322
  }
244
- async share(payload) {
323
+ // shareAppMessage 是 callback 式(2026-05-30 反编译核对),不是 Promise;这里包成 Promise。
324
+ share(payload) {
245
325
  requireTT("device.share");
246
- await TTMinis.game.shareAppMessage({
247
- title: payload.title,
248
- imageUrl: payload.imageUrl,
249
- query: payload.query
326
+ return new Promise((resolve, reject) => {
327
+ let settled = false;
328
+ try {
329
+ TTMinis.game.shareAppMessage({
330
+ title: payload.title,
331
+ imageUrl: payload.imageUrl,
332
+ query: payload.query,
333
+ success: () => {
334
+ if (!settled) {
335
+ settled = true;
336
+ resolve();
337
+ }
338
+ },
339
+ fail: (err) => {
340
+ if (!settled) {
341
+ settled = true;
342
+ reject(new Error(`share failed: ${err?.errMsg ?? "unknown"}`));
343
+ }
344
+ }
345
+ });
346
+ } catch (e) {
347
+ if (!settled) {
348
+ settled = true;
349
+ reject(e);
350
+ }
351
+ }
250
352
  });
251
353
  }
252
354
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maoyugames/phaser-framework",
3
- "version": "1.0.12",
3
+ "version": "1.0.16",
4
4
  "description": "多平台 Phaser 游戏框架:业务/底层分离,HTTP/WebSocket/KCP,Web/TikTok/微信/Facebook/App 隔离打包",
5
5
  "type": "module",
6
6
  "license": "MIT",