banking_dcb_sdk_react_native 0.1.9
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/LICENSE +20 -0
- package/PartnerReactNativeSdk.podspec +31 -0
- package/README.md +14 -0
- package/android/build.gradle +100 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/partnerreactnativesdk/PartnerReactNativeSdkModule.kt +23 -0
- package/android/src/main/java/com/partnerreactnativesdk/PartnerReactNativeSdkPackage.kt +33 -0
- package/ios/PartnerReactNativeSdk.h +6 -0
- package/ios/PartnerReactNativeSdk.mm +29 -0
- package/lib/module/helpers/ServiceNames.js +135 -0
- package/lib/module/helpers/ServiceNames.js.map +1 -0
- package/lib/module/helpers/analytics/analytics_event_model.js +37 -0
- package/lib/module/helpers/analytics/analytics_event_model.js.map +1 -0
- package/lib/module/helpers/analytics/analytics_logger.js +73 -0
- package/lib/module/helpers/analytics/analytics_logger.js.map +1 -0
- package/lib/module/helpers/analytics/event_storage.js +35 -0
- package/lib/module/helpers/analytics/event_storage.js.map +1 -0
- package/lib/module/helpers/banking_dcb_react_native.js +297 -0
- package/lib/module/helpers/banking_dcb_react_native.js.map +1 -0
- package/lib/module/helpers/helper.js +4 -0
- package/lib/module/helpers/helper.js.map +1 -0
- package/lib/module/helpers/network/APICall.js +111 -0
- package/lib/module/helpers/network/APICall.js.map +1 -0
- package/lib/module/helpers/network/Encryption.js +148 -0
- package/lib/module/helpers/network/Encryption.js.map +1 -0
- package/lib/module/helpers/network/network_manager.js +112 -0
- package/lib/module/helpers/network/network_manager.js.map +1 -0
- package/lib/module/helpers/utils/Constants.js +12 -0
- package/lib/module/helpers/utils/Constants.js.map +1 -0
- package/lib/module/helpers/utils/LibraryConstants.js +115 -0
- package/lib/module/helpers/utils/LibraryConstants.js.map +1 -0
- package/lib/module/helpers/utils/deviceInfoManager.js +39 -0
- package/lib/module/helpers/utils/deviceInfoManager.js.map +1 -0
- package/lib/module/helpers/utils/headerManager.js +21 -0
- package/lib/module/helpers/utils/headerManager.js.map +1 -0
- package/lib/module/helpers/utils/themeManager.js +40 -0
- package/lib/module/helpers/utils/themeManager.js.map +1 -0
- package/lib/module/helpers/utils/webviewCallback.js +20 -0
- package/lib/module/helpers/utils/webviewCallback.js.map +1 -0
- package/lib/module/helpers/webview.js +314 -0
- package/lib/module/helpers/webview.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/helpers/ServiceNames.d.ts +51 -0
- package/lib/typescript/src/helpers/ServiceNames.d.ts.map +1 -0
- package/lib/typescript/src/helpers/analytics/analytics_event_model.d.ts +16 -0
- package/lib/typescript/src/helpers/analytics/analytics_event_model.d.ts.map +1 -0
- package/lib/typescript/src/helpers/analytics/analytics_logger.d.ts +17 -0
- package/lib/typescript/src/helpers/analytics/analytics_logger.d.ts.map +1 -0
- package/lib/typescript/src/helpers/analytics/event_storage.d.ts +8 -0
- package/lib/typescript/src/helpers/analytics/event_storage.d.ts.map +1 -0
- package/lib/typescript/src/helpers/banking_dcb_react_native.d.ts +33 -0
- package/lib/typescript/src/helpers/banking_dcb_react_native.d.ts.map +1 -0
- package/lib/typescript/src/helpers/helper.d.ts +1 -0
- package/lib/typescript/src/helpers/helper.d.ts.map +1 -0
- package/lib/typescript/src/helpers/network/APICall.d.ts +13 -0
- package/lib/typescript/src/helpers/network/APICall.d.ts.map +1 -0
- package/lib/typescript/src/helpers/network/Encryption.d.ts +24 -0
- package/lib/typescript/src/helpers/network/Encryption.d.ts.map +1 -0
- package/lib/typescript/src/helpers/network/network_manager.d.ts +21 -0
- package/lib/typescript/src/helpers/network/network_manager.d.ts.map +1 -0
- package/lib/typescript/src/helpers/utils/Constants.d.ts +10 -0
- package/lib/typescript/src/helpers/utils/Constants.d.ts.map +1 -0
- package/lib/typescript/src/helpers/utils/LibraryConstants.d.ts +24 -0
- package/lib/typescript/src/helpers/utils/LibraryConstants.d.ts.map +1 -0
- package/lib/typescript/src/helpers/utils/deviceInfoManager.d.ts +4 -0
- package/lib/typescript/src/helpers/utils/deviceInfoManager.d.ts.map +1 -0
- package/lib/typescript/src/helpers/utils/headerManager.d.ts +5 -0
- package/lib/typescript/src/helpers/utils/headerManager.d.ts.map +1 -0
- package/lib/typescript/src/helpers/utils/themeManager.d.ts +12 -0
- package/lib/typescript/src/helpers/utils/themeManager.d.ts.map +1 -0
- package/lib/typescript/src/helpers/utils/webviewCallback.d.ts +13 -0
- package/lib/typescript/src/helpers/utils/webviewCallback.d.ts.map +1 -0
- package/lib/typescript/src/helpers/webview.d.ts +21 -0
- package/lib/typescript/src/helpers/webview.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +170 -0
- package/react-native.config.js +12 -0
- package/src/helpers/ServiceNames.tsx +170 -0
- package/src/helpers/analytics/analytics_event_model.tsx +47 -0
- package/src/helpers/analytics/analytics_logger.tsx +91 -0
- package/src/helpers/analytics/event_storage.tsx +44 -0
- package/src/helpers/banking_dcb_react_native.tsx +413 -0
- package/src/helpers/helper.tsx +1 -0
- package/src/helpers/network/APICall.tsx +154 -0
- package/src/helpers/network/Encryption.tsx +179 -0
- package/src/helpers/network/network_manager.tsx +122 -0
- package/src/helpers/utils/Constants.tsx +10 -0
- package/src/helpers/utils/LibraryConstants.tsx +133 -0
- package/src/helpers/utils/deviceInfoManager.tsx +37 -0
- package/src/helpers/utils/headerManager.tsx +22 -0
- package/src/helpers/utils/themeManager.tsx +51 -0
- package/src/helpers/utils/webviewCallback.tsx +25 -0
- package/src/helpers/webview.tsx +410 -0
- package/src/index.tsx +5 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { APICall } from './network/APICall';
|
|
2
|
+
import { DeviceInfoManager } from './utils/deviceInfoManager';
|
|
3
|
+
import { LibraryConstants } from './utils/LibraryConstants';
|
|
4
|
+
import { type WebViewCallbackFunction } from './utils/webviewCallback';
|
|
5
|
+
import { ServiceNames } from './ServiceNames';
|
|
6
|
+
import { Constants } from './utils/Constants';
|
|
7
|
+
import { WebView } from './webview';
|
|
8
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
9
|
+
import { StatusBar } from 'react-native';
|
|
10
|
+
export type { WebViewCallbackFunction } from './utils/webviewCallback';
|
|
11
|
+
import { AnalyticsLogger } from './analytics/analytics_logger';
|
|
12
|
+
import * as base64js from 'base64-js';
|
|
13
|
+
|
|
14
|
+
type InitOptions = {
|
|
15
|
+
whitelistedDomains: string[];
|
|
16
|
+
deviceBindingEnabled: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Extend the global type to include base64FromArrayBuffer
|
|
20
|
+
declare global {
|
|
21
|
+
var base64FromArrayBuffer: ((arrayBuffer: ArrayBuffer) => string) | undefined;
|
|
22
|
+
}
|
|
23
|
+
type JSONMap = Record<string, any>;
|
|
24
|
+
|
|
25
|
+
export class BankingDcbReactNative {
|
|
26
|
+
private static instance: BankingDcbReactNative | null = null;
|
|
27
|
+
private static intialized = false;
|
|
28
|
+
|
|
29
|
+
private _apiCall = new APICall();
|
|
30
|
+
static webViewCallback: WebViewCallbackFunction;
|
|
31
|
+
private _analyticsLogger!: AnalyticsLogger;
|
|
32
|
+
private constructor() {}
|
|
33
|
+
|
|
34
|
+
static getInstance(): BankingDcbReactNative {
|
|
35
|
+
if (!BankingDcbReactNative.instance) {
|
|
36
|
+
BankingDcbReactNative.instance = new BankingDcbReactNative();
|
|
37
|
+
}
|
|
38
|
+
return BankingDcbReactNative.instance;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static async init(
|
|
42
|
+
hostName: string,
|
|
43
|
+
options: InitOptions = {
|
|
44
|
+
whitelistedDomains: [],
|
|
45
|
+
deviceBindingEnabled: false,
|
|
46
|
+
}
|
|
47
|
+
): Promise<void> {
|
|
48
|
+
// console.log("PartnerLibrary.init - initialized:", PartnerLibrary.intialized);
|
|
49
|
+
if (BankingDcbReactNative.intialized) return;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
if (!global.base64FromArrayBuffer) {
|
|
53
|
+
global.base64FromArrayBuffer = (arrayBuffer: ArrayBuffer) => {
|
|
54
|
+
return base64js.fromByteArray(new Uint8Array(arrayBuffer));
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Ensure instance exists before setup
|
|
59
|
+
if (!BankingDcbReactNative.instance) {
|
|
60
|
+
// console.log("PartnerLibrary.init - creating new instance");
|
|
61
|
+
BankingDcbReactNative.instance = new BankingDcbReactNative();
|
|
62
|
+
}
|
|
63
|
+
await BankingDcbReactNative.instance._setup(
|
|
64
|
+
hostName,
|
|
65
|
+
options.whitelistedDomains,
|
|
66
|
+
options.deviceBindingEnabled
|
|
67
|
+
);
|
|
68
|
+
BankingDcbReactNative.intialized = true;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error('Initialization failed: ', error);
|
|
71
|
+
throw new Error('Initialization failed');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async _setup(
|
|
76
|
+
hostName: string,
|
|
77
|
+
whitelistedDomains: string[],
|
|
78
|
+
deviceBindingEnabled: boolean
|
|
79
|
+
): Promise<void> {
|
|
80
|
+
LibraryConstants.init({
|
|
81
|
+
host: hostName,
|
|
82
|
+
whitelistedDomains: whitelistedDomains,
|
|
83
|
+
deviceBinding: deviceBindingEnabled,
|
|
84
|
+
});
|
|
85
|
+
this._analyticsLogger = new AnalyticsLogger();
|
|
86
|
+
// this._analyticsLogger.logEvent({ event: 'REACT_NATIVE_INTIALIZED' });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async open(
|
|
90
|
+
module: string,
|
|
91
|
+
token: string,
|
|
92
|
+
WebViewCallbackFunction: WebViewCallbackFunction
|
|
93
|
+
): Promise<React.ReactElement> {
|
|
94
|
+
// this._analyticsLogger.logEvent({ event: 'REACT_NATIVE_OPEN_FN_CALLED' });
|
|
95
|
+
|
|
96
|
+
if (!BankingDcbReactNative.intialized) {
|
|
97
|
+
throw new Error('PartnerLibrary not initialized. Call init() first.');
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
let bank = '';
|
|
101
|
+
|
|
102
|
+
if (module.includes('banking/')) {
|
|
103
|
+
const startIndex = module.indexOf('banking/') + 'banking/'.length;
|
|
104
|
+
const temp = module.substring(startIndex);
|
|
105
|
+
const endIndex = temp.indexOf('/');
|
|
106
|
+
bank = endIndex === -1 ? temp : temp.substring(0, endIndex);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return this._loginAndNavigateToWebView(
|
|
110
|
+
module,
|
|
111
|
+
bank,
|
|
112
|
+
token,
|
|
113
|
+
WebViewCallbackFunction
|
|
114
|
+
);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
console.error('Error opening webview:', e);
|
|
117
|
+
throw e;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private async _checkDeviceBinding(
|
|
122
|
+
bank: string,
|
|
123
|
+
url: string,
|
|
124
|
+
WebViewCallbackFunction: WebViewCallbackFunction
|
|
125
|
+
): Promise<React.ReactElement> {
|
|
126
|
+
// this._analyticsLogger.logEvent({
|
|
127
|
+
// event: 'REACT_NATIVE_DEVICE_BINDING_CALLED',
|
|
128
|
+
// });
|
|
129
|
+
|
|
130
|
+
console.log('SDK:LOG: entered in check device binding');
|
|
131
|
+
if (LibraryConstants.deviceBindingEnabled) {
|
|
132
|
+
// TODO: Build the device binding flow
|
|
133
|
+
return Promise.resolve(<></>);
|
|
134
|
+
} else {
|
|
135
|
+
console.log('SDK:LOG: calling setup device session');
|
|
136
|
+
|
|
137
|
+
return this._setupDeviceSession(bank, url, WebViewCallbackFunction);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private async _loginAndNavigateToWebView(
|
|
142
|
+
module: string,
|
|
143
|
+
bank: string,
|
|
144
|
+
token: string,
|
|
145
|
+
WebViewCallbackFunction: WebViewCallbackFunction
|
|
146
|
+
): Promise<React.ReactElement> {
|
|
147
|
+
// console.log("PartnerLibrary._loginAndNavigateToWebView - LibraryConstants.hostName:", LibraryConstants.hostName);
|
|
148
|
+
try {
|
|
149
|
+
// this._analyticsLogger.logEvent({ event: 'REACT_NATIVE_LOGIN_FN_CALLED' });
|
|
150
|
+
let tokenResponse: any;
|
|
151
|
+
const url = ServiceNames.LOGIN_URL;
|
|
152
|
+
console.log('SDK:LOG: ', url);
|
|
153
|
+
console.log('SDK:LOG: Token is : ', token);
|
|
154
|
+
tokenResponse = await this._apiCall.callAPI(
|
|
155
|
+
'POST',
|
|
156
|
+
ServiceNames.LOGIN_URL,
|
|
157
|
+
{
|
|
158
|
+
body: {
|
|
159
|
+
token: token,
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
console.log('SDK:LOG Token response is : ', tokenResponse);
|
|
164
|
+
// this._analyticsLogger.logEvent({
|
|
165
|
+
// event: 'REACT_NATIVE_LOGIN_FN_RESPONSE :',
|
|
166
|
+
// response: tokenResponse,
|
|
167
|
+
// });
|
|
168
|
+
|
|
169
|
+
if (tokenResponse?.data?.code === 'USER_TOKEN_EXPIRED') {
|
|
170
|
+
console.log('SDK:LOG Token expired');
|
|
171
|
+
return Promise.resolve(<></>);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (
|
|
175
|
+
tokenResponse &&
|
|
176
|
+
tokenResponse.data &&
|
|
177
|
+
typeof tokenResponse.data === 'object' &&
|
|
178
|
+
tokenResponse.data !== null &&
|
|
179
|
+
'RATE_LIMIT_USER' in tokenResponse.data
|
|
180
|
+
) {
|
|
181
|
+
console.log('SDK:LOG: rate limit user');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// console.log("Checking login success condition:", {
|
|
185
|
+
// statusCode: tokenResponse?.statusCode,
|
|
186
|
+
// code: tokenResponse?.data?.code
|
|
187
|
+
// });
|
|
188
|
+
|
|
189
|
+
if (tokenResponse?.data?.code === 'USER_LOGIN_SUCCESS') {
|
|
190
|
+
console.log('SDK:LOG: User login success');
|
|
191
|
+
this.fetchAndSetTheme();
|
|
192
|
+
return this._checkDeviceBinding(bank, module, WebViewCallbackFunction);
|
|
193
|
+
} else {
|
|
194
|
+
console.log(
|
|
195
|
+
'SDK:LOG: Login not successful. Token Response:',
|
|
196
|
+
tokenResponse
|
|
197
|
+
);
|
|
198
|
+
return Promise.resolve(<></>);
|
|
199
|
+
}
|
|
200
|
+
} catch (e) {
|
|
201
|
+
console.error('SDK:LOG: Error during login:', e);
|
|
202
|
+
throw e;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private async _setupDeviceSession(
|
|
207
|
+
bank: string,
|
|
208
|
+
url: string,
|
|
209
|
+
WebViewCallbackFunction: WebViewCallbackFunction
|
|
210
|
+
): Promise<React.ReactElement> {
|
|
211
|
+
try {
|
|
212
|
+
console.log('SDK:LOG: in setup device session');
|
|
213
|
+
|
|
214
|
+
const deviceInfo = await DeviceInfoManager.getDeviceInfo();
|
|
215
|
+
const appVersion = deviceInfo['app_version'] || 'unknown';
|
|
216
|
+
|
|
217
|
+
if (__DEV__) {
|
|
218
|
+
console.log('Device Info:', deviceInfo);
|
|
219
|
+
console.log('App Version:', appVersion);
|
|
220
|
+
}
|
|
221
|
+
console.log('SDK:LOG device uuid is ');
|
|
222
|
+
|
|
223
|
+
const response = await this._apiCall.callAPI(
|
|
224
|
+
'POST',
|
|
225
|
+
ServiceNames.DEVICE_SESSION.params({ partner: bank }),
|
|
226
|
+
{
|
|
227
|
+
body: {
|
|
228
|
+
[Constants.MANUFACTURER]: deviceInfo[Constants.MANUFACTURER],
|
|
229
|
+
[Constants.MODEL]: deviceInfo[Constants.MODEL],
|
|
230
|
+
[Constants.DEVICE_UUID]: deviceInfo[Constants.DEVICE_UUID],
|
|
231
|
+
[Constants.OS]: deviceInfo[Constants.OS],
|
|
232
|
+
[Constants.OS_VERSION]: deviceInfo[Constants.OS_VERSION],
|
|
233
|
+
[Constants.APP_VERSION]: appVersion,
|
|
234
|
+
},
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
//console.log("response from setup device session is : ", response)
|
|
238
|
+
if (
|
|
239
|
+
response &&
|
|
240
|
+
response.data &&
|
|
241
|
+
typeof response.data === 'object' &&
|
|
242
|
+
response.data !== null &&
|
|
243
|
+
'RATE_LIMIT_USER' in response.data
|
|
244
|
+
) {
|
|
245
|
+
console.log('SDK:LOG: rate limit user');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (response?.status === 200) {
|
|
249
|
+
return this._openWebView(url, WebViewCallbackFunction);
|
|
250
|
+
} else {
|
|
251
|
+
console.warn('SDK:LOG: Error setting up device session:', response);
|
|
252
|
+
return this._openWebView(url, WebViewCallbackFunction);
|
|
253
|
+
}
|
|
254
|
+
} catch (e) {
|
|
255
|
+
console.error('SDK:LOG: Error setting up device session:', e);
|
|
256
|
+
throw e;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async _openWebView(
|
|
261
|
+
url: string,
|
|
262
|
+
WebViewCallbackFunction: WebViewCallbackFunction
|
|
263
|
+
): Promise<React.ReactElement> {
|
|
264
|
+
console.log('SDK:LOG: entered in open web view');
|
|
265
|
+
console.log('SDK:LOG: url is : ', LibraryConstants.hostName + url);
|
|
266
|
+
// this._analyticsLogger.logEvent({
|
|
267
|
+
// event: 'REACT_NATIVE_WEBVIEW_OPEN_CALLED',
|
|
268
|
+
// });
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<SafeAreaView style={{ flex: 1 }}>
|
|
272
|
+
<WebView
|
|
273
|
+
url={LibraryConstants.hostName + url}
|
|
274
|
+
onCallback={WebViewCallbackFunction}
|
|
275
|
+
whitelistedUrls={LibraryConstants.whitelistedDomains}
|
|
276
|
+
hostName={LibraryConstants.hostName}
|
|
277
|
+
onPageFinished={() =>
|
|
278
|
+
console.log('SDK:LOG: WebView page finished loading')
|
|
279
|
+
}
|
|
280
|
+
/>
|
|
281
|
+
</SafeAreaView>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async fetchAndSetTheme(): Promise<void> {
|
|
286
|
+
try {
|
|
287
|
+
console.log('SDK:LOG: Fetching theme from API');
|
|
288
|
+
|
|
289
|
+
const themeResponse: any = await this._apiCall.callAPI(
|
|
290
|
+
'GET',
|
|
291
|
+
ServiceNames.THEME_URL
|
|
292
|
+
);
|
|
293
|
+
const theme = themeResponse?.data;
|
|
294
|
+
console.log('SDK:LOG: Theme response:', theme);
|
|
295
|
+
|
|
296
|
+
if (theme && theme['--color-primary-500']) {
|
|
297
|
+
const rgbValues = theme['--color-primary-500'].split(' ').map(Number);
|
|
298
|
+
if (rgbValues.length === 3) {
|
|
299
|
+
const [r, g, b] = rgbValues;
|
|
300
|
+
|
|
301
|
+
// Update LibraryConstants color
|
|
302
|
+
LibraryConstants.setPrimaryColorFromRGB(r, g, b);
|
|
303
|
+
|
|
304
|
+
// Set status bar color
|
|
305
|
+
|
|
306
|
+
StatusBar.setBackgroundColor(LibraryConstants.primaryColor);
|
|
307
|
+
StatusBar.setBarStyle('light-content');
|
|
308
|
+
|
|
309
|
+
console.log(
|
|
310
|
+
'SDK:LOG: Theme color set successfully:',
|
|
311
|
+
LibraryConstants.primaryColor
|
|
312
|
+
);
|
|
313
|
+
} else {
|
|
314
|
+
console.warn('Invalid RGB values in theme response');
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
console.warn('--color-primary-500 not found in theme response');
|
|
318
|
+
}
|
|
319
|
+
} catch (e) {
|
|
320
|
+
console.error('Error Fetching theme: ', e);
|
|
321
|
+
throw e;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async publicapi(
|
|
325
|
+
method: string,
|
|
326
|
+
apiUrl: string,
|
|
327
|
+
options?: {
|
|
328
|
+
body?: Record<string, any>;
|
|
329
|
+
headers?: Record<string, any>;
|
|
330
|
+
}
|
|
331
|
+
): Promise<any> {
|
|
332
|
+
return this._apiCall.callAPI(method, apiUrl, options);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
public async sdkLogin(token: string, partner: string): Promise<JSONMap> {
|
|
336
|
+
try {
|
|
337
|
+
// 1) LOGIN
|
|
338
|
+
console.log('SDK:LOG: ', ServiceNames.LOGIN_URL);
|
|
339
|
+
console.log('SDK:LOG: Token is: ', token);
|
|
340
|
+
|
|
341
|
+
const loginResp = await this._apiCall.callAPI(
|
|
342
|
+
'POST',
|
|
343
|
+
ServiceNames.LOGIN_URL,
|
|
344
|
+
{
|
|
345
|
+
body: { token },
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
// Normalize to a JSON object
|
|
350
|
+
const loginData: JSONMap =
|
|
351
|
+
loginResp?.data && typeof loginResp.data === 'object'
|
|
352
|
+
? { ...loginResp.data }
|
|
353
|
+
: {};
|
|
354
|
+
|
|
355
|
+
const isSuccess =
|
|
356
|
+
loginResp?.status === 200 && loginData?.code === 'USER_LOGIN_SUCCESS';
|
|
357
|
+
|
|
358
|
+
if (!isSuccess) {
|
|
359
|
+
// Not a successful login → return login JSON as-is
|
|
360
|
+
return loginData;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// 2) DEVICE SESSION
|
|
364
|
+
try {
|
|
365
|
+
console.log('SDK:LOG: in device-session');
|
|
366
|
+
|
|
367
|
+
const deviceInfo = await DeviceInfoManager.getDeviceInfo();
|
|
368
|
+
const appVersion = deviceInfo['app_version'] || 'unknown';
|
|
369
|
+
|
|
370
|
+
if (__DEV__) {
|
|
371
|
+
console.log('Device Info:', deviceInfo);
|
|
372
|
+
console.log('App Version:', appVersion);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const deviceResp = await this._apiCall.callAPI(
|
|
376
|
+
'POST',
|
|
377
|
+
ServiceNames.DEVICE_SESSION.params({ partner }),
|
|
378
|
+
{
|
|
379
|
+
body: {
|
|
380
|
+
[Constants.MANUFACTURER]: deviceInfo[Constants.MANUFACTURER],
|
|
381
|
+
[Constants.MODEL]: deviceInfo[Constants.MODEL],
|
|
382
|
+
[Constants.DEVICE_UUID]: deviceInfo[Constants.DEVICE_UUID],
|
|
383
|
+
[Constants.OS]: deviceInfo[Constants.OS], // "Android"/"iOS" from your manager
|
|
384
|
+
[Constants.OS_VERSION]: deviceInfo[Constants.OS_VERSION],
|
|
385
|
+
[Constants.APP_VERSION]: appVersion,
|
|
386
|
+
},
|
|
387
|
+
}
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
// Nest device-session response into the same login JSON
|
|
391
|
+
loginData.device =
|
|
392
|
+
deviceResp?.data && typeof deviceResp.data === 'object'
|
|
393
|
+
? deviceResp.data
|
|
394
|
+
: { success: false, message: 'device session null' };
|
|
395
|
+
|
|
396
|
+
return loginData;
|
|
397
|
+
} catch (err: any) {
|
|
398
|
+
// Keep shape stable if device session fails
|
|
399
|
+
loginData.device = {
|
|
400
|
+
success: false,
|
|
401
|
+
message: String(err?.message || err),
|
|
402
|
+
};
|
|
403
|
+
return loginData;
|
|
404
|
+
}
|
|
405
|
+
} catch (err: any) {
|
|
406
|
+
// Absolute fallback: minimal error shape
|
|
407
|
+
return {
|
|
408
|
+
success: false,
|
|
409
|
+
message: `sdkLogin failed: ${String(err?.message || err)}`,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
console.log("Helper file loaded");
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import NetworkManager from './network_manager';
|
|
2
|
+
import { HeaderManager } from '../utils/headerManager';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
generateAESKey,
|
|
6
|
+
exportAESKeyBase64,
|
|
7
|
+
encryptAESKeyWithRSA,
|
|
8
|
+
encryptAES,
|
|
9
|
+
decryptAES,
|
|
10
|
+
getWebsiteKey,
|
|
11
|
+
} from './Encryption';
|
|
12
|
+
|
|
13
|
+
export class APICall {
|
|
14
|
+
private networkManager: NetworkManager = new NetworkManager();
|
|
15
|
+
private headerManager: HeaderManager = new HeaderManager();
|
|
16
|
+
|
|
17
|
+
async callAPI(
|
|
18
|
+
method: string,
|
|
19
|
+
apiUrl: string,
|
|
20
|
+
options?: {
|
|
21
|
+
body?: Record<string, any>;
|
|
22
|
+
headers?: Record<string, any>;
|
|
23
|
+
encrypted?: boolean;
|
|
24
|
+
}
|
|
25
|
+
): Promise<any> {
|
|
26
|
+
const { body, headers, encrypted = true } = options || {};
|
|
27
|
+
try {
|
|
28
|
+
const response = encrypted
|
|
29
|
+
? await this._callAPIEncrypt(method, apiUrl, body, headers)
|
|
30
|
+
: await this._callAPIRaw(method, apiUrl, body, headers);
|
|
31
|
+
|
|
32
|
+
return response;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.log('Error in callAPI:', error);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private async _callAPIRaw(
|
|
40
|
+
method: string,
|
|
41
|
+
apiUrl: string,
|
|
42
|
+
body?: Record<string, any>,
|
|
43
|
+
headers?: Record<string, any>
|
|
44
|
+
): Promise<any> {
|
|
45
|
+
const mergedHeaders = {
|
|
46
|
+
...(await this.headerManager.generateHeaders()),
|
|
47
|
+
...(headers || {}),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return this.networkManager.request({
|
|
51
|
+
url: apiUrl,
|
|
52
|
+
method: method,
|
|
53
|
+
body: body,
|
|
54
|
+
headers: mergedHeaders,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// ----------------- ENCRYPTED CALL (quick-crypto) -----------------
|
|
58
|
+
private async _callAPIEncrypt(
|
|
59
|
+
method: string,
|
|
60
|
+
apiUrl: string,
|
|
61
|
+
body?: Record<string, any>,
|
|
62
|
+
headers?: Record<string, any>
|
|
63
|
+
): Promise<any> {
|
|
64
|
+
// 1) Generate ephemeral AES-256 key (Uint8Array)
|
|
65
|
+
const aesKey = generateAESKey(); // Uint8Array (32 bytes)
|
|
66
|
+
|
|
67
|
+
// 2) Export AES key bytes -> Base64 (for RSA input)
|
|
68
|
+
const aesKeyBase64 = exportAESKeyBase64(aesKey); // string
|
|
69
|
+
|
|
70
|
+
// 3) Fetch server public key (cached) and RSA-wrap the AES key
|
|
71
|
+
const websiteKey = await getWebsiteKey(); // { kid, public, expiry }
|
|
72
|
+
const rsaEncryptedKey = encryptAESKeyWithRSA(
|
|
73
|
+
aesKeyBase64,
|
|
74
|
+
websiteKey.public
|
|
75
|
+
); // string
|
|
76
|
+
|
|
77
|
+
// 4) Encrypt payload with AES-GCM -> Base64(IV||CT||TAG)
|
|
78
|
+
const encryptedPayload = encryptAES(JSON.stringify(body ?? {}), aesKey); // string
|
|
79
|
+
|
|
80
|
+
// 5) Merge headers (stringify all values)
|
|
81
|
+
const baseHeaders = await this.headerManager.generateHeaders();
|
|
82
|
+
const mergedHeaders: Record<string, string> = {
|
|
83
|
+
...Object.fromEntries(
|
|
84
|
+
Object.entries(baseHeaders).map(([k, v]) => [k, String(v)])
|
|
85
|
+
),
|
|
86
|
+
...(headers
|
|
87
|
+
? Object.fromEntries(
|
|
88
|
+
Object.entries(headers).map(([k, v]) => [k, String(v)])
|
|
89
|
+
)
|
|
90
|
+
: {}),
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// 6) Add encryption headers
|
|
94
|
+
const encryptedHeaders: Record<string, string> = {
|
|
95
|
+
'Content-Type': 'application/json;charset=utf-8',
|
|
96
|
+
'Accept': 'application/json',
|
|
97
|
+
...mergedHeaders,
|
|
98
|
+
'encryption_kid': websiteKey.kid ?? '',
|
|
99
|
+
'key': rsaEncryptedKey ?? '',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// 7) Make the encrypted request with { encrypted: "<blob>" }
|
|
103
|
+
const response = await this.networkManager.request({
|
|
104
|
+
url: apiUrl,
|
|
105
|
+
method,
|
|
106
|
+
body: { encrypted: encryptedPayload },
|
|
107
|
+
headers: encryptedHeaders,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// 8) Decrypt response if it has { encrypted }
|
|
111
|
+
const decrypted = APICall.handleDecryptedResponse(response, aesKey);
|
|
112
|
+
if (decrypted != null) {
|
|
113
|
+
response!.data = decrypted;
|
|
114
|
+
}
|
|
115
|
+
return response;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ----------------- RESPONSE DECRYPT (quick-crypto) -----------------
|
|
119
|
+
static handleDecryptedResponse(
|
|
120
|
+
response: any,
|
|
121
|
+
aesKey: Uint8Array
|
|
122
|
+
): Record<string, any> | null {
|
|
123
|
+
try {
|
|
124
|
+
if (!response) return null;
|
|
125
|
+
|
|
126
|
+
const raw = response.data;
|
|
127
|
+
// Accept string or object
|
|
128
|
+
const data: Record<string, any> =
|
|
129
|
+
typeof raw === 'string' ? JSON.parse(raw) : (raw as any);
|
|
130
|
+
|
|
131
|
+
if (!data || typeof data !== 'object' || !('encrypted' in data)) {
|
|
132
|
+
// no encrypted field, return null to signal "not decrypted"
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const encryptedPayload = String(data.encrypted ?? '');
|
|
137
|
+
if (!encryptedPayload) return null;
|
|
138
|
+
|
|
139
|
+
const decryptedJson = decryptAES(encryptedPayload, aesKey); // sync
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const obj = JSON.parse(decryptedJson);
|
|
143
|
+
return obj && typeof obj === 'object'
|
|
144
|
+
? (obj as Record<string, any>)
|
|
145
|
+
: null;
|
|
146
|
+
} catch {
|
|
147
|
+
// decrypted payload wasn't valid JSON
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|