@retion-miniapp/sdk 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +74 -170
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -1,237 +1,141 @@
|
|
|
1
1
|
# @retion-miniapp/sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Mini app là web của bạn, được app tải về máy và mở full màn hình trong 1 conversation. SDK làm 3 việc:
|
|
6
|
-
|
|
7
|
-
1. Cho biết mini app đang mở ở **page nào, khách nào** → `getRouteParams()`
|
|
8
|
-
2. Đưa **token** để gọi API backend của bạn → `getAccessToken()`
|
|
9
|
-
3. **Đóng** mini app → `closeApp()`
|
|
10
|
-
|
|
11
|
-
Cùng 1 source chạy được **cả trong app lẫn trên web** (trình duyệt / iframe cũ) — ngoài app, SDK đọc thông tin từ query URL như hiện tại.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
## 1. Cài đặt
|
|
3
|
+
SDK cho web mini app chạy trong app Retion.
|
|
16
4
|
|
|
17
5
|
```bash
|
|
18
6
|
npm i @retion-miniapp/sdk
|
|
19
|
-
npm i -D @retion-miniapp/cli # build + deploy lên Retion
|
|
20
|
-
npx retion-miniapp init --app-id <app_id> --name "<Tên mini app>"
|
|
21
7
|
```
|
|
22
8
|
|
|
23
|
-
`app_id` lấy trên [Retion Mini App Console](https://retion-miniapp-platform.vercel.app/console) (trùng `app_id` của widget, vd `66515701a2be1c03411c5723`).
|
|
24
|
-
|
|
25
|
-
## 2. Cấu hình project
|
|
26
|
-
|
|
27
|
-
Mini app chạy từ file trên máy (không phải từ domain của bạn), nên cần 3 chỉnh nhỏ:
|
|
28
|
-
|
|
29
|
-
**a. Đường dẫn asset tương đối** — `vite.config.ts`:
|
|
30
|
-
|
|
31
9
|
```ts
|
|
32
|
-
|
|
33
|
-
base: './',
|
|
34
|
-
});
|
|
10
|
+
import { getRouteParams, getToken, closeApp } from '@retion-miniapp/sdk';
|
|
35
11
|
```
|
|
36
12
|
|
|
37
|
-
|
|
13
|
+
---
|
|
38
14
|
|
|
39
|
-
|
|
40
|
-
createRouter({ history: createWebHashHistory(), routes });
|
|
41
|
-
```
|
|
15
|
+
## `getRouteParams()`
|
|
42
16
|
|
|
43
|
-
|
|
17
|
+
Thông tin conversation đang mở mini app. Đồng bộ, gọi lúc nào cũng được.
|
|
44
18
|
|
|
45
19
|
```ts
|
|
46
|
-
const
|
|
20
|
+
const { org_id, page_id, client_id } = getRouteParams();
|
|
47
21
|
```
|
|
48
22
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
/** Page + khách của conversation đang mở. */
|
|
59
|
-
export const conversation = getRouteParams();
|
|
60
|
-
|
|
61
|
-
/** HTTP client gọi backend của bạn — tự gắn token của page hiện tại. */
|
|
62
|
-
export const api = axios.create({ baseURL: import.meta.env.VITE_API_URL });
|
|
63
|
-
|
|
64
|
-
api.interceptors.request.use(async (config) => {
|
|
65
|
-
config.headers.Authorization = `Bearer ${await getAccessToken()}`;
|
|
66
|
-
return config;
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
/** Đóng mini app. Chạy trên web thì quay lại trang trước. */
|
|
70
|
-
export const close = () => (isInHost() ? closeApp() : history.back());
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
> Dùng `fetch` thay axios: gọi `await getAccessToken()` rồi gắn vào header y hệt.
|
|
23
|
+
| Field | Kiểu | Ý nghĩa |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `org_id` | `string` | Tổ chức đang dùng app |
|
|
26
|
+
| `page_id` | `string` | Page (kênh) của conversation |
|
|
27
|
+
| `client_id` | `string` | Khách hàng của conversation |
|
|
28
|
+
| `message_id` | `string?` | Tin nhắn mở mini app (nếu mở từ 1 tin cụ thể) |
|
|
29
|
+
| `comment_id` | `string?` | Bình luận mở mini app (nếu mở từ bình luận) |
|
|
30
|
+
| `locale` | `string` | Ngôn ngữ app: `vi`, `en`, `id`, `lo`, `km`, `th`, `tl`, `zh` |
|
|
31
|
+
| `is_page_admin` | `1 \| 0` | `1` nếu người dùng là admin của page |
|
|
74
32
|
|
|
75
|
-
|
|
33
|
+
Ngoài app (trình duyệt / iframe cũ): đọc từ query URL.
|
|
76
34
|
|
|
77
|
-
|
|
78
|
-
<script setup lang="ts">
|
|
79
|
-
import { ref, onMounted } from 'vue';
|
|
80
|
-
import { api, conversation, close } from '@/lib/miniapp';
|
|
35
|
+
---
|
|
81
36
|
|
|
82
|
-
|
|
37
|
+
## `getToken(type)`
|
|
83
38
|
|
|
84
|
-
|
|
85
|
-
const { data } = await api.get('/orders', { params: { client_id: conversation.client_id } });
|
|
86
|
-
orders.value = data;
|
|
87
|
-
});
|
|
88
|
-
</script>
|
|
39
|
+
Lấy token để gọi API. Luôn trả token **của page đang mở**. Gọi mỗi lần cần, không lưu lại.
|
|
89
40
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<h1>Đơn hàng</h1>
|
|
93
|
-
</header>
|
|
94
|
-
<OrderList :orders="orders" />
|
|
95
|
-
<button @click="close">Xong</button>
|
|
96
|
-
</template>
|
|
41
|
+
```ts
|
|
42
|
+
const token = await getToken('chatbox');
|
|
97
43
|
```
|
|
98
44
|
|
|
99
|
-
|
|
45
|
+
| `type` | Token | Cần quyền |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `'access'` | Token của mini app cho page hiện tại | Không |
|
|
48
|
+
| `'partner'` | Token đối tác của page | `partner_token` |
|
|
49
|
+
| `'chatbox'` | Token đăng nhập của người dùng app (toàn quyền như người dùng) | `chatbox_token` |
|
|
100
50
|
|
|
101
|
-
|
|
51
|
+
Quyền do admin bật trên Console (app → *Quyền lấy token*). Chưa bật → lỗi `PERMISSION_DENIED`.
|
|
102
52
|
|
|
103
|
-
|
|
53
|
+
Ngoài app: đọc `?access_token=` / `?partner_token=` / `?chatbox_token=` trên URL.
|
|
104
54
|
|
|
105
|
-
|
|
106
|
-
http://localhost:5173/?page_id=100179064765476&client_id=8385907281421695&access_token=<token test>
|
|
107
|
-
```
|
|
55
|
+
> `getAccessToken()` = `getToken('access')`.
|
|
108
56
|
|
|
109
|
-
|
|
57
|
+
---
|
|
110
58
|
|
|
111
|
-
##
|
|
59
|
+
## `closeApp()`
|
|
112
60
|
|
|
113
|
-
|
|
114
|
-
export RETION_MINIAPP_API=https://retion-miniapp-platform.vercel.app/api
|
|
115
|
-
export RETION_MINIAPP_TOKEN=<deploy token của app — lấy trên Console>
|
|
61
|
+
Đóng mini app, quay về màn chat.
|
|
116
62
|
|
|
117
|
-
|
|
63
|
+
```ts
|
|
64
|
+
await closeApp();
|
|
118
65
|
```
|
|
119
66
|
|
|
120
|
-
|
|
67
|
+
Ngoài app → lỗi `NOT_IN_HOST`.
|
|
121
68
|
|
|
122
69
|
---
|
|
123
70
|
|
|
124
|
-
##
|
|
71
|
+
## `isInHost()`
|
|
125
72
|
|
|
126
|
-
|
|
127
|
-
|---|---|---|---|
|
|
128
|
-
| `getRouteParams()` | `RouteParams` | Thông tin app truyền vào | Đọc query URL |
|
|
129
|
-
| `getAccessToken()` | `Promise<string>` | Token của page hiện tại (app tự refresh) | Đọc `?access_token=` — không có thì lỗi `NOT_IN_HOST` |
|
|
130
|
-
| `getToken(type)` | `Promise<string>` | `'access'` \| `'partner'` \| `'chatbox'` — `partner` / `chatbox` cần quyền (xem dưới) | Đọc `?access_token=` / `?partner_token=` / `?chatbox_token=` |
|
|
131
|
-
| `onContextChange(cb)` | `() => void` | Gọi `cb` khi chuyển page / conversation (không nạp lại trang) | Không bao giờ gọi |
|
|
132
|
-
| `closeApp()` | `Promise<void>` | Đóng mini app | Lỗi `NOT_IN_HOST` |
|
|
133
|
-
| `onAppPaused(cb)` / `onAppResumed(cb)` | `() => void` | Mini app bị đóng (chạy ngầm) / mở lại | Không bao giờ gọi |
|
|
134
|
-
| `isInHost()` | `boolean` | `true` | `false` |
|
|
135
|
-
|
|
136
|
-
`RouteParams`: `page_id`, `client_id`, `org_id`, `message_id`, `comment_id`, `locale`, `is_page_admin`.
|
|
137
|
-
|
|
138
|
-
### Lỗi
|
|
139
|
-
|
|
140
|
-
Mọi lỗi là `MiniAppError` có `code`:
|
|
141
|
-
|
|
142
|
-
| `code` | Khi nào |
|
|
143
|
-
|---|---|
|
|
144
|
-
| `NOT_IN_HOST` | Chạy ngoài app mà gọi hàm cần app |
|
|
145
|
-
| `PERMISSION_DENIED` | Mini app chưa được cấp quyền lấy token đó |
|
|
146
|
-
| `TIMEOUT` | App không phản hồi sau 15s |
|
|
147
|
-
| `NOT_SUPPORTED` | Phiên bản app hiện tại chưa hỗ trợ |
|
|
148
|
-
| `HOST_ERROR` | Lỗi khác phía app (vd page chưa có token) |
|
|
73
|
+
`true` nếu đang chạy trong app Retion, `false` nếu trên trình duyệt / iframe cũ.
|
|
149
74
|
|
|
150
75
|
```ts
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
await getAccessToken();
|
|
155
|
-
} catch (e) {
|
|
156
|
-
if (e instanceof MiniAppError && e.code === ErrorCode.HOST_ERROR) showLoginAgain();
|
|
157
|
-
}
|
|
76
|
+
if (isInHost()) await closeApp();
|
|
77
|
+
else history.back();
|
|
158
78
|
```
|
|
159
79
|
|
|
160
|
-
|
|
80
|
+
---
|
|
161
81
|
|
|
162
|
-
|
|
163
|
-
|---|---|---|
|
|
164
|
-
| `access` | Token của mini app cho page hiện tại | Không — luôn được |
|
|
165
|
-
| `partner` | Token đối tác của page | `partner_token` |
|
|
166
|
-
| `chatbox` | Token đăng nhập của người dùng app (**toàn quyền** như người dùng) | `chatbox_token` |
|
|
82
|
+
## `onContextChange(callback)`
|
|
167
83
|
|
|
168
|
-
|
|
84
|
+
Mini app được mở ở page / conversation khác **mà không nạp lại trang**. Gọi lại API ở đây.
|
|
169
85
|
|
|
170
86
|
```ts
|
|
171
|
-
|
|
87
|
+
onContextChange((next, prev) => load());
|
|
172
88
|
```
|
|
173
89
|
|
|
174
|
-
|
|
90
|
+
| Tham số | Ý nghĩa |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `next` | `RouteParams` mới (như `getRouteParams()`) |
|
|
93
|
+
| `prev` | `RouteParams` trước đó |
|
|
175
94
|
|
|
176
|
-
|
|
95
|
+
Chỉ xảy ra khi `miniapp.config.json` có `"context_update": true`. Mặc định (`false`) app tự nạp lại trang → không cần hàm này. Đổi tổ chức luôn nạp lại trang.
|
|
177
96
|
|
|
178
|
-
|
|
97
|
+
Trả về hàm huỷ lắng nghe.
|
|
179
98
|
|
|
180
|
-
|
|
99
|
+
---
|
|
181
100
|
|
|
182
|
-
|
|
183
|
-
onMounted(load); // đã có
|
|
184
|
-
onContextChange(load); // đổi page / conversation → chạy lại load()
|
|
185
|
-
```
|
|
101
|
+
## `onAppPaused(callback)` / `onAppResumed(callback)`
|
|
186
102
|
|
|
187
|
-
|
|
103
|
+
Mini app bị đóng (vẫn chạy ngầm) / được mở lại.
|
|
188
104
|
|
|
189
105
|
```ts
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const load = async () => {
|
|
193
|
-
controller?.abort(); // huỷ request của page trước
|
|
194
|
-
controller = new AbortController();
|
|
195
|
-
const { page_id } = getRouteParams();
|
|
196
|
-
const res = await fetch(`${API}/pages/${page_id}`, {
|
|
197
|
-
headers: { Authorization: await getToken('access') },
|
|
198
|
-
signal: controller.signal,
|
|
199
|
-
});
|
|
200
|
-
// ...
|
|
201
|
-
};
|
|
106
|
+
onAppPaused(() => clearInterval(timer)); // dừng polling, video...
|
|
107
|
+
onAppResumed(() => load()); // làm mới dữ liệu
|
|
202
108
|
```
|
|
203
109
|
|
|
204
|
-
|
|
110
|
+
Trả về hàm huỷ lắng nghe.
|
|
205
111
|
|
|
206
|
-
|
|
112
|
+
---
|
|
207
113
|
|
|
208
|
-
|
|
209
|
-
onAppPaused(() => clearInterval(pollTimer));
|
|
210
|
-
onAppResumed(() => load());
|
|
211
|
-
```
|
|
114
|
+
## Lỗi
|
|
212
115
|
|
|
213
|
-
|
|
116
|
+
Mọi lỗi là `MiniAppError`, có `code` và `method`.
|
|
214
117
|
|
|
215
|
-
|
|
216
|
-
|
|
118
|
+
```ts
|
|
119
|
+
import { MiniAppError, ErrorCode } from '@retion-miniapp/sdk';
|
|
217
120
|
|
|
218
|
-
|
|
219
|
-
|
|
121
|
+
try {
|
|
122
|
+
await getToken('chatbox');
|
|
123
|
+
} catch (e) {
|
|
124
|
+
if (e instanceof MiniAppError && e.code === ErrorCode.PERMISSION_DENIED) { /* chưa được cấp quyền */ }
|
|
125
|
+
}
|
|
126
|
+
```
|
|
220
127
|
|
|
221
|
-
|
|
222
|
-
|
|
128
|
+
| `code` | Khi nào |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `NOT_IN_HOST` | Chạy ngoài app mà gọi hàm cần app |
|
|
131
|
+
| `PERMISSION_DENIED` | Chưa được cấp quyền lấy token đó |
|
|
132
|
+
| `TIMEOUT` | App không phản hồi sau 15s |
|
|
133
|
+
| `NOT_SUPPORTED` | Phiên bản app hiện tại chưa hỗ trợ |
|
|
134
|
+
| `HOST_ERROR` | Lỗi khác phía app (vd page chưa có token) |
|
|
223
135
|
|
|
224
136
|
---
|
|
225
137
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
```bash
|
|
229
|
-
npm i
|
|
230
|
-
npm run check # typecheck + lint + test + build + publint
|
|
231
|
-
npm run release:minor # ghi CHANGELOG mục "## Unreleased" trước
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Giao thức với app: xem chú thích đầu [`src/bridge.ts`](./src/bridge.ts).
|
|
138
|
+
Deploy lên app: [`@retion-miniapp/cli`](https://www.npmjs.com/package/@retion-miniapp/cli).
|
|
235
139
|
|
|
236
140
|
## License
|
|
237
141
|
|
package/dist/index.cjs
CHANGED
package/dist/index.js
CHANGED