@looplay/sdk 0.6.0 → 0.8.1
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.
- package/README.md +221 -4
- package/dist/looplay-sdk-realtime.cjs.js +73 -0
- package/dist/looplay-sdk-realtime.cjs.js.map +1 -0
- package/dist/looplay-sdk-realtime.esm.js +71 -0
- package/dist/looplay-sdk-realtime.esm.js.map +1 -0
- package/dist/looplay-sdk-realtime.min.js +3 -0
- package/dist/looplay-sdk-realtime.min.js.map +1 -0
- package/dist/looplay-sdk.cjs.js +133 -162
- package/dist/looplay-sdk.cjs.js.map +1 -1
- package/dist/looplay-sdk.esm.js +134 -162
- package/dist/looplay-sdk.esm.js.map +1 -1
- package/dist/looplay-sdk.min.js +2 -2
- package/dist/looplay-sdk.min.js.map +1 -1
- package/dist/types/apps/LooplaySDK.types.d.ts +54 -6
- package/dist/types/apps/api-client.d.ts +22 -16
- package/dist/types/apps/service-client.d.ts +33 -22
- package/dist/types/apps/ws-client.d.ts +35 -0
- package/dist/types/errors.d.ts +0 -3
- package/dist/types/realtime.d.ts +9 -0
- package/dist/types/types.d.ts +2 -1
- package/package.json +17 -1
package/dist/looplay-sdk.cjs.js
CHANGED
|
@@ -31,12 +31,6 @@ var NotAuthenticatedError = class extends LooplaySDKError {
|
|
|
31
31
|
this.name = "NotAuthenticatedError";
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
-
var MissingStoreError = class extends LooplaySDKError {
|
|
35
|
-
constructor(gameId) {
|
|
36
|
-
super(`Game ${gameId} has no storefront configured.`);
|
|
37
|
-
this.name = "MissingStoreError";
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
34
|
|
|
41
35
|
// src/auth/storage.ts
|
|
42
36
|
var MemoryAuthStorage = class {
|
|
@@ -252,54 +246,49 @@ var ServiceClient = class {
|
|
|
252
246
|
return this.http.request("POST", "/account/logout", { body });
|
|
253
247
|
}
|
|
254
248
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
255
|
-
// Games
|
|
249
|
+
// Games — one SDK integration = one game, resolved on every call from the
|
|
250
|
+
// public `x-game-key` header (the game's `appId`). There is no multi-game
|
|
251
|
+
// catalog browsing here (list/search/detail across games) and no `:id`/
|
|
252
|
+
// `storeId` path segments — that's the app-facing surface, out of scope
|
|
253
|
+
// for this per-game SDK. Never use the creator-secret tier
|
|
254
|
+
// (`x-creator-key`/`x-game-secret`) from client/browser code — that would
|
|
255
|
+
// leak the secret to every player.
|
|
256
256
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
257
|
-
async
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
async getGameDetail(gameId, bearerToken) {
|
|
261
|
-
return this.http.request("GET", `/sdk/games/${encodeURIComponent(gameId)}`, {
|
|
262
|
-
bearerToken
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
async listRecentPlayed(bearerToken, query) {
|
|
266
|
-
return this.http.request("GET", "/sdk/games/recent-play", { bearerToken, query });
|
|
267
|
-
}
|
|
268
|
-
async recordGameView(auth, gameId) {
|
|
269
|
-
await this.http.request("POST", `/sdk/games/${encodeURIComponent(gameId)}/views/record`, {
|
|
257
|
+
async recordGameView(gameKey, auth = {}) {
|
|
258
|
+
await this.http.request("POST", "/sdk/games/views/record", {
|
|
270
259
|
...auth,
|
|
271
|
-
headers: this.trackingHeaders(auth)
|
|
260
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) }
|
|
272
261
|
});
|
|
273
262
|
return true;
|
|
274
263
|
}
|
|
275
264
|
/** Signs a fresh `attemptToken` — required by `completeGameplayAttempt`/`completeGameplayMatch`/`recordGameplayAction`. */
|
|
276
|
-
async startGameplayAttempt(
|
|
277
|
-
return this.http.request("POST",
|
|
265
|
+
async startGameplayAttempt(gameKey, auth = {}, body = {}) {
|
|
266
|
+
return this.http.request("POST", "/sdk/games/attempts/start", {
|
|
278
267
|
...auth,
|
|
279
|
-
headers: this.trackingHeaders(auth),
|
|
268
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
280
269
|
body
|
|
281
270
|
});
|
|
282
271
|
}
|
|
283
|
-
async completeGameplayAttempt(
|
|
284
|
-
await this.http.request("POST",
|
|
272
|
+
async completeGameplayAttempt(gameKey, auth, body) {
|
|
273
|
+
await this.http.request("POST", "/sdk/games/attempts/complete", {
|
|
285
274
|
...auth,
|
|
286
|
-
headers: this.trackingHeaders(auth),
|
|
275
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
287
276
|
body
|
|
288
277
|
});
|
|
289
278
|
return true;
|
|
290
279
|
}
|
|
291
|
-
async completeGameplayMatch(
|
|
292
|
-
await this.http.request("POST",
|
|
280
|
+
async completeGameplayMatch(gameKey, auth, body) {
|
|
281
|
+
await this.http.request("POST", "/sdk/games/matches/complete", {
|
|
293
282
|
...auth,
|
|
294
|
-
headers: this.trackingHeaders(auth),
|
|
283
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
295
284
|
body
|
|
296
285
|
});
|
|
297
286
|
return true;
|
|
298
287
|
}
|
|
299
|
-
async recordGameplayAction(
|
|
300
|
-
return this.http.request("POST",
|
|
288
|
+
async recordGameplayAction(gameKey, auth, body) {
|
|
289
|
+
return this.http.request("POST", "/sdk/games/actions/record", {
|
|
301
290
|
...auth,
|
|
302
|
-
headers: this.trackingHeaders(auth),
|
|
291
|
+
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
303
292
|
body
|
|
304
293
|
});
|
|
305
294
|
}
|
|
@@ -319,104 +308,90 @@ var ServiceClient = class {
|
|
|
319
308
|
"x-looplay-anon-token": auth.anonTrackingToken
|
|
320
309
|
};
|
|
321
310
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
328
|
-
async getStoreByStoreId(storeId) {
|
|
329
|
-
return this.http.request("GET", `/sdk/games/store/${encodeURIComponent(storeId)}`);
|
|
311
|
+
/** Public storefront (sections + offers) for the integration's game. Read-only. */
|
|
312
|
+
async getStore(gameKey) {
|
|
313
|
+
return this.http.request("GET", "/sdk/games/store/current", {
|
|
314
|
+
headers: { "x-game-key": gameKey }
|
|
315
|
+
});
|
|
330
316
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
317
|
+
/** Public catalog of purchasable/ownable assets for the game. Read-only. */
|
|
318
|
+
async listGameAssets(gameKey) {
|
|
319
|
+
return this.http.request("GET", "/sdk/games/store/assets", {
|
|
320
|
+
headers: { "x-game-key": gameKey }
|
|
334
321
|
});
|
|
335
322
|
}
|
|
336
|
-
|
|
337
|
-
|
|
323
|
+
/** Assets the authenticated player already owns for this game. */
|
|
324
|
+
async listMyGameAssets(bearerToken, gameKey) {
|
|
325
|
+
return this.http.request("GET", "/sdk/games/store/assets/me", {
|
|
338
326
|
bearerToken,
|
|
339
|
-
headers: { "x-game-
|
|
340
|
-
body
|
|
327
|
+
headers: { "x-game-key": gameKey }
|
|
341
328
|
});
|
|
342
329
|
}
|
|
343
|
-
|
|
344
|
-
|
|
330
|
+
/** The authenticated player's past store purchases for this game. */
|
|
331
|
+
async listMyStorePurchases(bearerToken, gameKey) {
|
|
332
|
+
return this.http.request("GET", "/sdk/games/store/purchases/me", {
|
|
345
333
|
bearerToken,
|
|
346
|
-
headers: { "x-game-
|
|
347
|
-
body
|
|
334
|
+
headers: { "x-game-key": gameKey }
|
|
348
335
|
});
|
|
349
336
|
}
|
|
350
337
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
351
|
-
//
|
|
352
|
-
//
|
|
353
|
-
//
|
|
354
|
-
//
|
|
355
|
-
//
|
|
338
|
+
// Self-serve purchase (public tier) — same trust level as tracking: public
|
|
339
|
+
// `x-game-key` + the purchasing player's own bearer token, no backend or
|
|
340
|
+
// secret pair required. Safe to call directly from client/browser code —
|
|
341
|
+
// `checkoutToken` (short-lived, server-signed) plays the same role as
|
|
342
|
+
// `attemptToken` on the tracking routes. `ApiClient.purchaseStoreOffer`
|
|
343
|
+
// wraps this pair for you.
|
|
356
344
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return true;
|
|
363
|
-
}
|
|
364
|
-
async startGameplayAttemptForGameKey(gameKey, auth = {}, body = {}) {
|
|
365
|
-
return this.http.request("POST", "/sdk/games/runtime/attempts/start", {
|
|
366
|
-
...auth,
|
|
367
|
-
headers: { "x-game-key": gameKey, ...this.trackingHeaders(auth) },
|
|
345
|
+
/** Signs a `checkoutToken` for one purchase — required by `purchaseMyStoreOffer`. */
|
|
346
|
+
async createMyStoreCheckoutIntent(gameKey, bearerToken, body, trackingSessionId) {
|
|
347
|
+
return this.http.request("POST", "/sdk/games/store/checkout-intent/me", {
|
|
348
|
+
bearerToken,
|
|
349
|
+
headers: { "x-game-key": gameKey, "x-game-tracking-session-id": trackingSessionId },
|
|
368
350
|
body
|
|
369
351
|
});
|
|
370
352
|
}
|
|
371
|
-
async
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
headers: { "x-game-key": gameKey,
|
|
353
|
+
async purchaseMyStoreOffer(gameKey, bearerToken, body, trackingSessionId) {
|
|
354
|
+
return this.http.request("POST", "/sdk/games/store/purchase/me", {
|
|
355
|
+
bearerToken,
|
|
356
|
+
headers: { "x-game-key": gameKey, "x-game-tracking-session-id": trackingSessionId },
|
|
375
357
|
body
|
|
376
358
|
});
|
|
377
|
-
return true;
|
|
378
359
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
360
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
361
|
+
// Server-to-server (secret pair) — moves real coin balance. Requires the
|
|
362
|
+
// creator+game *secret* pair (`x-creator-key`/`x-game-secret`), distinct
|
|
363
|
+
// from the public `x-game-key` used everywhere above. Call these ONLY
|
|
364
|
+
// from your own backend (e.g. a Node service holding the secret pair) —
|
|
365
|
+
// never from client/browser code, or the secret is exposed to every
|
|
366
|
+
// player. `ApiClient` deliberately does not expose these — it auto-injects
|
|
367
|
+
// a *player's* access token for browser-facing calls and has no concept
|
|
368
|
+
// of your creator secret. Prefer the self-serve tier above unless you
|
|
369
|
+
// specifically need to initiate a purchase from your backend without the
|
|
370
|
+
// player present.
|
|
371
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
372
|
+
/** Signs a `checkoutToken` for one purchase — required by `purchaseGameStoreOffer`. */
|
|
373
|
+
async createGameStoreCheckoutIntent(auth, body) {
|
|
374
|
+
return this.http.request("POST", "/sdk/games/store/checkout-intent", {
|
|
375
|
+
bearerToken: auth.bearerToken,
|
|
376
|
+
headers: {
|
|
377
|
+
"x-creator-key": auth.creatorKey,
|
|
378
|
+
"x-game-secret": auth.gameSecret,
|
|
379
|
+
"x-game-tracking-session-id": auth.trackingSessionId
|
|
380
|
+
},
|
|
383
381
|
body
|
|
384
382
|
});
|
|
385
|
-
return true;
|
|
386
383
|
}
|
|
387
|
-
async
|
|
388
|
-
return this.http.request("POST", "/sdk/games/
|
|
389
|
-
|
|
390
|
-
headers: {
|
|
384
|
+
async purchaseGameStoreOffer(auth, body) {
|
|
385
|
+
return this.http.request("POST", "/sdk/games/store/purchase", {
|
|
386
|
+
bearerToken: auth.bearerToken,
|
|
387
|
+
headers: {
|
|
388
|
+
"x-creator-key": auth.creatorKey,
|
|
389
|
+
"x-game-secret": auth.gameSecret,
|
|
390
|
+
"x-game-tracking-session-id": auth.trackingSessionId
|
|
391
|
+
},
|
|
391
392
|
body
|
|
392
393
|
});
|
|
393
394
|
}
|
|
394
|
-
async getStoreForGameKey(gameKey) {
|
|
395
|
-
return this.http.request("GET", "/sdk/games/store/current", {
|
|
396
|
-
headers: { "x-game-key": gameKey }
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
async listGameAssetsForGameKey(gameKey) {
|
|
400
|
-
return this.http.request("GET", "/sdk/games/store/assets", {
|
|
401
|
-
headers: { "x-game-key": gameKey }
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
async listMyOwnedAssetsForGameKey(bearerToken, gameKey) {
|
|
405
|
-
return this.http.request("GET", "/sdk/games/store/assets/me", {
|
|
406
|
-
bearerToken,
|
|
407
|
-
headers: { "x-game-key": gameKey }
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
async listMyStorePurchasesForGameKey(bearerToken, gameKey) {
|
|
411
|
-
return this.http.request("GET", "/sdk/games/store/purchases/me", {
|
|
412
|
-
bearerToken,
|
|
413
|
-
headers: { "x-game-key": gameKey }
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
// No public/anonymous purchase route exists for the runtime tier —
|
|
417
|
-
// purchases move real coin balance and always require the JWT tier
|
|
418
|
-
// (createStoreCheckoutIntent/purchaseStoreOffer above) or the
|
|
419
|
-
// creator-secret tier, which this SDK intentionally never wires up.
|
|
420
395
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
421
396
|
// Balance
|
|
422
397
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -491,15 +466,15 @@ var ApiClient = class {
|
|
|
491
466
|
this.getAccessToken = options.getAccessToken;
|
|
492
467
|
this.getAnonymousId = options.getAnonymousId;
|
|
493
468
|
}
|
|
494
|
-
/** Expose the underlying route-level client (requires manual bearerToken passing). */
|
|
469
|
+
/** Expose the underlying route-level client (requires manual bearerToken/gameKey passing). */
|
|
495
470
|
unsafeRaw() {
|
|
496
471
|
return this.raw;
|
|
497
472
|
}
|
|
498
|
-
//
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
//
|
|
473
|
+
// Real-time (balance_change / store_purchase_completed WebSocket events)
|
|
474
|
+
// deliberately lives outside this class — import `WsClient` from
|
|
475
|
+
// '@looplay/sdk/realtime' and construct it with the same `getAccessToken`
|
|
476
|
+
// you passed here. Keeps the socket.io-client dependency (and its bundle
|
|
477
|
+
// weight) out of every consumer that doesn't need real-time updates.
|
|
503
478
|
async requireToken() {
|
|
504
479
|
const token = await this.getAccessToken?.();
|
|
505
480
|
if (!token) throw new NotAuthenticatedError();
|
|
@@ -555,15 +530,15 @@ var ApiClient = class {
|
|
|
555
530
|
* swallow failures, unlike `ensureTrackingSession`, since these calls
|
|
556
531
|
* are rejected outright without a valid token.
|
|
557
532
|
*/
|
|
558
|
-
async ensureGameplayAttempt(
|
|
533
|
+
async ensureGameplayAttempt(gameKey, auth, attemptId) {
|
|
559
534
|
const now = Date.now();
|
|
560
|
-
if (this.gameplayAttempt && this.gameplayAttempt.
|
|
535
|
+
if (this.gameplayAttempt && this.gameplayAttempt.gameKey === gameKey && this.gameplayAttempt.attemptId === attemptId && this.gameplayAttempt.expiresAtMs > now + 1e4) {
|
|
561
536
|
return this.gameplayAttempt;
|
|
562
537
|
}
|
|
563
538
|
if (!this.gameplayAttemptPromise) {
|
|
564
|
-
this.gameplayAttemptPromise = this.raw.startGameplayAttempt(
|
|
539
|
+
this.gameplayAttemptPromise = this.raw.startGameplayAttempt(gameKey, auth, { attemptId }).then((dto) => {
|
|
565
540
|
this.gameplayAttempt = {
|
|
566
|
-
|
|
541
|
+
gameKey,
|
|
567
542
|
attemptId: dto.attemptId,
|
|
568
543
|
attemptToken: dto.attemptToken,
|
|
569
544
|
expiresAtMs: new Date(dto.expiresAt).getTime()
|
|
@@ -575,24 +550,13 @@ var ApiClient = class {
|
|
|
575
550
|
}
|
|
576
551
|
return this.gameplayAttemptPromise;
|
|
577
552
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
async
|
|
583
|
-
const detail = await this.getGameDetail(gameId);
|
|
584
|
-
const storeId = detail?.storeId;
|
|
585
|
-
if (!storeId) throw new MissingStoreError(gameId);
|
|
586
|
-
return storeId;
|
|
587
|
-
}
|
|
588
|
-
async listRecentPlayed(query) {
|
|
589
|
-
const token = await this.requireToken();
|
|
590
|
-
return this.raw.listRecentPlayed(token, query);
|
|
591
|
-
}
|
|
592
|
-
/** Call once when the game view opens, before any play/match tracking. */
|
|
593
|
-
async trackView(gameId) {
|
|
553
|
+
/**
|
|
554
|
+
* Call once when the game view opens, before any play/match tracking.
|
|
555
|
+
* `gameKey` is the game's public `appId`.
|
|
556
|
+
*/
|
|
557
|
+
async trackView(gameKey) {
|
|
594
558
|
const auth = await this.resolveTrackingAuth();
|
|
595
|
-
return this.raw.recordGameView(
|
|
559
|
+
return this.raw.recordGameView(gameKey, auth);
|
|
596
560
|
}
|
|
597
561
|
/**
|
|
598
562
|
* Starts a new play attempt and returns its id. Call when the user
|
|
@@ -605,22 +569,22 @@ var ApiClient = class {
|
|
|
605
569
|
this.gameplayAttempt = void 0;
|
|
606
570
|
return this.currentAttemptId;
|
|
607
571
|
}
|
|
608
|
-
async trackPlay(
|
|
572
|
+
async trackPlay(gameKey, playTimeSeconds) {
|
|
609
573
|
const auth = await this.resolveTrackingAuth();
|
|
610
574
|
const attemptId = this.currentAttemptId ?? this.startAttempt();
|
|
611
|
-
const { attemptToken } = await this.ensureGameplayAttempt(
|
|
612
|
-
return this.raw.completeGameplayAttempt(
|
|
575
|
+
const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
|
|
576
|
+
return this.raw.completeGameplayAttempt(gameKey, auth, { playTimeSeconds, attemptId, attemptToken });
|
|
613
577
|
}
|
|
614
|
-
async trackMatch(
|
|
578
|
+
async trackMatch(gameKey, body) {
|
|
615
579
|
const auth = await this.resolveTrackingAuth();
|
|
616
580
|
const attemptId = this.currentAttemptId ?? this.startAttempt();
|
|
617
|
-
const { attemptToken } = await this.ensureGameplayAttempt(
|
|
618
|
-
return this.raw.completeGameplayMatch(
|
|
581
|
+
const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
|
|
582
|
+
return this.raw.completeGameplayMatch(gameKey, auth, { ...body, attemptId, attemptToken });
|
|
619
583
|
}
|
|
620
|
-
async emit(
|
|
584
|
+
async emit(gameKey, actionCode, opts) {
|
|
621
585
|
const auth = await this.resolveTrackingAuth();
|
|
622
586
|
const attemptId = this.currentAttemptId ?? this.startAttempt();
|
|
623
|
-
const { attemptToken } = await this.ensureGameplayAttempt(
|
|
587
|
+
const { attemptToken } = await this.ensureGameplayAttempt(gameKey, auth, attemptId);
|
|
624
588
|
const body = {
|
|
625
589
|
attemptId,
|
|
626
590
|
attemptToken,
|
|
@@ -629,33 +593,42 @@ var ApiClient = class {
|
|
|
629
593
|
refId: opts?.refId,
|
|
630
594
|
payload: opts?.payload
|
|
631
595
|
};
|
|
632
|
-
return this.raw.recordGameplayAction(
|
|
596
|
+
return this.raw.recordGameplayAction(gameKey, auth, body);
|
|
633
597
|
}
|
|
634
|
-
/** Fetches the storefront (sections + offers) for
|
|
635
|
-
async getStore(
|
|
636
|
-
|
|
637
|
-
return this.raw.getStoreByStoreId(storeId);
|
|
598
|
+
/** Fetches the storefront (sections + offers) for the integration's game. Read-only, no auth required. */
|
|
599
|
+
async getStore(gameKey) {
|
|
600
|
+
return this.raw.getStore(gameKey);
|
|
638
601
|
}
|
|
639
|
-
/**
|
|
640
|
-
async
|
|
641
|
-
|
|
642
|
-
|
|
602
|
+
/** Public catalog of purchasable/ownable assets for the game. Read-only, no auth required. */
|
|
603
|
+
async listGameAssets(gameKey) {
|
|
604
|
+
return this.raw.listGameAssets(gameKey);
|
|
605
|
+
}
|
|
606
|
+
/** Assets the authenticated player already owns for this game. */
|
|
607
|
+
async listMyGameAssets(gameKey) {
|
|
608
|
+
const token = await this.requireToken();
|
|
609
|
+
return this.raw.listMyGameAssets(token, gameKey);
|
|
610
|
+
}
|
|
611
|
+
/** Purchase history for the authenticated player in the game's store. */
|
|
612
|
+
async listMyStorePurchases(gameKey) {
|
|
613
|
+
const token = await this.requireToken();
|
|
614
|
+
return this.raw.listMyStorePurchases(token, gameKey);
|
|
643
615
|
}
|
|
644
616
|
/**
|
|
645
617
|
* Spends coin balance to purchase a store offer — signs a checkout intent
|
|
646
618
|
* first, then redeems it, mirroring the gameplay attempt flow above.
|
|
619
|
+
* Self-serve: only the player's own access token is used, no secret pair
|
|
620
|
+
* required, safe to call directly from the game client. Also see
|
|
621
|
+
* `onStorePurchase` to react to the result in real time from any tab.
|
|
647
622
|
*/
|
|
648
|
-
async purchaseStoreOffer(
|
|
649
|
-
const
|
|
650
|
-
const intent = await this.raw.
|
|
651
|
-
storeId,
|
|
623
|
+
async purchaseStoreOffer(gameKey, offerCode, opts) {
|
|
624
|
+
const token = await this.requireToken();
|
|
625
|
+
const intent = await this.raw.createMyStoreCheckoutIntent(gameKey, token, {
|
|
652
626
|
offerCode,
|
|
653
627
|
requestId: opts?.requestId,
|
|
654
628
|
quantity: opts?.quantity
|
|
655
629
|
});
|
|
656
|
-
return this.raw.
|
|
657
|
-
|
|
658
|
-
offerCode,
|
|
630
|
+
return this.raw.purchaseMyStoreOffer(gameKey, token, {
|
|
631
|
+
offerCode: intent.offerCode,
|
|
659
632
|
requestId: intent.requestId,
|
|
660
633
|
quantity: intent.quantity,
|
|
661
634
|
checkoutToken: intent.checkoutToken
|
|
@@ -791,9 +764,8 @@ var LooplaySDK = class {
|
|
|
791
764
|
}
|
|
792
765
|
async verifyGameId(params) {
|
|
793
766
|
if ((params.verifyMode ?? "none") === "none") return;
|
|
794
|
-
if (!this.auth) throw new MissingAuthError();
|
|
795
767
|
if (!this.api) throw new MissingBaseUrlError();
|
|
796
|
-
await this.api.
|
|
768
|
+
await this.api.listGameAssets(params.gameId);
|
|
797
769
|
}
|
|
798
770
|
};
|
|
799
771
|
|
|
@@ -1356,7 +1328,6 @@ exports.LooplaySDKError = LooplaySDKError;
|
|
|
1356
1328
|
exports.MemoryAuthStorage = MemoryAuthStorage;
|
|
1357
1329
|
exports.MissingAuthError = MissingAuthError;
|
|
1358
1330
|
exports.MissingBaseUrlError = MissingBaseUrlError;
|
|
1359
|
-
exports.MissingStoreError = MissingStoreError;
|
|
1360
1331
|
exports.NotAuthenticatedError = NotAuthenticatedError;
|
|
1361
1332
|
exports.NotInitializedError = NotInitializedError;
|
|
1362
1333
|
exports.ServiceClient = ServiceClient;
|