vc-cdp-rn-sdk 0.1.0 → 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 +600 -313
- package/dist/CdpRnSdk.d.ts +3 -0
- package/dist/CdpRnSdk.d.ts.map +1 -1
- package/dist/CdpRnSdk.js +26 -9
- package/dist/CdpRnSdk.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/api-reference.md +198 -0
- package/docs/events.md +151 -0
- package/docs/getting-started.md +156 -0
- package/docs/integration-react-navigation.md +202 -0
- package/docs/profile.md +196 -0
- package/docs/security.md +111 -0
- package/package.json +9 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Hướng dẫn tích hợp `vc-cdp-rn-sdk` vào React Native từ đầu.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Cài đặt
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install vc-cdp-rn-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Peer Dependencies
|
|
14
|
+
|
|
15
|
+
SDK yêu cầu các package sau phải được cài riêng trong project:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @react-native-async-storage/async-storage
|
|
19
|
+
npm install react-native-device-info
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
#### iOS — link native modules
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd ios && pod install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
#### Android
|
|
29
|
+
|
|
30
|
+
Không cần thêm cấu hình nếu dùng React Native >= 0.68 (auto-linking).
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Khởi tạo
|
|
35
|
+
|
|
36
|
+
Khởi tạo SDK **một lần duy nhất** tại entry point của app (thường là `App.tsx`). SDK dùng `AsyncStorage` nên init là **async**.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// App.tsx
|
|
40
|
+
import { CdpRnSdk } from 'vc-cdp-rn-sdk';
|
|
41
|
+
|
|
42
|
+
let cdp: CdpRnSdk;
|
|
43
|
+
|
|
44
|
+
export async function initCdp() {
|
|
45
|
+
cdp = await CdpRnSdk.create({
|
|
46
|
+
apiKey: 'YOUR_API_KEY',
|
|
47
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
48
|
+
source: 'DOP',
|
|
49
|
+
serviceName: 'VC_APP',
|
|
50
|
+
baseUrl: 'https://ingestlog.vietcredit.com.vn',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { cdp };
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// App.tsx — gọi trong useEffect
|
|
59
|
+
import { useEffect } from 'react';
|
|
60
|
+
import { initCdp } from './cdp';
|
|
61
|
+
|
|
62
|
+
export default function App() {
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
initCdp();
|
|
65
|
+
}, []);
|
|
66
|
+
// ...
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> **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.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 3. Track event đầu tiên
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { cdp } from './cdp';
|
|
78
|
+
|
|
79
|
+
// Track màn hình đầu tiên
|
|
80
|
+
cdp.screen('SplashScreen');
|
|
81
|
+
|
|
82
|
+
// Custom event
|
|
83
|
+
cdp.track('app_open', { platform: 'android', version: '2.1.0' });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 4. Identify user sau đăng nhập
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
await cdp.identifyUser('user_001', {
|
|
92
|
+
phone: '0901234567',
|
|
93
|
+
email: 'user@example.com',
|
|
94
|
+
full_name: 'Nguyễn Văn A',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
console.log(cdp.getProfileId()); // → "177671850461184000"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 5. Track events sau login
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
cdp.track('loan_viewed', {
|
|
106
|
+
loan_id: 'LOAN_001',
|
|
107
|
+
amount: 50_000_000,
|
|
108
|
+
term: 24,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
cdp.screen('LoanDetailScreen', { loan_code: 'CONSUMER_24M' });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 6. Đăng xuất
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
await cdp.destroy_session();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Cấu hình đầy đủ
|
|
125
|
+
|
|
126
|
+
| Tham số | Kiểu | Mặc định | Mô tả |
|
|
127
|
+
|---|---|---|---|
|
|
128
|
+
| `apiKey` | `string` | — | **Bắt buộc.** API Key do CDP cấp |
|
|
129
|
+
| `secretKey` | `string` | — | **Bắt buộc.** Secret Key để ký HMAC và mã hoá AES |
|
|
130
|
+
| `source` | `string` | — | **Bắt buộc.** Kênh phát triển (khai báo với CDP trước) |
|
|
131
|
+
| `serviceName` | `string` | — | **Bắt buộc.** `VC_APP` \| `DOP` \| `LOS` \| `LMS` |
|
|
132
|
+
| `baseUrl` | `string` | Staging URL | URL API CDP (không có `/v1`) |
|
|
133
|
+
| `isTest` | `boolean` | `false` | Đánh dấu môi trường test |
|
|
134
|
+
| `debug` | `boolean` | `false` | In log ra console |
|
|
135
|
+
| `batchSize` | `number` | `10` | Số events tối đa trong 1 batch |
|
|
136
|
+
| `batchInterval` | `number` | `5000` | Thời gian (ms) giữa các lần flush tự động |
|
|
137
|
+
| `enableEncryption` | `boolean` | `true` | Mã hoá PII bằng AES-256-CBC |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Môi trường
|
|
142
|
+
|
|
143
|
+
| Môi trường | baseUrl |
|
|
144
|
+
|---|---|
|
|
145
|
+
| Staging | `https://stg-ingestlog.vietcredit.com.vn` |
|
|
146
|
+
| Production | `https://ingestlog.vietcredit.com.vn` |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Bước tiếp theo
|
|
151
|
+
|
|
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)
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Tích hợp React Navigation
|
|
2
|
+
|
|
3
|
+
Hướng dẫn auto-track màn hình khi navigate trong React Navigation.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Setup cơ bản
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// src/lib/cdp.ts
|
|
11
|
+
import { CdpRnSdk } from 'vc-cdp-rn-sdk';
|
|
12
|
+
|
|
13
|
+
let _cdp: CdpRnSdk | null = null;
|
|
14
|
+
|
|
15
|
+
export async function initCdp(): Promise<CdpRnSdk> {
|
|
16
|
+
_cdp = await CdpRnSdk.create({
|
|
17
|
+
apiKey: process.env.CDP_API_KEY!,
|
|
18
|
+
secretKey: process.env.CDP_SECRET_KEY!,
|
|
19
|
+
source: 'DOP',
|
|
20
|
+
serviceName: 'VC_APP',
|
|
21
|
+
baseUrl: process.env.CDP_BASE_URL,
|
|
22
|
+
debug: __DEV__,
|
|
23
|
+
});
|
|
24
|
+
return _cdp;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getCdp(): CdpRnSdk {
|
|
28
|
+
if (!_cdp) throw new Error('CDP SDK chưa được khởi tạo. Gọi initCdp() trước.');
|
|
29
|
+
return _cdp;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Auto-track Screen (React Navigation 6+)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// App.tsx
|
|
39
|
+
import React, { useEffect, useRef } from 'react';
|
|
40
|
+
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
|
|
41
|
+
import { AppState, AppStateStatus } from 'react-native';
|
|
42
|
+
import { initCdp, getCdp } from './lib/cdp';
|
|
43
|
+
|
|
44
|
+
export default function App() {
|
|
45
|
+
const navRef = useNavigationContainerRef();
|
|
46
|
+
const appState = useRef(AppState.currentState);
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
initCdp();
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
52
|
+
// Flush khi app vào background
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const sub = AppState.addEventListener('change', async (next: AppStateStatus) => {
|
|
55
|
+
if (appState.current === 'active' && next.match(/inactive|background/)) {
|
|
56
|
+
await getCdp().flush();
|
|
57
|
+
}
|
|
58
|
+
appState.current = next;
|
|
59
|
+
});
|
|
60
|
+
return () => sub.remove();
|
|
61
|
+
}, []);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<NavigationContainer
|
|
65
|
+
ref={navRef}
|
|
66
|
+
onReady={() => {
|
|
67
|
+
const name = navRef.getCurrentRoute()?.name;
|
|
68
|
+
if (name) getCdp().screen(name);
|
|
69
|
+
}}
|
|
70
|
+
onStateChange={() => {
|
|
71
|
+
const name = navRef.getCurrentRoute()?.name;
|
|
72
|
+
if (name) getCdp().screen(name);
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
<RootNavigator />
|
|
76
|
+
</NavigationContainer>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Track trong Screen Component
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// screens/LoanDetailScreen.tsx
|
|
87
|
+
import React, { useEffect } from 'react';
|
|
88
|
+
import { View, Button } from 'react-native';
|
|
89
|
+
import { getCdp } from '../lib/cdp';
|
|
90
|
+
|
|
91
|
+
export function LoanDetailScreen({ route }: any) {
|
|
92
|
+
const { loanId, amount } = route.params;
|
|
93
|
+
const cdp = getCdp();
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
cdp.track('loan_detail_viewed', { loan_id: loanId, amount });
|
|
97
|
+
}, [loanId]);
|
|
98
|
+
|
|
99
|
+
const handleApply = () => {
|
|
100
|
+
cdp.track('loan_apply_clicked', { loan_id: loanId, amount });
|
|
101
|
+
// navigate to form
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<View>
|
|
106
|
+
<Button title="Đăng ký ngay" onPress={handleApply} />
|
|
107
|
+
</View>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Identify sau đăng nhập
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
// screens/LoginScreen.tsx
|
|
118
|
+
import { getCdp } from '../lib/cdp';
|
|
119
|
+
|
|
120
|
+
async function handleLogin(phone: string, password: string) {
|
|
121
|
+
const user = await authApi.login(phone, password);
|
|
122
|
+
|
|
123
|
+
await getCdp().identifyUser(user.id, {
|
|
124
|
+
phone: user.phone,
|
|
125
|
+
email: user.email,
|
|
126
|
+
full_name: user.name,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
navigation.navigate('Home');
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Đăng xuất
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// screens/ProfileScreen.tsx
|
|
139
|
+
import { getCdp } from '../lib/cdp';
|
|
140
|
+
|
|
141
|
+
async function handleLogout() {
|
|
142
|
+
await authApi.logout();
|
|
143
|
+
await getCdp().destroy_session();
|
|
144
|
+
navigation.reset({ index: 0, routes: [{ name: 'Login' }] });
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Hook tiện lợi
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// hooks/useCdp.ts
|
|
154
|
+
import { getCdp } from '../lib/cdp';
|
|
155
|
+
import type { CdpRnSdk } from 'vc-cdp-rn-sdk';
|
|
156
|
+
|
|
157
|
+
export function useCdp(): CdpRnSdk {
|
|
158
|
+
return getCdp();
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// Dùng trong component
|
|
164
|
+
function ProductCard({ product }: { product: Product }) {
|
|
165
|
+
const cdp = useCdp();
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<Pressable onPress={() => cdp.track('product_tapped', { code: product.code })}>
|
|
169
|
+
<Text>{product.name}</Text>
|
|
170
|
+
</Pressable>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Environment Variables (react-native-config)
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# .env
|
|
181
|
+
CDP_API_KEY=eff9ba9a-5b10-4fc0-af23-e2641b46d307
|
|
182
|
+
CDP_SECRET_KEY=91ed726c-60e8-4ef7-ab2d-112b5324f9a1
|
|
183
|
+
CDP_BASE_URL=https://stg-ingestlog.vietcredit.com.vn
|
|
184
|
+
|
|
185
|
+
# .env.production
|
|
186
|
+
CDP_API_KEY=<production_key>
|
|
187
|
+
CDP_SECRET_KEY=<production_secret>
|
|
188
|
+
CDP_BASE_URL=https://ingestlog.vietcredit.com.vn
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import Config from 'react-native-config';
|
|
193
|
+
|
|
194
|
+
const cdp = await CdpRnSdk.create({
|
|
195
|
+
apiKey: Config.CDP_API_KEY,
|
|
196
|
+
secretKey: Config.CDP_SECRET_KEY,
|
|
197
|
+
baseUrl: Config.CDP_BASE_URL,
|
|
198
|
+
source: 'DOP',
|
|
199
|
+
serviceName: 'VC_APP',
|
|
200
|
+
debug: __DEV__,
|
|
201
|
+
});
|
|
202
|
+
```
|
package/docs/profile.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Profile & Traits
|
|
2
|
+
|
|
3
|
+
Hướng dẫn quản lý thông tin người dùng trên CDP cho React Native.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## identifyUser() — Đăng nhập
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
await cdp.identifyUser(userId, traits?, partyType?)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Tham số | Kiểu | Bắt buộc | Mô tả |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| `userId` | `string` | ✓ | Mã user trong hệ thống nội bộ |
|
|
16
|
+
| `traits` | `UserTraits` | | Thông tin nhân khẩu học |
|
|
17
|
+
| `partyType` | `'PERSON' \| 'ORG'` | | Mặc định `'PERSON'` |
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// Cá nhân
|
|
21
|
+
await cdp.identifyUser('user_001', {
|
|
22
|
+
phone: '0901234567',
|
|
23
|
+
email: 'user@example.com',
|
|
24
|
+
full_name: 'Nguyễn Văn A',
|
|
25
|
+
gender: 'M',
|
|
26
|
+
dob: '1990-01-15',
|
|
27
|
+
idcard: '012345678901',
|
|
28
|
+
address: '123 Lê Lợi, Q.1, TP.HCM',
|
|
29
|
+
occupation: 'Kỹ sư phần mềm',
|
|
30
|
+
nationality: 'Vietnamese',
|
|
31
|
+
marital_status: 'single',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Tổ chức
|
|
35
|
+
await cdp.identifyUser('org_001', {
|
|
36
|
+
full_name: 'Công ty TNHH ABC',
|
|
37
|
+
phone: '0281234567',
|
|
38
|
+
email: 'contact@abc.vn',
|
|
39
|
+
}, 'ORG');
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Trait Setters — Cập nhật từng thuộc tính
|
|
45
|
+
|
|
46
|
+
Chainable. Mỗi setter gọi ngay `POST /v1/profiles/track` (type: update).
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
cdp
|
|
50
|
+
.add_first_name('Văn A')
|
|
51
|
+
.add_last_name('Nguyễn')
|
|
52
|
+
.add_user_name('Nguyễn Văn A')
|
|
53
|
+
.add_email('user@example.com')
|
|
54
|
+
.add_mobile('0901234567')
|
|
55
|
+
.add_gender('M')
|
|
56
|
+
.add_birthday('1990-01-15') // hoặc new Date('1990-01-15')
|
|
57
|
+
.add_idcard('012345678901')
|
|
58
|
+
.add_old_idcard('123456789')
|
|
59
|
+
.add_address('123 Lê Lợi, Q.1, TP.HCM')
|
|
60
|
+
.add_occupation('Kỹ sư phần mềm')
|
|
61
|
+
.add_nationality('Vietnamese')
|
|
62
|
+
.add_marital_status('single')
|
|
63
|
+
.add_religion('Buddhist')
|
|
64
|
+
.add_zalo_id('zalo_user_001')
|
|
65
|
+
.add_tiktok_id('tiktok_user_001');
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Method | Trường | Ghi chú |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `add_first_name(v)` | `first_name` | |
|
|
71
|
+
| `add_last_name(v)` | `last_name` | |
|
|
72
|
+
| `add_user_name(v)` | `full_name` | |
|
|
73
|
+
| `add_email(v)` | `email` | 🔒 PII |
|
|
74
|
+
| `add_mobile(v)` | `phone` | 🔒 PII |
|
|
75
|
+
| `add_gender(v)` | `gender` | `'M'` \| `'F'` \| `'O'` |
|
|
76
|
+
| `add_birthday(v)` | `dob` | `Date` hoặc `'yyyy-MM-dd'` |
|
|
77
|
+
| `add_idcard(v)` | `idcard` | 🔒 PII |
|
|
78
|
+
| `add_old_idcard(v)` | `old_idcard` | 🔒 PII |
|
|
79
|
+
| `add_address(v)` | `address` | 🔒 PII |
|
|
80
|
+
| `add_occupation(v)` | `occupation` | |
|
|
81
|
+
| `add_nationality(v)` | `nationality` | |
|
|
82
|
+
| `add_marital_status(v)` | `marital_status` | |
|
|
83
|
+
| `add_religion(v)` | `religion` | 🔒 PII |
|
|
84
|
+
| `add_zalo_id(v)` | `zalo_id` | |
|
|
85
|
+
| `add_tiktok_id(v)` | `tiktok_id` | |
|
|
86
|
+
| `add_user_attribute(name, val)` | custom | Bất kỳ trường nào |
|
|
87
|
+
| `add_user_attributes(obj)` | nhiều trường | Batch update |
|
|
88
|
+
| `setUserAttributes(obj)` | nhiều trường | Alias |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## updateProfile() — Force-update
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
await cdp.updateProfile({
|
|
96
|
+
traits: { kyc_status: 'verified', mps_verified: true },
|
|
97
|
+
consents: [{ consent_type: 'marketing_sms', status: true, consent_date: '2025-06-01' }],
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## getProfile() — Lấy thông tin profile
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const profile = await cdp.getProfile();
|
|
107
|
+
const profile = await cdp.getProfile('cdp_123');
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Consents
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
cdp.setConsents([
|
|
116
|
+
{ consent_type: 'marketing_sms', status: true, consent_date: '2025-06-01' },
|
|
117
|
+
{ consent_type: 'marketing_email', status: false },
|
|
118
|
+
{ consent_type: 'data_sharing', status: true },
|
|
119
|
+
]);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Scores
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
cdp.setScores([
|
|
128
|
+
{ score_type: 'credit_score', score_value: 720, score_date: '2025-06-01' },
|
|
129
|
+
]);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Products
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
cdp.setProducts([
|
|
138
|
+
{ product_code: 'LOAN_CONSUMER', product_name: 'Vay tiêu dùng', status: 'active' },
|
|
139
|
+
]);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Metadata, Campaign, Push Token
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
cdp.setMetadata({ schema_version: '2.0', referral_code: 'REF123' });
|
|
148
|
+
|
|
149
|
+
cdp.setCampaign({
|
|
150
|
+
utm_source: 'facebook',
|
|
151
|
+
utm_medium: 'cpc',
|
|
152
|
+
utm_campaign: 'summer_loan_2025',
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
cdp.setPushToken('ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]');
|
|
156
|
+
// hoặc FCM token trên Android
|
|
157
|
+
cdp.setPushToken('fcm_token_abc123...');
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## getUserIdentities()
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const ids = cdp.getUserIdentities();
|
|
166
|
+
// {
|
|
167
|
+
// user_id: 'user_001',
|
|
168
|
+
// profile_id: '177671850461184000',
|
|
169
|
+
// anonymous_id: 'uuid-...',
|
|
170
|
+
// device_id: 'uuid-...',
|
|
171
|
+
// session_id: 'uuid-...',
|
|
172
|
+
// }
|
|
173
|
+
|
|
174
|
+
cdp.getProfileId();
|
|
175
|
+
cdp.getDeviceId();
|
|
176
|
+
cdp.getSessionId();
|
|
177
|
+
cdp.getUserAttribute('phone');
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## destroy_session() — Đăng xuất
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
await cdp.destroy_session();
|
|
186
|
+
// hoặc alias:
|
|
187
|
+
await cdp.reset();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
| | Hành động |
|
|
191
|
+
|---|---|
|
|
192
|
+
| `userId`, `profileId`, `userTraits` | Xoá |
|
|
193
|
+
| `consents`, `scores`, `products`, `campaign` | Xoá |
|
|
194
|
+
| `anonymous_id` | Tạo mới, lưu AsyncStorage |
|
|
195
|
+
| `session_id` | Tạo mới, lưu AsyncStorage |
|
|
196
|
+
| `device_id` | **Giữ nguyên** |
|
package/docs/security.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Bảo mật
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## HMAC-SHA256 Signature
|
|
6
|
+
|
|
7
|
+
Mọi request đến CDP API đều được ký tự động.
|
|
8
|
+
|
|
9
|
+
### Headers
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
X-Api-Key: <apiKey>
|
|
13
|
+
X-Source: <source>
|
|
14
|
+
X-Timestamp: <unix_timestamp_s>
|
|
15
|
+
X-Signatures: <base64_signature>
|
|
16
|
+
X-Trace-Id: <uuid>
|
|
17
|
+
Content-Type: application/json
|
|
18
|
+
isTest: false
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Công thức
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
signature = Base64( HMAC-SHA256(secretKey, message) )
|
|
25
|
+
message = source + "|" + timestamp + "|" + JSON(payload)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| Thành phần | Mô tả |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `secretKey` | Secret Key CDP cấp |
|
|
31
|
+
| `source` | Kênh tích hợp (`DOP`, ...) |
|
|
32
|
+
| `timestamp` | Unix timestamp tính bằng **giây** (RN SDK) hoặc **ms** (Web SDK) |
|
|
33
|
+
| `payload` | Body JSON string (GET request: chuỗi rỗng `""`) |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## AES-256-CBC Encryption
|
|
38
|
+
|
|
39
|
+
Khi `enableEncryption: true`, các trường PII được mã hoá.
|
|
40
|
+
|
|
41
|
+
### Các trường PII bị mã hoá
|
|
42
|
+
|
|
43
|
+
| Trường | Mô tả |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `phone` | Số điện thoại |
|
|
46
|
+
| `email` | Email |
|
|
47
|
+
| `idcard` | CMND/CCCD mới |
|
|
48
|
+
| `old_idcard` | CMND cũ |
|
|
49
|
+
| `address` | Địa chỉ |
|
|
50
|
+
| `religion` | Tôn giáo |
|
|
51
|
+
| `full_name` | Họ tên đầy đủ |
|
|
52
|
+
| `first_name` | Tên |
|
|
53
|
+
| `last_name` | Họ |
|
|
54
|
+
| `gender` | Giới tính |
|
|
55
|
+
| `dob` | Ngày sinh |
|
|
56
|
+
|
|
57
|
+
### Công thức
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
encrypted = Base64( IV[16 bytes] || AES_CBC_PKCS7( key, IV, plaintext ) )
|
|
61
|
+
|
|
62
|
+
key = SHA256( secretKey + timestamp )
|
|
63
|
+
IV = random 16 bytes (prepend vào ciphertext)
|
|
64
|
+
plaintext = JSON.stringify({ chỉ các trường PII })
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Lưu ý
|
|
68
|
+
|
|
69
|
+
- **Không bật trên dev/staging** nếu server chưa cấu hình giải mã. Dùng `enableEncryption: false`.
|
|
70
|
+
- Production: `enableEncryption: true` (mặc định).
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Development
|
|
74
|
+
const cdp = await CdpRnSdk.create({
|
|
75
|
+
enableEncryption: false,
|
|
76
|
+
isTest: true,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Production
|
|
80
|
+
const cdp = await CdpRnSdk.create({
|
|
81
|
+
enableEncryption: true,
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Lưu trữ
|
|
88
|
+
|
|
89
|
+
| Dữ liệu | Nơi lưu | Vòng đời |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `device_id` | AsyncStorage | Vĩnh viễn (đến khi xoá app) |
|
|
92
|
+
| `anonymous_id` | AsyncStorage | Vĩnh viễn (rotate khi logout) |
|
|
93
|
+
| `session_id` | AsyncStorage | Tạo mới mỗi lần `create()` |
|
|
94
|
+
| `profile_id` | AsyncStorage | Xoá khi `destroy_session()` |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Best Practices
|
|
99
|
+
|
|
100
|
+
- Không log `secretKey` hay `apiKey` vào console production.
|
|
101
|
+
- Bật `debug: false` trên production build.
|
|
102
|
+
- Rotate key theo chính sách CDP team.
|
|
103
|
+
- Dùng environment variables, không hardcode credentials.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// ✅ Đúng — dùng env vars
|
|
107
|
+
const cdp = await CdpRnSdk.create({
|
|
108
|
+
apiKey: Config.CDP_API_KEY, // react-native-config
|
|
109
|
+
secretKey: Config.CDP_SECRET_KEY,
|
|
110
|
+
});
|
|
111
|
+
```
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vc-cdp-rn-sdk",
|
|
3
|
-
"version": "0.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",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
|
-
"README.md"
|
|
9
|
+
"README.md",
|
|
10
|
+
"docs"
|
|
10
11
|
],
|
|
11
12
|
"scripts": {
|
|
12
13
|
"build": "tsc",
|
|
@@ -25,6 +26,12 @@
|
|
|
25
26
|
],
|
|
26
27
|
"author": "VietCredit",
|
|
27
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",
|
|
28
35
|
"peerDependencies": {
|
|
29
36
|
"@react-native-async-storage/async-storage": ">=1.17.0",
|
|
30
37
|
"react": ">=17.0.0",
|