@jimrising/easymerchantsdk-react-native 1.2.0 → 1.2.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 (26) hide show
  1. package/.idea/caches/deviceStreaming.xml +209 -0
  2. package/.idea/workspace.xml +193 -0
  3. package/README.md +1 -1
  4. package/ios/Classes/EasyMerchantSdk.m +4 -4
  5. package/ios/Pods/UserDefaults/UserStoreSingleton.swift +233 -0
  6. package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +1137 -0
  7. package/ios/Pods/ViewControllers/BaseVC.swift +126 -0
  8. package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +803 -0
  9. package/ios/Pods/ViewControllers/BillingInfoVC/Cells/CityListTVC.swift +46 -0
  10. package/ios/Pods/ViewControllers/BillingInfoVC/Cells/CountryListTVC.swift +47 -0
  11. package/ios/Pods/ViewControllers/BillingInfoVC/Cells/StateListTVC.swift +46 -0
  12. package/ios/Pods/ViewControllers/CountryListVC.swift +404 -0
  13. package/ios/Pods/ViewControllers/EmailVerificationVC.swift +239 -0
  14. package/ios/Pods/ViewControllers/OTPVerificationVC.swift +1134 -0
  15. package/ios/Pods/ViewControllers/PaymentDoneVC.swift +159 -0
  16. package/ios/Pods/ViewControllers/PaymentErrorVC.swift +90 -0
  17. package/ios/Pods/ViewControllers/PaymentInformation/AccountTypeTVC.swift +41 -0
  18. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +5160 -0
  19. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInformationCVC.swift +35 -0
  20. package/ios/Pods/ViewControllers/PaymentInformation/SavedAccountsTVC/SavedAccountTVC.swift +77 -0
  21. package/ios/Pods/ViewControllers/PaymentInformation/SavedAccountsTVC/SavedAccountTVC.xib +163 -0
  22. package/ios/Pods/ViewControllers/PaymentInformation/SavedCardsTVC/SavedCardsTVC.swift +76 -0
  23. package/ios/Pods/ViewControllers/PaymentInformation/SavedCardsTVC/SavedCardsTVC.xib +184 -0
  24. package/ios/Pods/ViewControllers/TermAndConditionsVC.swift +63 -0
  25. package/ios/easymerchantsdk.podspec +1 -1
  26. package/package.json +1 -1
@@ -0,0 +1,1134 @@
1
+ //
2
+ // OTPVerificationVC.swift
3
+ // EasyPay
4
+ //
5
+ // Created by Mony's Mac on 14/08/24.
6
+ //
7
+
8
+ import UIKit
9
+
10
+ class OTPVerificationVC: BaseVC {
11
+
12
+ @IBOutlet weak var viewTextOTP1: UIView!
13
+ @IBOutlet weak var txtFieldOTPText1: UITextField!
14
+ @IBOutlet weak var viewTextOTP2: UIView!
15
+ @IBOutlet weak var txtFieldOTPText2: UITextField!
16
+ @IBOutlet weak var viewTextOTP3: UIView!
17
+ @IBOutlet weak var txtFieldOTPText3: UITextField!
18
+ @IBOutlet weak var viewTextOTP4: UIView!
19
+ @IBOutlet weak var txtFieldOTPText4: UITextField!
20
+ @IBOutlet weak var viewTextOTP5: UIView!
21
+ @IBOutlet weak var txtFieldOTPText5: UITextField!
22
+ @IBOutlet weak var viewTextOTP6: UIView!
23
+ @IBOutlet weak var txtFieldOTPText6: UITextField!
24
+ @IBOutlet weak var imgEsclamationMark: UIImageView!
25
+ @IBOutlet weak var lblOtpTimer: UILabel!
26
+ @IBOutlet weak var lblUntillResendOtp: UILabel!
27
+ @IBOutlet weak var btnResendOTP: UIButton!
28
+
29
+ @IBOutlet weak var btnConfirmCode: UIButton!
30
+ @IBOutlet weak var lblUsedSavedInfo: UILabel!
31
+ @IBOutlet weak var lblEnterOTP: UILabel!
32
+
33
+ var cardNumber: String?
34
+ var expiryDate: String?
35
+ var cvv: String?
36
+ var nameOnCard: String?
37
+ var billingInfoData: [String: Any]?
38
+
39
+ var email: String?
40
+
41
+ var selectedPaymentMethod: String?
42
+
43
+ //Banking Params
44
+ var accountName: String?
45
+ var routingNumber: String?
46
+ var accountType: String?
47
+ var accountNumber: String?
48
+
49
+ var easyPayDelegate: EasyPayViewControllerDelegate?
50
+
51
+ var easyPayVC: EasyPayViewController?
52
+
53
+ var isSavedForFuture: Bool = false
54
+
55
+ private var timeRemaining = 180 // 3 minutes
56
+ private var timer: Timer?
57
+
58
+ override func viewDidLoad() {
59
+ super.viewDidLoad()
60
+
61
+ uiFinishingTouchElements()
62
+
63
+ // Add tap gesture recognizer to dismiss the keyboard
64
+ let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
65
+ view.addGestureRecognizer(tapGesture)
66
+
67
+ setupTextFields()
68
+
69
+ btnResendOTP.isHidden = true
70
+ }
71
+
72
+ override func viewWillAppear(_ animated: Bool) {
73
+ startTimer()
74
+ uiFinishingTouchElements()
75
+ }
76
+
77
+ //MARK: - Ui Colors Setup.
78
+ func uiFinishingTouchElements() {
79
+ if let containerBGcolor = UserStoreSingleton.shared.container_bg_col,
80
+ let uiColor = UIColor(hex: containerBGcolor) {
81
+ self.view.backgroundColor = uiColor
82
+ }
83
+
84
+ if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
85
+ let uiColor = UIColor(hex: primaryFontColor) {
86
+ lblUsedSavedInfo.textColor = uiColor
87
+ txtFieldOTPText1.textColor = uiColor
88
+ txtFieldOTPText2.textColor = uiColor
89
+ txtFieldOTPText3.textColor = uiColor
90
+ txtFieldOTPText4.textColor = uiColor
91
+ txtFieldOTPText5.textColor = uiColor
92
+ txtFieldOTPText6.textColor = uiColor
93
+ }
94
+
95
+ if let primaryFontColor = UserStoreSingleton.shared.secondary_font_col,
96
+ let uiColor = UIColor(hex: primaryFontColor) {
97
+ lblEnterOTP.textColor = uiColor
98
+ lblUntillResendOtp.textColor = uiColor
99
+ }
100
+
101
+ if let primaryFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
102
+ let uiColor = UIColor(hex: primaryFontColor) {
103
+ btnConfirmCode.backgroundColor = uiColor
104
+ btnResendOTP.tintColor = uiColor
105
+ txtFieldOTPText1.tintColor = uiColor
106
+ txtFieldOTPText2.tintColor = uiColor
107
+ txtFieldOTPText3.tintColor = uiColor
108
+ txtFieldOTPText4.tintColor = uiColor
109
+ txtFieldOTPText5.tintColor = uiColor
110
+ txtFieldOTPText6.tintColor = uiColor
111
+ }
112
+
113
+ if let secondaryBtnBackgroundColor = UserStoreSingleton.shared.primary_btn_font_col,
114
+ let secondaryUIColor = UIColor(hex: secondaryBtnBackgroundColor) {
115
+ btnConfirmCode.setTitleColor(secondaryUIColor, for: .normal)
116
+ }
117
+
118
+ if let borderRadiusString = UserStoreSingleton.shared.border_radious,
119
+ let borderRadius = Double(borderRadiusString) { // Convert String to Double
120
+ btnConfirmCode.layer.cornerRadius = CGFloat(borderRadius) // Set corner radius
121
+ viewTextOTP1.layer.cornerRadius = CGFloat(borderRadius)
122
+ viewTextOTP2.layer.cornerRadius = CGFloat(borderRadius)
123
+ viewTextOTP3.layer.cornerRadius = CGFloat(borderRadius)
124
+ viewTextOTP4.layer.cornerRadius = CGFloat(borderRadius)
125
+ viewTextOTP5.layer.cornerRadius = CGFloat(borderRadius)
126
+ viewTextOTP6.layer.cornerRadius = CGFloat(borderRadius)
127
+ } else {
128
+ btnConfirmCode.layer.cornerRadius = 8 // Default value
129
+ }
130
+ btnConfirmCode.layer.masksToBounds = true // Ensure the corners are clipped properly
131
+
132
+ if let viewOtpPrimaryBackgroundColor = UserStoreSingleton.shared.primary_btn_bg_col,
133
+ let primaryUIColor = UIColor(hex: viewOtpPrimaryBackgroundColor),
134
+ let viewOtpSecondaryColor = UserStoreSingleton.shared.secondary_font_col,
135
+ let secondaryUIColor = UIColor(hex: viewOtpSecondaryColor) {
136
+
137
+ viewTextOTP1.layer.borderWidth = 1.0
138
+ viewTextOTP1.layer.borderColor = txtFieldOTPText1.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
139
+ viewTextOTP2.layer.borderWidth = 1.0
140
+ viewTextOTP2.layer.borderColor = txtFieldOTPText2.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
141
+ viewTextOTP3.layer.borderWidth = 1.0
142
+ viewTextOTP3.layer.borderColor = txtFieldOTPText3.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
143
+ viewTextOTP4.layer.borderWidth = 1.0
144
+ viewTextOTP4.layer.borderColor = txtFieldOTPText4.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
145
+ viewTextOTP5.layer.borderWidth = 1.0
146
+ viewTextOTP5.layer.borderColor = txtFieldOTPText5.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
147
+ viewTextOTP6.layer.borderWidth = 1.0
148
+ viewTextOTP6.layer.borderColor = txtFieldOTPText6.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
149
+ }
150
+
151
+ if let fontSizeString = UserStoreSingleton.shared.fontSize,
152
+ let fontSizeDouble = Double(fontSizeString) { // Convert String to Double
153
+ let fontSize = CGFloat(fontSizeDouble) // Convert Double to CGFloat
154
+ lblEnterOTP.font = UIFont.systemFont(ofSize: fontSize)
155
+ lblOtpTimer.font = UIFont.systemFont(ofSize: fontSize)
156
+ lblUntillResendOtp.font = UIFont.systemFont(ofSize: fontSize)
157
+ btnResendOTP.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
158
+ btnConfirmCode.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
159
+
160
+ txtFieldOTPText1.font = UIFont.systemFont(ofSize: fontSize)
161
+ txtFieldOTPText2.font = UIFont.systemFont(ofSize: fontSize)
162
+ txtFieldOTPText3.font = UIFont.systemFont(ofSize: fontSize)
163
+ txtFieldOTPText4.font = UIFont.systemFont(ofSize: fontSize)
164
+ txtFieldOTPText5.font = UIFont.systemFont(ofSize: fontSize)
165
+ txtFieldOTPText6.font = UIFont.systemFont(ofSize: fontSize)
166
+ }
167
+ }
168
+
169
+ // Method to dismiss the keyboard
170
+ @objc func dismissKeyboard() {
171
+ view.endEditing(true)
172
+ }
173
+
174
+ private func startTimer() {
175
+ timeRemaining = 180 // 3 minutes
176
+ lblOtpTimer.text = formatTime(timeRemaining)
177
+ timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
178
+ }
179
+
180
+ private func stopTimer() {
181
+ timer?.invalidate()
182
+ timer = nil
183
+ }
184
+
185
+ @objc private func updateTimer() {
186
+ if timeRemaining > 0 {
187
+ timeRemaining -= 1
188
+ lblOtpTimer.text = formatTime(timeRemaining)
189
+ } else {
190
+ stopTimer()
191
+ btnResendOTP.isHidden = false
192
+ imgEsclamationMark.isHidden = true
193
+ lblOtpTimer.isHidden = true
194
+ lblUntillResendOtp.isHidden = true
195
+ }
196
+ }
197
+
198
+ private func formatTime(_ seconds: Int) -> String {
199
+ let minutes = seconds / 60
200
+ let seconds = seconds % 60
201
+ return String(format: "%02d:%02d", minutes, seconds)
202
+ }
203
+
204
+ private func setupTextFields() {
205
+ let textFields = [txtFieldOTPText1, txtFieldOTPText2, txtFieldOTPText3, txtFieldOTPText4, txtFieldOTPText5, txtFieldOTPText6]
206
+
207
+ for textField in textFields {
208
+ textField?.delegate = self
209
+ textField?.textContentType = .oneTimeCode
210
+ textField?.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
211
+ }
212
+ }
213
+
214
+ //MARK: - OTP Verification Api
215
+ func otpVerificationApi() {
216
+ showLoadingIndicator()
217
+
218
+ // var components = URLComponents()
219
+ // components.scheme = "https"
220
+ // components.host = "stage-api.stage-easymerchant.io"
221
+ // components.path = "/api/v1/customer/verify_otp"
222
+ //
223
+ // guard let serviceURL = components.url else {
224
+ // print("Invalid URL")
225
+ // hideLoadingIndicator()
226
+ // return
227
+ // }
228
+
229
+ // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
230
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.verifyOtp.path()
231
+
232
+ guard let serviceURL = URL(string: fullURL) else {
233
+ print("Invalid URL")
234
+ hideLoadingIndicator()
235
+ return
236
+ }
237
+
238
+ var request = URLRequest(url: serviceURL)
239
+ request.httpMethod = "POST"
240
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
241
+
242
+ let token = UserStoreSingleton.shared.clientToken
243
+ print("Setting clientToken header: \(token ?? "None")")
244
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
245
+
246
+ let params: [String: Any] = [
247
+ "card_search_value": email ?? "",
248
+ "card_search_key": "email",
249
+ "otp": getCombinedOTP()
250
+ ]
251
+
252
+ do {
253
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
254
+ request.httpBody = jsonData
255
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
256
+ print("JSON Payload: \(jsonString)")
257
+ }
258
+ } catch let error {
259
+ print("Error creating JSON data: \(error)")
260
+ hideLoadingIndicator()
261
+ return
262
+ }
263
+
264
+ let session = URLSession.shared
265
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
266
+
267
+ DispatchQueue.main.async {
268
+ self.hideLoadingIndicator() // Stop loader when response is received
269
+ }
270
+
271
+ if let error = error {
272
+ print("Error: \(error.localizedDescription)")
273
+ return
274
+ }
275
+
276
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
277
+ print("Invalid response")
278
+ return
279
+ }
280
+
281
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
282
+ if let data = serviceData {
283
+ do {
284
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
285
+ print("Response Data: \(responseObject)")
286
+ if self.selectedPaymentMethod == "Card" {
287
+ if let dataSection = responseObject["data"] as? [String: Any],
288
+ let innerData = dataSection["data"] as? [String: Any],
289
+ let customerId = innerData["customer_id"] as? String {
290
+ print("Extracted customer_id: \(customerId)")
291
+ if self.billingInfoData == nil {
292
+ self.paymentIntentWithSavedCardApi(customerId: customerId)
293
+ }
294
+ else {
295
+ self.paymentIntentApi(customerId: customerId)
296
+ }
297
+ } else {
298
+ print("customer_id not found in nested data. Falling back to nil.")
299
+ self.paymentIntentApi(customerId: nil)
300
+ }
301
+ }
302
+ else if self.selectedPaymentMethod == "Bank" {
303
+
304
+ if let dataSection = responseObject["data"] as? [String: Any],
305
+ let innerData = dataSection["data"] as? [String: Any],
306
+ let customerId = innerData["customer_id"] as? String {
307
+ print("Extracted customer_id: \(customerId)")
308
+ if self.billingInfoData == nil {
309
+ self.accountChargeWithSavedAccountApi(customerId: customerId)
310
+ }
311
+ else {
312
+ self.accountChargeApi(customerId: customerId)
313
+ }
314
+ } else {
315
+ print("customer_id not found in nested data. Falling back to nil.")
316
+ if self.billingInfoData == nil {
317
+ self.accountChargeWithSavedAccountApi(customerId: nil)
318
+ }
319
+ else {
320
+ self.accountChargeApi(customerId: nil)
321
+ }
322
+ }
323
+
324
+ }
325
+ } else {
326
+ print("Invalid JSON format")
327
+ }
328
+ } catch let jsonError {
329
+ print("Error parsing JSON: \(jsonError)")
330
+ }
331
+ } else {
332
+ print("No data received")
333
+ }
334
+ } else {
335
+ print("HTTP Status Code: \(httpResponse.statusCode)")
336
+ }
337
+ }
338
+ task.resume()
339
+ }
340
+
341
+ //MARK: - Send OTP Email Verification Api
342
+ func emailVerificationApi() {
343
+ showLoadingIndicator()
344
+ // var components = URLComponents()
345
+ // components.scheme = "https"
346
+ // components.host = "stage-api.stage-easymerchant.io"
347
+ // components.path = "/api/v1/customer/send_otp"
348
+ //
349
+ // guard let serviceURL = components.url else {
350
+ // print("Invalid URL")
351
+ // hideLoadingIndicator()
352
+ // return
353
+ // }
354
+
355
+ // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
356
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.emailVerification.path()
357
+
358
+ guard let serviceURL = URL(string: fullURL) else {
359
+ print("Invalid URL")
360
+ hideLoadingIndicator()
361
+ return
362
+ }
363
+
364
+ var request = URLRequest(url: serviceURL)
365
+ request.httpMethod = "POST"
366
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
367
+
368
+ let token = UserStoreSingleton.shared.clientToken
369
+ print("Setting clientToken header: \(token ?? "None")")
370
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
371
+
372
+ let params: [String: Any] = [
373
+ "card_search_value": email ?? "",
374
+ "card_search_key": "email"
375
+ ]
376
+
377
+ do {
378
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
379
+ request.httpBody = jsonData
380
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
381
+ print("JSON Payload: \(jsonString)")
382
+ }
383
+ } catch let error {
384
+ print("Error creating JSON data: \(error)")
385
+ hideLoadingIndicator()
386
+ return
387
+ }
388
+
389
+ let session = URLSession.shared
390
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
391
+
392
+ DispatchQueue.main.async {
393
+ self.hideLoadingIndicator() // Stop loader when response is received
394
+ }
395
+
396
+ if let error = error {
397
+ print("Error: \(error.localizedDescription)")
398
+ return
399
+ }
400
+
401
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
402
+ print("Invalid response")
403
+ return
404
+ }
405
+
406
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
407
+ if let data = serviceData {
408
+ do {
409
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
410
+ print("Response Data: \(responseObject)")
411
+
412
+ } else {
413
+ print("Invalid JSON format")
414
+ }
415
+ } catch let jsonError {
416
+ print("Error parsing JSON: \(jsonError)")
417
+ }
418
+ } else {
419
+ print("No data received")
420
+ }
421
+ } else {
422
+ print("HTTP Status Code: \(httpResponse.statusCode)")
423
+ }
424
+ }
425
+ task.resume()
426
+ }
427
+
428
+ //MARK: - Card Charge Api
429
+ func paymentIntentApi(customerId: String?) {
430
+ showLoadingIndicator()
431
+
432
+ // var components = URLComponents()
433
+ // components.scheme = "https"
434
+ // components.host = "stage-api.stage-easymerchant.io"
435
+ // components.path = "/api/v1/charges"
436
+ //
437
+ // guard let serviceURL = components.url else {
438
+ // print("Invalid URL")
439
+ // hideLoadingIndicator()
440
+ // return
441
+ // }
442
+
443
+ // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
444
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
445
+
446
+ guard let serviceURL = URL(string: fullURL) else {
447
+ print("Invalid URL")
448
+ hideLoadingIndicator()
449
+ return
450
+ }
451
+
452
+ var request = URLRequest(url: serviceURL)
453
+ request.httpMethod = "POST"
454
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
455
+
456
+ let token = UserStoreSingleton.shared.clientToken
457
+ print("Setting clientToken header: \(token ?? "None")")
458
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
459
+
460
+ guard let billingInfoData = billingInfoData else {
461
+ print("Billing info data is nil")
462
+ return
463
+ }
464
+
465
+ let additionalInfoFromBilling = billingInfoData["additional_info"] as? [String: Any] ?? [:]
466
+
467
+ let additionalInfo: [String: Any] = [
468
+ "name": additionalInfoFromBilling["name"] as? String ?? "",
469
+ "email": additionalInfoFromBilling["email"] as? String ?? "",
470
+ "phone_number": additionalInfoFromBilling["phone_number"] as? String ?? "",
471
+ "description": additionalInfoFromBilling["description"] as? String ?? ""
472
+ ]
473
+
474
+ let billingInfo: [String: Any] = [
475
+ "address": billingInfoData["address"] as? String ?? "",
476
+ "country": billingInfoData["country"] as? String ?? "",
477
+ "state": billingInfoData["state"] as? String ?? "",
478
+ "city": billingInfoData["city"] as? String ?? "",
479
+ "postal_code": billingInfoData["postal_code"] as? String ?? ""
480
+ ]
481
+
482
+ let emailPrefix = email?.components(separatedBy: "@").first ?? ""
483
+
484
+ var params: [String: Any] = [
485
+ "name": additionalInfoFromBilling["name"] as? String ?? "",
486
+ "card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
487
+ "cardholder_name": nameOnCard ?? "",
488
+ "exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
489
+ "exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
490
+ "cvc": cvv ?? "",
491
+ "description": additionalInfoFromBilling["description"] as? String ?? "",
492
+ "currency": "usd",
493
+ "billing_info": billingInfo,
494
+ "additional_info": additionalInfo,
495
+ "payment_method": selectedPaymentMethod ?? "",
496
+ "save_card": 1,
497
+ "is_default": "1"
498
+ ]
499
+
500
+ if let customerId = customerId {
501
+ params["customer"] = customerId
502
+ params["customer_id"] = customerId
503
+ } else {
504
+ params["username"] = emailPrefix
505
+ params["email"] = email ?? ""
506
+ }
507
+
508
+ print(params)
509
+
510
+ do {
511
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
512
+ request.httpBody = jsonData
513
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
514
+ print("JSON Payload: \(jsonString)")
515
+ }
516
+ } catch let error {
517
+ print("Error creating JSON data: \(error)")
518
+ hideLoadingIndicator()
519
+ return
520
+ }
521
+
522
+ let session = URLSession.shared
523
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
524
+
525
+ DispatchQueue.main.async {
526
+ self.hideLoadingIndicator() // Stop loader when response is received
527
+ }
528
+
529
+ if let error = error {
530
+ self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
531
+ return
532
+ }
533
+
534
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
535
+ self.presentPaymentErrorVC(errorMessage: "Invalid response")
536
+ return
537
+ }
538
+
539
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
540
+ if let data = serviceData {
541
+ do {
542
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
543
+ print("Response Data: \(responseObject)")
544
+
545
+ // Check if status is 0 and handle the error
546
+ if let status = responseObject["status"] as? Int, status == 0 {
547
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
548
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
549
+ } else {
550
+ DispatchQueue.main.async {
551
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
552
+ paymentDoneVC.chargeData = responseObject
553
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
554
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
555
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
556
+ }
557
+ }
558
+ }
559
+ } else {
560
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
561
+ }
562
+ } catch let jsonError {
563
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
564
+ }
565
+ } else {
566
+ self.presentPaymentErrorVC(errorMessage: "No data received")
567
+ }
568
+ } else {
569
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
570
+ }
571
+ }
572
+ task.resume()
573
+ }
574
+
575
+ //MARK: - Card Charge Api If billing info is nil and saved card
576
+ func paymentIntentWithSavedCardApi(customerId: String?) {
577
+ showLoadingIndicator()
578
+
579
+ // var components = URLComponents()
580
+ // components.scheme = "https"
581
+ // components.host = "stage-api.stage-easymerchant.io"
582
+ // components.path = "/api/v1/charges"
583
+ //
584
+ // guard let serviceURL = components.url else {
585
+ // print("Invalid URL")
586
+ // hideLoadingIndicator()
587
+ // return
588
+ // }
589
+
590
+ // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
591
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
592
+
593
+ guard let serviceURL = URL(string: fullURL) else {
594
+ print("Invalid URL")
595
+ hideLoadingIndicator()
596
+ return
597
+ }
598
+
599
+ var request = URLRequest(url: serviceURL)
600
+ request.httpMethod = "POST"
601
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
602
+
603
+ let token = UserStoreSingleton.shared.clientToken
604
+ print("Setting clientToken header: \(token ?? "None")")
605
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
606
+
607
+ let emailPrefix = email?.components(separatedBy: "@").first ?? ""
608
+
609
+ var params: [String: Any] = [
610
+ "name": emailPrefix,
611
+ "card_number": cardNumber?.replacingOccurrences(of: " ", with: "") ?? "",
612
+ "cardholder_name": nameOnCard ?? "",
613
+ "exp_month": expiryDate?.components(separatedBy: "/").first ?? "",
614
+ "exp_year": expiryDate?.components(separatedBy: "/").last ?? "",
615
+ "cvc": cvv ?? "",
616
+ "description": "description",
617
+ "currency": "usd",
618
+ "payment_method": selectedPaymentMethod ?? "",
619
+ "save_card": 1,
620
+ "is_default": "1"
621
+ ]
622
+
623
+ if let customerId = customerId {
624
+ params["customer"] = customerId
625
+ params["customer_id"] = customerId
626
+ } else {
627
+ params["username"] = emailPrefix
628
+ params["email"] = email ?? ""
629
+ }
630
+
631
+ print(params)
632
+
633
+ do {
634
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
635
+ request.httpBody = jsonData
636
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
637
+ print("JSON Payload: \(jsonString)")
638
+ }
639
+ } catch let error {
640
+ print("Error creating JSON data: \(error)")
641
+ hideLoadingIndicator()
642
+ return
643
+ }
644
+
645
+ let session = URLSession.shared
646
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
647
+
648
+ DispatchQueue.main.async {
649
+ self.hideLoadingIndicator() // Stop loader when response is received
650
+ }
651
+
652
+ if let error = error {
653
+ self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
654
+ return
655
+ }
656
+
657
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
658
+ self.presentPaymentErrorVC(errorMessage: "Invalid response")
659
+ return
660
+ }
661
+
662
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
663
+ if let data = serviceData {
664
+ do {
665
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
666
+ print("Response Data: \(responseObject)")
667
+
668
+ // Check if status is 0 and handle the error
669
+ if let status = responseObject["status"] as? Int, status == 0 {
670
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
671
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
672
+ } else {
673
+ DispatchQueue.main.async {
674
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
675
+ paymentDoneVC.chargeData = responseObject
676
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
677
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
678
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
679
+ }
680
+ }
681
+ }
682
+ } else {
683
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
684
+ }
685
+ } catch let jsonError {
686
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
687
+ }
688
+ } else {
689
+ self.presentPaymentErrorVC(errorMessage: "No data received")
690
+ }
691
+ } else {
692
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
693
+ }
694
+ }
695
+ task.resume()
696
+ }
697
+
698
+ func presentPaymentErrorVC(errorMessage: String) {
699
+ DispatchQueue.main.async {
700
+ if let paymentErrorVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentErrorVC") as? PaymentErrorVC {
701
+ paymentErrorVC.errorMessage = errorMessage
702
+ paymentErrorVC.easyPayDelegate = self.easyPayDelegate // Pass the reference here
703
+ self.navigationController?.pushViewController(paymentErrorVC, animated: true)
704
+ }
705
+ }
706
+ }
707
+
708
+ //MARK: - Account Charge Api
709
+ func accountChargeApi(customerId: String?) {
710
+ showLoadingIndicator()
711
+
712
+ // var components = URLComponents()
713
+ // components.scheme = "https"
714
+ // components.host = "stage-api.stage-easymerchant.io"
715
+ // components.path = "/api/v1/ach/charge"
716
+ //
717
+ // guard let serviceURL = components.url else {
718
+ // print("Invalid URL")
719
+ // hideLoadingIndicator()
720
+ // return
721
+ // }
722
+
723
+ // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
724
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
725
+
726
+ guard let serviceURL = URL(string: fullURL) else {
727
+ print("Invalid URL")
728
+ hideLoadingIndicator()
729
+ return
730
+ }
731
+
732
+ var request = URLRequest(url: serviceURL)
733
+ request.httpMethod = "POST"
734
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
735
+
736
+ let token = UserStoreSingleton.shared.clientToken
737
+ print("Setting clientToken header: \(token ?? "None")")
738
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
739
+
740
+ guard let billingInfoData = billingInfoData else {
741
+ print("Billing info data is nil")
742
+ return
743
+ }
744
+
745
+ let additionalInfoFromBilling = billingInfoData["additional_info"] as? [String: Any] ?? [:]
746
+
747
+ let additionalInfo: [String: Any] = [
748
+ "name": additionalInfoFromBilling["name"] as? String ?? "",
749
+ "email": additionalInfoFromBilling["email"] as? String ?? "",
750
+ "phone_number": additionalInfoFromBilling["phone_number"] as? String ?? "",
751
+ "description": additionalInfoFromBilling["description"] as? String ?? ""
752
+ ]
753
+
754
+ let billingInfo: [String: Any] = [
755
+ "address": billingInfoData["address"] as? String ?? "",
756
+ "country": billingInfoData["country"] as? String ?? "",
757
+ "state": billingInfoData["state"] as? String ?? "",
758
+ "city": billingInfoData["city"] as? String ?? "",
759
+ "postal_code": billingInfoData["postal_code"] as? String ?? ""
760
+ ]
761
+
762
+ let emailPrefix = email?.components(separatedBy: "@").first ?? ""
763
+
764
+ var params: [String: Any] = [
765
+ "name": additionalInfoFromBilling["name"] as? String ?? "",
766
+ "email": email ?? "",
767
+ "description": additionalInfoFromBilling["description"] as? String ?? "",
768
+ "currency": "usd",
769
+ "billing_info": billingInfo,
770
+ "additional_info": additionalInfo,
771
+ "account_type": accountType?.lowercased() ?? "",
772
+ "routing_number": routingNumber ?? "",
773
+ "account_number": accountNumber ?? "",
774
+ "payment_mode": "auth_and_capture",
775
+ "payment_intent": UserStoreSingleton.shared.paymentIntent ?? "",
776
+ "levelIndicator": 1,
777
+ "save_account": 1,
778
+ "payment_method": "ach"
779
+ ]
780
+
781
+ if let customerId = customerId {
782
+ params["customer"] = customerId
783
+ } else {
784
+ params["username"] = emailPrefix
785
+ }
786
+
787
+ print(params)
788
+
789
+ do {
790
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
791
+ request.httpBody = jsonData
792
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
793
+ print("JSON Payload: \(jsonString)")
794
+ }
795
+ } catch let error {
796
+ print("Error creating JSON data: \(error)")
797
+ hideLoadingIndicator()
798
+ return
799
+ }
800
+
801
+ let session = URLSession.shared
802
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
803
+
804
+ DispatchQueue.main.async {
805
+ self.hideLoadingIndicator() // Stop loader when response is received
806
+ }
807
+
808
+ if let error = error {
809
+ self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
810
+ return
811
+ }
812
+
813
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
814
+ self.presentPaymentErrorVC(errorMessage: "Invalid response")
815
+ return
816
+ }
817
+
818
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
819
+ if let data = serviceData {
820
+ do {
821
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
822
+ print("Response Data: \(responseObject)")
823
+
824
+ // Check if status is 0 and handle the error
825
+ if let status = responseObject["status"] as? Int, status == 0 {
826
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
827
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
828
+ } else {
829
+ DispatchQueue.main.async {
830
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
831
+ paymentDoneVC.chargeData = responseObject
832
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
833
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
834
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
835
+ }
836
+ }
837
+ }
838
+ } else {
839
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
840
+ }
841
+ } catch let jsonError {
842
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
843
+ }
844
+ } else {
845
+ self.presentPaymentErrorVC(errorMessage: "No data received")
846
+ }
847
+ } else {
848
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
849
+ }
850
+ }
851
+ task.resume()
852
+ }
853
+
854
+ //MARK: - Account Charge Api if billing info is nil and saved account
855
+ func accountChargeWithSavedAccountApi(customerId: String?) {
856
+ showLoadingIndicator()
857
+
858
+ // var components = URLComponents()
859
+ // components.scheme = "https"
860
+ // components.host = "stage-api.stage-easymerchant.io"
861
+ // components.path = "/api/v1/ach/charge"
862
+ //
863
+ // guard let serviceURL = components.url else {
864
+ // print("Invalid URL")
865
+ // hideLoadingIndicator()
866
+ // return
867
+ // }
868
+
869
+ // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
870
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
871
+
872
+ guard let serviceURL = URL(string: fullURL) else {
873
+ print("Invalid URL")
874
+ hideLoadingIndicator()
875
+ return
876
+ }
877
+
878
+ var request = URLRequest(url: serviceURL)
879
+ request.httpMethod = "POST"
880
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
881
+
882
+ let token = UserStoreSingleton.shared.clientToken
883
+ print("Setting clientToken header: \(token ?? "None")")
884
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
885
+
886
+ let emailPrefix = email?.components(separatedBy: "@").first ?? ""
887
+
888
+ var params: [String: Any] = [
889
+ "name": emailPrefix,
890
+ "email": email ?? "",
891
+ "description": "Test Description",
892
+ "currency": "usd",
893
+ "account_type": accountType?.lowercased() ?? "",
894
+ "routing_number": routingNumber ?? "",
895
+ "account_number": accountNumber ?? "",
896
+ "payment_mode": "auth_and_capture",
897
+ "payment_intent": UserStoreSingleton.shared.paymentIntent ?? "",
898
+ "levelIndicator": 1,
899
+ "save_account": 1,
900
+ "payment_method": "ach"
901
+ ]
902
+
903
+ if let customerId = customerId {
904
+ params["customer"] = customerId
905
+ } else {
906
+ params["username"] = emailPrefix
907
+ }
908
+
909
+ print(params)
910
+
911
+ do {
912
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
913
+ request.httpBody = jsonData
914
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
915
+ print("JSON Payload: \(jsonString)")
916
+ }
917
+ } catch let error {
918
+ print("Error creating JSON data: \(error)")
919
+ hideLoadingIndicator()
920
+ return
921
+ }
922
+
923
+ let session = URLSession.shared
924
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
925
+
926
+ DispatchQueue.main.async {
927
+ self.hideLoadingIndicator() // Stop loader when response is received
928
+ }
929
+
930
+ if let error = error {
931
+ self.presentPaymentErrorVC(errorMessage: error.localizedDescription)
932
+ return
933
+ }
934
+
935
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
936
+ self.presentPaymentErrorVC(errorMessage: "Invalid response")
937
+ return
938
+ }
939
+
940
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
941
+ if let data = serviceData {
942
+ do {
943
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
944
+ print("Response Data: \(responseObject)")
945
+
946
+ // Check if status is 0 and handle the error
947
+ if let status = responseObject["status"] as? Int, status == 0 {
948
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
949
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
950
+ } else {
951
+ DispatchQueue.main.async {
952
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
953
+ paymentDoneVC.chargeData = responseObject
954
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
955
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
956
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
957
+ }
958
+ }
959
+ }
960
+ } else {
961
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
962
+ }
963
+ } catch let jsonError {
964
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
965
+ }
966
+ } else {
967
+ self.presentPaymentErrorVC(errorMessage: "No data received")
968
+ }
969
+ } else {
970
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
971
+ }
972
+ }
973
+ task.resume()
974
+ }
975
+
976
+ @IBAction func actionBtnCancel(_ sender: UIButton) {
977
+ stopTimer()
978
+ for viewController in self.navigationController?.viewControllers ?? [] {
979
+ if viewController is PaymentInfoVC {
980
+ self.navigationController?.popToViewController(viewController, animated: true)
981
+ break
982
+ }
983
+ }
984
+ }
985
+
986
+ @IBAction func actionBtnResendOTP(_ sender: UIButton) {
987
+ startTimer()
988
+ btnResendOTP.isHidden = true
989
+ imgEsclamationMark.isHidden = false
990
+ lblOtpTimer.isHidden = false
991
+ lblUntillResendOtp.isHidden = false
992
+
993
+ // Call email verification APi
994
+ emailVerificationApi()
995
+ }
996
+
997
+ @IBAction func actionBtnConfirmCode(_ sender: UIButton) {
998
+ otpVerificationApi()
999
+ }
1000
+
1001
+ }
1002
+
1003
+ extension OTPVerificationVC: UITextFieldDelegate {
1004
+
1005
+ func textFieldDidChangeSelection(_ textField: UITextField) {
1006
+ guard let otpCode = textField.text, otpCode.count >= 6, textField.textContentType == .oneTimeCode else { return }
1007
+ txtFieldOTPText1.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 0)])
1008
+ txtFieldOTPText2.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 1)])
1009
+ txtFieldOTPText3.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 2)])
1010
+ txtFieldOTPText4.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 3)])
1011
+ txtFieldOTPText5.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 4)])
1012
+ txtFieldOTPText6.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 5)])
1013
+ txtFieldOTPText6.becomeFirstResponder()
1014
+ }
1015
+
1016
+ func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
1017
+ if [txtFieldOTPText1, txtFieldOTPText2, txtFieldOTPText3, txtFieldOTPText4, txtFieldOTPText5, txtFieldOTPText6].contains(textField) {
1018
+ if string.count == 1 {
1019
+ // Single character input
1020
+ textField.text = string
1021
+ switch textField {
1022
+ case txtFieldOTPText1:
1023
+ txtFieldOTPText2.becomeFirstResponder()
1024
+ case txtFieldOTPText2:
1025
+ txtFieldOTPText3.becomeFirstResponder()
1026
+ case txtFieldOTPText3:
1027
+ txtFieldOTPText4.becomeFirstResponder()
1028
+ case txtFieldOTPText4:
1029
+ txtFieldOTPText5.becomeFirstResponder()
1030
+ case txtFieldOTPText5:
1031
+ txtFieldOTPText6.becomeFirstResponder()
1032
+ case txtFieldOTPText6:
1033
+ txtFieldOTPText6.resignFirstResponder()
1034
+ // Call OTP verification API when the last field is filled
1035
+ if getCombinedOTP().count == 6 {
1036
+ // otpVerificationApi()
1037
+ }
1038
+ default:
1039
+ break
1040
+ }
1041
+ updateViewColors()
1042
+ return false
1043
+ } else if string.isEmpty {
1044
+ // Handle backspace
1045
+ textField.text = ""
1046
+ switch textField {
1047
+ case txtFieldOTPText6:
1048
+ txtFieldOTPText5.becomeFirstResponder()
1049
+ case txtFieldOTPText5:
1050
+ txtFieldOTPText4.becomeFirstResponder()
1051
+ case txtFieldOTPText4:
1052
+ txtFieldOTPText3.becomeFirstResponder()
1053
+ case txtFieldOTPText3:
1054
+ txtFieldOTPText2.becomeFirstResponder()
1055
+ case txtFieldOTPText2:
1056
+ txtFieldOTPText1.becomeFirstResponder()
1057
+ default:
1058
+ break
1059
+ }
1060
+ updateViewColors()
1061
+ return false
1062
+ }
1063
+ return textField.text?.count == 0
1064
+ }
1065
+
1066
+ return true
1067
+ }
1068
+
1069
+ // Update the view colors based on text presence in OTP fields
1070
+ private func updateViewColors() {
1071
+ if let primaryBtnBackgroundColor = UserStoreSingleton.shared.primary_btn_bg_col,
1072
+ let primaryUIColor = UIColor(hex: primaryBtnBackgroundColor),
1073
+ let secondaryFontColor = UserStoreSingleton.shared.secondary_font_col,
1074
+ let secondaryUIColor = UIColor(hex: secondaryFontColor) {
1075
+
1076
+ viewTextOTP1.layer.borderWidth = 1.0
1077
+ viewTextOTP1.layer.borderColor = txtFieldOTPText1.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
1078
+ viewTextOTP2.layer.borderWidth = 1.0
1079
+ viewTextOTP2.layer.borderColor = txtFieldOTPText2.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
1080
+ viewTextOTP3.layer.borderWidth = 1.0
1081
+ viewTextOTP3.layer.borderColor = txtFieldOTPText3.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
1082
+ viewTextOTP4.layer.borderWidth = 1.0
1083
+ viewTextOTP4.layer.borderColor = txtFieldOTPText4.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
1084
+ viewTextOTP5.layer.borderWidth = 1.0
1085
+ viewTextOTP5.layer.borderColor = txtFieldOTPText5.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
1086
+ viewTextOTP6.layer.borderWidth = 1.0
1087
+ viewTextOTP6.layer.borderColor = txtFieldOTPText6.text?.isEmpty == false ? primaryUIColor.cgColor : secondaryUIColor.cgColor
1088
+ }
1089
+ else {
1090
+ viewTextOTP1.layer.borderWidth = 1.0
1091
+ viewTextOTP1.layer.borderColor = (txtFieldOTPText1.text?.isEmpty == false ? UIColor.systemBlue : UIColor.systemGray).cgColor
1092
+ viewTextOTP2.layer.borderWidth = 1.0
1093
+ viewTextOTP2.layer.borderColor = (txtFieldOTPText2.text?.isEmpty == false ? UIColor.systemBlue : UIColor.systemGray).cgColor
1094
+ viewTextOTP3.layer.borderWidth = 1.0
1095
+ viewTextOTP3.layer.borderColor = (txtFieldOTPText3.text?.isEmpty == false ? UIColor.systemBlue : UIColor.systemGray).cgColor
1096
+ viewTextOTP4.layer.borderWidth = 1.0
1097
+ viewTextOTP4.layer.borderColor = (txtFieldOTPText4.text?.isEmpty == false ? UIColor.systemBlue : UIColor.systemGray).cgColor
1098
+ viewTextOTP5.layer.borderWidth = 1.0
1099
+ viewTextOTP5.layer.borderColor = (txtFieldOTPText5.text?.isEmpty == false ? UIColor.systemBlue : UIColor.systemGray).cgColor
1100
+ viewTextOTP6.layer.borderWidth = 1.0
1101
+ viewTextOTP6.layer.borderColor = (txtFieldOTPText6.text?.isEmpty == false ? UIColor.systemBlue : UIColor.systemGray).cgColor
1102
+ }
1103
+ }
1104
+
1105
+ @objc func textFieldDidChange(_ textField: UITextField) {
1106
+ guard let otpCode = textField.text, otpCode.count >= 6, textField.textContentType == .oneTimeCode else { return }
1107
+ // Split the OTP into each text field
1108
+ txtFieldOTPText1.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 0)])
1109
+ txtFieldOTPText2.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 1)])
1110
+ txtFieldOTPText3.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 2)])
1111
+ txtFieldOTPText4.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 3)])
1112
+ txtFieldOTPText5.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 4)])
1113
+ txtFieldOTPText6.text = String(otpCode[otpCode.index(otpCode.startIndex, offsetBy: 5)])
1114
+ // Automatically move to the last text field
1115
+ txtFieldOTPText6.becomeFirstResponder()
1116
+ }
1117
+
1118
+ func getCombinedOTP() -> String {
1119
+ let otp1 = txtFieldOTPText1.text ?? ""
1120
+ let otp2 = txtFieldOTPText2.text ?? ""
1121
+ let otp3 = txtFieldOTPText3.text ?? ""
1122
+ let otp4 = txtFieldOTPText4.text ?? ""
1123
+ let otp5 = txtFieldOTPText5.text ?? ""
1124
+ let otp6 = txtFieldOTPText6.text ?? ""
1125
+
1126
+ return otp1 + otp2 + otp3 + otp4 + otp5 + otp6
1127
+ }
1128
+
1129
+ }
1130
+
1131
+
1132
+
1133
+
1134
+