react-native-trustdevice 1.1.6 → 2.0.0

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/Package.swift ADDED
@@ -0,0 +1,35 @@
1
+ // swift-tools-version: 6.0
2
+
3
+ import PackageDescription
4
+
5
+ // React Native 0.88 links this package from
6
+ // ios/build/generated/autolinking/libs/ReactNativeTrustDevice. These relative
7
+ // paths point to the React Native packages generated by `react-native spm`.
8
+ let package = Package(
9
+ name: "ReactNativeTrustDevice",
10
+ platforms: [.iOS("15.1")],
11
+ products: [
12
+ .library(name: "ReactNativeTrustDevice", targets: ["ReactNativeTrustDevice"]),
13
+ ],
14
+ dependencies: [
15
+ .package(name: "ReactNative", path: "../../../../xcframeworks"),
16
+ .package(name: "React-GeneratedCode", path: "../../../ios"),
17
+ .package(url: "https://github.com/trustdecision/trustdevice-pro-ios.git", from: "5.3.3"),
18
+ ],
19
+ targets: [
20
+ .target(
21
+ name: "ReactNativeTrustDevice",
22
+ dependencies: [
23
+ .product(name: "ReactHeaders", package: "ReactNative"),
24
+ .product(name: "ReactNativeHeaders", package: "ReactNative"),
25
+ .product(name: "ReactNativeDependenciesHeaders", package: "ReactNative"),
26
+ .product(name: "ReactAppHeaders", package: "React-GeneratedCode"),
27
+ .product(name: "TrustDecisionPro", package: "trustdevice-pro-ios"),
28
+ ],
29
+ path: "ios",
30
+ sources: ["TrustDevice.m", "TrustDeviceSe.m"],
31
+ publicHeadersPath: ".",
32
+ linkerSettings: [.unsafeFlags(["-Xlinker", "-ObjC"]) ]
33
+ ),
34
+ ]
35
+ )
package/README.md CHANGED
@@ -20,7 +20,7 @@ Please note that when integrating SDK products provided by the TrustDecision in
20
20
 
21
21
  | | Android | iOS |
22
22
  | ------------------------- | ------------------------------------ | :----------------- |
23
- | Supported System Versions | Android 5.0 and above | iOS 9.0 and above |
23
+ | Supported System Versions | Android 5.0 and above | iOS 17.6 and above with the RN 0.88 SPM example (check the native SDK minimum for your own app) |
24
24
  | Supported Architectures | armeabi-v7a, arm64-v8a, x86 | armv7,arm64,x86_64 |
25
25
 
26
26
  # Integrate
@@ -32,32 +32,44 @@ Please note that when integrating SDK products provided by the TrustDecision in
32
32
  Please execute in your react native project root directory
33
33
 
34
34
  ```shell
35
- npm install --save react-native-trustdevice@1.1.6
35
+ npm install --save react-native-trustdevice
36
36
  ```
37
37
 
38
- ### iOS behavior collection dependency (Swift Package Manager)
38
+ ### iOS (React Native 0.88.1, Swift Package Manager)
39
39
 
40
- The React Native bridge is still auto-linked through CocoaPods, but the iOS
41
- behavior collection SDK is distributed through Swift Package Manager. After
42
- running `pod install`, the host app must add the following package in Xcode:
43
-
44
- 1. Open `ios/<YourApp>.xcworkspace`.
45
- 2. Select **File > Add Package Dependencies...**.
46
- 3. Enter `https://github.com/trustdecision/TDBehavior_spm`.
47
- 4. Select **Exact Version** and enter `1.5.2`.
48
- 5. Add the `TDBehavior` product to the host application target. Do not add it
49
- to the Pods project or a Pods target.
50
-
51
- This app-target dependency cannot be declared by the plugin podspec. It is a
52
- one-time host project setting and remains in the Xcode project after subsequent
53
- `npm install` and `pod install` commands. CI can resolve it with:
40
+ The plugin contains a `Package.swift` that links the React Native bridge to
41
+ `TrustDecisionPro` (5.3.3 or later). `TDBehavior` is not included by default.
42
+ In an RN 0.88.1
43
+ SPM app, install the npm package, then run the RN SPM migration in the host app:
54
44
 
55
45
  ```shell
56
- xcodebuild -resolvePackageDependencies \
57
- -workspace ios/<YourApp>.xcworkspace \
58
- -scheme <YourApp>
46
+ npx react-native spm add --deintegrate
59
47
  ```
60
48
 
49
+ Open the generated Xcode project and build the app. The RN SPM autolinker
50
+ discovers this plugin's `Package.swift`; customers do not need to add a second
51
+ Swift package for the bridge manually. The fingerprint SDK is declared
52
+ in the bridge manifest. Run `npx react-native spm update` after changing native
53
+ dependencies. RN 0.88's SPM integration is experimental; use Xcode and Node
54
+ versions supported by RN 0.88.1 (Node 22.13+).
55
+
56
+ The example app keeps a comment-only `ios/Podfile` solely because CLI 20.2
57
+ uses that filename to detect an iOS project. It is not a CocoaPods integration.
58
+
59
+ The legacy `react-native-trustdevice.podspec` remains for existing CocoaPods
60
+ hosts. Do not link the SDK through both CocoaPods and SPM in the same target.
61
+ The native CocoaPods distribution is no longer maintained, so new iOS projects
62
+ should use the SPM path above.
63
+
64
+ ### iOS behavior collection (optional)
65
+
66
+ Only if your app calls `start`, `stop`, or `collect`, add
67
+ `https://github.com/trustdecision/TDBehavior_spm` in Xcode via **File > Add
68
+ Package Dependencies...**. Select **Exact Version** `1.5.2` and add the
69
+ `TDBehavior` product to the host App target. Do not add it to the plugin's
70
+ `Package.swift` or link it a second time through CocoaPods. Fingerprint APIs
71
+ such as `getBlackBoxAsync` and `getDeviceInfoAsync` do not require this package.
72
+
61
73
  ### Android behavior collection dependency
62
74
 
63
75
  The plugin does not bundle the Android behavior collection SDK. If behavior
@@ -73,6 +85,29 @@ dependencies {
73
85
  Then rebuild the native application. Adding or removing this dependency cannot
74
86
  be applied through Metro hot reload.
75
87
 
88
+ ### Device information and verification UI
89
+
90
+ `TrustDevice` is the enterprise module (the package's default export), and
91
+ `TrustDeviceSe` is the standard module (named export). Both expose
92
+ `getDeviceInfo()` and `getDeviceInfoAsync()` as Promise methods returning
93
+ `blackBox`, `fpVersion`, `anonymousId`, `deviceRiskScore`, `sealedResult`, and
94
+ `apiStatus: {code, message}`. The standard iOS SDK has only a callback API, so
95
+ both methods use that asynchronous native call. Call them after initialization.
96
+
97
+ ```javascript
98
+ import TrustDevice, {TrustDeviceSe} from 'react-native-trustdevice';
99
+
100
+ const info = await TrustDevice.getDeviceInfoAsync();
101
+ // For a standard-edition integration:
102
+ // const info = await TrustDeviceSe.getDeviceInfoAsync();
103
+
104
+ const captcha = await TrustDevice.showCaptcha();
105
+ // captcha.function is onSuccess or onFailed; onReady is emitted as
106
+ // a TrustDeviceCaptcha event (TrustDeviceSeCaptcha for the standard module).
107
+ const liveness = await TrustDevice.showLiveness({license: 'YOUR_LICENSE'});
108
+ // liveness.function is onSuccess or onFailed.
109
+ ```
110
+
76
111
  ### AndroidManifest.xml
77
112
 
78
113
  Declare the following permissions in the AndroidManifest.xml file under the application module
@@ -18,6 +18,9 @@ import com.trustdecision.mobrisk.TDRisk;
18
18
  import com.trustdecision.mobrisk.TDRiskCallback;
19
19
  import com.trustdecision.mobrisk.TDAPISignResult;
20
20
  import com.trustdecision.mobrisk.TDBehaviorResult;
21
+ import com.trustdecision.mobrisk.TDDeviceInfo;
22
+ import com.trustdecision.mobrisk.TDDeviceInfoCallbackAdapter;
23
+ import com.trustdecision.mobrisk.TDDeviceAPIStatus;
21
24
 
22
25
  public class TrustdeviceModule extends ReactContextBaseJavaModule {
23
26
  private final ReactApplicationContext reactContext;
@@ -74,6 +77,69 @@ public class TrustdeviceModule extends ReactContextBaseJavaModule {
74
77
  }
75
78
  }
76
79
 
80
+ private WritableMap deviceInfoResult(TDDeviceInfo info) {
81
+ WritableMap result = Arguments.createMap();
82
+ TDDeviceAPIStatus status = info.getApiStatus();
83
+ int code = status == null ? -1 : status.getCode();
84
+ String message = status == null ? "SDK returned no API status" : status.getMessage();
85
+ WritableMap apiStatus = Arguments.createMap();
86
+ apiStatus.putInt("code", code);
87
+ apiStatus.putString("message", message);
88
+ result.putString("blackBox", info.getBlackBox());
89
+ result.putString("fpVersion", info.getFpVersion());
90
+ result.putString("anonymousId", info.getAnonymousId());
91
+ result.putInt("deviceRiskScore", info.getDeviceRiskScore());
92
+ result.putString("sealedResult", info.getSealedResult());
93
+ result.putMap("apiStatus", apiStatus);
94
+ result.putInt("code", code);
95
+ result.putString("msg", message);
96
+ return result;
97
+ }
98
+
99
+ @ReactMethod
100
+ public void getDeviceInfo(Promise promise) {
101
+ new Thread(() -> {
102
+ try {
103
+ TDDeviceInfo info = TDRisk.getDeviceInfo();
104
+ if (info == null) {
105
+ promise.reject("device_info_unavailable", "SDK returned null device info");
106
+ } else {
107
+ promise.resolve(deviceInfoResult(info));
108
+ }
109
+ } catch (Exception e) {
110
+ promise.reject("getDeviceInfo Error", e);
111
+ }
112
+ }, "TrustDevice-getDeviceInfo").start();
113
+ }
114
+
115
+ @ReactMethod
116
+ public void getDeviceInfoAsync(Promise promise) {
117
+ try {
118
+ TDRisk.getDeviceInfo(new TDDeviceInfoCallbackAdapter() {
119
+ @Override
120
+ public void onResult(TDDeviceInfo info) {
121
+ if (info == null) {
122
+ promise.reject("device_info_unavailable", "SDK returned null device info");
123
+ } else {
124
+ promise.resolve(deviceInfoResult(info));
125
+ }
126
+ }
127
+ });
128
+ } catch (Exception e) {
129
+ promise.reject("getDeviceInfoAsync Error", e);
130
+ }
131
+ }
132
+
133
+ @ReactMethod
134
+ public void showCaptcha(Promise promise) {
135
+ TrustdeviceUiBridge.showCaptcha(reactContext, "TrustDeviceCaptcha", promise);
136
+ }
137
+
138
+ @ReactMethod
139
+ public void showLiveness(ReadableMap options, Promise promise) {
140
+ TrustdeviceUiBridge.showLiveness(reactContext, options, promise);
141
+ }
142
+
77
143
  @ReactMethod
78
144
  public void sign(String path, Promise promise) {
79
145
  try {
@@ -38,11 +38,17 @@ public class TrustdeviceSeModule extends ReactContextBaseJavaModule {
38
38
 
39
39
  @ReactMethod
40
40
  public void initWithOptions(ReadableMap readableMap, Promise promise) {
41
- if (readableMap != null) {
41
+ if (readableMap == null) {
42
+ promise.reject("initWithOptions Error", "options must not be null");
43
+ return;
44
+ }
45
+ try {
42
46
  HashMap<String, Object> options = readableMap.toHashMap();
43
47
  TDDeviceManager.Builder builder = TDUniRiskUtils.mapToBuilderSe(options);
44
48
  TDDeviceManager.initWithOptions(this.reactContext.getApplicationContext(), builder);
45
49
  promise.resolve(null);
50
+ } catch (Exception e) {
51
+ promise.reject("initWithOptions Error", e);
46
52
  }
47
53
  }
48
54
 
@@ -79,18 +85,20 @@ public class TrustdeviceSeModule extends ReactContextBaseJavaModule {
79
85
  // deviceRiskScore: 设备风险分数
80
86
  // apiStatus: API状态信息
81
87
  // sealedResult: 密封结果
82
- int code = apiStatus.getCode();
83
-
84
- // 创建 WritableMap 来返回结构化数据
85
88
  WritableMap resultMap = Arguments.createMap();
86
-
87
- resultMap.putInt("code", apiStatus.getCode());
89
+ int code = apiStatus == null ? -1 : apiStatus.getCode();
90
+ String message = apiStatus == null ? "SDK returned no API status" : apiStatus.getMessage();
91
+ WritableMap statusMap = Arguments.createMap();
92
+ statusMap.putInt("code", code);
93
+ statusMap.putString("message", message);
94
+ resultMap.putInt("code", code);
88
95
  resultMap.putString("fpVersion", fpVersion);
89
96
  resultMap.putString("blackBox", blackBox);
90
97
  resultMap.putString("anonymousId", anonymousId);
91
98
  resultMap.putInt("deviceRiskScore", deviceRiskScore);
92
99
  resultMap.putString("sealedResult", sealedResult);
93
- resultMap.putString("msg", apiStatus.getMessage());
100
+ resultMap.putMap("apiStatus", statusMap);
101
+ resultMap.putString("msg", message);
94
102
 
95
103
  promise.resolve(resultMap);
96
104
  }
@@ -101,6 +109,21 @@ public class TrustdeviceSeModule extends ReactContextBaseJavaModule {
101
109
  }
102
110
  }
103
111
 
112
+ @ReactMethod
113
+ public void getDeviceInfoAsync(Promise promise) {
114
+ getDeviceInfo(promise);
115
+ }
116
+
117
+ @ReactMethod
118
+ public void showCaptcha(Promise promise) {
119
+ TrustdeviceUiBridge.showCaptcha(reactContext, "TrustDeviceSeCaptcha", promise);
120
+ }
121
+
122
+ @ReactMethod
123
+ public void showLiveness(ReadableMap options, Promise promise) {
124
+ TrustdeviceUiBridge.showLiveness(reactContext, options, promise);
125
+ }
126
+
104
127
  @ReactMethod
105
128
  public void sign(String path, Promise promise) {
106
129
  try {
@@ -120,7 +143,7 @@ public class TrustdeviceSeModule extends ReactContextBaseJavaModule {
120
143
 
121
144
  @ReactMethod
122
145
  public void getSDKVersion(Promise promise) {
123
- String sdkVersion = TDRisk.getSDKVersion();
146
+ String sdkVersion = TDDeviceManager.getSDKVersion();
124
147
  promise.resolve(sdkVersion);
125
148
  }
126
- }
149
+ }
@@ -0,0 +1,125 @@
1
+ package com.trustdevice;
2
+
3
+ import android.app.Activity;
4
+
5
+ import com.facebook.react.bridge.Arguments;
6
+ import com.facebook.react.bridge.Promise;
7
+ import com.facebook.react.bridge.ReactApplicationContext;
8
+ import com.facebook.react.bridge.ReadableMap;
9
+ import com.facebook.react.bridge.UiThreadUtil;
10
+ import com.facebook.react.bridge.WritableMap;
11
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
12
+ import com.trustdecision.mobrisk.TDRisk;
13
+ import com.trustdecision.mobrisk.TDRiskCaptchaCallback;
14
+ import com.trustdecision.mobrisk.TDRiskLivenessCallback;
15
+
16
+ import org.json.JSONObject;
17
+
18
+ import java.util.Iterator;
19
+
20
+ final class TrustdeviceUiBridge {
21
+ private TrustdeviceUiBridge() {}
22
+
23
+ static void showCaptcha(ReactApplicationContext context, String eventName, Promise promise) {
24
+ UiThreadUtil.runOnUiThread(() -> {
25
+ Activity activity = context.getCurrentActivity();
26
+ if (activity == null) {
27
+ promise.reject("captcha_no_activity", "No active Activity");
28
+ return;
29
+ }
30
+ try {
31
+ TDRisk.showCaptcha(activity, new TDRiskCaptchaCallback() {
32
+ @Override
33
+ public void onReady() {
34
+ WritableMap ready = Arguments.createMap();
35
+ ready.putString("function", "onReady");
36
+ context.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
37
+ .emit(eventName, ready);
38
+ }
39
+
40
+ @Override
41
+ public void onSuccess(String token) {
42
+ WritableMap result = Arguments.createMap();
43
+ result.putString("function", "onSuccess");
44
+ result.putString("token", token);
45
+ promise.resolve(result);
46
+ }
47
+
48
+ @Override
49
+ public void onFailed(int code, String message) {
50
+ WritableMap result = Arguments.createMap();
51
+ result.putString("function", "onFailed");
52
+ result.putInt("errorCode", code);
53
+ result.putString("errorMsg", message);
54
+ promise.resolve(result);
55
+ }
56
+ });
57
+ } catch (Exception e) {
58
+ promise.reject("showCaptcha Error", e);
59
+ }
60
+ });
61
+ }
62
+
63
+ static void showLiveness(ReactApplicationContext context, ReadableMap options, Promise promise) {
64
+ UiThreadUtil.runOnUiThread(() -> {
65
+ Activity activity = context.getCurrentActivity();
66
+ if (activity == null) {
67
+ promise.reject("liveness_no_activity", "No active Activity");
68
+ return;
69
+ }
70
+ String license = options != null && options.hasKey("license") && !options.isNull("license")
71
+ ? options.getString("license") : null;
72
+ try {
73
+ TDRisk.showLiveness(activity, license, new TDRiskLivenessCallback() {
74
+ @Override
75
+ public void onSuccess(String json) {
76
+ WritableMap result = jsonResult(json);
77
+ result.putString("function", "onSuccess");
78
+ promise.resolve(result);
79
+ }
80
+
81
+ @Override
82
+ public void onError(String errorCode, String errorMsg, String sequenceId) {
83
+ WritableMap result = Arguments.createMap();
84
+ result.putString("function", "onFailed");
85
+ try {
86
+ result.putInt("code", Integer.parseInt(errorCode));
87
+ } catch (NumberFormatException ignored) {
88
+ result.putString("code", errorCode);
89
+ }
90
+ result.putString("message", errorMsg);
91
+ result.putString("sequence_id", sequenceId);
92
+ result.putString("liveness_id", "");
93
+ promise.resolve(result);
94
+ }
95
+ });
96
+ } catch (Exception e) {
97
+ promise.reject("showLiveness Error", e);
98
+ }
99
+ });
100
+ }
101
+
102
+ private static WritableMap jsonResult(String json) {
103
+ WritableMap result = Arguments.createMap();
104
+ try {
105
+ JSONObject object = new JSONObject(json);
106
+ Iterator<String> keys = object.keys();
107
+ while (keys.hasNext()) {
108
+ String key = keys.next();
109
+ Object value = object.opt(key);
110
+ if (value instanceof Boolean) {
111
+ result.putBoolean(key, (Boolean) value);
112
+ } else if (value instanceof Number) {
113
+ result.putDouble(key, ((Number) value).doubleValue());
114
+ } else if (value == JSONObject.NULL) {
115
+ result.putNull(key);
116
+ } else if (value != null) {
117
+ result.putString(key, String.valueOf(value));
118
+ }
119
+ }
120
+ } catch (Exception ignored) {
121
+ result.putString("raw", json);
122
+ }
123
+ return result;
124
+ }
125
+ }
package/index.d.ts CHANGED
@@ -10,18 +10,58 @@ export interface TDBehaviorResult {
10
10
  msg: string;
11
11
  }
12
12
 
13
+ export interface TDDeviceInfoResult {
14
+ blackBox: string;
15
+ fpVersion: string;
16
+ anonymousId: string;
17
+ deviceRiskScore: number;
18
+ sealedResult: string;
19
+ apiStatus: {code: number; message: string};
20
+ code: number;
21
+ msg: string;
22
+ }
23
+
24
+ export type TDCaptchaResult =
25
+ | {function: 'onSuccess'; token: string}
26
+ | {function: 'onFailed'; errorCode: number; errorMsg: string};
27
+
28
+ export interface TDLivenessResult {
29
+ function: 'onSuccess' | 'onFailed';
30
+ code?: number | string;
31
+ message?: string;
32
+ sequence_id?: string;
33
+ liveness_id?: string;
34
+ image?: string;
35
+ [key: string]: unknown;
36
+ }
37
+
13
38
  export interface TrustDeviceModule {
14
39
  initWithOptions(options: Record<string, unknown>): Promise<void> | void;
15
40
  getSDKVersion(): Promise<string>;
16
41
  getBlackBox(): Promise<string>;
17
42
  getBlackBoxAsync(): Promise<string>;
43
+ getDeviceInfo(): Promise<TDDeviceInfoResult>;
44
+ getDeviceInfoAsync(): Promise<TDDeviceInfoResult>;
18
45
  sign(path: string): Promise<TDSignResult>;
46
+ showCaptcha(): Promise<TDCaptchaResult>;
47
+ showLiveness(options: {license?: string; showStyle?: 'present' | 'push'}): Promise<TDLivenessResult>;
19
48
  start(): Promise<void>;
20
49
  stop(): Promise<void>;
21
50
  collect(): Promise<TDBehaviorResult>;
22
51
  }
23
52
 
53
+ export interface TrustDeviceSeModule {
54
+ initWithOptions(options: Record<string, unknown>): Promise<void> | void;
55
+ getSDKVersion(): Promise<string>;
56
+ getDeviceInfo(): Promise<TDDeviceInfoResult>;
57
+ getDeviceInfoAsync(): Promise<TDDeviceInfoResult>;
58
+ sign(path: string): Promise<TDSignResult>;
59
+ showCaptcha(): Promise<TDCaptchaResult>;
60
+ showLiveness(options: {license?: string; showStyle?: 'present' | 'push'}): Promise<TDLivenessResult>;
61
+ }
62
+
24
63
  declare const TrustDevice: TrustDeviceModule;
25
64
 
26
65
  export {TrustDevice};
66
+ export const TrustDeviceSe: TrustDeviceSeModule;
27
67
  export default TrustDevice;
package/index.js CHANGED
@@ -3,7 +3,9 @@
3
3
  // export once and preserve all existing public import styles.
4
4
  const platformModule = require('./src/TrustDevice');
5
5
  const TrustDevice = platformModule.default || platformModule;
6
+ const TrustDeviceSe = platformModule.TrustDeviceSe;
6
7
 
7
8
  module.exports = TrustDevice;
8
9
  module.exports.default = TrustDevice;
9
10
  module.exports.TrustDevice = TrustDevice;
11
+ module.exports.TrustDeviceSe = TrustDeviceSe;
package/ios/TrustDevice.h CHANGED
@@ -6,11 +6,11 @@
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
9
- #import <React/RCTBridgeModule.h>
9
+ #import <React/RCTEventEmitter.h>
10
10
 
11
11
  NS_ASSUME_NONNULL_BEGIN
12
12
 
13
- @interface TrustDevice : NSObject <RCTBridgeModule>
13
+ @interface TrustDevice : RCTEventEmitter <RCTBridgeModule>
14
14
 
15
15
  @end
16
16
 
package/ios/TrustDevice.m CHANGED
@@ -10,10 +10,31 @@
10
10
  #import <React/RCTConvert.h>
11
11
  #import <TDMobRisk/TDMobRisk.h>
12
12
 
13
+ static NSString *TDString(const char *value) {
14
+ return value ? ([NSString stringWithUTF8String:value] ?: @"") : @"";
15
+ }
16
+
17
+ static NSDictionary *TDDeviceInfoDictionary(TDDeviceResponse response) {
18
+ NSString *message = TDString(response.apiStatus.message);
19
+ return @{
20
+ @"blackBox": TDString(response.blackBox),
21
+ @"fpVersion": TDString(response.fpVersion),
22
+ @"anonymousId": TDString(response.anonymousId),
23
+ @"deviceRiskScore": @(response.deviceRiskScore),
24
+ @"sealedResult": TDString(response.sealedResult),
25
+ @"apiStatus": @{ @"code": @(response.apiStatus.code), @"message": message },
26
+ @"code": @(response.apiStatus.code),
27
+ @"msg": message,
28
+ };
29
+ }
30
+
13
31
  @implementation TrustDevice
14
32
 
15
33
  RCT_EXPORT_MODULE();
16
34
 
35
+ - (NSArray<NSString *> *)supportedEvents {
36
+ return @[@"TrustDeviceCaptcha"];
37
+ }
17
38
 
18
39
  RCT_REMAP_METHOD(getSDKVersion,
19
40
  getSDKVersionResolver:(RCTPromiseResolveBlock)resolve
@@ -26,21 +47,22 @@ RCT_REMAP_METHOD(getSDKVersion,
26
47
 
27
48
  RCT_EXPORT_METHOD(initWithOptions:(NSDictionary*)options)
28
49
  {
29
- NSMutableDictionary* mutableOptions = [options mutableCopy];
30
-
31
- // 1. 判断 debug 是否为空(nil 或 NSNull)
32
- id debugValue = options[@"debug"];
33
- if (debugValue && debugValue != [NSNull null]) {
34
- mutableOptions[@"allowd"] = debugValue; // 原逻辑保留
35
- } else {
36
- // debug 为空时,可能设置默认值或不设置
37
- mutableOptions[@"allowd"] = @(NO); // 示例默认 false
50
+ NSMutableDictionary *mutableOptions = [options mutableCopy];
51
+ if ([options[@"debug"] isKindOfClass:[NSNumber class]] && [options[@"debug"] boolValue]) {
52
+ mutableOptions[@"allowed"] = @"allowed";
38
53
  }
39
-
40
- // 2. 增加 dataCenter 赋值给 country
41
- id dataCenter = options[@"dataCenter"];
42
- if (dataCenter && dataCenter != [NSNull null]) {
43
- mutableOptions[@"country"] = dataCenter;
54
+ if ([options[@"location"] isKindOfClass:[NSNumber class]] && ![options[@"location"] boolValue]) {
55
+ mutableOptions[@"noLocation"] = @"noLocation";
56
+ }
57
+ if ([options[@"IDFA"] isKindOfClass:[NSNumber class]] && ![options[@"IDFA"] boolValue]) {
58
+ mutableOptions[@"noIDFA"] = @"noIDFA";
59
+ }
60
+ if ([options[@"deviceName"] isKindOfClass:[NSNumber class]] && ![options[@"deviceName"] boolValue]) {
61
+ mutableOptions[@"noDeviceName"] = @"noDeviceName";
62
+ }
63
+ NSString *dataCenter = options[@"dataCenter"];
64
+ if ([dataCenter isKindOfClass:[NSString class]] && dataCenter.length > 0) {
65
+ mutableOptions[@"country"] = dataCenter;
44
66
  }
45
67
 
46
68
  TDMobRiskManager_t *manager = [TDMobRiskManager sharedManager];
@@ -66,6 +88,35 @@ RCT_REMAP_METHOD(getBlackBoxAsync,
66
88
  });
67
89
  }
68
90
 
91
+ RCT_REMAP_METHOD(getDeviceInfo,
92
+ getDeviceInfoResolver:(RCTPromiseResolveBlock)resolve
93
+ rejecter:(RCTPromiseRejectBlock)reject)
94
+ {
95
+ dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{
96
+ TDMobRiskManager_t *manager = [TDMobRiskManager sharedManager];
97
+ if (manager == NULL || manager->getDeviceInfoSync == NULL) {
98
+ reject(@"device_info_unavailable", @"getDeviceInfoSync is unavailable", nil);
99
+ return;
100
+ }
101
+ TDDeviceResponse response = manager->getDeviceInfoSync();
102
+ resolve(TDDeviceInfoDictionary(response));
103
+ });
104
+ }
105
+
106
+ RCT_REMAP_METHOD(getDeviceInfoAsync,
107
+ getDeviceInfoAsyncResolver:(RCTPromiseResolveBlock)resolve
108
+ rejecter:(RCTPromiseRejectBlock)reject)
109
+ {
110
+ TDMobRiskManager_t *manager = [TDMobRiskManager sharedManager];
111
+ if (manager == NULL || manager->getDeviceInfo == NULL) {
112
+ reject(@"device_info_unavailable", @"getDeviceInfo is unavailable", nil);
113
+ return;
114
+ }
115
+ manager->getDeviceInfo(^(TDDeviceResponse response) {
116
+ resolve(TDDeviceInfoDictionary(response));
117
+ });
118
+ }
119
+
69
120
  RCT_EXPORT_METHOD(sign:(NSString*)path
70
121
  resolver:(RCTPromiseResolveBlock)resolve
71
122
  rejecter:(RCTPromiseRejectBlock)reject)
@@ -188,32 +239,76 @@ RCT_REMAP_METHOD(showCaptcha,
188
239
  rejecter:(RCTPromiseRejectBlock)reject)
189
240
  {
190
241
  TDMobRiskManager_t *manager = [TDMobRiskManager sharedManager];
191
-
242
+ if (manager == NULL || manager->showCaptcha == NULL) {
243
+ reject(@"captcha_unavailable", @"showCaptcha is unavailable", nil);
244
+ return;
245
+ }
192
246
  dispatch_async(dispatch_get_main_queue(), ^{
193
247
  UIWindow *keyWindow = [self getKeyWindow];
248
+ if (keyWindow == nil) {
249
+ reject(@"captcha_no_window", @"No active application window", nil);
250
+ return;
251
+ }
194
252
  manager->showCaptcha(keyWindow,^(TDShowCaptchaResultStruct resultStruct) {
195
- NSMutableDictionary *resultDictionary = [NSMutableDictionary dictionary];
253
+ NSDictionary *resultDictionary;
196
254
  switch (resultStruct.resultType) {
197
255
  case TDShowCaptchaResultTypeSuccess:
198
- resultDictionary[@"function"] = @"onSuccess";
199
- resultDictionary[@"token"] = @(resultStruct.validateToken);
256
+ resultDictionary = @{ @"function": @"onSuccess", @"token": TDString(resultStruct.validateToken) };
200
257
  break;
201
258
  case TDShowCaptchaResultTypeFailed:
202
- resultDictionary[@"function"] = @"onFailed";
203
- resultDictionary[@"errorCode"] = @(resultStruct.errorCode);
204
- resultDictionary[@"errorMsg"] = @(resultStruct.errorMsg);
205
- break;
206
- case TDShowCaptchaResultTypeReady:
207
- resultDictionary[@"function"] = @"onReady";
259
+ resultDictionary = @{ @"function": @"onFailed", @"errorCode": @(resultStruct.errorCode),
260
+ @"errorMsg": TDString(resultStruct.errorMsg) };
208
261
  break;
262
+ case TDShowCaptchaResultTypeReady: {
263
+ dispatch_async(dispatch_get_main_queue(), ^{
264
+ [self sendEventWithName:@"TrustDeviceCaptcha" body:@{ @"function": @"onReady" }];
265
+ });
266
+ return;
267
+ }
209
268
  default:
210
- break;
269
+ return;
211
270
  }
212
271
  resolve(resultDictionary);
213
272
  });
214
273
  });
215
274
  }
216
275
 
276
+ RCT_REMAP_METHOD(showLiveness,
277
+ showLivenessWithOptions:(NSDictionary *)options
278
+ resolver:(RCTPromiseResolveBlock)resolve
279
+ rejecter:(RCTPromiseRejectBlock)reject)
280
+ {
281
+ TDMobRiskManager_t *manager = [TDMobRiskManager sharedManager];
282
+ if (manager == NULL || manager->showLivenessWithShowStyle == NULL) {
283
+ reject(@"liveness_unavailable", @"showLiveness is unavailable", nil);
284
+ return;
285
+ }
286
+
287
+ dispatch_async(dispatch_get_main_queue(), ^{
288
+ UIViewController *controller = [self getKeyWindow].rootViewController;
289
+ while (controller.presentedViewController != nil) {
290
+ controller = controller.presentedViewController;
291
+ }
292
+ if (controller == nil) {
293
+ reject(@"liveness_no_controller", @"No active view controller", nil);
294
+ return;
295
+ }
296
+ NSString *license = [options[@"license"] isKindOfClass:[NSString class]] ? options[@"license"] : nil;
297
+ TDLivenessShowStyle style = [options[@"showStyle"] isEqual:@"push"]
298
+ ? TDLivenessShowStylePush : TDLivenessShowStylePresent;
299
+ manager->showLivenessWithShowStyle(controller, license, style,
300
+ ^(NSDictionary *success) {
301
+ NSMutableDictionary *result = [success mutableCopy] ?: [NSMutableDictionary dictionary];
302
+ result[@"function"] = @"onSuccess";
303
+ resolve(result);
304
+ },
305
+ ^(NSDictionary *failure) {
306
+ NSMutableDictionary *result = [failure mutableCopy] ?: [NSMutableDictionary dictionary];
307
+ result[@"function"] = @"onFailed";
308
+ resolve(result);
309
+ });
310
+ });
311
+ }
217
312
 
218
313
 
219
314
 
@@ -6,11 +6,11 @@
6
6
  //
7
7
 
8
8
  #import <Foundation/Foundation.h>
9
- #import <React/RCTBridgeModule.h>
9
+ #import <React/RCTEventEmitter.h>
10
10
 
11
11
  NS_ASSUME_NONNULL_BEGIN
12
12
 
13
- @interface TrustDeviceSe : NSObject <RCTBridgeModule>
13
+ @interface TrustDeviceSe : RCTEventEmitter <RCTBridgeModule>
14
14
 
15
15
  @end
16
16
 
@@ -10,10 +10,31 @@
10
10
  #import <React/RCTConvert.h>
11
11
  #import <TDMobRisk/TDMobRisk.h>
12
12
 
13
+ static NSString *TDSeString(const char *value) {
14
+ return value ? ([NSString stringWithUTF8String:value] ?: @"") : @"";
15
+ }
16
+
17
+ static NSDictionary *TDSeDeviceInfoDictionary(TDDeviceResponse response) {
18
+ NSString *message = TDSeString(response.apiStatus.message);
19
+ return @{
20
+ @"blackBox": TDSeString(response.blackBox),
21
+ @"fpVersion": TDSeString(response.fpVersion),
22
+ @"anonymousId": TDSeString(response.anonymousId),
23
+ @"deviceRiskScore": @(response.deviceRiskScore),
24
+ @"sealedResult": TDSeString(response.sealedResult),
25
+ @"apiStatus": @{ @"code": @(response.apiStatus.code), @"message": message },
26
+ @"code": @(response.apiStatus.code),
27
+ @"msg": message,
28
+ };
29
+ }
30
+
13
31
  @implementation TrustDeviceSe
14
32
 
15
33
  RCT_EXPORT_MODULE();
16
34
 
35
+ - (NSArray<NSString *> *)supportedEvents {
36
+ return @[@"TrustDeviceSeCaptcha"];
37
+ }
17
38
 
18
39
  RCT_REMAP_METHOD(getSDKVersion,
19
40
  getSDKVersionResolver:(RCTPromiseResolveBlock)resolve
@@ -26,22 +47,23 @@ RCT_REMAP_METHOD(getSDKVersion,
26
47
 
27
48
  RCT_EXPORT_METHOD(initWithOptions:(NSDictionary*)options)
28
49
  {
29
- NSMutableDictionary* mutableOptions = [options mutableCopy];
30
-
31
- // 1. 判断 debug 是否为空(nil 或 NSNull)
32
- id debugValue = options[@"debug"];
33
- if (debugValue && debugValue != [NSNull null]) {
34
- mutableOptions[@"allowd"] = debugValue; // 原逻辑保留
35
- } else {
36
- // debug 为空时,可能设置默认值或不设置
37
- mutableOptions[@"allowd"] = @(NO); // 示例默认 false
38
- }
39
-
40
- // 2. 增加 dataCenter 赋值给 country
41
- id dataCenter = options[@"dataCenter"];
42
- if (dataCenter && dataCenter != [NSNull null]) {
43
- mutableOptions[@"country"] = dataCenter;
44
- }
50
+ NSMutableDictionary *mutableOptions = [options mutableCopy];
51
+ if ([options[@"debug"] isKindOfClass:[NSNumber class]] && [options[@"debug"] boolValue]) {
52
+ mutableOptions[@"allowed"] = @"allowed";
53
+ }
54
+ if ([options[@"location"] isKindOfClass:[NSNumber class]] && ![options[@"location"] boolValue]) {
55
+ mutableOptions[@"noLocation"] = @"noLocation";
56
+ }
57
+ if ([options[@"IDFA"] isKindOfClass:[NSNumber class]] && ![options[@"IDFA"] boolValue]) {
58
+ mutableOptions[@"noIDFA"] = @"noIDFA";
59
+ }
60
+ if ([options[@"deviceName"] isKindOfClass:[NSNumber class]] && ![options[@"deviceName"] boolValue]) {
61
+ mutableOptions[@"noDeviceName"] = @"noDeviceName";
62
+ }
63
+ NSString *dataCenter = options[@"dataCenter"];
64
+ if ([dataCenter isKindOfClass:[NSString class]] && dataCenter.length > 0) {
65
+ mutableOptions[@"country"] = dataCenter;
66
+ }
45
67
 
46
68
  TDDeviceManager_t *manager = [TDDeviceManager sharedManager];
47
69
  manager->initWithOptions([mutableOptions copy]);
@@ -51,41 +73,25 @@ RCT_REMAP_METHOD(getDeviceInfo,
51
73
  getDeviceInfoResolver:(RCTPromiseResolveBlock)resolve
52
74
  rejecter:(RCTPromiseRejectBlock)reject)
53
75
  {
54
- TDDeviceManager_t *manager = [TDDeviceManager sharedManager];
55
- manager->getDeviceInfo(^(TDDeviceResponse response){
56
- int code = response.apiStatus.code;
57
- int deviceRiskScore = response.deviceRiskScore;
58
-
59
-
60
- NSString* fpVersion = @"";
61
- NSString* blackBox = @"";
62
- NSString* anonymousId = @"";
63
- NSString* message = @"";
64
- NSString* sealedResult = @"";
65
-
66
- if(code == 0) {
67
-
68
- fpVersion = response.fpVersion ? [NSString stringWithUTF8String:response.fpVersion] : @"";
69
- blackBox = response.blackBox ? [NSString stringWithUTF8String:response.blackBox] : @"";
70
- anonymousId = response.anonymousId ? [NSString stringWithUTF8String:response.anonymousId] : @"";
71
- sealedResult = response.sealedResult ? [NSString stringWithUTF8String:response.sealedResult] : @"";
72
- message = response.apiStatus.message ? [NSString stringWithUTF8String:response.apiStatus.message] : @"";
73
- } else {
74
- message = response.apiStatus.message ? [NSString stringWithUTF8String:response.apiStatus.message] : @"";
75
- }
76
-
77
- // 创建结果字典
78
- NSMutableDictionary *resultDict = [NSMutableDictionary dictionary];
79
- resultDict[@"code"] = @(code);
80
- resultDict[@"blackBox"] = blackBox;
81
- resultDict[@"fpVersion"] = fpVersion;
82
- resultDict[@"anonymousId"] = anonymousId;
83
- resultDict[@"sealedResult"] = sealedResult;
84
- resultDict[@"deviceRiskScore"] = @(deviceRiskScore);
85
- resultDict[@"msg"] = message;
86
-
87
- resolve(resultDict);
88
- });
76
+ [self resolveDeviceInfo:resolve rejecter:reject];
77
+ }
78
+
79
+ RCT_REMAP_METHOD(getDeviceInfoAsync,
80
+ getDeviceInfoAsyncResolver:(RCTPromiseResolveBlock)resolve
81
+ rejecter:(RCTPromiseRejectBlock)reject)
82
+ {
83
+ [self resolveDeviceInfo:resolve rejecter:reject];
84
+ }
85
+
86
+ - (void)resolveDeviceInfo:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
87
+ TDDeviceManager_t *manager = [TDDeviceManager sharedManager];
88
+ if (manager == NULL || manager->getDeviceInfo == NULL) {
89
+ reject(@"device_info_unavailable", @"getDeviceInfo is unavailable", nil);
90
+ return;
91
+ }
92
+ manager->getDeviceInfo(^(TDDeviceResponse response) {
93
+ resolve(TDSeDeviceInfoDictionary(response));
94
+ });
89
95
  }
90
96
 
91
97
  RCT_EXPORT_METHOD(sign:(NSString*)path
@@ -144,32 +150,75 @@ RCT_REMAP_METHOD(showCaptcha,
144
150
  rejecter:(RCTPromiseRejectBlock)reject)
145
151
  {
146
152
  TDMobRiskManager_t *manager = [TDMobRiskManager sharedManager];
147
-
153
+ if (manager == NULL || manager->showCaptcha == NULL) {
154
+ reject(@"captcha_unavailable", @"showCaptcha is unavailable", nil);
155
+ return;
156
+ }
148
157
  dispatch_async(dispatch_get_main_queue(), ^{
149
158
  UIWindow *keyWindow = [self getKeyWindow];
159
+ if (keyWindow == nil) {
160
+ reject(@"captcha_no_window", @"No active application window", nil);
161
+ return;
162
+ }
150
163
  manager->showCaptcha(keyWindow,^(TDShowCaptchaResultStruct resultStruct) {
151
- NSMutableDictionary *resultDictionary = [NSMutableDictionary dictionary];
164
+ NSDictionary *resultDictionary;
152
165
  switch (resultStruct.resultType) {
153
166
  case TDShowCaptchaResultTypeSuccess:
154
- resultDictionary[@"function"] = @"onSuccess";
155
- resultDictionary[@"token"] = @(resultStruct.validateToken);
167
+ resultDictionary = @{ @"function": @"onSuccess", @"token": TDSeString(resultStruct.validateToken) };
156
168
  break;
157
169
  case TDShowCaptchaResultTypeFailed:
158
- resultDictionary[@"function"] = @"onFailed";
159
- resultDictionary[@"errorCode"] = @(resultStruct.errorCode);
160
- resultDictionary[@"errorMsg"] = @(resultStruct.errorMsg);
161
- break;
162
- case TDShowCaptchaResultTypeReady:
163
- resultDictionary[@"function"] = @"onReady";
170
+ resultDictionary = @{ @"function": @"onFailed", @"errorCode": @(resultStruct.errorCode),
171
+ @"errorMsg": TDSeString(resultStruct.errorMsg) };
164
172
  break;
173
+ case TDShowCaptchaResultTypeReady: {
174
+ dispatch_async(dispatch_get_main_queue(), ^{
175
+ [self sendEventWithName:@"TrustDeviceSeCaptcha" body:@{ @"function": @"onReady" }];
176
+ });
177
+ return;
178
+ }
165
179
  default:
166
- break;
180
+ return;
167
181
  }
168
182
  resolve(resultDictionary);
169
183
  });
170
184
  });
171
185
  }
172
186
 
187
+ RCT_REMAP_METHOD(showLiveness,
188
+ showLivenessWithOptions:(NSDictionary *)options
189
+ resolver:(RCTPromiseResolveBlock)resolve
190
+ rejecter:(RCTPromiseRejectBlock)reject)
191
+ {
192
+ TDMobRiskManager_t *manager = [TDMobRiskManager sharedManager];
193
+ if (manager == NULL || manager->showLivenessWithShowStyle == NULL) {
194
+ reject(@"liveness_unavailable", @"showLiveness is unavailable", nil);
195
+ return;
196
+ }
197
+ dispatch_async(dispatch_get_main_queue(), ^{
198
+ UIViewController *controller = [self getKeyWindow].rootViewController;
199
+ while (controller.presentedViewController != nil) {
200
+ controller = controller.presentedViewController;
201
+ }
202
+ if (controller == nil) {
203
+ reject(@"liveness_no_controller", @"No active view controller", nil);
204
+ return;
205
+ }
206
+ NSString *license = [options[@"license"] isKindOfClass:[NSString class]] ? options[@"license"] : nil;
207
+ TDLivenessShowStyle style = [options[@"showStyle"] isEqual:@"push"]
208
+ ? TDLivenessShowStylePush : TDLivenessShowStylePresent;
209
+ manager->showLivenessWithShowStyle(controller, license, style,
210
+ ^(NSDictionary *success) {
211
+ NSMutableDictionary *result = [success mutableCopy] ?: [NSMutableDictionary dictionary];
212
+ result[@"function"] = @"onSuccess";
213
+ resolve(result);
214
+ },
215
+ ^(NSDictionary *failure) {
216
+ NSMutableDictionary *result = [failure mutableCopy] ?: [NSMutableDictionary dictionary];
217
+ result[@"function"] = @"onFailed";
218
+ resolve(result);
219
+ });
220
+ });
221
+ }
173
222
 
174
223
 
175
224
 
package/package.json CHANGED
@@ -3,10 +3,13 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.1.6",
6
+ "version": "2.0.0",
7
7
  "description": "React Native TrustDevice component",
8
8
  "main": "index.js",
9
9
  "react-native": "index.js",
10
+ "swiftpmConfig": {
11
+ "name": "ReactNativeTrustDevice"
12
+ },
10
13
  "scripts": {
11
14
  "test": "echo \"Error: no test specified\" && exit 1"
12
15
  },
@@ -24,9 +27,11 @@
24
27
  "android/build.gradle",
25
28
  "android/src",
26
29
  "ios",
30
+ "Package.swift",
27
31
  "src",
28
32
  "index.js",
29
33
  "index.d.ts",
34
+ "react-native.config.js",
30
35
  "react-native-trustdevice.podspec"
31
36
  ],
32
37
 
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ android: {
5
+ packageImportPath: 'import com.trustdevice.TrustdevicePackage;',
6
+ packageInstance: 'new TrustdevicePackage()',
7
+ },
8
+ },
9
+ },
10
+ };
@@ -1,4 +1,5 @@
1
1
 
2
2
  import {NativeModules} from 'react-native';
3
3
  const TrustDevice = NativeModules.TrustDevice;
4
- var _default=exports.default=TrustDevice;
4
+ export const TrustDeviceSe = NativeModules.TrustDeviceSe;
5
+ export default TrustDevice;
@@ -1,8 +1,8 @@
1
1
 
2
- import React, { Component, PropTypes } from 'react';
3
- import { requireNativeComponent,NativeModules } from 'react-native';
2
+ import {NativeModules} from 'react-native';
4
3
 
5
4
 
6
5
  const TrustDevice = NativeModules.TrustDevice;
6
+ export const TrustDeviceSe = NativeModules.TrustDeviceSe;
7
7
 
8
- var _default=exports.default=TrustDevice;
8
+ export default TrustDevice;