@jimrising/easymerchantsdk-react-native 2.2.9 → 2.3.1
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/.idea/caches/deviceStreaming.xml +11 -0
- package/README.md +310 -171
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$2.dex +0 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$3.dex +0 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule.dex +0 -0
- package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
- package/android/build/.transforms/3f3a228346492fb34e3f574e941b632f/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule$2.dex +0 -0
- package/android/build/.transforms/3f3a228346492fb34e3f574e941b632f/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule$3.dex +0 -0
- package/android/build/.transforms/3f3a228346492fb34e3f574e941b632f/transformed/bundleLibRuntimeToDirRelease/bundleLibRuntimeToDirRelease_dex/com/reactlibrary/RNEasymerchantsdkModule.dex +0 -0
- package/android/build/.transforms/3f3a228346492fb34e3f574e941b632f/transformed/bundleLibRuntimeToDirRelease/desugar_graph.bin +0 -0
- package/android/build/.transforms/58b147bdc43284da2468dab19bc4a64d/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/.transforms/e9a664a11ce12edf79cd87b1e07aa243/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/intermediates/aar_main_jar/release/syncReleaseLibJars/classes.jar +0 -0
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/compile_library_classes_jar/release/bundleLibCompileToJarRelease/classes.jar +0 -0
- package/android/build/intermediates/full_jar/release/createFullJarRelease/full.jar +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-dependencies.xml +4 -4
- package/android/build/intermediates/incremental/lintVitalAnalyzeRelease/release-artifact-libraries.xml +4 -4
- package/android/build/intermediates/incremental/release/mergeReleaseResources/compile-file-map.properties +916 -916
- package/android/build/intermediates/incremental/release/mergeReleaseResources/merger.xml +3 -3
- package/android/build/intermediates/incremental/release/packageReleaseResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$3.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
- package/android/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
- package/android/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$3.class +0 -0
- package/android/build/intermediates/javac/release/compileReleaseJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
- package/android/build/intermediates/lint-cache/lintVitalAnalyzeRelease/maven.google/com/android/tools/build/group-index.xml +22 -0
- package/android/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-dependencies.xml +4 -4
- package/android/build/intermediates/lint_model/release/generateReleaseLintModel/release-artifact-libraries.xml +4 -4
- package/android/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-dependencies.xml +4 -4
- package/android/build/intermediates/lint_vital_lint_model/release/generateReleaseLintVitalModel/release-artifact-libraries.xml +4 -4
- package/android/build/intermediates/local_aar_for_lint/release/out.aar +0 -0
- package/android/build/intermediates/merged_res/release/mergeReleaseResources/layout/activity_payment_done_url.xml +2 -1
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/multi-v2/values-night-v8.json +2 -2
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/multi-v2/values.json +38 -38
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/anim-v21.json +9 -9
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/anim.json +24 -24
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/animator-v21.json +1 -1
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/animator.json +34 -34
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/color-night-v8.json +3 -3
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/color-v31.json +10 -10
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/color.json +153 -153
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-anydpi-v21.json +6 -6
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-hdpi-v4.json +13 -13
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-ldpi-v4.json +6 -6
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-mdpi-v4.json +12 -12
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-v21.json +4 -4
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-v23.json +7 -7
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-v29.json +1 -1
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-xhdpi-v4.json +12 -12
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-xxhdpi-v4.json +7 -7
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable-xxxhdpi-v4.json +7 -7
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/drawable.json +395 -395
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/font.json +9 -9
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/interpolator-v21.json +10 -10
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/interpolator.json +11 -11
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout-land.json +3 -3
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout-sw600dp-v13.json +2 -2
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout-v21.json +4 -4
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout-v26.json +1 -1
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/layout.json +108 -108
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-anydpi-v26.json +2 -2
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-hdpi-v4.json +2 -2
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-mdpi-v4.json +2 -2
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xhdpi-v4.json +2 -2
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xxhdpi-v4.json +2 -2
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/mipmap-xxxhdpi-v4.json +2 -2
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/raw.json +46 -46
- package/android/build/intermediates/merged_res_blame_folder/release/mergeReleaseResources/out/single/xml.json +3 -3
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$3.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule$3.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/release/bundleLibRuntimeToDirRelease/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/release/bundleLibRuntimeToJarRelease/classes.jar +0 -0
- package/android/build/intermediates/source_set_path_map/release/mapReleaseSourceSetPaths/file-map.txt +38 -38
- package/android/build/intermediates/verified_library_resources/release/verifyReleaseResources/compiled/layout_activity_payment_done_url.xml.flat +0 -0
- package/android/build/outputs/aar/jimrising_easymerchantsdk-react-native-release.aar +0 -0
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build/tmp/compileReleaseJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/reactlibrary/RNEasymerchantsdkModule.java +56 -1
- package/ios/Classes/EasyMerchantSdk.m +124 -141
- package/ios/Classes/EasyMerchantSdk.swift +270 -502
- package/ios/Classes/EasyPayViewController.swift +18 -15
- package/ios/CustomComponents/TextFieldStackView.swift +16 -46
- package/ios/EnvironmentConfig.swift +16 -0
- package/ios/Example/ViewController.swift +513 -76
- package/ios/Models/Request.swift +365 -171
- package/ios/Pods/UserDefaults/UserStoreSingleton.swift +116 -12
- package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +2 -32
- package/ios/Pods/ViewControllers/GrailPayVC.swift +258 -18
- package/ios/Pods/ViewControllers/OTPVerificationVC.swift +1 -66
- package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +571 -369
- package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +18 -9
- package/ios/easymerchantsdk.podspec +1 -1
- package/ios/easymerchantsdk.storyboard +108 -85
- package/package.json +1 -1
|
@@ -4,568 +4,336 @@ import React
|
|
|
4
4
|
|
|
5
5
|
@objc(EasyMerchantSdkPlugin)
|
|
6
6
|
public class EasyMerchantSdkPlugin: NSObject, RCTBridgeModule {
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
// MARK: - React Native Module Setup
|
|
9
|
+
|
|
10
|
+
@objc
|
|
9
11
|
public static func moduleName() -> String! {
|
|
10
12
|
return "EasyMerchantSdk"
|
|
11
13
|
}
|
|
12
|
-
|
|
14
|
+
|
|
15
|
+
@objc
|
|
13
16
|
public static func requiresMainQueueSetup() -> Bool {
|
|
14
|
-
|
|
15
|
-
return true
|
|
17
|
+
return true // UI-related module
|
|
16
18
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// MARK: - Stored Promise Callbacks
|
|
21
|
-
private var billingResolver: RCTPromiseResolveBlock?
|
|
22
|
-
private var billingRejecter: RCTPromiseRejectBlock?
|
|
23
|
-
private var paymentReferenceResolver: RCTPromiseResolveBlock?
|
|
24
|
-
private var paymentReferenceRejecter: RCTPromiseRejectBlock?
|
|
25
|
-
|
|
26
|
-
// MARK: - Notification tokens (so we can remove safely)
|
|
27
|
-
private var clientTokenObserver: NSObjectProtocol?
|
|
28
|
-
private var referenceObserver: NSObjectProtocol?
|
|
29
|
-
|
|
30
|
-
// MARK: - View Controller Reference
|
|
19
|
+
|
|
20
|
+
// MARK: - Properties
|
|
21
|
+
|
|
31
22
|
private weak var viewController: UIViewController?
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
private var resolve: RCTPromiseResolveBlock?
|
|
24
|
+
private var reject: RCTPromiseRejectBlock?
|
|
25
|
+
|
|
26
|
+
// MARK: - Init / Deinit
|
|
27
|
+
|
|
28
|
+
override public init() {
|
|
35
29
|
super.init()
|
|
30
|
+
// Removed NotificationCenter observers; using delegate pattern instead
|
|
36
31
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
print("✅ EasyMerchantSdkPlugin bridge initialized")
|
|
41
|
-
}
|
|
32
|
+
|
|
33
|
+
deinit {
|
|
34
|
+
// No need for NotificationCenter cleanup
|
|
42
35
|
}
|
|
43
|
-
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
36
|
+
|
|
37
|
+
// MARK: - React Native Exposed Methods
|
|
38
|
+
|
|
39
|
+
@objc(configureEnvironment:apiKey:apiSecret:resolver:rejecter:)
|
|
40
|
+
public func configureEnvironment(_ env: String,
|
|
41
|
+
apiKey: String,
|
|
42
|
+
apiSecret: String,
|
|
43
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
44
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
45
|
+
let environment: EnvironmentConfig.Environment
|
|
53
46
|
switch env.lowercased() {
|
|
54
|
-
case "production":
|
|
55
|
-
case "sandbox":
|
|
56
|
-
|
|
47
|
+
case "production": environment = .production
|
|
48
|
+
case "sandbox": environment = .sandbox
|
|
49
|
+
case "staging": environment = .staging
|
|
50
|
+
default:
|
|
51
|
+
reject("INVALID_ENVIRONMENT", "Environment must be 'production', 'sandbox', or 'staging'", nil)
|
|
52
|
+
return
|
|
57
53
|
}
|
|
54
|
+
|
|
55
|
+
EnvironmentConfig.setEnvironment(environment)
|
|
58
56
|
EnvironmentConfig.configure(apiKey: apiKey, apiSecret: apiSecret)
|
|
57
|
+
resolve("Environment configured successfully")
|
|
59
58
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
59
|
+
|
|
60
|
+
@objc(makePayment:resolver:rejecter:)
|
|
61
|
+
public func makePayment(_ jsonConfig: String,
|
|
62
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
63
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
64
|
+
launchPayment(jsonConfig: jsonConfig, resolve: resolve, reject: reject)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@objc(makePaymentV2:resolver:rejecter:)
|
|
68
|
+
public func makePaymentV2(_ jsonConfig: String,
|
|
69
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
70
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
71
|
+
launchPayment(jsonConfig: jsonConfig, resolve: resolve, reject: reject)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private func launchPayment(jsonConfig: String,
|
|
75
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
76
|
+
reject: @escaping RCTPromiseRejectBlock) {
|
|
77
|
+
self.resolve = resolve
|
|
78
|
+
self.reject = reject
|
|
79
|
+
|
|
80
|
+
guard let jsonData = jsonConfig.data(using: .utf8),
|
|
81
|
+
let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] else {
|
|
82
|
+
reject("INVALID_JSON", "Failed to parse JSON configuration", nil)
|
|
83
|
+
clearCallbacks()
|
|
71
84
|
return
|
|
72
85
|
}
|
|
73
86
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
amount = amountString
|
|
79
|
-
} else if let amountNumber = amountValue as? NSNumber {
|
|
80
|
-
amount = amountNumber.stringValue
|
|
81
|
-
} else if let amountDouble = amountValue as? Double {
|
|
82
|
-
amount = String(amountDouble)
|
|
83
|
-
} else {
|
|
84
|
-
amount = "0"
|
|
85
|
-
}
|
|
86
|
-
} else {
|
|
87
|
-
amount = "0"
|
|
87
|
+
guard let presentingVC = viewController ?? getTopViewController() else {
|
|
88
|
+
reject("NO_VIEW_CONTROLLER", "Unable to find a valid view controller", nil)
|
|
89
|
+
clearCallbacks()
|
|
90
|
+
return
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
let
|
|
91
|
-
let
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if paymentMethods.contains("ach") && !paymentMethods.contains("bank") {
|
|
96
|
-
print("🔄 iOS SDK: Converting 'ach' to 'bank' for payment methods")
|
|
97
|
-
paymentMethods = paymentMethods.map { method in
|
|
98
|
-
method.lowercased() == "ach" ? "bank" : method
|
|
99
|
-
}
|
|
93
|
+
let request = createRequest(from: jsonObject)
|
|
94
|
+
if let errorMessage = request.initializationErrorMessage {
|
|
95
|
+
reject("REQUEST_ERROR", errorMessage, nil)
|
|
96
|
+
clearCallbacks()
|
|
97
|
+
return
|
|
100
98
|
}
|
|
101
|
-
let appearanceSettings = config["appearanceSettings"] as? [String: Any]
|
|
102
|
-
let tokenOnly = config["tokenOnly"] as? Bool ?? false
|
|
103
|
-
let saveCard = config["saveCard"] as? Bool ?? true
|
|
104
|
-
let saveAccount = config["saveAccount"] as? Bool ?? true
|
|
105
|
-
let authenticatedACH = config["authenticatedACH"] as? Bool ?? false
|
|
106
|
-
let grailPayParams = config["grailPayParams"] as? [String: Any]
|
|
107
|
-
let submitButtonText = config["submitButtonText"] as? String ?? "Submit"
|
|
108
|
-
let isRecurring = config["is_recurring"] as? Bool ?? false
|
|
109
|
-
let numOfCycle = config["numOfCycle"] as? Int ?? 1
|
|
110
|
-
let recurringIntervals = config["recurringIntervals"] as? [String]
|
|
111
|
-
let recurringStartDateType = config["recurringStartDateType"] as? String
|
|
112
|
-
let recurringStartDate = config["recurringStartDate"] as? String
|
|
113
|
-
let secureAuthentication = config["secureAuthentication"] as? Bool ?? false
|
|
114
|
-
let showReceipt = config["showReceipt"] as? Bool ?? true
|
|
115
|
-
let showTotal = config["showTotal"] as? Bool ?? true
|
|
116
|
-
let showSubmitButton = config["showSubmitButton"] as? Bool ?? true
|
|
117
|
-
let isEmail = config["isEmail"] as? Bool ?? true
|
|
118
|
-
let email = config["email"] as? String
|
|
119
|
-
let name = config["name"] as? String
|
|
120
|
-
let enable3DS = false // Default to false for now
|
|
121
|
-
let metadata = config["metadata"] as? [String : Any]
|
|
122
99
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
print("Currency: \(currency ?? "nil")")
|
|
127
|
-
print("Payment Methods: \(paymentMethods)")
|
|
128
|
-
print("Email: \(email ?? "nil")")
|
|
129
|
-
print("Name: \(name ?? "nil")")
|
|
130
|
-
print("Token Only: \(tokenOnly)")
|
|
131
|
-
print("Save Card: \(saveCard)")
|
|
132
|
-
print("Save Account: \(saveAccount)")
|
|
133
|
-
print("Authenticated ACH: \(authenticatedACH)")
|
|
134
|
-
print("Is Recurring: \(isRecurring)")
|
|
135
|
-
print("Recurring Cycles: \(numOfCycle)")
|
|
136
|
-
print("Recurring Intervals: \(recurringIntervals ?? [])")
|
|
137
|
-
print("Recurring Start Date: \(recurringStartDate ?? "nil")")
|
|
138
|
-
print("Secure Authentication: \(secureAuthentication)")
|
|
139
|
-
print("Show Receipt: \(showReceipt)")
|
|
140
|
-
print("Show Total: \(showTotal)")
|
|
141
|
-
print("Show Submit Button: \(showSubmitButton)")
|
|
142
|
-
print("Is Email: \(isEmail)")
|
|
143
|
-
|
|
144
|
-
if let appearance = appearanceSettings {
|
|
145
|
-
print("Appearance Settings: \(appearance)")
|
|
100
|
+
let controller = EasyPayViewController(request: request, delegate: self)
|
|
101
|
+
DispatchQueue.main.async {
|
|
102
|
+
presentingVC.present(controller, animated: true)
|
|
146
103
|
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@objc(paymentReference:resolver:rejecter:)
|
|
107
|
+
public func paymentReference(_ referenceToken: String,
|
|
108
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
109
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
110
|
+
self.resolve = resolve
|
|
111
|
+
self.reject = reject
|
|
147
112
|
|
|
148
|
-
|
|
149
|
-
|
|
113
|
+
let request = Request(referenceID: true, referenceToken: referenceToken)
|
|
114
|
+
if let errorMessage = request.initializationErrorMessage {
|
|
115
|
+
reject("REQUEST_ERROR", errorMessage, nil)
|
|
116
|
+
clearCallbacks()
|
|
117
|
+
return
|
|
150
118
|
}
|
|
151
119
|
|
|
152
|
-
|
|
153
|
-
|
|
120
|
+
guard let presentingVC = viewController ?? getTopViewController() else {
|
|
121
|
+
reject("NO_VIEW_CONTROLLER", "Unable to find a valid view controller", nil)
|
|
122
|
+
clearCallbacks()
|
|
123
|
+
return
|
|
154
124
|
}
|
|
155
125
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
print("⚠️ Metadata is nil!")
|
|
126
|
+
let controller = EasyPayViewController(request: request, delegate: self)
|
|
127
|
+
DispatchQueue.main.async {
|
|
128
|
+
presentingVC.present(controller, animated: true)
|
|
160
129
|
}
|
|
161
|
-
|
|
162
|
-
// Call the existing billing method with extracted parameters
|
|
163
|
-
billing(
|
|
164
|
-
amount,
|
|
165
|
-
currency: currency,
|
|
166
|
-
fields: fields,
|
|
167
|
-
paymentMethods: paymentMethods,
|
|
168
|
-
appearanceSettings: appearanceSettings,
|
|
169
|
-
tokenOnly: tokenOnly,
|
|
170
|
-
saveCard: saveCard,
|
|
171
|
-
saveAccount: saveAccount,
|
|
172
|
-
authenticatedACH: authenticatedACH,
|
|
173
|
-
grailPayParams: grailPayParams,
|
|
174
|
-
submitButtonText: submitButtonText,
|
|
175
|
-
isRecurring: isRecurring,
|
|
176
|
-
numOfCycle: numOfCycle,
|
|
177
|
-
recurringIntervals: recurringIntervals,
|
|
178
|
-
recurringStartDateType: recurringStartDateType,
|
|
179
|
-
recurringStartDate: recurringStartDate,
|
|
180
|
-
secureAuthentication: secureAuthentication,
|
|
181
|
-
showReceipt: showReceipt,
|
|
182
|
-
showTotal: showTotal,
|
|
183
|
-
showSubmitButton: showSubmitButton,
|
|
184
|
-
isEmail: isEmail,
|
|
185
|
-
email: email,
|
|
186
|
-
name: name,
|
|
187
|
-
enable3DS: enable3DS,
|
|
188
|
-
metadata: metadata,
|
|
189
|
-
resolver: resolver,
|
|
190
|
-
rejecter: rejecter
|
|
191
|
-
)
|
|
192
130
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
name: String?,
|
|
219
|
-
enable3DS: Bool,
|
|
220
|
-
metadata: [String: Any]?,
|
|
221
|
-
resolver: @escaping RCTPromiseResolveBlock,
|
|
222
|
-
rejecter: @escaping RCTPromiseRejectBlock
|
|
223
|
-
) {
|
|
224
|
-
// 1) Validate amount
|
|
225
|
-
guard let amountValue = Double(amount), amountValue >= 0.50 else {
|
|
226
|
-
rejecter("INVALID_AMOUNT", "Amount must be at least $0.50", nil)
|
|
227
|
-
return
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// 2) Process billing info into FieldSection
|
|
231
|
-
var billingInfoData: Data? = nil
|
|
232
|
-
if let billingDict = fields {
|
|
233
|
-
var billingFields: [FieldItem] = []
|
|
234
|
-
var additionalFields: [FieldItem] = []
|
|
235
|
-
var billingVisibility = true
|
|
236
|
-
var additionalVisibility = true
|
|
237
|
-
|
|
238
|
-
// Parse billing fields
|
|
239
|
-
if let billing = billingDict["billing"] as? [[String: Any]] {
|
|
240
|
-
for field in billing {
|
|
241
|
-
guard let name = field["name"] as? String,
|
|
242
|
-
let value = field["value"] as? String,
|
|
243
|
-
let required = field["required"] as? Bool else { continue }
|
|
244
|
-
|
|
245
|
-
if let billingField = BillingFieldName(rawValue: name) {
|
|
246
|
-
billingFields.append(FieldItem(name: billingField, required: required, value: value))
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Parse additional fields
|
|
252
|
-
if let additional = billingDict["additional"] as? [[String: Any]] {
|
|
253
|
-
for field in additional {
|
|
254
|
-
guard let name = field["name"] as? String,
|
|
255
|
-
let value = field["value"] as? String,
|
|
256
|
-
let required = field["required"] as? Bool else { continue }
|
|
257
|
-
|
|
258
|
-
if let additionalField = AdditionalFieldName(rawValue: name) {
|
|
259
|
-
additionalFields.append(FieldItem(name: additionalField, required: required, value: value))
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Parse visibility
|
|
265
|
-
if let visibility = billingDict["visibility"] as? [String: Bool] {
|
|
266
|
-
billingVisibility = visibility["billing"] ?? true
|
|
267
|
-
additionalVisibility = visibility["additional"] ?? true
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
let field = FieldSection(
|
|
271
|
-
visibility: FieldsVisibility(billing: billingVisibility, additional: additionalVisibility),
|
|
272
|
-
billing: billingFields,
|
|
273
|
-
additional: additionalFields
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
do {
|
|
277
|
-
billingInfoData = try JSONEncoder().encode(field)
|
|
278
|
-
} catch {
|
|
279
|
-
rejecter("INVALID_BILLING_INFO", "Failed to encode billing info: \(error.localizedDescription)", nil)
|
|
280
|
-
return
|
|
281
|
-
}
|
|
131
|
+
|
|
132
|
+
@objc(setViewController:resolver:rejecter:)
|
|
133
|
+
public func setViewController(_ viewController: UIViewController,
|
|
134
|
+
resolver resolve: @escaping RCTPromiseResolveBlock,
|
|
135
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
136
|
+
self.viewController = viewController
|
|
137
|
+
resolve("View controller set successfully")
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@objc(getPlatformVersion:rejecter:)
|
|
141
|
+
public func getPlatformVersion(_ resolve: @escaping RCTPromiseResolveBlock,
|
|
142
|
+
rejecter reject: @escaping RCTPromiseRejectBlock) {
|
|
143
|
+
resolve("iOS \(UIDevice.current.systemVersion)")
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// MARK: - Helpers
|
|
147
|
+
|
|
148
|
+
private func getTopViewController() -> UIViewController? {
|
|
149
|
+
guard let windowScene = UIApplication.shared.connectedScenes
|
|
150
|
+
.filter({ $0.activationState == .foregroundActive })
|
|
151
|
+
.first as? UIWindowScene,
|
|
152
|
+
let rootVC = windowScene.windows
|
|
153
|
+
.filter({ $0.isKeyWindow })
|
|
154
|
+
.first?.rootViewController else {
|
|
155
|
+
return nil
|
|
282
156
|
}
|
|
283
|
-
|
|
284
|
-
// 3) Map payment methods with ACH to Bank conversion
|
|
285
|
-
var processedPaymentMethods = paymentMethods
|
|
286
157
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
158
|
+
var topVC = rootVC
|
|
159
|
+
while let presentedVC = topVC.presentedViewController {
|
|
160
|
+
topVC = presentedVC
|
|
161
|
+
}
|
|
162
|
+
return topVC
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private func resolveJSON(_ object: Any) {
|
|
166
|
+
do {
|
|
167
|
+
let jsonData = try JSONSerialization.data(withJSONObject: object, options: [])
|
|
168
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
169
|
+
resolve?(jsonString)
|
|
170
|
+
} else {
|
|
171
|
+
reject?("JSON_ERROR", "Failed to serialize response", nil)
|
|
292
172
|
}
|
|
173
|
+
} catch {
|
|
174
|
+
reject?("JSON_ERROR", "Serialization failed: \(error.localizedDescription)", error)
|
|
293
175
|
}
|
|
176
|
+
clearCallbacks()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private func clearCallbacks() {
|
|
180
|
+
resolve = nil
|
|
181
|
+
reject = nil
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private func createRequest(from json: [String: Any]) -> Request {
|
|
185
|
+
let amount = (json["amount"] as? String).flatMap(Double.init)
|
|
186
|
+
let currency = json["currency"] as? String
|
|
187
|
+
let fieldsData = json["fields"].flatMap { try? JSONSerialization.data(withJSONObject: $0, options: []) }
|
|
294
188
|
|
|
295
|
-
let
|
|
189
|
+
let paymentMethods = (json["paymentMethods"] as? [String])?.compactMap {
|
|
296
190
|
switch $0.lowercased() {
|
|
297
|
-
case "card":
|
|
298
|
-
case "
|
|
299
|
-
|
|
300
|
-
case "wallet": return .Wallet
|
|
301
|
-
default: return nil
|
|
191
|
+
case "card": return PaymentMethod.Card
|
|
192
|
+
case "ach": return PaymentMethod.Bank
|
|
193
|
+
default: return nil
|
|
302
194
|
}
|
|
303
195
|
}
|
|
304
196
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
197
|
+
let theme = (json["appearanceSettings"] as? [String: Any]).flatMap {
|
|
198
|
+
ThemeConfiguration(
|
|
199
|
+
bodyBackgroundColor: $0["bodyBackgroundColor"] as? String,
|
|
200
|
+
containerBackgroundColor: $0["containerBackgroundColor"] as? String,
|
|
201
|
+
primaryFontColor: $0["primaryFontColor"] as? String,
|
|
202
|
+
secondaryFontColor: $0["secondaryFontColor"] as? String,
|
|
203
|
+
primaryButtonBackgroundColor: $0["primaryButtonBackgroundColor"] as? String,
|
|
204
|
+
primaryButtonHoverColor: $0["primaryButtonHoverColor"] as? String,
|
|
205
|
+
primaryButtonFontColor: $0["primaryButtonFontColor"] as? String,
|
|
206
|
+
secondaryButtonBackgroundColor: $0["secondaryButtonBackgroundColor"] as? String,
|
|
207
|
+
secondaryButtonHoverColor: $0["secondaryButtonHoverColor"] as? String,
|
|
208
|
+
secondaryButtonFontColor: $0["secondaryButtonFontColor"] as? String,
|
|
209
|
+
borderRadius: $0["borderRadius"] as? String,
|
|
210
|
+
fontSize: $0["fontSize"] as? String,
|
|
211
|
+
fontWeight: $0["fontWeight"] as? Int,
|
|
212
|
+
fontFamily: $0["fontFamily"] as? String
|
|
317
213
|
)
|
|
318
214
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
primaryButtonFontColor: appearanceSettings?["primaryButtonFontColor"] as? String ?? "#FFFFFF",
|
|
329
|
-
secondaryButtonBackgroundColor: appearanceSettings?["secondaryButtonBackgroundColor"] as? String ?? "#374151",
|
|
330
|
-
secondaryButtonHoverColor: appearanceSettings?["secondaryButtonHoverColor"] as? String ?? "#4B5563",
|
|
331
|
-
secondaryButtonFontColor: appearanceSettings?["secondaryButtonFontColor"] as? String ?? "#E5E7EB",
|
|
332
|
-
borderRadius: appearanceSettings?["borderRadius"] as? String ?? "8",
|
|
333
|
-
fontSize: appearanceSettings?["fontSize"] as? String ?? "14",
|
|
334
|
-
fontWeight: appearanceSettings?["fontWeight"] as? Int ?? 500,
|
|
335
|
-
fontFamily: appearanceSettings?["fontFamily"] as? String ?? "\"Inter\", sans-serif"
|
|
336
|
-
)
|
|
337
|
-
|
|
338
|
-
// 6) Map recurring intervals
|
|
339
|
-
let intervals: [RecurringIntervals] = (recurringIntervals ?? []).compactMap { raw in
|
|
340
|
-
let cleaned = raw.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
|
341
|
-
let noDot = cleaned.hasPrefix(".") ? String(cleaned.dropFirst()) : cleaned
|
|
342
|
-
return RecurringIntervals(rawValue: noDot)
|
|
215
|
+
|
|
216
|
+
let grailPay = (json["grailPayParams"] as? [String: Any]).flatMap {
|
|
217
|
+
GrailPayRequest(
|
|
218
|
+
role: $0["role"] as? String ?? "",
|
|
219
|
+
timeout: $0["timeout"] as? Int ?? 0,
|
|
220
|
+
brandingName: $0["brandingName"] as? String ?? "",
|
|
221
|
+
finderSubtitle: $0["finderSubtitle"] as? String ?? "",
|
|
222
|
+
searchPlaceholder: $0["searchPlaceholder"] as? String ?? ""
|
|
223
|
+
)
|
|
343
224
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
return
|
|
225
|
+
|
|
226
|
+
let recurringIntervals = (json["recurringIntervals"] as? [String])?.compactMap {
|
|
227
|
+
switch $0.lowercased() {
|
|
228
|
+
case "daily": return RecurringIntervals.daily
|
|
229
|
+
case "weekly": return .weekly
|
|
230
|
+
case "monthly": return .monthly
|
|
231
|
+
case "quarterly": return .quarterly
|
|
232
|
+
case "yearly": return .yearly
|
|
233
|
+
default: return nil
|
|
234
|
+
}
|
|
348
235
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
236
|
+
|
|
237
|
+
let recurringStartDateType = (json["recurringStartDateType"] as? String).flatMap {
|
|
238
|
+
switch $0.lowercased() {
|
|
239
|
+
case "fixed": return RecurringStartDateType.fixed
|
|
353
240
|
case "custom": return .custom
|
|
354
|
-
|
|
355
|
-
default: return nil
|
|
356
|
-
}
|
|
357
|
-
}()
|
|
358
|
-
|
|
359
|
-
// 8) Build the request object
|
|
360
|
-
let request = Request(
|
|
361
|
-
amount: amountValue,
|
|
362
|
-
currency: currency ?? "usd",
|
|
363
|
-
fields: billingInfoData,
|
|
364
|
-
paymentMethods: methods.isEmpty ? [.Card, .Bank] : methods,
|
|
365
|
-
appearanceSettings: themeConfig,
|
|
366
|
-
tokenOnly: tokenOnly,
|
|
367
|
-
saveCard: saveCard,
|
|
368
|
-
saveAccount: saveAccount,
|
|
369
|
-
submitButtonText: submitButtonText ?? "Submit",
|
|
370
|
-
authenticatedACH: authenticatedACH,
|
|
371
|
-
grailPayParams: grailParams,
|
|
372
|
-
is_recurring: isRecurring,
|
|
373
|
-
numOfCycle: numOfCycle,
|
|
374
|
-
recurringIntervals: intervals,
|
|
375
|
-
recurringStartDateType: startType,
|
|
376
|
-
recurringStartDate: recurringStartDate,
|
|
377
|
-
secureAuthentication: secureAuthentication,
|
|
378
|
-
showReceipt: showReceipt,
|
|
379
|
-
showTotal: showTotal,
|
|
380
|
-
showSubmitButton: showSubmitButton,
|
|
381
|
-
referenceID: enable3DS,
|
|
382
|
-
referenceToken: nil,
|
|
383
|
-
isEmail: isEmail,
|
|
384
|
-
email: email,
|
|
385
|
-
name: name,
|
|
386
|
-
metadata: metadata
|
|
387
|
-
)
|
|
388
|
-
|
|
389
|
-
// Store resolvers
|
|
390
|
-
self.billingResolver = resolver
|
|
391
|
-
self.billingRejecter = rejecter
|
|
392
|
-
|
|
393
|
-
// Handle tokenOnly or present EasyPayViewController
|
|
394
|
-
if tokenOnly {
|
|
395
|
-
// Register observer directly (no extra Dispatch wrapper).
|
|
396
|
-
// Store the token so we can remove it later.
|
|
397
|
-
clientTokenObserver = NotificationCenter.default.addObserver(
|
|
398
|
-
forName: NSNotification.Name("ClientTokenReceived"),
|
|
399
|
-
object: nil,
|
|
400
|
-
queue: .main
|
|
401
|
-
) { [weak self] notification in
|
|
402
|
-
guard let self = self else { return }
|
|
403
|
-
if let clientToken = notification.object as? String {
|
|
404
|
-
self.billingResolver?(["status": "success", "clientToken": clientToken])
|
|
405
|
-
} else {
|
|
406
|
-
self.billingRejecter?("TOKEN_ERROR", "Failed to receive client token.", nil)
|
|
407
|
-
}
|
|
408
|
-
self.clearResolvers()
|
|
409
|
-
}
|
|
410
|
-
} else {
|
|
411
|
-
// Always present on main thread
|
|
412
|
-
DispatchQueue.main.async { [weak self] in
|
|
413
|
-
guard let self = self else { return }
|
|
414
|
-
let vc = EasyPayViewController(request: request, delegate: self)
|
|
415
|
-
|
|
416
|
-
if let host = self.viewController {
|
|
417
|
-
host.present(vc, animated: true, completion: nil)
|
|
418
|
-
} else if let root = Self.topMostViewController() {
|
|
419
|
-
root.present(vc, animated: true, completion: nil)
|
|
420
|
-
} else {
|
|
421
|
-
rejecter("NO_VIEW_CONTROLLER", "Cannot find a view controller to present.", nil)
|
|
422
|
-
self.clearResolvers()
|
|
423
|
-
}
|
|
241
|
+
default: return nil
|
|
424
242
|
}
|
|
425
243
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
// Validate reference token
|
|
435
|
-
guard !referenceToken.isEmpty else {
|
|
436
|
-
rejecter("INVALID_REFERENCE_TOKEN", "Reference token cannot be empty.", nil)
|
|
437
|
-
return
|
|
244
|
+
|
|
245
|
+
let environmentString = json["environment"] as? String
|
|
246
|
+
let environment: EnvironmentConfig.Environment
|
|
247
|
+
switch environmentString?.lowercased() {
|
|
248
|
+
case "production": environment = .production
|
|
249
|
+
case "sandbox": environment = .sandbox
|
|
250
|
+
case "staging": environment = .staging
|
|
251
|
+
default: environment = .sandbox
|
|
438
252
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
253
|
+
|
|
254
|
+
return Request(
|
|
255
|
+
amount: amount,
|
|
256
|
+
currency: currency,
|
|
257
|
+
fields: fieldsData,
|
|
258
|
+
paymentMethods: paymentMethods ?? [],
|
|
259
|
+
appearanceSettings: theme,
|
|
260
|
+
tokenOnly: json["tokenOnly"] as? Bool ?? false,
|
|
261
|
+
saveCard: json["saveCard"] as? Bool ?? false,
|
|
262
|
+
saveAccount: json["saveAccount"] as? Bool ?? false,
|
|
263
|
+
submitButtonText: json["submitButtonText"] as? String,
|
|
264
|
+
authenticatedACH: json["authenticatedACH"] as? Bool ?? false,
|
|
265
|
+
grailPayParams: grailPay,
|
|
266
|
+
is_recurring: json["is_recurring"] as? Bool ?? false,
|
|
267
|
+
numOfCycle: json["numOfCycle"] as? Int,
|
|
268
|
+
recurringIntervals: recurringIntervals,
|
|
269
|
+
recurringStartDateType: recurringStartDateType,
|
|
270
|
+
recurringStartDate: json["recurringStartDate"] as? String,
|
|
271
|
+
secureAuthentication: json["secureAuthentication"] as? Bool ?? false,
|
|
272
|
+
showReceipt: json["showReceipt"] as? Bool ?? false,
|
|
273
|
+
showTotal: json["showTotal"] as? Bool ?? false,
|
|
274
|
+
showSubmitButton: json["showSubmitButton"] as? Bool ?? false,
|
|
275
|
+
referenceID: false,
|
|
276
|
+
referenceToken: nil,
|
|
277
|
+
isEmail: json["isEmail"] as? Bool ?? false,
|
|
278
|
+
email: json["email"] as? String,
|
|
279
|
+
name: json["name"] as? String,
|
|
280
|
+
clientToken: json["clientToken"] as? String,
|
|
281
|
+
metadata: json["metadata"] as? [String: Any],
|
|
282
|
+
environment: environment
|
|
448
283
|
)
|
|
449
|
-
|
|
450
|
-
// Register observer directly (no outer Dispatch wrapper).
|
|
451
|
-
referenceObserver = NotificationCenter.default.addObserver(
|
|
452
|
-
forName: NSNotification.Name("ReferenceIDResponse"),
|
|
453
|
-
object: nil,
|
|
454
|
-
queue: .main
|
|
455
|
-
) { [weak self] notification in
|
|
456
|
-
guard let self = self else { return }
|
|
457
|
-
if let data = notification.object as? [String: Any] {
|
|
458
|
-
self.paymentReferenceResolver?(data)
|
|
459
|
-
} else {
|
|
460
|
-
self.paymentReferenceRejecter?("INVALID_RESPONSE", "Invalid response data.", nil)
|
|
461
|
-
}
|
|
462
|
-
self.clearReferenceResolvers()
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// MARK: - getPlatformVersion(...) Exposed to RN
|
|
467
|
-
@objc public func getPlatformVersion(
|
|
468
|
-
_ resolver: RCTPromiseResolveBlock,
|
|
469
|
-
rejecter: RCTPromiseRejectBlock
|
|
470
|
-
) {
|
|
471
|
-
resolver("iOS \(UIDevice.current.systemVersion)")
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// MARK: - Private Helpers
|
|
475
|
-
private func clearResolvers() {
|
|
476
|
-
billingResolver = nil
|
|
477
|
-
billingRejecter = nil
|
|
478
|
-
if let token = clientTokenObserver {
|
|
479
|
-
NotificationCenter.default.removeObserver(token)
|
|
480
|
-
clientTokenObserver = nil
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
private func clearReferenceResolvers() {
|
|
485
|
-
paymentReferenceResolver = nil
|
|
486
|
-
paymentReferenceRejecter = nil
|
|
487
|
-
if let token = referenceObserver {
|
|
488
|
-
NotificationCenter.default.removeObserver(token)
|
|
489
|
-
referenceObserver = nil
|
|
490
|
-
}
|
|
491
|
-
// (Old approach of removing by name kept out to avoid double-removal.)
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Find a top-most VC safely (iOS 13+ friendly)
|
|
495
|
-
private static func topMostViewController(base: UIViewController? = {
|
|
496
|
-
// Use key window from connected scenes
|
|
497
|
-
return UIApplication.shared.connectedScenes
|
|
498
|
-
.compactMap { $0 as? UIWindowScene }
|
|
499
|
-
.flatMap { $0.windows }
|
|
500
|
-
.first(where: { $0.isKeyWindow })?
|
|
501
|
-
.rootViewController
|
|
502
|
-
}()) -> UIViewController? {
|
|
503
|
-
if let nav = base as? UINavigationController {
|
|
504
|
-
return topMostViewController(base: nav.visibleViewController)
|
|
505
|
-
}
|
|
506
|
-
if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
|
|
507
|
-
return topMostViewController(base: selected)
|
|
508
|
-
}
|
|
509
|
-
if let presented = base?.presentedViewController {
|
|
510
|
-
return topMostViewController(base: presented)
|
|
511
|
-
}
|
|
512
|
-
return base
|
|
513
284
|
}
|
|
514
285
|
}
|
|
515
286
|
|
|
516
|
-
// MARK: -
|
|
287
|
+
// MARK: - EasyPayViewControllerDelegate
|
|
288
|
+
|
|
517
289
|
extension EasyMerchantSdkPlugin: EasyPayViewControllerDelegate {
|
|
518
290
|
public func easyPayController(_ controller: EasyPayViewController, didFinishWith result: SDKResult) {
|
|
519
|
-
|
|
291
|
+
DispatchQueue.main.async { [weak self] in
|
|
520
292
|
guard let self = self else { return }
|
|
521
|
-
|
|
293
|
+
|
|
294
|
+
var response: [String: Any] = [:]
|
|
295
|
+
|
|
522
296
|
switch result.type {
|
|
523
|
-
case .cancelled:
|
|
524
|
-
if let message = result.chargeData?["message"] as? String {
|
|
525
|
-
self.billingResolver?(["status": "cancelled", "message": message])
|
|
526
|
-
} else {
|
|
527
|
-
self.billingResolver?(["status": "cancelled"])
|
|
528
|
-
}
|
|
529
|
-
|
|
530
297
|
case .success:
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if let
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
if let bi = result.billingInfo {
|
|
537
|
-
payload["billingInfo"] = bi
|
|
298
|
+
response["status"] = "success"
|
|
299
|
+
|
|
300
|
+
if let chargeData = result.chargeData {
|
|
301
|
+
response["chargeData"] = chargeData
|
|
538
302
|
}
|
|
539
|
-
if let
|
|
540
|
-
|
|
303
|
+
if let billing = result.billingInfo {
|
|
304
|
+
response["billingInfo"] = billing
|
|
541
305
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
306
|
+
if let additional = result.additionalInfo {
|
|
307
|
+
response["additionalInfo"] = additional
|
|
308
|
+
|
|
309
|
+
if let threeDSecureStatus = additional["threeDSecureStatus"] as? [String: Any],
|
|
310
|
+
let data = threeDSecureStatus["data"] as? [String: Any],
|
|
311
|
+
let token = data["ref_token"] as? String {
|
|
312
|
+
response["referenceToken"] = token
|
|
313
|
+
}
|
|
549
314
|
}
|
|
550
|
-
|
|
551
|
-
self.billingResolver?(payload)
|
|
552
|
-
|
|
315
|
+
|
|
553
316
|
case .error:
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
]
|
|
561
|
-
|
|
562
|
-
|
|
317
|
+
let errorMessage =
|
|
318
|
+
result.error?.localizedDescription ??
|
|
319
|
+
(result.chargeData?["message"] as? String) ??
|
|
320
|
+
"Unknown error occurred"
|
|
321
|
+
|
|
322
|
+
response["status"] = "error"
|
|
323
|
+
response["message"] = errorMessage
|
|
324
|
+
|
|
325
|
+
case .cancelled:
|
|
326
|
+
response["status"] = "cancelled"
|
|
327
|
+
response["message"] = "Payment cancelled"
|
|
328
|
+
|
|
329
|
+
if let chargeData = result.chargeData {
|
|
330
|
+
response["chargeData"] = chargeData
|
|
331
|
+
}
|
|
563
332
|
}
|
|
564
|
-
|
|
565
|
-
self.
|
|
566
|
-
controller.dismiss(animated: true
|
|
333
|
+
|
|
334
|
+
self.resolveJSON(response)
|
|
335
|
+
controller.dismiss(animated: true)
|
|
567
336
|
}
|
|
568
|
-
|
|
569
|
-
DispatchQueue.main.async(execute: workItem)
|
|
570
337
|
}
|
|
571
338
|
}
|
|
339
|
+
|