dongdayu 6 years ago
commit
fe80c8ebc7
43 changed files with 3611 additions and 0 deletions
  1. 1
    0
      .gitattributes
  2. 44
    0
      .gitignore
  3. 21
    0
      LICENSE
  4. 208
    0
      README.md
  5. 22
    0
      RNGeetestSensebot.podspec
  6. 38
    0
      android/build.gradle
  7. BIN
      android/libs/geetest_testbutton_android_v3.5.3.aar
  8. 2
    0
      android/proguard-rules.pro
  9. 9
    0
      android/src/main/AndroidManifest.xml
  10. 16
    0
      android/src/main/java/com/rnlib/geetest/sensebot/ReactGSEventType.java
  11. 152
    0
      android/src/main/java/com/rnlib/geetest/sensebot/ReactGeetestSensebotModule.java
  12. 28
    0
      android/src/main/java/com/rnlib/geetest/sensebot/ReactGeetestSensebotPackage.java
  13. 21
    0
      index.js
  14. 25
    0
      ios/Helper/GSColorHelper.h
  15. 17
    0
      ios/Helper/GSColorHelper.m
  16. 20
    0
      ios/RCTGeetestSensebot.h
  17. 155
    0
      ios/RCTGeetestSensebot.m
  18. 290
    0
      ios/RCTGeetestSensebot.xcodeproj/project.pbxproj
  19. BIN
      ios/SDK/GT3Captcha.bundle/Assets.car
  20. BIN
      ios/SDK/GT3Captcha.bundle/Info.plist
  21. BIN
      ios/SDK/GT3Captcha.bundle/en.lproj/GT3Captcha.strings
  22. BIN
      ios/SDK/GT3Captcha.bundle/zh-Hans.lproj/GT3Captcha.strings
  23. BIN
      ios/SDK/GT3Captcha.bundle/zh-Hans.lproj/Info.plist
  24. BIN
      ios/SDK/GT3Captcha.bundle/zh-Hant.lproj/GT3Captcha.strings
  25. BIN
      ios/SDK/GT3Captcha.bundle/zh-Hant.lproj/Info.plist
  26. BIN
      ios/SDK/GT3Captcha.framework/GT3Captcha
  27. 21
    0
      ios/SDK/GT3Captcha.framework/Headers/GT3Captcha.h
  28. 131
    0
      ios/SDK/GT3Captcha.framework/Headers/GT3CaptchaButton.h
  29. 403
    0
      ios/SDK/GT3Captcha.framework/Headers/GT3CaptchaManager.h
  30. 61
    0
      ios/SDK/GT3Captcha.framework/Headers/GT3Error.h
  31. 112
    0
      ios/SDK/GT3Captcha.framework/Headers/GT3Utils.h
  32. BIN
      ios/SDK/GT3Captcha.framework/Info.plist
  33. 6
    0
      ios/SDK/GT3Captcha.framework/Modules/module.modulemap
  34. 40
    0
      package.json
  35. 40
    0
      scripts/file.js
  36. 18
    0
      scripts/postinstall.js
  37. 7
    0
      scripts/postlink.js
  38. 261
    0
      src/api.js
  39. 10
    0
      src/constant.js
  40. 3
    0
      src/lib/processColor.js
  41. 11
    0
      src/lib/validCaptchaParams.js
  42. 12
    0
      src/lib/validUrl.js
  43. 1406
    0
      yarn.lock

+ 1
- 0
.gitattributes View File

@@ -0,0 +1 @@
1
+*.pbxproj -text

+ 44
- 0
.gitignore View File

@@ -0,0 +1,44 @@
1
+# OSX
2
+#
3
+.DS_Store
4
+
5
+# node.js
6
+#
7
+node_modules/
8
+npm-debug.log
9
+yarn-error.log
10
+
11
+
12
+# Xcode
13
+#
14
+build/
15
+*.pbxuser
16
+!default.pbxuser
17
+*.mode1v3
18
+!default.mode1v3
19
+*.mode2v3
20
+!default.mode2v3
21
+*.perspectivev3
22
+!default.perspectivev3
23
+xcuserdata
24
+*.xccheckout
25
+*.moved-aside
26
+DerivedData
27
+*.hmap
28
+*.ipa
29
+*.xcuserstate
30
+project.xcworkspace
31
+
32
+
33
+# Android/IntelliJ
34
+#
35
+build/
36
+.idea
37
+.gradle
38
+local.properties
39
+*.iml
40
+
41
+# BUCK
42
+buck-out/
43
+\.buckd/
44
+*.keystore

+ 21
- 0
LICENSE View File

@@ -0,0 +1,21 @@
1
+MIT License
2
+
3
+Copyright (c) present, Dayu Dong.
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+SOFTWARE.

+ 208
- 0
README.md View File

@@ -0,0 +1,208 @@
1
+# react-native-geetest-sensebot
2
+
3
+[GEETEST极验行为验证](https://docs.geetest.com/install/overview/start/) for React Native
4
+
5
+## 安装
6
+
7
+```bash
8
+yarn add @yyyyu/react-native-geetest-sensebot
9
+```
10
+
11
+or
12
+
13
+```bash
14
+npm install --save @yyyyu/react-native-geetest-sensebot
15
+```
16
+
17
+## 配置
18
+
19
+### ios
20
+
21
+#### 1. 自动配置(推荐)
22
+
23
+```bash
24
+react-native link @yyyyu/react-native-geetest-sensebot
25
+```
26
+
27
+如果项目**使用 Pods 管理依赖**需要在 Podfile 中添加
28
+
29
+```ruby
30
+pod 'React', :path => '../node_modules/react-native', :subspecs => ['Dependency']
31
+pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
32
+```
33
+
34
+#### 2. 手动配置
35
+
36
+1. 使用 Xcode 打开项目,在项目依赖目录(Libraries)下添加 node_modules 中的 @yyyyu/react-native-geetest-sensebot 项目
37
+2. 在 Linked Frameworks and Libraries 添加 libRCTGeetestSensebot.a
38
+
39
+#### 额外配置
40
+
41
+**手动配置和非 Pods 管理依赖情况下**需要
42
+- 将 **node_modules/@yyyyu/react-native-geetest-sensebot/ios/SDK** 目录中 **GT3Captcha.framework** **GT3Captcha.bundle** 两个文件添加到主项目中
43
+- 将 **GT3Captcha.framework** 文件所在路径添加到 Build Settings 的 Framework Search Paths 中
44
+
45
+### android
46
+
47
+#### 1. 自动配置(如果 IOS 已经运行过,不需要重复运行)
48
+
49
+```bash
50
+react-native link @yyyyu/react-native-geetest-sensebot
51
+```
52
+
53
+#### 2. 手动配置
54
+
55
+1. 在 android/settings.gradle 文件中添加
56
+
57
+    ```Groovy
58
+    include ':react-native-geetest-sensebot'
59
+    project(':react-native-geetest-sensebot').projectDir = new File(rootProject.projectDir, '../node_modules/@yyyyu/react-native-geetest-sensebot/android')
60
+    ```
61
+
62
+2. 在 android/app/build.gradle 文件中依赖部分添加
63
+
64
+    ```Groovy
65
+    dependencies {
66
+        // other dependencies
67
+        compile project(':react-native-geetest-sensebot')
68
+    }
69
+    ```
70
+
71
+3. 在 MainApplication.java 文件中添加
72
+
73
+    ```Java
74
+    import com.rnlib.geetest.sensebot.ReactGeetestSensebotPackage;
75
+
76
+    @Override
77
+    protected List<ReactPackage> getPackages() {
78
+        return Arrays.<ReactPackage>asList(
79
+            // other packages
80
+            new ReactGeetestSensebotPackage()
81
+        );
82
+    }
83
+    ```
84
+
85
+#### 额外配置
86
+
87
+1. 在 android/build.gradle 文件中添加
88
+    ```Groovy
89
+    allprojects {
90
+        repositories {
91
+            // ...other
92
+            flatDir {
93
+                dirs project(':react-native-geetest-sensebot').file('libs')
94
+            }
95
+        }
96
+    }
97
+    ```
98
+
99
+2. android/app/build.gradle 配置构建工具版本需要 (buildToolsVersion >= 25)
100
+
101
+3. AndroidManifest.xml
102
+    ```xml
103
+    // 如果存在 android:allowBackup="false" 则添加 tools:replace="android:allowBackup"
104
+    <manifest xmlns:tools="http://schemas.android.com/tools">
105
+        <application
106
+          android:allowBackup="false"
107
+            tools:replace="android:allowBackup">
108
+
109
+        </application>
110
+    </manifest>
111
+    ```
112
+
113
+## JS API
114
+
115
+```javascript
116
+import GeetestSensebot from '@yyyyu/react-native-geetest-sensebot'
117
+
118
+const api1 = 'http://www.geetest.com/demo/gt/register-test'
119
+const api2 = 'http://www.geetest.com/demo/gt/validate-test'
120
+
121
+GeetestSensebot.configApi(api1, api2)
122
+GeetestSensebot.captcha()
123
+```
124
+
125
+#### configApi 配置 api 请求地址
126
+
127
+```javascript
128
+GeetestSensebot.configApi('api1 address', 'api2 address')
129
+```
130
+
131
+#### captcha 进行行为认证
132
+
133
+```javascript
134
+GeetestSensebot.captcha({
135
+  api1ReqReplacer: (DefaultApi1Req) => { return ModifyApi1Req },
136
+  api1RespHandler: (Api1Resq) => { return { success: number, gt: string, challenge: string } },
137
+  api2ReqReplacer: (DefaultApi2Req) => { return ModifyApi2Req },
138
+  api2RespHandler: (Api2Resq) => { // do anything }
139
+})
140
+```
141
+
142
+因为 api1、api2 由使用者自行配置,所以接口的请求及返回处理方式不一定按照官方实例的方式进行,所以这里提供了 4 个可选方法,用于替换 Request 以及处理 Response,关于创建 Request 及处理 Response 参考 [MDN Request](https://developer.mozilla.org/en-US/docs/Web/API/Request)、[MDN Response](https://developer.mozilla.org/en-US/docs/Web/API/Response),同时这 4 个方法均为可选参数,不传会使用默认的方式,函数支持使用 es7 async
143
+
144
+关于 **api1RespHandler** 特别说明,由于行为验证需要将 api1 的结果数据传递到 sdk 组件中,所以需要规定一个格式才能流程正常进行下去,api1RespHandler 返回值格式必须为
145
+```json
146
+{
147
+  success: number,
148
+  gt: string,
149
+  challenge: string
150
+}
151
+```
152
+
153
+错误处理方式
154
+```javascript
155
+import GeetestSensebot, { GSError, ERROR_TYPE } from '@yyyyu/react-native-geetest-sensebot'
156
+try {
157
+  // await GeetestSensebot.captcha() ...
158
+} catch (e) {
159
+  if (e instanceof GSError) {
160
+    const { errCode, errMsg } = e
161
+    if (errCode === ERROR_TYPE.API1) {
162
+    	// api1 出现错误
163
+    }
164
+    if (errCode === ERROR_TYPE.API2) {
165
+    	// api2 出现错误
166
+    }
167
+    if (errCode === ERROR_TYPE.CAPTCHA) {
168
+    	// 行为认证过程出现错误
169
+    }
170
+    console.error(errMsg)
171
+    // 由于这个过程涉及多个接口,出错情况多种多样,所以报错使用过程来区分
172
+    // 原 SDK IOS 有错误代码及描述信息,Android 只有错误代码
173
+    // errCode 是自己定义的,errMsg IOS 使用描述信息 Android 使用错误代码
174
+    // errMsg 参数只适用于开发,如果显示给用户建议自行定义错误描述
175
+  }
176
+}
177
+```
178
+
179
+官方 API
180
+- API1 [http://www.geetest.com/demo/gt/register-test](http://www.geetest.com/demo/gt/register-test) GET 请求 无参数 返回 json 对象格式为
181
+    ```json
182
+    {
183
+      "success": number,
184
+      "challenge": string,
185
+      "gt": string,
186
+      "new_captcha": boolean
187
+    }
188
+    ```
189
+- API2 [http://www.geetest.com/demo/gt/validate-test](http://www.geetest.com/demo/gt/validate-test) POST 请求 请求参数包含
190
+    ```json
191
+    {
192
+      "geetest_challenge": string,
193
+      "geetest_seccode": string,
194
+      "geetest_validate": string
195
+    }
196
+    ```
197
+
198
+#### setMaskColor 设置认证页面背景颜色 iosOnly
199
+
200
+```javascript
201
+GeetestSensebot.setMaskColor('color string')
202
+```
203
+
204
+#### enableDebug 设置是否输出调试信息 iosOnly
205
+
206
+```javascript
207
+GeetestSensebot.enableDebug(true)
208
+```

+ 22
- 0
RNGeetestSensebot.podspec View File

@@ -0,0 +1,22 @@
1
+require "json"
2
+
3
+package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+Pod::Spec.new do |s|
6
+  s.name                = "RNGeetestSensebot"
7
+  s.version             = package["version"]
8
+  s.summary             = "react native for geetest sensebot."
9
+  s.description         = package["description"]
10
+  s.license             = package["license"]
11
+  s.author              = { package["author"] => "g592842897@gmail.com" }
12
+  s.homepage            = package["homepage"]
13
+  s.source              = { :git => package["repository"]["url"], :tag => package["version"] }
14
+
15
+  s.requires_arc        = true
16
+  s.platform            = :ios, "8.0"
17
+  s.source_files        = "ios/RCTGeetestSensebot.{h,m}", "ios/Helper/*.{h,m}"
18
+  s.vendored_frameworks = "ios/SDK/GT3Captcha.framework"
19
+  s.resource            = "ios/SDK/GT3Captcha.bundle"
20
+  s.framework           = "JavaScriptCore", "WebKit"
21
+
22
+end

+ 38
- 0
android/build.gradle View File

@@ -0,0 +1,38 @@
1
+buildscript {
2
+    repositories {
3
+        jcenter()
4
+    }
5
+
6
+    dependencies {
7
+        classpath 'com.android.tools.build:gradle:1.3.1'
8
+    }
9
+}
10
+
11
+apply plugin: 'com.android.library'
12
+
13
+android {
14
+    compileSdkVersion 23
15
+    buildToolsVersion "25.0.3"
16
+
17
+    defaultConfig {
18
+        minSdkVersion 16
19
+        targetSdkVersion 22
20
+        versionCode 1
21
+        versionName "1.0"
22
+    }
23
+    lintOptions {
24
+        abortOnError false
25
+    }
26
+}
27
+
28
+repositories {
29
+    mavenCentral()
30
+    flatDir {
31
+        dirs 'libs'
32
+    }
33
+}
34
+
35
+dependencies {
36
+    compile 'com.facebook.react:react-native:+'
37
+    compile(name: 'geetest_testbutton_android_v3.5.3', ext: 'aar')
38
+}

BIN
android/libs/geetest_testbutton_android_v3.5.3.aar View File


+ 2
- 0
android/proguard-rules.pro View File

@@ -0,0 +1,2 @@
1
+-dontwarn com.geetest.sdk.**
2
+-keep class com.geetest.sdk.** { *; }

+ 9
- 0
android/src/main/AndroidManifest.xml View File

@@ -0,0 +1,9 @@
1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+    package="com.rnlib.geetest.sensebot">
3
+
4
+    <uses-permission android:name="android.permission.INTERNET" />
5
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
6
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
7
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
8
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
9
+</manifest>

+ 16
- 0
android/src/main/java/com/rnlib/geetest/sensebot/ReactGSEventType.java View File

@@ -0,0 +1,16 @@
1
+package com.rnlib.geetest.sensebot;
2
+
3
+public enum ReactGSEventType {
4
+    GS_CAPTCHA(1),
5
+    GS_ERROR(-1);
6
+
7
+    private final int eventType;
8
+
9
+    ReactGSEventType(int eventType) {
10
+        this.eventType = eventType;
11
+    }
12
+
13
+    public int getValue() {
14
+        return eventType;
15
+    }
16
+}

+ 152
- 0
android/src/main/java/com/rnlib/geetest/sensebot/ReactGeetestSensebotModule.java View File

@@ -0,0 +1,152 @@
1
+package com.rnlib.geetest.sensebot;
2
+
3
+import com.facebook.react.bridge.Promise;
4
+import com.facebook.react.bridge.ReactApplicationContext;
5
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
6
+import com.facebook.react.bridge.ReactMethod;
7
+import com.facebook.react.bridge.WritableMap;
8
+import com.facebook.react.bridge.WritableNativeMap;
9
+import com.facebook.react.modules.core.DeviceEventManagerModule;
10
+import com.geetest.sdk.Bind.GT3GeetestBindListener;
11
+import com.geetest.sdk.Bind.GT3GeetestUtilsBind;
12
+
13
+import org.json.JSONObject;
14
+
15
+public class ReactGeetestSensebotModule extends ReactContextBaseJavaModule {
16
+    private final ReactApplicationContext mReactContext;
17
+    private static final String ReactGSEventName = "RNGeetestSensebotEvent";
18
+    private static final String urlPlaceHolder = "";
19
+    private GT3GeetestUtilsBind gt3GeetestUtils;
20
+
21
+    public ReactGeetestSensebotModule(ReactApplicationContext reactContext) {
22
+        super(reactContext);
23
+        mReactContext = reactContext;
24
+    }
25
+
26
+    @Override
27
+    public String getName() {
28
+        return "GeetestSensebot";
29
+    }
30
+
31
+    @ReactMethod
32
+    public void initCaptchaMgr(Integer maskColor, Boolean isDebug, Promise promise) {
33
+        gt3GeetestUtils = new GT3GeetestUtilsBind(mReactContext.getCurrentActivity());
34
+        gt3GeetestUtils.setTimeout(5000);
35
+        gt3GeetestUtils.getISonto();
36
+        gt3GeetestUtils.setDialogTouch(true);
37
+
38
+        promise.resolve(true);
39
+    }
40
+
41
+    @ReactMethod
42
+    public void captcha(final Integer success, final String gt, final String challenge, String api2, Promise promise) {
43
+        if (gt3GeetestUtils == null) {
44
+            promise.resolve(false);
45
+            return;
46
+        }
47
+
48
+        // api1 result pass to captcha
49
+        JSONObject captchaParams = new JSONObject();
50
+        try {
51
+            captchaParams.put("success", success.intValue());
52
+            captchaParams.put("gt", gt);
53
+            captchaParams.put("challenge", challenge);
54
+        } catch (Exception ignored) {
55
+        }
56
+        gt3GeetestUtils.gtSetApi1Json(captchaParams);
57
+
58
+        // start captcha
59
+        mReactContext.runOnUiQueueThread(new Runnable() {
60
+            @Override
61
+            public void run() {
62
+                gt3GeetestUtils.getGeetest(mReactContext.getCurrentActivity(), urlPlaceHolder, urlPlaceHolder, null, new GT3GeetestBindListener() {
63
+                    // 行为验证结果
64
+                    @Override
65
+                    public void gt3GetDialogResult(boolean status, String resultString) {
66
+                        if (status) {
67
+                            JSONObject resultJson;
68
+                            try {
69
+                                resultJson = new JSONObject(resultString);
70
+                            } catch (Exception e) {
71
+                                resultJson = new JSONObject();
72
+                            }
73
+                            WritableMap eventBody = new WritableNativeMap();
74
+                            eventBody.putInt("type", ReactGSEventType.GS_CAPTCHA.getValue());
75
+
76
+                            WritableMap eventPayload = new WritableNativeMap();
77
+                            eventPayload.putString("code", "1");
78
+                            eventPayload.putString("message", "Success");
79
+
80
+                            WritableMap eventResult = new WritableNativeMap();
81
+                            try {
82
+                                eventResult.putString("geetest_challenge", resultJson.getString("geetest_challenge"));
83
+                                eventResult.putString("geetest_validate", resultJson.getString("geetest_validate"));
84
+                                eventResult.putString("geetest_seccode", resultJson.getString("geetest_seccode"));
85
+                            } catch (Exception ignored) {
86
+                            }
87
+
88
+                            eventPayload.putMap("result", eventResult);
89
+                            eventBody.putMap("payload", eventPayload);
90
+
91
+                            sendEvent(eventBody);
92
+
93
+                            gt3GeetestUtils.gt3TestFinish();
94
+                        }
95
+                    }
96
+
97
+                    // 没有进行行为验证就关闭了验证码
98
+                    @Override
99
+                    public void gt3CloseDialog(int i) {
100
+                        WritableMap eventBody = new WritableNativeMap();
101
+                        eventBody.putInt("type", ReactGSEventType.GS_CAPTCHA.getValue());
102
+                        eventBody.putString("errCode", "close view");
103
+                        eventBody.putString("errMsg", "user close captcha view.");
104
+
105
+                        sendEvent(eventBody);
106
+                    }
107
+
108
+                    // 自定义 api2
109
+                    @Override
110
+                    public boolean gt3SetIsCustom() {
111
+                        return true;
112
+                    }
113
+
114
+                    // 框架出错
115
+                    @Override
116
+                    public void gt3DialogOnError(String errCode) {
117
+                        WritableMap eventBody = new WritableNativeMap();
118
+                        eventBody.putInt("type", ReactGSEventType.GS_ERROR.getValue());
119
+                        eventBody.putString("errCode", errCode);
120
+                        eventBody.putString("errMsg", errCode);
121
+
122
+                        sendEvent(eventBody);
123
+                    }
124
+                });
125
+            }
126
+        });
127
+
128
+        promise.resolve(true);
129
+    }
130
+
131
+    @ReactMethod
132
+    public void stopCaptcha(Promise promise) {
133
+        if (gt3GeetestUtils != null) {
134
+            mReactContext.runOnUiQueueThread(new Runnable() {
135
+                @Override
136
+                public void run() {
137
+                    gt3GeetestUtils.cancelUtils();
138
+                    gt3GeetestUtils.gt3TestClose();
139
+                    gt3GeetestUtils = null;
140
+                }
141
+            });
142
+        }
143
+
144
+        promise.resolve(true);
145
+    }
146
+
147
+    private void sendEvent(WritableMap mBody) {
148
+        mReactContext
149
+                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
150
+                .emit(ReactGSEventName, mBody);
151
+    }
152
+}

+ 28
- 0
android/src/main/java/com/rnlib/geetest/sensebot/ReactGeetestSensebotPackage.java View File

@@ -0,0 +1,28 @@
1
+package com.rnlib.geetest.sensebot;
2
+
3
+import com.facebook.react.ReactPackage;
4
+import com.facebook.react.bridge.JavaScriptModule;
5
+import com.facebook.react.bridge.NativeModule;
6
+import com.facebook.react.bridge.ReactApplicationContext;
7
+import com.facebook.react.uimanager.ViewManager;
8
+
9
+import java.util.Arrays;
10
+import java.util.Collections;
11
+import java.util.List;
12
+
13
+public class ReactGeetestSensebotPackage implements ReactPackage {
14
+    @Override
15
+    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
16
+        return Arrays.<NativeModule>asList(new ReactGeetestSensebotModule(reactContext));
17
+    }
18
+
19
+    // Deprecated from RN 0.47
20
+    public List<Class<? extends JavaScriptModule>> createJSModules() {
21
+        return Collections.emptyList();
22
+    }
23
+
24
+    @Override
25
+    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
26
+        return Collections.emptyList();
27
+    }
28
+}

+ 21
- 0
index.js View File

@@ -0,0 +1,21 @@
1
+import {
2
+  enableDebug,
3
+  configApi,
4
+  setMaskColor,
5
+  captcha
6
+
7
+  GSError
8
+} from './src/api'
9
+import { ERROR_TYPE } from './src/constant'
10
+
11
+export {
12
+  GSError,
13
+  ERROR_TYPE
14
+}
15
+
16
+export default {
17
+  enableDebug,
18
+  configApi,
19
+  setMaskColor,
20
+  captcha
21
+}

+ 25
- 0
ios/Helper/GSColorHelper.h View File

@@ -0,0 +1,25 @@
1
+//
2
+//  GSColorHelper.h
3
+//  RNGeetestSensebot
4
+//
5
+//  Created by dayu dong on 2018/5/24.
6
+//
7
+
8
+#ifndef GSColorHelper_h
9
+#define GSColorHelper_h
10
+
11
+#import <UIKit/UIKit.h>
12
+
13
+#if __has_include(<React/RCTConvert.h>)
14
+#import <React/RCTConvert.h>
15
+#else
16
+#import "RCTConvert.h"
17
+#endif
18
+
19
+@interface GSColorHelper : NSObject
20
+
21
++ (UIColor *)parseColor:(NSNumber *)aColorNumber;
22
+
23
+@end
24
+
25
+#endif /* GSColorHelper_h */

+ 17
- 0
ios/Helper/GSColorHelper.m View File

@@ -0,0 +1,17 @@
1
+//
2
+//  GSColorHelper.m
3
+//  RNGeetestSensebot
4
+//
5
+//  Created by dayu dong on 2018/5/24.
6
+//
7
+
8
+#import "GSColorHelper.h"
9
+
10
+@implementation GSColorHelper
11
+
12
++ (UIColor *)parseColor:(NSNumber *)aColorNumber
13
+{
14
+    return [RCTConvert UIColor:aColorNumber];
15
+}
16
+
17
+@end

+ 20
- 0
ios/RCTGeetestSensebot.h View File

@@ -0,0 +1,20 @@
1
+#if __has_include(<React/RCTBridgeModule.h>)
2
+#import <React/RCTBridgeModule.h>
3
+#else
4
+#import "RCTBridgeModule.h"
5
+#endif
6
+#if __has_include(<React/RCTEventEmitter.h>)
7
+#import <React/RCTEventEmitter.h>
8
+#else
9
+#import "RCTEventEmitter.h"
10
+#endif
11
+
12
+#if __has_include(<GT3Captcha/GT3Captcha.h>)
13
+#import <GT3Captcha/GT3Captcha.h>
14
+#else
15
+#import "GT3Captcha.h"
16
+#endif
17
+
18
+@interface RCTGeetestSensebot : RCTEventEmitter <RCTBridgeModule, GT3CaptchaManagerDelegate, GT3CaptchaManagerViewDelegate>
19
+
20
+@end

+ 155
- 0
ios/RCTGeetestSensebot.m View File

@@ -0,0 +1,155 @@
1
+#import "RCTGeetestSensebot.h"
2
+#import "GSColorHelper.h"
3
+
4
+#define GSResolveEvent(event) [NSNumber numberWithInteger:event]
5
+#define GSResolveBool(bool) [NSNumber numberWithBool:bool]
6
+
7
+static NSString* RCTGSEventName = @"RNGeetestSensebotEvent";
8
+
9
+typedef NS_ENUM(NSInteger, RCTGSEventType) {
10
+    GS_CAPTCHA = 1,
11
+    GS_ERROR = -1
12
+};
13
+
14
+@implementation RCTGeetestSensebot {
15
+    GT3CaptchaManager *_captchaManager;
16
+}
17
+
18
++ (BOOL)requiresMainQueueSetup
19
+{
20
+    return YES;
21
+}
22
+
23
+- (dispatch_queue_t)methodQueue
24
+{
25
+    return dispatch_get_main_queue();
26
+}
27
+
28
+RCT_EXPORT_MODULE()
29
+
30
+
31
+#pragma export methods
32
+
33
+RCT_REMAP_METHOD(initCaptchaMgr,
34
+               maskColorNum:(nonnull NSNumber *)aMaskColorNum
35
+                    isDebug:(BOOL)isDebug
36
+                   resolver:(RCTPromiseResolveBlock)resolve
37
+                   rejecter:(RCTPromiseRejectBlock)reject)
38
+{
39
+    // 初始化 captcha manager
40
+    _captchaManager = [[GT3CaptchaManager alloc] initWithAPI1:nil API2:nil timeout:5.0];
41
+    _captchaManager.delegate = self;
42
+
43
+    UIColor *aMaskColor = [GSColorHelper parseColor:aMaskColorNum];
44
+    _captchaManager.maskColor = aMaskColor;
45
+    [_captchaManager enableDebugMode:isDebug];
46
+
47
+    resolve(GSResolveBool(YES));
48
+}
49
+
50
+RCT_REMAP_METHOD(captcha,
51
+     captchaWithSuccess:(nonnull NSNumber *)success
52
+                     gt:(NSString *)gt
53
+              challenge:(NSString *)challenge
54
+                   api2:(NSString *)api2
55
+               resolver:(RCTPromiseResolveBlock)resolve
56
+               rejecter:(RCTPromiseRejectBlock)reject)
57
+{
58
+    if (!_captchaManager) {
59
+        resolve(GSResolveBool(NO));
60
+        return;
61
+    }
62
+    // 行为验证
63
+    [_captchaManager configureGTest:gt challenge:challenge success:success withAPI2:api2];
64
+    [_captchaManager startGTCaptchaWithAnimated:YES];
65
+
66
+    resolve(GSResolveBool(YES));
67
+}
68
+
69
+RCT_REMAP_METHOD(stopCaptcha,
70
+         stopCaptchaResolver:(RCTPromiseResolveBlock)resolve
71
+                    rejecter:(RCTPromiseRejectBlock)reject)
72
+{
73
+    [self stopCaptchaAndClean];
74
+
75
+    resolve(GSResolveBool(YES));
76
+}
77
+
78
+
79
+#pragma other methods
80
+
81
+- (void)stopCaptchaAndClean
82
+{
83
+    if (_captchaManager) {
84
+        [_captchaManager stopGTCaptcha];
85
+        [_captchaManager closeGTViewIfIsOpen];
86
+        _captchaManager = nil;
87
+    }
88
+}
89
+
90
+
91
+#pragma gt3 delegate
92
+
93
+// disable sdk request api1
94
+- (BOOL)shouldUseDefaultRegisterAPI:(GT3CaptchaManager *)manager
95
+{
96
+    return NO;
97
+}
98
+
99
+// get captcha result
100
+- (void)gtCaptcha:(GT3CaptchaManager *)manager didReceiveCaptchaCode:(NSString *)code result:(NSDictionary *)result message:(NSString *)message
101
+{
102
+    [self sendEvent:@{
103
+                      @"type": GSResolveEvent(GS_CAPTCHA),
104
+                      @"payload": @{
105
+                              @"code": code,
106
+                              @"message": message,
107
+                              @"result": result
108
+                              }
109
+                      }];
110
+}
111
+
112
+// send user close view event to js
113
+- (void)gtCaptchaUserDidCloseGTView:(GT3CaptchaManager *)manager
114
+{
115
+    [self sendEvent:@{
116
+                      @"type": GSResolveEvent(GS_CAPTCHA),
117
+                      @"errCode": @"close view",
118
+                      @"errMsg": @"user close captcha view."
119
+                      }];
120
+}
121
+
122
+// disable sdk request api2
123
+- (BOOL)shouldUseDefaultSecondaryValidate:(GT3CaptchaManager *)manager
124
+{
125
+    return NO;
126
+}
127
+
128
+- (void)gtCaptcha:(GT3CaptchaManager *)manager didReceiveSecondaryCaptchaData:(NSData *)data response:(NSURLResponse *)response error:(GT3Error *)error decisionHandler:(void (^)(GT3SecondaryCaptchaPolicy))decisionHandler
129
+{
130
+    // no use
131
+}
132
+
133
+// manager error
134
+- (void)gtCaptcha:(GT3CaptchaManager *)manager errorHandler:(GT3Error *)error
135
+{
136
+    [self sendEvent:@{
137
+                      @"type": GSResolveEvent(GS_ERROR),
138
+                      @"errCode": error.error_code,
139
+                      @"errMsg": error.gtDescription
140
+                      }];
141
+}
142
+
143
+
144
+#pragma event
145
+
146
+- (NSArray<NSString *> *)supportedEvents
147
+{
148
+    return @[RCTGSEventName];
149
+}
150
+
151
+- (void)sendEvent:(id)body {
152
+    [self sendEventWithName:RCTGSEventName body:body];
153
+}
154
+
155
+@end

+ 290
- 0
ios/RCTGeetestSensebot.xcodeproj/project.pbxproj View File

@@ -0,0 +1,290 @@
1
+// !$*UTF8*$!
2
+{
3
+	archiveVersion = 1;
4
+	classes = {
5
+	};
6
+	objectVersion = 46;
7
+	objects = {
8
+
9
+/* Begin PBXBuildFile section */
10
+		3643C27A20B64B8E00A0B890 /* GSColorHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 3643C27920B64B8E00A0B890 /* GSColorHelper.m */; };
11
+		B3E7B58A1CC2AC0600A0062D /* RCTGeetestSensebot.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RCTGeetestSensebot.m */; };
12
+/* End PBXBuildFile section */
13
+
14
+/* Begin PBXCopyFilesBuildPhase section */
15
+		58B511D91A9E6C8500147676 /* Copy Files */ = {
16
+			isa = PBXCopyFilesBuildPhase;
17
+			buildActionMask = 2147483647;
18
+			dstPath = "include/$(PRODUCT_NAME)";
19
+			dstSubfolderSpec = 16;
20
+			files = (
21
+			);
22
+			name = "Copy Files";
23
+			runOnlyForDeploymentPostprocessing = 0;
24
+		};
25
+/* End PBXCopyFilesBuildPhase section */
26
+
27
+/* Begin PBXFileReference section */
28
+		134814201AA4EA6300B7C361 /* libRCTGeetestSensebot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTGeetestSensebot.a; sourceTree = BUILT_PRODUCTS_DIR; };
29
+		3643C27820B64B2F00A0B890 /* GSColorHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GSColorHelper.h; sourceTree = "<group>"; };
30
+		3643C27920B64B8E00A0B890 /* GSColorHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GSColorHelper.m; sourceTree = "<group>"; };
31
+		B3E7B5881CC2AC0600A0062D /* RCTGeetestSensebot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTGeetestSensebot.h; sourceTree = "<group>"; };
32
+		B3E7B5891CC2AC0600A0062D /* RCTGeetestSensebot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGeetestSensebot.m; sourceTree = "<group>"; };
33
+/* End PBXFileReference section */
34
+
35
+/* Begin PBXFrameworksBuildPhase section */
36
+		58B511D81A9E6C8500147676 /* Frameworks */ = {
37
+			isa = PBXFrameworksBuildPhase;
38
+			buildActionMask = 2147483647;
39
+			files = (
40
+			);
41
+			runOnlyForDeploymentPostprocessing = 0;
42
+		};
43
+/* End PBXFrameworksBuildPhase section */
44
+
45
+/* Begin PBXGroup section */
46
+		134814211AA4EA7D00B7C361 /* Products */ = {
47
+			isa = PBXGroup;
48
+			children = (
49
+				134814201AA4EA6300B7C361 /* libRCTGeetestSensebot.a */,
50
+			);
51
+			name = Products;
52
+			sourceTree = "<group>";
53
+		};
54
+		3643C27620B64ADF00A0B890 /* Helper */ = {
55
+			isa = PBXGroup;
56
+			children = (
57
+				3643C27820B64B2F00A0B890 /* GSColorHelper.h */,
58
+				3643C27920B64B8E00A0B890 /* GSColorHelper.m */,
59
+			);
60
+			path = Helper;
61
+			sourceTree = "<group>";
62
+		};
63
+		36CD983820B80C0500647AC8 /* Frameworks */ = {
64
+			isa = PBXGroup;
65
+			children = (
66
+			);
67
+			name = Frameworks;
68
+			sourceTree = "<group>";
69
+		};
70
+		58B511D21A9E6C8500147676 = {
71
+			isa = PBXGroup;
72
+			children = (
73
+				B3E7B5881CC2AC0600A0062D /* RCTGeetestSensebot.h */,
74
+				B3E7B5891CC2AC0600A0062D /* RCTGeetestSensebot.m */,
75
+				3643C27620B64ADF00A0B890 /* Helper */,
76
+				134814211AA4EA7D00B7C361 /* Products */,
77
+				36CD983820B80C0500647AC8 /* Frameworks */,
78
+			);
79
+			sourceTree = "<group>";
80
+		};
81
+/* End PBXGroup section */
82
+
83
+/* Begin PBXNativeTarget section */
84
+		58B511DA1A9E6C8500147676 /* RCTGeetestSensebot */ = {
85
+			isa = PBXNativeTarget;
86
+			buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTGeetestSensebot" */;
87
+			buildPhases = (
88
+				58B511D71A9E6C8500147676 /* Sources */,
89
+				58B511D81A9E6C8500147676 /* Frameworks */,
90
+				58B511D91A9E6C8500147676 /* Copy Files */,
91
+			);
92
+			buildRules = (
93
+			);
94
+			dependencies = (
95
+			);
96
+			name = RCTGeetestSensebot;
97
+			productName = RCTDataManager;
98
+			productReference = 134814201AA4EA6300B7C361 /* libRCTGeetestSensebot.a */;
99
+			productType = "com.apple.product-type.library.static";
100
+		};
101
+/* End PBXNativeTarget section */
102
+
103
+/* Begin PBXProject section */
104
+		58B511D31A9E6C8500147676 /* Project object */ = {
105
+			isa = PBXProject;
106
+			attributes = {
107
+				LastUpgradeCheck = 0830;
108
+				ORGANIZATIONNAME = Facebook;
109
+				TargetAttributes = {
110
+					58B511DA1A9E6C8500147676 = {
111
+						CreatedOnToolsVersion = 6.1.1;
112
+					};
113
+				};
114
+			};
115
+			buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTGeetestSensebot" */;
116
+			compatibilityVersion = "Xcode 3.2";
117
+			developmentRegion = English;
118
+			hasScannedForEncodings = 0;
119
+			knownRegions = (
120
+				en,
121
+			);
122
+			mainGroup = 58B511D21A9E6C8500147676;
123
+			productRefGroup = 58B511D21A9E6C8500147676;
124
+			projectDirPath = "";
125
+			projectRoot = "";
126
+			targets = (
127
+				58B511DA1A9E6C8500147676 /* RCTGeetestSensebot */,
128
+			);
129
+		};
130
+/* End PBXProject section */
131
+
132
+/* Begin PBXSourcesBuildPhase section */
133
+		58B511D71A9E6C8500147676 /* Sources */ = {
134
+			isa = PBXSourcesBuildPhase;
135
+			buildActionMask = 2147483647;
136
+			files = (
137
+				B3E7B58A1CC2AC0600A0062D /* RCTGeetestSensebot.m in Sources */,
138
+				3643C27A20B64B8E00A0B890 /* GSColorHelper.m in Sources */,
139
+			);
140
+			runOnlyForDeploymentPostprocessing = 0;
141
+		};
142
+/* End PBXSourcesBuildPhase section */
143
+
144
+/* Begin XCBuildConfiguration section */
145
+		58B511ED1A9E6C8500147676 /* Debug */ = {
146
+			isa = XCBuildConfiguration;
147
+			buildSettings = {
148
+				ALWAYS_SEARCH_USER_PATHS = NO;
149
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
150
+				CLANG_CXX_LIBRARY = "libc++";
151
+				CLANG_ENABLE_MODULES = YES;
152
+				CLANG_ENABLE_OBJC_ARC = YES;
153
+				CLANG_WARN_BOOL_CONVERSION = YES;
154
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
155
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
156
+				CLANG_WARN_EMPTY_BODY = YES;
157
+				CLANG_WARN_ENUM_CONVERSION = YES;
158
+				CLANG_WARN_INFINITE_RECURSION = YES;
159
+				CLANG_WARN_INT_CONVERSION = YES;
160
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
161
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
162
+				CLANG_WARN_UNREACHABLE_CODE = YES;
163
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
164
+				COPY_PHASE_STRIP = NO;
165
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
166
+				ENABLE_TESTABILITY = YES;
167
+				GCC_C_LANGUAGE_STANDARD = gnu99;
168
+				GCC_DYNAMIC_NO_PIC = NO;
169
+				GCC_NO_COMMON_BLOCKS = YES;
170
+				GCC_OPTIMIZATION_LEVEL = 0;
171
+				GCC_PREPROCESSOR_DEFINITIONS = (
172
+					"DEBUG=1",
173
+					"$(inherited)",
174
+				);
175
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
176
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
177
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
178
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
179
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
180
+				GCC_WARN_UNUSED_FUNCTION = YES;
181
+				GCC_WARN_UNUSED_VARIABLE = YES;
182
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
183
+				MTL_ENABLE_DEBUG_INFO = YES;
184
+				ONLY_ACTIVE_ARCH = YES;
185
+				SDKROOT = iphoneos;
186
+			};
187
+			name = Debug;
188
+		};
189
+		58B511EE1A9E6C8500147676 /* Release */ = {
190
+			isa = XCBuildConfiguration;
191
+			buildSettings = {
192
+				ALWAYS_SEARCH_USER_PATHS = NO;
193
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
194
+				CLANG_CXX_LIBRARY = "libc++";
195
+				CLANG_ENABLE_MODULES = YES;
196
+				CLANG_ENABLE_OBJC_ARC = YES;
197
+				CLANG_WARN_BOOL_CONVERSION = YES;
198
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
199
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
200
+				CLANG_WARN_EMPTY_BODY = YES;
201
+				CLANG_WARN_ENUM_CONVERSION = YES;
202
+				CLANG_WARN_INFINITE_RECURSION = YES;
203
+				CLANG_WARN_INT_CONVERSION = YES;
204
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
205
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
206
+				CLANG_WARN_UNREACHABLE_CODE = YES;
207
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
208
+				COPY_PHASE_STRIP = YES;
209
+				ENABLE_NS_ASSERTIONS = NO;
210
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
211
+				GCC_C_LANGUAGE_STANDARD = gnu99;
212
+				GCC_NO_COMMON_BLOCKS = YES;
213
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
214
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
215
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
216
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
217
+				GCC_WARN_UNUSED_FUNCTION = YES;
218
+				GCC_WARN_UNUSED_VARIABLE = YES;
219
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
220
+				MTL_ENABLE_DEBUG_INFO = NO;
221
+				SDKROOT = iphoneos;
222
+				VALIDATE_PRODUCT = YES;
223
+			};
224
+			name = Release;
225
+		};
226
+		58B511F01A9E6C8500147676 /* Debug */ = {
227
+			isa = XCBuildConfiguration;
228
+			buildSettings = {
229
+				FRAMEWORK_SEARCH_PATHS = (
230
+					"$(inherited)",
231
+					"$(PROJECT_DIR)/SDK",
232
+				);
233
+				HEADER_SEARCH_PATHS = (
234
+					"$(inherited)",
235
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
236
+					"$(SRCROOT)/../../../React/**",
237
+					"$(SRCROOT)/../../react-native/React/**",
238
+				);
239
+				LIBRARY_SEARCH_PATHS = "$(inherited)";
240
+				OTHER_LDFLAGS = "-ObjC";
241
+				PRODUCT_NAME = RCTGeetestSensebot;
242
+				SKIP_INSTALL = YES;
243
+			};
244
+			name = Debug;
245
+		};
246
+		58B511F11A9E6C8500147676 /* Release */ = {
247
+			isa = XCBuildConfiguration;
248
+			buildSettings = {
249
+				FRAMEWORK_SEARCH_PATHS = (
250
+					"$(inherited)",
251
+					"$(PROJECT_DIR)/SDK",
252
+				);
253
+				HEADER_SEARCH_PATHS = (
254
+					"$(inherited)",
255
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
256
+					"$(SRCROOT)/../../../React/**",
257
+					"$(SRCROOT)/../../react-native/React/**",
258
+				);
259
+				LIBRARY_SEARCH_PATHS = "$(inherited)";
260
+				OTHER_LDFLAGS = "-ObjC";
261
+				PRODUCT_NAME = RCTGeetestSensebot;
262
+				SKIP_INSTALL = YES;
263
+			};
264
+			name = Release;
265
+		};
266
+/* End XCBuildConfiguration section */
267
+
268
+/* Begin XCConfigurationList section */
269
+		58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTGeetestSensebot" */ = {
270
+			isa = XCConfigurationList;
271
+			buildConfigurations = (
272
+				58B511ED1A9E6C8500147676 /* Debug */,
273
+				58B511EE1A9E6C8500147676 /* Release */,
274
+			);
275
+			defaultConfigurationIsVisible = 0;
276
+			defaultConfigurationName = Release;
277
+		};
278
+		58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTGeetestSensebot" */ = {
279
+			isa = XCConfigurationList;
280
+			buildConfigurations = (
281
+				58B511F01A9E6C8500147676 /* Debug */,
282
+				58B511F11A9E6C8500147676 /* Release */,
283
+			);
284
+			defaultConfigurationIsVisible = 0;
285
+			defaultConfigurationName = Release;
286
+		};
287
+/* End XCConfigurationList section */
288
+	};
289
+	rootObject = 58B511D31A9E6C8500147676 /* Project object */;
290
+}

BIN
ios/SDK/GT3Captcha.bundle/Assets.car View File


BIN
ios/SDK/GT3Captcha.bundle/Info.plist View File


BIN
ios/SDK/GT3Captcha.bundle/en.lproj/GT3Captcha.strings View File


BIN
ios/SDK/GT3Captcha.bundle/zh-Hans.lproj/GT3Captcha.strings View File


BIN
ios/SDK/GT3Captcha.bundle/zh-Hans.lproj/Info.plist View File


BIN
ios/SDK/GT3Captcha.bundle/zh-Hant.lproj/GT3Captcha.strings View File


BIN
ios/SDK/GT3Captcha.bundle/zh-Hant.lproj/Info.plist View File


BIN
ios/SDK/GT3Captcha.framework/GT3Captcha View File


+ 21
- 0
ios/SDK/GT3Captcha.framework/Headers/GT3Captcha.h View File

@@ -0,0 +1,21 @@
1
+//
2
+//  GT3Captcha.h
3
+//  GT3Captcha
4
+//
5
+//  Created by NikoXu on 17/02/2017.
6
+//  Copyright © 2017 Xniko. All rights reserved.
7
+//
8
+
9
+#import <UIKit/UIKit.h>
10
+
11
+//! Project version number for GT3Captcha.
12
+FOUNDATION_EXPORT double GT3CaptchaVersionNumber;
13
+
14
+//! Project version string for GT3Captcha.
15
+FOUNDATION_EXPORT const unsigned char GT3CaptchaVersionString[];
16
+
17
+#import <GT3Captcha/GT3CaptchaManager.h>
18
+#import <GT3Captcha/GT3CaptchaButton.h>
19
+#import <GT3Captcha/GT3Utils.h>
20
+#import <GT3Captcha/GT3Error.h>
21
+

+ 131
- 0
ios/SDK/GT3Captcha.framework/Headers/GT3CaptchaButton.h View File

@@ -0,0 +1,131 @@
1
+//
2
+//  GTCaptchanActivityIndicator.h
3
+//  GT3Captcha
4
+//
5
+//  Created by NikoXu on 9/27/16.
6
+//  Copyright © 2016 Geetest. All rights reserved.
7
+//
8
+
9
+#import "GT3Utils.h"
10
+
11
+@class GT3CaptchaManager;
12
+
13
+@protocol GT3CaptchaButtonDelegate;
14
+
15
+@interface GT3CaptchaButton : UIControl
16
+
17
+/** Captcha Manager */
18
+@property (nonatomic, readonly, strong) GT3CaptchaManager *captchaManager;
19
+
20
+/** Captcha Button Delegate */
21
+@property (nonatomic, weak) id<GT3CaptchaButtonDelegate> delegate;
22
+
23
+/** Captcha State */
24
+@property (nonatomic, readonly, assign) GT3CaptchaState captchaState;
25
+
26
+/** Defines Inset for Captcha Button. */
27
+@property (nonatomic, assign) UIEdgeInsets captchaEdgeInsets;
28
+
29
+/**
30
+ *  @abstract
31
+ *  Use thoes keys to config title label in different
32
+ *  state on the captcha button.
33
+ *
34
+ *  @discussion
35
+ *  - contain keys: 'inactive', 'active', 'initial',
36
+ *  'waiting', 'collecting', 'computing', 'success', 
37
+ *  'fail', 'error', 'cancel'.
38
+ */
39
+@property (nonatomic, strong) NSMutableDictionary<NSString *, NSAttributedString *> *tipsDict;
40
+
41
+/**
42
+ *  Captcha Button `backgroundColor`. Defaults to
43
+ *  0xf3f3f3. Animatable.
44
+ */
45
+@property (nonatomic, strong) UIColor *mainColor;
46
+
47
+/**
48
+ *  Defines color for Captcha Indicator View background.
49
+ */
50
+@property (nonatomic, strong) UIColor *indicatorColor;
51
+
52
+/**
53
+ *  Captcha Button `layer.borderColor`. Defaults 
54
+ *  to 0xcccccc. Animatable.
55
+ */
56
+@property (nonatomic, assign) CGColorRef borderColor;
57
+
58
+/**
59
+ *  Captcha Button `layer.borderWidth` Defaults to
60
+ *  1.0. Animatable.
61
+ */
62
+@property (nonatomic, assign) CGFloat borderWidth;
63
+
64
+/**
65
+ *  Captcha Button `layer.cornerRadius`. Defaults
66
+ *  to 3.0. Animatable.
67
+ */
68
+@property (nonatomic, assign) CGFloat cornerRadius;
69
+
70
+/**
71
+ *  Logo Image View, just work in same configuration 
72
+ *  as back-end.
73
+ */
74
+@property (nonatomic, strong) UIImage *logoImage;
75
+
76
+/**
77
+ *  @abstract
78
+ *  Initializes and returns a newly allocated captcha
79
+ *  button object with the specified frame rectangle
80
+ *
81
+ *  @param frame            The frame rectangle for the button, measured in points.
82
+ *  @param captchaManager   GT3CaptchaManager instance.
83
+ *  @return A initialized GTCaptchaButton object.
84
+ */
85
+- (instancetype)initWithFrame:(CGRect)frame captchaManager:(GT3CaptchaManager *)captchaManager;
86
+
87
+/**
88
+ *  @abstract Start Captcha.
89
+ *
90
+ *  @discussion
91
+ *  Depending on captcha state, call GT3CaptchaManager
92
+ *  instance method `startGTCaptchaWithAnimated:`, 
93
+ *  `requestGTCaptcha`, `showGTViewIfRegiested` inner.
94
+ */
95
+- (void)startCaptcha;
96
+
97
+/**
98
+ *  @abstract Stop Captcha.
99
+ *
100
+ *  @discussion Call GT3CaptchaManager instance method
101
+ *  `stopGTCaptcha` inner.
102
+ */
103
+- (void)stopCaptcha;
104
+
105
+/**
106
+ *  @abstract Reset Captcha.
107
+ *
108
+ *  @discussion Call GT3CaptchaManager instance method
109
+ *  `resetCaptcha` inner.
110
+ */
111
+- (void)resetCaptcha;
112
+
113
+/**
114
+ *  @abstract Update captcha button tips label instantly.
115
+ *
116
+ *  @param title An attributed string for title
117
+ */
118
+- (void)updateTitleLabel:(NSAttributedString *)title;
119
+
120
+@end
121
+
122
+@protocol GT3CaptchaButtonDelegate <NSObject>
123
+
124
+@optional
125
+/** Return NO to disallow captcha event. Default YES. */
126
+- (BOOL)captchaButtonShouldBeginTapAction:(GT3CaptchaButton *)button;
127
+
128
+/** Called this method after GT3CaptchaButton's property 'cpatchaState' did change. */
129
+- (void)captchaButton:(GT3CaptchaButton *)button didChangeState:(GT3CaptchaState)state;
130
+
131
+@end

+ 403
- 0
ios/SDK/GT3Captcha.framework/Headers/GT3CaptchaManager.h View File

@@ -0,0 +1,403 @@
1
+//
2
+//  GTCaptchaManager.h
3
+//  GTCaptcha
4
+//
5
+//  Created by NikoXu on 8/22/16.
6
+//  Copyright © 2016 Geetest. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+#import "GT3Utils.h"
11
+#import "GT3Error.h"
12
+
13
+@protocol GT3CaptchaManagerDelegate, GT3CaptchaManagerViewDelegate, GT3CaptchaManagerStatisticDelegate;
14
+
15
+@interface GT3CaptchaManager : NSObject
16
+
17
+/** SDK版本号 */
18
++ (NSString *)sdkVersion;
19
+
20
+/** 验证管理代理 */
21
+@property (nonatomic, weak) id<GT3CaptchaManagerDelegate> delegate;
22
+/** 验证视图代理 */
23
+@property (nonatomic, weak) id<GT3CaptchaManagerViewDelegate> viewDelegate;
24
+/** 验证统计代理 */
25
+@property (nonatomic, weak) id<GT3CaptchaManagerStatisticDelegate> statisticDelegate;
26
+
27
+/** 验证状态 */
28
+@property (nonatomic, readonly) GT3CaptchaState captchaState;
29
+/** 图形验证的展示状态 */
30
+@property (nonatomic, readonly) BOOL isShowing;
31
+/** 获取启动验证参数的接口 */
32
+@property (nonatomic, readonly) NSURL *API_1;
33
+/** 进行二次验证的接口 */
34
+@property (nonatomic, readonly) NSURL *API_2;
35
+/** 验证的ID */
36
+@property (nonatomic, readonly, strong) NSString *gt_captcha_id;
37
+/** 验证的会话标识 */
38
+@property (nonatomic, readonly, strong) NSString *gt_challenge;
39
+/** 验证的服务状态, 1正常/0宕机 */
40
+@property (nonatomic, readonly, strong) NSNumber *gt_success_code;
41
+
42
+/** 背景验证 */
43
+@property (nonatomic, strong) UIColor *maskColor;
44
+
45
+#pragma mark 基本方法
46
+
47
+/** 验证单例 */
48
++ (instancetype)sharedGTManagerWithAPI1:(NSString *)api_1
49
+                                   API2:(NSString *)api_2
50
+                                timeout:(NSTimeInterval)timeout;
51
+
52
+/**
53
+ *  @abstract 验证初始化方法
54
+ *
55
+ *  @discussion 请不要在接口api_1和api_2的URL上带动态参数, 如果需要对api_1和api_2的请求做修改见GT3CaptchaManagerDelegate代理方法`gtCaptcha:willSendRequestAPI1:withReplacedHandler:`及 `gtCaptcha:willSendSecondaryCaptchaRequest:withReplacedRequest:`
56
+ *
57
+ *  @seealso `gtCaptcha:willSendRequestAPI1:withReplacedHandler:`及`gtCaptcha:willSendSecondaryCaptchaRequest:withReplacedRequest:`
58
+ *
59
+ *  @param api_1    获取验证参数的接口
60
+ *  @param api_2    进行二次验证的接口
61
+ *  @param timeout  超时时长
62
+ *  @return GT3CaptchaManager 实例
63
+ *
64
+ */
65
+- (instancetype)initWithAPI1:(NSString *)api_1
66
+                        API2:(NSString *)api_2
67
+                     timeout:(NSTimeInterval)timeout NS_DESIGNATED_INITIALIZER;
68
+
69
+/**
70
+ *  @abstract 取消异步请求
71
+ *
72
+ *  @discussion
73
+ *  当希望取消正在执行的<b>NSURLSessionDataTask</b>时,调用此方法
74
+ *
75
+ *  ❗️<b>内部请求基于NSURLSeesion</b>
76
+ */
77
+- (void)cancelRequest;
78
+
79
+/**
80
+ *  @abstract 自定义配置验证参数
81
+ *
82
+ *  @discussion
83
+ *  从后端sdk获取的验证参数, 其中单个challenge只能使用在同一次验证会话中
84
+ *
85
+ *  @param gt_public_key    在官网申请的captcha_id
86
+ *  @param gt_challenge     根据极验服务器sdk生成的challenge
87
+ *  @param gt_success_code  网站主服务器监测geetest服务的可用状态 0/1 不可用/可用
88
+ *  @param api_2            用于二次验证的接口.网站主根据极验服务端sdk来部署.
89
+ *
90
+ */
91
+- (void)configureGTest:(NSString *)gt_public_key
92
+             challenge:(NSString *)gt_challenge
93
+               success:(NSNumber *)gt_success_code
94
+              withAPI2:(NSString *)api_2;
95
+
96
+/**
97
+ *
98
+ *  @abstract 注册验证
99
+ *
100
+ *  @param completionHandler 注册成功后的回调
101
+ */
102
+- (void)registerCaptcha:(GT3CaptchaDefaultBlock)completionHandler;
103
+
104
+/**
105
+ *  ❗️<b>必要方法</b>❗️
106
+ *  @abstract 开始验证
107
+ *
108
+ *  @discussion
109
+ *  获取姿态, 提交分析后, 如有必要在`[[UIApplication sharedApplication].delegate window]`上显示极验验证的GTView验证视图
110
+ *  极验验证GTWebView通过JS与SDK通信
111
+ *  内部逻辑会根据当前的`captchaState`属性的状态不同而变更
112
+ *
113
+ */
114
+- (void)startGTCaptchaWithAnimated:(BOOL)animated;
115
+
116
+/**
117
+ *  终止验证
118
+ */
119
+- (void)stopGTCaptcha;
120
+
121
+/**
122
+ *  @abstract 重置验证
123
+ *
124
+ *  @discussion
125
+ *  内部先调用`stopGTCaptcha`后, 在主线程延迟0.3秒后
126
+ *  执行`startCaptcha`的内部方法。
127
+ *  只在`GT3CaptchaStateFail`,`GT3CaptchaStateError`,
128
+ *  `GT3CaptchaStateSuccess`, `GT3CaptchaStateCancel`状态下执行。
129
+ */
130
+- (void)resetGTCaptcha;
131
+
132
+/**
133
+ *  若验证显示则关闭验证界面
134
+ */
135
+- (void)closeGTViewIfIsOpen;
136
+
137
+/**
138
+ *  获取cookie value
139
+ *
140
+ *  @param cookieName cookie的键名
141
+ *  @return 对应的cookie的值
142
+ */
143
+- (NSString *)getCookieValue:(NSString *)cookieName;
144
+
145
+#pragma mark 其他配置的方法
146
+
147
+/**
148
+ *  @abstract 图形验证超时的时长
149
+ *
150
+ *  @param timeout GT3WebView资源请求超时时间
151
+ */
152
+- (void)useGTViewWithTimeout:(NSTimeInterval)timeout;
153
+
154
+/**
155
+ *  @abstract 验证标题
156
+ *
157
+ *  @discussion
158
+ *  默认不开启. 字符长度不能超过28, 一个中文字符为两个2字符长度.
159
+ *
160
+ *  @param title 验证标题字符串
161
+ */
162
+- (void)useGTViewWithTitle:(NSString *)title;
163
+
164
+/**
165
+ *  @abstract 配置状态指示器
166
+ *
167
+ *  @discussion
168
+ *  为了能方便的调试动画,真机调试模拟低速网络 Settings->Developer
169
+ *  ->Status->Enable->Edge(😂)
170
+ *
171
+ *  @param animationBlock 自定义时需要实现的动画block,仅在type配置为GTIndicatorCustomType时才执行
172
+ *  @param type           状态指示器的类型
173
+ */
174
+- (void)useAnimatedAcitvityIndicator:(GT3IndicatorAnimationViewBlock)animationBlock
175
+                         withIndicatorType:(GT3ActivityIndicatorType)type;
176
+
177
+/**
178
+ *  @abstract 配置背景模糊
179
+ *
180
+ *  @discussion
181
+ *  iOS8以上生效
182
+ *
183
+ *  @param blurEffect 模糊特效
184
+ */
185
+- (void)useVisualViewWithEffect:(UIBlurEffect *)blurEffect;
186
+
187
+/**
188
+ *  @abstract 切换验证语言
189
+ *
190
+ *  @discussion
191
+ *  默认中文
192
+ *
193
+ *  @param Type 语言类型
194
+ */
195
+- (void)useLanguage:(GT3LanguageType)Type;
196
+
197
+/**
198
+ *  @abstract 完全使用HTTPS协议请求验证
199
+ *
200
+ *  @discussion
201
+ *  默认开启HTTPS
202
+ *
203
+ *  @param disable 是否禁止https支持
204
+ */
205
+- (void)disableSecurityAuthentication:(BOOL)disable;
206
+
207
+/**
208
+ *  @abstract 验证背景交互事件的开关
209
+ *
210
+ *  @discussion 默认关闭
211
+ *
212
+ *  @param disable YES忽略交互事件/NO接受交互事件
213
+ */
214
+- (void)disableBackgroundUserInteraction:(BOOL)disable;
215
+
216
+/**
217
+ *  @abstract 控制验证管理器内部的网络可达性检测
218
+ *
219
+ *  @param enable YES 开启/NO 关闭. 默认YES.
220
+ */
221
+- (void)enableNetworkReachability:(BOOL)enable;
222
+
223
+/**
224
+ *  @abstract Debug Mode
225
+ *
226
+ *  @discussion
227
+ *  开启debugMode,在开启验证之前调用此方法
228
+ *  默认不开启
229
+ *
230
+ *  @param enable YES开启,NO关闭
231
+ */
232
+- (void)enableDebugMode:(BOOL)enable;
233
+
234
+@end
235
+
236
+#pragma mark - 验证代理方法
237
+
238
+@protocol GT3CaptchaManagerDelegate <NSObject>
239
+
240
+@required
241
+/**
242
+ *  验证错误处理
243
+ *
244
+ *  @discussion 抛出内部错误, 比如GTWebView等错误
245
+ *
246
+ *  @param manager  验证管理器
247
+ *  @param error    错误源
248
+ */
249
+- (void)gtCaptcha:(GT3CaptchaManager *)manager errorHandler:(GT3Error *)error;
250
+
251
+/**
252
+ *  @abstract 通知已经收到二次验证结果, 在此处理最终验证结果
253
+ *
254
+ *  @discussion
255
+ *  二次验证的错误只在这里返回, `decisionHandler`需要处理
256
+ *
257
+ *  @param manager          验证管理器
258
+ *  @param data             二次验证返回的数据
259
+ *  @param response         二次验证的响应
260
+ *  @param error            错误源
261
+ *  @param decisionHandler  更新验证结果的视图
262
+ */
263
+- (void)gtCaptcha:(GT3CaptchaManager *)manager didReceiveSecondaryCaptchaData:(NSData *)data response:(NSURLResponse *)response error:(GT3Error *)error decisionHandler:(void (^)(GT3SecondaryCaptchaPolicy captchaPolicy))decisionHandler;
264
+
265
+@optional
266
+
267
+/**
268
+ *  @abstract 是否使用内部默认的API1请求逻辑
269
+ *
270
+ *  @param manager 验证管理器
271
+ *  @return YES使用,NO不使用
272
+ */
273
+- (BOOL)shouldUseDefaultRegisterAPI:(GT3CaptchaManager *)manager;
274
+
275
+/**
276
+ *  @abstract 将要向<b>API1</b>发送请求的时候调用此方法, 通过此方法可以修改将要发送的请求
277
+ *
278
+ *  @discussion 调用此方法的时候必须执行<b>requestHandler</b>, 否则导致内存泄露。 不支持子线程。
279
+ *
280
+ *  @param manager         验证管理器
281
+ *  @param originalRequest 默认发送的请求对象
282
+ *  @param replacedHandler 修改请求的执行block
283
+ */
284
+- (void)gtCaptcha:(GT3CaptchaManager *)manager willSendRequestAPI1:(NSURLRequest *)originalRequest withReplacedHandler:(void (^)(NSURLRequest * request))replacedHandler;
285
+
286
+/**
287
+ *  @abstract 当接收到从<b>API1</b>的数据, 通知返回字典, 包括<b>gt_public_key</b>,
288
+ *  <b>gt_challenge</b>, <b>gt_success_code</b>
289
+ *
290
+ *  @discussion
291
+ *  如果实现此方法, 需要解析验证需要的数据并返回。
292
+    如果不返回验证初始化数据, 使用内部的解析规则进行解析。默认先解析一级结构, 再匹配键名为"data"或"gtcap"中的数据。
293
+ *
294
+ *  @param manager      验证管理器
295
+ *  @param dictionary   API1返回的数据(未解析)
296
+ *  @param error        返回的错误
297
+ *
298
+ *  @return 验证初始化数据, 格式见下方
299
+ <pre>
300
+ {
301
+ "challenge" : "12ae1159ffdfcbbc306897e8d9bf6d06",
302
+ "gt" : "ad872a4e1a51888967bdb7cb45589605",
303
+ "success" : 1
304
+ }
305
+ </pre>
306
+ */
307
+- (NSDictionary *)gtCaptcha:(GT3CaptchaManager *)manager didReceiveDataFromAPI1:(NSDictionary *)dictionary withError:(GT3Error *)error;
308
+
309
+/**
310
+ *  @abstract 通知接收到返回的验证交互结果
311
+ *
312
+ *  @discussion 此方法仅仅是前端返回的初步结果, 并非验证最终结果。
313
+ *
314
+ *  @param manager 验证管理器
315
+ *  @param code    验证交互结果, 0失败/1成功
316
+ *  @param result  二次验证数据
317
+ *  @param message 附带消息
318
+ */
319
+- (void)gtCaptcha:(GT3CaptchaManager *)manager didReceiveCaptchaCode:(NSString *)code result:(NSDictionary *)result message:(NSString *)message;
320
+
321
+/**
322
+ *  @abstract 是否使用内部默认的API2请求逻辑
323
+ *
324
+ *  @discussion 默认返回YES;
325
+ *
326
+ *  @param manager 验证管理器
327
+ *  @return YES使用,NO不使用
328
+ */
329
+- (BOOL)shouldUseDefaultSecondaryValidate:(GT3CaptchaManager *)manager;
330
+
331
+/**
332
+ *  @abstract 通知即将进行二次验证, 再次修改发送至<b>API2</b>的验证。 不支持子线程。
333
+ *
334
+ *  @discussion
335
+ *  请不要修改<b>requestHandler</b>执行所在的线程或队列, 否则可能导
336
+ *  请求修改失败. 二次验证的请求方式应为<b>POST</b>, 头部信息应为:
337
+ *  <pre>{"Content-Type":@"application/x-www-form-urlencoded;charset=UTF-8"}</pre>
338
+ *
339
+ *  @param manager        验证管理器
340
+ *  @param replacedRequest 修改二次验证请求的block
341
+ */
342
+- (void)gtCaptcha:(GT3CaptchaManager *)manager willSendSecondaryCaptchaRequest:(NSURLRequest *)originalRequest withReplacedRequest:(void (^)(NSMutableURLRequest * request))replacedRequest;
343
+
344
+/**
345
+ *  @abstract 用户主动关闭了验证码界面
346
+ *
347
+ *  @param manager 验证管理器
348
+ */
349
+- (void)gtCaptchaUserDidCloseGTView:(GT3CaptchaManager *)manager;
350
+
351
+@end
352
+
353
+@protocol GT3CaptchaManagerViewDelegate <NSObject>
354
+
355
+@optional
356
+
357
+/**
358
+ *  @abstract 通知验证模式
359
+ *
360
+ *  @param manager 验证管理器
361
+ *  @param mode    验证模式
362
+ */
363
+- (void)gtCaptcha:(GT3CaptchaManager *)manager notifyCaptchaMode:(GT3CaptchaMode)mode;
364
+
365
+/**
366
+ *  @abstract 通知将要显示图形验证
367
+ *
368
+ *  @param manager 验证管理器
369
+ */
370
+- (void)gtCaptchaWillShowGTView:(GT3CaptchaManager *)manager;
371
+
372
+/**
373
+ *  @abstract 更新验证状态
374
+ *
375
+ *  @param manager 验证管理器
376
+ *  @param state   验证状态
377
+ *  @param error   错误信息
378
+ */
379
+- (void)gtCaptcha:(GT3CaptchaManager *)manager updateCaptchaStatus:(GT3CaptchaState)state error:(GT3Error *)error;
380
+
381
+/**
382
+ *  @abstract 更新验证视图
383
+ *
384
+ *  @param manager         验证管理器
385
+ *  @param fromValue       起始值
386
+ *  @param toValue         终止值
387
+ *  @param timeInterval    时间间隔
388
+ */
389
+- (void)gtCaptcha:(GT3CaptchaManager *)manager updateCaptchaViewWithFactor:(CGFloat)fromValue to:(CGFloat)toValue timeInterval:(NSTimeInterval)timeInterval;
390
+
391
+@end
392
+
393
+@protocol GT3CaptchaManagerStatisticDelegate <NSObject>
394
+
395
+@optional
396
+
397
+- (void)gtCaptchaDidStartCaptcha:(GT3CaptchaManager *)manager;
398
+- (void)gtCaptcha:(GT3CaptchaManager *)manager didReceiveFullpageResult:(NSString *)result;
399
+- (void)gtCaptchaNotifyGTViewDidReady:(GT3CaptchaManager *)manager;
400
+
401
+- (void)gtCaptcha:(GT3CaptchaManager *)manager didReturnStatisticInfomation:(NSData *)data;
402
+
403
+@end

+ 61
- 0
ios/SDK/GT3Captcha.framework/Headers/GT3Error.h View File

@@ -0,0 +1,61 @@
1
+//
2
+//  GT3Error.h
3
+//  GTViewManager
4
+//
5
+//  Created by NikoXu on 8/16/16.
6
+//  Copyright © 2016 Geetest. All rights reserved.
7
+//
8
+
9
+#import <Foundation/Foundation.h>
10
+
11
+NS_ASSUME_NONNULL_BEGIN
12
+
13
+/**
14
+ *  极验定义的错误类型
15
+ */
16
+typedef NS_ENUM(NSUInteger, GT3ErrorType) {
17
+    /** 用户中断验证导致 */
18
+    GT3ErrorTypeUser,
19
+    /** 服务端返回错误 */
20
+    GT3ErrorTypeServer,
21
+    /** 内部网络抛出错误类型 */
22
+    GT3ErrorTypeNetWorking,
23
+    /** 内部浏览器抛出的错误类型 */
24
+    GT3ErrorTypeWebView,
25
+    /** 从前端库抛出的错误类型 */
26
+    GT3ErrorTypeJavaScript,
27
+    /** 内部解码错误类型 */
28
+    GT3ErrorTypeDecode,
29
+    /** 未知错误类型 */
30
+    GT3ErrorTypeUnknown
31
+};
32
+
33
+/**
34
+ *  极验封装的NSError
35
+ */
36
+@interface GT3Error : NSError
37
+
38
+/** 发生错误时接收到的元数据, 没有数据则为nil */
39
+@property (nonatomic, readonly, strong) NSData * _Nullable metaData;
40
+/** 用于定位极验问题的错误码 */
41
+@property (nonatomic, strong, readonly) NSString *error_code;
42
+/** 极验的额外错误信息, 返回userInfo */
43
+@property (nonatomic, readonly, strong) NSString *gtDescription;
44
+
45
+/** 原始的error */
46
+@property (nonatomic, readonly, strong) NSError * _Nullable originalError;
47
+
48
+/** 
49
+ *  通过提供的详细的参数初始化GT3Error
50
+ *  @seealso NSError
51
+ */
52
++ (instancetype)errorWithDomainType:(GT3ErrorType)type code:(NSInteger)code userInfo:(nullable NSDictionary *)dict withGTDesciption:(NSString *)description;
53
+/** 
54
+ *  基于提供的NSError封装成GT3Error
55
+ *  @seealso NSError
56
+ */
57
++ (instancetype)errorWithDomainType:(GT3ErrorType)type originalError:(NSError *)originalError withGTDesciption:(NSString *)description;
58
+
59
+@end
60
+
61
+NS_ASSUME_NONNULL_END

+ 112
- 0
ios/SDK/GT3Captcha.framework/Headers/GT3Utils.h View File

@@ -0,0 +1,112 @@
1
+//
2
+//  GTUtils.h
3
+//  GTFramework
4
+//
5
+//  Created by LYJ on 15/5/18.
6
+//  Copyright (c) 2015年 gt. All rights reserved.
7
+//
8
+
9
+#ifndef GTFramework_GTUtils_h
10
+#define GTFramework_GTUtils_h
11
+
12
+#import <UIKit/UIKit.h>
13
+
14
+
15
+/**
16
+ 极验验证状态的枚举量
17
+ */
18
+typedef NS_ENUM(NSInteger, GT3CaptchaState) {
19
+    /** 验证未激活 */
20
+    GT3CaptchaStateInactive = 0,
21
+    /** 验证激活 */
22
+    GT3CaptchaStateActive,
23
+    /** 验证初始化中 */
24
+    GT3CaptchaStateInitial,
25
+    /** 验证等待交互中 */
26
+    GT3CaptchaStateWaiting,
27
+    /** 验证检测数据中 */
28
+    GT3CaptchaStateCollecting,
29
+    /** 验证结果判定中 */
30
+    GT3CaptchaStateComputing,
31
+    /** 验证通过 */
32
+    GT3CaptchaStateSuccess,
33
+    /** 验证失败 */
34
+    GT3CaptchaStateFail,
35
+    /** 验证取消 */
36
+    GT3CaptchaStateCancel,
37
+    /** 验证发生错误 */
38
+    GT3CaptchaStateError
39
+};
40
+
41
+
42
+/**
43
+ * 验证模式枚举量
44
+ */
45
+typedef NS_ENUM(NSInteger, GT3CaptchaMode) {
46
+    /** 验证默认模式*/
47
+    GT3CaptchaModeDefault,
48
+    /** 验证宕机模式*/
49
+    GT3CaptchaModeFailback,
50
+    GT3CaptchaModeNoLogo,
51
+    GT3CaptchaModeLogo
52
+};
53
+
54
+/**
55
+ *  视图上结果的更新策略
56
+ */
57
+typedef NS_ENUM(NSInteger, GT3SecondaryCaptchaPolicy) {
58
+    /** 二次验证通过 */
59
+    GT3SecondaryCaptchaPolicyAllow,
60
+    /** 二次验证拒绝 */
61
+    GT3SecondaryCaptchaPolicyForbidden
62
+};
63
+
64
+/**
65
+ *  图形验证的语言选项
66
+ */
67
+typedef NS_ENUM(NSInteger, GT3LanguageType) {
68
+    /** Simplified Chinese */
69
+    GT3LANGTYPE_ZH_CN = 0,
70
+    /** Traditional Chinese */
71
+    GT3LANGTYPE_ZH_TW,
72
+    /** Traditional Chinese */
73
+    GT3LANGTYPE_ZH_HK,
74
+    /** Korean */
75
+    GT3LANGTYPE_KO_KR,// 暂不支持
76
+    /** Japenese */
77
+    GT3LANGTYPE_JA_JP,// 暂不支持
78
+    /** English */
79
+    GT3LANGTYPE_EN_US,
80
+    /** System language*/
81
+    GT3LANGTYPE_AUTO
82
+};
83
+
84
+/**
85
+ *  活动指示器类型
86
+ */
87
+typedef NS_ENUM(NSInteger, GT3ActivityIndicatorType) {
88
+    /** Geetest Defualt Indicator Type */
89
+    GT3IndicatorTypeDefault = 0,
90
+    /** System Indicator Type */
91
+    GT3IndicatorTypeSystem,
92
+    /** Cirle */
93
+    GT3IndicatorTypeCirle,
94
+    /** Custom Indicator Type */
95
+    GT3IndicatorTypeCustom
96
+};
97
+
98
+/**
99
+ *  验证默认回调
100
+ */
101
+typedef void(^GT3CaptchaDefaultBlock)(void);
102
+
103
+/**
104
+ *  自定义状态指示器的动画实现block
105
+ *
106
+ *  @param layer 状态指示器视图的layer
107
+ *  @param size  layer的大小,默认 {64, 64}
108
+ *  @param color layer的颜色,默认 蓝色 [UIColor colorWithRed:0.3 green:0.6 blue:0.9 alpha:1]
109
+ */
110
+typedef void(^GT3IndicatorAnimationViewBlock)(CALayer *layer, CGSize size, UIColor *color);
111
+
112
+#endif

BIN
ios/SDK/GT3Captcha.framework/Info.plist View File


+ 6
- 0
ios/SDK/GT3Captcha.framework/Modules/module.modulemap View File

@@ -0,0 +1,6 @@
1
+framework module GT3Captcha {
2
+  umbrella header "GT3Captcha.h"
3
+
4
+  export *
5
+  module * { export * }
6
+}

+ 40
- 0
package.json View File

@@ -0,0 +1,40 @@
1
+{
2
+  "name": "@yyyyu/react-native-geetest-sensebot",
3
+  "author": "yyyyu <g592842897@gmail.com>",
4
+  "version": "1.0.0",
5
+  "keywords": [
6
+    "react-native",
7
+    "geetest"
8
+  ],
9
+  "description": "react native for geetest sensebot.",
10
+  "license": "MIT",
11
+  "homepage": "https://github.com/yyyyu/react-native-geetest-sensebot",
12
+  "repository": {
13
+    "type": "git",
14
+    "url": "git@github.com:yyyyu/react-native-geetest-sensebot.git"
15
+  },
16
+  "main": "index.js",
17
+  "scripts": {
18
+    "test": "echo \"Error: no test specified\" && exit 1",
19
+    "postinstall": "node scripts/postinstall"
20
+  },
21
+  "rnpm": {
22
+    "commands": {
23
+      "postlink": "node node_modules/@yyyyu/react-native-geetest-sensebot/scripts/postlink"
24
+    }
25
+  },
26
+  "dependencies": {
27
+    "invariant": "^2.2.4"
28
+  },
29
+  "peerDependencies": {
30
+    "react-native": ">0.50.0"
31
+  },
32
+  "devDependencies": {
33
+    "babel-eslint": "^8.2.3",
34
+    "standard": "^11.0.1"
35
+  },
36
+  "standard": {
37
+    "parser": "babel-eslint",
38
+    "globals": ["fetch", "Request"]
39
+  }
40
+}

+ 40
- 0
scripts/file.js View File

@@ -0,0 +1,40 @@
1
+const fs = require('fs')
2
+
3
+function fileInsert (path, insStr, insSign, avoidDupSign) {
4
+  fileReadAndWrite(path, function (data) {
5
+    if (avoidDupSign && data.indexOf(avoidDupSign) !== -1) return
6
+    const position = data.indexOf(insSign)
7
+    return data.slice(0, position) + insStr + data.slice(position)
8
+  })
9
+}
10
+
11
+function fileReplace (path, search, replace) {
12
+  fileReadAndWrite(path, function (data) {
13
+    return data.replace(new RegExp(search, 'g'), replace)
14
+  })
15
+}
16
+
17
+function fileReadAndWrite (path, callback) {
18
+  if (!fs.existsSync(path)) return
19
+  const encoding = 'utf8'
20
+  let data = null
21
+
22
+  try {
23
+    data = fs.readFileSync(path, encoding)
24
+  } catch (e) {
25
+    return console.error(e)
26
+  }
27
+
28
+  const result = callback(data)
29
+
30
+  try {
31
+    fs.writeFileSync(path, result, encoding)
32
+  } catch (e) {
33
+    console.error(e)
34
+  }
35
+}
36
+
37
+module.exports = {
38
+  fileInsert,
39
+  fileReplace
40
+}

+ 18
- 0
scripts/postinstall.js View File

@@ -0,0 +1,18 @@
1
+const path = require('path')
2
+const file = require('./file')
3
+
4
+const nodeModuleDir = path.dirname(__filename) + '/../../..'
5
+
6
+const modifies = {
7
+  get reactSpec () {
8
+    const avoidDupSign = '# Dependency'
9
+    return [
10
+      nodeModuleDir + '/react-native/React.podspec',
11
+      avoidDupSign + '\n  s.subspec "Dependency" do |ss|\n    ss.source_files         = "React/**/*.h"\n  end\n\n  ',
12
+      's.subspec "Core" do |ss|',
13
+      avoidDupSign
14
+    ]
15
+  }
16
+}
17
+
18
+file.fileInsert(...modifies.reactSpec)

+ 7
- 0
scripts/postlink.js View File

@@ -0,0 +1,7 @@
1
+const path = require('path')
2
+const file = require('./file')
3
+
4
+const projectDir = path.dirname(__filename) + '/../../../..'
5
+
6
+file.fileReplace(projectDir + '/android/settings.gradle', ':@yyyyu/react-native-geetest-sensebot', ':react-native-geetest-sensebot')
7
+file.fileReplace(projectDir + '/android/app/build.gradle', ':@yyyyu/react-native-geetest-sensebot', ':react-native-geetest-sensebot')

+ 261
- 0
src/api.js View File

@@ -0,0 +1,261 @@
1
+import { NativeEventEmitter, NativeModules, Platform } from 'react-native'
2
+import processColor from './lib/processColor'
3
+import validUrl from './lib/validUrl'
4
+import validCaptchaParams from './lib/validCaptchaParams'
5
+import invariant from 'invariant'
6
+import { EVENT_TYPE, ERROR_TYPE } from './constant'
7
+
8
+const { GeetestSensebot } = NativeModules
9
+
10
+const GSConfig = {
11
+  api1: null,
12
+  api2: null,
13
+  maskColor: processColor('transparent'),
14
+  isDebug: false
15
+}
16
+
17
+/**
18
+ * configApi 配置 api 地址
19
+ * @param  {String} api1
20
+ * @param  {String} api2
21
+ * @return {void}
22
+ */
23
+export const configApi = (api1, api2) => {
24
+  validUrl(api1)
25
+  GSConfig.api1 = api1
26
+  validUrl(api2)
27
+  GSConfig.api2 = api2
28
+}
29
+
30
+/**
31
+ * setMaskColor 配置行为验证背景遮罩颜色 iosOnly
32
+ * @param  {String} color
33
+ * @return {void}
34
+ */
35
+export const setMaskColor = color => {
36
+  if (Platform.OS !== 'ios') return // 避免不必要的计算
37
+  GSConfig.maskColor = processColor(color)
38
+}
39
+
40
+/**
41
+ * enableDebug 开启调试 iosOnly
42
+ * @param  {Boolean} isDebug
43
+ * @return {void}
44
+ */
45
+export const enableDebug = isDebug => {
46
+  GSConfig.isDebug = !!isDebug
47
+}
48
+
49
+/**
50
+ * captcha 行为验证
51
+ */
52
+export const captcha = async argument => {
53
+  invariant(
54
+    GSConfig.api1 !== null && GSConfig.api2 !== null,
55
+    'api address must be set, please run configApi function first.'
56
+  )
57
+
58
+  const {
59
+    api1ReqReplacer, api1RespHandler,
60
+    api2ReqReplacer, api2RespHandler
61
+  } = argument || {}
62
+
63
+  let payload = null
64
+  let errCode = null
65
+  let errMsg = null
66
+
67
+  // initial captcha manager
68
+  await GeetestSensebot.initCaptchaMgr(GSConfig.maskColor, GSConfig.isDebug)
69
+
70
+  // api1 request
71
+  let api1Resp = null
72
+  let captchaParams = null
73
+  try {
74
+    let api1Req = new Request(GSConfig.api1, {
75
+      method: 'GET',
76
+      headers: { 'Content-Type': 'application/json' },
77
+      credentials: 'include'
78
+    })
79
+    if (typeof api1ReqReplacer === 'function') {
80
+      api1Req = await api1ReqReplacer(api2Req)
81
+      if (!(api1Req instanceof Request)) {
82
+        throw Error('api1ReqReplacer return value not a valid request object')
83
+      }
84
+    }
85
+    api1Resp = await fetch(api1Req)
86
+  } catch (e) {
87
+    throw new GSError({
88
+      errCode: ERROR_TYPE.API1,
89
+      errMsg: `api1 request failed, ${e.message}`
90
+    })
91
+  }
92
+  // api1 resp handle
93
+  if (typeof api1RespHandler === 'function') {
94
+    captchaParams = await api1RespHandler(api1Resp)
95
+  } else {
96
+    captchaParams = await defaultApi1RespHandler(api1Resp)
97
+  }
98
+
99
+  // captcha
100
+  try {
101
+    validCaptchaParams(captchaParams)
102
+  } catch (e) {
103
+    throw new GSError({
104
+      errCode: ERROR_TYPE.CAPTCHA,
105
+      errMsg: `captcha params error, ${e.message}`
106
+    })
107
+  }
108
+  const capcataStartRes = await GeetestSensebot.captcha(
109
+    captchaParams.success,
110
+    captchaParams.gt,
111
+    captchaParams.challenge,
112
+    GSConfig.api2
113
+  )
114
+  if (!capcataStartRes) {
115
+    throw new GSError({
116
+      errCode: ERROR_TYPE.CAPTCHA,
117
+      errMsg: 'captcha start failed, can\'t find captcha manager.'
118
+    })
119
+  }
120
+  const captchaRes = await rnGSEventListener(EVENT_TYPE.CAPTCHA)
121
+  payload = captchaRes.payload
122
+  errCode = captchaRes.errCode
123
+  errMsg = captchaRes.errMsg
124
+  if (errCode) {
125
+    throw new GSError({ errCode: ERROR_TYPE.CAPTCHA, errMsg })
126
+  }
127
+  // from sdk @param code 验证交互结果, 0失败/1成功
128
+  if (payload.code !== '1') {
129
+    throw new GSError({
130
+      errCode: ERROR_TYPE.CAPTCHA,
131
+      errMsg: `captcha valid failed, ${payload.message}`
132
+    })
133
+  }
134
+
135
+  // api2 request
136
+  // @param { geetest_challenge: "", geetest_seccode: "", geetest_validate: "" }
137
+  const api2ReqParams = payload.result
138
+  let api2Resp = null
139
+  try {
140
+    let api2Req = new Request(GSConfig.api2, {
141
+      method: 'POST',
142
+      headers: { 'Content-Type': 'application/json' },
143
+      credentials: 'include',
144
+      body: JSON.stringify(api2ReqParams)
145
+    })
146
+    if (typeof api2ReqReplacer === 'function') {
147
+      api2Req = await api2ReqReplacer(api2Req)
148
+      if (!(api2Req instanceof Request)) {
149
+        throw Error('second params not a valid request object')
150
+      }
151
+    }
152
+    api2Resp = await fetch(api2Req)
153
+  } catch (e) {
154
+    throw new GSError({
155
+      errCode: EVENT_TYPE.API2,
156
+      errMsg: `api2 request failed, ${e.message}`
157
+    })
158
+  }
159
+  // clean
160
+  stopCaptcha()
161
+
162
+  // api2 resp handle
163
+  if (typeof api2RespHandler === 'function') {
164
+    return api2RespHandler(api2Resp)
165
+  } else {
166
+    return defaultApi2RespHandler(api2Resp)
167
+  }
168
+}
169
+
170
+/**
171
+ * stopCaptcha 停止行为验证
172
+ */
173
+const stopCaptcha = () => {
174
+  return GeetestSensebot.stopCaptcha()
175
+}
176
+
177
+
178
+/* event handle */
179
+const GSEmitter = new NativeEventEmitter(GeetestSensebot)
180
+
181
+const rnGSEventListener = listenEventType => {
182
+  return new Promise(resolve => {
183
+    let subscription = null
184
+    const handleGSEvent = ({ type, ...otherParams }) => {
185
+      if (type === EVENT_TYPE.ERROR) {
186
+        resolve(otherParams)
187
+        return
188
+      }
189
+      if (listenEventType !== type) return
190
+      clearSubscribe()
191
+      resolve(otherParams)
192
+    }
193
+    const clearSubscribe = () => {
194
+      subscription.remove()
195
+    }
196
+
197
+    subscription = GSEmitter.addListener('RNGeetestSensebotEvent', handleGSEvent)
198
+  })
199
+}
200
+/* event handle end */
201
+
202
+
203
+/* default captcha funcs */
204
+const defaultApi1RespHandler = async api1Resp => {
205
+  /**
206
+   * api1 官方示例接口
207
+   * http://www.geetest.com/demo/gt/register-test
208
+   */
209
+  if (api1Resp.status !== 200) {
210
+    throw new GSError({
211
+      errCode: ERROR_TYPE.API1,
212
+      errMsg: `api1 request error, ${await api1Resp.text()}`
213
+    })
214
+  }
215
+  try {
216
+    return api1Resp.json()
217
+  } catch (e) {
218
+    throw new GSError({
219
+      errCode: ERROR_TYPE.API1,
220
+      errMsg: `api1 request error, response result need json format, but get ${await api1Resp.text()}`
221
+    })
222
+  }
223
+}
224
+
225
+const defaultApi2RespHandler = async api2Resp => {
226
+  /**
227
+   * api2 官方示例接口
228
+   * http://www.geetest.com/demo/gt/validate-test
229
+   */
230
+  if (api2Resp.status !== 200) {
231
+    throw new GSError({
232
+      errCode: ERROR_TYPE.API2,
233
+      errMsg: `api2 request error, ${await api2Resp.text()}`
234
+    })
235
+  }
236
+  try {
237
+    return api2Resp.json()
238
+  } catch (e) {
239
+    return api2Resp.text()
240
+  }
241
+}
242
+/* default captcha funcs end */
243
+
244
+
245
+export class GSError extends Error {
246
+  constructor ({ errCode, errMsg }) {
247
+    super(errMsg)
248
+
249
+    this.name = 'GSError'
250
+    this.errCode = errCode
251
+    this.errMsg = errMsg
252
+
253
+    if (typeof Object.setPrototypeOf === 'function') {
254
+      Object.setPrototypeOf(this, GSError.prototype)
255
+    } else {
256
+      this.__proto__ = GSError.prototype
257
+    }
258
+
259
+    stopCaptcha()
260
+  }
261
+}

+ 10
- 0
src/constant.js View File

@@ -0,0 +1,10 @@
1
+export const EVENT_TYPE = {
2
+  CAPTCHA: 1,
3
+  ERROR: -1
4
+}
5
+
6
+export const ERROR_TYPE = {
7
+  API1: 1,
8
+  API2: 2,
9
+  CAPTCHA: 3
10
+}

+ 3
- 0
src/lib/processColor.js View File

@@ -0,0 +1,3 @@
1
+import processColor from 'react-native/Libraries/StyleSheet/processColor'
2
+
3
+export default processColor

+ 11
- 0
src/lib/validCaptchaParams.js View File

@@ -0,0 +1,11 @@
1
+import invariant from 'invariant'
2
+
3
+export default params => {
4
+  invariant(
5
+    typeof params === 'object' &&
6
+    typeof params.success === 'number' &&
7
+    typeof params.gt === 'string' &&
8
+    typeof params.challenge === 'string',
9
+    'captcha params must be an object have success(Number) gt(String) challenge(String), but get %s', params
10
+  )
11
+}

+ 12
- 0
src/lib/validUrl.js View File

@@ -0,0 +1,12 @@
1
+import invariant from 'invariant'
2
+
3
+export default arg => {
4
+  invariant(
5
+    typeof arg === 'string',
6
+    'expect url is string type, but get %s', typeof arg
7
+  )
8
+  invariant(
9
+    /^https?:\/\//.test(arg),
10
+    'expect url is valid http address, but get %s', arg
11
+  )
12
+}

+ 1406
- 0
yarn.lock
File diff suppressed because it is too large
View File