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