@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,803 @@
1
+ //
2
+ // BillingInfoVC.swift
3
+ // EasyPay
4
+ //
5
+ // Created by Mony's Mac on 12/08/24.
6
+ //
7
+
8
+ import UIKit
9
+
10
+ // @available(iOS 16.0, *)
11
+
12
+ protocol BillingInfoVCDelegate: AnyObject {
13
+ func didPassTextBack(_ text: String)
14
+ }
15
+
16
+ class BillingInfoVC: BaseVC {
17
+
18
+ @IBOutlet weak var viewBillingInfo: UIView!
19
+ @IBOutlet weak var btnPrevious: UIButton!
20
+ @IBOutlet weak var viewCountryList: UIView!
21
+ @IBOutlet weak var tblViewCountryList: UITableView!
22
+ @IBOutlet weak var txtFieldAddress: UITextField!
23
+ @IBOutlet weak var txtFieldCountry: UITextField!
24
+ @IBOutlet weak var viewStateList: UIView!
25
+ @IBOutlet weak var tblViewStateList: UITableView!
26
+ @IBOutlet weak var txtFieldState: UITextField!
27
+ @IBOutlet weak var viewCityList: UIView!
28
+ @IBOutlet weak var tblViewCityList: UITableView!
29
+ @IBOutlet weak var txtFieldCity: UITextField!
30
+ @IBOutlet weak var txtFieldPostalCode: UITextField!
31
+
32
+ @IBOutlet weak var buttonBottomConstraint: NSLayoutConstraint!
33
+
34
+ @IBOutlet weak var lblBillingInfo: UILabel!
35
+ @IBOutlet weak var lblEasyMerchant: UILabel!
36
+ @IBOutlet weak var btnNext: UIButton!
37
+
38
+ // Variable to store the auth token
39
+ var authToken: String?
40
+
41
+ var countryList: [[String: Any]] = []
42
+ var stateList: [[String: Any]] = []
43
+ var cityList: [[String: Any]] = []
44
+
45
+ var billingInfoData: [String: Any]?
46
+
47
+ var cardNumber: String?
48
+ var expiryDate: String?
49
+ var cvv: String?
50
+ var nameOnCard: String?
51
+
52
+ //Banking Params
53
+ var accountName: String?
54
+ var routingNumber: String?
55
+ var accountType: String?
56
+ var accountNumber: String?
57
+
58
+ var selectedPaymentMethod: String?
59
+
60
+ private let keyboardObserver = KeyboardObserver()
61
+
62
+ var isSavedForFuture: Bool = false
63
+ var isSavedNewCard: Bool = false
64
+
65
+ private var request: Request!
66
+ var selectedCard: CardModel?
67
+ var amount: Int?
68
+ var cvvText: String?
69
+
70
+ var isFrom = String()
71
+
72
+ //From Regular Saved Bank Accounts
73
+ var customerID: String?
74
+ var accountID: String?
75
+
76
+ var isSavedNewAccount: Bool?
77
+
78
+ weak var delegate: BillingInfoVCDelegate?
79
+
80
+ override func viewDidLoad() {
81
+ super.viewDidLoad()
82
+
83
+ uiFinishingTouchElements()
84
+
85
+ setupShadowForListViews()
86
+
87
+ tblViewCountryList.delegate = self
88
+ tblViewCountryList.dataSource = self
89
+ viewCountryList.isHidden = true
90
+
91
+ tblViewStateList.delegate = self
92
+ tblViewStateList.dataSource = self
93
+ viewStateList.isHidden = true
94
+
95
+ tblViewCityList.delegate = self
96
+ tblViewCityList.dataSource = self
97
+ viewCityList.isHidden = true
98
+
99
+ txtFieldCity.delegate = self
100
+ txtFieldState.delegate = self
101
+ txtFieldAddress.delegate = self
102
+ txtFieldCountry.delegate = self
103
+ txtFieldPostalCode.delegate = self
104
+
105
+ getCountryListApi()
106
+
107
+ // Add tap gesture to hide the views and dismiss the keyboard
108
+ let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapOutside))
109
+ tapGesture.cancelsTouchesInView = false
110
+ self.view.addGestureRecognizer(tapGesture)
111
+
112
+ // Print data for verification
113
+ if let billingInfoData = billingInfoData {
114
+ print("Billing Info Data: \(billingInfoData)")
115
+
116
+ if let address = billingInfoData["address"] as? String {
117
+ txtFieldAddress.text = address
118
+ }
119
+
120
+ if let country = billingInfoData["country"] as? String {
121
+ txtFieldCountry.text = country
122
+ }
123
+
124
+ if let state = billingInfoData["state"] as? String {
125
+ txtFieldState.text = state
126
+ }
127
+
128
+ if let city = billingInfoData["city"] as? String {
129
+ txtFieldCity.text = city
130
+ }
131
+
132
+ if let postalCode = billingInfoData["postal_code"] as? String {
133
+ txtFieldPostalCode.text = postalCode
134
+ }
135
+ }
136
+
137
+ }
138
+
139
+ override func viewWillAppear(_ animated: Bool) {
140
+ super.viewWillAppear(animated)
141
+
142
+ uiFinishingTouchElements()
143
+
144
+ keyboardObserver.animateChanges({ [self] height in
145
+ let newConstant = CGFloat.maximum(height - self.view.safeAreaInsets.bottom + 8, 8)
146
+ buttonBottomConstraint.constant = newConstant
147
+ self.view.setNeedsLayout()
148
+ self.view.layoutIfNeeded()
149
+ })
150
+ }
151
+
152
+ override func viewWillDisappear(_ animated: Bool) {
153
+ super.viewWillDisappear(animated)
154
+ keyboardObserver.invalidate()
155
+ }
156
+
157
+ func uiFinishingTouchElements() {
158
+ // Set background color for the main view
159
+ if let containerBGcolor = UserStoreSingleton.shared.container_bg_col,
160
+ let uiColor = UIColor(hex: containerBGcolor) {
161
+ self.view.backgroundColor = uiColor
162
+ viewCountryList.backgroundColor = uiColor
163
+ viewStateList.backgroundColor = uiColor
164
+ viewCityList.backgroundColor = uiColor
165
+ tblViewCountryList.backgroundColor = uiColor
166
+ tblViewStateList.backgroundColor = uiColor
167
+ tblViewCityList.backgroundColor = uiColor
168
+ }
169
+
170
+ if let primaryBtnBackGroundColor = UserStoreSingleton.shared.primary_btn_bg_col,
171
+ let uiColor = UIColor(hex: primaryBtnBackGroundColor) {
172
+ btnNext.backgroundColor = uiColor
173
+ btnPrevious.setTitleColor(uiColor, for: .normal)
174
+ btnPrevious.layer.borderColor = uiColor.cgColor
175
+ }
176
+
177
+ if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_font_col,
178
+ let secondaryUIColor = UIColor(hex: primaryBtnFontColor) {
179
+ btnNext.setTitleColor(secondaryUIColor, for: .normal)
180
+ }
181
+
182
+ if let secondaryFontColor = UserStoreSingleton.shared.secondary_font_col,
183
+ let placeholderColor = UIColor(hex: secondaryFontColor) {
184
+ lblEasyMerchant.textColor = placeholderColor
185
+ viewBillingInfo.layer.borderColor = placeholderColor.cgColor
186
+ }
187
+ else {
188
+ viewBillingInfo.layer.borderColor = UIColor.systemGray.cgColor
189
+ }
190
+
191
+ if let borderRadiusString = UserStoreSingleton.shared.border_radious,
192
+ let borderRadius = Double(borderRadiusString) { // Convert String to Double
193
+ btnNext.layer.cornerRadius = CGFloat(borderRadius) // Set corner radius
194
+ btnPrevious.layer.cornerRadius = CGFloat(borderRadius)
195
+ btnPrevious.layer.borderWidth = 1
196
+ viewBillingInfo.layer.cornerRadius = CGFloat(borderRadius)
197
+ viewBillingInfo.layer.borderWidth = 1
198
+ } else {
199
+ btnNext.layer.cornerRadius = 8 // Default value
200
+ btnPrevious.layer.cornerRadius = 8
201
+ viewBillingInfo.layer.borderWidth = 1
202
+ viewBillingInfo.layer.cornerRadius = 8
203
+ }
204
+ btnNext.layer.masksToBounds = true // Ensure the corners are clipped properly
205
+ btnPrevious.layer.masksToBounds = true
206
+ viewBillingInfo.layer.masksToBounds = true
207
+
208
+ if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
209
+ let uiColor = UIColor(hex: primaryFontColor) {
210
+ lblBillingInfo.textColor = uiColor
211
+ }
212
+
213
+ if let fontSizeString = UserStoreSingleton.shared.fontSize,
214
+ let fontSizeDouble = Double(fontSizeString) { // Convert String to Double
215
+ let fontSize = CGFloat(fontSizeDouble) // Convert Double to CGFloat
216
+ lblEasyMerchant.font = UIFont.systemFont(ofSize: fontSize)
217
+ btnNext.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
218
+ btnPrevious.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
219
+ }
220
+
221
+ }
222
+
223
+ @objc func didTapOutside() {
224
+ // Dismiss the keyboard
225
+ self.view.endEditing(true)
226
+
227
+ // Hide the country list view if it is visible
228
+ if !viewCountryList.isHidden {
229
+ viewCountryList.isHidden = true
230
+ }
231
+
232
+ // Hide the state list view if it is visible
233
+ if !viewStateList.isHidden {
234
+ viewStateList.isHidden = true
235
+ }
236
+
237
+ // Hide the city list view if it is visible
238
+ if !viewCityList.isHidden {
239
+ viewCityList.isHidden = true
240
+ }
241
+ }
242
+
243
+ func setupShadowForListViews() {
244
+ // Ensure the view does not clip its content or shadows
245
+ viewCountryList.clipsToBounds = false
246
+ // Apply shadow properties
247
+ viewCountryList.layer.shadowColor = UIColor.black.cgColor
248
+ viewCountryList.layer.shadowOpacity = 0.3
249
+ viewCountryList.layer.shadowOffset = CGSize(width: 0, height: 2)
250
+ viewCountryList.layer.shadowRadius = 4
251
+ // Optionally, you might want to set a corner radius to the view as well
252
+ viewCountryList.layer.cornerRadius = 8
253
+ tblViewCountryList.layer.cornerRadius = 8
254
+
255
+ // Ensure the view does not clip its content or shadows
256
+ viewStateList.clipsToBounds = false
257
+ // Apply shadow properties
258
+ viewStateList.layer.shadowColor = UIColor.black.cgColor
259
+ viewStateList.layer.shadowOpacity = 0.3
260
+ viewStateList.layer.shadowOffset = CGSize(width: 0, height: 2)
261
+ viewStateList.layer.shadowRadius = 4
262
+ // Optionally, you might want to set a corner radius to the view as well
263
+ viewStateList.layer.cornerRadius = 8
264
+ tblViewStateList.layer.cornerRadius = 8
265
+
266
+ // Ensure the view does not clip its content or shadows
267
+ viewCityList.clipsToBounds = false
268
+ // Apply shadow properties
269
+ viewCityList.layer.shadowColor = UIColor.black.cgColor
270
+ viewCityList.layer.shadowOpacity = 0.3
271
+ viewCityList.layer.shadowOffset = CGSize(width: 0, height: 2)
272
+ viewCityList.layer.shadowRadius = 4
273
+ // Optionally, you might want to set a corner radius to the view as well
274
+ viewCityList.layer.cornerRadius = 8
275
+ tblViewCityList.layer.cornerRadius = 8
276
+ }
277
+
278
+ //MARK: Get Country List
279
+ func getCountryListApi() {
280
+ let session = URLSession.shared
281
+ let serviceURL = URL(string: "https://countriesnow.space/api/v0.1/countries/iso")!
282
+
283
+ var request = URLRequest(url: serviceURL)
284
+ request.httpMethod = "GET"
285
+
286
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
287
+ if let error = error {
288
+ self.hideLoadingIndicator()
289
+ print("Error: \(error.localizedDescription)")
290
+ return
291
+ }
292
+
293
+ guard let httpResponse = serviceResponse as? HTTPURLResponse, httpResponse.statusCode == 200 else {
294
+ self.hideLoadingIndicator()
295
+ print("Error: HTTP Status Code \(String(describing: (serviceResponse as? HTTPURLResponse)?.statusCode))")
296
+ return
297
+ }
298
+
299
+ guard let data = serviceData else {
300
+ self.hideLoadingIndicator()
301
+ print("Error: No data received")
302
+ return
303
+ }
304
+
305
+ do {
306
+ if let jsonResponse = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any],
307
+ let countryData = jsonResponse["data"] as? [[String: Any]] {
308
+
309
+ self.countryList = countryData // Store the list of countries
310
+
311
+ DispatchQueue.main.async {
312
+ self.tblViewCountryList.reloadData() // Reload the table view
313
+ }
314
+ } else {
315
+ self.hideLoadingIndicator()
316
+ print("Error: Unable to parse JSON or find country data")
317
+ }
318
+ } catch {
319
+ self.hideLoadingIndicator()
320
+ print("Error parsing JSON: \(error.localizedDescription)")
321
+ }
322
+ }
323
+ task.resume()
324
+ }
325
+
326
+ ///** Normal Case
327
+ // //MARK: Get State List
328
+ // func getStateListApi(for country: String) {
329
+ // let urlString = "https://countriesnow.space/api/v0.1/countries/states"
330
+ // guard let serviceURL = URL(string: urlString) else {
331
+ // print("Invalid URL")
332
+ // hideLoadingIndicator()
333
+ // return
334
+ // }
335
+ //
336
+ // var request = URLRequest(url: serviceURL)
337
+ // request.httpMethod = "POST"
338
+ // request.addValue("application/json", forHTTPHeaderField: "Content-Type")
339
+ //
340
+ // let params: [String: Any] = [
341
+ // "country": country
342
+ // ]
343
+ //
344
+ // do {
345
+ // let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
346
+ // request.httpBody = jsonData
347
+ // } catch {
348
+ // print("Error creating JSON data: \(error)")
349
+ // return
350
+ // }
351
+ //
352
+ // let session = URLSession.shared
353
+ // let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
354
+ // if let error = error {
355
+ // print("Error: \(error.localizedDescription)")
356
+ // return
357
+ // }
358
+ //
359
+ // guard let httpResponse = serviceResponse as? HTTPURLResponse, httpResponse.statusCode == 200 else {
360
+ // print("Invalid response or status code")
361
+ // return
362
+ // }
363
+ //
364
+ // if let data = serviceData {
365
+ // do {
366
+ // if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
367
+ // let dataObject = responseObject["data"] as? [String: Any],
368
+ // let statesArray = dataObject["states"] as? [[String: Any]] {
369
+ //
370
+ // DispatchQueue.main.async {
371
+ // self.stateList = statesArray
372
+ // self.tblViewStateList.reloadData()
373
+ //
374
+ // // Pick a random state and set it in txtFieldState
375
+ // if let randomState = statesArray.randomElement(),
376
+ // let stateName = randomState["name"] as? String {
377
+ // self.txtFieldState.text = stateName
378
+ //
379
+ // // Fetch cities for the randomly selected state and country
380
+ // if let country = self.txtFieldCountry.text {
381
+ // self.getCityListListApi(for: country, state: stateName)
382
+ // }
383
+ // } else {
384
+ // self.txtFieldState.text = "" // No state available
385
+ // }
386
+ // }
387
+ // } else {
388
+ // print("Error: Invalid JSON structure")
389
+ // }
390
+ // } catch {
391
+ // print("Error parsing JSON: \(error)")
392
+ // }
393
+ // } else {
394
+ // print("No data received")
395
+ // }
396
+ // }
397
+ // task.resume()
398
+ // }
399
+ //
400
+
401
+ ///** Normal Case
402
+ // //MARK: Get City List
403
+ // func getCityListListApi(for country: String, state: String) {
404
+ // let urlString = "https://countriesnow.space/api/v0.1/countries/state/cities"
405
+ // guard let serviceURL = URL(string: urlString) else {
406
+ // print("Invalid URL")
407
+ // hideLoadingIndicator()
408
+ // return
409
+ // }
410
+ //
411
+ // var request = URLRequest(url: serviceURL)
412
+ // request.httpMethod = "POST"
413
+ // request.addValue("application/json", forHTTPHeaderField: "Content-Type")
414
+ //
415
+ // let params: [String: Any] = [
416
+ // "country": country,
417
+ // "state": state
418
+ // ]
419
+ //
420
+ // do {
421
+ // let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
422
+ // request.httpBody = jsonData
423
+ // } catch {
424
+ // print("Error creating JSON data: \(error)")
425
+ // return
426
+ // }
427
+ //
428
+ // let session = URLSession.shared
429
+ // let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
430
+ // if let error = error {
431
+ // print("Error: \(error.localizedDescription)")
432
+ // return
433
+ // }
434
+ //
435
+ // guard let httpResponse = serviceResponse as? HTTPURLResponse, httpResponse.statusCode == 200 else {
436
+ // print("Invalid response or status code")
437
+ // return
438
+ // }
439
+ //
440
+ // if let data = serviceData {
441
+ // do {
442
+ // if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
443
+ // let citiesArray = responseObject["data"] as? [String] { // Directly accessing city list array
444
+ //
445
+ // DispatchQueue.main.async {
446
+ // self.cityList = citiesArray.map { ["city_name": $0] } // Converting to expected format
447
+ // self.tblViewCityList.reloadData()
448
+ //
449
+ // // Pick a random city and set it in txtFieldCity
450
+ // if let randomCity = citiesArray.randomElement() {
451
+ // self.txtFieldCity.text = randomCity
452
+ // } else {
453
+ // self.txtFieldCity.text = "" // No city available
454
+ // }
455
+ // }
456
+ // } else {
457
+ // print("Error: Invalid JSON structure")
458
+ // }
459
+ // } catch {
460
+ // print("Error parsing JSON: \(error)")
461
+ // }
462
+ // } else {
463
+ // print("No data received")
464
+ // }
465
+ // }
466
+ // task.resume()
467
+ // }
468
+
469
+
470
+ ///**In case of state and city name not found
471
+ func getStateListApi(for country: String) {
472
+ let urlString = "https://countriesnow.space/api/v0.1/countries/states"
473
+ guard let serviceURL = URL(string: urlString) else {
474
+ print("Invalid URL")
475
+ hideLoadingIndicator()
476
+ return
477
+ }
478
+
479
+ var request = URLRequest(url: serviceURL)
480
+ request.httpMethod = "POST"
481
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
482
+
483
+ let params: [String: Any] = [
484
+ "country": country
485
+ ]
486
+
487
+ do {
488
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
489
+ request.httpBody = jsonData
490
+ } catch {
491
+ print("Error creating JSON data: \(error)")
492
+ return
493
+ }
494
+
495
+ let session = URLSession.shared
496
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
497
+ if let error = error {
498
+ print("Error: \(error.localizedDescription)")
499
+ return
500
+ }
501
+
502
+ guard let httpResponse = serviceResponse as? HTTPURLResponse, httpResponse.statusCode == 200 else {
503
+ print("Invalid response or status code")
504
+ return
505
+ }
506
+
507
+ if let data = serviceData {
508
+ do {
509
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
510
+ let dataObject = responseObject["data"] as? [String: Any],
511
+ let statesArray = dataObject["states"] as? [[String: Any]] {
512
+
513
+ DispatchQueue.main.async {
514
+ self.stateList = statesArray
515
+ self.tblViewStateList.reloadData()
516
+
517
+ // If no state is found, clear the state and city fields and fill country in both
518
+ if statesArray.isEmpty {
519
+ self.txtFieldState.text = country
520
+ self.txtFieldCity.text = country
521
+ } else {
522
+ // Pick a random state and set it in txtFieldState
523
+ if let randomState = statesArray.randomElement(),
524
+ let stateName = randomState["name"] as? String {
525
+ self.txtFieldState.text = stateName
526
+
527
+ // Fetch cities for the randomly selected state and country
528
+ self.getCityListListApi(for: country, state: stateName)
529
+ }
530
+ }
531
+ }
532
+ } else {
533
+ print("Error: Invalid JSON structure")
534
+ }
535
+ } catch {
536
+ print("Error parsing JSON: \(error)")
537
+ }
538
+ } else {
539
+ print("No data received")
540
+ }
541
+ }
542
+ task.resume()
543
+ }
544
+
545
+ ///**In case of state and city name not found
546
+ func getCityListListApi(for country: String, state: String) {
547
+ let urlString = "https://countriesnow.space/api/v0.1/countries/state/cities"
548
+ guard let serviceURL = URL(string: urlString) else {
549
+ print("Invalid URL")
550
+ hideLoadingIndicator()
551
+ return
552
+ }
553
+
554
+ var request = URLRequest(url: serviceURL)
555
+ request.httpMethod = "POST"
556
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
557
+
558
+ let params: [String: Any] = [
559
+ "country": country,
560
+ "state": state
561
+ ]
562
+
563
+ do {
564
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
565
+ request.httpBody = jsonData
566
+ } catch {
567
+ print("Error creating JSON data: \(error)")
568
+ return
569
+ }
570
+
571
+ let session = URLSession.shared
572
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
573
+ if let error = error {
574
+ print("Error: \(error.localizedDescription)")
575
+ return
576
+ }
577
+
578
+ guard let httpResponse = serviceResponse as? HTTPURLResponse, httpResponse.statusCode == 200 else {
579
+ print("Invalid response or status code")
580
+ return
581
+ }
582
+
583
+ if let data = serviceData {
584
+ do {
585
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
586
+ let citiesArray = responseObject["data"] as? [String] { // Directly accessing city list array
587
+
588
+ DispatchQueue.main.async {
589
+ self.cityList = citiesArray.map { ["city_name": $0] } // Converting to expected format
590
+ self.tblViewCityList.reloadData()
591
+
592
+ // If no cities found, set state name in city field
593
+ if citiesArray.isEmpty {
594
+ self.txtFieldCity.text = self.txtFieldState.text
595
+ } else {
596
+ // Pick a random city and set it in txtFieldCity
597
+ if let randomCity = citiesArray.randomElement() {
598
+ self.txtFieldCity.text = randomCity
599
+ }
600
+ }
601
+ }
602
+ } else {
603
+ print("Error: Invalid JSON structure")
604
+ }
605
+ } catch {
606
+ print("Error parsing JSON: \(error)")
607
+ }
608
+ } else {
609
+ print("No data received")
610
+ }
611
+ }
612
+ task.resume()
613
+ }
614
+
615
+ func updateBillingInfoData() {
616
+ billingInfoData?["address"] = txtFieldAddress.text
617
+ billingInfoData?["country"] = txtFieldCountry.text
618
+ billingInfoData?["state"] = txtFieldState.text
619
+ billingInfoData?["city"] = txtFieldCity.text
620
+ billingInfoData?["postal_code"] = txtFieldPostalCode.text
621
+ }
622
+
623
+ @IBAction func actionBtnSelectCountry(_ sender: UIButton) {
624
+ UIView.animate(withDuration: 0.3) {
625
+ self.viewCountryList.isHidden.toggle()
626
+ }
627
+ }
628
+
629
+ @IBAction func actionBtnSelectState(_ sender: UIButton) {
630
+ UIView.animate(withDuration: 0.3) {
631
+ self.viewStateList.isHidden.toggle()
632
+ }
633
+ }
634
+
635
+ @IBAction func actionBtnSelectCity(_ sender: UIButton) {
636
+ UIView.animate(withDuration: 0.3) {
637
+ self.viewCityList.isHidden.toggle()
638
+ }
639
+ }
640
+
641
+ @IBAction func actionBtnPrevious(_ sender: UIButton) {
642
+ // Pass the text back to the previous screen
643
+ delegate?.didPassTextBack("NewAccount")
644
+ self.navigationController?.popViewController(animated: true)
645
+ }
646
+
647
+ @IBAction func actionBtnNext(_ sender: UIButton) {
648
+ let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
649
+ // Set properties on AdditionalInfoVC
650
+ vc.cardNumber = cardNumber
651
+ vc.expiryDate = expiryDate
652
+ vc.cvv = cvv
653
+ vc.nameOnCard = nameOnCard
654
+ updateBillingInfoData()
655
+ vc.billingInfoData = billingInfoData
656
+ // Pass the selected payment method
657
+ vc.selectedPaymentMethod = selectedPaymentMethod
658
+ vc.isSavedForFuture = isSavedForFuture
659
+
660
+ //Banking Case
661
+ vc.accountName = accountName
662
+ vc.routingNumber = routingNumber
663
+ vc.accountType = accountType
664
+ vc.accountNumber = accountNumber
665
+
666
+ vc.customerID = customerID
667
+ vc.accountID = accountID
668
+ vc.isFrom = isFrom
669
+
670
+ vc.isSavedNewAccount = isSavedNewAccount
671
+ vc.amount = amount
672
+
673
+ if isFrom == "SavedCards" {
674
+ vc.isFrom = "SavedCards"
675
+ vc.selectedCard = selectedCard // Passing the selected card data
676
+ vc.cvvText = cvvText // Passing the CVV text
677
+ vc.amount = amount
678
+ }
679
+
680
+ if isFrom == "AddNewCard" {
681
+ vc.isFrom = "AddNewCard"
682
+ vc.amount = amount
683
+ vc.isSavedNewCard = isSavedNewCard
684
+ }
685
+
686
+ self.navigationController?.pushViewController(vc, animated: true)
687
+ }
688
+
689
+ }
690
+
691
+ //MARK: - Table View
692
+ // @available(iOS 16.0, *)
693
+ extension BillingInfoVC: UITableViewDelegate, UITableViewDataSource {
694
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
695
+ if tableView == tblViewCountryList {
696
+ return countryList.count
697
+ }
698
+ else if tableView == tblViewStateList {
699
+ return stateList.count
700
+ }
701
+ else if tableView == tblViewCityList {
702
+ return cityList.count
703
+ }
704
+ else {
705
+ return 0
706
+ }
707
+ }
708
+
709
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
710
+ if tableView == tblViewCountryList {
711
+ let cell = tableView.dequeueReusableCell(withIdentifier: "CountryListTVC") as! CountryListTVC
712
+ let country = countryList[indexPath.row]
713
+ cell.lblCountryName.text = country["name"] as? String
714
+ return cell
715
+ } else if tableView == tblViewStateList {
716
+ let cell = tableView.dequeueReusableCell(withIdentifier: "StateListTVC") as! StateListTVC
717
+ let state = stateList[indexPath.row]
718
+ cell.lblStateName.text = state["name"] as? String
719
+ return cell
720
+ } else if tableView == tblViewCityList {
721
+ let cell = tableView.dequeueReusableCell(withIdentifier: "CityListTVC") as! CityListTVC
722
+ let city = cityList[indexPath.row]
723
+ cell.lblCityName.text = city["city_name"] as? String
724
+ return cell
725
+ } else {
726
+ return UITableViewCell()
727
+ }
728
+ }
729
+
730
+ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
731
+ if tableView == tblViewCountryList {
732
+ let selectedCountry = countryList[indexPath.row]["name"] as? String
733
+ txtFieldCountry.text = selectedCountry
734
+ viewCountryList.isHidden = true
735
+
736
+ // Fetch states for the selected country
737
+ if let country = selectedCountry {
738
+ getStateListApi(for: country)
739
+ }
740
+ } else if tableView == tblViewStateList {
741
+ let selectedState = stateList[indexPath.row]["name"] as? String
742
+ txtFieldState.text = selectedState
743
+ viewStateList.isHidden = true
744
+
745
+ // Fetch cities for the selected state and country
746
+ if let state = selectedState, let country = txtFieldCountry.text {
747
+ getCityListListApi(for: country, state: state)
748
+ }
749
+ } else if tableView == tblViewCityList {
750
+ let selectedCity = cityList[indexPath.row]["city_name"] as? String
751
+ txtFieldCity.text = selectedCity
752
+ viewCityList.isHidden = true
753
+
754
+ // Fetch the city list again based on selected state & country
755
+ if let state = txtFieldState.text, let country = txtFieldCountry.text {
756
+ getCityListListApi(for: country, state: state)
757
+ }
758
+ }
759
+
760
+ updateBillingInfoData()
761
+ }
762
+
763
+ func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
764
+ return 50
765
+ }
766
+
767
+ }
768
+
769
+ // @available(iOS 16.0, *)
770
+ extension BillingInfoVC: UITextFieldDelegate {
771
+ func textFieldShouldReturn(_ textField: UITextField) -> Bool {
772
+ textField.resignFirstResponder() // Dismiss the keyboard
773
+ return true
774
+ }
775
+
776
+ // Update billingInfoData when user finishes editing a field
777
+ func textFieldDidEndEditing(_ textField: UITextField) {
778
+ switch textField {
779
+ case txtFieldAddress:
780
+ billingInfoData?["address"] = textField.text
781
+ case txtFieldCountry:
782
+ billingInfoData?["country"] = textField.text
783
+ case txtFieldState:
784
+ billingInfoData?["state"] = textField.text
785
+ case txtFieldCity:
786
+ billingInfoData?["city"] = textField.text
787
+ case txtFieldPostalCode:
788
+ billingInfoData?["postal_code"] = textField.text
789
+ default:
790
+ break
791
+ }
792
+ }
793
+ }
794
+
795
+
796
+
797
+
798
+
799
+
800
+
801
+
802
+
803
+