react-amwal-pay 0.1.17 → 0.1.18
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/ReactAmwalPay.podspec +2 -1
- package/android/build.gradle +1 -1
- package/android/gradle.properties +1 -1
- package/ios/ReactAmwalPay.swift +95 -348
- package/lib/module/AmwalPaySDK.js +57 -16
- package/lib/module/AmwalPaySDK.js.map +1 -1
- package/lib/module/index.js +2 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/network/NetworkClient.js +38 -11
- package/lib/module/network/NetworkClient.js.map +1 -1
- package/lib/module/utils/Logger.js +151 -0
- package/lib/module/utils/Logger.js.map +1 -0
- package/lib/typescript/src/AmwalPaySDK.d.ts +13 -0
- package/lib/typescript/src/AmwalPaySDK.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/network/NetworkClient.d.ts +1 -0
- package/lib/typescript/src/network/NetworkClient.d.ts.map +1 -1
- package/lib/typescript/src/utils/Logger.d.ts +76 -0
- package/lib/typescript/src/utils/Logger.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/AmwalPaySDK.ts +72 -28
- package/src/index.tsx +4 -0
- package/src/network/NetworkClient.ts +43 -11
- package/src/utils/Logger.ts +162 -0
package/ReactAmwalPay.podspec
CHANGED
|
@@ -14,10 +14,11 @@ Pod::Spec.new do |s|
|
|
|
14
14
|
s.source = { :git => "https://github.com/amwal-pay/AnwalPaySDKReactNative.git", :tag => "#{s.version}" }
|
|
15
15
|
|
|
16
16
|
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
|
+
s.exclude_files = "ios/build/**"
|
|
17
18
|
s.private_header_files = "ios/**/*.h"
|
|
18
19
|
|
|
19
20
|
# Default to Release subspec
|
|
20
21
|
amwal_subspec = ENV['AMWAL_SUBSPEC'] || 'Release'
|
|
21
|
-
s.dependency "amwalsdk/#{amwal_subspec}"
|
|
22
|
+
s.dependency "amwalsdk/#{amwal_subspec}", '>= 1.1.93'
|
|
22
23
|
install_modules_dependencies(s)
|
|
23
24
|
end
|
package/android/build.gradle
CHANGED
|
@@ -79,7 +79,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
|
79
79
|
dependencies {
|
|
80
80
|
implementation "com.facebook.react:react-android"
|
|
81
81
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
82
|
-
implementation("com.amwal-pay:amwal_sdk
|
|
82
|
+
implementation("com.amwal-pay:amwal_sdk:1.1.90"){
|
|
83
83
|
exclude group: 'com.android.support', module: 'support-v4'
|
|
84
84
|
exclude group: 'com.android.support', module: 'design'
|
|
85
85
|
}
|
package/ios/ReactAmwalPay.swift
CHANGED
|
@@ -3,397 +3,144 @@ import amwalsdk
|
|
|
3
3
|
import React
|
|
4
4
|
import UIKit
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
init(sdkViewController: UIViewController) {
|
|
13
|
-
self.sdkViewController = sdkViewController
|
|
14
|
-
super.init(nibName: nil, bundle: nil)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
required init?(coder: NSCoder) {
|
|
18
|
-
fatalError("init(coder:) has not been implemented")
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
override func viewDidLoad() {
|
|
22
|
-
super.viewDidLoad()
|
|
23
|
-
|
|
24
|
-
// Add SDK view controller as child - this keeps it alive during 3DS presentation
|
|
25
|
-
addChild(sdkViewController)
|
|
26
|
-
view.addSubview(sdkViewController.view)
|
|
27
|
-
sdkViewController.view.frame = view.bounds
|
|
28
|
-
sdkViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
29
|
-
sdkViewController.didMove(toParent: self)
|
|
30
|
-
|
|
31
|
-
view.backgroundColor = .clear
|
|
32
|
-
sdkViewController.view.backgroundColor = .clear
|
|
33
|
-
|
|
34
|
-
// Enable presentation context to handle 3DS WebView presentation
|
|
35
|
-
// This is critical: allows 3DS WebView to present on top without dismissing the SDK
|
|
36
|
-
definesPresentationContext = true
|
|
37
|
-
providesPresentationContextTransitionStyle = true
|
|
38
|
-
|
|
39
|
-
// Ensure the SDK view controller also allows nested presentations
|
|
40
|
-
// This prevents it from being dismissed when 3DS presents its WebView
|
|
41
|
-
sdkViewController.definesPresentationContext = true
|
|
42
|
-
sdkViewController.providesPresentationContextTransitionStyle = true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
override func viewDidAppear(_ animated: Bool) {
|
|
46
|
-
super.viewDidAppear(animated)
|
|
47
|
-
// Ensure container is ready for nested presentations
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private func topmostPresentedViewController() -> UIViewController {
|
|
51
|
-
var topController: UIViewController = self
|
|
52
|
-
while let presented = topController.presentedViewController {
|
|
53
|
-
topController = presented
|
|
54
|
-
}
|
|
55
|
-
return topController
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
|
59
|
-
// If already presenting, present from the topmost controller
|
|
60
|
-
// This allows 3DS WebView to present on top without dismissing the SDK
|
|
61
|
-
if let presented = presentedViewController {
|
|
62
|
-
let topmost = topmostPresentedViewController()
|
|
63
|
-
topmost.present(viewControllerToPresent, animated: flag, completion: completion)
|
|
64
|
-
} else {
|
|
65
|
-
super.present(viewControllerToPresent, animated: flag, completion: completion)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
6
|
+
struct AmwalLog {
|
|
7
|
+
private static let prefix = "[AMWAL_PAY_SDK]"
|
|
8
|
+
static func debug(_ msg: String, tag: String = "") { print("\(prefix) 🔵 [\(tag)] \(msg)") }
|
|
9
|
+
static func info(_ msg: String, tag: String = "") { print("\(prefix) 🟢 [\(tag)] \(msg)") }
|
|
10
|
+
static func warn(_ msg: String, tag: String = "") { print("\(prefix) 🟡 [\(tag)] \(msg)") }
|
|
11
|
+
static func error(_ msg: String, tag: String = "") { print("\(prefix) 🔴 [\(tag)] \(msg)") }
|
|
68
12
|
}
|
|
69
13
|
|
|
70
|
-
|
|
71
|
-
public extension UIViewController {
|
|
72
|
-
static let swizzlePresentOnce: Void = {
|
|
73
|
-
let originalSelector = #selector(UIViewController.present(_:animated:completion:))
|
|
74
|
-
let swizzledSelector = #selector(UIViewController.swizzled_present(_:animated:completion:))
|
|
75
|
-
|
|
76
|
-
guard let originalMethod = class_getInstanceMethod(UIViewController.self, originalSelector),
|
|
77
|
-
let swizzledMethod = class_getInstanceMethod(UIViewController.self, swizzledSelector) else {
|
|
78
|
-
return
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
method_exchangeImplementations(originalMethod, swizzledMethod)
|
|
82
|
-
}()
|
|
83
|
-
|
|
84
|
-
@objc dynamic func swizzled_present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
|
85
|
-
// Check if this view controller's view is in the window hierarchy
|
|
86
|
-
if self.view.window == nil {
|
|
87
|
-
print("⚠️ Attempting to present on VC not in hierarchy, finding correct presenter...")
|
|
88
|
-
// Find the correct view controller to present from
|
|
89
|
-
if let topVC = UIViewController.getTopMostViewController() {
|
|
90
|
-
topVC.swizzled_present(viewControllerToPresent, animated: flag, completion: completion)
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Special handling for ShareableContainerViewController and its children
|
|
96
|
-
// If presenting from a child of ShareableContainerViewController, forward to container
|
|
97
|
-
if let container = findShareableContainer() {
|
|
98
|
-
if container != self {
|
|
99
|
-
// We're a child of the container, forward to container
|
|
100
|
-
container.swizzled_present(viewControllerToPresent, animated: flag, completion: completion)
|
|
101
|
-
return
|
|
102
|
-
}
|
|
103
|
-
// We ARE the container - allow nested presentations
|
|
104
|
-
// Don't auto-dismiss, just present on top
|
|
105
|
-
} else if self.presentedViewController != nil && !self.definesPresentationContext {
|
|
106
|
-
// Not a container, and already presenting - auto-dismiss
|
|
107
|
-
print("⚠️ Already presenting, dismissing first...")
|
|
108
|
-
self.dismiss(animated: false) { [weak self] in
|
|
109
|
-
self?.swizzled_present(viewControllerToPresent, animated: flag, completion: completion)
|
|
110
|
-
}
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Call original implementation
|
|
115
|
-
self.swizzled_present(viewControllerToPresent, animated: flag, completion: completion)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Helper to find ShareableContainerViewController in parent hierarchy
|
|
119
|
-
private func findShareableContainer() -> ShareableContainerViewController? {
|
|
120
|
-
var current: UIViewController? = self
|
|
121
|
-
while let parent = current?.parent {
|
|
122
|
-
if let container = parent as? ShareableContainerViewController {
|
|
123
|
-
return container
|
|
124
|
-
}
|
|
125
|
-
current = parent
|
|
126
|
-
}
|
|
127
|
-
if let container = self as? ShareableContainerViewController {
|
|
128
|
-
return container
|
|
129
|
-
}
|
|
130
|
-
return nil
|
|
131
|
-
}
|
|
132
|
-
|
|
14
|
+
extension UIViewController {
|
|
133
15
|
static func getTopMostViewController() -> UIViewController? {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
topController = keyWindow?.rootViewController
|
|
142
|
-
} else {
|
|
143
|
-
topController = UIApplication.shared.keyWindow?.rootViewController
|
|
16
|
+
let keyWindow = UIApplication.shared.connectedScenes
|
|
17
|
+
.compactMap { $0 as? UIWindowScene }
|
|
18
|
+
.flatMap { $0.windows }
|
|
19
|
+
.first { $0.isKeyWindow }
|
|
20
|
+
var top = keyWindow?.rootViewController
|
|
21
|
+
while let presented = top?.presentedViewController {
|
|
22
|
+
top = presented
|
|
144
23
|
}
|
|
145
|
-
|
|
146
|
-
while let presented = topController?.presentedViewController {
|
|
147
|
-
topController = presented
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return topController
|
|
24
|
+
return top
|
|
151
25
|
}
|
|
152
26
|
}
|
|
153
27
|
|
|
154
28
|
@objc(ReactAmwalPay)
|
|
155
29
|
open class ReactAmwalPay: RCTEventEmitter {
|
|
156
30
|
private var hasListeners = false
|
|
157
|
-
private var
|
|
158
|
-
|
|
159
|
-
// Initialize swizzling when the class is first loaded
|
|
160
|
-
private static let initializeSwizzling: Void = {
|
|
161
|
-
_ = UIViewController.swizzlePresentOnce
|
|
162
|
-
}()
|
|
163
|
-
|
|
164
|
-
// Initialize swizzling when the module is loaded
|
|
165
|
-
public override init() {
|
|
166
|
-
super.init()
|
|
167
|
-
_ = ReactAmwalPay.initializeSwizzling
|
|
168
|
-
}
|
|
31
|
+
private weak var presentingVC: UIViewController?
|
|
169
32
|
|
|
170
33
|
open override func supportedEvents() -> [String]! {
|
|
171
34
|
return ["onResponse", "onCustomerId"]
|
|
172
35
|
}
|
|
173
36
|
|
|
174
|
-
open override func startObserving() {
|
|
175
|
-
|
|
176
|
-
hasListeners = true
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
open override func stopObserving() {
|
|
180
|
-
print("🔴 stopObserving called - setting hasListeners = false")
|
|
181
|
-
hasListeners = false
|
|
182
|
-
}
|
|
37
|
+
open override func startObserving() { hasListeners = true }
|
|
38
|
+
open override func stopObserving() { hasListeners = false }
|
|
183
39
|
|
|
184
40
|
private func emitOnResponse(_ params: [String: Any]) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
print("🔴 Sending onResponse event to JS")
|
|
189
|
-
sendEvent(withName: "onResponse", body: params)
|
|
190
|
-
} else {
|
|
191
|
-
print("🔴 NOT sending - no listeners!")
|
|
41
|
+
guard hasListeners else {
|
|
42
|
+
AmwalLog.warn("onResponse not sent — no listeners", tag: "EVENTS")
|
|
43
|
+
return
|
|
192
44
|
}
|
|
45
|
+
sendEvent(withName: "onResponse", body: params)
|
|
193
46
|
}
|
|
194
47
|
|
|
195
48
|
private func emitOnCustomerId(_ customerId: String?) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if hasListeners {
|
|
199
|
-
print("🔴 Sending onCustomerId event to JS")
|
|
200
|
-
sendEvent(withName: "onCustomerId", body: customerId)
|
|
201
|
-
} else {
|
|
202
|
-
print("🔴 NOT sending - no listeners!")
|
|
203
|
-
}
|
|
49
|
+
guard hasListeners else { return }
|
|
50
|
+
sendEvent(withName: "onCustomerId", body: customerId)
|
|
204
51
|
}
|
|
205
52
|
|
|
206
|
-
private func mapEnvironment(
|
|
207
|
-
switch
|
|
208
|
-
case "PROD": return .PROD
|
|
209
|
-
case "UAT": return .UAT
|
|
210
|
-
case "SIT": return .SIT
|
|
211
|
-
default: return .PROD
|
|
212
|
-
}
|
|
53
|
+
private func mapEnvironment(_ s: String) -> Config.Environment {
|
|
54
|
+
switch s { case "PROD": return .PROD; case "UAT": return .UAT; default: return .SIT }
|
|
213
55
|
}
|
|
214
|
-
|
|
215
|
-
private func
|
|
216
|
-
switch
|
|
217
|
-
case "OMR": return .OMR
|
|
218
|
-
default: return .OMR
|
|
219
|
-
}
|
|
56
|
+
private func mapCurrency(_ s: String) -> Config.Currency { return .OMR }
|
|
57
|
+
private func mapTransactionType(_ s: String) -> Config.TransactionType {
|
|
58
|
+
switch s { case "NFC": return .nfc; case "APPLE_PAY": return .applePay; default: return .cardWallet }
|
|
220
59
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
switch transactionType {
|
|
224
|
-
case "CARD_WALLET": return .cardWallet
|
|
225
|
-
case "NFC": return .nfc
|
|
226
|
-
case "APPLE_PAY": return .applePay
|
|
227
|
-
default: return .cardWallet
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
private func mapLocale(locale: String) -> Config.Locale {
|
|
232
|
-
switch locale {
|
|
233
|
-
case "en": return .en
|
|
234
|
-
case "ar": return .ar
|
|
235
|
-
default: return .en
|
|
236
|
-
}
|
|
60
|
+
private func mapLocale(_ s: String) -> Config.Locale {
|
|
61
|
+
switch s { case "ar": return .ar; default: return .en }
|
|
237
62
|
}
|
|
238
|
-
|
|
239
|
-
private func prepareConfig(config: [String: Any]) -> Config {
|
|
240
|
-
// Handle additionValues
|
|
241
|
-
var additionValues: [String: String] = Config.generateDefaultAdditionValues()
|
|
242
|
-
if let configAdditionValues = config["additionValues"] as? [String: String] {
|
|
243
|
-
// Merge with default values, allowing override
|
|
244
|
-
for (key, value) in configAdditionValues {
|
|
245
|
-
additionValues[key] = value
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
63
|
|
|
64
|
+
private func buildConfig(_ raw: [String: Any]) -> Config {
|
|
65
|
+
var additionValues = Config.generateDefaultAdditionValues()
|
|
66
|
+
if let extra = raw["additionValues"] as? [String: String] {
|
|
67
|
+
extra.forEach { additionValues[$0] = $1 }
|
|
68
|
+
}
|
|
69
|
+
let secureHash = raw["secureHash"] as? String
|
|
249
70
|
return Config(
|
|
250
|
-
environment: mapEnvironment(
|
|
251
|
-
sessionToken:
|
|
252
|
-
currency: mapCurrency(
|
|
253
|
-
amount:
|
|
254
|
-
merchantId:
|
|
255
|
-
terminalId:
|
|
256
|
-
customerId:
|
|
257
|
-
locale: mapLocale(
|
|
258
|
-
transactionType: mapTransactionType(
|
|
259
|
-
transactionId:
|
|
71
|
+
environment: mapEnvironment(raw["environment"] as? String ?? "SIT"),
|
|
72
|
+
sessionToken: raw["sessionToken"] as? String ?? "",
|
|
73
|
+
currency: mapCurrency(raw["currency"] as? String ?? "OMR"),
|
|
74
|
+
amount: raw["amount"] as? String ?? "",
|
|
75
|
+
merchantId: raw["merchantId"] as? String ?? "",
|
|
76
|
+
terminalId: raw["terminalId"] as? String ?? "",
|
|
77
|
+
customerId: raw["customerId"] as? String,
|
|
78
|
+
locale: mapLocale(raw["locale"] as? String ?? "en"),
|
|
79
|
+
transactionType: mapTransactionType(raw["transactionType"] as? String ?? "CARD_WALLET"),
|
|
80
|
+
transactionId: raw["transactionId"] as? String ?? Config.generateTransactionId(),
|
|
260
81
|
additionValues: additionValues,
|
|
261
|
-
merchantReference:
|
|
262
|
-
secureHash:
|
|
82
|
+
merchantReference: raw["merchantReference"] as? String,
|
|
83
|
+
secureHash: secureHash
|
|
263
84
|
)
|
|
264
85
|
}
|
|
265
|
-
|
|
86
|
+
|
|
266
87
|
@objc
|
|
267
88
|
open func initiate(_ config: [String: Any]) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
"status": "error",
|
|
311
|
-
"message": "Invalid response format",
|
|
312
|
-
"rawResponse": response
|
|
313
|
-
]
|
|
314
|
-
self?.emitOnResponse(errorData)
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
onCustomerId: { [weak self] customerId in
|
|
318
|
-
print("🟠 SDK onCustomerId callback fired with: \(customerId ?? "nil")")
|
|
319
|
-
self?.emitOnCustomerId(customerId)
|
|
320
|
-
}
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
// Ensure transparency
|
|
324
|
-
sdkVC.view.backgroundColor = .clear
|
|
325
|
-
sdkVC.view.isOpaque = false
|
|
326
|
-
|
|
327
|
-
// Wrap SDK view controller in container
|
|
328
|
-
let containerVC = ShareableContainerViewController(sdkViewController: sdkVC)
|
|
329
|
-
containerVC.view.backgroundColor = .clear
|
|
330
|
-
|
|
331
|
-
print("🟠 SDK ViewController wrapped in container")
|
|
332
|
-
print("🟠 Creating separate window for SDK...")
|
|
333
|
-
|
|
334
|
-
// Create a separate window for the SDK
|
|
335
|
-
// This prevents modal presentation issues - SDK lives in its own window
|
|
336
|
-
// 3DS can present on top without affecting the SDK
|
|
337
|
-
if #available(iOS 13.0, *) {
|
|
338
|
-
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
|
|
339
|
-
self.sdkWindow = UIWindow(windowScene: windowScene)
|
|
340
|
-
}
|
|
341
|
-
} else {
|
|
342
|
-
self.sdkWindow = UIWindow(frame: UIScreen.main.bounds)
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
guard let sdkWindow = self.sdkWindow else {
|
|
346
|
-
print("🟠 Failed to create SDK window, falling back to modal presentation")
|
|
347
|
-
rootVC.present(containerVC, animated: true)
|
|
348
|
-
return
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
sdkWindow.rootViewController = containerVC
|
|
352
|
-
sdkWindow.windowLevel = .normal + 1 // Above main window
|
|
353
|
-
sdkWindow.backgroundColor = .clear
|
|
354
|
-
sdkWindow.isOpaque = false
|
|
355
|
-
sdkWindow.makeKeyAndVisible()
|
|
356
|
-
|
|
357
|
-
print("🟠 SDK window created and made visible")
|
|
358
|
-
} catch {
|
|
359
|
-
print("Presentation failed: \(error.localizedDescription)")
|
|
360
|
-
let errorData: [String: Any] = [
|
|
361
|
-
"data": [
|
|
362
|
-
"status": "ERROR",
|
|
363
|
-
"message": error.localizedDescription
|
|
364
|
-
]
|
|
365
|
-
]
|
|
366
|
-
self.emitOnResponse(errorData)
|
|
367
|
-
}
|
|
368
|
-
}
|
|
89
|
+
DispatchQueue.main.async {
|
|
90
|
+
do {
|
|
91
|
+
let sdkConfig = self.buildConfig(config)
|
|
92
|
+
AmwalLog.info("Building SDK config — env: \(sdkConfig.environment), amount: \(sdkConfig.amount)", tag: "SDK")
|
|
93
|
+
|
|
94
|
+
guard let rootVC = UIViewController.getTopMostViewController() else {
|
|
95
|
+
AmwalLog.error("No root VC found", tag: "SDK")
|
|
96
|
+
self.emitOnResponse(["status": "ERROR", "message": "No root view controller"])
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
AmwalLog.info("Presenting from: \(type(of: rootVC))", tag: "SDK")
|
|
100
|
+
|
|
101
|
+
let sdk = AmwalSDK()
|
|
102
|
+
let sdkVC = try sdk.createViewController(
|
|
103
|
+
config: sdkConfig,
|
|
104
|
+
onResponse: { [weak self] response in
|
|
105
|
+
AmwalLog.info("onResponse received", tag: "SDK")
|
|
106
|
+
DispatchQueue.main.async {
|
|
107
|
+
self?.presentingVC?.dismiss(animated: true)
|
|
108
|
+
self?.presentingVC = nil
|
|
109
|
+
}
|
|
110
|
+
guard let response = response,
|
|
111
|
+
let data = response.data(using: .utf8),
|
|
112
|
+
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
113
|
+
else { return }
|
|
114
|
+
self?.emitOnResponse(json)
|
|
115
|
+
},
|
|
116
|
+
onCustomerId: { [weak self] customerId in
|
|
117
|
+
self?.emitOnCustomerId(customerId)
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
sdkVC.modalPresentationStyle = .overFullScreen
|
|
122
|
+
self.presentingVC = sdkVC
|
|
123
|
+
rootVC.present(sdkVC, animated: true) {
|
|
124
|
+
AmwalLog.info("SDK presented", tag: "SDK")
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
AmwalLog.error("initiate failed: \(error)", tag: "SDK")
|
|
128
|
+
self.emitOnResponse(["status": "ERROR", "message": error.localizedDescription])
|
|
129
|
+
}
|
|
130
|
+
}
|
|
369
131
|
}
|
|
370
|
-
|
|
132
|
+
|
|
371
133
|
@objc
|
|
372
134
|
open override func addListener(_ eventName: String) {
|
|
373
135
|
super.addListener(eventName)
|
|
374
|
-
|
|
375
|
-
if !hasListeners {
|
|
376
|
-
hasListeners = true
|
|
377
|
-
print("🔴 Set hasListeners = true")
|
|
378
|
-
}
|
|
136
|
+
hasListeners = true
|
|
379
137
|
}
|
|
380
138
|
|
|
381
139
|
@objc
|
|
382
140
|
open override func removeListeners(_ count: Double) {
|
|
383
141
|
super.removeListeners(count)
|
|
384
|
-
print("🔴 removeListeners called with count: \(count)")
|
|
385
142
|
}
|
|
386
143
|
|
|
387
144
|
@objc
|
|
388
|
-
public override static func requiresMainQueueSetup() -> Bool {
|
|
389
|
-
return true
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Dismiss SDK window when payment completes
|
|
393
|
-
private func dismissSDKWindow() {
|
|
394
|
-
print("🟠 Dismissing SDK window...")
|
|
395
|
-
sdkWindow?.isHidden = true
|
|
396
|
-
sdkWindow?.rootViewController = nil
|
|
397
|
-
sdkWindow = nil
|
|
398
|
-
}
|
|
145
|
+
public override static func requiresMainQueueSetup() -> Bool { return true }
|
|
399
146
|
}
|