@jimrising/easymerchantsdk-react-native 1.3.9 → 1.4.2

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.
Files changed (80) hide show
  1. package/.idea/caches/deviceStreaming.xml +77 -0
  2. package/README.md +140 -81
  3. package/android/.gradle/8.10/checksums/checksums.lock +0 -0
  4. package/android/.gradle/8.10/checksums/md5-checksums.bin +0 -0
  5. package/android/.gradle/8.10/checksums/sha1-checksums.bin +0 -0
  6. package/android/.gradle/8.10/fileHashes/fileHashes.lock +0 -0
  7. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  8. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  9. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  10. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  11. package/android/.gradle/buildOutputCleanup/cache.properties +1 -1
  12. package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/results.bin +1 -0
  13. package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/BuildConfig.dex +0 -0
  14. package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$1.dex +0 -0
  15. package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule$2.dex +0 -0
  16. package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkModule.dex +0 -0
  17. package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/reactlibrary/RNEasymerchantsdkPackage.dex +0 -0
  18. package/android/build/.transforms/20e1216b87bd06eaab3c9e5c68d4267a/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
  19. package/android/build/.transforms/e9a664a11ce12edf79cd87b1e07aa243/results.bin +1 -0
  20. package/android/build/.transforms/e9a664a11ce12edf79cd87b1e07aa243/transformed/classes/classes_dex/classes.dex +0 -0
  21. package/android/build/generated/source/buildConfig/debug/com/reactlibrary/BuildConfig.java +10 -0
  22. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +7 -0
  23. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
  24. package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
  25. package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
  26. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  27. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  28. package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +0 -0
  29. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -0
  30. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  31. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  32. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  33. package/android/build/intermediates/incremental/packageDebugAssets/merger.xml +2 -0
  34. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/BuildConfig.class +0 -0
  35. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  36. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  37. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  38. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkPackage.class +0 -0
  39. package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +2 -0
  40. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +7 -0
  41. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +7 -0
  42. package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
  43. package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
  44. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/BuildConfig.class +0 -0
  45. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  46. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  47. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  48. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkPackage.class +0 -0
  49. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  50. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -0
  51. package/android/build/outputs/logs/manifest-merger-debug-report.txt +17 -0
  52. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$1.class.uniqueId2 +0 -0
  53. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule$2.class.uniqueId0 +0 -0
  54. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkModule.class.uniqueId3 +0 -0
  55. package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/RNEasymerchantsdkPackage.class.uniqueId1 +0 -0
  56. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  57. package/android/build.gradle +4 -1
  58. package/android/src/main/java/com/reactlibrary/RNEasymerchantsdkModule.java +158 -36
  59. package/android/src/main/java/com/reactlibrary/RNEasymerchantsdkPackage.java +45 -13
  60. package/ios/Classes/EasyMerchantSdk.m +106 -55
  61. package/ios/Classes/EasyMerchantSdk.swift +199 -77
  62. package/ios/Classes/EasyPayViewController.swift +1 -1
  63. package/ios/CustomComponents/DatePickerHandler.swift +15 -4
  64. package/ios/EnvironmentConfig.swift +32 -30
  65. package/ios/Models/Request.swift +176 -14
  66. package/ios/Models/Result.swift +12 -5
  67. package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +855 -366
  68. package/ios/Pods/ViewControllers/BaseVC.swift +51 -36
  69. package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +1985 -178
  70. package/ios/Pods/ViewControllers/CountryListVC.swift +20 -1
  71. package/ios/Pods/ViewControllers/CustomOverlay.swift +199 -0
  72. package/ios/Pods/ViewControllers/EmailVerificationVC.swift +74 -5
  73. package/ios/Pods/ViewControllers/GrailPayVC.swift +131 -107
  74. package/ios/Pods/ViewControllers/OTPVerificationVC.swift +296 -106
  75. package/ios/Pods/ViewControllers/PaymentDoneVC.swift +35 -26
  76. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +1276 -545
  77. package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +607 -24
  78. package/ios/easymerchantsdk.podspec +1 -1
  79. package/ios/easymerchantsdk.storyboard +1388 -1165
  80. package/package.json +1 -1
@@ -5,35 +5,398 @@
5
5
  // Created by Mony's Mac on 20/05/25.
6
6
  //
7
7
 
8
+ //import UIKit
9
+ //
10
+ //class ThreeDSecurePaymentDoneVC: UIViewController {
11
+ //
12
+ // @IBOutlet weak var imgViewPaymentDone: UIImageView!
13
+ // @IBOutlet weak var imgViewLoading: UIImageView!
14
+ // @IBOutlet weak var viewMain: UIView!
15
+ // @IBOutlet weak var lblCompleteAuthentication: UILabel!
16
+ // @IBOutlet weak var btnDone: UIButton!
17
+ // @IBOutlet weak var lblPaymentLink: UILabel!
18
+ // @IBOutlet weak var lblBillingInfoData: UILabel!
19
+ //
20
+ // var easyPayDelegate: EasyPayViewControllerDelegate?
21
+ // var redirectURL: String?
22
+ // var chargeData: [String: Any] = [:]
23
+ //
24
+ // // Add these two properties:
25
+ // var billingInfo: [String: Any]?
26
+ // var additionalInfo: [String: Any]?
27
+ //
28
+ // var threeDSecureStatusResponse: [String: Any]?
29
+ //
30
+ // var statusCheckTimer: Timer?
31
+ //
32
+ // var additionalInfoData: [FieldItem]?
33
+ // var billingInfoData: [FieldItem]?
34
+ // var visibility: FieldsVisibility?
35
+ //
36
+ // override func viewDidLoad() {
37
+ // super.viewDidLoad()
38
+ // uiFinishingTouchElements()
39
+ //
40
+ // setupTapOnLabel()
41
+ //
42
+ // // showBillingInfo()
43
+ //
44
+ // checkThreeDSecureStatus()
45
+ // startStatusCheckTimer()
46
+ // }
47
+ //
48
+ // override func viewWillAppear(_ animated: Bool) {
49
+ // uiFinishingTouchElements()
50
+ // startStatusCheckTimer()
51
+ // }
52
+ //
53
+ // override func viewWillDisappear(_ animated: Bool) {
54
+ // super.viewWillDisappear(animated)
55
+ // statusCheckTimer?.invalidate()
56
+ // statusCheckTimer = nil
57
+ // }
58
+ //
59
+ // func startRotatingImage() {
60
+ // let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
61
+ // rotationAnimation.toValue = CGFloat.pi * 2 // 360 degrees in radians
62
+ // rotationAnimation.duration = 1 // Adjust the duration as needed
63
+ // rotationAnimation.isCumulative = true
64
+ // rotationAnimation.repeatCount = .infinity // Continuous rotation
65
+ // imgViewLoading.layer.add(rotationAnimation, forKey: "rotationAnimation")
66
+ // }
67
+ //
68
+ // private func setupTapOnLabel() {
69
+ // if let url = redirectURL {
70
+ // lblPaymentLink.text = url
71
+ // }
72
+ // lblPaymentLink.textColor = .systemBlue
73
+ // lblPaymentLink.isUserInteractionEnabled = true
74
+ //
75
+ // let tapGesture = UITapGestureRecognizer(target: self, action: #selector(linkTapped))
76
+ // lblPaymentLink.addGestureRecognizer(tapGesture)
77
+ // }
78
+ //
79
+ // @objc private func linkTapped() {
80
+ // guard let text = lblPaymentLink.text,
81
+ // let url = URL(string: text),
82
+ // UIApplication.shared.canOpenURL(url) else {
83
+ // return
84
+ // }
85
+ // UIApplication.shared.open(url, options: [:], completionHandler: nil)
86
+ // }
87
+ //
88
+ //// func showBillingInfo() {
89
+ //// var displayText = ""
90
+ ////
91
+ //// if let visibility = visibility {
92
+ //// if visibility.billing, let billing = billingInfo {
93
+ //// displayText += "Billing Info:-\n"
94
+ //// for (key, value) in billing {
95
+ //// displayText += "\(key.capitalized): \(value)\n"
96
+ //// }
97
+ //// }
98
+ ////
99
+ //// if visibility.additional, let additional = additionalInfo {
100
+ //// if !displayText.isEmpty {
101
+ //// displayText += "\n"
102
+ //// }
103
+ //// displayText += "Additional Info:-\n"
104
+ //// for (key, value) in additional {
105
+ //// displayText += "\(key.capitalized): \(value)\n"
106
+ //// }
107
+ //// }
108
+ //// }
109
+ ////
110
+ //// if displayText.isEmpty {
111
+ //// lblBillingInfoData.isHidden = true
112
+ //// } else {
113
+ //// lblBillingInfoData.text = displayText.trimmingCharacters(in: .whitespacesAndNewlines)
114
+ //// lblBillingInfoData.isHidden = false
115
+ //// }
116
+ //// }
117
+ //
118
+ // private func startStatusCheckTimer() {
119
+ // statusCheckTimer?.invalidate() // Invalidate any existing timer
120
+ // statusCheckTimer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { [weak self] _ in
121
+ // self?.checkThreeDSecureStatus()
122
+ // }
123
+ // }
124
+ //
125
+ // @IBAction func actionBtnDone(_ sender: UIButton) {
126
+ // // ✅ Combine 3DS status into additionalInfo
127
+ // var combinedAdditionalInfo = additionalInfo ?? [:]
128
+ // if let threeDS = threeDSecureStatusResponse {
129
+ // combinedAdditionalInfo["threeDSecureStatus"] = threeDS
130
+ // }
131
+ //
132
+ // let result = Result(type: .success,
133
+ // chargeData: chargeData,
134
+ // billingInfo: billingInfo,
135
+ // additionalInfo: combinedAdditionalInfo)
136
+ //
137
+ // easyPayDelegate?.easyPayController(self.navigationController as! EasyPayViewController, didFinishWith: result)
138
+ //
139
+ // if let easyPayVC = self.navigationController as? EasyPayViewController {
140
+ // easyPayVC.dismiss(animated: true, completion: {
141
+ // if let delegate = easyPayVC.easyPayDelegate {
142
+ // UserStoreSingleton.shared.isLoggedIn = false
143
+ // delegate.easyPayController(easyPayVC, didFinishWith: result)
144
+ // }
145
+ // })
146
+ // }
147
+ // }
148
+ //
149
+ // func uiFinishingTouchElements() {
150
+ // // Set background color for the main view
151
+ // if let containerBGcolor = UserStoreSingleton.shared.container_bg_col,
152
+ // let uiColor = UIColor(hex: containerBGcolor) {
153
+ // self.view.backgroundColor = uiColor
154
+ // self.viewMain.backgroundColor = uiColor
155
+ // }
156
+ //
157
+ // if let primaryBtnBackGroundColor = UserStoreSingleton.shared.primary_btn_bg_col,
158
+ // let uiColor = UIColor(hex: primaryBtnBackGroundColor) {
159
+ // btnDone.backgroundColor = uiColor
160
+ // }
161
+ //
162
+ // if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_font_col,
163
+ // let secondaryUIColor = UIColor(hex: primaryBtnFontColor) {
164
+ // btnDone.setTitleColor(secondaryUIColor, for: .normal)
165
+ // }
166
+ //
167
+ // if let borderRadiusString = UserStoreSingleton.shared.border_radious,
168
+ // let borderRadius = Double(borderRadiusString) { // Convert String to Double
169
+ // btnDone.layer.cornerRadius = CGFloat(borderRadius) // Set corner radius
170
+ // } else {
171
+ // btnDone.layer.cornerRadius = 8 // Default value
172
+ // }
173
+ // btnDone.layer.masksToBounds = true // Ensure the corners are clipped properly
174
+ //
175
+ // if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
176
+ // let uiColor = UIColor(hex: primaryFontColor) {
177
+ // lblCompleteAuthentication.textColor = uiColor
178
+ // lblBillingInfoData.textColor = uiColor
179
+ // }
180
+ //
181
+ // if let fontSizeString = UserStoreSingleton.shared.fontSize,
182
+ // let fontSizeDouble = Double(fontSizeString) { // Convert String to Double
183
+ // let fontSize = CGFloat(fontSizeDouble) // Convert Double to CGFloat
184
+ // lblCompleteAuthentication.font = UIFont.systemFont(ofSize: fontSize, weight: .semibold)
185
+ // lblPaymentLink.font = UIFont.systemFont(ofSize: fontSize, weight: .medium)
186
+ // lblBillingInfoData.font = UIFont.systemFont(ofSize: fontSize, weight: .medium)
187
+ // }
188
+ //
189
+ // }
190
+ //
191
+ // // MARK: - GET Transaction Status API
192
+ // private func checkThreeDSecureStatus() {
193
+ // guard let secureToken = chargeData["secure_token"] as? String else {
194
+ // print("Secure token not found in chargeData")
195
+ // return
196
+ // }
197
+ //
198
+ // let urlString = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecureStatus(secureToken).path()
199
+ // print("Final 3DS status URL: \(urlString)")
200
+ //
201
+ // guard let url = URL(string: urlString) else {
202
+ // print("Invalid URL")
203
+ // return
204
+ // }
205
+ //
206
+ // var uRLRequest = URLRequest(url: url)
207
+ // uRLRequest.httpMethod = "GET"
208
+ // uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
209
+ //
210
+ // let token = UserStoreSingleton.shared.customerToken
211
+ // print("Setting customerToken header: \(token ?? "None")")
212
+ // uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
213
+ //
214
+ // // Add API key/secret headers
215
+ // if let apiKey = EnvironmentConfig.apiKey,
216
+ // let apiSecret = EnvironmentConfig.apiSecret {
217
+ // uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
218
+ // uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
219
+ // }
220
+ //
221
+ // let task = URLSession.shared.dataTask(with: uRLRequest) { [weak self] data, response, error in
222
+ // guard let self = self else { return }
223
+ //
224
+ // if let error = error {
225
+ // print("3DS status check error:", error.localizedDescription)
226
+ // return
227
+ // }
228
+ //
229
+ // guard let data = data else {
230
+ // print("No data in response")
231
+ // return
232
+ // }
233
+ //
234
+ // do {
235
+ // if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
236
+ // print("3DS status response:", json)
237
+ // self.threeDSecureStatusResponse = json
238
+ // }
239
+ // } catch {
240
+ // print("Error parsing JSON:", error.localizedDescription)
241
+ // }
242
+ // }
243
+ //
244
+ // task.resume()
245
+ // }
246
+ //
247
+ //}
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
8
256
  import UIKit
9
257
 
10
- class ThreeDSecurePaymentDoneVC: UIViewController {
258
+ class ThreeDSecurePaymentDoneVC: BaseVC {
11
259
 
260
+ @IBOutlet weak var imgViewPaymentDone: UIImageView!
261
+ @IBOutlet weak var imgViewLoading: UIImageView!
12
262
  @IBOutlet weak var viewMain: UIView!
13
263
  @IBOutlet weak var lblCompleteAuthentication: UILabel!
14
264
  @IBOutlet weak var btnDone: UIButton!
15
265
  @IBOutlet weak var lblPaymentLink: UILabel!
16
266
  @IBOutlet weak var lblBillingInfoData: UILabel!
17
267
 
268
+ @IBOutlet weak var viewPaymentDetails: UIStackView!
269
+ @IBOutlet weak var lblDateHeading: UILabel!
270
+ @IBOutlet weak var lblTransactionHeading: UILabel!
271
+ @IBOutlet weak var lblSubscriptionHeading: UILabel!
272
+ @IBOutlet weak var lblChargeIdHeading: UILabel!
273
+ @IBOutlet weak var lblReferenceIdHeading: UILabel!
274
+ @IBOutlet weak var lblPaymentMethodHeading: UILabel!
275
+ @IBOutlet weak var lblTotalHeading: UILabel!
276
+ @IBOutlet weak var lblStatusHeading: UILabel!
277
+
278
+ @IBOutlet weak var lblDate: UILabel!
279
+ @IBOutlet weak var lblTransactionId: UILabel!
280
+ @IBOutlet weak var lblSubscriptionId: UILabel!
281
+ @IBOutlet weak var lblChargeId: UILabel!
282
+ @IBOutlet weak var lblTotal: UILabel!
283
+ @IBOutlet weak var lblRefferenceId: UILabel!
284
+ @IBOutlet weak var lblPaymentMethodValue: UILabel!
285
+ @IBOutlet weak var lblStatus: UILabel!
286
+
18
287
  var easyPayDelegate: EasyPayViewControllerDelegate?
19
288
  var redirectURL: String?
20
289
  var chargeData: [String: Any] = [:]
21
290
 
22
- // Add these two properties:
23
291
  var billingInfo: [String: Any]?
24
292
  var additionalInfo: [String: Any]?
25
293
 
294
+ var threeDSecureStatusResponse: [String: Any]?
295
+
296
+ // Rename for clarity, this will be for the periodic API check
297
+ var apiStatusCheckTimer: Timer?
298
+ // New property for the 1-minute countdown
299
+ var oneMinuteCountdownTimer: DispatchSourceTimer?
300
+ var countdownRemaining: Int = 60 // 60 seconds
301
+
302
+ var additionalInfoData: [FieldItem]?
303
+ var billingInfoData: [FieldItem]?
304
+ var visibility: FieldsVisibility?
305
+
306
+ // Dispatch group to wait for initial API call
307
+ let initialAPIGroup = DispatchGroup()
308
+
309
+ var amount: Int?
310
+
26
311
  override func viewDidLoad() {
27
312
  super.viewDidLoad()
28
313
  uiFinishingTouchElements()
29
314
 
30
315
  setupTapOnLabel()
31
316
 
32
- showBillingInfo()
317
+ // Initially hide done image and show loading image
318
+ imgViewPaymentDone.isHidden = true
319
+ imgViewLoading.isHidden = false
320
+ lblCompleteAuthentication.text = "Completing Authentication..." // Initial text
321
+ btnDone.isHidden = true
322
+
323
+ startRotatingImage()
324
+ startOneMinuteCountdownAndAPICheck()
325
+
326
+ NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil)
327
+ }
328
+
329
+ deinit {
330
+ NotificationCenter.default.removeObserver(self)
331
+ }
332
+
333
+ @objc private func appDidBecomeActive() {
334
+ if !imgViewLoading.isHidden && imgViewLoading.layer.animation(forKey: "rotationAnimation") == nil {
335
+ startRotatingImage()
336
+ }
33
337
  }
34
338
 
35
339
  override func viewWillAppear(_ animated: Bool) {
340
+ super.viewWillAppear(animated)
36
341
  uiFinishingTouchElements()
342
+
343
+ // Restart rotating animation if missing
344
+ if !imgViewLoading.isHidden && imgViewLoading.layer.animation(forKey: "rotationAnimation") == nil {
345
+ startRotatingImage()
346
+ }
347
+
348
+ if let chargeId = (threeDSecureStatusResponse?["data"] as? [String: Any])?["charge_id"] as? String,
349
+ !chargeId.isEmpty {
350
+ // Charge already found - do nothing
351
+ return
352
+ }
353
+
354
+ if countdownRemaining > 0 {
355
+ imgViewLoading.isHidden = false
356
+ imgViewPaymentDone.isHidden = true
357
+ lblCompleteAuthentication.text = "Please wait while Authentication is in process..."
358
+ btnDone.isHidden = true
359
+
360
+ if apiStatusCheckTimer == nil {
361
+ apiStatusCheckTimer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { [weak self] _ in
362
+ guard let self = self else { return }
363
+ if self.countdownRemaining > 0 {
364
+ self.checkThreeDSecureStatus(completion: nil)
365
+ }
366
+ }
367
+ }
368
+
369
+ } else {
370
+ imgViewLoading.layer.removeAllAnimations()
371
+ imgViewLoading.isHidden = true
372
+ imgViewPaymentDone.isHidden = false
373
+ imgViewPaymentDone.image = UIImage(systemName: "exclamationmark.circle.fill")
374
+ imgViewPaymentDone.tintColor = .systemRed
375
+ lblCompleteAuthentication.text = "Please complete 3DS Authentication.!!"
376
+ btnDone.isHidden = false
377
+ lblPaymentLink.isHidden = false
378
+ viewPaymentDetails.isHidden = false
379
+
380
+ apiStatusCheckTimer?.invalidate()
381
+ apiStatusCheckTimer = nil
382
+ }
383
+ }
384
+
385
+ override func viewWillDisappear(_ animated: Bool) {
386
+ super.viewWillDisappear(animated)
387
+ apiStatusCheckTimer?.invalidate()
388
+ apiStatusCheckTimer = nil
389
+ oneMinuteCountdownTimer?.cancel() // Cancel the dispatch source timer
390
+ oneMinuteCountdownTimer = nil
391
+ }
392
+
393
+ func startRotatingImage() {
394
+ let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
395
+ rotationAnimation.toValue = CGFloat.pi * 2 // 360 degrees in radians
396
+ rotationAnimation.duration = 1 // Adjust the duration as needed
397
+ rotationAnimation.isCumulative = true
398
+ rotationAnimation.repeatCount = .infinity // Continuous rotation
399
+ imgViewLoading.layer.add(rotationAnimation, forKey: "rotationAnimation")
37
400
  }
38
401
 
39
402
  private func setupTapOnLabel() {
@@ -55,37 +418,96 @@ class ThreeDSecurePaymentDoneVC: UIViewController {
55
418
  }
56
419
  UIApplication.shared.open(url, options: [:], completionHandler: nil)
57
420
  }
58
-
59
- func showBillingInfo() {
60
- var displayText = ""
61
421
 
62
- if let billing = billingInfo {
63
- displayText += "Billing Info:\n"
64
- for (key, value) in billing {
65
- displayText += "\(key.capitalized): \(value)\n"
66
- }
67
-
68
- if let additional = additionalInfo {
69
- displayText += "\nAdditional Info:\n"
70
- for (key, value) in additional {
71
- displayText += "\(key.capitalized): \(value)\n"
422
+ private func startOneMinuteCountdownAndAPICheck() {
423
+ startRotatingImage()
424
+ imgViewPaymentDone.isHidden = true
425
+ imgViewLoading.isHidden = false
426
+ lblCompleteAuthentication.text = "Please wait while Authentication is in process..."
427
+ btnDone.isHidden = true
428
+
429
+ apiStatusCheckTimer?.invalidate()
430
+ apiStatusCheckTimer = nil
431
+
432
+ initialAPIGroup.enter()
433
+ checkThreeDSecureStatus { [weak self] in
434
+ self?.initialAPIGroup.leave()
435
+ }
436
+
437
+ countdownRemaining = 60
438
+
439
+ oneMinuteCountdownTimer = DispatchSource.makeTimerSource(queue: .main)
440
+ oneMinuteCountdownTimer?.schedule(deadline: .now(), repeating: .seconds(1))
441
+ oneMinuteCountdownTimer?.setEventHandler { [weak self] in
442
+ guard let self = self else { return }
443
+
444
+ self.countdownRemaining -= 1
445
+ if self.countdownRemaining <= 0 {
446
+ self.oneMinuteCountdownTimer?.cancel()
447
+ self.oneMinuteCountdownTimer = nil
448
+
449
+ DispatchQueue.main.async {
450
+ self.imgViewLoading.layer.removeAllAnimations()
451
+ self.imgViewLoading.isHidden = true
452
+ self.imgViewPaymentDone.isHidden = false
453
+ self.lblCompleteAuthentication.text = "Please complete 3DS Authentication.!!"
454
+ self.btnDone.isHidden = false
455
+ self.lblPaymentLink.isHidden = false
456
+ self.viewPaymentDetails.isHidden = false
457
+
458
+ if let json = self.threeDSecureStatusResponse,
459
+ let data = json["data"] as? [String: Any],
460
+ let chargeId = data["charge_id"],
461
+ chargeId is NSNull || (chargeId as? String ?? "").isEmpty {
462
+ self.imgViewPaymentDone.image = UIImage(systemName: "exclamationmark.circle.fill")
463
+ self.imgViewPaymentDone.tintColor = .systemRed
464
+
465
+ let refToken = data["ref_token"] as? String ?? "N/A"
466
+ let chargeId = data["charge_id"] as? String ?? "N/A"
467
+ let transactionId = data["transaction_id"] as? String ?? "N/A"
468
+ let subscriptionId = data["subscription_id"] as? String ?? "N/A"
469
+ let createdAt = data["created_at"] as? String ?? ""
470
+ let status = data["status"] as? String ?? ""
471
+
472
+ let formattedDate: String
473
+ if let date = self.convertToDate(dateString: createdAt) {
474
+ formattedDate = self.formatDate(date: date)
475
+ } else {
476
+ formattedDate = "N/A"
477
+ }
478
+
479
+ self.lblChargeId.text = "\(chargeId)"
480
+ self.lblTransactionId.text = "\(transactionId)"
481
+ self.lblSubscriptionId.text = "\(subscriptionId)"
482
+ self.lblDate.text = "\(formattedDate)"
483
+ self.lblTotal.text = "$\(self.amount ?? 0)"
484
+ self.lblRefferenceId.text = "\(refToken)"
485
+ self.lblStatus.text = "\(status)"
486
+ }
72
487
  }
73
488
  }
74
-
75
- // Set text and make label visible
76
- lblBillingInfoData.text = displayText.trimmingCharacters(in: .whitespacesAndNewlines)
77
- lblBillingInfoData.isHidden = false
78
- } else {
79
- // Hide label if billing info is not available
80
- lblBillingInfoData.isHidden = true
489
+ }
490
+ oneMinuteCountdownTimer?.resume()
491
+
492
+ // API check timer: runs continuously every 5 seconds until success/failure
493
+ apiStatusCheckTimer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in
494
+ guard let self = self else { return }
495
+ self.checkThreeDSecureStatus(completion: nil)
81
496
  }
82
497
  }
498
+
83
499
 
84
500
  @IBAction func actionBtnDone(_ sender: UIButton) {
501
+ // ✅ Combine 3DS status into additionalInfo
502
+ var combinedAdditionalInfo = additionalInfo ?? [:]
503
+ if let threeDS = threeDSecureStatusResponse {
504
+ combinedAdditionalInfo["threeDSecureStatus"] = threeDS
505
+ }
506
+
85
507
  let result = Result(type: .success,
86
508
  chargeData: chargeData,
87
509
  billingInfo: billingInfo,
88
- additionalInfo: additionalInfo)
510
+ additionalInfo: combinedAdditionalInfo)
89
511
 
90
512
  easyPayDelegate?.easyPayController(self.navigationController as! EasyPayViewController, didFinishWith: result)
91
513
 
@@ -105,11 +527,14 @@ class ThreeDSecurePaymentDoneVC: UIViewController {
105
527
  let uiColor = UIColor(hex: containerBGcolor) {
106
528
  self.view.backgroundColor = uiColor
107
529
  self.viewMain.backgroundColor = uiColor
530
+ // self.viewOverlay.backgroundColor = uiColor.withAlphaComponent(0.3)
108
531
  }
109
532
 
110
533
  if let primaryBtnBackGroundColor = UserStoreSingleton.shared.primary_btn_bg_col,
111
534
  let uiColor = UIColor(hex: primaryBtnBackGroundColor) {
112
535
  btnDone.backgroundColor = uiColor
536
+ imgViewLoading.image = UIImage(systemName: "arrow.2.circlepath")?.withRenderingMode(.alwaysTemplate)
537
+ imgViewLoading.tintColor = uiColor
113
538
  }
114
539
 
115
540
  if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_font_col,
@@ -117,6 +542,26 @@ class ThreeDSecurePaymentDoneVC: UIViewController {
117
542
  btnDone.setTitleColor(secondaryUIColor, for: .normal)
118
543
  }
119
544
 
545
+ if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
546
+ let uiColor = UIColor(hex: primaryFontColor) {
547
+ lblDateHeading.textColor = uiColor
548
+ lblTransactionHeading.textColor = uiColor
549
+ lblSubscriptionHeading.textColor = uiColor
550
+ lblChargeIdHeading.textColor = uiColor
551
+ lblReferenceIdHeading.textColor = uiColor
552
+ lblPaymentMethodHeading.textColor = uiColor
553
+ lblTotalHeading.textColor = uiColor
554
+ lblDate.textColor = uiColor
555
+ lblTransactionId.textColor = uiColor
556
+ lblSubscriptionId.textColor = uiColor
557
+ lblChargeId.textColor = uiColor
558
+ lblTotal.textColor = uiColor
559
+ lblRefferenceId.textColor = uiColor
560
+ lblPaymentMethodValue.textColor = uiColor
561
+ lblStatusHeading.textColor = uiColor
562
+ lblStatus.textColor = uiColor
563
+ }
564
+
120
565
  if let borderRadiusString = UserStoreSingleton.shared.border_radious,
121
566
  let borderRadius = Double(borderRadiusString) { // Convert String to Double
122
567
  btnDone.layer.cornerRadius = CGFloat(borderRadius) // Set corner radius
@@ -141,4 +586,142 @@ class ThreeDSecurePaymentDoneVC: UIViewController {
141
586
 
142
587
  }
143
588
 
589
+ private func convertToDate(dateString: String) -> Date? {
590
+ let formatter = DateFormatter()
591
+ formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
592
+ formatter.timeZone = TimeZone(abbreviation: "UTC")
593
+ return formatter.date(from: dateString)
594
+ }
595
+
596
+ private func formatDate(date: Date) -> String {
597
+ let formatter = DateFormatter()
598
+ formatter.dateFormat = "dd/MM/yyyy, HH:mm:ss"
599
+ formatter.timeZone = TimeZone.current
600
+ return formatter.string(from: date)
601
+ }
602
+
603
+ // MARK: - GET Transaction Status API
604
+ private func checkThreeDSecureStatus(completion: (() -> Void)?) {
605
+ guard let secureToken = chargeData["secure_token"] as? String else {
606
+ print("Secure token not found in chargeData")
607
+ completion?()
608
+ return
609
+ }
610
+
611
+ let urlString = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecureStatus(secureToken).path()
612
+ print("Final 3DS status URL: \(urlString)")
613
+
614
+ guard let url = URL(string: urlString) else {
615
+ print("Invalid URL")
616
+ completion?()
617
+ return
618
+ }
619
+
620
+ var uRLRequest = URLRequest(url: url)
621
+ uRLRequest.httpMethod = "GET"
622
+ uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
623
+
624
+ if let token = UserStoreSingleton.shared.customerToken {
625
+ uRLRequest.addValue(token, forHTTPHeaderField: "Customer-Token")
626
+ }
627
+
628
+ if let apiKey = EnvironmentConfig.apiKey,
629
+ let apiSecret = EnvironmentConfig.apiSecret {
630
+ uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
631
+ uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
632
+ }
633
+
634
+ let task = URLSession.shared.dataTask(with: uRLRequest) { [weak self] data, response, error in
635
+ defer { completion?() }
636
+
637
+ guard let self = self else { return }
638
+
639
+ if let error = error {
640
+ print("3DS status check error:", error.localizedDescription)
641
+ return
642
+ }
643
+
644
+ guard let data = data else {
645
+ print("No data in response")
646
+ return
647
+ }
648
+
649
+ do {
650
+ if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
651
+ print("3DS status response:", json)
652
+ self.threeDSecureStatusResponse = json
653
+
654
+ if let dataDict = json["data"] as? [String: Any] {
655
+ DispatchQueue.main.async {
656
+ let chargeId = dataDict["charge_id"] as? String ?? "N/A"
657
+ let transactionId = dataDict["transaction_id"] as? String ?? "N/A"
658
+ let subscriptionId = dataDict["subscription_id"] as? String ?? "N/A"
659
+ let createdAt = dataDict["created_at"] as? String ?? ""
660
+ let referenceId = dataDict["ref_token"] as? String ?? "N/A"
661
+ let status = dataDict["status"] as? String ?? "N/A"
662
+
663
+ let formattedDate: String
664
+ if let date = self.convertToDate(dateString: createdAt) {
665
+ formattedDate = self.formatDate(date: date)
666
+ } else {
667
+ formattedDate = "N/A"
668
+ }
669
+
670
+ self.lblChargeId.text = chargeId
671
+ self.lblTransactionId.text = transactionId
672
+ self.lblSubscriptionId.text = subscriptionId
673
+ self.lblDate.text = formattedDate
674
+ self.lblTotal.text = "$\(self.amount ?? 0)"
675
+ self.lblRefferenceId.text = referenceId
676
+ self.lblStatus.text = status
677
+
678
+ if status.lowercased() == "failed" {
679
+ // Handle failed status
680
+ self.imgViewPaymentDone.image = UIImage(systemName: "xmark.circle.fill")
681
+ self.imgViewPaymentDone.tintColor = .systemRed
682
+ self.apiStatusCheckTimer?.invalidate()
683
+ self.apiStatusCheckTimer = nil
684
+ self.oneMinuteCountdownTimer?.cancel()
685
+ self.oneMinuteCountdownTimer = nil
686
+
687
+ self.imgViewLoading.layer.removeAllAnimations()
688
+ self.imgViewLoading.isHidden = true
689
+ self.imgViewPaymentDone.isHidden = false
690
+ self.lblCompleteAuthentication.text = "Payment Failed"
691
+ self.btnDone.isHidden = false
692
+ self.lblPaymentLink.isHidden = false
693
+ self.viewPaymentDetails.isHidden = false
694
+
695
+ } else if !(chargeId.isEmpty || chargeId == "N/A") {
696
+ // Success case
697
+ self.imgViewPaymentDone.image = UIImage(named: "payment_done_icon")
698
+ self.apiStatusCheckTimer?.invalidate()
699
+ self.apiStatusCheckTimer = nil
700
+ self.oneMinuteCountdownTimer?.cancel()
701
+ self.oneMinuteCountdownTimer = nil
702
+
703
+ self.imgViewLoading.layer.removeAllAnimations()
704
+ self.imgViewLoading.isHidden = true
705
+ self.imgViewPaymentDone.isHidden = false
706
+ self.lblCompleteAuthentication.text = "Authentication Completed"
707
+ self.btnDone.isHidden = false
708
+ self.lblPaymentLink.isHidden = false
709
+ self.viewPaymentDetails.isHidden = false
710
+
711
+ } else {
712
+ // Still waiting
713
+ self.imgViewPaymentDone.image = UIImage(systemName: "exclamationmark.circle.fill")
714
+ self.imgViewPaymentDone.tintColor = .systemRed
715
+ }
716
+ }
717
+ }
718
+ }
719
+ } catch {
720
+ print("Error parsing JSON:", error.localizedDescription)
721
+ }
722
+ }
723
+ task.resume()
724
+ }
725
+
144
726
  }
727
+