@grabjs/superapp-sdk 2.0.0-beta.38 → 2.0.0-beta.47
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/api-reference/api.json +5649 -6906
- package/dist/index.d.ts +65 -73
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -1
- package/skills/SKILL.md +336 -76
package/skills/SKILL.md
CHANGED
|
@@ -42,10 +42,11 @@ import { isSuccess, isError } from '@grabjs/superapp-sdk';
|
|
|
42
42
|
|
|
43
43
|
#### CDN (UMD Bundle)
|
|
44
44
|
|
|
45
|
-
If you are not using a bundler, load the SDK from a CDN and access it via the `SuperAppSDK` global
|
|
45
|
+
If you are not using a bundler, load the SDK from a CDN and access it via the `SuperAppSDK` global.
|
|
46
|
+
**Always pin to a specific version** (e.g., `@x.y.z`) — omitting the version always fetches the latest release, which may contain breaking changes.
|
|
46
47
|
|
|
47
48
|
```html
|
|
48
|
-
<script src="https://cdn.jsdelivr.net/npm/@grabjs/superapp-sdk/dist/index.js"></script>
|
|
49
|
+
<script src="https://cdn.jsdelivr.net/npm/@grabjs/superapp-sdk@x.y.z/dist/index.js"></script>
|
|
49
50
|
<script>
|
|
50
51
|
const { ContainerModule, ScopeModule, isSuccess, isError } = window.SuperAppSDK;
|
|
51
52
|
</script>
|
|
@@ -61,25 +62,20 @@ SDK methods communicate with the native Grab SuperApp via JSBridge. They only wo
|
|
|
61
62
|
Every SDK method returns a bridge response object with an HTTP-style `status_code`. SDK methods never throw — use type guards instead of try/catch.
|
|
62
63
|
|
|
63
64
|
```typescript
|
|
64
|
-
import {
|
|
65
|
+
import { ProfileModule, isSuccess, isError } from '@grabjs/superapp-sdk';
|
|
65
66
|
|
|
66
|
-
const
|
|
67
|
-
const response = await
|
|
67
|
+
const profile = new ProfileModule();
|
|
68
|
+
const response = await profile.fetchEmail();
|
|
68
69
|
|
|
69
70
|
if (isSuccess(response)) {
|
|
70
|
-
|
|
71
|
-
case 200:
|
|
72
|
-
console.log('QR Code scanned:', response.result.qrCode);
|
|
73
|
-
break;
|
|
74
|
-
case 204:
|
|
75
|
-
// operation completed with no content
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
71
|
+
console.log('Result:', response.result);
|
|
78
72
|
} else if (isError(response)) {
|
|
79
|
-
// response.error: string is guaranteed
|
|
80
73
|
switch (response.status_code) {
|
|
81
74
|
case 403:
|
|
82
|
-
// call IdentityModule.authorize() then ScopeModule.reloadScopes()
|
|
75
|
+
// Missing OAuth scope - call IdentityModule.authorize() then ScopeModule.reloadScopes()
|
|
76
|
+
break;
|
|
77
|
+
case 426:
|
|
78
|
+
// Grab app version too old - prompt user to update their app
|
|
83
79
|
break;
|
|
84
80
|
default:
|
|
85
81
|
console.error(`Error ${response.status_code}: ${response.error}`);
|
|
@@ -91,19 +87,19 @@ if (isSuccess(response)) {
|
|
|
91
87
|
|
|
92
88
|
The SDK uses HTTP-style status codes for all responses:
|
|
93
89
|
|
|
94
|
-
| Code | Type | Description
|
|
95
|
-
|
|
|
96
|
-
| `200` | OK | Request successful, `result` contains response data
|
|
97
|
-
| `204` | No Content | Request successful, no data returned
|
|
98
|
-
| `302` | Redirect | Redirect in progress
|
|
99
|
-
| `400` | Bad Request | Invalid request parameters
|
|
100
|
-
| `401` | Unauthorized | Authentication required
|
|
101
|
-
| `403` | Forbidden | Insufficient
|
|
102
|
-
| `404` | Not Found | Resource not found
|
|
103
|
-
| `424` | Failed Dependency | Underlying native request failed
|
|
104
|
-
| `426` | Upgrade Required |
|
|
105
|
-
| `500` | Internal Error | Unexpected SDK error
|
|
106
|
-
| `501` | Not Implemented |
|
|
90
|
+
| Code | Type | Description |
|
|
91
|
+
| :---- | :---------------- | :---------------------------------------------------------- |
|
|
92
|
+
| `200` | OK | Request successful, `result` contains response data |
|
|
93
|
+
| `204` | No Content | Request successful, no data returned |
|
|
94
|
+
| `302` | Redirect | Redirect in progress |
|
|
95
|
+
| `400` | Bad Request | Invalid request parameters |
|
|
96
|
+
| `401` | Unauthorized | Authentication required |
|
|
97
|
+
| `403` | Forbidden | Insufficient permission (see `@requiredOAuthScope` tag) |
|
|
98
|
+
| `404` | Not Found | Resource not found |
|
|
99
|
+
| `424` | Failed Dependency | Underlying native request failed |
|
|
100
|
+
| `426` | Upgrade Required | Grab app version too old (see `@minimumGrabAppVersion` tag) |
|
|
101
|
+
| `500` | Internal Error | Unexpected SDK error |
|
|
102
|
+
| `501` | Not Implemented | Outside Grab SuperApp environment |
|
|
107
103
|
|
|
108
104
|
### Type Guards
|
|
109
105
|
|
|
@@ -156,44 +152,159 @@ subscription.unsubscribe();
|
|
|
156
152
|
|
|
157
153
|
You can also `await` a stream method directly to get its first value.
|
|
158
154
|
|
|
155
|
+
### Scopes and Permissions
|
|
156
|
+
|
|
157
|
+
The SDK categorizes permissions into two distinct types based on their execution context:
|
|
158
|
+
|
|
159
|
+
#### Permission Types
|
|
160
|
+
|
|
161
|
+
- **Backend Scopes** (`openid`, `profile.read`, `phone`)
|
|
162
|
+
- **Purpose**: Access protected resources and user data via your server.
|
|
163
|
+
- **Flow**: Requires a backend token exchange after authorization to retrieve data.
|
|
164
|
+
- **Mobile Scopes** (`mobile.geolocation`, `mobile.checkout`)
|
|
165
|
+
- **Purpose**: Access native device capabilities directly within the MiniApp.
|
|
166
|
+
- **Flow**: Grants in-app permission immediately; no backend exchange is necessary.
|
|
167
|
+
|
|
168
|
+
#### Authorization Patterns
|
|
169
|
+
|
|
170
|
+
When designing your MiniApp, you can choose between two common patterns for requesting scopes:
|
|
171
|
+
|
|
172
|
+
- **Upfront Authorization**
|
|
173
|
+
- Request all required scopes during app initialisation, typically alongside backend sign-in.
|
|
174
|
+
- _Best for_: Core permissions essential for the app to function.
|
|
175
|
+
- **Deferred Authorization**
|
|
176
|
+
- Request scopes only when the user triggers a specific feature that requires them.
|
|
177
|
+
- _Best for_: Optional permissions (e.g., location) to improve user experience and build trust.
|
|
178
|
+
|
|
179
|
+
#### Permission Verification Strategies
|
|
180
|
+
|
|
181
|
+
You can verify permissions either proactively before calling a method, or reactively by handling errors.
|
|
182
|
+
|
|
183
|
+
##### Proactive Checking
|
|
184
|
+
|
|
185
|
+
Proactively verify if the current session has the necessary permissions for a method using `ScopeModule.hasAccessTo()`. This is recommended before calling gated methods, as users can revoke permissions at any time via the Grab app settings.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const scope = new ScopeModule();
|
|
189
|
+
const hasAccess = await scope.hasAccessTo('LocationModule', 'getCoordinate');
|
|
190
|
+
|
|
191
|
+
if (isSuccess(hasAccess) && hasAccess.result) {
|
|
192
|
+
// Permission is available, safe to call the method
|
|
193
|
+
const location = await location.getCoordinate();
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
##### Reactive Checking (Handling 403 Forbidden)
|
|
198
|
+
|
|
199
|
+
Methods tagged with `@requiredOAuthScope` require specific permissions. If the user hasn't granted the required scope, the method returns `403`. You must request authorization and reload scopes before retrying:
|
|
200
|
+
|
|
201
|
+
1. Call `IdentityModule.authorize()` to request the scope.
|
|
202
|
+
2. Call `ScopeModule.reloadScopes()` to refresh the SDK's internal permission state.
|
|
203
|
+
3. Retry the original method call.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import {
|
|
207
|
+
LocationModule,
|
|
208
|
+
IdentityModule,
|
|
209
|
+
ScopeModule,
|
|
210
|
+
isSuccess,
|
|
211
|
+
isError,
|
|
212
|
+
} from '@grabjs/superapp-sdk';
|
|
213
|
+
|
|
214
|
+
const location = new LocationModule();
|
|
215
|
+
const identity = new IdentityModule();
|
|
216
|
+
const scope = new ScopeModule();
|
|
217
|
+
|
|
218
|
+
const response = await location.getCoordinate();
|
|
219
|
+
|
|
220
|
+
if (isError(response) && response.status_code === 403) {
|
|
221
|
+
// 1. Request authorization for the required scope
|
|
222
|
+
const auth = await identity.authorize({
|
|
223
|
+
clientId: 'your-client-id',
|
|
224
|
+
redirectUri: 'https://your-app.com/callback',
|
|
225
|
+
scope: 'mobile.geolocation', // The scope defined in @requiredOAuthScope
|
|
226
|
+
environment: 'production',
|
|
227
|
+
responseMode: 'in_place',
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
if (isSuccess(auth)) {
|
|
231
|
+
// 2. Reload scopes so the new permission is available
|
|
232
|
+
await scope.reloadScopes();
|
|
233
|
+
|
|
234
|
+
// 3. Retry the original call
|
|
235
|
+
const retry = await location.getCoordinate();
|
|
236
|
+
if (isSuccess(retry)) {
|
|
237
|
+
console.log('Result:', retry.result);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
159
243
|
|
|
160
244
|
## Integration Guide
|
|
161
245
|
|
|
162
246
|
This guide covers the recommended setup for a MiniApp entry point — loading scopes, configuring the container UI, signalling readiness, and handling permissions.
|
|
163
247
|
|
|
164
|
-
|
|
248
|
+
> **Note:** The [demo](https://github.com/grab/superapp-sdk/tree/master/demo) folder contains two complete MiniApp samples demonstrating these integration patterns in action — one using CDN (vanilla HTML/JS) and one using React. Both implement the same user flow: OAuth authorization, user profile display, deferred location permissions, and checkout payment.
|
|
165
249
|
|
|
166
|
-
|
|
250
|
+
### Initialization
|
|
251
|
+
|
|
252
|
+
Follow these steps when your MiniApp launches to configure the container, authenticate the user, and track the entry event.
|
|
167
253
|
|
|
168
254
|
```typescript
|
|
169
|
-
import {
|
|
255
|
+
import {
|
|
256
|
+
ContainerModule,
|
|
257
|
+
ScopeModule,
|
|
258
|
+
ContainerAnalyticsEventState,
|
|
259
|
+
isSuccess,
|
|
260
|
+
} from '@grabjs/superapp-sdk';
|
|
170
261
|
|
|
171
262
|
const container = new ContainerModule();
|
|
172
263
|
const scope = new ScopeModule();
|
|
173
264
|
|
|
174
265
|
async function init() {
|
|
175
|
-
// 1.
|
|
176
|
-
await
|
|
266
|
+
// 1. Verify the environment
|
|
267
|
+
const connection = await container.isConnected();
|
|
268
|
+
if (!isSuccess(connection) || !connection.result?.connected) {
|
|
269
|
+
// Handle case where app is opened outside Grab SuperApp
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
177
272
|
|
|
178
273
|
// 2. Configure the container UI
|
|
179
274
|
await container.setTitle('My MiniApp');
|
|
180
275
|
await container.setBackgroundColor('#FFFFFF');
|
|
181
276
|
await container.hideBackButton();
|
|
277
|
+
await container.hideRefreshButton();
|
|
182
278
|
|
|
183
279
|
// 3. Dismiss the native loader
|
|
184
280
|
await container.hideLoader();
|
|
281
|
+
|
|
282
|
+
// 4. Track app launch
|
|
283
|
+
await container.sendAnalyticsEvent({
|
|
284
|
+
state: ContainerAnalyticsEventState.HOMEPAGE,
|
|
285
|
+
name: 'DEFAULT',
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// 5. Authenticate the user
|
|
289
|
+
// (Implementation detailed in the Authentication section below)
|
|
290
|
+
await signIn();
|
|
291
|
+
|
|
292
|
+
// 6. Load permission scopes — always do this before making module calls
|
|
293
|
+
await scope.reloadScopes();
|
|
185
294
|
}
|
|
186
295
|
|
|
187
296
|
init();
|
|
188
297
|
```
|
|
189
298
|
|
|
190
|
-
|
|
299
|
+
### Authentication
|
|
191
300
|
|
|
192
|
-
|
|
301
|
+
Trigger `IdentityModule.authorize()` to start the authorization process and request user permissions.
|
|
193
302
|
|
|
194
|
-
|
|
303
|
+
Once the user consents, retrieve the authorization artifacts (which include the `code`, `state`, `nonce`, and PKCE `codeVerifier`) via `IdentityModule.getAuthorizationArtifacts()`.
|
|
195
304
|
|
|
196
|
-
|
|
305
|
+
Forward these artifacts to your backend to exchange the token, validate the `id_token`, fetch user info, and establish the user's session.
|
|
306
|
+
|
|
307
|
+
After the session is established, call `IdentityModule.clearAuthorizationArtifacts()` and `ScopeModule.reloadScopes()` so your MiniApp can begin using the newly granted permissions.
|
|
197
308
|
|
|
198
309
|
```typescript
|
|
199
310
|
import { IdentityModule, ScopeModule, isSuccess, isError } from '@grabjs/superapp-sdk';
|
|
@@ -201,45 +312,79 @@ import { IdentityModule, ScopeModule, isSuccess, isError } from '@grabjs/superap
|
|
|
201
312
|
const identity = new IdentityModule();
|
|
202
313
|
const scope = new ScopeModule();
|
|
203
314
|
|
|
204
|
-
async function
|
|
315
|
+
async function signIn() {
|
|
205
316
|
const response = await identity.authorize({
|
|
206
317
|
clientId: 'your-client-id',
|
|
207
318
|
redirectUri: 'https://your-miniapp.example.com/callback',
|
|
208
|
-
scope: '
|
|
319
|
+
scope: 'openid profile.read phone mobile.storage',
|
|
209
320
|
environment: 'production',
|
|
321
|
+
responseMode: 'in_place',
|
|
210
322
|
});
|
|
211
323
|
|
|
212
324
|
if (isSuccess(response)) {
|
|
213
|
-
|
|
214
|
-
|
|
325
|
+
if (response.status_code === 200) {
|
|
326
|
+
// 1. Retrieve authorization artifacts
|
|
327
|
+
const artifacts = await identity.getAuthorizationArtifacts();
|
|
328
|
+
if (isSuccess(artifacts)) {
|
|
329
|
+
const { codeVerifier, nonce, redirectUri } = artifacts.result;
|
|
330
|
+
const { code } = response.result;
|
|
331
|
+
|
|
332
|
+
// 2. Send the artifacts to your backend for token exchange (see Backend Token Exchange section below)
|
|
333
|
+
// await myBackend.exchangeTokens({ code, codeVerifier, nonce, redirectUri });
|
|
334
|
+
|
|
335
|
+
// 3. Clear artifacts and reload scopes
|
|
336
|
+
await identity.clearAuthorizationArtifacts();
|
|
337
|
+
await scope.reloadScopes();
|
|
338
|
+
}
|
|
339
|
+
} else if (response.status_code === 204) {
|
|
340
|
+
// User cancelled the authorization flow
|
|
341
|
+
await identity.clearAuthorizationArtifacts();
|
|
342
|
+
}
|
|
215
343
|
} else if (isError(response)) {
|
|
216
344
|
console.error('Authorization failed:', response.error);
|
|
345
|
+
await identity.clearAuthorizationArtifacts();
|
|
217
346
|
}
|
|
218
347
|
}
|
|
219
348
|
```
|
|
220
349
|
|
|
221
|
-
### Navigation
|
|
350
|
+
### Container UI & Navigation
|
|
351
|
+
|
|
352
|
+
Control the native container's appearance and behavior to match your MiniApp's branding and navigation flow.
|
|
222
353
|
|
|
223
|
-
####
|
|
354
|
+
#### Title and Background
|
|
224
355
|
|
|
225
|
-
|
|
356
|
+
Set the title and background color for the native container.
|
|
226
357
|
|
|
227
358
|
```typescript
|
|
359
|
+
await container.setTitle('My MiniApp');
|
|
360
|
+
await container.setBackgroundColor('#FFFFFF');
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### Back and Refresh Buttons
|
|
364
|
+
|
|
365
|
+
Hide these buttons when your MiniApp manages its own navigation or requires a focused, non-refreshable view. Restore them when appropriate.
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
// Hide buttons
|
|
228
369
|
await container.hideBackButton();
|
|
370
|
+
await container.hideRefreshButton();
|
|
229
371
|
|
|
230
|
-
//
|
|
372
|
+
// Restore buttons
|
|
231
373
|
await container.showBackButton();
|
|
374
|
+
await container.showRefreshButton();
|
|
232
375
|
```
|
|
233
376
|
|
|
234
377
|
#### Closing the MiniApp
|
|
235
378
|
|
|
379
|
+
Programmatically close the MiniApp and return the user to the Grab SuperApp.
|
|
380
|
+
|
|
236
381
|
```typescript
|
|
237
382
|
await container.close();
|
|
238
383
|
```
|
|
239
384
|
|
|
240
|
-
|
|
385
|
+
### Opening External Links
|
|
241
386
|
|
|
242
|
-
Use `openExternalLink` to open URLs in the system browser instead of navigating away from the WebView
|
|
387
|
+
Use `ContainerModule.openExternalLink()` to open URLs in the system browser instead of navigating away from the MiniApp WebView.
|
|
243
388
|
|
|
244
389
|
```typescript
|
|
245
390
|
const response = await container.openExternalLink('https://example.com');
|
|
@@ -249,6 +394,121 @@ if (isError(response)) {
|
|
|
249
394
|
}
|
|
250
395
|
```
|
|
251
396
|
|
|
397
|
+
### Analytics Event Tracking
|
|
398
|
+
|
|
399
|
+
Track user interactions to monitor performance and conversion. Events are categorised by journey stage using `ContainerAnalyticsEventState`.
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import { ContainerModule, ContainerAnalyticsEventState, isSuccess } from '@grabjs/superapp-sdk';
|
|
403
|
+
|
|
404
|
+
const container = new ContainerModule();
|
|
405
|
+
|
|
406
|
+
// 1. System Event (DEFAULT)
|
|
407
|
+
// Send when a user lands on a key page
|
|
408
|
+
await container.sendAnalyticsEvent({
|
|
409
|
+
state: ContainerAnalyticsEventState.HOMEPAGE,
|
|
410
|
+
name: 'DEFAULT',
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// 2. Named Action (INITIATE / TRANSACT)
|
|
414
|
+
// Send when a user performs a primary action
|
|
415
|
+
await container.sendAnalyticsEvent({
|
|
416
|
+
state: ContainerAnalyticsEventState.HOMEPAGE,
|
|
417
|
+
name: 'INITIATE',
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// 3. Custom Interaction
|
|
421
|
+
// Send for specific interactions with additional metadata
|
|
422
|
+
await container.sendAnalyticsEvent({
|
|
423
|
+
state: ContainerAnalyticsEventState.CUSTOM,
|
|
424
|
+
name: 'BANNER_CLICK',
|
|
425
|
+
data: {
|
|
426
|
+
page: 'homepage',
|
|
427
|
+
banner_id: 'promo-summer-2024',
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Journey Stages
|
|
433
|
+
|
|
434
|
+
| State | Description |
|
|
435
|
+
| :----------------- | :----------------------------------------------- |
|
|
436
|
+
| `HOMEPAGE` | Entry point or main landing page. |
|
|
437
|
+
| `CHECKOUT_PAGE` | Transaction confirmation or payment selection. |
|
|
438
|
+
| `COMPLETION_POINT` | Post-transaction or success page. |
|
|
439
|
+
| `CUSTOM` | Any other interaction outside the standard flow. |
|
|
440
|
+
|
|
441
|
+
#### Best Practices
|
|
442
|
+
|
|
443
|
+
- Track system events automatically when users navigate to the corresponding pages.
|
|
444
|
+
- Always include required data fields for transaction events to enable accurate revenue tracking.
|
|
445
|
+
- Use descriptive names for custom events that clearly indicate the user action being tracked.
|
|
446
|
+
- Never include Personally Identifiable Information (PII) in event data.
|
|
447
|
+
|
|
448
|
+
### Checkout
|
|
449
|
+
|
|
450
|
+
The checkout flow is a two-step process: your backend first initializes a transaction using your partner credentials, then your frontend triggers the native payment interface using the response from your backend.
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
import {
|
|
454
|
+
CheckoutModule,
|
|
455
|
+
IdentityModule,
|
|
456
|
+
ScopeModule,
|
|
457
|
+
isSuccess,
|
|
458
|
+
isError,
|
|
459
|
+
} from '@grabjs/superapp-sdk';
|
|
460
|
+
|
|
461
|
+
const checkout = new CheckoutModule();
|
|
462
|
+
const identity = new IdentityModule();
|
|
463
|
+
const scope = new ScopeModule();
|
|
464
|
+
|
|
465
|
+
async function processPayment() {
|
|
466
|
+
// 1. Proactively check for checkout permission
|
|
467
|
+
const hasAccess = await scope.hasAccessTo('CheckoutModule', 'triggerCheckout');
|
|
468
|
+
|
|
469
|
+
if (!isSuccess(hasAccess) || !hasAccess.result) {
|
|
470
|
+
// Request authorization for mobile.checkout
|
|
471
|
+
// Note: mobile.checkout is a mobile scope; no backend exchange is needed for auth.
|
|
472
|
+
const authResponse = await identity.authorize({
|
|
473
|
+
clientId: 'your-client-id',
|
|
474
|
+
redirectUri: window.location.href,
|
|
475
|
+
scope: 'mobile.checkout',
|
|
476
|
+
environment: 'production',
|
|
477
|
+
responseMode: 'in_place',
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
if (isSuccess(authResponse) && authResponse.status_code === 200) {
|
|
481
|
+
await scope.reloadScopes();
|
|
482
|
+
} else {
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// 2. Fetch the initialized transaction payload from your backend
|
|
488
|
+
const response = await fetch('https://your-backend.example.com/init-transaction', {
|
|
489
|
+
method: 'POST',
|
|
490
|
+
headers: { 'Content-Type': 'application/json' },
|
|
491
|
+
body: JSON.stringify({ orderId: 'order-123' }),
|
|
492
|
+
});
|
|
493
|
+
const { partnerTxID, request, sessionID } = await response.json();
|
|
494
|
+
|
|
495
|
+
// 3. Trigger checkout with the backend response
|
|
496
|
+
const checkoutResult = await checkout.triggerCheckout({
|
|
497
|
+
partnerTxID,
|
|
498
|
+
request,
|
|
499
|
+
sessionID,
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
if (isSuccess(checkoutResult)) {
|
|
503
|
+
console.log(checkoutResult.result);
|
|
504
|
+
} else if (isError(checkoutResult)) {
|
|
505
|
+
console.error('Checkout error:', checkoutResult.error);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
For the complete API reference, see [GrabPay API](https://developer.grab.com/docs/partner-apps/pages/developer-resources/payment/) and [CheckoutModule](https://grab.github.io/superapp-sdk/classes/CheckoutModule.html).
|
|
511
|
+
|
|
252
512
|
|
|
253
513
|
## API Reference
|
|
254
514
|
|
|
@@ -260,25 +520,25 @@ JSBridge module for accessing the device camera.
|
|
|
260
520
|
|
|
261
521
|
#### `CheckoutModule`
|
|
262
522
|
JSBridge module for triggering native payment flows.
|
|
263
|
-
- `triggerCheckout(request: Record<string, unknown>): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: { status: "success"; transactionID: string } | { errorCode: string; errorMessage: string; status: "failure"; transactionID: string } | { status: "pending"; transactionID: string } | { status: "userInitiatedCancel" }; status_code: 200 }>` — Triggers the native checkout flow for payment processing.
|
|
523
|
+
- `triggerCheckout(request: Record<string, unknown>): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: { status: "success"; transactionID: string } | { errorCode: string; errorMessage: string; status: "failure"; transactionID: string } | { status: "pending"; transactionID: string } | { status: "userInitiatedCancel" }; status_code: 200 }>` — Triggers the native checkout flow for payment processing. (**OAuth Scope:** mobile.checkout)
|
|
264
524
|
|
|
265
525
|
#### `ContainerModule`
|
|
266
526
|
JSBridge module for controlling the WebView container.
|
|
267
|
-
- `close(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
527
|
+
- `close(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Close the container and return to the previous screen.
|
|
268
528
|
- `getSessionParams(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: string; status_code: 200 }>` — Get the session parameters from the container.
|
|
269
|
-
- `hideBackButton(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
270
|
-
- `hideLoader(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
271
|
-
- `hideRefreshButton(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
529
|
+
- `hideBackButton(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Hide the back button on the container header.
|
|
530
|
+
- `hideLoader(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Hide the full-screen loading indicator.
|
|
531
|
+
- `hideRefreshButton(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Hide the refresh button on the container header.
|
|
272
532
|
- `isConnected(): Promise<{ result: { connected: boolean }; status_code: 200 } | { error: string; status_code: 404 }>` — Check if the web app is connected to the Grab SuperApp via JSBridge.
|
|
273
533
|
- `onContentLoaded(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: boolean; status_code: 200 }>` — Notify the Grab SuperApp that the page content has loaded.
|
|
274
534
|
- `onCtaTap(request: string): Promise<{ error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: boolean; status_code: 200 }>` — Notify the client that the user has tapped a call-to-action (CTA).
|
|
275
|
-
- `openExternalLink(request: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
276
|
-
- `sendAnalyticsEvent(request: { data?: Record<string, unknown>; name: string; state: string }): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
277
|
-
- `setBackgroundColor(request: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
278
|
-
- `setTitle(request: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
279
|
-
- `showBackButton(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
280
|
-
- `showLoader(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
281
|
-
- `showRefreshButton(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }
|
|
535
|
+
- `openExternalLink(request: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Open a link in the external browser.
|
|
536
|
+
- `sendAnalyticsEvent(request: { data?: Record<string, unknown>; name: string; state: string }): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Use this method to track user interactions and page transitions.
|
|
537
|
+
- `setBackgroundColor(request: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Set the background color of the container header.
|
|
538
|
+
- `setTitle(request: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Set the title of the container header.
|
|
539
|
+
- `showBackButton(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Show the back button on the container header.
|
|
540
|
+
- `showLoader(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Show the full-screen loading indicator.
|
|
541
|
+
- `showRefreshButton(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 }>` — Show the refresh button on the container header.
|
|
282
542
|
|
|
283
543
|
#### `DeviceModule`
|
|
284
544
|
JSBridge module for querying native device information.
|
|
@@ -304,17 +564,17 @@ JSBridge module for accessing device locale settings.
|
|
|
304
564
|
|
|
305
565
|
#### `LocationModule`
|
|
306
566
|
JSBridge module for accessing device location services.
|
|
307
|
-
- `getCoordinate(): Promise<{ error: string; status_code: 403 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: { latitude: number; longitude: number }; status_code: 200 } | { error: string; status_code: 424 }>` — Get the current geographic coordinates of the device.
|
|
308
|
-
- `getCountryCode(): Promise<{ status_code: 204 } | { error: string; status_code: 403 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: string; status_code: 200 } | { error: string; status_code: 424 }>` — Get the country code based on the device's current location.
|
|
309
|
-
- `observeLocationChange(): ObserveLocationChangeResponse` — Subscribe to location change updates from the device.
|
|
567
|
+
- `getCoordinate(): Promise<{ error: string; status_code: 403 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: { latitude: number; longitude: number }; status_code: 200 } | { error: string; status_code: 424 }>` — Get the current geographic coordinates of the device. (**OAuth Scope:** mobile.geolocation)
|
|
568
|
+
- `getCountryCode(): Promise<{ status_code: 204 } | { error: string; status_code: 403 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { result: string; status_code: 200 } | { error: string; status_code: 424 }>` — Get the country code based on the device's current location. (**OAuth Scope:** mobile.geolocation)
|
|
569
|
+
- `observeLocationChange(): ObserveLocationChangeResponse` — Subscribe to location change updates from the device. (**OAuth Scope:** mobile.geolocation)
|
|
310
570
|
|
|
311
571
|
#### `Logger`
|
|
312
572
|
Provides scoped logging for SDK modules.
|
|
313
573
|
|
|
314
574
|
#### `MediaModule`
|
|
315
575
|
JSBridge module for playing DRM-protected media content.
|
|
316
|
-
- `observePlayDRMContent(data: DRMContentConfig): ObserveDRMPlaybackResponse` — Observes DRM-protected media content playback events.
|
|
317
|
-
- `playDRMContent(data: DRMContentConfig): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { length: number; position: number; titleId: string; type: "START_PLAYBACK" | "PROGRESS_PLAYBACK" | "START_SEEK" | "STOP_SEEK" | "STOP_PLAYBACK" | "CLOSE_PLAYBACK" | "PAUSE_PLAYBACK" | "RESUME_PLAYBACK" | "FAST_FORWARD_PLAYBACK" | "REWIND_PLAYBACK" | "ERROR_PLAYBACK" | "CHANGE_VOLUME" }; status_code: 200 }>` — Plays DRM-protected media content in the native media player.
|
|
576
|
+
- `observePlayDRMContent(data: DRMContentConfig): ObserveDRMPlaybackResponse` — Observes DRM-protected media content playback events. (**OAuth Scope:** mobile.media)
|
|
577
|
+
- `playDRMContent(data: DRMContentConfig): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { length: number; position: number; titleId: string; type: "START_PLAYBACK" | "PROGRESS_PLAYBACK" | "START_SEEK" | "STOP_SEEK" | "STOP_PLAYBACK" | "CLOSE_PLAYBACK" | "PAUSE_PLAYBACK" | "RESUME_PLAYBACK" | "FAST_FORWARD_PLAYBACK" | "REWIND_PLAYBACK" | "ERROR_PLAYBACK" | "CHANGE_VOLUME" }; status_code: 200 }>` — Plays DRM-protected media content in the native media player. (**OAuth Scope:** mobile.media)
|
|
318
578
|
|
|
319
579
|
#### `NetworkModule`
|
|
320
580
|
JSBridge module for making network requests via the native bridge.
|
|
@@ -327,8 +587,8 @@ This navigates back in the native navigation stack.
|
|
|
327
587
|
|
|
328
588
|
#### `ProfileModule`
|
|
329
589
|
JSBridge module for accessing user profile information.
|
|
330
|
-
- `fetchEmail(): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 403 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 426 } | { result: { email: string }; status_code: 200 }>` — Fetches the user's email address from their Grab profile.
|
|
331
|
-
- `verifyEmail(request?: { email?: string; skipUserInput?: boolean }): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 403 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 426 } | { result: { email: string }; status_code: 200 }>` — Verifies the user's email address by triggering email capture bottom sheet and OTP verification.
|
|
590
|
+
- `fetchEmail(): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 403 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 426 } | { result: { email: string }; status_code: 200 }>` — Fetches the user's email address from their Grab profile. (**OAuth Scope:** mobile.profile | **Minimum Grab App Version:** Android: 5.399.0, iOS: 5.399.0)
|
|
591
|
+
- `verifyEmail(request?: { email?: string; skipUserInput?: boolean }): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 403 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 426 } | { result: { email: string }; status_code: 200 }>` — Verifies the user's email address by triggering email capture bottom sheet and OTP verification. (**OAuth Scope:** mobile.profile | **Minimum Grab App Version:** Android: 5.399.0, iOS: 5.399.0)
|
|
332
592
|
|
|
333
593
|
#### `ScopeModule`
|
|
334
594
|
JSBridge module for checking and refreshing API access permissions.
|
|
@@ -342,16 +602,16 @@ JSBridge module for controlling the native splash / Lottie loading screen.
|
|
|
342
602
|
|
|
343
603
|
#### `StorageModule`
|
|
344
604
|
JSBridge module for persisting key-value data to native storage.
|
|
345
|
-
- `getBoolean(key: string): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { value: boolean | null }; status_code: 200 }>` — Retrieves a boolean value from the native storage.
|
|
346
|
-
- `getDouble(key: string): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { value: number | null }; status_code: 200 }>` — Retrieves a double (floating point) value from the native storage.
|
|
347
|
-
- `getInt(key: string): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { value: number | null }; status_code: 200 }>` — Retrieves an integer value from the native storage.
|
|
348
|
-
- `getString(key: string): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { value: string | null }; status_code: 200 }>` — Retrieves a string value from the native storage.
|
|
349
|
-
- `remove(key: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Removes a single value from the native storage by key.
|
|
350
|
-
- `removeAll(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Removes all values from the native storage.
|
|
351
|
-
- `setBoolean(key: string, value: boolean): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Stores a boolean value in the native storage.
|
|
352
|
-
- `setDouble(key: string, value: number): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Stores a double (floating point) value in the native storage.
|
|
353
|
-
- `setInt(key: string, value: number): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Stores an integer value in the native storage.
|
|
354
|
-
- `setString(key: string, value: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Stores a string value in the native storage.
|
|
605
|
+
- `getBoolean(key: string): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { value: boolean | null }; status_code: 200 }>` — Retrieves a boolean value from the native storage. (**OAuth Scope:** mobile.storage)
|
|
606
|
+
- `getDouble(key: string): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { value: number | null }; status_code: 200 }>` — Retrieves a double (floating point) value from the native storage. (**OAuth Scope:** mobile.storage)
|
|
607
|
+
- `getInt(key: string): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { value: number | null }; status_code: 200 }>` — Retrieves an integer value from the native storage. (**OAuth Scope:** mobile.storage)
|
|
608
|
+
- `getString(key: string): Promise<{ error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 } | { result: { value: string | null }; status_code: 200 }>` — Retrieves a string value from the native storage. (**OAuth Scope:** mobile.storage)
|
|
609
|
+
- `remove(key: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Removes a single value from the native storage by key. (**OAuth Scope:** mobile.storage)
|
|
610
|
+
- `removeAll(): Promise<{ status_code: 204 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Removes all values from the native storage. (**OAuth Scope:** mobile.storage)
|
|
611
|
+
- `setBoolean(key: string, value: boolean): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Stores a boolean value in the native storage. (**OAuth Scope:** mobile.storage)
|
|
612
|
+
- `setDouble(key: string, value: number): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Stores a double (floating point) value in the native storage. (**OAuth Scope:** mobile.storage)
|
|
613
|
+
- `setInt(key: string, value: number): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Stores an integer value in the native storage. (**OAuth Scope:** mobile.storage)
|
|
614
|
+
- `setString(key: string, value: string): Promise<{ status_code: 204 } | { error: string; status_code: 400 } | { error: string; status_code: 500 } | { error: string; status_code: 501 } | { error: string; status_code: 424 }>` — Stores a string value in the native storage. (**OAuth Scope:** mobile.storage)
|
|
355
615
|
|
|
356
616
|
#### `SystemWebViewKitModule`
|
|
357
617
|
JSBridge module for opening URLs in the device's system browser.
|