@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
|
@@ -24,6 +24,19 @@ export interface GameTrackingSessionDto {
|
|
|
24
24
|
export interface StartGameplayAttemptRequest {
|
|
25
25
|
/** Optional client-generated id. When omitted, the server creates one. */
|
|
26
26
|
attemptId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* One-call convenience: when set, the server also records this action
|
|
29
|
+
* (e.g. `"GAME_STARTED"`) right after issuing the attempt, using the
|
|
30
|
+
* freshly signed `attemptId`/`attemptToken` — skips a separate
|
|
31
|
+
* `recordGameplayAction` call. See `matchId`/`value`/`refId`/`eventId`/
|
|
32
|
+
* `payload` below for the action's own fields.
|
|
33
|
+
*/
|
|
34
|
+
actionCode?: string;
|
|
35
|
+
matchId?: string;
|
|
36
|
+
value?: number;
|
|
37
|
+
refId?: string;
|
|
38
|
+
eventId?: string;
|
|
39
|
+
payload?: Record<string, unknown>;
|
|
27
40
|
}
|
|
28
41
|
export interface GameplayAttemptSessionDto {
|
|
29
42
|
trackingSessionId: string;
|
|
@@ -131,9 +144,15 @@ export interface GameOwnedAssetDto {
|
|
|
131
144
|
assetName: string;
|
|
132
145
|
quantity: number;
|
|
133
146
|
}
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Body shape shared by both purchase tiers: self-serve
|
|
149
|
+
* (`POST sdk/games/store/checkout-intent/me`, public `x-game-key` + the
|
|
150
|
+
* purchasing player's own JWT — see `ServiceClient.createMyStoreCheckoutIntent`)
|
|
151
|
+
* and server-to-server (`POST sdk/games/store/checkout-intent`, creator+game
|
|
152
|
+
* secret pair — see `ServiceClient.createGameStoreCheckoutIntent`). Signs a
|
|
153
|
+
* short-lived `checkoutToken`, mirroring the gameplay attempt flow.
|
|
154
|
+
*/
|
|
155
|
+
export interface SdkCreateGameStoreCheckoutIntentRequest {
|
|
137
156
|
offerCode: string;
|
|
138
157
|
/** Optional client-supplied dedupe key so a retried request returns the same result instead of charging twice. */
|
|
139
158
|
requestId?: string;
|
|
@@ -149,11 +168,40 @@ export interface GameStoreCheckoutIntentDto {
|
|
|
149
168
|
issuedAt: string;
|
|
150
169
|
expiresAt: string;
|
|
151
170
|
}
|
|
152
|
-
/**
|
|
153
|
-
export interface
|
|
154
|
-
/** Signed by the server when the checkout intent is created — see `
|
|
171
|
+
/** Shared by both purchase tiers — see `SdkCreateGameStoreCheckoutIntentRequest`. */
|
|
172
|
+
export interface SdkPurchaseGameStoreOfferRuntimeRequest extends SdkCreateGameStoreCheckoutIntentRequest {
|
|
173
|
+
/** Signed by the server when the checkout intent is created — see `SdkCreateGameStoreCheckoutIntentRequest`. */
|
|
155
174
|
checkoutToken: string;
|
|
156
175
|
}
|
|
176
|
+
/** Pushed on every balance change — purchases, quest rewards, referral payouts, etc. */
|
|
177
|
+
export interface BalanceChangeEvent {
|
|
178
|
+
balanceType: string;
|
|
179
|
+
changeAmount: number;
|
|
180
|
+
changeType: string;
|
|
181
|
+
/** Present when multiple balance changes were batched into one push. */
|
|
182
|
+
changes?: Array<{
|
|
183
|
+
balanceType: string;
|
|
184
|
+
changeAmount: number;
|
|
185
|
+
changeType: string;
|
|
186
|
+
}>;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Pushed once a store purchase finishes — full result (items granted,
|
|
190
|
+
* quantity, price), unlike `BalanceChangeEvent` which only reports the coin
|
|
191
|
+
* delta. Fires for purchases made via either tier (self-serve or
|
|
192
|
+
* server-to-server), so a hosted-checkout backend flow can complete a
|
|
193
|
+
* purchase and have the game's own client react without polling.
|
|
194
|
+
*/
|
|
195
|
+
export interface StorePurchaseCompletedEvent {
|
|
196
|
+
gameId: string;
|
|
197
|
+
offerCode: string;
|
|
198
|
+
name: string;
|
|
199
|
+
quantity: number;
|
|
200
|
+
priceCoin: number;
|
|
201
|
+
totalCoin: number;
|
|
202
|
+
requestId: string;
|
|
203
|
+
items: GameStoreOfferItemDto[];
|
|
204
|
+
}
|
|
157
205
|
/** Mirrors the backend's `AppPlatform` enum — kept as a literal union to avoid a cross-repo enum dependency. */
|
|
158
206
|
export type AppPlatform = 'telegram' | 'tiktok' | 'web' | 'base' | 'worldapp';
|
|
159
207
|
/** Mirrors the backend's `MobileOs` enum. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EmitGameEventResponse, GameEventKey, GameStoreDto, GameStoreOfferPurchaseResultDto, PagingResponse, TaskListQuery } from './LooplaySDK.types';
|
|
1
|
+
import type { EmitGameEventResponse, GameAssetDto, GameEventKey, GameOwnedAssetDto, GameStoreDto, GameStoreOfferPurchaseResultDto, PagingResponse, TaskListQuery } from './LooplaySDK.types';
|
|
2
2
|
import { ServiceClient } from './service-client';
|
|
3
3
|
import type { ServiceClientOptions } from './service-client';
|
|
4
4
|
export interface ApiClientOptions extends Omit<ServiceClientOptions, 'baseUrl'> {
|
|
@@ -24,9 +24,8 @@ export declare class ApiClient {
|
|
|
24
24
|
private gameplayAttempt?;
|
|
25
25
|
private gameplayAttemptPromise?;
|
|
26
26
|
constructor(options: ApiClientOptions);
|
|
27
|
-
/** Expose the underlying route-level client (requires manual bearerToken passing). */
|
|
27
|
+
/** Expose the underlying route-level client (requires manual bearerToken/gameKey passing). */
|
|
28
28
|
unsafeRaw(): ServiceClient;
|
|
29
|
-
listGames(query?: Record<string, any>): Promise<PagingResponse<any>>;
|
|
30
29
|
private requireToken;
|
|
31
30
|
/**
|
|
32
31
|
* Resolves auth for the tracking endpoints: a real bearer token if logged
|
|
@@ -50,11 +49,11 @@ export declare class ApiClient {
|
|
|
50
49
|
* are rejected outright without a valid token.
|
|
51
50
|
*/
|
|
52
51
|
private ensureGameplayAttempt;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
trackView(
|
|
52
|
+
/**
|
|
53
|
+
* Call once when the game view opens, before any play/match tracking.
|
|
54
|
+
* `gameKey` is the game's public `appId`.
|
|
55
|
+
*/
|
|
56
|
+
trackView(gameKey: string): Promise<boolean>;
|
|
58
57
|
/**
|
|
59
58
|
* Starts a new play attempt and returns its id. Call when the user
|
|
60
59
|
* presses Play; `trackPlay`/`trackMatch`/`emit` reuse this id (and the
|
|
@@ -62,27 +61,34 @@ export declare class ApiClient {
|
|
|
62
61
|
* `startAttempt()`, auto-starting one on first use if none was started.
|
|
63
62
|
*/
|
|
64
63
|
startAttempt(): string;
|
|
65
|
-
trackPlay(
|
|
66
|
-
trackMatch(
|
|
64
|
+
trackPlay(gameKey: string, playTimeSeconds?: number): Promise<boolean>;
|
|
65
|
+
trackMatch(gameKey: string, body: {
|
|
67
66
|
matchId: string;
|
|
68
67
|
matchDurationSeconds: number;
|
|
69
68
|
isCompleted?: boolean;
|
|
70
69
|
isWin?: boolean;
|
|
71
70
|
}): Promise<boolean>;
|
|
72
|
-
emit(
|
|
71
|
+
emit(gameKey: string, actionCode: GameEventKey | string, opts?: {
|
|
73
72
|
value?: number;
|
|
74
73
|
refId?: string;
|
|
75
74
|
payload?: Record<string, unknown>;
|
|
76
75
|
}): Promise<EmitGameEventResponse>;
|
|
77
|
-
/** Fetches the storefront (sections + offers) for
|
|
78
|
-
getStore(
|
|
79
|
-
/**
|
|
80
|
-
|
|
76
|
+
/** Fetches the storefront (sections + offers) for the integration's game. Read-only, no auth required. */
|
|
77
|
+
getStore(gameKey: string): Promise<GameStoreDto>;
|
|
78
|
+
/** Public catalog of purchasable/ownable assets for the game. Read-only, no auth required. */
|
|
79
|
+
listGameAssets(gameKey: string): Promise<GameAssetDto[]>;
|
|
80
|
+
/** Assets the authenticated player already owns for this game. */
|
|
81
|
+
listMyGameAssets(gameKey: string): Promise<GameOwnedAssetDto[]>;
|
|
82
|
+
/** Purchase history for the authenticated player in the game's store. */
|
|
83
|
+
listMyStorePurchases(gameKey: string): Promise<GameStoreOfferPurchaseResultDto[]>;
|
|
81
84
|
/**
|
|
82
85
|
* Spends coin balance to purchase a store offer — signs a checkout intent
|
|
83
86
|
* first, then redeems it, mirroring the gameplay attempt flow above.
|
|
87
|
+
* Self-serve: only the player's own access token is used, no secret pair
|
|
88
|
+
* required, safe to call directly from the game client. Also see
|
|
89
|
+
* `onStorePurchase` to react to the result in real time from any tab.
|
|
84
90
|
*/
|
|
85
|
-
purchaseStoreOffer(
|
|
91
|
+
purchaseStoreOffer(gameKey: string, offerCode: string, opts?: {
|
|
86
92
|
requestId?: string;
|
|
87
93
|
quantity?: number;
|
|
88
94
|
}): Promise<GameStoreOfferPurchaseResultDto>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type HttpClientOptions } from './http';
|
|
2
|
-
import type { AuthResultDto, CompleteGameplayAttemptRequest, CompleteGameplayMatchRequest,
|
|
2
|
+
import type { AuthResultDto, CompleteGameplayAttemptRequest, CompleteGameplayMatchRequest, EmitGameEventResponse, GameAssetDto, GameOwnedAssetDto, GameplayAttemptSessionDto, GameStoreCheckoutIntentDto, GameStoreDto, GameStoreOfferPurchaseResultDto, GameTrackingSessionDto, PagingResponse, RecordGameplayActionRequest, SdkCreateGameStoreCheckoutIntentRequest, SdkPurchaseGameStoreOfferRuntimeRequest, StartGameplayAttemptRequest, TaskListQuery } from './LooplaySDK.types';
|
|
3
3
|
export interface ServiceClientOptions extends HttpClientOptions {
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
@@ -17,6 +17,19 @@ export interface TrackingAuth {
|
|
|
17
17
|
trackingSessionId?: string;
|
|
18
18
|
anonTrackingToken?: string;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Auth for the creator-secret tier (`createGameStoreCheckoutIntent`/
|
|
22
|
+
* `purchaseGameStoreOffer`) — moves real coin balance. `creatorKey`/
|
|
23
|
+
* `gameSecret` identify your integration (never embed these in
|
|
24
|
+
* client/browser code); `bearerToken` identifies the player you're
|
|
25
|
+
* purchasing on behalf of, same as any other authenticated call.
|
|
26
|
+
*/
|
|
27
|
+
export interface SdkKeyPairAuth {
|
|
28
|
+
creatorKey: string;
|
|
29
|
+
gameSecret: string;
|
|
30
|
+
bearerToken: string;
|
|
31
|
+
trackingSessionId?: string;
|
|
32
|
+
}
|
|
20
33
|
/**
|
|
21
34
|
* Low-level HTTP client that maps 1:1 to backend routes.
|
|
22
35
|
* Methods requiring user auth accept `bearerToken` explicitly.
|
|
@@ -27,15 +40,12 @@ export declare class ServiceClient {
|
|
|
27
40
|
telegramLogin(telegramInitData: string): Promise<AuthResultDto>;
|
|
28
41
|
refresh(refreshToken: string): Promise<AuthResultDto>;
|
|
29
42
|
logout(refreshToken: string): Promise<boolean>;
|
|
30
|
-
|
|
31
|
-
getGameDetail(gameId: string, bearerToken?: string): Promise<any>;
|
|
32
|
-
listRecentPlayed(bearerToken: string, query?: Record<string, any>): Promise<PagingResponse<any>>;
|
|
33
|
-
recordGameView(auth: TrackingAuth, gameId: string): Promise<boolean>;
|
|
43
|
+
recordGameView(gameKey: string, auth?: TrackingAuth): Promise<boolean>;
|
|
34
44
|
/** Signs a fresh `attemptToken` — required by `completeGameplayAttempt`/`completeGameplayMatch`/`recordGameplayAction`. */
|
|
35
|
-
startGameplayAttempt(
|
|
36
|
-
completeGameplayAttempt(
|
|
37
|
-
completeGameplayMatch(
|
|
38
|
-
recordGameplayAction(
|
|
45
|
+
startGameplayAttempt(gameKey: string, auth?: TrackingAuth, body?: StartGameplayAttemptRequest): Promise<GameplayAttemptSessionDto>;
|
|
46
|
+
completeGameplayAttempt(gameKey: string, auth: TrackingAuth, body: CompleteGameplayAttemptRequest): Promise<boolean>;
|
|
47
|
+
completeGameplayMatch(gameKey: string, auth: TrackingAuth, body: CompleteGameplayMatchRequest): Promise<boolean>;
|
|
48
|
+
recordGameplayAction(gameKey: string, auth: TrackingAuth, body: RecordGameplayActionRequest): Promise<EmitGameEventResponse>;
|
|
39
49
|
/**
|
|
40
50
|
* Anonymous runtimes must call this before any tracking call; logged-in
|
|
41
51
|
* runtimes should still call it to get a `trackingSessionId` for unified
|
|
@@ -43,19 +53,20 @@ export declare class ServiceClient {
|
|
|
43
53
|
*/
|
|
44
54
|
bootstrapTrackingSession(anonId?: string): Promise<GameTrackingSessionDto>;
|
|
45
55
|
private trackingHeaders;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
/** Public storefront (sections + offers) for the integration's game. Read-only. */
|
|
57
|
+
getStore(gameKey: string): Promise<GameStoreDto>;
|
|
58
|
+
/** Public catalog of purchasable/ownable assets for the game. Read-only. */
|
|
59
|
+
listGameAssets(gameKey: string): Promise<GameAssetDto[]>;
|
|
60
|
+
/** Assets the authenticated player already owns for this game. */
|
|
61
|
+
listMyGameAssets(bearerToken: string, gameKey: string): Promise<GameOwnedAssetDto[]>;
|
|
62
|
+
/** The authenticated player's past store purchases for this game. */
|
|
63
|
+
listMyStorePurchases(bearerToken: string, gameKey: string): Promise<GameStoreOfferPurchaseResultDto[]>;
|
|
64
|
+
/** Signs a `checkoutToken` for one purchase — required by `purchaseMyStoreOffer`. */
|
|
65
|
+
createMyStoreCheckoutIntent(gameKey: string, bearerToken: string, body: SdkCreateGameStoreCheckoutIntentRequest, trackingSessionId?: string): Promise<GameStoreCheckoutIntentDto>;
|
|
66
|
+
purchaseMyStoreOffer(gameKey: string, bearerToken: string, body: SdkPurchaseGameStoreOfferRuntimeRequest, trackingSessionId?: string): Promise<GameStoreOfferPurchaseResultDto>;
|
|
67
|
+
/** Signs a `checkoutToken` for one purchase — required by `purchaseGameStoreOffer`. */
|
|
68
|
+
createGameStoreCheckoutIntent(auth: SdkKeyPairAuth, body: SdkCreateGameStoreCheckoutIntentRequest): Promise<GameStoreCheckoutIntentDto>;
|
|
69
|
+
purchaseGameStoreOffer(auth: SdkKeyPairAuth, body: SdkPurchaseGameStoreOfferRuntimeRequest): Promise<GameStoreOfferPurchaseResultDto>;
|
|
59
70
|
getBalances(bearerToken: string, query?: Record<string, any>): Promise<any>;
|
|
60
71
|
getBalanceHistory(bearerToken: string, query?: Record<string, any>): Promise<PagingResponse<any>>;
|
|
61
72
|
getMyProfile(bearerToken: string): Promise<any>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { BalanceChangeEvent, StorePurchaseCompletedEvent } from './LooplaySDK.types';
|
|
2
|
+
export interface WsClientOptions {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
/** Resolves the current player's access token — re-checked on every (re)connect. */
|
|
5
|
+
getAccessToken: () => Promise<string | undefined>;
|
|
6
|
+
}
|
|
7
|
+
type Listener<T> = (payload: T) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Lazy-connecting Socket.IO client mirroring gbs-service's AppGateway.
|
|
10
|
+
* Both events it exposes (`balance_change`, `store_purchase_completed`) are
|
|
11
|
+
* pushed only to the authenticated player's own room — there is nothing to
|
|
12
|
+
* receive without an access token, so `subscribe*` is a no-op until one is
|
|
13
|
+
* available.
|
|
14
|
+
*
|
|
15
|
+
* The connection is opened on the first subscription and closed once the
|
|
16
|
+
* last listener unsubscribes — a game that never calls `onBalanceChange`/
|
|
17
|
+
* `onStorePurchase` never opens a socket at all.
|
|
18
|
+
*/
|
|
19
|
+
export declare class WsClient {
|
|
20
|
+
private readonly options;
|
|
21
|
+
private socket?;
|
|
22
|
+
private connectPromise?;
|
|
23
|
+
private readonly balanceListeners;
|
|
24
|
+
private readonly purchaseListeners;
|
|
25
|
+
constructor(options: WsClientOptions);
|
|
26
|
+
/** Fires on every balance change — purchases, quest rewards, referral payouts, etc. */
|
|
27
|
+
onBalanceChange(listener: Listener<BalanceChangeEvent>): () => void;
|
|
28
|
+
/** Fires once a store purchase finishes — see `StorePurchaseCompletedEvent`. */
|
|
29
|
+
onStorePurchase(listener: Listener<StorePurchaseCompletedEvent>): () => void;
|
|
30
|
+
/** Closes the socket immediately, regardless of active listeners. */
|
|
31
|
+
disconnect(): void;
|
|
32
|
+
private ensureConnected;
|
|
33
|
+
private disconnectIfIdle;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional real-time subpath — `import { WsClient } from '@looplay/sdk/realtime'`.
|
|
3
|
+
* Kept out of the main `@looplay/sdk` entry point (and its browser IIFE
|
|
4
|
+
* bundle) because it pulls in `socket.io-client`, which most integrations
|
|
5
|
+
* — tracking-only or store-browsing-only — don't need.
|
|
6
|
+
*/
|
|
7
|
+
export { WsClient } from './apps/ws-client';
|
|
8
|
+
export type { WsClientOptions } from './apps/ws-client';
|
|
9
|
+
export type { BalanceChangeEvent, StorePurchaseCompletedEvent } from './apps/LooplaySDK.types';
|
package/dist/types/types.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ export interface LooplaySDKCreateOptions {
|
|
|
7
7
|
defaultHeaders?: Record<string, string>;
|
|
8
8
|
}
|
|
9
9
|
export interface LooplayInitParams {
|
|
10
|
+
/** The game's public `appId` — same value as `x-game-key`, from your creator dashboard. */
|
|
10
11
|
gameId: string;
|
|
11
|
-
/** 'none': skip verification | 'strict':
|
|
12
|
+
/** 'none': skip verification | 'strict': resolve gameId via a read-only, unauthenticated call */
|
|
12
13
|
verifyMode?: 'none' | 'strict';
|
|
13
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@looplay/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "LooplaySDK (auth + points) for game integrations",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -17,6 +17,14 @@
|
|
|
17
17
|
},
|
|
18
18
|
"./browser": {
|
|
19
19
|
"default": "./dist/looplay-sdk.min.js"
|
|
20
|
+
},
|
|
21
|
+
"./realtime": {
|
|
22
|
+
"types": "./dist/types/realtime.d.ts",
|
|
23
|
+
"import": "./dist/looplay-sdk-realtime.esm.js",
|
|
24
|
+
"require": "./dist/looplay-sdk-realtime.cjs.js"
|
|
25
|
+
},
|
|
26
|
+
"./realtime/browser": {
|
|
27
|
+
"default": "./dist/looplay-sdk-realtime.min.js"
|
|
20
28
|
}
|
|
21
29
|
},
|
|
22
30
|
"files": [
|
|
@@ -30,6 +38,14 @@
|
|
|
30
38
|
"dependencies": {
|
|
31
39
|
"@looplay/types": "^0.1.0"
|
|
32
40
|
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"socket.io-client": "^4.8.3"
|
|
43
|
+
},
|
|
44
|
+
"peerDependenciesMeta": {
|
|
45
|
+
"socket.io-client": {
|
|
46
|
+
"optional": true
|
|
47
|
+
}
|
|
48
|
+
},
|
|
33
49
|
"scripts": {
|
|
34
50
|
"build": "npm run build:js && npm run build:types",
|
|
35
51
|
"build:js": "tsup",
|