vc-cdp-rn-sdk 0.1.1 → 0.1.2

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 CHANGED
@@ -10,24 +10,28 @@ CDP SDK cho React Native — tracking events và user profiles cho hệ thống
10
10
  - [Khởi tạo](#khởi-tạo)
11
11
  - [Luồng hoạt động](#luồng-hoạt-động)
12
12
  - [User / Profile](#user--profile)
13
- - [Đăng nhập](#đăng-nhập-identifyuser)
14
- - [Cập nhật profile](#cập-nhật-profile-updateprofile)
15
- - [Lấy thông tin profile](#lấy-thông-tin-profile-getprofile)
16
- - [Trait setters](#trait-setters-chainable)
13
+ - [identifyUser](#identifyuser--đăng-nhập)
14
+ - [Trait Setters](#trait-setters--cập-nhật-từng-thuộc-tính)
15
+ - [updateProfile](#updateprofile--force-update)
16
+ - [getProfile](#getprofile--lấy-thông-tin-profile)
17
17
  - [Consents](#consents)
18
18
  - [Scores](#scores)
19
19
  - [Products](#products)
20
- - [Metadata](#metadata)
21
- - [Campaign / UTM](#campaign--utm)
22
- - [Push Token](#push-token)
23
- - [Đăng xuất](#đăng-xuất)
20
+ - [Metadata, Campaign, Push Token](#metadata-campaign-push-token)
21
+ - [getUserIdentities](#getuseridentities)
22
+ - [destroy_session](#destroy_session--đăng-xuất)
24
23
  - [Event Tracking](#event-tracking)
25
- - [Track sự kiện](#track-sự-kiện)
26
- - [Track màn hình](#track-màn-hình-screen)
27
- - [Flush thủ công](#flush-thủ-công)
28
- - [Lấy thông tin định danh](#lấy-thông-tin-định-danh)
24
+ - [track](#track--custom-event)
25
+ - [screen](#screen--màn-hình)
26
+ - [React Navigation](#tích-hợp-react-navigation)
27
+ - [Batch Processing](#batch-processing)
28
+ - [flush](#flush--gửi-ngay)
29
+ - [destroy](#destroy--dọn-dẹp)
29
30
  - [Bảo mật](#bảo-mật)
30
- - [Dependencies](#dependencies)
31
+ - [HMAC-SHA256](#hmac-sha256-signature)
32
+ - [AES-256-CBC](#aes-256-cbc-encryption)
33
+ - [Best Practices](#best-practices)
34
+ - [Tích hợp React Navigation đầy đủ](#tích-hợp-react-navigation-đầy-đủ)
31
35
  - [API Reference](#api-reference)
32
36
 
33
37
  ---
@@ -35,49 +39,92 @@ CDP SDK cho React Native — tracking events và user profiles cho hệ thống
35
39
  ## Cài đặt
36
40
 
37
41
  ```bash
38
- npm install vc-cdp-rn-sdk @react-native-async-storage/async-storage react-native-device-info crypto-js
42
+ npm install vc-cdp-rn-sdk
39
43
  ```
40
44
 
41
- ### iOS
45
+ ### Peer Dependencies
46
+
47
+ ```bash
48
+ npm install @react-native-async-storage/async-storage
49
+ npm install react-native-device-info
50
+ ```
51
+
52
+ #### iOS
42
53
 
43
54
  ```bash
44
55
  cd ios && pod install
45
56
  ```
46
57
 
47
- ### Android
58
+ #### Android
48
59
 
49
- Không cần bước cấu hình thêm.
60
+ Không cần thêm cấu hình nếu dùng React Native >= 0.68 (auto-linking).
50
61
 
51
62
  ---
52
63
 
53
64
  ## Khởi tạo
54
65
 
55
- Gọi `CdpRnSdk.create()` **một lần duy nhất** tại entry point của app (ví dụ `App.tsx`). SDK dùng `AsyncStorage` nên init là bất đồng bộ.
66
+ Khởi tạo SDK **một lần duy nhất** tại entry point của app. SDK dùng `AsyncStorage` nên init là **async**.
56
67
 
57
68
  ```typescript
69
+ // src/lib/cdp.ts
58
70
  import { CdpRnSdk } from 'vc-cdp-rn-sdk';
59
71
 
60
- let cdp: CdpRnSdk;
61
-
62
- export async function setupCdp() {
63
- cdp = await CdpRnSdk.create({
64
- apiKey: 'YOUR_API_KEY', // Bắt buộc — do CDP cấp
65
- secretKey: 'YOUR_SECRET_KEY', // Bắt buộc — dùng ký HMAC + mã hoá AES
66
- source: 'DOP', // Bắt buộc — kênh phát triển (khai báo với CDP trước)
67
- serviceName: 'DOP', // Bắt buộc — DOP | LOS | LMS
68
-
69
- // Tuỳ chọn
70
- baseUrl: 'https://stg-ingestlog.vietcredit.com.vn', // mặc định: staging
71
- isTest: true, // đánh dấu môi trường test
72
- debug: true, // in log ra console
73
- batchSize: 10, // gửi batch khi queue đủ n events (mặc định: 10)
74
- batchInterval: 5000, // hoặc sau n ms dù chưa đủ batch (mặc định: 5000)
75
- enableEncryption: true // mã hoá PII bằng AES-256-CBC (mặc định: true)
72
+ let _cdp: CdpRnSdk | null = null;
73
+
74
+ export async function initCdp(): Promise<CdpRnSdk> {
75
+ _cdp = await CdpRnSdk.create({
76
+ apiKey: 'YOUR_API_KEY',
77
+ secretKey: 'YOUR_SECRET_KEY',
78
+ source: 'DOP',
79
+ serviceName: 'VC_APP',
80
+ baseUrl: 'https://ingestlog.vietcredit.com.vn',
81
+ debug: __DEV__,
76
82
  });
83
+ return _cdp;
84
+ }
85
+
86
+ export function getCdp(): CdpRnSdk {
87
+ if (!_cdp) throw new Error('CDP SDK chưa được khởi tạo. Gọi initCdp() trước.');
88
+ return _cdp;
77
89
  }
78
90
  ```
79
91
 
80
- > **Lưu ý:** Các events được gọi trước khi `create()` hoàn thành sẽ được buffer lại và tự động gửi sau khi khởi tạo xong — không bị mất.
92
+ ```typescript
93
+ // App.tsx
94
+ import { useEffect } from 'react';
95
+ import { initCdp } from './lib/cdp';
96
+
97
+ export default function App() {
98
+ useEffect(() => {
99
+ initCdp();
100
+ }, []);
101
+ // ...
102
+ }
103
+ ```
104
+
105
+ > **Lưu ý:** Mỗi lần `CdpRnSdk.create()` được gọi, SDK tự động tạo `session_id` mới — tương ứng với 1 lần mở app.
106
+
107
+ ### Cấu hình đầy đủ
108
+
109
+ | Tham số | Kiểu | Mặc định | Mô tả |
110
+ |---|---|---|---|
111
+ | `apiKey` | `string` | — | **Bắt buộc.** API Key do CDP cấp |
112
+ | `secretKey` | `string` | — | **Bắt buộc.** Secret Key để ký HMAC và mã hoá AES |
113
+ | `source` | `string` | — | **Bắt buộc.** Kênh tích hợp (khai báo với CDP trước) |
114
+ | `serviceName` | `string` | — | **Bắt buộc.** `VC_APP` \| `DOP` \| `LOS` \| `LMS` |
115
+ | `baseUrl` | `string` | Staging URL | URL API CDP (không có `/v1`) |
116
+ | `isTest` | `boolean` | `false` | Đánh dấu môi trường test |
117
+ | `debug` | `boolean` | `false` | In log ra console |
118
+ | `batchSize` | `number` | `10` | Số events tối đa trong 1 batch |
119
+ | `batchInterval` | `number` | `5000` | Thời gian (ms) giữa các lần flush tự động |
120
+ | `enableEncryption` | `boolean` | `true` | Mã hoá PII bằng AES-256-CBC |
121
+
122
+ ### Môi trường
123
+
124
+ | Môi trường | baseUrl |
125
+ |---|---|
126
+ | Staging | `https://stg-ingestlog.vietcredit.com.vn` |
127
+ | Production | `https://ingestlog.vietcredit.com.vn` |
81
128
 
82
129
  ---
83
130
 
@@ -85,113 +132,77 @@ export async function setupCdp() {
85
132
 
86
133
  ```
87
134
  App khởi động
88
- └─> CdpRnSdk.create(config)
89
- │ - Tạo session_id mới (luôn fresh mỗi lần mở app)
90
- │ - Load anonymous_id, device_id từ AsyncStorage (persistent)
91
- │ - Load profile_id từ AsyncStorage (nếu đã login trước đó)
92
- │ - Thu thập device info (brand, model, OS)
135
+ └─> CdpRnSdk.create(config) ← async
136
+ │ - Load/tạo device_id → AsyncStorage (persist vĩnh viễn)
137
+ │ - Load/tạo anonymous_id AsyncStorage (persist vĩnh viễn)
138
+ │ - Tạo session_id mới → AsyncStorage (mỗi lần mở app)
139
+ │ - Load profile_id → AsyncStorage (nếu đã login)
93
140
 
94
141
  ├─ User CHƯA login
95
- │ └─> cdp.track(event)
142
+ │ └─> cdp.track() / cdp.screen()
96
143
  │ profile_id = null, gắn anonymous_id + device_id + session_id
97
- │ CDP sẽ identity-resolve khi user login sau
98
144
 
99
145
  └─ User login
100
146
  └─> cdp.identifyUser(userId, traits)
101
147
  └─> POST /v1/profiles/track → nhận profile_id
102
- └─> cdp.track(event)
103
- profile_id được gắn vào mọi event tiếp theo
148
+ └─> cdp.track() gắn profile_id vào mọi event
104
149
  ```
105
150
 
106
- **Session ID:** Mỗi lần mở app tạo một `session_id` mới. `device_id` và `anonymous_id` được giữ nguyên giữa các lần mở.
151
+ **Storage scope (AsyncStorage):**
152
+
153
+ | | Vòng đời |
154
+ |---|---|
155
+ | `device_id` | Vĩnh viễn (đến khi xoá app) |
156
+ | `anonymous_id` | Vĩnh viễn (rotate khi logout) |
157
+ | `session_id` | Tạo mới mỗi lần `create()` |
158
+ | `profile_id` | Xoá khi `destroy_session()` |
107
159
 
108
160
  ---
109
161
 
110
162
  ## User / Profile
111
163
 
112
- ### Đăng nhập (`identifyUser`)
113
-
114
- Gọi sau khi user đăng nhập thành công. SDK sẽ gọi `POST /v1/profiles/track`, nhận `profile_id` từ CDP và lưu vào storage để dùng cho các lần sau.
164
+ ### identifyUser — Đăng nhập
115
165
 
116
166
  ```typescript
117
- await cdp.identifyUser('user_123', {
118
- phone: '0901234567',
119
- full_name: 'Nguyễn Văn A',
120
- email: 'user@example.com',
121
- gender: 'M', // 'M' | 'F' | 'O'
122
- dob: '1990-01-15', // yyyy-MM-dd
123
- });
124
-
125
- // Tài khoản tổ chức (doanh nghiệp)
126
- await cdp.identifyUser('org_456', { full_name: 'Công ty ABC' }, 'ORG');
127
-
128
- // Alias ngắn gọn
129
- await cdp.identify('user_123', { phone: '0901234567' });
167
+ await cdp.identifyUser(userId, traits?, partyType?)
130
168
  ```
131
169
 
132
170
  | Tham số | Kiểu | Bắt buộc | Mô tả |
133
171
  |---|---|---|---|
134
- | `userId` | `string` | ✓ | Mã user trong hệ thống |
172
+ | `userId` | `string` | ✓ | Mã user trong hệ thống nội bộ |
135
173
  | `traits` | `UserTraits` | | Thông tin nhân khẩu học |
136
174
  | `partyType` | `'PERSON' \| 'ORG'` | | Mặc định `'PERSON'` |
137
175
 
138
- ---
139
-
140
- ### Cập nhật profile (`updateProfile`)
141
-
142
- Force-update profile theo `profile_id` — không trigger identity resolution. Dùng khi cần cập nhật một số trường cụ thể sau khi đã có `profile_id`.
143
-
144
- Gọi `POST /v1/profiles/update`.
145
-
146
176
  ```typescript
147
- await cdp.updateProfile({
148
- traits: {
149
- kyc_status: 'verified',
150
- mps_verified: true,
151
- },
152
- consents: [
153
- { consent_type: 'marketing_email', status: true, consent_date: '2025-06-01' },
154
- ],
177
+ // Cá nhân
178
+ await cdp.identifyUser('user_001', {
179
+ phone: '0901234567',
180
+ email: 'user@example.com',
181
+ full_name: 'Nguyễn Văn A',
182
+ gender: 'M',
183
+ dob: '1990-01-15',
184
+ idcard: '012345678901',
185
+ address: '123 Lê Lợi, Q.1, TP.HCM',
186
+ occupation: 'Kỹ sư phần mềm',
187
+ nationality: 'Vietnamese',
188
+ marital_status: 'single',
155
189
  });
156
190
 
157
- // Cập nhật profile_id khác (không phải profile hiện tại)
158
- await cdp.updateProfile({
159
- profile_id: 'cdp_profile_xyz',
160
- traits: { occupation: 'Kỹ sư' },
161
- });
162
- ```
163
-
164
- | Tham số | Kiểu | Mô tả |
165
- |---|---|---|
166
- | `profile_id` | `string` | Mặc định lấy profile_id hiện tại |
167
- | `traits` | `UserTraits` | Các trường cần update |
168
- | `consents` | `ConsentInfo[]` | Danh sách consent |
169
- | `scores` | `ScoreInfo[]` | Danh sách điểm |
170
- | `products` | `ProductInfo[]` | Danh sách sản phẩm |
171
- | `metadata` | `object` | Thông tin bổ sung |
172
-
173
- ---
174
-
175
- ### Lấy thông tin profile (`getProfile`)
176
-
177
- Gọi `GET /v1/profiles/get/{profile_id}`.
178
-
179
- ```typescript
180
- // Lấy profile hiện tại
181
- const profile = await cdp.getProfile();
182
-
183
- // Hoặc chỉ định profile_id cụ thể
184
- const profile = await cdp.getProfile('cdp_profile_xyz');
191
+ console.log(cdp.getProfileId()); // "177671850461184000"
185
192
 
186
- console.log(profile?.traits);
187
- console.log(profile?.consents);
193
+ // Tổ chức
194
+ await cdp.identifyUser('org_001', {
195
+ full_name: 'Công ty TNHH ABC',
196
+ phone: '0281234567',
197
+ email: 'contact@abc.vn',
198
+ }, 'ORG');
188
199
  ```
189
200
 
190
201
  ---
191
202
 
192
- ### Trait setters (chainable)
203
+ ### Trait Setters — Cập nhật từng thuộc tính
193
204
 
194
- Cập nhật từng thuộc tính riêng lẻ. Mỗi setter sẽ tự gọi `POST /v1/profiles/track` với `type: update`. Các setter có thể nối chuỗi.
205
+ Chainable. Mỗi setter gọi ngay `POST /v1/profiles/track` (type: update).
195
206
 
196
207
  ```typescript
197
208
  cdp
@@ -201,352 +212,615 @@ cdp
201
212
  .add_email('user@example.com')
202
213
  .add_mobile('0901234567')
203
214
  .add_gender('M')
204
- .add_birthday(new Date('1990-01-15')) // hoặc '1990-01-15'
215
+ .add_birthday('1990-01-15')
205
216
  .add_idcard('012345678901')
206
217
  .add_old_idcard('123456789')
207
- .add_address('123 Đường ABC, Nội')
218
+ .add_address('123 Lợi, Q.1, TP.HCM')
208
219
  .add_occupation('Kỹ sư phần mềm')
209
220
  .add_nationality('Vietnamese')
210
221
  .add_marital_status('single')
211
222
  .add_religion('Buddhist')
212
- .add_zalo_id('zalo_123')
213
- .add_tiktok_id('tiktok_abc');
214
-
215
- // Cập nhật nhiều thuộc tính cùng lúc
216
- cdp.add_user_attributes({
217
- occupation: 'Kỹ sư',
218
- nationality: 'Vietnamese',
219
- mps_verified: true,
220
- });
221
-
222
- // Cập nhật một thuộc tính tùy chỉnh
223
- cdp.add_user_attribute('custom_field', 'value');
223
+ .add_zalo_id('zalo_user_001')
224
+ .add_tiktok_id('tiktok_user_001');
224
225
  ```
225
226
 
226
- **Danh sách trait setters:**
227
-
228
- | Method | Trường trong `traits` | Ghi chú |
227
+ | Method | Trường | Ghi chú |
229
228
  |---|---|---|
230
229
  | `add_first_name(v)` | `first_name` | |
231
230
  | `add_last_name(v)` | `last_name` | |
232
231
  | `add_user_name(v)` | `full_name` | |
233
- | `add_email(v)` | `email` | PII — tự động mã hoá |
234
- | `add_mobile(v)` | `phone` | PII — tự động mã hoá |
232
+ | `add_email(v)` | `email` | 🔒 PII |
233
+ | `add_mobile(v)` | `phone` | 🔒 PII |
235
234
  | `add_gender(v)` | `gender` | `'M'` \| `'F'` \| `'O'` |
236
235
  | `add_birthday(v)` | `dob` | `Date` hoặc `'yyyy-MM-dd'` |
237
- | `add_idcard(v)` | `idcard` | PII — tự động mã hoá |
238
- | `add_old_idcard(v)` | `old_idcard` | PII — tự động mã hoá |
239
- | `add_address(v)` | `address` | PII — tự động mã hoá |
236
+ | `add_idcard(v)` | `idcard` | 🔒 PII |
237
+ | `add_old_idcard(v)` | `old_idcard` | 🔒 PII |
238
+ | `add_address(v)` | `address` | 🔒 PII |
240
239
  | `add_occupation(v)` | `occupation` | |
241
240
  | `add_nationality(v)` | `nationality` | |
242
241
  | `add_marital_status(v)` | `marital_status` | |
243
- | `add_religion(v)` | `religion` | PII — tự động mã hoá |
242
+ | `add_religion(v)` | `religion` | 🔒 PII |
244
243
  | `add_zalo_id(v)` | `zalo_id` | |
245
244
  | `add_tiktok_id(v)` | `tiktok_id` | |
246
- | `add_user_attribute(name, val)` | tùy chỉnh | |
247
- | `add_user_attributes(obj)` | nhiều trường | |
248
- | `setUserAttributes(obj)` | nhiều trường | alias |
245
+ | `add_user_attribute(name, val)` | custom | Bất kỳ trường nào |
246
+ | `add_user_attributes(obj)` | nhiều trường | Batch update |
247
+ | `setUserAttributes(obj)` | nhiều trường | Alias |
249
248
 
250
249
  ---
251
250
 
252
- ### Consents
251
+ ### updateProfile — Force-update
253
252
 
254
- Lưu danh sách consent của user. Sẽ được đính kèm vào mọi lần gọi profile sau đó.
253
+ ```typescript
254
+ await cdp.updateProfile({
255
+ traits: { kyc_status: 'verified', mps_verified: true },
256
+ consents: [{ consent_type: 'marketing_sms', status: true, consent_date: '2025-06-01' }],
257
+ });
258
+ ```
259
+
260
+ ---
261
+
262
+ ### getProfile — Lấy thông tin profile
263
+
264
+ ```typescript
265
+ const profile = await cdp.getProfile();
266
+ const profile = await cdp.getProfile('cdp_123');
267
+ ```
268
+
269
+ ---
270
+
271
+ ### Consents
255
272
 
256
273
  ```typescript
257
274
  cdp.setConsents([
258
- {
259
- consent_type: 'marketing_sms',
260
- status: true,
261
- consent_date: '2025-06-01',
262
- },
263
- {
264
- consent_type: 'marketing_email',
265
- status: false,
266
- },
275
+ { consent_type: 'marketing_sms', status: true, consent_date: '2025-06-01' },
276
+ { consent_type: 'marketing_email', status: false },
277
+ { consent_type: 'data_sharing', status: true },
267
278
  ]);
268
279
  ```
269
280
 
270
- | Trường | Kiểu | Bắt buộc | Mô tả |
271
- |---|---|---|---|
272
- | `consent_type` | `string` | ✓ | Loại consent |
273
- | `status` | `boolean` | ✓ | `true` = đồng ý |
274
- | `consent_date` | `string` | | Ngày đồng ý |
275
-
276
281
  ---
277
282
 
278
283
  ### Scores
279
284
 
280
- Lưu điểm tín dụng / rủi ro của user.
281
-
282
285
  ```typescript
283
286
  cdp.setScores([
284
- {
285
- score_type: 'credit_score',
286
- score_value: 720,
287
- score_date: '2025-06-01',
288
- },
289
- {
290
- score_type: 'risk_score',
291
- score_value: 85,
292
- },
287
+ { score_type: 'credit_score', score_value: 720, score_date: '2025-06-01' },
293
288
  ]);
294
289
  ```
295
290
 
296
- | Trường | Kiểu | Bắt buộc | Mô tả |
297
- |---|---|---|---|
298
- | `score_type` | `string` | ✓ | Loại điểm |
299
- | `score_value` | `number` | ✓ | Giá trị |
300
- | `score_date` | `string` | | Ngày tính điểm |
301
-
302
291
  ---
303
292
 
304
293
  ### Products
305
294
 
306
- Lưu danh sách sản phẩm mà user đang sử dụng.
307
-
308
295
  ```typescript
309
296
  cdp.setProducts([
310
- {
311
- product_code: 'LOAN_CONSUMER',
312
- product_name: 'Vay tiêu dùng',
313
- status: 'active',
314
- },
315
- {
316
- product_code: 'CREDIT_CARD',
317
- product_name: 'Thẻ tín dụng',
318
- status: 'pending',
319
- },
297
+ { product_code: 'LOAN_CONSUMER', product_name: 'Vay tiêu dùng', status: 'active' },
320
298
  ]);
321
299
  ```
322
300
 
323
- | Trường | Kiểu | Bắt buộc | Mô tả |
324
- |---|---|---|---|
325
- | `product_code` | `string` | ✓ | Mã sản phẩm |
326
- | `product_name` | `string` | | Tên sản phẩm |
327
- | `status` | `string` | | Trạng thái |
328
-
329
301
  ---
330
302
 
331
- ### Metadata
332
-
333
- Gắn thêm thông tin tùy chỉnh vào tất cả các payload (profile + event).
303
+ ### Metadata, Campaign, Push Token
334
304
 
335
305
  ```typescript
336
- cdp.setMetadata({
337
- schema_version: '2.0',
338
- app_flavor: 'production',
339
- referral_code: 'REF123',
340
- });
341
- ```
342
-
343
- ---
344
-
345
- ### Campaign / UTM
306
+ cdp.setMetadata({ schema_version: '2.0', referral_code: 'REF123' });
346
307
 
347
- Gắn thông tin chiến dịch vào tất cả event và profile call tiếp theo.
348
-
349
- ```typescript
350
308
  cdp.setCampaign({
351
- utm_source: 'facebook',
352
- utm_medium: 'cpc',
309
+ utm_source: 'facebook',
310
+ utm_medium: 'cpc',
353
311
  utm_campaign: 'summer_loan_2025',
354
- utm_term: 'vay_tieu_dung',
355
- utm_content: 'banner_top',
356
312
  });
313
+
314
+ // Push notification token (Expo hoặc FCM)
315
+ cdp.setPushToken('ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]');
316
+ cdp.setPushToken('fcm_token_abc123...');
357
317
  ```
358
318
 
359
319
  ---
360
320
 
361
- ### Push Token
362
-
363
- Lưu FCM token (Android) hoặc APNs token (iOS) để CDP dùng cho push notification.
321
+ ### getUserIdentities
364
322
 
365
323
  ```typescript
366
- cdp.setPushToken('FCM_OR_APNS_TOKEN_HERE');
324
+ const ids = cdp.getUserIdentities();
325
+ // {
326
+ // user_id: 'user_001',
327
+ // profile_id: '177671850461184000',
328
+ // anonymous_id: 'uuid-...',
329
+ // device_id: 'uuid-...',
330
+ // session_id: 'uuid-...',
331
+ // }
332
+
333
+ cdp.getProfileId();
334
+ cdp.getDeviceId();
335
+ cdp.getSessionId();
336
+ cdp.getUserAttribute('phone');
367
337
  ```
368
338
 
369
339
  ---
370
340
 
371
- ### Đăng xuất
372
-
373
- Gửi event logout, xoá toàn bộ dữ liệu user, rotate `anonymous_id` và `session_id`. `device_id` được giữ lại.
341
+ ### destroy_session — Đăng xuất
374
342
 
375
343
  ```typescript
376
344
  await cdp.destroy_session();
377
-
378
- // Alias
345
+ // hoặc alias:
379
346
  await cdp.reset();
380
347
  ```
381
348
 
382
- **Sau khi logout:**
383
- - `userId`, `profileId`, `userTraits`, `consents`, `scores`, `products`, `campaign` → xoá
384
- - `anonymousId`, `sessionId` tạo mới
385
- - `deviceId` giữ nguyên
349
+ | | Hành động |
350
+ |---|---|
351
+ | `userId`, `profileId`, `userTraits` | Xoá |
352
+ | `consents`, `scores`, `products`, `campaign` | Xoá |
353
+ | `anonymous_id` | Tạo mới, lưu AsyncStorage |
354
+ | `session_id` | Tạo mới, lưu AsyncStorage |
355
+ | `device_id` | **Giữ nguyên** |
386
356
 
387
357
  ---
388
358
 
389
359
  ## Event Tracking
390
360
 
391
- ### Track sự kiện
361
+ ### track Custom event
392
362
 
393
363
  ```typescript
394
- cdp.track('button_click', {
395
- button_name: 'apply_loan',
396
- screen: 'HomeScreen',
397
- loan_amount: 50_000_000,
398
- });
399
-
400
- cdp.track('form_submit', {
401
- form_name: 'personal_info',
402
- step: 2,
403
- duration_seconds: 45,
404
- });
364
+ cdp.track(eventName, properties?)
405
365
  ```
406
366
 
407
- > **Batching:** Events được gom theo `batchSize` hoặc `batchInterval`, sau đó gửi một lần qua `POST /v2/events/batch`. Nếu `batchSize = 1`, mỗi event được gửi ngay qua `POST /v2/events/track`.
367
+ ```typescript
368
+ cdp.track('loan_viewed', {
369
+ loan_id: 'LOAN_001',
370
+ amount: 50_000_000,
371
+ term: 24,
372
+ type: 'consumer',
373
+ });
408
374
 
409
- Mỗi event tự động bao gồm:
375
+ cdp.track('button_click', { button: 'apply_now', screen: 'LoanDetailScreen' });
410
376
 
411
- | Trường | Nguồn |
412
- |---|---|
413
- | `event_id` | UUID mới mỗi event |
414
- | `session_id` | Session hiện tại |
415
- | `device_id` | Persistent per device |
416
- | `profile_id` | Từ CDP sau khi login |
417
- | `anonymous_id` | Persistent, rotate khi logout |
418
- | `platforms` | Từ `react-native-device-info` |
419
- | `context.locale` | Ngôn ngữ thiết bị |
420
- | `context.timezone` | Múi giờ thiết bị |
421
- | `context.app.version` | Phiên bản app |
422
- | `event_time` | ISO 8601 tại thời điểm gọi |
377
+ cdp.track('form_submitted', { form: 'loan_application', success: true });
378
+ ```
423
379
 
424
380
  ---
425
381
 
426
- ### Track màn hình (`screen`)
382
+ ### screen Màn hình
383
+
384
+ Track lượt xem màn hình. Gửi event `screen_view`.
427
385
 
428
386
  ```typescript
429
387
  cdp.screen('HomeScreen');
430
-
431
- cdp.screen('LoanDetailScreen', {
432
- loan_id: 'LOAN_001',
433
- loan_amount: 50_000_000,
434
- });
388
+ cdp.screen('LoanDetailScreen', { loan_code: 'CONSUMER_24M' });
389
+ cdp.screen('ProfileScreen', { tab: 'personal_info' });
435
390
  ```
436
391
 
437
- Tương đương với:
392
+ ---
393
+
394
+ ### Tích hợp React Navigation
395
+
438
396
  ```typescript
439
- cdp.track('screen_view', { screen_name: 'HomeScreen', ...properties });
397
+ // App.tsx
398
+ import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
399
+ import { getCdp } from './lib/cdp';
400
+
401
+ export default function App() {
402
+ const navRef = useNavigationContainerRef();
403
+
404
+ return (
405
+ <NavigationContainer
406
+ ref={navRef}
407
+ onReady={() => {
408
+ const name = navRef.getCurrentRoute()?.name;
409
+ if (name) getCdp().screen(name);
410
+ }}
411
+ onStateChange={() => {
412
+ const name = navRef.getCurrentRoute()?.name;
413
+ if (name) getCdp().screen(name);
414
+ }}
415
+ >
416
+ {/* Stack / Tab / Drawer navigators */}
417
+ </NavigationContainer>
418
+ );
419
+ }
440
420
  ```
441
421
 
442
422
  ---
443
423
 
444
- ### Flush thủ công
424
+ ### Batch Processing
425
+
426
+ Events được gom vào **queue** rồi flush theo điều kiện:
427
+
428
+ | Điều kiện | Mô tả |
429
+ |---|---|
430
+ | `batchSize` đạt ngưỡng | Mặc định 10 events |
431
+ | `batchInterval` timeout | Mặc định 5 000 ms |
432
+ | `flush()` gọi thủ công | Ngay lập tức |
433
+ | App vào background | Gọi `flush()` trước `AppState` change |
434
+
435
+ ---
445
436
 
446
- Gửi toàn bộ events đang trong queue ngay lập tức. Hữu ích khi app sắp vào background hoặc trước khi đăng xuất.
437
+ ### flush Gửi ngay
447
438
 
448
439
  ```typescript
449
440
  await cdp.flush();
450
441
  ```
451
442
 
452
- ---
443
+ ```typescript
444
+ // Flush khi app vào background
445
+ import { AppState } from 'react-native';
453
446
 
454
- ## Lấy thông tin định danh
447
+ AppState.addEventListener('change', async (state) => {
448
+ if (state === 'background') {
449
+ await cdp.flush();
450
+ }
451
+ });
452
+ ```
455
453
 
456
- ```typescript
457
- const ids = cdp.getUserIdentities();
458
- // {
459
- // user_id: 'user_123',
460
- // profile_id: 'cdp_abc123',
461
- // anonymous_id: 'uuid-...',
462
- // device_id: 'uuid-...',
463
- // session_id: 'uuid-...',
464
- // }
454
+ ---
465
455
 
466
- cdp.getProfileId(); // profile_id từ CDP
467
- cdp.getDeviceId(); // persistent device identifier
468
- cdp.getSessionId(); // session hiện tại (mới mỗi lần mở app)
456
+ ### destroy Dọn dẹp
469
457
 
470
- cdp.getUserAttribute('phone'); // lấy giá trị trait cụ thể
458
+ ```typescript
459
+ await cdp.destroy();
471
460
  ```
472
461
 
462
+ Flush queue còn lại và xoá timer. Không xoá user data.
463
+
473
464
  ---
474
465
 
475
466
  ## Bảo mật
476
467
 
477
- ### HMAC Signature
468
+ ### HMAC-SHA256 Signature
469
+
470
+ Mọi request đến CDP API đều được ký tự động.
478
471
 
479
- Mọi request tự động được ký. Header `X-Signatures` được tính theo công thức:
472
+ **Headers:**
480
473
 
481
474
  ```
482
- X-Signatures = Base64(HMAC-SHA256(source + "|" + timestamp + "|" + payload))
475
+ X-Api-Key: <apiKey>
476
+ X-Source: <source>
477
+ X-Timestamp: <unix_timestamp_s>
478
+ X-Signatures: <base64_signature>
479
+ X-Trace-Id: <uuid>
480
+ Content-Type: application/json
481
+ isTest: false
483
482
  ```
484
483
 
485
- ### Mã hoá PII (AES-256-CBC)
484
+ **Công thức:**
486
485
 
487
- Khi `enableEncryption: true`, các trường nhạy cảm sau được mã hoá trước khi gửi:
486
+ ```
487
+ signature = Base64( HMAC-SHA256(secretKey, message) )
488
+ message = source + "|" + timestamp + "|" + JSON(payload)
489
+ ```
490
+
491
+ > **Lưu ý:** RN SDK dùng timestamp tính bằng **giây** (khác với Web SDK dùng milliseconds).
492
+
493
+ ---
494
+
495
+ ### AES-256-CBC Encryption
496
+
497
+ Khi `enableEncryption: true`, các trường PII được mã hoá.
488
498
 
489
- `full_name`, `first_name`, `last_name`, `phone`, `email`, `idcard`, `old_idcard`, `gender`, `dob`, `address`, `religion`
499
+ **Các trường PII bị hoá:**
500
+
501
+ | Trường | Mô tả |
502
+ |---|---|
503
+ | `phone` | Số điện thoại |
504
+ | `email` | Email |
505
+ | `idcard` | CMND/CCCD mới |
506
+ | `old_idcard` | CMND cũ |
507
+ | `address` | Địa chỉ |
508
+ | `religion` | Tôn giáo |
509
+ | `full_name` | Họ tên đầy đủ |
510
+ | `first_name` | Tên |
511
+ | `last_name` | Họ |
512
+ | `gender` | Giới tính |
513
+ | `dob` | Ngày sinh |
514
+
515
+ **Công thức:**
490
516
 
491
- Công thức mã hoá:
492
517
  ```
493
- traits = Base64(IV + AES_CBC_PKCS5(key, plaintext_traits_json))
494
- Key = SHA256(secretKey + X-Timestamp) → 32 bytes (AES-256)
495
- IV = Random 16 bytes
518
+ encrypted = Base64( IV[16 bytes] || AES_CBC_PKCS7( key, IV, plaintext ) )
519
+
520
+ key = SHA256( secretKey + timestamp )
521
+ IV = random 16 bytes (prepend vào ciphertext)
522
+ plaintext = JSON.stringify({ chỉ các trường PII })
496
523
  ```
497
524
 
498
- Sử dụng thư viện `crypto-js` pure JavaScript, không cần link native, hoạt động trên cả iOS và Android.
525
+ > **Lưu ý:** Không bật trên dev/staging nếu server chưa cấu hình giải mã. Dùng `enableEncryption: false`.
499
526
 
500
527
  ---
501
528
 
502
- ## Dependencies
529
+ ### Best Practices
503
530
 
504
- | Package | do |
505
- |---|---|
506
- | `@react-native-async-storage/async-storage` | Lưu `device_id`, `anonymous_id`, `profile_id` |
507
- | `react-native-device-info` | Lấy `brand`, `model`, `os_version`, `app_version` |
508
- | `crypto-js` | HMAC-SHA256 signature + AES-256-CBC encryption (pure JS) |
531
+ - Không log `secretKey` hay `apiKey` vào console production.
532
+ - Bật `debug: false` trên production build.
533
+ - Rotate key theo chính sách CDP team.
534
+ - Dùng environment variables, không hardcode credentials.
535
+
536
+ ```typescript
537
+ // ✅ Đúng — dùng env vars
538
+ import Config from 'react-native-config';
539
+
540
+ const cdp = await CdpRnSdk.create({
541
+ apiKey: Config.CDP_API_KEY,
542
+ secretKey: Config.CDP_SECRET_KEY,
543
+ source: 'DOP',
544
+ serviceName: 'VC_APP',
545
+ debug: __DEV__,
546
+ });
547
+ ```
548
+
549
+ ```bash
550
+ # .env
551
+ CDP_API_KEY=YOUR_API_KEY
552
+ CDP_SECRET_KEY=YOUR_SECRET_KEY
553
+ CDP_BASE_URL=https://stg-ingestlog.vietcredit.com.vn
554
+
555
+ # .env.production
556
+ CDP_API_KEY=<production_key>
557
+ CDP_SECRET_KEY=<production_secret>
558
+ CDP_BASE_URL=https://ingestlog.vietcredit.com.vn
559
+ ```
509
560
 
510
561
  ---
511
562
 
512
- ## API Reference
563
+ ## Tích hợp React Navigation đầy đủ
513
564
 
514
- ### `CdpRnSdk.create(config)`
565
+ Setup hoàn chỉnh với auto screen tracking và AppState flush:
515
566
 
516
- | Tham số | Kiểu | Bắt buộc | Mặc định |
517
- |---|---|---|---|
518
- | `apiKey` | `string` | ✓ | |
519
- | `secretKey` | `string` | ✓ | |
520
- | `source` | `string` | | |
521
- | `serviceName` | `string` | ✓ | |
522
- | `baseUrl` | `string` | | `https://stg-ingestlog.vietcredit.com.vn` |
523
- | `isTest` | `boolean` | | `false` |
524
- | `debug` | `boolean` | | `false` |
525
- | `batchSize` | `number` | | `10` |
526
- | `batchInterval` | `number` (ms) | | `5000` |
527
- | `enableEncryption` | `boolean` | | `true` |
528
-
529
- ### Endpoint mapping
530
-
531
- | Method SDK | HTTP | Endpoint |
532
- |---|---|---|
533
- | `identifyUser()` / `identify()` | POST | `/v1/profiles/track` |
534
- | `updateProfile()` | POST | `/v1/profiles/update` |
535
- | `getProfile()` | GET | `/v1/profiles/get/{profile_id}` |
536
- | `track()` (batchSize=1) | POST | `/v2/events/track` |
537
- | `track()` / `flush()` (batch) | POST | `/v2/events/batch` |
538
- | Trait setters | POST | `/v1/profiles/track` (type: update) |
539
- | `destroy_session()` | POST | `/v1/profiles/track` (type: logout) |
567
+ ```typescript
568
+ // src/lib/cdp.ts
569
+ import { CdpRnSdk } from 'vc-cdp-rn-sdk';
570
+
571
+ let _cdp: CdpRnSdk | null = null;
572
+
573
+ export async function initCdp(): Promise<CdpRnSdk> {
574
+ _cdp = await CdpRnSdk.create({
575
+ apiKey: process.env.CDP_API_KEY!,
576
+ secretKey: process.env.CDP_SECRET_KEY!,
577
+ source: 'DOP',
578
+ serviceName: 'VC_APP',
579
+ baseUrl: process.env.CDP_BASE_URL,
580
+ debug: __DEV__,
581
+ });
582
+ return _cdp;
583
+ }
584
+
585
+ export function getCdp(): CdpRnSdk {
586
+ if (!_cdp) throw new Error('CDP SDK chưa được khởi tạo. Gọi initCdp() trước.');
587
+ return _cdp;
588
+ }
589
+ ```
590
+
591
+ ```typescript
592
+ // App.tsx
593
+ import React, { useEffect, useRef } from 'react';
594
+ import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
595
+ import { AppState, AppStateStatus } from 'react-native';
596
+ import { initCdp, getCdp } from './lib/cdp';
597
+
598
+ export default function App() {
599
+ const navRef = useNavigationContainerRef();
600
+ const appState = useRef(AppState.currentState);
601
+
602
+ useEffect(() => {
603
+ initCdp();
604
+ }, []);
605
+
606
+ // Flush khi app vào background
607
+ useEffect(() => {
608
+ const sub = AppState.addEventListener('change', async (next: AppStateStatus) => {
609
+ if (appState.current === 'active' && next.match(/inactive|background/)) {
610
+ await getCdp().flush();
611
+ }
612
+ appState.current = next;
613
+ });
614
+ return () => sub.remove();
615
+ }, []);
616
+
617
+ return (
618
+ <NavigationContainer
619
+ ref={navRef}
620
+ onReady={() => {
621
+ const name = navRef.getCurrentRoute()?.name;
622
+ if (name) getCdp().screen(name);
623
+ }}
624
+ onStateChange={() => {
625
+ const name = navRef.getCurrentRoute()?.name;
626
+ if (name) getCdp().screen(name);
627
+ }}
628
+ >
629
+ <RootNavigator />
630
+ </NavigationContainer>
631
+ );
632
+ }
633
+ ```
634
+
635
+ ```typescript
636
+ // Hook tiện lợi
637
+ // hooks/useCdp.ts
638
+ import { getCdp } from '../lib/cdp';
639
+ import type { CdpRnSdk } from 'vc-cdp-rn-sdk';
640
+
641
+ export function useCdp(): CdpRnSdk {
642
+ return getCdp();
643
+ }
644
+ ```
645
+
646
+ ```typescript
647
+ // Identify sau đăng nhập
648
+ async function handleLogin(phone: string, password: string) {
649
+ const user = await authApi.login(phone, password);
650
+ await getCdp().identifyUser(user.id, {
651
+ phone: user.phone,
652
+ email: user.email,
653
+ full_name: user.name,
654
+ });
655
+ navigation.navigate('Home');
656
+ }
657
+
658
+ // Đăng xuất
659
+ async function handleLogout() {
660
+ await authApi.logout();
661
+ await getCdp().destroy_session();
662
+ navigation.reset({ index: 0, routes: [{ name: 'Login' }] });
663
+ }
664
+ ```
540
665
 
541
666
  ---
542
667
 
543
- ## Tài liệu chi tiết
668
+ ## API Reference
544
669
 
545
- | File | Nội dung |
546
- |---|---|
547
- | [docs/getting-started.md](docs/getting-started.md) | Hướng dẫn bắt đầu từ đầu |
548
- | [docs/profile.md](docs/profile.md) | Profile, traits, consents, scores, products |
549
- | [docs/events.md](docs/events.md) | Event tracking, batching, screen view |
550
- | [docs/security.md](docs/security.md) | HMAC signature, AES encryption |
551
- | [docs/api-reference.md](docs/api-reference.md) | Toàn bộ API với type signatures |
552
- | [docs/integration-react-navigation.md](docs/integration-react-navigation.md) | React Navigation integration |
670
+ ### Factory
671
+
672
+ ```typescript
673
+ static async create(config: CdpRnConfig): Promise<CdpRnSdk>
674
+ ```
675
+
676
+ ### CdpRnConfig
677
+
678
+ ```typescript
679
+ interface CdpRnConfig {
680
+ apiKey: string; // bắt buộc
681
+ secretKey: string; // bắt buộc
682
+ source: string; // bắt buộc — kênh tích hợp
683
+ serviceName: string; // bắt buộc — VC_APP | DOP | LOS | LMS
684
+ baseUrl?: string; // mặc định staging
685
+ isTest?: boolean; // mặc định false
686
+ debug?: boolean; // mặc định false
687
+ batchSize?: number; // mặc định 10
688
+ batchInterval?: number; // mặc định 5000 ms
689
+ enableEncryption?: boolean; // mặc định true
690
+ }
691
+ ```
692
+
693
+ ### Profile Methods
694
+
695
+ ```typescript
696
+ async identifyUser(userId: string, traits?: UserTraits, partyType?: 'PERSON' | 'ORG'): Promise<this>
697
+ async identify(userId: string, traits?: UserTraits): Promise<this> // alias
698
+
699
+ async updateProfile(payload: {
700
+ profile_id?: string;
701
+ traits?: UserTraits;
702
+ consents?: ConsentInfo[];
703
+ scores?: ScoreInfo[];
704
+ products?: ProductInfo[];
705
+ metadata?: Record<string, unknown>;
706
+ }): Promise<ProfileResponse | undefined>
707
+
708
+ async getProfile(profileId?: string): Promise<ProfileData | undefined>
709
+ ```
710
+
711
+ ### Trait Setter Methods (chainable → `this`)
712
+
713
+ ```typescript
714
+ add_first_name(v: string): this
715
+ add_last_name(v: string): this
716
+ add_user_name(v: string): this
717
+ add_email(v: string): this
718
+ add_mobile(v: string): this
719
+ add_gender(v: 'M' | 'F' | 'O'): this
720
+ add_birthday(v: string | Date): this
721
+ add_idcard(v: string): this
722
+ add_old_idcard(v: string): this
723
+ add_address(v: string): this
724
+ add_occupation(v: string): this
725
+ add_nationality(v: string): this
726
+ add_marital_status(v: string): this
727
+ add_religion(v: string): this
728
+ add_zalo_id(v: string): this
729
+ add_tiktok_id(v: string): this
730
+ add_user_attribute(name: string, value: unknown): this
731
+ add_user_attributes(attrs: UserTraits): this
732
+ setUserAttributes(attrs: UserTraits): this
733
+ ```
734
+
735
+ ### Consent / Score / Product / Campaign Methods
736
+
737
+ ```typescript
738
+ setConsents(consents: ConsentInfo[]): this
739
+ setScores(scores: ScoreInfo[]): this
740
+ setProducts(products: ProductInfo[]): this
741
+ setMetadata(meta: Record<string, unknown>): this
742
+ setCampaign(info: CampaignInfo): this
743
+ setPlatform(info: PlatformInfo): this
744
+ setPushToken(token: string): this
745
+ ```
746
+
747
+ ### Event Methods
748
+
749
+ ```typescript
750
+ track(eventName: string, properties?: Record<string, unknown>): void
751
+ screen(screenName: string, properties?: Record<string, unknown>): void
752
+ async flush(): Promise<void>
753
+ ```
754
+
755
+ ### Session & Identity Methods
756
+
757
+ ```typescript
758
+ getUserIdentities(): UserIdentities
759
+ getProfileId(): string | null
760
+ getDeviceId(): string
761
+ getSessionId(): string
762
+ getUserAttribute(name: keyof UserTraits): unknown
763
+
764
+ async destroy_session(): Promise<this>
765
+ async reset(): Promise<this> // alias
766
+ async destroy(): Promise<void> // cleanup timers + flush
767
+ ```
768
+
769
+ ### Types
770
+
771
+ ```typescript
772
+ interface UserTraits {
773
+ full_name?: string;
774
+ first_name?: string;
775
+ last_name?: string;
776
+ phone?: string; // 🔒 PII
777
+ email?: string; // 🔒 PII
778
+ gender?: 'M' | 'F' | 'O';
779
+ dob?: string; // 'yyyy-MM-dd'
780
+ idcard?: string; // 🔒 PII
781
+ old_idcard?: string; // 🔒 PII
782
+ address?: string; // 🔒 PII
783
+ occupation?: string;
784
+ nationality?: string;
785
+ marital_status?: string;
786
+ religion?: string; // 🔒 PII
787
+ zalo_id?: string;
788
+ tiktok_id?: string;
789
+ mps_verified?: boolean;
790
+ kyc_status?: string;
791
+ [key: string]: unknown;
792
+ }
793
+
794
+ interface ConsentInfo {
795
+ consent_type: string;
796
+ status: boolean;
797
+ consent_date?: string; // 'yyyy-MM-dd'
798
+ }
799
+
800
+ interface ScoreInfo {
801
+ score_type: string;
802
+ score_value: number;
803
+ score_date?: string;
804
+ }
805
+
806
+ interface ProductInfo {
807
+ product_code: string;
808
+ product_name?: string;
809
+ status?: string;
810
+ }
811
+
812
+ interface UserIdentities {
813
+ user_id: string | null;
814
+ profile_id: string | null;
815
+ anonymous_id: string;
816
+ device_id: string;
817
+ session_id: string;
818
+ }
819
+
820
+ interface ProfileResponse {
821
+ code: number;
822
+ message: string;
823
+ transaction_id: string;
824
+ profile_id: string;
825
+ }
826
+ ```
@@ -149,8 +149,8 @@ await cdp.destroy_session();
149
149
 
150
150
  ## Bước tiếp theo
151
151
 
152
- - [Profile & Traits →](profile.md)
153
- - [Event Tracking →](events.md)
154
- - [Tích hợp React Navigation →](integration-react-navigation.md)
155
- - [Bảo mật →](security.md)
156
- - [API Reference →](api-reference.md)
152
+ - [Profile & Traits →](https://gitlab.ovp.vn/dlc/vc-cdk-sdk/-/blob/master/vc-cdp-rn-sdk/docs/profile.md)
153
+ - [Event Tracking →](https://gitlab.ovp.vn/dlc/vc-cdk-sdk/-/blob/master/vc-cdp-rn-sdk/docs/events.md)
154
+ - [Tích hợp React Navigation →](https://gitlab.ovp.vn/dlc/vc-cdk-sdk/-/blob/master/vc-cdp-rn-sdk/docs/integration-react-navigation.md)
155
+ - [Bảo mật →](https://gitlab.ovp.vn/dlc/vc-cdk-sdk/-/blob/master/vc-cdp-rn-sdk/docs/security.md)
156
+ - [API Reference →](https://gitlab.ovp.vn/dlc/vc-cdk-sdk/-/blob/master/vc-cdp-rn-sdk/docs/api-reference.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vc-cdp-rn-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "CDP SDK for React Native - Customer Data Platform tracking for iOS and Android",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,6 +26,12 @@
26
26
  ],
27
27
  "author": "VietCredit",
28
28
  "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://gitlab.ovp.vn/dlc/vc-cdk-sdk.git",
32
+ "directory": "vc-cdp-rn-sdk"
33
+ },
34
+ "homepage": "https://gitlab.ovp.vn/dlc/vc-cdk-sdk/-/tree/master/vc-cdp-rn-sdk#readme",
29
35
  "peerDependencies": {
30
36
  "@react-native-async-storage/async-storage": ">=1.17.0",
31
37
  "react": ">=17.0.0",