@momo-cloud/gami-sdk 0.0.68 → 0.0.82
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 +173 -775
- package/dist/index.public.d.ts +32 -311
- package/dist/index.public.js +1607 -1213
- package/package.json +3 -6
package/README.md
CHANGED
|
@@ -1,858 +1,256 @@
|
|
|
1
|
-
# Gami SDK
|
|
1
|
+
# Gami SDK Integration Guide
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`@momo-cloud/gami-sdk` is the integration layer between your web game and MoMo host capabilities.
|
|
4
|
+
It provides:
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
- Game backend APIs (spin, shake, mission, leaderboard, balance, exchange)
|
|
7
|
+
- Platform bridge APIs (toast, share, permission, contacts, deep-link, calendar, clipboard, and more)
|
|
8
|
+
- Runtime utilities (server-time sync, storage helpers, SSE, event bus)
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
This document is written for engineering teams integrating a game into the MoMo ecosystem.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## 1) Integration Architecture
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
- 🎮 **Game Lifecycle Management** - Start, end, and manage game sessions
|
|
13
|
-
- 💾 **Storage API** - Client-side storage with automatic key prefixing
|
|
14
|
-
- 📊 **Leaderboard & Rankings** - Built-in support for competitive features
|
|
15
|
-
- 📅 **Calendar Integration** - Schedule reminders and events
|
|
16
|
-
- 🎯 **Mission System** - Track and complete user missions
|
|
17
|
-
- 🎲 **Game Events** - Real-time event handling and tracking
|
|
18
|
-
- 📈 **Analytics & Tracking** - Built-in event tracking and user behavior analytics
|
|
19
|
-
- 🌐 **Cross-Platform** - Works in browser and MoMo app
|
|
20
|
-
- 📦 **TypeScript Support** - Full type definitions included
|
|
14
|
+
At runtime, `GamiSDK` composes two API groups:
|
|
21
15
|
|
|
22
|
-
|
|
16
|
+
- `platformApi.exposeApi`: host bridge and device/platform functions
|
|
17
|
+
- `gamiApi.exposeApi`: game-domain APIs and session workflow
|
|
23
18
|
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
```typescript
|
|
38
|
-
import GamiSDK from '@momo-cloud/gami-sdk';
|
|
39
|
-
|
|
40
|
-
// Initialize the SDK
|
|
41
|
-
await GamiSDK.init({
|
|
42
|
-
gameId: 'your-game-id',
|
|
43
|
-
appjson: '',
|
|
44
|
-
source: 'game-launch-source'
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
console.log('User ID:', GamiSDK.userId);
|
|
48
|
-
console.log('Token:', GamiSDK.token);
|
|
49
|
-
|
|
50
|
-
// Start game session
|
|
51
|
-
await GamiSDK.startGame();
|
|
52
|
-
|
|
53
|
-
// Your game logic here...
|
|
54
|
-
|
|
55
|
-
// make spin to get reward
|
|
56
|
-
const result = await GamiSDK.spin();
|
|
57
|
-
|
|
58
|
-
// End game session
|
|
59
|
-
await GamiSDK.endGame();
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## 📖 API Reference
|
|
63
|
-
|
|
64
|
-
### Initialization
|
|
65
|
-
|
|
66
|
-
#### `init(options)`
|
|
67
|
-
|
|
68
|
-
Initialize the SDK with your game configuration.
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
await GamiSDK.init({
|
|
72
|
-
gameId: 'demo',
|
|
73
|
-
appjson?: '',
|
|
74
|
-
source: 'momo',
|
|
75
|
-
userId?: '1234567890' // Optional, for testing
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Parameters:**
|
|
80
|
-
- `gameId` (string, optional): Your game identifier. If not provided, uses `featureId` from platform
|
|
81
|
-
- `appjson` (string, required): app.json configuration file
|
|
82
|
-
- `source` (string, optional): Launch source for analytics
|
|
83
|
-
- `userId` (string, optional): Override user ID for testing
|
|
84
|
-
|
|
85
|
-
**Returns:** `Promise<void>`
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
### Game Management
|
|
90
|
-
|
|
91
|
-
#### `startGame()`
|
|
92
|
-
|
|
93
|
-
Begin a new game session and notify the backend.
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
const response = await GamiSDK.startGame();
|
|
97
|
-
console.log('Game started:', response);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Returns:** `Promise<any>` - Server response with session data
|
|
101
|
-
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
#### `endGame()`
|
|
105
|
-
|
|
106
|
-
End the current game session and dismiss the app.
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
await GamiSDK.endGame();
|
|
110
|
-
// App will be dismissed automatically
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**Returns:** `Promise<void>`
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
### Configuration & Data
|
|
118
|
-
|
|
119
|
-
#### `getConfig()`
|
|
120
|
-
|
|
121
|
-
Fetch game configuration from the backend.
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
const config = await GamiSDK.getConfig();
|
|
125
|
-
console.log('Game config:', config);
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
**Returns:** `Promise<any>` - Game configuration object
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
#### `getCoinExchangeInfo()`
|
|
133
|
-
|
|
134
|
-
Load exchange metadata for the current game (coin → turn). The backend uses the fixed action `exchange_coin_to_turn`; the active `gameId` comes from `init`.
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
const res = await GamiSDK.getCoinExchangeInfo();
|
|
138
|
-
const { balance, exchangeRate, counter, counterLimit, fromCurrency, toCurrency } = res.result;
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
**Returns:** `Promise<TGetExchangeResponse>` - Standard `response_info` plus `result` with rate, balances, counters, and currency pair
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
#### `coinExchange(options)`
|
|
146
|
-
|
|
147
|
-
Submit a coin-to-turn exchange for the current game.
|
|
148
|
-
|
|
149
|
-
```typescript
|
|
150
|
-
const res = await GamiSDK.coinExchange({ amount: 100 });
|
|
151
|
-
const { from, to, counter } = res.result;
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
**Parameters:**
|
|
155
|
-
- `amount` (number): Coin amount to convert
|
|
156
|
-
|
|
157
|
-
**Returns:** `Promise<TPostExchangeResponse>` - Updated `from` / `to` wallet snapshots and `counter` state
|
|
158
|
-
|
|
159
|
-
---
|
|
160
|
-
|
|
161
|
-
#### `spin()`
|
|
162
|
-
|
|
163
|
-
Perform a spinner spin for the current game. Sends `POST` to the twirler service with the active `gameId` (set via `init`).
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
const result = await GamiSDK.spin();
|
|
167
|
-
console.log('Spin result:', result);
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
**Returns:** `Promise<any>` - Server response with spin outcome (e.g. prize payload); exact shape follows the backend contract
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
#### `getBalance(options)`
|
|
175
|
-
|
|
176
|
-
Get user's balance for specified balance IDs.
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
const { result: balance } = await GamiSDK.getBalance({
|
|
180
|
-
balanceId: ['turn', 'coin', 'gem', 'ticket']
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
console.log('Coins:', balance.turn);
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
**Parameters:**
|
|
187
|
-
- `balanceId` (string): Balance Id
|
|
188
|
-
|
|
189
|
-
**Returns:** `Promise<TBalance>` - Balance data object
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
#### `getBalanceConfig()`
|
|
194
|
-
|
|
195
|
-
Get available balance types (pockets) configuration.
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
const pockets = await GamiSDK.getBalanceConfig();
|
|
199
|
-
console.log('Available balances:', pockets);
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
**Returns:** `Promise<any>` - Pocket configuration
|
|
203
|
-
|
|
204
|
-
---
|
|
205
|
-
|
|
206
|
-
#### `getHistory(options)`
|
|
207
|
-
|
|
208
|
-
Get user's game history.
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
const history = await GamiSDK.getHistory({
|
|
212
|
-
page: 1,
|
|
213
|
-
limit: 0
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
console.log('Recent games:', history.items);
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
**Returns:** `Promise<THistory>` - History records
|
|
220
|
-
|
|
221
|
-
---
|
|
222
|
-
|
|
223
|
-
### Events & Missions
|
|
224
|
-
|
|
225
|
-
#### `getEvent()`
|
|
226
|
-
|
|
227
|
-
Fetch active events for the game.
|
|
228
|
-
|
|
229
|
-
```typescript
|
|
230
|
-
const events = await GamiSDK.getEvent();
|
|
231
|
-
|
|
232
|
-
events.forEach(event => {
|
|
233
|
-
console.log(`Event: ${event.name}, Ends: ${event.endTime}`);
|
|
234
|
-
});
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Returns:** `Promise<TEvent>` - Event data
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
#### `mission.get()`
|
|
242
|
-
|
|
243
|
-
Get user's mission status.
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
import { mission } from '@momo-cloud/gami-sdk';
|
|
247
|
-
|
|
248
|
-
const missions = await mission.get();
|
|
249
|
-
|
|
250
|
-
missions.forEach(m => {
|
|
251
|
-
console.log(`Mission: ${m.name}, Progress: ${m.progress}/${m.target}`);
|
|
252
|
-
});
|
|
19
|
+
```text
|
|
20
|
+
Game UI (React/Preact/Pixi)
|
|
21
|
+
|
|
|
22
|
+
v
|
|
23
|
+
GamiSDK
|
|
24
|
+
+-----------+
|
|
25
|
+
| platform | -> host bridge (toast, share, permission, refId, app events)
|
|
26
|
+
| game api | -> game services (config, balance, spin, shake, mission, ...)
|
|
27
|
+
+-----------+
|
|
28
|
+
|
|
|
29
|
+
v
|
|
30
|
+
MoMo host + backend services
|
|
253
31
|
```
|
|
254
32
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
#### `mission.sendEvent(eventName, serviceId?)`
|
|
33
|
+
## 2) Installation
|
|
260
34
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
import { mission } from '@momo-cloud/gami-sdk';
|
|
265
|
-
|
|
266
|
-
await mission.done('level_completed', 'service-123');
|
|
35
|
+
```bash
|
|
36
|
+
npm install @momo-cloud/gami-sdk
|
|
267
37
|
```
|
|
268
38
|
|
|
269
|
-
|
|
270
|
-
- `eventName` (string): Event identifier
|
|
271
|
-
- `serviceId` (string, optional): Related service ID
|
|
272
|
-
|
|
273
|
-
**Returns:** `Promise<any>` - Server response
|
|
39
|
+
or
|
|
274
40
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
### Storage
|
|
278
|
-
|
|
279
|
-
#### `saveItem(key, value)`
|
|
280
|
-
|
|
281
|
-
Save data to local storage with automatic game ID prefixing.
|
|
282
|
-
|
|
283
|
-
```typescript
|
|
284
|
-
import { saveItem } from '@momo-cloud/gami-sdk';
|
|
285
|
-
|
|
286
|
-
// Save JSON object
|
|
287
|
-
await saveItem('userProgress', {
|
|
288
|
-
level: 5,
|
|
289
|
-
score: 1200,
|
|
290
|
-
items: ['sword', 'shield']
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// Save string
|
|
294
|
-
await saveItem('playerName', 'MoMoGamer123');
|
|
41
|
+
```bash
|
|
42
|
+
yarn add @momo-cloud/gami-sdk
|
|
295
43
|
```
|
|
296
44
|
|
|
297
|
-
|
|
298
|
-
- `key` (string): Storage key (will be prefixed with gameId)
|
|
299
|
-
- `value` (any): Data to store (will be JSON stringified)
|
|
300
|
-
|
|
301
|
-
**Returns:** `Promise<void>`
|
|
302
|
-
|
|
303
|
-
---
|
|
304
|
-
|
|
305
|
-
#### `getItem(key)`
|
|
45
|
+
## 3) Quick Start (Recommended Bootstrap)
|
|
306
46
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
47
|
+
```ts
|
|
48
|
+
import GamiSDK from '@momo-cloud/gami-sdk';
|
|
49
|
+
import appJson from '../app.json';
|
|
50
|
+
|
|
51
|
+
export async function bootstrapGame() {
|
|
52
|
+
await GamiSDK.init({
|
|
53
|
+
gameId: import.meta.env.VITE_DEFAULT_GAME_ID,
|
|
54
|
+
userId: import.meta.env.VITE_USER_ID, // optional in host runtime
|
|
55
|
+
appjson: JSON.stringify(appJson),
|
|
56
|
+
source: 'momo', // optional analytics source
|
|
57
|
+
});
|
|
58
|
+
}
|
|
317
59
|
```
|
|
318
60
|
|
|
319
|
-
|
|
320
|
-
- `key` (string): Storage key (gameId prefix added automatically)
|
|
61
|
+
## 4) Integration Phases
|
|
321
62
|
|
|
322
|
-
|
|
63
|
+
### Phase A - Initialize SDK
|
|
323
64
|
|
|
324
|
-
|
|
65
|
+
Call once at app startup:
|
|
325
66
|
|
|
326
|
-
|
|
67
|
+
- `init({ appjson, gameId?, userId?, source? })`
|
|
68
|
+
- `wait()`
|
|
327
69
|
|
|
328
|
-
|
|
70
|
+
Then rely on runtime properties:
|
|
329
71
|
|
|
330
|
-
|
|
331
|
-
|
|
72
|
+
- `GamiSDK.userId`
|
|
73
|
+
- `GamiSDK.userInfo`
|
|
74
|
+
- `GamiSDK.token`
|
|
75
|
+
- `GamiSDK.gameId`
|
|
76
|
+
- `GamiSDK.appProfile`
|
|
77
|
+
- `GamiSDK.deviceInfo`
|
|
332
78
|
|
|
333
|
-
|
|
334
|
-
const cachedUrl = await cacheFile(imageUrl);
|
|
79
|
+
### Phase B - Start Gameplay Session
|
|
335
80
|
|
|
336
|
-
|
|
337
|
-
|
|
81
|
+
```ts
|
|
82
|
+
await GamiSDK.startGame();
|
|
338
83
|
```
|
|
339
84
|
|
|
340
|
-
|
|
341
|
-
- `url` (string): Remote file URL
|
|
342
|
-
|
|
343
|
-
**Returns:** `Promise<string>` - Blob URL or original URL if caching fails
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
|
|
347
|
-
### Calendar
|
|
348
|
-
|
|
349
|
-
#### `addCalendar(options)`
|
|
350
|
-
|
|
351
|
-
Add event to device calendar.
|
|
352
|
-
|
|
353
|
-
```typescript
|
|
354
|
-
import { addCalendar } from '@momo-cloud/gami-sdk';
|
|
85
|
+
Typical game loop calls:
|
|
355
86
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
```
|
|
87
|
+
- `getConfig()`
|
|
88
|
+
- `getBalance({ balanceId })`
|
|
89
|
+
- `getBalanceConfig()`
|
|
90
|
+
- `spin()`
|
|
91
|
+
- `shake()`
|
|
92
|
+
- `getCoinBalance()`
|
|
93
|
+
- `getCoinExchangeInfo()`
|
|
94
|
+
- `coinExchange({ amount })`
|
|
95
|
+
- `getMission({ viewId? })`
|
|
96
|
+
- `getLeaderboard({ boardId, group?, limit?, page? })`
|
|
97
|
+
- `getHistory({ page, limit })`
|
|
98
|
+
- `getEvent({ eventId })`
|
|
369
99
|
|
|
370
|
-
|
|
371
|
-
- `title` (string): Event title
|
|
372
|
-
- `startDate` (Date | number): Event start time
|
|
373
|
-
- `endDate` (Date | number): Event end time
|
|
374
|
-
- `notes` (string): Event notes
|
|
375
|
-
- `key` (string): Unique identifier for the event
|
|
376
|
-
- `alarm` (Date | number): Alarm/notification time
|
|
377
|
-
- `desc` (string): Event description
|
|
378
|
-
- `toast` (string, optional): Permission request message
|
|
100
|
+
### Phase C - Close or Navigate
|
|
379
101
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
102
|
+
- `await GamiSDK.endGame()` to close session
|
|
103
|
+
- `GamiSDK.closeApp()` to dismiss host view
|
|
104
|
+
- `GamiSDK.goHome()` to return to home
|
|
383
105
|
|
|
384
|
-
|
|
106
|
+
## 5) Platform Bridge APIs (Most Used)
|
|
385
107
|
|
|
386
|
-
The SDK exposes
|
|
108
|
+
The SDK exposes host-native actions directly:
|
|
387
109
|
|
|
388
|
-
|
|
110
|
+
- Feedback: `showToast`, `showAlert`
|
|
111
|
+
- Navigation: `openWeb`, `openURL`, `startRefId`
|
|
112
|
+
- Social: `shareExternal`, `shareFacebook`, `shareMessenger`
|
|
113
|
+
- Device: `scanQRCode`, `copyToClipBoard`, `getImage`, `saveImage`
|
|
114
|
+
- Permissions: `requestPermission`, `checkPermission`
|
|
115
|
+
- Contacts and notifications: `getContacts`, `registerNoti`, `unregisterNoti`
|
|
116
|
+
- Calendar: `saveCalendarEvent`
|
|
117
|
+
- Analytics: `trackingEvent`, `screenTracking`
|
|
118
|
+
- Host lifecycle: `onFocusApp`, `onBlurApp`, `listenShaking`
|
|
389
119
|
|
|
390
|
-
|
|
120
|
+
Example:
|
|
391
121
|
|
|
392
|
-
```
|
|
122
|
+
```ts
|
|
393
123
|
GamiSDK.showToast({
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
type: 'success' // 'success' | 'failure' | 'info'
|
|
398
|
-
});
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
**Parameters:**
|
|
402
|
-
- `title` (string, optional): Toast title
|
|
403
|
-
- `description` (string): Toast message
|
|
404
|
-
- `duration` (number): Display duration in milliseconds
|
|
405
|
-
- `type` (string, optional): Toast type
|
|
406
|
-
|
|
407
|
-
---
|
|
408
|
-
|
|
409
|
-
#### `showAlert(title, message, buttons?)`
|
|
410
|
-
|
|
411
|
-
Show a native alert dialog.
|
|
412
|
-
|
|
413
|
-
```typescript
|
|
414
|
-
GamiSDK.showAlert(
|
|
415
|
-
'Confirm Action',
|
|
416
|
-
'Are you sure you want to proceed?',
|
|
417
|
-
['Cancel', 'Confirm']
|
|
418
|
-
);
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
**Parameters:**
|
|
422
|
-
- `title` (string): Alert title
|
|
423
|
-
- `message` (string): Alert message
|
|
424
|
-
- `buttons` (string[], optional): Button labels (default: ['OK'])
|
|
425
|
-
|
|
426
|
-
---
|
|
427
|
-
|
|
428
|
-
#### `openWeb(options)`
|
|
429
|
-
|
|
430
|
-
Open a URL in an in-app webview.
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
|
-
GamiSDK.openWeb({
|
|
434
|
-
url: 'https://example.com/rules',
|
|
435
|
-
title: 'Game Rules'
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
// Or with custom HTML
|
|
439
|
-
GamiSDK.openWeb({
|
|
440
|
-
html: '<h1>Custom Content</h1>',
|
|
441
|
-
title: 'Info'
|
|
442
|
-
});
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
**Parameters:**
|
|
446
|
-
- `url` (string, optional): URL to open
|
|
447
|
-
- `html` (string, optional): Custom HTML content
|
|
448
|
-
- `title` (string, optional): Webview title
|
|
449
|
-
|
|
450
|
-
**Returns:** `Promise<boolean>`
|
|
451
|
-
|
|
452
|
-
---
|
|
453
|
-
|
|
454
|
-
#### `openURL(url)`
|
|
455
|
-
|
|
456
|
-
Open a URL in the device's external browser.
|
|
457
|
-
|
|
458
|
-
```typescript
|
|
459
|
-
GamiSDK.openURL('https://momo.vn');
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
**Parameters:**
|
|
463
|
-
- `url` (string): URL to open
|
|
464
|
-
|
|
465
|
-
**Returns:** `Promise<boolean>`
|
|
466
|
-
|
|
467
|
-
---
|
|
468
|
-
|
|
469
|
-
#### `shareExternal(options)`
|
|
470
|
-
|
|
471
|
-
Share content to external apps.
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
GamiSDK.shareExternal({
|
|
475
|
-
title: 'Check out this game!',
|
|
476
|
-
message: 'I just scored 1000 points!',
|
|
477
|
-
url: 'https://momo.vn/game',
|
|
478
|
-
subject: 'Game Score'
|
|
479
|
-
});
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
**Parameters:**
|
|
483
|
-
- `title` (string, optional): Share title
|
|
484
|
-
- `message` (string, optional): Share message
|
|
485
|
-
- `url` (string, optional): URL to share
|
|
486
|
-
- `subject` (string, optional): Email subject
|
|
487
|
-
|
|
488
|
-
**Returns:** `Promise<boolean>`
|
|
489
|
-
|
|
490
|
-
---
|
|
491
|
-
|
|
492
|
-
#### `copyToClipBoard(text, message?)`
|
|
493
|
-
|
|
494
|
-
Copy text to clipboard.
|
|
495
|
-
|
|
496
|
-
```typescript
|
|
497
|
-
GamiSDK.copyToClipBoard(
|
|
498
|
-
'PROMO2025',
|
|
499
|
-
'Promo code copied!'
|
|
500
|
-
);
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
**Parameters:**
|
|
504
|
-
- `text` (string): Text to copy
|
|
505
|
-
- `message` (string, optional): Success message to display
|
|
506
|
-
|
|
507
|
-
**Returns:** `Promise<boolean>`
|
|
508
|
-
|
|
509
|
-
---
|
|
510
|
-
|
|
511
|
-
#### `shareFacebook(url)` / `shareMessenger(url)`
|
|
512
|
-
|
|
513
|
-
Share URL to Facebook or Messenger (MoMo app only).
|
|
514
|
-
|
|
515
|
-
```typescript
|
|
516
|
-
GamiSDK.shareFacebook('https://momo.vn/game');
|
|
517
|
-
GamiSDK.shareMessenger('https://momo.vn/game');
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
**Parameters:**
|
|
521
|
-
- `url` (string): URL to share
|
|
522
|
-
|
|
523
|
-
**Returns:** `Promise<boolean>`
|
|
524
|
-
|
|
525
|
-
---
|
|
526
|
-
|
|
527
|
-
#### `requestPermission(permission)` / `checkPermission(permission)`
|
|
528
|
-
|
|
529
|
-
Request or check device permissions.
|
|
530
|
-
|
|
531
|
-
```typescript
|
|
532
|
-
// Request permission
|
|
533
|
-
const status = await GamiSDK.requestPermission('calendars');
|
|
534
|
-
console.log('Permission status:', status); // 'granted' | 'denied'
|
|
535
|
-
|
|
536
|
-
// Check existing permission
|
|
537
|
-
const hasPermission = await GamiSDK.checkPermission('contacts');
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
**Parameters:**
|
|
541
|
-
- `permission` (string): Permission type ('calendars', 'contacts', etc.)
|
|
542
|
-
|
|
543
|
-
**Returns:** `Promise<string>` - Permission status
|
|
544
|
-
|
|
545
|
-
---
|
|
546
|
-
|
|
547
|
-
#### `getContacts()`
|
|
548
|
-
|
|
549
|
-
Retrieve device contacts (MoMo app only, requires permission).
|
|
550
|
-
|
|
551
|
-
```typescript
|
|
552
|
-
const contacts = await GamiSDK.getContacts();
|
|
553
|
-
console.log('Contacts:', contacts);
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
**Returns:** `Promise<any[] | null>`
|
|
557
|
-
|
|
558
|
-
---
|
|
559
|
-
|
|
560
|
-
#### `saveCalendarEvent(options)`
|
|
561
|
-
|
|
562
|
-
Save an event to the device calendar (MoMo app only).
|
|
563
|
-
|
|
564
|
-
```typescript
|
|
565
|
-
const saved = await GamiSDK.saveCalendarEvent({
|
|
566
|
-
title: 'Daily Bonus Available',
|
|
567
|
-
startDate: new Date('2025-12-15T10:00:00'),
|
|
568
|
-
endDate: new Date('2025-12-15T12:00:00'),
|
|
569
|
-
notes: 'Come back to claim your daily bonus!',
|
|
570
|
-
key: 'daily_bonus_reminder',
|
|
571
|
-
alarm: new Date('2025-12-15T09:45:00'),
|
|
572
|
-
des: 'Daily bonus notification',
|
|
573
|
-
toast: 'Please grant calendar permission'
|
|
124
|
+
description: 'Reward claimed',
|
|
125
|
+
duration: 2000,
|
|
126
|
+
type: 'success',
|
|
574
127
|
});
|
|
575
128
|
|
|
576
|
-
console.log('Event saved:', saved);
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
**Parameters:**
|
|
580
|
-
- `title` (string): Event title
|
|
581
|
-
- `startDate` (Date | number): Event start time
|
|
582
|
-
- `endDate` (Date | number): Event end time
|
|
583
|
-
- `notes` (string): Event notes
|
|
584
|
-
- `key` (string): Storage key for event ID
|
|
585
|
-
- `alarm` (Date | number): Reminder time
|
|
586
|
-
- `des` (string): Event description
|
|
587
|
-
- `toast` (string, optional): Permission request message
|
|
588
|
-
|
|
589
|
-
**Returns:** `Promise<boolean>`
|
|
590
|
-
|
|
591
|
-
---
|
|
592
|
-
|
|
593
|
-
#### `trackingEvent(event, data)`
|
|
594
|
-
|
|
595
|
-
Track analytics events.
|
|
596
|
-
|
|
597
|
-
```typescript
|
|
598
|
-
GamiSDK.trackingEvent('level_completed', {
|
|
599
|
-
level: 5,
|
|
600
|
-
score: 1200,
|
|
601
|
-
duration: 45
|
|
602
|
-
});
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
**Parameters:**
|
|
606
|
-
- `event` (string): Event name
|
|
607
|
-
- `data` (object): Event data
|
|
608
|
-
|
|
609
|
-
---
|
|
610
|
-
|
|
611
|
-
#### `screenTracking(options)`
|
|
612
|
-
|
|
613
|
-
Track screen views and user actions.
|
|
614
|
-
|
|
615
|
-
```typescript
|
|
616
129
|
GamiSDK.screenTracking({
|
|
617
|
-
game_id: 'vn.momo.web.luckywheel',
|
|
618
130
|
event_name: 'screen_view',
|
|
619
|
-
action_name: '
|
|
620
|
-
screen_name: '
|
|
621
|
-
extra: { tab: 'weekly' },
|
|
622
|
-
error_code: 0
|
|
131
|
+
action_name: 'open_home',
|
|
132
|
+
screen_name: 'home',
|
|
623
133
|
});
|
|
624
134
|
```
|
|
625
135
|
|
|
626
|
-
|
|
627
|
-
- `game_id` (string, optional): Game identifier
|
|
628
|
-
- `event_name` (string): Event name
|
|
629
|
-
- `action_name` (string): Action identifier
|
|
630
|
-
- `screen_name` (string): Screen identifier
|
|
631
|
-
- `extra` (object, optional): Additional data
|
|
632
|
-
- `error_code` (number | string, optional): Error code if applicable
|
|
136
|
+
## 6) Shared Utilities
|
|
633
137
|
|
|
634
|
-
|
|
138
|
+
Named exports are available for common integration helpers:
|
|
635
139
|
|
|
636
|
-
|
|
140
|
+
- Storage: `saveItem`, `getItem`, `cacheFile`
|
|
141
|
+
- Calendar wrapper: `addCalendar`
|
|
142
|
+
- Time sync: `setServerTime`, `getServerTime`
|
|
143
|
+
- Event bus: `GameEvent`
|
|
144
|
+
- SSE support: from `features/sse/*`
|
|
145
|
+
- Types: from `features/types/*`
|
|
637
146
|
|
|
638
|
-
|
|
147
|
+
Example:
|
|
639
148
|
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
GamiSDK.startRefId({
|
|
643
|
-
refId: 'FEATURE_CODE_123',
|
|
644
|
-
refExtra: { source: 'game' }
|
|
645
|
-
});
|
|
149
|
+
```ts
|
|
150
|
+
import { saveItem, getItem, GameEvent } from '@momo-cloud/gami-sdk';
|
|
646
151
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
refId: 'https://example.com',
|
|
650
|
-
useWeb: true // Open in webview instead of external browser
|
|
651
|
-
});
|
|
152
|
+
await saveItem('progress', { level: 3, stars: 15 });
|
|
153
|
+
const progress = await getItem('progress');
|
|
652
154
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
refId: '/wallet/topup'
|
|
155
|
+
GameEvent.on('GAME_ON_BACK', () => {
|
|
156
|
+
// route back to your home screen
|
|
656
157
|
});
|
|
657
158
|
```
|
|
658
159
|
|
|
659
|
-
|
|
660
|
-
- `refId` (string): Feature code, URL, or internal path
|
|
661
|
-
- `refExtra` (object | string, optional): Additional parameters
|
|
662
|
-
- `useWeb` (boolean, optional): Open URLs in webview (default: false)
|
|
663
|
-
|
|
664
|
-
---
|
|
665
|
-
|
|
666
|
-
#### `listenShaking(options)`
|
|
160
|
+
## 7) Error Handling Pattern
|
|
667
161
|
|
|
668
|
-
|
|
162
|
+
Most game APIs return payloads with `response_info`.
|
|
163
|
+
Treat non-zero error codes as business failures:
|
|
669
164
|
|
|
670
|
-
```
|
|
671
|
-
GamiSDK.
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
});
|
|
165
|
+
```ts
|
|
166
|
+
const res = await GamiSDK.spin();
|
|
167
|
+
if (res?.response_info?.error_code !== 0) {
|
|
168
|
+
GamiSDK.showToast({
|
|
169
|
+
description: res?.response_info?.error_message || 'Spin failed',
|
|
170
|
+
duration: 2000,
|
|
171
|
+
type: 'failure',
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
678
175
|
```
|
|
679
176
|
|
|
680
|
-
|
|
681
|
-
- `onShake` (function): Callback when device is shaken
|
|
177
|
+
## 8) Browser vs Host Runtime
|
|
682
178
|
|
|
683
|
-
|
|
179
|
+
- In MoMo host: full platform bridge behavior is available.
|
|
180
|
+
- In browser/dev mode: host-only behavior may be mocked, no-op, or limited.
|
|
684
181
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
#### `onFocusApp(callback)` / `onBlurApp(callback)`
|
|
182
|
+
Integration recommendation:
|
|
688
183
|
|
|
689
|
-
|
|
184
|
+
- Keep game domain flow independent from host-only APIs.
|
|
185
|
+
- Wrap optional host actions with graceful fallback in browser.
|
|
690
186
|
|
|
691
|
-
|
|
692
|
-
GamiSDK.onFocusApp(() => {
|
|
693
|
-
console.log('App is now active');
|
|
694
|
-
resumeGame();
|
|
695
|
-
});
|
|
696
|
-
|
|
697
|
-
GamiSDK.onBlurApp(() => {
|
|
698
|
-
console.log('App went to background');
|
|
699
|
-
pauseGame();
|
|
700
|
-
});
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
**Parameters:**
|
|
704
|
-
- `callback` (function): Function to call on focus/blur
|
|
705
|
-
|
|
706
|
-
---
|
|
187
|
+
## 9) Build and Publish Modes
|
|
707
188
|
|
|
708
|
-
|
|
189
|
+
This repository supports two build modes:
|
|
709
190
|
|
|
710
|
-
|
|
191
|
+
- Internal build (`src/index.ts`): full internal integration surface
|
|
192
|
+
- Public build (`src/index.public.ts`): limited/public package output
|
|
711
193
|
|
|
712
|
-
|
|
713
|
-
// Register
|
|
714
|
-
GamiSDK.registerNoti((data) => {
|
|
715
|
-
console.log('Received notification:', data);
|
|
716
|
-
handleNotification(data);
|
|
717
|
-
});
|
|
194
|
+
Commands:
|
|
718
195
|
|
|
719
|
-
|
|
720
|
-
|
|
196
|
+
```bash
|
|
197
|
+
npm run build:internal
|
|
198
|
+
npm run build:public
|
|
721
199
|
```
|
|
722
200
|
|
|
723
|
-
|
|
724
|
-
- `callback` (function): Notification handler
|
|
725
|
-
|
|
726
|
-
---
|
|
727
|
-
|
|
728
|
-
### Utilities
|
|
201
|
+
Publish scripts:
|
|
729
202
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
```typescript
|
|
735
|
-
import { getServerTime } from '@momo-cloud/gami-sdk';
|
|
736
|
-
|
|
737
|
-
const serverTime = getServerTime();
|
|
738
|
-
console.log('Server time:', new Date(serverTime));
|
|
203
|
+
```bash
|
|
204
|
+
npm run publish:nexus
|
|
205
|
+
npm run publish:npm
|
|
206
|
+
npm run publish:all
|
|
739
207
|
```
|
|
740
208
|
|
|
741
|
-
|
|
209
|
+
## 10) Integration Checklist
|
|
742
210
|
|
|
743
|
-
|
|
211
|
+
Before releasing a game integration:
|
|
744
212
|
|
|
745
|
-
|
|
213
|
+
- `init()` and `wait()` executed exactly once on app bootstrap
|
|
214
|
+
- `startGame()` called on session start, `endGame()` on session end
|
|
215
|
+
- Critical APIs wrapped with business error handling (`response_info`)
|
|
216
|
+
- Host-only APIs guarded for browser fallback
|
|
217
|
+
- Analytics events wired (`trackingEvent` and `screenTracking`)
|
|
218
|
+
- Local cache keys use SDK helpers to avoid collisions
|
|
219
|
+
- Permissions flow validated for calendar/contacts/image capture
|
|
746
220
|
|
|
747
|
-
|
|
221
|
+
## 11) Minimal End-to-End Example
|
|
748
222
|
|
|
749
|
-
```
|
|
750
|
-
import
|
|
751
|
-
|
|
752
|
-
setServerTime(Date.now() + 1000); // 1 second ahead
|
|
753
|
-
```
|
|
754
|
-
|
|
755
|
-
**Parameters:**
|
|
756
|
-
- `time` (number): Server timestamp in milliseconds
|
|
757
|
-
|
|
758
|
-
---
|
|
759
|
-
|
|
760
|
-
### Properties
|
|
223
|
+
```ts
|
|
224
|
+
import GamiSDK from '@momo-cloud/gami-sdk';
|
|
225
|
+
import appJson from '../app.json';
|
|
761
226
|
|
|
762
|
-
|
|
227
|
+
export async function run() {
|
|
228
|
+
await GamiSDK.init({
|
|
229
|
+
appjson: JSON.stringify(appJson),
|
|
230
|
+
gameId: import.meta.env.VITE_DEFAULT_GAME_ID,
|
|
231
|
+
userId: import.meta.env.VITE_USER_ID,
|
|
232
|
+
});
|
|
233
|
+
await GamiSDK.wait();
|
|
763
234
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
console.log(GamiSDK.userId); // string: User ID
|
|
767
|
-
console.log(GamiSDK.userInfo); // IUserInfo: Full user profile
|
|
768
|
-
console.log(GamiSDK.token); // string: Auth token
|
|
769
|
-
console.log(GamiSDK.gameId); // string: Current game ID
|
|
235
|
+
await GamiSDK.startGame();
|
|
236
|
+
const spinResult = await GamiSDK.spin();
|
|
770
237
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
console.log(GamiSDK.feature); // any: Platform features
|
|
775
|
-
```
|
|
238
|
+
if (spinResult?.response_info?.error_code === 0) {
|
|
239
|
+
GamiSDK.showToast({ description: 'Spin success', duration: 1500, type: 'success' });
|
|
240
|
+
}
|
|
776
241
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
interface IUserInfo {
|
|
780
|
-
id: string;
|
|
781
|
-
name: string;
|
|
782
|
-
phone?: string;
|
|
783
|
-
avatar?: string;
|
|
784
|
-
// ... additional fields
|
|
242
|
+
// Later when leaving the game:
|
|
243
|
+
await GamiSDK.endGame();
|
|
785
244
|
}
|
|
786
245
|
```
|
|
787
246
|
|
|
788
|
-
##
|
|
247
|
+
## 12) Notes for Architects
|
|
789
248
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
↓
|
|
795
|
-
Get user profile and authentication
|
|
796
|
-
↓
|
|
797
|
-
Backend: login(appToken) → Exchange for gameToken
|
|
798
|
-
↓
|
|
799
|
-
Backend: getProfile(gameToken) → Get game profile
|
|
800
|
-
↓
|
|
801
|
-
Backend: getBalance(gameToken) → Get balances
|
|
802
|
-
↓
|
|
803
|
-
Backend: getServerTime() → Sync time
|
|
804
|
-
↓
|
|
805
|
-
Game Ready ✓
|
|
806
|
-
```
|
|
807
|
-
|
|
808
|
-
## 🌐 Platform Support
|
|
809
|
-
|
|
810
|
-
| Feature | Browser | MoMo App | Notes |
|
|
811
|
-
|---------|---------|----------|-------|
|
|
812
|
-
| Authentication | ✅ | ✅ | Mock in browser |
|
|
813
|
-
| Storage | ✅ | ✅ | localStorage / native |
|
|
814
|
-
| Calendar | ❌ | ✅ | Native only |
|
|
815
|
-
| Notifications | ❌ | ✅ | Native only |
|
|
816
|
-
| Game APIs | ✅ | ✅ | Full support |
|
|
817
|
-
|
|
818
|
-
## ⚙️ Configuration
|
|
819
|
-
|
|
820
|
-
### TypeScript
|
|
821
|
-
|
|
822
|
-
The SDK is written in TypeScript and includes type definitions:
|
|
823
|
-
|
|
824
|
-
```typescript
|
|
825
|
-
import GamiSDK, {
|
|
826
|
-
TBalance,
|
|
827
|
-
TGetExchangeResponse,
|
|
828
|
-
TLeaderboard,
|
|
829
|
-
TPostExchangeResponse,
|
|
830
|
-
IUserInfo
|
|
831
|
-
} from '@momo-cloud/gami-sdk';
|
|
832
|
-
|
|
833
|
-
const balance: TBalance = await GamiSDK.getBalance({
|
|
834
|
-
balanceIds: ['coin']
|
|
835
|
-
});
|
|
249
|
+
- Keep `GamiSDK` behind your own `sdkService` adapter in app code.
|
|
250
|
+
- Separate domain logic from host bridge calls for easier testability.
|
|
251
|
+
- Use typed DTOs from `features/types` in application boundaries.
|
|
252
|
+
- Favor centralized error normalization to avoid duplicated checks.
|
|
836
253
|
|
|
837
|
-
|
|
838
|
-
const posted: TPostExchangeResponse = await GamiSDK.coinExchange({ amount: 100 });
|
|
839
|
-
```
|
|
840
|
-
|
|
841
|
-
### Vite Integration
|
|
842
|
-
|
|
843
|
-
```typescript
|
|
844
|
-
// vite.config.ts
|
|
845
|
-
import { defineConfig } from 'vite';
|
|
846
|
-
|
|
847
|
-
export default defineConfig({
|
|
848
|
-
resolve: {
|
|
849
|
-
alias: {
|
|
850
|
-
'@sdk': '@momo-cloud/gami-sdk'
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
});
|
|
854
|
-
```
|
|
855
|
-
|
|
856
|
-
## 📄 License
|
|
254
|
+
---
|
|
857
255
|
|
|
858
|
-
UNLICENSED
|
|
256
|
+
License: `UNLICENSED`
|