@valifysolutions/react-native-vidvdigitalcontract 1.1.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/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # VIDV Digital Contract — React Native Plugin
2
+
3
+ A React Native bridge for the **VIDV Digital Contract** native SDK. Identical functionality,
4
+ API design, and flow to the Flutter plugin (`VIDVDigitalContractFlutter`).
5
+
6
+ ---
7
+
8
+ ## Requirements
9
+
10
+ | Platform | Minimum version |
11
+ |----------|----------------|
12
+ | iOS | 13.0 |
13
+ | Android | API 24 (7.0) |
14
+ | React Native | ≥ 0.70 |
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ ```sh
21
+ # npm
22
+ npm install vidv-digital-contract-react-native
23
+
24
+ # yarn
25
+ yarn add vidv-digital-contract-react-native
26
+ ```
27
+
28
+ ### Android — repository credentials
29
+
30
+ The native SDK is hosted on Valify's private Artifactory. Add the repository to your
31
+ **project-level** `build.gradle` (or `settings.gradle` for newer React Native templates):
32
+
33
+ ```groovy
34
+ repositories {
35
+ maven {
36
+ credentials {
37
+ username "sdk"
38
+ password "sdk123456"
39
+ }
40
+ url "https://www.valifystage.com/artifactory/libs-release/"
41
+ }
42
+ maven { url "https://jitpack.io" }
43
+ maven { url "https://developer.huawei.com/repo/" }
44
+ }
45
+ ```
46
+
47
+ ### Android — manifest entries
48
+
49
+ Add the following to your app's `AndroidManifest.xml`. Both the main activity and the
50
+ SDK host activity **must share the same `taskAffinity`** so the flow returns correctly:
51
+
52
+ ```xml
53
+ <activity
54
+ android:name=".MainActivity"
55
+ android:taskAffinity="com.yourapp.example"
56
+ android:launchMode="singleTop"
57
+ ... />
58
+
59
+ <!-- Required SDK host activity -->
60
+ <activity
61
+ android:name="me.vidv.vidvdigitalcontractsdk.VIDVDigitalContractHostActivity"
62
+ android:exported="true"
63
+ android:taskAffinity="com.yourapp.example"
64
+ android:documentLaunchMode="never"
65
+ android:excludeFromRecents="true"
66
+ tools:replace="android:taskAffinity,android:documentLaunchMode,android:excludeFromRecents" />
67
+ ```
68
+
69
+ Also add packaging exclusions in `app/build.gradle`:
70
+
71
+ ```groovy
72
+ packagingOptions {
73
+ pickFirst 'META-INF/versions/9/OSGI-INF/MANIFEST.MF'
74
+ }
75
+ ```
76
+
77
+ ### iOS — CocoaPods
78
+
79
+ ```sh
80
+ cd ios && pod install
81
+ ```
82
+
83
+ The plugin's podspec depends on `VIDVDigitalContract ~> 1.1.0`. Make sure your team has
84
+ access to the CocoaPods source for this pod, or point your Podfile to the private spec repo.
85
+
86
+ ---
87
+
88
+ ## Usage
89
+
90
+ ```ts
91
+ import { startDigitalContract } from 'vidv-digital-contract-react-native';
92
+ import type { DigitalContractResponse } from 'vidv-digital-contract-react-native';
93
+
94
+ async function launch() {
95
+ const response: DigitalContractResponse = await startDigitalContract({
96
+ vidvBaseUrl: 'https://www.valifystage.com/',
97
+ dcBaseUrl: 'https://valify.staging.knfrm.com/',
98
+ bundleKey: '<your-bundle-key>',
99
+ vidvAccessToken: '<vidv-oauth-token>',
100
+ dcAccessToken: '<dc-auth-token>',
101
+ tenantId: '<tenant-id>',
102
+ userReferenceId: 'user-123',
103
+ language: 'en', // 'en' or 'ar'
104
+ templateVersionId: 2,
105
+ contractExpiryMinutes: 30,
106
+ contractData: JSON.stringify([
107
+ { field_id: '1', value: 'John' },
108
+ { field_id: '2', value: 'Doe' },
109
+ ]),
110
+ allowHandSignature: false, // optional
111
+ });
112
+
113
+ switch (response.state) {
114
+ case 'SUCCESS':
115
+ console.log('Contract signed!', response.result);
116
+ break;
117
+ case 'FAILURE':
118
+ console.warn('Service failure', response.result);
119
+ break;
120
+ case 'ERROR':
121
+ console.error('Builder error', response.result);
122
+ break;
123
+ case 'EXIT':
124
+ console.log('User exited', response.result);
125
+ break;
126
+ }
127
+ }
128
+ ```
129
+
130
+ ---
131
+
132
+ ## API
133
+
134
+ ### `startDigitalContract(config)`
135
+
136
+ Returns `Promise<DigitalContractResponse>`.
137
+
138
+ #### `DigitalContractConfig`
139
+
140
+ | Field | Type | Required | Description |
141
+ |-------|------|----------|-------------|
142
+ | `vidvBaseUrl` | `string` | ✓ | VIDV backend base URL |
143
+ | `dcBaseUrl` | `string` | ✓ | Digital Contract backend base URL |
144
+ | `bundleKey` | `string` | ✓ | Your integration bundle key |
145
+ | `vidvAccessToken` | `string` | ✓ | OAuth token from VIDV (`/api/o/token/`) |
146
+ | `dcAccessToken` | `string` | ✓ | Auth token from DC (`/api/v1/auth-token`) |
147
+ | `tenantId` | `string` | ✓ | Tenant ID returned with DC token |
148
+ | `userReferenceId` | `string` | ✓ | Your internal user identifier |
149
+ | `language` | `'en' \| 'ar'` | ✓ | UI language |
150
+ | `templateVersionId` | `number` | ✓ | Contract template version (> 0) |
151
+ | `contractExpiryMinutes` | `number` | ✓ | Session expiry in minutes (> 0) |
152
+ | `contractData` | `string` | ✓ | JSON array of `{field_id, value}` objects |
153
+ | `allowHandSignature` | `boolean` | — | Enable hand-signature capture |
154
+
155
+ #### `DigitalContractResponse`
156
+
157
+ ```ts
158
+ {
159
+ state: 'SUCCESS' | 'ERROR' | 'FAILURE' | 'EXIT';
160
+ result: {
161
+ // SUCCESS
162
+ data?: { contractID?: string; iframeURL?: string; contractStatus?: string };
163
+
164
+ // ERROR (builder validation)
165
+ code?: string | number;
166
+ message?: string;
167
+
168
+ // FAILURE (service error)
169
+ code?: string | number;
170
+ message?: string;
171
+ data?: DigitalContractData;
172
+ sdkError?: { errorCode?: string | number; message?: string };
173
+
174
+ // EXIT (user cancelled)
175
+ step?: string;
176
+ data?: DigitalContractData;
177
+ };
178
+ }
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Token fetching
184
+
185
+ Tokens are **not** fetched by the plugin — your app must obtain them before calling
186
+ `startDigitalContract`.
187
+
188
+ ### VIDV token (OAuth password grant)
189
+
190
+ ```ts
191
+ const res = await fetch(`${vidvBaseUrl}/api/o/token/`, {
192
+ method: 'POST',
193
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
194
+ body: `username=...&password=...&client_id=...&client_secret=...&grant_type=password`,
195
+ });
196
+ const { access_token } = await res.json();
197
+ ```
198
+
199
+ ### DC token (HMAC-SHA256 authenticated)
200
+
201
+ ```ts
202
+ import CryptoJS from 'crypto-js';
203
+
204
+ const body = JSON.stringify({ username, password, client_id, client_secret });
205
+ const hmac = CryptoJS.HmacSHA256(body, secretKey).toString(CryptoJS.enc.Hex);
206
+
207
+ const res = await fetch(`${dcBaseUrl}/api/v1/auth-token`, {
208
+ method: 'POST',
209
+ headers: { 'Content-Type': 'application/json', hmac },
210
+ body,
211
+ });
212
+ const { data: { token, tenant_id } } = await res.json();
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Example app
218
+
219
+ A complete demo app is located in `example/`. It mirrors the Flutter example app exactly.
220
+
221
+ ```sh
222
+ cd example
223
+ yarn install
224
+
225
+ # iOS
226
+ cd ios && pod install && cd ..
227
+ yarn ios
228
+
229
+ # Android
230
+ yarn android
231
+ ```
232
+
233
+ ---
234
+
235
+ ## Native SDK versions
236
+
237
+ | Platform | SDK | Version |
238
+ |----------|-----|---------|
239
+ | Android | `com.vidv:vidvdigitalcontractsdk` | `1.2.1` |
240
+ | iOS | `VIDVDigitalContract` (CocoaPod) | `~> 1.1.0` |
@@ -0,0 +1,59 @@
1
+ buildscript {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ }
6
+ dependencies {
7
+ classpath "com.android.tools.build:gradle:8.1.4"
8
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.24"
9
+ }
10
+ }
11
+
12
+ apply plugin: 'com.android.library'
13
+ apply plugin: 'kotlin-android'
14
+
15
+ android {
16
+ compileSdkVersion 35
17
+ namespace "com.vidv.digitalcontract"
18
+
19
+ compileOptions {
20
+ sourceCompatibility JavaVersion.VERSION_17
21
+ targetCompatibility JavaVersion.VERSION_17
22
+ }
23
+
24
+ kotlinOptions {
25
+ jvmTarget = "17"
26
+ freeCompilerArgs += ["-Xskip-metadata-version-check"]
27
+ }
28
+
29
+ sourceSets {
30
+ main {
31
+ java.srcDirs += 'src/main/kotlin'
32
+ }
33
+ }
34
+
35
+ defaultConfig {
36
+ minSdkVersion 24
37
+ targetSdkVersion 34
38
+ }
39
+ }
40
+
41
+ repositories {
42
+ google()
43
+ mavenCentral()
44
+ maven {
45
+ credentials {
46
+ username "sdk"
47
+ password "sdk123456"
48
+ }
49
+ url "https://www.valifystage.com/artifactory/libs-release/"
50
+ }
51
+ maven { url "https://jitpack.io" }
52
+ maven { url "https://developer.huawei.com/repo/" }
53
+ }
54
+
55
+ dependencies {
56
+ implementation "com.facebook.react:react-native:+"
57
+ implementation "com.vidv:vidvdigitalcontractsdk:1.2.3"
58
+ implementation "com.google.code.gson:gson:2.11.0"
59
+ }
@@ -0,0 +1,3 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ package="com.vidv.digitalcontract">
3
+ </manifest>
@@ -0,0 +1,111 @@
1
+ package com.vidv.digitalcontract
2
+
3
+ import android.app.Activity
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
7
+ import com.facebook.react.bridge.ReactMethod
8
+ import com.facebook.react.bridge.ReadableMap
9
+ import com.google.gson.Gson
10
+ import me.vidv.vidvdigitalcontractsdk.sdk.VIDVDigitalContractConfig
11
+ import me.vidv.vidvdigitalcontractsdk.sdk.VIDVDigitalContractData
12
+ import me.vidv.vidvdigitalcontractsdk.sdk.VIDVDigitalContractListener
13
+ import me.vidv.vidvdigitalcontractsdk.sdk.VIDVDigitalContractResponse
14
+
15
+ class VIDVDigitalContractModule(reactContext: ReactApplicationContext) :
16
+ ReactContextBaseJavaModule(reactContext) {
17
+
18
+ private val gson = Gson()
19
+ private var pendingPromise: Promise? = null
20
+
21
+ override fun getName(): String = "VIDVDigitalContract"
22
+
23
+ @ReactMethod
24
+ fun startDigitalContract(args: ReadableMap, promise: Promise) {
25
+ if (pendingPromise != null) {
26
+ promise.reject("ALREADY_RUNNING", "Another Digital Contract flow is in progress.")
27
+ return
28
+ }
29
+ val activity: Activity? = currentActivity
30
+ if (activity == null) {
31
+ promise.reject("NO_ACTIVITY", "Plugin is not attached to an Activity.")
32
+ return
33
+ }
34
+ val argsMap: Map<String, Any?> = args.toHashMap()
35
+ pendingPromise = promise
36
+ try {
37
+ startSDK(activity, argsMap)
38
+ } catch (t: Throwable) {
39
+ pendingPromise?.reject("START_ERROR", t.message)
40
+ pendingPromise = null
41
+ }
42
+ }
43
+
44
+ private fun numberFromArg(value: Any?): Int? = when (value) {
45
+ is Int -> value
46
+ is Long -> value.toInt()
47
+ is Double -> value.toInt()
48
+ is String -> value.toIntOrNull()
49
+ else -> null
50
+ }
51
+
52
+ private fun startSDK(activity: Activity, args: Map<String, Any?>) {
53
+ val builder = VIDVDigitalContractConfig.Builder()
54
+ .setVIDVBaseUrl(args["vidv_base_url"] as String)
55
+ .setDCBaseUrl(args["dc_base_url"] as String)
56
+ .setVIDVAccessToken(args["vidv_access_token"] as String)
57
+ .setDCAccessToken(args["dc_access_token"] as String)
58
+ .setBundleKey(args["bundle_key"] as String)
59
+ .setTenantId(args["tenant_id"] as String)
60
+ .setUserReferenceId(args["user_reference_id"] as String)
61
+ .setLanguage(args["language"] as String)
62
+ .setTemplateVersionId(numberFromArg(args["template_version_id"]) ?: 0)
63
+ .setExpiryMinutes(numberFromArg(args["contract_expiry_minutes"]) ?: 0)
64
+ .setContractData(args["contract_data"] as String)
65
+
66
+ if ((args["allow_hand_signature"] as? Boolean) == true) {
67
+ builder.setAllowHandSignature(true)
68
+ }
69
+
70
+ builder.start(activity, object : VIDVDigitalContractListener {
71
+ override fun onDigitalContractResult(result: VIDVDigitalContractResponse) {
72
+ when (result) {
73
+ is VIDVDigitalContractResponse.Success -> sendJson(
74
+ "SUCCESS",
75
+ mapOf("data" to serializeData(result.data))
76
+ )
77
+ is VIDVDigitalContractResponse.BuilderError -> sendJson(
78
+ "ERROR",
79
+ mapOf("code" to result.code, "message" to result.message)
80
+ )
81
+ is VIDVDigitalContractResponse.ServiceFailure -> sendJson(
82
+ "FAILURE",
83
+ mapOf(
84
+ "code" to result.code,
85
+ "message" to result.message,
86
+ )
87
+ )
88
+ is VIDVDigitalContractResponse.Exit -> sendJson(
89
+ "EXIT",
90
+ mapOf("data" to serializeData(result.data))
91
+ )
92
+ }
93
+ }
94
+ })
95
+ }
96
+
97
+ private fun serializeData(data: VIDVDigitalContractData?): Map<String, Any?>? {
98
+ if (data == null) return null
99
+ return mapOf(
100
+ "contractID" to data.contractId,
101
+ "iframeURL" to data.iframeURL,
102
+ "contractStatus" to data.contractStatus
103
+ )
104
+ }
105
+
106
+ private fun sendJson(state: String, result: Any) {
107
+ val payload = mapOf("state" to state, "result" to result)
108
+ pendingPromise?.resolve(gson.toJson(payload))
109
+ pendingPromise = null
110
+ }
111
+ }
@@ -0,0 +1,14 @@
1
+ package com.vidv.digitalcontract
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class VIDVDigitalContractPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
10
+ listOf(VIDVDigitalContractModule(reactContext))
11
+
12
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> =
13
+ emptyList()
14
+ }
@@ -0,0 +1,16 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface RCT_EXTERN_REMAP_MODULE(VIDVDigitalContract, VIDVDigitalContractModule, NSObject)
4
+
5
+ RCT_EXTERN_METHOD(
6
+ startDigitalContract:(NSDictionary *)args
7
+ resolve:(RCTPromiseResolveBlock)resolve
8
+ rejecter:(RCTPromiseRejectBlock)reject
9
+ )
10
+
11
+ + (BOOL)requiresMainQueueSetup
12
+ {
13
+ return YES;
14
+ }
15
+
16
+ @end
@@ -0,0 +1,156 @@
1
+ import Foundation
2
+ import UIKit
3
+ import VIDVDigitalContract
4
+
5
+ // Swift-native type aliases that match the ObjC RCTPromise block signatures.
6
+ // Using these instead of RCTPromiseResolveBlock/RCTPromiseRejectBlock avoids
7
+ // a compile failure when React-Core is not imported as a Swift module
8
+ // (i.e. when use_frameworks! is NOT used, which is the common RN setup).
9
+ typealias Resolve = (Any?) -> Void
10
+ typealias Reject = (String, String, Error?) -> Void
11
+
12
+ @objc(VIDVDigitalContractModule)
13
+ class VIDVDigitalContractModule: NSObject {
14
+
15
+ private static var pendingResolve: Resolve?
16
+ private static var pendingReject: Reject?
17
+ private static var delegateHolder = DigitalContractDelegateHolder()
18
+
19
+ // The selector name must exactly match the RCT_EXTERN_METHOD declaration in .m
20
+ @objc(startDigitalContract:resolve:rejecter:)
21
+ func startDigitalContract(
22
+ _ args: NSDictionary,
23
+ resolve: @escaping Resolve,
24
+ rejecter reject: @escaping Reject
25
+ ) {
26
+ guard Self.pendingResolve == nil else {
27
+ reject("ALREADY_RUNNING", "Another Digital Contract flow is in progress.", nil)
28
+ return
29
+ }
30
+
31
+ guard let argsDict = args as? [String: Any] else {
32
+ reject("INVALID_ARGS", "Arguments must be a map.", nil)
33
+ return
34
+ }
35
+
36
+ // Store callbacks before dispatching to main thread
37
+ Self.pendingResolve = resolve
38
+ Self.pendingReject = reject
39
+
40
+ Self.delegateHolder.onResult = { json in
41
+ Self.pendingResolve?(json)
42
+ Self.pendingResolve = nil
43
+ Self.pendingReject = nil
44
+ }
45
+ Self.delegateHolder.onError = { code, message in
46
+ Self.pendingReject?(code, message, nil)
47
+ Self.pendingResolve = nil
48
+ Self.pendingReject = nil
49
+ }
50
+
51
+ // All UIKit access (topViewController + builder.start) must be on the main thread
52
+ DispatchQueue.main.async {
53
+ guard let rootVC = Self.topViewController() else {
54
+ Self.pendingReject?("NO_VIEW_CONTROLLER", "No view controller to present from.", nil)
55
+ Self.pendingResolve = nil
56
+ Self.pendingReject = nil
57
+ return
58
+ }
59
+ Self.startSDK(from: rootVC, args: argsDict, delegate: Self.delegateHolder)
60
+ }
61
+ }
62
+
63
+ private static func intFrom(_ value: Any?) -> Int? {
64
+ switch value {
65
+ case let n as Int: return n
66
+ case let n as Int64: return Int(n)
67
+ case let n as Double: return Int(n)
68
+ case let s as String: return Int(s)
69
+ default: return nil
70
+ }
71
+ }
72
+
73
+ private static func startSDK(
74
+ from viewController: UIViewController,
75
+ args: [String: Any],
76
+ delegate: VIDVDigitalContractDelegate
77
+ ) {
78
+ var builder = VIDVDigitalContractBuilder()
79
+ .setVIDVBaseUrl(args["vidv_base_url"] as! String)
80
+ .setDCBaseUrl(args["dc_base_url"] as! String)
81
+ .setVIDVAccessToken(args["vidv_access_token"] as! String)
82
+ .setDcAccessToken(args["dc_access_token"] as! String)
83
+ .setBundleKey(args["bundle_key"] as! String)
84
+ .setTenantId(args["tenant_id"] as! String)
85
+ .setUserReferenceId(args["user_reference_id"] as! String)
86
+ .setLanguage(args["language"] as! String)
87
+ .setTempleteVersionId(intFrom(args["template_version_id"]) ?? 0)
88
+ .setContractExpiryMinutes(intFrom(args["contract_expiry_minutes"]) ?? 0)
89
+ .contractData(args["contract_data"] as! String)
90
+
91
+ if args["allow_hand_signature"] as? Bool == true {
92
+ builder = builder.allowHandSignature(true)
93
+ }
94
+
95
+ builder.start(viewController, delegate: delegate)
96
+ }
97
+
98
+ private static func topViewController() -> UIViewController? {
99
+ let scenes = UIApplication.shared.connectedScenes
100
+ .compactMap { $0 as? UIWindowScene }
101
+ let window = scenes.flatMap { $0.windows }.first { $0.isKeyWindow }
102
+ var top = window?.rootViewController
103
+ while let presented = top?.presentedViewController {
104
+ top = presented
105
+ }
106
+ return top
107
+ }
108
+ }
109
+
110
+ // MARK: - Delegate holder
111
+
112
+ private final class DigitalContractDelegateHolder: NSObject, VIDVDigitalContractDelegate {
113
+ var onResult: ((String) -> Void)?
114
+ var onError: ((String, String) -> Void)?
115
+
116
+ func onContractResult(_ result: ContractResult) {
117
+ switch result {
118
+ case .success(let data):
119
+ onResult?(jsonEnvelope(state: "SUCCESS", body: ["data": serializeData(data) as Any]))
120
+
121
+ case .builderError(let code, let message):
122
+ onResult?(jsonEnvelope(state: "ERROR", body: ["code": code, "message": message]))
123
+
124
+ case .serviceFailure(let message, let sdkError):
125
+ var body: [String: Any] = ["message": message]
126
+ if let sdkError = sdkError {
127
+ body["sdkError"] = [
128
+ "errorCode": sdkError.errorCode as Any,
129
+ "message": sdkError.message as Any,
130
+ ]
131
+ }
132
+ onResult?(jsonEnvelope(state: "FAILURE", body: body))
133
+
134
+ case .exit(let data):
135
+ onResult?(jsonEnvelope(state: "EXIT", body: ["data": serializeData(data) as Any]))
136
+ }
137
+ }
138
+
139
+ private func serializeData(_ data: VIDVDigitalContractResult?) -> [String: Any]? {
140
+ guard let data = data else { return nil }
141
+ var map: [String: Any] = [:]
142
+ if let contractId = data.contractId { map["contractID"] = contractId }
143
+ if let iframeURL = data.iframeURL { map["iframeURL"] = iframeURL }
144
+ if let contractStatus = data.contractStatus { map["contractStatus"] = contractStatus }
145
+ return map.isEmpty ? nil : map
146
+ }
147
+
148
+ private func jsonEnvelope(state: String, body: [String: Any]) -> String {
149
+ let payload: [String: Any] = ["state": state, "result": body]
150
+ guard let data = try? JSONSerialization.data(withJSONObject: payload),
151
+ let json = String(data: data, encoding: .utf8) else {
152
+ return "{\"state\":\"\(state)\",\"result\":{}}"
153
+ }
154
+ return json
155
+ }
156
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@valifysolutions/react-native-vidvdigitalcontract",
3
+ "version": "1.1.0",
4
+ "description": "React Native plugin for the VIDV Digital Contract SDK",
5
+ "main": "src/index",
6
+ "types": "src/index",
7
+ "files": [
8
+ "src",
9
+ "android",
10
+ "ios",
11
+ "vidvdigitalcontract-react-native.podspec",
12
+ "react-native.config.js",
13
+ "!android/build",
14
+ "!android/.gradle",
15
+ "!ios/build",
16
+ "!**/__tests__"
17
+ ],
18
+ "scripts": {
19
+ "example": "yarn --cwd example",
20
+ "typescript": "tsc --noEmit"
21
+ },
22
+ "keywords": [
23
+ "react-native",
24
+ "ios",
25
+ "android",
26
+ "vidv",
27
+ "digital-contract"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/valify/VIDVDigitalContract-ReactNative.git"
32
+ },
33
+ "author": "Valify",
34
+ "license": "MIT",
35
+ "dependencies": {
36
+ "@babel/runtime": "^7.20.0"
37
+ },
38
+ "peerDependencies": {
39
+ "react": ">=18.0.0",
40
+ "react-native": ">=0.70.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/react": "^18.2.0",
44
+ "react": "18.2.0",
45
+ "react-native": "0.73.6",
46
+ "typescript": "^5.0.0"
47
+ }
48
+ }
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ android: {
5
+ packageImportPath: 'import com.vidv.digitalcontract.VIDVDigitalContractPackage;',
6
+ packageInstance: 'new VIDVDigitalContractPackage()',
7
+ },
8
+ ios: {},
9
+ },
10
+ },
11
+ };
package/src/index.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+ import type { DigitalContractResponse } from './types';
3
+
4
+ export * from './types';
5
+
6
+ const LINKING_ERROR =
7
+ `The package 'vidv-digital-contract-react-native' doesn't seem to be linked. Make sure: \n\n` +
8
+ Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
9
+ '- You rebuilt the app after installing the package\n' +
10
+ '- You are not using Expo managed workflow\n';
11
+
12
+ const NativeVIDVDigitalContract = NativeModules.VIDVDigitalContract
13
+ ? NativeModules.VIDVDigitalContract
14
+ : new Proxy(
15
+ {},
16
+ {
17
+ get() {
18
+ throw new Error(LINKING_ERROR);
19
+ },
20
+ }
21
+ );
22
+
23
+ /**
24
+ * Thin React Native wrapper around the native VIDV Digital Contract SDK
25
+ * (iOS `VIDVDigitalContract`, Android `vidvdigitalcontractsdk`).
26
+ *
27
+ * The native SDKs present their own UI. This module just launches that flow
28
+ * with the given args map and returns the SDK's terminal result.
29
+ *
30
+ * Mirrors the Flutter plugin's `VIDVDigitalContract` class exactly.
31
+ */
32
+ export class VIDVDigitalContract {
33
+ /**
34
+ * Starts the native Digital Contract flow described by {@link args} and
35
+ * resolves with the result as a JSON string,
36
+ * e.g. `{"state":"SUCCESS","result":{...}}`.
37
+ *
38
+ * `state` is one of `SUCCESS`, `FAILURE`, `EXIT`, or `ERROR`.
39
+ *
40
+ * Throws (rejects) only for pre-flight failures:
41
+ * - `ALREADY_RUNNING` — another start() call is already in progress
42
+ * - `NO_ACTIVITY` (Android) — not attached to an Activity
43
+ * - `NO_VIEW_CONTROLLER` (iOS) — root view controller not found
44
+ * - `START_ERROR` (Android) — exception while building the native flow
45
+ */
46
+ async start(args: Record<string, unknown>): Promise<string | null> {
47
+ const json: string = await NativeVIDVDigitalContract.startDigitalContract(args);
48
+ return json ?? null;
49
+ }
50
+
51
+ /**
52
+ * Convenience wrapper that parses the JSON string returned by {@link start}
53
+ * into a typed {@link DigitalContractResponse} object.
54
+ */
55
+ async startParsed(args: Record<string, unknown>): Promise<DigitalContractResponse | null> {
56
+ const json = await this.start(args);
57
+ if (json == null) return null;
58
+ return JSON.parse(json) as DigitalContractResponse;
59
+ }
60
+ }
package/src/types.ts ADDED
@@ -0,0 +1,40 @@
1
+ export interface DigitalContractData {
2
+ contractID?: string;
3
+ iframeURL?: string;
4
+ contractStatus?: string;
5
+ }
6
+
7
+ export type DigitalContractState = 'SUCCESS' | 'ERROR' | 'FAILURE' | 'EXIT';
8
+
9
+ export interface DigitalContractSuccessResult {
10
+ data?: DigitalContractData;
11
+ }
12
+
13
+ export interface DigitalContractErrorResult {
14
+ code?: string | number;
15
+ message?: string;
16
+ }
17
+
18
+ export interface DigitalContractFailureResult {
19
+ code?: string | number;
20
+ message?: string;
21
+ data?: DigitalContractData;
22
+ sdkError?: {
23
+ errorCode?: string | number;
24
+ message?: string;
25
+ };
26
+ }
27
+
28
+ export interface DigitalContractExitResult {
29
+ step?: string;
30
+ data?: DigitalContractData;
31
+ }
32
+
33
+ export interface DigitalContractResponse {
34
+ state: DigitalContractState;
35
+ result:
36
+ | DigitalContractSuccessResult
37
+ | DigitalContractErrorResult
38
+ | DigitalContractFailureResult
39
+ | DigitalContractExitResult;
40
+ }
@@ -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 = "vidvdigitalcontract-react-native"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["repository"]["url"]
10
+ s.license = package["license"]
11
+ s.authors = { "Valify" => "info@valify.me" }
12
+ s.platforms = { :ios => "13.0" }
13
+ s.source = { :git => package["repository"]["url"], :tag => "#{s.version}" }
14
+
15
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
16
+
17
+ s.dependency "React-Core"
18
+ s.dependency "VIDVDigitalContract", "~> 1.1.4"
19
+
20
+ s.swift_version = "5.0"
21
+
22
+ end