@jimrising/easymerchantsdk-react-native 1.3.8 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.idea/caches/deviceStreaming.xml +714 -0
  2. package/.idea/em-MobileCheckoutSDK-ReactNative.iml +9 -0
  3. package/.idea/misc.xml +6 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/README.md +140 -81
  7. package/android/.gradle/8.10/checksums/checksums.lock +0 -0
  8. package/android/.gradle/8.10/checksums/md5-checksums.bin +0 -0
  9. package/android/.gradle/8.10/checksums/sha1-checksums.bin +0 -0
  10. package/android/.gradle/8.10/fileHashes/fileHashes.lock +0 -0
  11. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  12. package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
  13. package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
  14. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  15. package/android/.gradle/buildOutputCleanup/cache.properties +2 -2
  16. package/ios/Classes/EasyMerchantSdk.m +106 -55
  17. package/ios/Classes/EasyMerchantSdk.swift +232 -117
  18. package/ios/Classes/EasyPayViewController.swift +1 -1
  19. package/ios/CustomComponents/DatePickerHandler.swift +15 -4
  20. package/ios/EnvironmentConfig.swift +32 -30
  21. package/ios/Models/Request.swift +176 -14
  22. package/ios/Models/Result.swift +15 -35
  23. package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +851 -342
  24. package/ios/Pods/ViewControllers/BaseVC.swift +39 -35
  25. package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +1975 -170
  26. package/ios/Pods/ViewControllers/CountryListVC.swift +0 -1
  27. package/ios/Pods/ViewControllers/EmailVerificationVC.swift +74 -5
  28. package/ios/Pods/ViewControllers/GrailPayVC.swift +131 -107
  29. package/ios/Pods/ViewControllers/OTPVerificationVC.swift +305 -107
  30. package/ios/Pods/ViewControllers/PaymentDoneVC.swift +61 -14
  31. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +1277 -501
  32. package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +133 -2
  33. package/ios/easymerchantsdk.podspec +1 -1
  34. package/ios/easymerchantsdk.storyboard +713 -590
  35. package/package.json +2 -2
@@ -76,6 +76,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
76
76
  @IBOutlet weak var viewTxtFieldStartDateCard: UIView!
77
77
  @IBOutlet weak var imgViewChosePlanCardDropIcon: UIImageView!
78
78
  @IBOutlet weak var imgViewCalenderIconCard: UIImageView!
79
+ @IBOutlet weak var txtFieldEmailCardView: TextFieldStackView!
80
+ @IBOutlet weak var viewTxtFieldEmailCardView: UIView!
79
81
 
80
82
  @IBOutlet weak var btnScanCard: UIButton!
81
83
 
@@ -176,7 +178,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
176
178
  @IBOutlet weak var viewTextFieldConfirmAccount: UIView!
177
179
  @IBOutlet weak var txtFieldSelectPlanViewBankFields: TextFieldStackView!
178
180
  @IBOutlet weak var txtFieldSelectDateViewBankFields: TextFieldStackView!
181
+ @IBOutlet weak var txtFieldEmailAccountView: TextFieldStackView!
182
+ @IBOutlet weak var viewTxtFieldEmailAccountView: UIView!
179
183
 
184
+ @IBOutlet weak var viewSaveCardForFutureCardView: UIStackView!
180
185
  @IBOutlet weak var heightViewBankFields: NSLayoutConstraint!
181
186
 
182
187
  //GrailPay
@@ -199,6 +204,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
199
204
  @IBOutlet weak var txtFieldChosePlanGrailPayBankView: TextFieldStackView!
200
205
  @IBOutlet weak var txtFieldSelectDateGrailPayBankView: TextFieldStackView!
201
206
  @IBOutlet weak var bankIconGrailPayBankView: UIImageView!
207
+ @IBOutlet weak var viewSaveAccountForFutureGrailPayView: UIStackView!
202
208
 
203
209
  //New GrailPay Account
204
210
  @IBOutlet weak var viewAddNewGrailPayAccount: UIView!
@@ -223,6 +229,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
223
229
  @IBOutlet weak var txtFieldChosePlanNewGrailPayBankView: TextFieldStackView!
224
230
  @IBOutlet weak var txtFieldSelectDateNewGrailPayBankView: TextFieldStackView!
225
231
  @IBOutlet weak var imgViewBankIconGrailPayNewBank: UIImageView!
232
+ @IBOutlet weak var viewSaveAccountForFututeNewGrailPayAccountView: UIStackView!
226
233
 
227
234
  //SavedBank
228
235
  @IBOutlet weak var viewSingleSavedAccount: UIView!
@@ -265,6 +272,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
265
272
 
266
273
  @IBOutlet weak var heightViewNewAccountFields: NSLayoutConstraint!
267
274
  @IBOutlet weak var heightSubViewNewAccountFields: NSLayoutConstraint!
275
+ @IBOutlet weak var viewSaveAccountForFutureNewAccountView: UIStackView!
276
+
268
277
 
269
278
  //Crypto
270
279
  @IBOutlet weak var viewCrypto: UIView!
@@ -339,7 +348,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
339
348
  var selectedBank: BankAccountModel?
340
349
  var isSelectForPayBank: Bool = false
341
350
  var selectedBankIndex: Int? = nil
342
- var arrAccountType = ["Saving", "Checking"]
351
+ var arrAccountType = ["saving", "checking", "ledger"]
343
352
 
344
353
  var agreeTermsAndCondtition: Bool = false
345
354
 
@@ -353,7 +362,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
353
362
  var isFrom = String()
354
363
 
355
364
  //Blink Card
356
- // var blinkCardRecognizer: MBCBlinkCardRecognizer!
365
+ // var blinkCardRecognizer: MBCBlinkCardRecognizer!
357
366
 
358
367
  // Variables for Card Scanning Data Back
359
368
  var cardNumber: String?
@@ -388,6 +397,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
388
397
 
389
398
  var startDatePickerHandler: DatePickerHandler?
390
399
 
400
+ var isSavedNewCard: Bool = false
401
+
391
402
  //MARK: - View Did Load
392
403
  override func viewDidLoad() {
393
404
  super.viewDidLoad()
@@ -412,10 +423,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
412
423
 
413
424
  //// Check if billingInfoData is available
414
425
  if let billingInfoData = request.billingInfoData,
415
- let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
416
- let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
417
- // If `submitButtonText` is not empty, use it; otherwise, default to "Next (Billing Info)"
418
- let buttonText = (request?.submitButtonText?.isEmpty == false ? request!.submitButtonText! + " (Billing Info)" : "Next (Billing Info)")
426
+ let fieldSection = try? JSONDecoder().decode(FieldSection.self, from: billingInfoData) {
427
+
428
+ let billingVisible = fieldSection.visibility.billing
429
+ let suffix = billingVisible ? " (Billing Info)" : " (Additional Info)"
430
+ let buttonText = (request?.submitButtonText?.isEmpty == false
431
+ ? request!.submitButtonText! + suffix
432
+ : "Next" + suffix)
419
433
 
420
434
  btnNext.setTitle(buttonText, for: .normal)
421
435
  btnPayNowNewCardView.setTitle(buttonText, for: .normal)
@@ -541,9 +555,20 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
541
555
  self.txtFieldSelectDateGrailPayBankView.isHidden = false
542
556
  self.txtFieldSelectDateNewGrailPayBankView.isHidden = false
543
557
 
544
- self.heightViewBankFields.constant = 696
545
- self.heightViewNewCardFields.constant = 624
546
- self.heightViewNewAccountFields.constant = 808
558
+ if request.saveAccount == false {
559
+ self.heightViewBankFields.constant = 790
560
+ self.heightViewNewAccountFields.constant = 788
561
+ } else {
562
+ self.heightViewBankFields.constant = 836
563
+ self.heightViewNewAccountFields.constant = 844
564
+ }
565
+
566
+ if request.saveCard == false {
567
+ self.heightViewNewCardFields.constant = 595
568
+ } else {
569
+ self.heightViewNewCardFields.constant = 648
570
+ }
571
+
547
572
  self.heightSubViewNewAccountFields.constant = 738
548
573
  } else {
549
574
  self.txtFieldStartDateCard.isHidden = true
@@ -555,9 +580,20 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
555
580
  self.txtFieldSelectDateGrailPayBankView.isHidden = true
556
581
  self.txtFieldSelectDateNewGrailPayBankView.isHidden = true
557
582
 
558
- self.heightViewBankFields.constant = 600
559
- self.heightViewNewCardFields.constant = 529
560
- self.heightViewNewAccountFields.constant = 713
583
+ if request.saveAccount == false {
584
+ self.heightViewBankFields.constant = 700
585
+ self.heightViewNewAccountFields.constant = 690
586
+ } else {
587
+ self.heightViewBankFields.constant = 740
588
+ self.heightViewNewAccountFields.constant = 738
589
+ }
590
+
591
+ if request.saveCard == false {
592
+ self.heightViewNewCardFields.constant = 500
593
+ } else {
594
+ self.heightViewNewCardFields.constant = 553
595
+ }
596
+
561
597
  self.heightSubViewNewAccountFields.constant = 643
562
598
  }
563
599
 
@@ -587,9 +623,20 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
587
623
  self.txtFieldSelectDateGrailPayBankView.isHidden = true
588
624
  self.txtFieldSelectDateNewGrailPayBankView.isHidden = true
589
625
 
590
- self.heightViewBankFields.constant = 506
591
- self.heightViewNewCardFields.constant = 434
592
- self.heightViewNewAccountFields.constant = 628
626
+ if request.saveAccount == false {
627
+ self.heightViewBankFields.constant = 604
628
+ self.heightViewNewAccountFields.constant = 600
629
+ } else {
630
+ self.heightViewBankFields.constant = 644
631
+ self.heightViewNewAccountFields.constant = 660
632
+ }
633
+
634
+ if request.saveCard == false {
635
+ self.heightViewNewCardFields.constant = 404
636
+ } else {
637
+ self.heightViewNewCardFields.constant = 458
638
+ }
639
+
593
640
  self.heightSubViewNewAccountFields.constant = 558
594
641
  }
595
642
 
@@ -666,10 +713,24 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
666
713
  else {
667
714
  self.viewSingleSavedAccount.isHidden = true
668
715
  self.viewBankFields.isHidden = true
669
- self.viewBtnShowSavedCards.isHidden = false
670
- self.lblBtnShowSaveCard.text = "Show Saved Cards"
671
- self.viewBtnShowSavedCardHeight.constant = 50
672
- self.viewBtnShowSavedCardTopCon.constant = 20
716
+
717
+ // self.viewBtnShowSavedCards.isHidden = false
718
+ // self.lblBtnShowSaveCard.text = "Show Saved Cards"
719
+ // self.viewBtnShowSavedCardHeight.constant = 50
720
+ // self.viewBtnShowSavedCardTopCon.constant = 20
721
+
722
+ if request.saveCard == false {
723
+ self.viewBtnShowSavedCards.isHidden = true
724
+ self.viewBtnShowSavedCardHeight.constant = 0
725
+ self.viewBtnShowSavedCardTopCon.constant = 0
726
+ }
727
+ else {
728
+ self.viewBtnShowSavedCards.isHidden = false
729
+ self.lblBtnShowSaveCard.text = "Show Saved Cards"
730
+ self.viewBtnShowSavedCardHeight.constant = 50
731
+ self.viewBtnShowSavedCardTopCon.constant = 20
732
+ }
733
+
673
734
  self.OTPView.isHidden = true
674
735
  self.emailView.isHidden = true
675
736
  self.viewCardFields.isHidden = false
@@ -778,10 +839,23 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
778
839
  }
779
840
  else {
780
841
  // self.viewBankFields.isHidden = false
781
- self.viewBtnShowSavedCards.isHidden = false
782
- self.lblBtnShowSaveCard.text = "Show Saved Accounts"
783
- self.viewBtnShowSavedCardHeight.constant = 50
784
- self.viewBtnShowSavedCardTopCon.constant = 20
842
+ // self.viewBtnShowSavedCards.isHidden = false
843
+ // self.lblBtnShowSaveCard.text = "Show Saved Accounts"
844
+ // self.viewBtnShowSavedCardHeight.constant = 50
845
+ // self.viewBtnShowSavedCardTopCon.constant = 20
846
+
847
+ if request.saveAccount == false {
848
+ self.viewBtnShowSavedCards.isHidden = true
849
+ self.viewBtnShowSavedCardHeight.constant = 0
850
+ self.viewBtnShowSavedCardTopCon.constant = 0
851
+ }
852
+ else {
853
+ self.viewBtnShowSavedCards.isHidden = false
854
+ self.lblBtnShowSaveCard.text = "Show Saved Accounts"
855
+ self.viewBtnShowSavedCardHeight.constant = 50
856
+ self.viewBtnShowSavedCardTopCon.constant = 20
857
+ }
858
+
785
859
  self.OTPView.isHidden = true
786
860
  self.emailView.isHidden = true
787
861
  self.viewCardFields.isHidden = true
@@ -1161,6 +1235,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1161
1235
  cardExpiryTextField.textField.delegate = self
1162
1236
  cardCvvTextField.textField.delegate = self
1163
1237
  cardNameTextField.textField.delegate = self
1238
+ txtFieldEmailCardView.textField.delegate = self
1164
1239
 
1165
1240
  txtFieldEmail.delegate = self
1166
1241
 
@@ -1184,20 +1259,42 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1184
1259
  txtFieldAccountTypeNewAccountView.delegate = self
1185
1260
  txtFieldAccountNumber.delegate = self
1186
1261
  txtFieldConfirmBankAccount.delegate = self
1262
+ txtFieldEmailAccountView.textField.delegate = self
1187
1263
  }
1188
1264
 
1189
1265
  private func updateSaveButtons() {
1190
1266
  let isSavedCard = request?.saveCard ?? false
1191
1267
  let isSavedAccount = request?.saveAccount ?? false
1192
1268
 
1193
- let cardImageName = isSavedCard ? "checkmark.square.fill" : "square"
1194
- let accountImageName = isSavedAccount ? "checkmark.square.fill" : "square"
1269
+ let defaultImageName = "square" // always show unselected initially
1195
1270
 
1196
- btnCheckBoxSavedCard.setImage(UIImage(systemName: cardImageName), for: .normal)
1197
- btnSavedCardForFutureNewCardView.setImage(UIImage(systemName: cardImageName), for: .normal)
1271
+ // Card Buttons: Show/hide based on saveCard
1272
+ btnCheckBoxSavedCard.isHidden = !isSavedCard
1273
+ btnSavedCardForFutureNewCardView.isHidden = !isSavedCard
1198
1274
 
1199
- btnCheckSavedAccountForFuture.setImage(UIImage(systemName: accountImageName), for: .normal)
1200
- btnSavedNewAccountForFuture.setImage(UIImage(systemName: accountImageName), for: .normal)
1275
+ if isSavedCard {
1276
+ btnCheckBoxSavedCard.setImage(UIImage(systemName: defaultImageName), for: .normal)
1277
+ btnSavedCardForFutureNewCardView.setImage(UIImage(systemName: defaultImageName), for: .normal)
1278
+ }
1279
+
1280
+ // Account Buttons: Show/hide based on saveAccount
1281
+ let accountButtons: [UIButton] = [
1282
+ btnCheckBoxGrailPaySaveAccount,
1283
+ btnCheckBoxGrailPayNewAccountSaveAccount
1284
+ ]
1285
+
1286
+ for button in accountButtons {
1287
+ button.isHidden = !isSavedAccount
1288
+ if isSavedAccount {
1289
+ button.setImage(UIImage(systemName: defaultImageName), for: .normal)
1290
+ }
1291
+ }
1292
+
1293
+ // Other related UI changes for account
1294
+ viewSaveCardForFutureCardView?.isHidden = !isSavedAccount
1295
+ viewSaveAccountForFutureNewAccountView?.isHidden = !isSavedAccount
1296
+ viewSaveAccountForFutureGrailPayView?.isHidden = !isSavedAccount
1297
+ viewSaveAccountForFututeNewGrailPayAccountView?.isHidden = !isSavedAccount
1201
1298
  }
1202
1299
 
1203
1300
  //MARK: - Term & Conditions setup for normal bank fields view
@@ -1759,53 +1856,88 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1759
1856
  let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
1760
1857
  print("Billing Info Data: \(jsonDict)")
1761
1858
 
1762
- let cvvText = txtFieldCVVSingleSavedCard.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
1763
-
1764
- if cvvText.isEmpty {
1765
- self.showAlert(title: "Missing CVV", message: "Please enter the CVV to proceed.")
1766
- return
1767
- } else if cvvText.count < 3 {
1768
- self.showAlert(title: "Invalid CVV", message: "CVV must be at least 3 digits.")
1769
- return
1770
- }
1771
-
1772
- // Recurring Plan + Date Validation (only if is_recurring is true)
1773
- if let req = self.request, req.is_recurring == true {
1774
- if txtFieldSelectPlanSingleSavedCard.text.isEmpty {
1775
- self.showAlert(title: "Missing Information", message: "Please choose your plan.")
1776
- return
1777
- }
1859
+ do {
1860
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
1778
1861
 
1779
- if let recurringType = req.recurringStartDateType, recurringType == .custom {
1780
- if txtFieldSelectDateSingleSavedCard.text.isEmpty {
1781
- self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
1862
+ DispatchQueue.main.async {
1863
+ let cvvText = self.txtFieldCVVSingleSavedCard.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
1864
+
1865
+ if cvvText.isEmpty {
1866
+ self.showAlert(title: "Missing CVV", message: "Please enter the CVV to proceed.")
1867
+ return
1868
+ } else if cvvText.count < 3 {
1869
+ self.showAlert(title: "Invalid CVV", message: "CVV must be at least 3 digits.")
1782
1870
  return
1783
1871
  }
1872
+
1873
+ // Recurring Plan + Date Validation (only if is_recurring is true)
1874
+ if let req = self.request, req.is_recurring == true {
1875
+ if self.txtFieldSelectPlanSingleSavedCard.text.isEmpty {
1876
+ self.showAlert(title: "Missing Information", message: "Please choose your plan.")
1877
+ return
1878
+ }
1879
+
1880
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
1881
+ if self.txtFieldSelectDateSingleSavedCard.text.isEmpty {
1882
+ self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
1883
+ return
1884
+ }
1885
+ }
1886
+ }
1887
+
1888
+ // Redirect based on billing visibility
1889
+ if fieldSection.visibility.billing {
1890
+ // Instantiate BillingInfoVC and pass the selected card data and CVV text
1891
+ let billingInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
1892
+
1893
+ billingInfoVC.isFrom = "SavedCards"
1894
+ billingInfoVC.selectedCard = self.selectedCard // Passing the selected card data
1895
+ billingInfoVC.cvvText = cvvText // Passing the CVV text
1896
+ billingInfoVC.amount = Int(self.request.amount ?? 0)
1897
+ billingInfoVC.selectedPaymentMethod = self.selectedPaymentMethod
1898
+
1899
+ if let billingInfoData = self.request.billingInfoData {
1900
+ billingInfoVC.billingInfoData = billingInfoData
1901
+ }
1902
+
1903
+ billingInfoVC.request = self.request
1904
+ billingInfoVC.chosenPlan = self.txtFieldSelectPlanSingleSavedCard.text
1905
+ billingInfoVC.startDate = self.txtFieldSelectDateSingleSavedCard.text
1906
+
1907
+ billingInfoVC.billingInfo = fieldSection.billing
1908
+ billingInfoVC.additionalInfo = fieldSection.additional
1909
+ billingInfoVC.visibility = fieldSection.visibility
1910
+
1911
+ // Navigate to BillingInfoVC
1912
+ self.navigationController?.pushViewController(billingInfoVC, animated: true)
1913
+ }
1914
+ else {
1915
+ let additionalInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
1916
+ additionalInfoVC.isFrom = "SavedCards"
1917
+ additionalInfoVC.selectedCard = self.selectedCard // Passing the selected card data
1918
+ additionalInfoVC.cvvText = cvvText // Passing the CVV text
1919
+ additionalInfoVC.amount = Int(self.request.amount ?? 0)
1920
+ additionalInfoVC.selectedPaymentMethod = self.selectedPaymentMethod
1921
+
1922
+ if let billingInfoData = self.request.billingInfoData {
1923
+ additionalInfoVC.billingInfoData = billingInfoData
1924
+ }
1925
+
1926
+ additionalInfoVC.request = self.request
1927
+ additionalInfoVC.chosenPlan = self.txtFieldSelectPlanSingleSavedCard.text
1928
+ additionalInfoVC.startDate = self.txtFieldSelectDateSingleSavedCard.text
1929
+
1930
+ additionalInfoVC.billingInfo = fieldSection.billing
1931
+ additionalInfoVC.additionalInfo = fieldSection.additional
1932
+ additionalInfoVC.visibility = fieldSection.visibility
1933
+
1934
+ self.navigationController?.pushViewController(additionalInfoVC, animated: true)
1935
+ }
1784
1936
  }
1785
1937
  }
1786
-
1787
- // Instantiate BillingInfoVC and pass the selected card data and CVV text
1788
- let billingInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
1789
-
1790
- billingInfoVC.isFrom = "SavedCards"
1791
- billingInfoVC.selectedCard = selectedCard // Passing the selected card data
1792
- billingInfoVC.cvvText = cvvText // Passing the CVV text
1793
- billingInfoVC.amount = Int(request.amount)
1794
- billingInfoVC.selectedPaymentMethod = selectedPaymentMethod
1795
-
1796
- // Pass the billing info data and auth token
1797
- if let billingInfoData = request.billingInfoData,
1798
- let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
1799
- let jsonDict = json as? [String: Any] {
1800
- billingInfoVC.billingInfoData = jsonDict
1938
+ catch {
1939
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
1801
1940
  }
1802
-
1803
- billingInfoVC.request = self.request
1804
- billingInfoVC.chosenPlan = txtFieldSelectPlanSingleSavedCard.text
1805
- billingInfoVC.startDate = txtFieldSelectDateSingleSavedCard.text
1806
-
1807
- // Navigate to BillingInfoVC
1808
- self.navigationController?.pushViewController(billingInfoVC, animated: true)
1809
1941
  }
1810
1942
  else {
1811
1943
  // If billingInfoData is nil or empty, set the button title to "Pay Now"
@@ -1886,99 +2018,140 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1886
2018
  let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
1887
2019
  print("Billing Info Data: \(jsonDict)")
1888
2020
 
1889
- // Retrieve and trim text field values (removing leading and trailing whitespace)
1890
- let cardNumber = txtFieldCardNumberNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
1891
- let expiryDate = txtFieldExpiryDateNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
1892
- let cvv = txtFieldCVVNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
1893
- let nameOnCard = txtFieldNameOnCardNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
1894
-
1895
- // Check if any field is empty
1896
- if cardNumber.isEmpty || expiryDate.isEmpty || cvv.isEmpty || nameOnCard.isEmpty {
1897
- let alert = UIAlertController(title: "Missing Information", message: "Please fill in all card details.", preferredStyle: .alert)
1898
- alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
1899
- self.present(alert, animated: true, completion: nil)
1900
- return
1901
- }
1902
-
1903
- // Validate expiry date format
1904
- let expiryDateFormat = "MM/yyyy"
1905
- let dateFormatter = DateFormatter()
1906
- dateFormatter.dateFormat = expiryDateFormat
1907
- dateFormatter.locale = Locale(identifier: "en_US_POSIX")
1908
-
1909
- // Split the expiry date and validate its format
1910
- let exp = expiryDate.split(separator: "/")
1911
- if exp.count != 2 || exp[0].count != 2 || exp[1].count != 4 {
1912
- let alert = UIAlertController(title: "Invalid Expiry Date", message: "Please enter the expiry date in the format MM/yyyy.", preferredStyle: .alert)
1913
- alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
1914
- self.present(alert, animated: true, completion: nil)
1915
- return
1916
- }
1917
-
1918
- // Check if the expiry date is valid
1919
- if let expiryDateObj = dateFormatter.date(from: expiryDate) {
1920
- // Check if the expiry date is in the past
1921
- let currentDate = Date()
1922
- let calendar = Calendar.current
1923
- let currentMonthYear = calendar.date(from: calendar.dateComponents([.year, .month], from: currentDate))
1924
- if expiryDateObj < currentMonthYear! {
1925
- let alert = UIAlertController(title: "Expired Card", message: "The expiry date cannot be in the past. Please enter a valid expiry date.", preferredStyle: .alert)
1926
- alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
1927
- self.present(alert, animated: true, completion: nil)
1928
- return
1929
- }
1930
- } else {
1931
- let alert = UIAlertController(title: "Invalid Expiry Date", message: "Please enter the expiry date in the format MM/yyyy.", preferredStyle: .alert)
1932
- alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
1933
- self.present(alert, animated: true, completion: nil)
1934
- return
1935
- }
1936
-
1937
- // Recurring Plan + Date Validation (only if is_recurring is true)
1938
- if let req = self.request, req.is_recurring == true {
1939
- if txtFieldSelectPlanNewCardView.text.isEmpty {
1940
- self.showAlert(title: "Missing Information", message: "Please choose your plan.")
1941
- return
1942
- }
2021
+ do {
2022
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
1943
2023
 
1944
- if let recurringType = req.recurringStartDateType, recurringType == .custom {
1945
- if txtFieldSelectDateNewCardView.text.isEmpty {
1946
- self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
2024
+ DispatchQueue.main.async {
2025
+ // Retrieve and trim text field values (removing leading and trailing whitespace)
2026
+ let cardNumber = self.txtFieldCardNumberNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2027
+ let expiryDate = self.txtFieldExpiryDateNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2028
+ let cvv = self.txtFieldCVVNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2029
+ let nameOnCard = self.txtFieldNameOnCardNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2030
+
2031
+ // Check if any field is empty
2032
+ if cardNumber.isEmpty || expiryDate.isEmpty || cvv.isEmpty || nameOnCard.isEmpty {
2033
+ let alert = UIAlertController(title: "Missing Information", message: "Please fill in all card details.", preferredStyle: .alert)
2034
+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
2035
+ self.present(alert, animated: true, completion: nil)
2036
+ return
2037
+ }
2038
+
2039
+ // Validate expiry date format
2040
+ let expiryDateFormat = "MM/yyyy"
2041
+ let dateFormatter = DateFormatter()
2042
+ dateFormatter.dateFormat = expiryDateFormat
2043
+ dateFormatter.locale = Locale(identifier: "en_US_POSIX")
2044
+
2045
+ // Split the expiry date and validate its format
2046
+ let exp = expiryDate.split(separator: "/")
2047
+ if exp.count != 2 || exp[0].count != 2 || exp[1].count != 4 {
2048
+ let alert = UIAlertController(title: "Invalid Expiry Date", message: "Please enter the expiry date in the format MM/yyyy.", preferredStyle: .alert)
2049
+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
2050
+ self.present(alert, animated: true, completion: nil)
2051
+ return
2052
+ }
2053
+
2054
+ // Check if the expiry date is valid
2055
+ if let expiryDateObj = dateFormatter.date(from: expiryDate) {
2056
+ // Check if the expiry date is in the past
2057
+ let currentDate = Date()
2058
+ let calendar = Calendar.current
2059
+ let currentMonthYear = calendar.date(from: calendar.dateComponents([.year, .month], from: currentDate))
2060
+ if expiryDateObj < currentMonthYear! {
2061
+ let alert = UIAlertController(title: "Expired Card", message: "The expiry date cannot be in the past. Please enter a valid expiry date.", preferredStyle: .alert)
2062
+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
2063
+ self.present(alert, animated: true, completion: nil)
2064
+ return
2065
+ }
2066
+ } else {
2067
+ let alert = UIAlertController(title: "Invalid Expiry Date", message: "Please enter the expiry date in the format MM/yyyy.", preferredStyle: .alert)
2068
+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
2069
+ self.present(alert, animated: true, completion: nil)
1947
2070
  return
1948
2071
  }
2072
+
2073
+ // Recurring Plan + Date Validation (only if is_recurring is true)
2074
+ if let req = self.request, req.is_recurring == true {
2075
+ if self.txtFieldSelectPlanNewCardView.text.isEmpty {
2076
+ self.showAlert(title: "Missing Information", message: "Please choose your plan.")
2077
+ return
2078
+ }
2079
+
2080
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
2081
+ if self.txtFieldSelectDateNewCardView.text.isEmpty {
2082
+ self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
2083
+ return
2084
+ }
2085
+ }
2086
+ }
2087
+
2088
+ // Redirect based on billing visibility
2089
+ if fieldSection.visibility.billing {
2090
+ // Instantiate BillingInfoVC and pass the card details
2091
+ let billingInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2092
+
2093
+ // Pass the card details
2094
+ billingInfoVC.cardNumber = cardNumber
2095
+ billingInfoVC.expiryDate = expiryDate
2096
+ billingInfoVC.cvv = cvv
2097
+ billingInfoVC.nameOnCard = nameOnCard
2098
+ billingInfoVC.isSavedNewCard = self.isSavedNewCardForFuture
2099
+
2100
+ billingInfoVC.isFrom = "AddNewCard"
2101
+ billingInfoVC.amount = Int(self.request.amount ?? 0)
2102
+ billingInfoVC.selectedPaymentMethod = self.selectedPaymentMethod
2103
+
2104
+ if let billingInfoData = self.request.billingInfoData {
2105
+ billingInfoVC.billingInfoData = billingInfoData
2106
+ }
2107
+
2108
+ billingInfoVC.request = self.request
2109
+ billingInfoVC.chosenPlan = self.txtFieldSelectPlanNewCardView.text
2110
+ billingInfoVC.startDate = self.txtFieldSelectDateNewCardView.text
2111
+
2112
+ billingInfoVC.billingInfo = fieldSection.billing
2113
+ billingInfoVC.additionalInfo = fieldSection.additional
2114
+ billingInfoVC.visibility = fieldSection.visibility
2115
+
2116
+ // Navigate to BillingInfoVC
2117
+ self.navigationController?.pushViewController(billingInfoVC, animated: true)
2118
+ }
2119
+ else {
2120
+ let additionalInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
2121
+
2122
+ // Pass the card details
2123
+ additionalInfoVC.cardNumber = cardNumber
2124
+ additionalInfoVC.expiryDate = expiryDate
2125
+ additionalInfoVC.cvv = cvv
2126
+ additionalInfoVC.nameOnCard = nameOnCard
2127
+ additionalInfoVC.isSavedNewCard = self.isSavedNewCardForFuture
2128
+
2129
+ additionalInfoVC.isFrom = "AddNewCard"
2130
+ additionalInfoVC.amount = Int(self.request.amount ?? 0)
2131
+ additionalInfoVC.selectedPaymentMethod = self.selectedPaymentMethod
2132
+
2133
+ if let billingInfoData = self.request.billingInfoData {
2134
+ additionalInfoVC.billingInfoData = billingInfoData
2135
+ }
2136
+
2137
+ additionalInfoVC.request = self.request
2138
+ additionalInfoVC.chosenPlan = self.txtFieldSelectPlanNewCardView.text
2139
+ additionalInfoVC.startDate = self.txtFieldSelectDateNewCardView.text
2140
+
2141
+ additionalInfoVC.billingInfo = fieldSection.billing
2142
+ additionalInfoVC.additionalInfo = fieldSection.additional
2143
+ additionalInfoVC.visibility = fieldSection.visibility
2144
+
2145
+ self.navigationController?.pushViewController(additionalInfoVC, animated: true)
2146
+ }
1949
2147
  }
1950
2148
  }
1951
-
1952
- // Instantiate BillingInfoVC and pass the card details
1953
- let billingInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
1954
-
1955
- // Pass the card details
1956
- billingInfoVC.cardNumber = cardNumber
1957
- billingInfoVC.expiryDate = expiryDate
1958
- billingInfoVC.cvv = cvv
1959
- billingInfoVC.nameOnCard = nameOnCard
1960
- billingInfoVC.isSavedNewCard = isSavedNewCardForFuture
1961
-
1962
- billingInfoVC.isFrom = "AddNewCard"
1963
- billingInfoVC.amount = Int(request.amount)
1964
- billingInfoVC.selectedPaymentMethod = selectedPaymentMethod
1965
-
1966
- // Pass the billing info data and auth token
1967
- if let billingInfoData = request.billingInfoData,
1968
- let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
1969
- let jsonDict = json as? [String: Any] {
1970
- billingInfoVC.billingInfoData = jsonDict
2149
+ catch {
2150
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
1971
2151
  }
1972
-
1973
- billingInfoVC.request = self.request
1974
- billingInfoVC.chosenPlan = txtFieldSelectPlanNewCardView.text
1975
- billingInfoVC.startDate = txtFieldSelectDateNewCardView.text
1976
-
1977
- // Navigate to BillingInfoVC
1978
- self.navigationController?.pushViewController(billingInfoVC, animated: true)
1979
2152
  }
1980
2153
  else {
1981
- if request.enable3DS == true {
2154
+ if request.secureAuthentication == true {
1982
2155
  threeDSecurePaymentNewCardApi(customerId: UserStoreSingleton.shared.customerId ?? "")
1983
2156
  }
1984
2157
  else {
@@ -2397,24 +2570,146 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2397
2570
  }
2398
2571
 
2399
2572
  guard agreeTermsAndCondtition else {
2400
- self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
2573
+ self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions.")
2401
2574
  return
2402
2575
  }
2403
2576
 
2404
2577
  if isSavedForFuture {
2405
- // Navigate to next screen without calling the API
2406
- let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
2407
- vc.selectedPaymentMethod = "GrailPay"
2408
- vc.easyPayDelegate = self.easyPayDelegate
2409
- vc.grailPayAccountID = self.grailPayAccountID
2410
- vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
2411
- vc.selectedGrailPayAccountName = self.selectedGrailPayAccountName
2412
- self.navigationController?.pushViewController(vc, animated: true)
2578
+ //If billing info is available
2579
+ if let billingInfoData = request.billingInfoData {
2580
+ do {
2581
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
2582
+
2583
+ DispatchQueue.main.async {
2584
+
2585
+ if let billingInfoData = self.request.billingInfoData,
2586
+ let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
2587
+ let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
2588
+ print("Billing Info Data: \(jsonDict)")
2589
+
2590
+ // Redirect based on billing visibility
2591
+ if fieldSection.visibility.billing {
2592
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2593
+ vc.billingInfoData = billingInfoData
2594
+ vc.request = self.request
2595
+ vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
2596
+ vc.startDate = self.txtFieldSelectDateGrailPayBankView.text
2597
+ vc.selectedPaymentMethod = "GrailPay"
2598
+ vc.isSavedForFuture = self.isSavedForFuture
2599
+ vc.grailPayAccountID = self.grailPayAccountID
2600
+ vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
2601
+ vc.selectedGrailPayAccountName = self.selectedGrailPayAccountName
2602
+ vc.amount = self.amount
2603
+ vc.billingInfo = fieldSection.billing
2604
+ vc.additionalInfo = fieldSection.additional
2605
+ vc.visibility = fieldSection.visibility
2606
+ vc.easyPayDelegate = self.easyPayDelegate
2607
+ self.navigationController?.pushViewController(vc, animated: true)
2608
+ }
2609
+ else {
2610
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
2611
+ vc.billingInfoData = billingInfoData
2612
+ vc.request = self.request
2613
+ vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
2614
+ vc.startDate = self.txtFieldSelectDateGrailPayBankView.text
2615
+ vc.selectedPaymentMethod = "GrailPay"
2616
+ vc.isSavedForFuture = self.isSavedForFuture
2617
+ vc.grailPayAccountID = self.grailPayAccountID
2618
+ vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
2619
+ vc.selectedGrailPayAccountName = self.selectedGrailPayAccountName
2620
+ vc.amount = self.amount
2621
+ vc.billingInfo = fieldSection.billing
2622
+ vc.additionalInfo = fieldSection.additional
2623
+ vc.visibility = fieldSection.visibility
2624
+ vc.easyPayDelegate = self.easyPayDelegate
2625
+ self.navigationController?.pushViewController(vc, animated: true)
2626
+ }
2627
+ }
2628
+ }
2629
+ }
2630
+ catch {
2631
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
2632
+ }
2633
+ }
2634
+ else {
2635
+ //If billing info is nil
2636
+ // Navigate to EmailVerificationVC directly
2637
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
2638
+ vc.easyPayDelegate = self.easyPayDelegate
2639
+ vc.grailPayAccountID = self.grailPayAccountID
2640
+ vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
2641
+ vc.selectedGrailPayAccountName = self.selectedGrailPayAccountName
2642
+ vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
2643
+ vc.startDate = self.txtFieldSelectDateGrailPayBankView.text
2644
+ vc.request = self.request
2645
+ vc.isSavedForFuture = self.isSavedForFuture
2646
+ vc.selectedPaymentMethod = "GrailPay"
2647
+ self.navigationController?.pushViewController(vc, animated: true)
2648
+ }
2413
2649
  } else {
2414
- // Proceed to call API if not saving for future
2415
- grailPayAccountChargeApi()
2650
+ // Check if billing info data exists and is valid JSON without save
2651
+ if let billingInfoData = request.billingInfoData {
2652
+ do {
2653
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
2654
+
2655
+ DispatchQueue.main.async {
2656
+
2657
+ if let billingInfoData = self.request.billingInfoData,
2658
+ let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
2659
+ let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
2660
+ print("Billing Info Data: \(jsonDict)")
2661
+
2662
+ // Redirect based on billing visibility
2663
+ if fieldSection.visibility.billing {
2664
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2665
+ vc.billingInfoData = billingInfoData
2666
+ vc.request = self.request
2667
+ vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
2668
+ vc.startDate = self.txtFieldSelectDateGrailPayBankView.text
2669
+ vc.selectedPaymentMethod = "GrailPay"
2670
+ vc.isSavedForFuture = self.isSavedForFuture
2671
+ vc.grailPayAccountID = self.grailPayAccountID
2672
+ vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
2673
+ vc.selectedGrailPayAccountName = self.selectedGrailPayAccountName
2674
+ vc.amount = self.amount
2675
+ vc.billingInfo = fieldSection.billing
2676
+ vc.additionalInfo = fieldSection.additional
2677
+ vc.visibility = fieldSection.visibility
2678
+ vc.easyPayDelegate = self.easyPayDelegate
2679
+ self.navigationController?.pushViewController(vc, animated: true)
2680
+ }
2681
+ else {
2682
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
2683
+ vc.billingInfoData = billingInfoData
2684
+ vc.request = self.request
2685
+ vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
2686
+ vc.startDate = self.txtFieldSelectDateGrailPayBankView.text
2687
+ vc.selectedPaymentMethod = "GrailPay"
2688
+ vc.isSavedForFuture = self.isSavedForFuture
2689
+ vc.grailPayAccountID = self.grailPayAccountID
2690
+ vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
2691
+ vc.selectedGrailPayAccountName = self.selectedGrailPayAccountName
2692
+ vc.amount = self.amount
2693
+ vc.billingInfo = fieldSection.billing
2694
+ vc.additionalInfo = fieldSection.additional
2695
+ vc.visibility = fieldSection.visibility
2696
+ vc.easyPayDelegate = self.easyPayDelegate
2697
+ self.navigationController?.pushViewController(vc, animated: true)
2698
+ }
2699
+ }
2700
+ }
2701
+ }
2702
+ catch {
2703
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
2704
+ }
2705
+ }
2706
+ else {
2707
+ // Proceed to call API if billing info not available
2708
+ grailPayAccountChargeApi()
2709
+ }
2416
2710
  }
2417
2711
  } else {
2712
+ // Launch GrailPay account linking
2418
2713
  grailPaySource = .existingAccount
2419
2714
  launchGrailPay()
2420
2715
  }
@@ -2443,10 +2738,124 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2443
2738
  }
2444
2739
 
2445
2740
  if isSavedForFuture {
2446
- grailPayNewAccountSavingChargeApi()
2741
+ //If billing info is available
2742
+ if let billingInfoData = request.billingInfoData {
2743
+ do {
2744
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
2745
+
2746
+ DispatchQueue.main.async {
2747
+
2748
+ if let billingInfoData = self.request.billingInfoData,
2749
+ let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
2750
+ let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
2751
+ print("Billing Info Data: \(jsonDict)")
2752
+
2753
+ // Redirect based on billing visibility
2754
+ if fieldSection.visibility.billing {
2755
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2756
+ vc.billingInfoData = billingInfoData
2757
+ vc.request = self.request
2758
+ vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
2759
+ vc.startDate = self.txtFieldSelectDateNewGrailPayBankView.text
2760
+ vc.selectedPaymentMethod = "NewGrailPayAccount"
2761
+ vc.isSavedForFuture = self.isSavedForFuture
2762
+ vc.grailPayAccountID = self.newGrailPayAccountID
2763
+ vc.selectedGrailPayAccountType = self.selectedNewGrailPayAccountType
2764
+ vc.selectedGrailPayAccountName = self.selectedNewGrailPayAccountName
2765
+ vc.amount = self.amount
2766
+ vc.billingInfo = fieldSection.billing
2767
+ vc.additionalInfo = fieldSection.additional
2768
+ vc.visibility = fieldSection.visibility
2769
+ vc.easyPayDelegate = self.easyPayDelegate
2770
+ self.navigationController?.pushViewController(vc, animated: true)
2771
+ }
2772
+ else {
2773
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
2774
+ vc.billingInfoData = billingInfoData
2775
+ vc.request = self.request
2776
+ vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
2777
+ vc.startDate = self.txtFieldSelectDateNewGrailPayBankView.text
2778
+ vc.selectedPaymentMethod = "NewGrailPayAccount"
2779
+ vc.isSavedForFuture = self.isSavedForFuture
2780
+ vc.grailPayAccountID = self.newGrailPayAccountID
2781
+ vc.selectedGrailPayAccountType = self.selectedNewGrailPayAccountType
2782
+ vc.selectedGrailPayAccountName = self.selectedNewGrailPayAccountName
2783
+ vc.amount = self.amount
2784
+ vc.billingInfo = fieldSection.billing
2785
+ vc.additionalInfo = fieldSection.additional
2786
+ vc.visibility = fieldSection.visibility
2787
+ vc.easyPayDelegate = self.easyPayDelegate
2788
+ self.navigationController?.pushViewController(vc, animated: true)
2789
+ }
2790
+ }
2791
+ }
2792
+ }
2793
+ catch {
2794
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
2795
+ }
2796
+ }
2447
2797
  }
2448
2798
  else {
2449
- grailPayNewAccountChargeApi()
2799
+ // Check if billing info data exists and is valid JSON without save
2800
+ if let billingInfoData = request.billingInfoData {
2801
+ do {
2802
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
2803
+
2804
+ DispatchQueue.main.async {
2805
+
2806
+ if let billingInfoData = self.request.billingInfoData,
2807
+ let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
2808
+ let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
2809
+ print("Billing Info Data: \(jsonDict)")
2810
+
2811
+ // Redirect based on billing visibility
2812
+ if fieldSection.visibility.billing {
2813
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2814
+ vc.billingInfoData = billingInfoData
2815
+ vc.request = self.request
2816
+ vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
2817
+ vc.startDate = self.txtFieldSelectDateNewGrailPayBankView.text
2818
+ vc.selectedPaymentMethod = "NewGrailPayAccount"
2819
+ vc.isSavedForFuture = self.isSavedForFuture
2820
+ vc.grailPayAccountID = self.newGrailPayAccountID
2821
+ vc.selectedGrailPayAccountType = self.selectedNewGrailPayAccountType
2822
+ vc.selectedGrailPayAccountName = self.selectedNewGrailPayAccountName
2823
+ vc.amount = self.amount
2824
+ vc.billingInfo = fieldSection.billing
2825
+ vc.additionalInfo = fieldSection.additional
2826
+ vc.visibility = fieldSection.visibility
2827
+ vc.easyPayDelegate = self.easyPayDelegate
2828
+ self.navigationController?.pushViewController(vc, animated: true)
2829
+ }
2830
+ else {
2831
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
2832
+ vc.billingInfoData = billingInfoData
2833
+ vc.request = self.request
2834
+ vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
2835
+ vc.startDate = self.txtFieldSelectDateNewGrailPayBankView.text
2836
+ vc.selectedPaymentMethod = "NewGrailPayAccount"
2837
+ vc.isSavedForFuture = self.isSavedForFuture
2838
+ vc.grailPayAccountID = self.newGrailPayAccountID
2839
+ vc.selectedGrailPayAccountType = self.selectedNewGrailPayAccountType
2840
+ vc.selectedGrailPayAccountName = self.selectedNewGrailPayAccountName
2841
+ vc.amount = self.amount
2842
+ vc.billingInfo = fieldSection.billing
2843
+ vc.additionalInfo = fieldSection.additional
2844
+ vc.visibility = fieldSection.visibility
2845
+ vc.easyPayDelegate = self.easyPayDelegate
2846
+ self.navigationController?.pushViewController(vc, animated: true)
2847
+ }
2848
+ }
2849
+ }
2850
+ }
2851
+ catch {
2852
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
2853
+ }
2854
+ }
2855
+ else {
2856
+ // Proceed to call API if billing info not available
2857
+ grailPayNewAccountChargeApi()
2858
+ }
2450
2859
  }
2451
2860
  } else {
2452
2861
  grailPaySource = .newAccount
@@ -2467,30 +2876,30 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2467
2876
  case .success:
2468
2877
  if let chargeData = result.chargeData,
2469
2878
  let dataArray = chargeData["data"] as? [[String: Any]],
2470
- let firstAccount = dataArray.first {
2879
+ let selectedAccount = dataArray.first {
2471
2880
 
2472
- print("✅ Bank connected: \(firstAccount)")
2473
- self.selectedGrailPayAccountType = firstAccount["account_type"] as? String
2474
- self.selectedGrailPayAccountName = firstAccount["name"] as? String
2881
+ print("✅ Bank connected: \(selectedAccount)")
2882
+ self.selectedGrailPayAccountType = selectedAccount["account_type"] as? String
2883
+ self.selectedGrailPayAccountName = selectedAccount["name"] as? String
2475
2884
 
2476
2885
  if self.grailPaySource == .existingAccount {
2477
- self.accountConnectApi(account: firstAccount)
2886
+ self.accountConnectApi(account: selectedAccount)
2478
2887
  self.lblGrailPayAabandonError.text = "Default account successfully set"
2479
2888
  self.viewGrailPayAbandon.isHidden = false
2480
2889
  } else if self.grailPaySource == .newAccount {
2481
- self.newGrailPayAccountConnectApi(account: firstAccount)
2890
+ self.newGrailPayAccountConnectApi(account: selectedAccount)
2482
2891
  self.lblAbandonGrailPayNewAccountView.text = "Default account successfully set"
2483
2892
  self.viewAbandonGrailPayNewAccountView.isHidden = false
2484
2893
 
2485
- self.selectedNewGrailPayAccountType = firstAccount["account_type"] as? String
2486
- self.selectedNewGrailPayAccountName = firstAccount["name"] as? String
2894
+ self.selectedNewGrailPayAccountType = selectedAccount["account_type"] as? String
2895
+ self.selectedNewGrailPayAccountName = selectedAccount["name"] as? String
2487
2896
  }
2488
2897
 
2489
2898
  if self.selectedPaymentMethod == "Bank" {
2490
2899
  self.viewBtnShowSavedCards.isHidden = true
2491
2900
  }
2492
2901
 
2493
- let amountText = String(format: "$%.2f", self.request.amount)
2902
+ let amountText = String(format: "$%.2f", self.request.amount ?? 0)
2494
2903
  let submitText = self.request.submitButtonText
2495
2904
  let defaultTitle = (submitText?.isEmpty == false)
2496
2905
  ? "\(submitText!) (\(amountText))"
@@ -2570,7 +2979,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2570
2979
  "account_type": self.selectedGrailPayAccountType ?? "",
2571
2980
  "name": self.selectedGrailPayAccountName ?? "",
2572
2981
  "description": "payment checkout",
2573
- "email": "newmerchantadminuser12@test.com"
2982
+ "email": UserStoreSingleton.shared.merchantEmail ?? ""
2574
2983
  ]
2575
2984
 
2576
2985
  // Add these if recurring is enabled
@@ -2643,7 +3052,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2643
3052
  paymentDoneVC.chargeData = responseObject
2644
3053
  // Pass the selected payment method
2645
3054
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
2646
- paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
3055
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
3056
+ // Pass billingInfo and additionalInfo
3057
+ if let billingData = self.request.billingInfoData,
3058
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
3059
+
3060
+ // Extract main billing fields
3061
+ let cleanBillingInfo: [String: Any] = [
3062
+ "postal_code": billingInfoDict["postal_code"] ?? "",
3063
+ "country": billingInfoDict["country"] ?? "",
3064
+ "city": billingInfoDict["city"] ?? "",
3065
+ "address": billingInfoDict["address"] ?? "",
3066
+ "state": billingInfoDict["state"] ?? ""
3067
+ ]
3068
+ paymentDoneVC.billingInfo = cleanBillingInfo
3069
+
3070
+ // Extract additional_info
3071
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
3072
+ let cleanAdditionalInfo: [String: Any] = [
3073
+ "email": additional["email"] ?? "",
3074
+ "phone_number": additional["phone_number"] ?? "",
3075
+ "name": additional["name"] ?? "",
3076
+ "country_code": additional["country_code"] ?? ""
3077
+ ]
3078
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
3079
+ }
3080
+ }
2647
3081
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
2648
3082
  }
2649
3083
  }
@@ -2774,140 +3208,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2774
3208
  paymentDoneVC.chargeData = responseObject
2775
3209
  // Pass the selected payment method
2776
3210
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
2777
- paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
2778
- self.navigationController?.pushViewController(paymentDoneVC, animated: true)
2779
- }
2780
- }
2781
- }
2782
- } else {
2783
- self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
2784
- }
2785
- } catch let jsonError {
2786
- self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
2787
- }
2788
- } else {
2789
- self.presentPaymentErrorVC(errorMessage: "No data received")
2790
- }
2791
- } else {
2792
- if let data = serviceData,
2793
- let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
2794
- let message = responseObj["message"] as? String {
2795
- self.presentPaymentErrorVC(errorMessage: message)
2796
- } else {
2797
- self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
2798
- }
2799
- }
2800
- }
2801
- task.resume()
2802
- }
2803
-
2804
- //MARK: - GrailPay New Account Banking Payment Charge Api with saving bank.
2805
- func grailPayNewAccountSavingChargeApi() {
2806
- showLoadingIndicator()
2807
- let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
2808
-
2809
- guard let serviceURL = URL(string: fullURL) else {
2810
- print("Invalid URL")
2811
- hideLoadingIndicator()
2812
- return
2813
- }
2814
-
2815
- var uRLRequest = URLRequest(url: serviceURL)
2816
- uRLRequest.httpMethod = "POST"
2817
- uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
2818
-
2819
- let token = UserStoreSingleton.shared.clientToken
2820
- print("Setting clientToken header: \(token ?? "None")")
2821
- uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2822
-
2823
- if let apiKey = EnvironmentConfig.apiKey {
2824
- uRLRequest.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
2825
- }
2826
- if let apiSecret = EnvironmentConfig.apiSecret {
2827
- uRLRequest.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
2828
- }
2829
-
2830
- var params: [String: Any] = [
2831
- "account_id": self.newGrailPayAccountID ?? "",
2832
- "account_type": self.selectedNewGrailPayAccountType ?? "",
2833
- "name": self.selectedNewGrailPayAccountName ?? "",
2834
- "description": "payment checkout",
2835
- "customer_id": self.grailPayNewAccountCustomerID ?? "",
2836
- "save_account" : 1,
2837
- "is_default": 1
2838
- ]
2839
-
2840
- // Add these if recurring is enabled
2841
- if let req = request, req.is_recurring == true {
2842
- if let recurringType = req.recurringStartDateType, recurringType == .custom {
2843
- // Only send start_date if type is .custom and field is not empty
2844
- if let startDateText = txtFieldSelectDateNewGrailPayBankView?.text, !startDateText.isEmpty {
2845
- let inputFormatter = DateFormatter()
2846
- inputFormatter.dateFormat = "dd/MM/yyyy"
2847
-
2848
- let outputFormatter = DateFormatter()
2849
- outputFormatter.dateFormat = "MM/dd/yyyy"
2850
-
2851
- if let date = inputFormatter.date(from: startDateText) {
2852
- let apiFormattedDate = outputFormatter.string(from: date)
2853
- params["start_date"] = apiFormattedDate
2854
- } else {
2855
- print("Invalid date format in startDateText")
2856
- }
2857
- }
2858
- }
2859
-
2860
- params["interval"] = txtFieldChosePlanNewGrailPayBankView.text.lowercased()
2861
- }
2862
-
2863
- print(params)
2864
-
2865
- do {
2866
- let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
2867
- uRLRequest.httpBody = jsonData
2868
- if let jsonString = String(data: jsonData, encoding: .utf8) {
2869
- print("JSON Payload: \(jsonString)")
2870
- }
2871
- } catch let error {
2872
- print("Error creating JSON data: \(error)")
2873
- hideLoadingIndicator()
2874
- return
2875
- }
2876
-
2877
- let session = URLSession.shared
2878
- let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
2879
-
2880
- DispatchQueue.main.async {
2881
- self.hideLoadingIndicator() // Stop loader when response is received
2882
- }
2883
-
2884
- if let error = error {
2885
- print("Error: \(error.localizedDescription)")
2886
- return
2887
- }
2888
-
2889
- guard let httpResponse = serviceResponse as? HTTPURLResponse else {
2890
- print("Invalid response")
2891
- return
2892
- }
2893
-
2894
- if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
2895
- if let data = serviceData {
2896
- do {
2897
- if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
2898
- print("Response Data: \(responseObject)")
2899
-
2900
- // Check if status is 0 and handle the error
2901
- if let status = responseObject["status"] as? Int, status == 0 {
2902
- let errorMessage = responseObject["message"] as? String ?? "Unknown error"
2903
- self.presentPaymentErrorVC(errorMessage: errorMessage)
2904
- } else {
2905
- DispatchQueue.main.async {
2906
- if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
2907
- paymentDoneVC.chargeData = responseObject
2908
- // Pass the selected payment method
2909
- paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
2910
- paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
3211
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
3212
+ // Pass billingInfo and additionalInfo
3213
+ if let billingData = self.request.billingInfoData,
3214
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
3215
+
3216
+ // Extract main billing fields
3217
+ let cleanBillingInfo: [String: Any] = [
3218
+ "postal_code": billingInfoDict["postal_code"] ?? "",
3219
+ "country": billingInfoDict["country"] ?? "",
3220
+ "city": billingInfoDict["city"] ?? "",
3221
+ "address": billingInfoDict["address"] ?? "",
3222
+ "state": billingInfoDict["state"] ?? ""
3223
+ ]
3224
+ paymentDoneVC.billingInfo = cleanBillingInfo
3225
+
3226
+ // Extract additional_info
3227
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
3228
+ let cleanAdditionalInfo: [String: Any] = [
3229
+ "email": additional["email"] ?? "",
3230
+ "phone_number": additional["phone_number"] ?? "",
3231
+ "name": additional["name"] ?? "",
3232
+ "country_code": additional["country_code"] ?? ""
3233
+ ]
3234
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
3235
+ }
3236
+ }
2911
3237
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
2912
3238
  }
2913
3239
  }
@@ -3069,34 +3395,20 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3069
3395
  @IBAction func actionBtnNext(_ sender: UIButton) {
3070
3396
  // Check if billing info data exists and is valid JSON
3071
3397
  if let billingInfoData = request.billingInfoData {
3072
- DispatchQueue.global(qos: .userInitiated).async { [weak self] in
3073
- // Perform JSON parsing in a background thread
3074
- guard let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
3075
- let jsonDict = json as? [String: Any], !jsonDict.isEmpty else {
3076
- return
3077
- }
3398
+ do {
3399
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
3078
3400
 
3079
3401
  DispatchQueue.main.async {
3080
- // Ensure UI updates happen on the main thread
3081
- guard let self = self else { return }
3082
-
3083
3402
  if self.selectedPaymentMethod == "Card" {
3084
- // Trim whitespace and newlines
3085
3403
  let cardNumber = self.cardNumberTextField.text.trimmingCharacters(in: .whitespacesAndNewlines)
3086
3404
  let expiryDate = self.cardExpiryTextField.text.trimmingCharacters(in: .whitespacesAndNewlines)
3087
3405
  let cvv = self.cardCvvTextField.text.trimmingCharacters(in: .whitespacesAndNewlines)
3088
3406
  let cardName = self.cardNameTextField.text.trimmingCharacters(in: .whitespacesAndNewlines)
3089
-
3090
- // Remove spaces from card number for validation
3407
+ let email = self.txtFieldEmailCardView.text.trimmingCharacters(in: .whitespacesAndNewlines)
3091
3408
  let sanitizedCardNumber = cardNumber.replacingOccurrences(of: " ", with: "")
3092
-
3093
- // Regular expression for expiry date format MM/yyyy
3094
3409
  let expiryDateRegex = #"^(0[1-9]|1[0-2])\/\d{4}$"#
3095
-
3096
- // Regular expression for a valid name (only letters and spaces)
3097
3410
  let nameRegex = #"^[A-Za-z\s]+$"#
3098
3411
 
3099
- // Validation checks
3100
3412
  if cardNumber.isEmpty {
3101
3413
  self.showAlert(title: "Missing Information", message: "Card Number is required.")
3102
3414
  return
@@ -3106,22 +3418,17 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3106
3418
  } else if expiryDate.isEmpty {
3107
3419
  self.showAlert(title: "Missing Information", message: "Card Expiry Date is required.")
3108
3420
  return
3109
- }
3110
- else if !expiryDate.matches(expiryDateRegex) {
3421
+ } else if !expiryDate.matches(expiryDateRegex) {
3111
3422
  self.showAlert(title: "Invalid Expiry Date", message: "Expiry Date must be in format MM/yyyy (e.g., 02/2026).")
3112
- return
3113
- }
3114
- else {
3115
- // Check if expiry date is past tomorrow
3423
+ return
3424
+ } else {
3116
3425
  let dateFormatter = DateFormatter()
3117
3426
  dateFormatter.dateFormat = "MM/yyyy"
3118
3427
  dateFormatter.timeZone = TimeZone.current
3119
3428
  if let expDate = dateFormatter.date(from: expiryDate) {
3120
3429
  let calendar = Calendar.current
3121
- // Get start of the month following expiry
3122
3430
  let expiryMonthStart = calendar.date(from: calendar.dateComponents([.year, .month], from: expDate))!
3123
3431
  let endOfMonth = calendar.date(byAdding: DateComponents(month: 1, day: -1), to: expiryMonthStart)!
3124
-
3125
3432
  let tomorrow = calendar.date(byAdding: .day, value: 1, to: Date())!
3126
3433
  if endOfMonth < tomorrow {
3127
3434
  self.showAlert(title: "Expired Card", message: "Card is expired or please enter a valid expiry date.")
@@ -3145,9 +3452,15 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3145
3452
  } else if !cardName.matches(nameRegex) {
3146
3453
  self.showAlert(title: "Invalid Name", message: "Name must contain only letters and spaces.")
3147
3454
  return
3455
+ } else if email.isEmpty {
3456
+ self.showAlert(title: "Missing Information", message: "Please enter an email address.")
3457
+ return
3458
+ } else if !self.isValidEmail(email) {
3459
+ self.showAlert(title: "Invalid Email", message: "Please enter a valid email address.")
3460
+ return
3148
3461
  }
3149
3462
 
3150
- // Recurring Plan + Date Validation (only if is_recurring is true)
3463
+ // Recurring plan validation
3151
3464
  if let req = self.request, req.is_recurring == true {
3152
3465
  if self.txtFieldChosePlanCard.text.isEmpty {
3153
3466
  self.showAlert(title: "Missing Information", message: "Please choose your plan.")
@@ -3162,22 +3475,51 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3162
3475
  }
3163
3476
  }
3164
3477
 
3165
- // Proceed to BillingInfoVC
3166
- let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3167
- vc.billingInfoData = jsonDict
3168
- vc.cardNumber = cardNumber
3169
- vc.expiryDate = expiryDate
3170
- vc.cvv = cvv
3171
- vc.nameOnCard = cardName
3172
- vc.selectedPaymentMethod = self.selectedPaymentMethod
3173
- vc.isSavedForFuture = self.isSavedForFuture
3174
- vc.request = self.request
3175
- vc.chosenPlan = self.txtFieldChosePlanCard.text
3176
- vc.startDate = self.txtFieldStartDateCard.text
3177
- self.navigationController?.pushViewController(vc, animated: true)
3478
+ // Redirect based on billing visibility
3479
+ if fieldSection.visibility.billing {
3480
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3481
+ vc.billingInfoData = billingInfoData
3482
+ vc.cardNumber = cardNumber
3483
+ vc.expiryDate = expiryDate
3484
+ vc.cvv = cvv
3485
+ vc.nameOnCard = cardName
3486
+ vc.selectedPaymentMethod = self.selectedPaymentMethod
3487
+ vc.isSavedForFuture = self.isSavedForFuture
3488
+ vc.request = self.request
3489
+ vc.chosenPlan = self.txtFieldChosePlanCard.text
3490
+ vc.startDate = self.txtFieldStartDateCard.text
3491
+ vc.userEmail = self.txtFieldEmailCardView.text
3492
+ vc.amount = self.amount
3493
+ vc.billingInfo = fieldSection.billing
3494
+ vc.additionalInfo = fieldSection.additional
3495
+ vc.visibility = fieldSection.visibility
3496
+ vc.easyPayDelegate = self.easyPayDelegate
3497
+ self.navigationController?.pushViewController(vc, animated: true)
3498
+ } else {
3499
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3500
+ vc.billingInfoData = billingInfoData
3501
+ vc.cardNumber = cardNumber
3502
+ vc.expiryDate = expiryDate
3503
+ vc.cvv = cvv
3504
+ vc.nameOnCard = cardName
3505
+ vc.selectedPaymentMethod = self.selectedPaymentMethod
3506
+ vc.isSavedForFuture = self.isSavedForFuture
3507
+ vc.request = self.request
3508
+ vc.chosenPlan = self.txtFieldChosePlanCard.text
3509
+ vc.startDate = self.txtFieldStartDateCard.text
3510
+ vc.userEmail = self.txtFieldEmailCardView.text
3511
+ vc.billingInfo = fieldSection.billing
3512
+ vc.additionalInfo = fieldSection.additional
3513
+ vc.amount = self.amount
3514
+ vc.visibility = fieldSection.visibility
3515
+ vc.easyPayDelegate = self.easyPayDelegate
3516
+ self.navigationController?.pushViewController(vc, animated: true)
3517
+ }
3178
3518
  }
3179
3519
  else if self.selectedPaymentMethod == "Bank" {
3180
3520
  // Bank Case
3521
+ let email = self.txtFieldEmailAccountView.text.trimmingCharacters(in: .whitespacesAndNewlines)
3522
+
3181
3523
  if self.txtFieldAccountName.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
3182
3524
  self.showAlert(title: "Missing Information", message: "Bank account name is required.")
3183
3525
  return
@@ -3204,6 +3546,14 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3204
3546
  self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
3205
3547
  return
3206
3548
  }
3549
+ else if email.isEmpty {
3550
+ self.showAlert(title: "Missing Information", message: "Please enter an email address.")
3551
+ return
3552
+ } else if !self.isValidEmail(email) {
3553
+ self.showAlert(title: "Invalid Email", message: "Please enter a valid email address.")
3554
+ return
3555
+ }
3556
+
3207
3557
  else if !self.agreeTermsAndCondtition {
3208
3558
  self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
3209
3559
  return
@@ -3224,24 +3574,59 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3224
3574
  }
3225
3575
  }
3226
3576
 
3227
- // Proceed to BillingInfoVC
3228
- let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3229
- vc.accountName = self.txtFieldAccountName.text
3230
- vc.routingNumber = self.txtFieldRoutingNumber.text
3231
- vc.accountType = self.txtFieldAccountType.text
3232
- vc.accountNumber = self.txtFieldAccountNumber.text
3233
- vc.billingInfoData = jsonDict
3234
- vc.selectedPaymentMethod = self.selectedPaymentMethod
3235
- vc.isSavedForFuture = self.isSavedForFuture
3236
- vc.isFrom = "NormalBankPayWithoutSave"
3237
- vc.chosenPlan = self.txtFieldSelectPlanViewBankFields.text
3238
- vc.startDate = self.txtFieldSelectDateViewBankFields.text
3239
- vc.request = self.request
3240
- self.navigationController?.pushViewController(vc, animated: true)
3577
+ // Redirect based on billing visibility
3578
+ if fieldSection.visibility.billing {
3579
+ // Proceed to BillingInfoVC
3580
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3581
+ vc.accountName = self.txtFieldAccountName.text
3582
+ vc.routingNumber = self.txtFieldRoutingNumber.text
3583
+ vc.accountType = self.txtFieldAccountType.text
3584
+ vc.accountNumber = self.txtFieldAccountNumber.text
3585
+ // vc.billingInfoData = jsonDict
3586
+ vc.billingInfoData = billingInfoData
3587
+ vc.selectedPaymentMethod = self.selectedPaymentMethod
3588
+ vc.isSavedForFuture = self.isSavedForFuture
3589
+ vc.isFrom = "NormalBankPayWithoutSave"
3590
+ vc.chosenPlan = self.txtFieldSelectPlanViewBankFields.text
3591
+ vc.startDate = self.txtFieldSelectDateViewBankFields.text
3592
+ vc.request = self.request
3593
+ vc.userEmail = self.txtFieldEmailAccountView.text
3594
+ vc.amount = self.amount
3595
+ vc.billingInfo = fieldSection.billing
3596
+ vc.additionalInfo = fieldSection.additional
3597
+ vc.visibility = fieldSection.visibility
3598
+ vc.easyPayDelegate = self.easyPayDelegate
3599
+ self.navigationController?.pushViewController(vc, animated: true)
3600
+ }
3601
+ else {
3602
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3603
+ vc.accountName = self.txtFieldAccountName.text
3604
+ vc.routingNumber = self.txtFieldRoutingNumber.text
3605
+ vc.accountType = self.txtFieldAccountType.text
3606
+ vc.accountNumber = self.txtFieldAccountNumber.text
3607
+ // vc.billingInfoData = jsonDict
3608
+ vc.billingInfoData = billingInfoData
3609
+ vc.selectedPaymentMethod = self.selectedPaymentMethod
3610
+ vc.isSavedForFuture = self.isSavedForFuture
3611
+ vc.isFrom = "NormalBankPayWithoutSave"
3612
+ vc.chosenPlan = self.txtFieldSelectPlanViewBankFields.text
3613
+ vc.startDate = self.txtFieldSelectDateViewBankFields.text
3614
+ vc.request = self.request
3615
+ vc.userEmail = self.txtFieldEmailAccountView.text
3616
+ vc.amount = self.amount
3617
+ vc.billingInfo = fieldSection.billing
3618
+ vc.additionalInfo = fieldSection.additional
3619
+ vc.visibility = fieldSection.visibility
3620
+ vc.easyPayDelegate = self.easyPayDelegate
3621
+ self.navigationController?.pushViewController(vc, animated: true)
3622
+ }
3241
3623
  }
3242
3624
  }
3625
+ } catch {
3626
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
3243
3627
  }
3244
- } else {
3628
+ }
3629
+ else {
3245
3630
  // Billing info is nil or empty
3246
3631
  if selectedPaymentMethod == "Card" {
3247
3632
  /// Card Case
@@ -3251,6 +3636,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3251
3636
  let expiryDate = self.cardExpiryTextField.text.trimmingCharacters(in: .whitespacesAndNewlines)
3252
3637
  let cvv = self.cardCvvTextField.text.trimmingCharacters(in: .whitespacesAndNewlines)
3253
3638
  let cardName = self.cardNameTextField.text.trimmingCharacters(in: .whitespacesAndNewlines)
3639
+ let email = self.txtFieldEmailCardView.text.trimmingCharacters(in: .whitespacesAndNewlines)
3254
3640
 
3255
3641
  // Remove spaces from card number for validation
3256
3642
  let sanitizedCardNumber = cardNumber.replacingOccurrences(of: " ", with: "")
@@ -3272,7 +3658,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3272
3658
  self.showAlert(title: "Missing Information", message: "Card Expiry Date is required.")
3273
3659
  return
3274
3660
  }
3275
-
3276
3661
  else if !expiryDate.matches(expiryDateRegex) {
3277
3662
  self.showAlert(title: "Invalid Expiry Date", message: "Expiry Date must be in format MM/yyyy (e.g., 02/2026).")
3278
3663
  return
@@ -3312,6 +3697,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3312
3697
  self.showAlert(title: "Invalid Name", message: "Name must contain only letters and spaces.")
3313
3698
  return
3314
3699
  }
3700
+ else if email.isEmpty {
3701
+ self.showAlert(title: "Missing Information", message: "Please enter an email address.")
3702
+ return
3703
+ } else if !self.isValidEmail(email) {
3704
+ self.showAlert(title: "Invalid Email", message: "Please enter a valid email address.")
3705
+ return
3706
+ }
3315
3707
 
3316
3708
  // Recurring Plan + Date Validation (only if is_recurring is true)
3317
3709
  if let req = self.request, req.is_recurring == true {
@@ -3332,7 +3724,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3332
3724
  if isSavedForFuture {
3333
3725
  navigateCardDataToEmailVerificationVC()
3334
3726
  } else {
3335
- if request.enable3DS == true {
3727
+ if request.secureAuthentication == true {
3336
3728
  threeDSecurePaymentApi()
3337
3729
  }
3338
3730
  else {
@@ -3342,6 +3734,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3342
3734
  }
3343
3735
  else if selectedPaymentMethod == "Bank" {
3344
3736
  // Bank Case
3737
+ let email = self.txtFieldEmailAccountView.text.trimmingCharacters(in: .whitespacesAndNewlines)
3738
+
3345
3739
  if txtFieldAccountName.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
3346
3740
  showAlert(title: "Missing Information", message: "Bank account name is required.")
3347
3741
  return
@@ -3370,6 +3764,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3370
3764
  self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
3371
3765
  return
3372
3766
  }
3767
+ else if email.isEmpty {
3768
+ self.showAlert(title: "Missing Information", message: "Please enter an email address.")
3769
+ return
3770
+ } else if !self.isValidEmail(email) {
3771
+ self.showAlert(title: "Invalid Email", message: "Please enter a valid email address.")
3772
+ return
3773
+ }
3373
3774
  else if !agreeTermsAndCondtition {
3374
3775
  showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
3375
3776
  return
@@ -3452,10 +3853,23 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3452
3853
  if selectedPaymentMethod == "Card" {
3453
3854
  self.viewSingleSavedAccount.isHidden = true
3454
3855
  self.viewBankFields.isHidden = true
3455
- self.viewBtnShowSavedCards.isHidden = false
3456
- self.lblBtnShowSaveCard.text = "Show Saved Cards"
3457
- self.viewBtnShowSavedCardHeight.constant = 50
3458
- self.viewBtnShowSavedCardTopCon.constant = 20
3856
+ // self.viewBtnShowSavedCards.isHidden = false
3857
+ // self.lblBtnShowSaveCard.text = "Show Saved Cards"
3858
+ // self.viewBtnShowSavedCardHeight.constant = 50
3859
+ // self.viewBtnShowSavedCardTopCon.constant = 20
3860
+
3861
+ if request.saveCard == false {
3862
+ self.viewBtnShowSavedCards.isHidden = true
3863
+ self.viewBtnShowSavedCardHeight.constant = 0
3864
+ self.viewBtnShowSavedCardTopCon.constant = 0
3865
+ }
3866
+ else {
3867
+ self.viewBtnShowSavedCards.isHidden = false
3868
+ self.lblBtnShowSaveCard.text = "Show Saved Cards"
3869
+ self.viewBtnShowSavedCardHeight.constant = 50
3870
+ self.viewBtnShowSavedCardTopCon.constant = 20
3871
+ }
3872
+
3459
3873
  self.OTPView.isHidden = true
3460
3874
  self.emailView.isHidden = true
3461
3875
  self.viewCardFields.isHidden = false
@@ -3471,10 +3885,23 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3471
3885
  }
3472
3886
  else if selectedPaymentMethod == "Bank" {
3473
3887
  self.viewBankFields.isHidden = false
3474
- self.viewBtnShowSavedCards.isHidden = false
3475
- self.lblBtnShowSaveCard.text = "Show Saved Accounts"
3476
- self.viewBtnShowSavedCardHeight.constant = 50
3477
- self.viewBtnShowSavedCardTopCon.constant = 20
3888
+ // self.viewBtnShowSavedCards.isHidden = false
3889
+ // self.lblBtnShowSaveCard.text = "Show Saved Accounts"
3890
+ // self.viewBtnShowSavedCardHeight.constant = 50
3891
+ // self.viewBtnShowSavedCardTopCon.constant = 20
3892
+
3893
+ if request.saveAccount == false {
3894
+ self.viewBtnShowSavedCards.isHidden = true
3895
+ self.viewBtnShowSavedCardHeight.constant = 0
3896
+ self.viewBtnShowSavedCardTopCon.constant = 0
3897
+ }
3898
+ else {
3899
+ self.viewBtnShowSavedCards.isHidden = false
3900
+ self.lblBtnShowSaveCard.text = "Show Saved Accounts"
3901
+ self.viewBtnShowSavedCardHeight.constant = 50
3902
+ self.viewBtnShowSavedCardTopCon.constant = 20
3903
+ }
3904
+
3478
3905
  self.OTPView.isHidden = true
3479
3906
  self.emailView.isHidden = true
3480
3907
  self.viewCardFields.isHidden = true
@@ -3547,6 +3974,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3547
3974
  btnPayNowSingleAccountView.isHidden = !isSelectForPayBank
3548
3975
 
3549
3976
  if isSelectForPayBank {
3977
+ selectedBankIndex = 0
3550
3978
  // Show or hide recurring plan fields based on request.enableRecurring
3551
3979
  if request?.is_recurring == true {
3552
3980
  txtFieldSelectPlanSingleSavedBankView.isHidden = false
@@ -3560,6 +3988,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3560
3988
  txtFieldSelectDateSingleSavedBankView.isHidden = true
3561
3989
  }
3562
3990
  } else {
3991
+ selectedBankIndex = nil
3563
3992
  // Hide all related fields if not selected
3564
3993
  btnNext.isHidden = true
3565
3994
  btnNextHeight.constant = 0
@@ -3616,42 +4045,80 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3616
4045
  grailPayAaccountChargeSingleSavedAccountApi()
3617
4046
  } else {
3618
4047
  // Check if billingInfoData is not nil or empty
3619
- if let billingInfoData = request.billingInfoData,
3620
- let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
3621
- let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
3622
-
3623
- // Recurring Plan + Date Validation (only if is_recurring is true)
3624
- if let req = self.request, req.is_recurring == true {
3625
- if txtFieldSelectPlanSingleSavedBankView.text.isEmpty {
3626
- self.showAlert(title: "Missing Information", message: "Please choose your plan.")
3627
- return
3628
- }
4048
+ if let billingInfoData = request.billingInfoData {
4049
+ do {
4050
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
3629
4051
 
3630
- if let recurringType = req.recurringStartDateType, recurringType == .custom {
3631
- if txtFieldSelectDateSingleSavedBankView.text.isEmpty {
3632
- self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
3633
- return
4052
+ DispatchQueue.main.async {
4053
+
4054
+ if let billingInfoData = self.request.billingInfoData,
4055
+ let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
4056
+ let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
4057
+
4058
+ // Recurring Plan + Date Validation (only if is_recurring is true)
4059
+ if let req = self.request, req.is_recurring == true {
4060
+ if self.txtFieldSelectPlanSingleSavedBankView.text.isEmpty {
4061
+ self.showAlert(title: "Missing Information", message: "Please choose your plan.")
4062
+ return
4063
+ }
4064
+
4065
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
4066
+ if self.txtFieldSelectDateSingleSavedBankView.text.isEmpty {
4067
+ self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
4068
+ return
4069
+ }
4070
+ }
4071
+ }
4072
+
4073
+ // Check if the terms and conditions are agreed
4074
+ if !self.agreeTermsAndCondtition {
4075
+ self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
4076
+ return
4077
+ }
4078
+
4079
+ // Redirect based on billing visibility
4080
+ if fieldSection.visibility.billing {
4081
+ // Proceed with the Pay Now action
4082
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
4083
+ // Pass the customer_id and account_id to BillingInfoVC
4084
+ vc.customerID = self.selectedbankAccounts?.customer_id
4085
+ vc.accountID = self.selectedbankAccounts?.account_id
4086
+ vc.billingInfoData = billingInfoData
4087
+ vc.isFrom = "SavedBank"
4088
+ vc.selectedPaymentMethod = "Bank"
4089
+ vc.chosenPlan = self.txtFieldSelectPlanSingleSavedBankView.text
4090
+ vc.startDate = self.txtFieldSelectDateSingleSavedBankView.text
4091
+ vc.request = self.request
4092
+ vc.amount = self.amount
4093
+ vc.billingInfo = fieldSection.billing
4094
+ vc.additionalInfo = fieldSection.additional
4095
+ vc.visibility = fieldSection.visibility
4096
+ vc.easyPayDelegate = self.easyPayDelegate
4097
+ self.navigationController?.pushViewController(vc, animated: true)
4098
+ }
4099
+ else {
4100
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
4101
+ vc.customerID = self.selectedbankAccounts?.customer_id
4102
+ vc.accountID = self.selectedbankAccounts?.account_id
4103
+ vc.billingInfoData = billingInfoData
4104
+ vc.isFrom = "SavedBank"
4105
+ vc.selectedPaymentMethod = "Bank"
4106
+ vc.chosenPlan = self.txtFieldSelectPlanSingleSavedBankView.text
4107
+ vc.startDate = self.txtFieldSelectDateSingleSavedBankView.text
4108
+ vc.request = self.request
4109
+ vc.amount = self.amount
4110
+ vc.billingInfo = fieldSection.billing
4111
+ vc.additionalInfo = fieldSection.additional
4112
+ vc.visibility = fieldSection.visibility
4113
+ vc.easyPayDelegate = self.easyPayDelegate
4114
+ self.navigationController?.pushViewController(vc, animated: true)
4115
+ }
3634
4116
  }
3635
4117
  }
3636
4118
  }
3637
-
3638
- // Check if the terms and conditions are agreed
3639
- if !agreeTermsAndCondtition {
3640
- showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
3641
- return
4119
+ catch {
4120
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
3642
4121
  }
3643
- // Proceed with the Pay Now action
3644
- let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3645
- // Pass the customer_id and account_id to BillingInfoVC
3646
- vc.customerID = selectedbankAccounts?.customer_id
3647
- vc.accountID = selectedbankAccounts?.account_id
3648
- vc.billingInfoData = jsonDict
3649
- vc.isFrom = "SavedBank"
3650
- vc.selectedPaymentMethod = "Bank"
3651
- vc.chosenPlan = self.txtFieldSelectPlanSingleSavedBankView.text
3652
- vc.startDate = self.txtFieldSelectDateSingleSavedBankView.text
3653
- vc.request = self.request
3654
- self.navigationController?.pushViewController(vc, animated: true)
3655
4122
  }
3656
4123
  else {
3657
4124
  //If Billing info is nil or empty
@@ -3699,72 +4166,115 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3699
4166
 
3700
4167
  @IBAction func actionBtnPayNowNewAccountView(_ sender: UIButton) {
3701
4168
  // Check if billingInfoData is not nil or empty
3702
- if let billingInfoData = request.billingInfoData,
3703
- let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
3704
- let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
3705
- print("Billing Info Data: \(jsonDict)")
3706
-
3707
- // Retrieve and trim text field values (removing leading and trailing whitespace)
3708
- let accountName = txtFieldAccountNameNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3709
- let routingNumber = txtFieldRoutingNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3710
- let accountType = txtFieldAccountTypeNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3711
- let accountNumber = txtFieldAccountNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3712
- let confirmAccountNumber = txtFieldConfirmAccountNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3713
-
3714
- // Check if any field is empty
3715
- if accountName.isEmpty || routingNumber.isEmpty || accountType.isEmpty || accountNumber.isEmpty || confirmAccountNumber.isEmpty {
3716
- let alert = UIAlertController(title: "Missing Information", message: "Please fill in all bank details.", preferredStyle: .alert)
3717
- alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
3718
- self.present(alert, animated: true, completion: nil)
3719
- return
3720
- }
3721
-
3722
- // Check if routing number is exactly 9 digits and numeric
3723
- if routingNumber.count != 9 || !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: routingNumber)) {
3724
- self.showAlert(title: "Invalid Routing Number", message: "Routing number is not correct. It must be exactly 9 digits.")
3725
- return
3726
- }
3727
-
3728
- // Check if account number and confirmation match
3729
- if accountNumber != confirmAccountNumber {
3730
- self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
3731
- return
3732
- }
3733
-
3734
- // Recurring Plan + Date Validation (only if is_recurring is true)
3735
- if let req = self.request, req.is_recurring == true {
3736
- if txtFieldSelectPlanNewAccountView.text.isEmpty {
3737
- self.showAlert(title: "Missing Information", message: "Please choose your plan.")
3738
- return
3739
- }
4169
+ if let billingInfoData = request.billingInfoData {
4170
+ do {
4171
+ let fieldSection = try JSONDecoder().decode(FieldSection.self, from: billingInfoData)
3740
4172
 
3741
- if let recurringType = req.recurringStartDateType, recurringType == .custom {
3742
- if txtFieldSelectDateNewAccountView.text.isEmpty {
3743
- self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
3744
- return
4173
+ DispatchQueue.main.async {
4174
+
4175
+ if let billingInfoData = self.request.billingInfoData,
4176
+ let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
4177
+ let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
4178
+ print("Billing Info Data: \(jsonDict)")
4179
+
4180
+ // Retrieve and trim text field values (removing leading and trailing whitespace)
4181
+ let accountName = self.txtFieldAccountNameNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
4182
+ let routingNumber = self.txtFieldRoutingNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
4183
+ let accountType = self.txtFieldAccountTypeNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
4184
+ let accountNumber = self.txtFieldAccountNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
4185
+ let confirmAccountNumber = self.txtFieldConfirmAccountNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
4186
+
4187
+ // Check if any field is empty
4188
+ if accountName.isEmpty || routingNumber.isEmpty || accountType.isEmpty || accountNumber.isEmpty || confirmAccountNumber.isEmpty {
4189
+ let alert = UIAlertController(title: "Missing Information", message: "Please fill in all bank details.", preferredStyle: .alert)
4190
+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
4191
+ self.present(alert, animated: true, completion: nil)
4192
+ return
4193
+ }
4194
+
4195
+ // Check if routing number is exactly 9 digits and numeric
4196
+ if routingNumber.count != 9 || !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: routingNumber)) {
4197
+ self.showAlert(title: "Invalid Routing Number", message: "Routing number is not correct. It must be exactly 9 digits.")
4198
+ return
4199
+ }
4200
+
4201
+ // Check if account number and confirmation match
4202
+ if accountNumber != confirmAccountNumber {
4203
+ self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
4204
+ return
4205
+ }
4206
+
4207
+ // Recurring Plan + Date Validation (only if is_recurring is true)
4208
+ if let req = self.request, req.is_recurring == true {
4209
+ if self.txtFieldSelectPlanNewAccountView.text.isEmpty {
4210
+ self.showAlert(title: "Missing Information", message: "Please choose your plan.")
4211
+ return
4212
+ }
4213
+
4214
+ if let recurringType = req.recurringStartDateType, recurringType == .custom {
4215
+ if self.txtFieldSelectDateNewAccountView.text.isEmpty {
4216
+ self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
4217
+ return
4218
+ }
4219
+ }
4220
+ }
4221
+
4222
+ // Determine the vc.isFrom value based on isSavedNewAccount
4223
+ let isSavedNewAccount = self.isSavedNewAccount
4224
+ let isFromValue = isSavedNewAccount ? "AddNewAccountWithSave" : "AddNewAccountWithoutSave"
4225
+
4226
+ // Redirect based on billing visibility
4227
+ if fieldSection.visibility.billing {
4228
+ // Proceed with the Pay Now action
4229
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
4230
+ vc.accountName = accountName
4231
+ vc.routingNumber = routingNumber
4232
+ vc.accountType = accountType
4233
+ vc.accountNumber = accountNumber
4234
+ vc.billingInfoData = billingInfoData
4235
+ vc.selectedPaymentMethod = "Bank"
4236
+ vc.isFrom = isFromValue
4237
+ vc.isSavedNewAccount = isSavedNewAccount
4238
+ vc.delegate = self
4239
+ vc.chosenPlan = self.txtFieldSelectPlanNewAccountView.text
4240
+ vc.startDate = self.txtFieldSelectDateNewAccountView.text
4241
+ vc.request = self.request
4242
+ vc.selectedPaymentMethod = self.selectedPaymentMethod
4243
+ vc.amount = self.amount
4244
+ vc.billingInfo = fieldSection.billing
4245
+ vc.additionalInfo = fieldSection.additional
4246
+ vc.visibility = fieldSection.visibility
4247
+ vc.easyPayDelegate = self.easyPayDelegate
4248
+ self.navigationController?.pushViewController(vc, animated: true)
4249
+ }
4250
+ else {
4251
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
4252
+ vc.accountName = accountName
4253
+ vc.routingNumber = routingNumber
4254
+ vc.accountType = accountType
4255
+ vc.accountNumber = accountNumber
4256
+ vc.billingInfoData = billingInfoData
4257
+ vc.selectedPaymentMethod = "Bank"
4258
+ vc.isFrom = isFromValue
4259
+ vc.isSavedNewAccount = isSavedNewAccount
4260
+ // vc.delegate = self
4261
+ vc.chosenPlan = self.txtFieldSelectPlanNewAccountView.text
4262
+ vc.startDate = self.txtFieldSelectDateNewAccountView.text
4263
+ vc.request = self.request
4264
+ vc.selectedPaymentMethod = self.selectedPaymentMethod
4265
+ vc.amount = self.amount
4266
+ vc.billingInfo = fieldSection.billing
4267
+ vc.additionalInfo = fieldSection.additional
4268
+ vc.visibility = fieldSection.visibility
4269
+ vc.easyPayDelegate = self.easyPayDelegate
4270
+ self.navigationController?.pushViewController(vc, animated: true)
4271
+ }
3745
4272
  }
3746
4273
  }
3747
4274
  }
3748
-
3749
- // Determine the vc.isFrom value based on isSavedNewAccount
3750
- let isSavedNewAccount = isSavedNewAccount
3751
- let isFromValue = isSavedNewAccount ? "AddNewAccountWithSave" : "AddNewAccountWithoutSave"
3752
-
3753
- // Proceed with the Pay Now action
3754
- let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3755
- vc.accountName = accountName
3756
- vc.routingNumber = routingNumber
3757
- vc.accountType = accountType
3758
- vc.accountNumber = accountNumber
3759
- vc.billingInfoData = jsonDict
3760
- vc.selectedPaymentMethod = "Bank"
3761
- vc.isFrom = isFromValue
3762
- vc.isSavedNewAccount = isSavedNewAccount
3763
- vc.delegate = self
3764
- vc.chosenPlan = self.txtFieldSelectPlanNewAccountView.text
3765
- vc.startDate = self.txtFieldSelectDateNewAccountView.text
3766
- vc.request = self.request
3767
- self.navigationController?.pushViewController(vc, animated: true)
4275
+ catch {
4276
+ print("Failed to decode billingInfoData: \(error.localizedDescription)")
4277
+ }
3768
4278
  }
3769
4279
  else {
3770
4280
  //If Billing info is nil or empty
@@ -4479,6 +4989,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
4479
4989
  paymentDoneVC.chargeData = responseObject
4480
4990
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
4481
4991
  paymentDoneVC.easyPayDelegate = self.easyPayDelegate
4992
+
4993
+ // Pass billingInfo and additionalInfo
4994
+ if let billingData = self.request.billingInfoData,
4995
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
4996
+
4997
+ // Extract main billing fields
4998
+ let cleanBillingInfo: [String: Any] = [
4999
+ "postal_code": billingInfoDict["postal_code"] ?? "",
5000
+ "country": billingInfoDict["country"] ?? "",
5001
+ "city": billingInfoDict["city"] ?? "",
5002
+ "address": billingInfoDict["address"] ?? "",
5003
+ "state": billingInfoDict["state"] ?? ""
5004
+ ]
5005
+ paymentDoneVC.billingInfo = cleanBillingInfo
5006
+
5007
+ // Extract additional_info
5008
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
5009
+ let cleanAdditionalInfo: [String: Any] = [
5010
+ "email": additional["email"] ?? "",
5011
+ "phone_number": additional["phone_number"] ?? "",
5012
+ "name": additional["name"] ?? "",
5013
+ "country_code": additional["country_code"] ?? ""
5014
+ ]
5015
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
5016
+ }
5017
+ }
4482
5018
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
4483
5019
  }
4484
5020
  }
@@ -4536,24 +5072,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
4536
5072
  uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4537
5073
  }
4538
5074
 
4539
- ///OLD Params
4540
- // // Prepare parameters
4541
- // let email = UserStoreSingleton.shared.verificationEmail ?? ""
4542
- // let namePart = email.split(separator: "@").first.map(String.init) ?? ""
4543
- //
4544
- // var params: [String: Any] = [
4545
- // "name": namePart,
4546
- // "email": email,
4547
- // "description": "TestDescription",
4548
- // "currency": "usd",
4549
- // "payment_method": "card",
4550
- // "save_card": 0,
4551
- // "customer": selectedCard?.customerId ?? "",
4552
- // "card_id": selectedCard?.cardId ?? "",
4553
- // "cvc": txtFieldCVVSingleSavedCard.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "",
4554
- // "customer_id": selectedCard?.customerId ?? ""
4555
- // ]
4556
-
4557
5075
  var params: [String: Any] = [
4558
5076
  "description": "payment checkout",
4559
5077
  "currency": "usd",
@@ -4631,6 +5149,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
4631
5149
  paymentDoneVC.chargeData = responseObject
4632
5150
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
4633
5151
  paymentDoneVC.easyPayDelegate = self.easyPayDelegate
5152
+
5153
+ // Pass billingInfo and additionalInfo
5154
+ if let billingData = self.request.billingInfoData,
5155
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
5156
+
5157
+ // Extract main billing fields
5158
+ let cleanBillingInfo: [String: Any] = [
5159
+ "postal_code": billingInfoDict["postal_code"] ?? "",
5160
+ "country": billingInfoDict["country"] ?? "",
5161
+ "city": billingInfoDict["city"] ?? "",
5162
+ "address": billingInfoDict["address"] ?? "",
5163
+ "state": billingInfoDict["state"] ?? ""
5164
+ ]
5165
+ paymentDoneVC.billingInfo = cleanBillingInfo
5166
+
5167
+ // Extract additional_info
5168
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
5169
+ let cleanAdditionalInfo: [String: Any] = [
5170
+ "email": additional["email"] ?? "",
5171
+ "phone_number": additional["phone_number"] ?? "",
5172
+ "name": additional["name"] ?? "",
5173
+ "country_code": additional["country_code"] ?? ""
5174
+ ]
5175
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
5176
+ }
5177
+ }
4634
5178
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
4635
5179
  }
4636
5180
  }
@@ -4849,6 +5393,31 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
4849
5393
  paymentDoneVC.chargeData = responseObject
4850
5394
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
4851
5395
  paymentDoneVC.easyPayDelegate = self.easyPayDelegate
5396
+ // Pass billingInfo and additionalInfo
5397
+ if let billingData = self.request.billingInfoData,
5398
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
5399
+
5400
+ // Extract main billing fields
5401
+ let cleanBillingInfo: [String: Any] = [
5402
+ "postal_code": billingInfoDict["postal_code"] ?? "",
5403
+ "country": billingInfoDict["country"] ?? "",
5404
+ "city": billingInfoDict["city"] ?? "",
5405
+ "address": billingInfoDict["address"] ?? "",
5406
+ "state": billingInfoDict["state"] ?? ""
5407
+ ]
5408
+ paymentDoneVC.billingInfo = cleanBillingInfo
5409
+
5410
+ // Extract additional_info
5411
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
5412
+ let cleanAdditionalInfo: [String: Any] = [
5413
+ "email": additional["email"] ?? "",
5414
+ "phone_number": additional["phone_number"] ?? "",
5415
+ "name": additional["name"] ?? "",
5416
+ "country_code": additional["country_code"] ?? ""
5417
+ ]
5418
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
5419
+ }
5420
+ }
4852
5421
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
4853
5422
  }
4854
5423
  }
@@ -5382,7 +5951,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5382
5951
  paymentDoneVC.chargeData = responseObject
5383
5952
  // Pass the selected payment method
5384
5953
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
5385
- paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
5954
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
5955
+ // Pass billingInfo and additionalInfo
5956
+ if let billingData = self.request.billingInfoData,
5957
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
5958
+
5959
+ // Extract main billing fields
5960
+ let cleanBillingInfo: [String: Any] = [
5961
+ "postal_code": billingInfoDict["postal_code"] ?? "",
5962
+ "country": billingInfoDict["country"] ?? "",
5963
+ "city": billingInfoDict["city"] ?? "",
5964
+ "address": billingInfoDict["address"] ?? "",
5965
+ "state": billingInfoDict["state"] ?? ""
5966
+ ]
5967
+ paymentDoneVC.billingInfo = cleanBillingInfo
5968
+
5969
+ // Extract additional_info
5970
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
5971
+ let cleanAdditionalInfo: [String: Any] = [
5972
+ "email": additional["email"] ?? "",
5973
+ "phone_number": additional["phone_number"] ?? "",
5974
+ "name": additional["name"] ?? "",
5975
+ "country_code": additional["country_code"] ?? ""
5976
+ ]
5977
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
5978
+ }
5979
+ }
5386
5980
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
5387
5981
  }
5388
5982
  }
@@ -5514,7 +6108,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5514
6108
  paymentDoneVC.chargeData = responseObject
5515
6109
  // Pass the selected payment method
5516
6110
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
5517
- paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
6111
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
6112
+ // Pass billingInfo and additionalInfo
6113
+ if let billingData = self.request.billingInfoData,
6114
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
6115
+
6116
+ // Extract main billing fields
6117
+ let cleanBillingInfo: [String: Any] = [
6118
+ "postal_code": billingInfoDict["postal_code"] ?? "",
6119
+ "country": billingInfoDict["country"] ?? "",
6120
+ "city": billingInfoDict["city"] ?? "",
6121
+ "address": billingInfoDict["address"] ?? "",
6122
+ "state": billingInfoDict["state"] ?? ""
6123
+ ]
6124
+ paymentDoneVC.billingInfo = cleanBillingInfo
6125
+
6126
+ // Extract additional_info
6127
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
6128
+ let cleanAdditionalInfo: [String: Any] = [
6129
+ "email": additional["email"] ?? "",
6130
+ "phone_number": additional["phone_number"] ?? "",
6131
+ "name": additional["name"] ?? "",
6132
+ "country_code": additional["country_code"] ?? ""
6133
+ ]
6134
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
6135
+ }
6136
+ }
5518
6137
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
5519
6138
  }
5520
6139
  }
@@ -5656,7 +6275,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5656
6275
  paymentDoneVC.chargeData = responseObject
5657
6276
  // Pass the selected payment method
5658
6277
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
5659
- paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
6278
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate
6279
+ // Pass billingInfo and additionalInfo
6280
+ if let billingData = self.request.billingInfoData,
6281
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
6282
+
6283
+ // Extract main billing fields
6284
+ let cleanBillingInfo: [String: Any] = [
6285
+ "postal_code": billingInfoDict["postal_code"] ?? "",
6286
+ "country": billingInfoDict["country"] ?? "",
6287
+ "city": billingInfoDict["city"] ?? "",
6288
+ "address": billingInfoDict["address"] ?? "",
6289
+ "state": billingInfoDict["state"] ?? ""
6290
+ ]
6291
+ paymentDoneVC.billingInfo = cleanBillingInfo
6292
+
6293
+ // Extract additional_info
6294
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
6295
+ let cleanAdditionalInfo: [String: Any] = [
6296
+ "email": additional["email"] ?? "",
6297
+ "phone_number": additional["phone_number"] ?? "",
6298
+ "name": additional["name"] ?? "",
6299
+ "country_code": additional["country_code"] ?? ""
6300
+ ]
6301
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
6302
+ }
6303
+ }
5660
6304
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
5661
6305
  }
5662
6306
  }
@@ -5809,6 +6453,31 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5809
6453
  paymentDoneVC.chargeData = responseObject
5810
6454
  paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
5811
6455
  paymentDoneVC.easyPayDelegate = self.easyPayDelegate
6456
+ // Pass billingInfo and additionalInfo
6457
+ if let billingData = self.request.billingInfoData,
6458
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
6459
+
6460
+ // Extract main billing fields
6461
+ let cleanBillingInfo: [String: Any] = [
6462
+ "postal_code": billingInfoDict["postal_code"] ?? "",
6463
+ "country": billingInfoDict["country"] ?? "",
6464
+ "city": billingInfoDict["city"] ?? "",
6465
+ "address": billingInfoDict["address"] ?? "",
6466
+ "state": billingInfoDict["state"] ?? ""
6467
+ ]
6468
+ paymentDoneVC.billingInfo = cleanBillingInfo
6469
+
6470
+ // Extract additional_info
6471
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
6472
+ let cleanAdditionalInfo: [String: Any] = [
6473
+ "email": additional["email"] ?? "",
6474
+ "phone_number": additional["phone_number"] ?? "",
6475
+ "name": additional["name"] ?? "",
6476
+ "country_code": additional["country_code"] ?? ""
6477
+ ]
6478
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
6479
+ }
6480
+ }
5812
6481
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
5813
6482
  }
5814
6483
  }
@@ -6196,6 +6865,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6196
6865
  params["interval"] = txtFieldChosePlanCard.text.lowercased()
6197
6866
  }
6198
6867
 
6868
+ print(params)
6869
+
6199
6870
  do {
6200
6871
  let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
6201
6872
  uRLRequest.httpBody = jsonData
@@ -6243,7 +6914,31 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6243
6914
  paymentDoneVC.redirectURL = urlString
6244
6915
  paymentDoneVC.chargeData = responseObject
6245
6916
  paymentDoneVC.easyPayDelegate = self.easyPayDelegate
6246
-
6917
+ // Pass billingInfo and additionalInfo
6918
+ if let billingData = self.request.billingInfoData,
6919
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
6920
+
6921
+ // Extract main billing fields
6922
+ let cleanBillingInfo: [String: Any] = [
6923
+ "postal_code": billingInfoDict["postal_code"] ?? "",
6924
+ "country": billingInfoDict["country"] ?? "",
6925
+ "city": billingInfoDict["city"] ?? "",
6926
+ "address": billingInfoDict["address"] ?? "",
6927
+ "state": billingInfoDict["state"] ?? ""
6928
+ ]
6929
+ paymentDoneVC.billingInfo = cleanBillingInfo
6930
+
6931
+ // Extract additional_info
6932
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
6933
+ let cleanAdditionalInfo: [String: Any] = [
6934
+ "email": additional["email"] ?? "",
6935
+ "phone_number": additional["phone_number"] ?? "",
6936
+ "name": additional["name"] ?? "",
6937
+ "country_code": additional["country_code"] ?? ""
6938
+ ]
6939
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
6940
+ }
6941
+ }
6247
6942
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
6248
6943
  }
6249
6944
  }
@@ -6453,7 +7148,31 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6453
7148
  paymentDoneVC.redirectURL = urlString
6454
7149
  paymentDoneVC.chargeData = responseObject
6455
7150
  paymentDoneVC.easyPayDelegate = self.easyPayDelegate
6456
-
7151
+ // Pass billingInfo and additionalInfo
7152
+ if let billingData = self.request.billingInfoData,
7153
+ let billingInfoDict = try? JSONSerialization.jsonObject(with: billingData, options: []) as? [String: Any] {
7154
+
7155
+ // Extract main billing fields
7156
+ let cleanBillingInfo: [String: Any] = [
7157
+ "postal_code": billingInfoDict["postal_code"] ?? "",
7158
+ "country": billingInfoDict["country"] ?? "",
7159
+ "city": billingInfoDict["city"] ?? "",
7160
+ "address": billingInfoDict["address"] ?? "",
7161
+ "state": billingInfoDict["state"] ?? ""
7162
+ ]
7163
+ paymentDoneVC.billingInfo = cleanBillingInfo
7164
+
7165
+ // Extract additional_info
7166
+ if let additional = billingInfoDict["additional_info"] as? [String: Any] {
7167
+ let cleanAdditionalInfo: [String: Any] = [
7168
+ "email": additional["email"] ?? "",
7169
+ "phone_number": additional["phone_number"] ?? "",
7170
+ "name": additional["name"] ?? "",
7171
+ "country_code": additional["country_code"] ?? ""
7172
+ ]
7173
+ paymentDoneVC.additionalInfo = cleanAdditionalInfo
7174
+ }
7175
+ }
6457
7176
  self.navigationController?.pushViewController(paymentDoneVC, animated: true)
6458
7177
  }
6459
7178
  }
@@ -6580,10 +7299,23 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
6580
7299
  if UserStoreSingleton.shared.isLoggedIn == false {
6581
7300
  self.viewSingleSavedAccount.isHidden = true
6582
7301
  self.viewBankFields.isHidden = true
6583
- self.viewBtnShowSavedCards.isHidden = false
6584
- self.lblBtnShowSaveCard.text = "Show Saved Cards"
6585
- self.viewBtnShowSavedCardHeight.constant = 50
6586
- self.viewBtnShowSavedCardTopCon.constant = 20
7302
+ // self.viewBtnShowSavedCards.isHidden = false
7303
+ // self.lblBtnShowSaveCard.text = "Show Saved Cards"
7304
+ // self.viewBtnShowSavedCardHeight.constant = 50
7305
+ // self.viewBtnShowSavedCardTopCon.constant = 20
7306
+
7307
+ if request.saveCard == false {
7308
+ self.viewBtnShowSavedCards.isHidden = true
7309
+ self.viewBtnShowSavedCardHeight.constant = 0
7310
+ self.viewBtnShowSavedCardTopCon.constant = 0
7311
+ }
7312
+ else {
7313
+ self.viewBtnShowSavedCards.isHidden = false
7314
+ self.lblBtnShowSaveCard.text = "Show Saved Cards"
7315
+ self.viewBtnShowSavedCardHeight.constant = 50
7316
+ self.viewBtnShowSavedCardTopCon.constant = 20
7317
+ }
7318
+
6587
7319
  self.btnShowSavedCard.isHidden = false
6588
7320
  self.OTPView.isHidden = true
6589
7321
  self.emailView.isHidden = true
@@ -6653,10 +7385,23 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
6653
7385
  case "Bank":
6654
7386
  if UserStoreSingleton.shared.isLoggedIn == false {
6655
7387
  // self.viewBankFields.isHidden = false
6656
- self.viewBtnShowSavedCards.isHidden = false
6657
- self.lblBtnShowSaveCard.text = "Show Saved Accounts"
6658
- self.viewBtnShowSavedCardHeight.constant = 50
6659
- self.viewBtnShowSavedCardTopCon.constant = 20
7388
+ // self.viewBtnShowSavedCards.isHidden = false
7389
+ // self.lblBtnShowSaveCard.text = "Show Saved Accounts"
7390
+ // self.viewBtnShowSavedCardHeight.constant = 50
7391
+ // self.viewBtnShowSavedCardTopCon.constant = 20
7392
+
7393
+ if request.saveAccount == false {
7394
+ self.viewBtnShowSavedCards.isHidden = true
7395
+ self.viewBtnShowSavedCardHeight.constant = 0
7396
+ self.viewBtnShowSavedCardTopCon.constant = 0
7397
+ }
7398
+ else {
7399
+ self.viewBtnShowSavedCards.isHidden = false
7400
+ self.lblBtnShowSaveCard.text = "Show Saved Cards"
7401
+ self.viewBtnShowSavedCardHeight.constant = 50
7402
+ self.viewBtnShowSavedCardTopCon.constant = 20
7403
+ }
7404
+
6660
7405
  self.OTPView.isHidden = true
6661
7406
  self.emailView.isHidden = true
6662
7407
  self.viewCardFields.isHidden = true
@@ -7465,6 +8210,13 @@ extension PaymentInfoVC: UITextFieldDelegate {
7465
8210
  } else {
7466
8211
  viewTxtFieldNameOnCard.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
7467
8212
  }
8213
+ } else if textField == txtFieldEmailCardView.textField {
8214
+ if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
8215
+ let uiColor = UIColor(hex: primaryBtnFontColor) {
8216
+ viewTxtFieldEmailCardView.layer.borderColor = uiColor.cgColor
8217
+ } else {
8218
+ viewTxtFieldEmailCardView.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
8219
+ }
7468
8220
  }
7469
8221
  else if textField == txtFieldEmail {
7470
8222
  if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
@@ -7582,6 +8334,14 @@ extension PaymentInfoVC: UITextFieldDelegate {
7582
8334
  viewTextFieldConfirmAccount.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
7583
8335
  }
7584
8336
  }
8337
+ else if textField == txtFieldEmailAccountView.textField {
8338
+ if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
8339
+ let uiColor = UIColor(hex: primaryBtnFontColor) {
8340
+ viewTxtFieldEmailAccountView.layer.borderColor = uiColor.cgColor
8341
+ } else {
8342
+ viewTxtFieldEmailAccountView.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
8343
+ }
8344
+ }
7585
8345
  //New Bank Account View
7586
8346
  else if textField == txtFieldAccountNameNewAccountView {
7587
8347
  if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
@@ -7658,6 +8418,14 @@ extension PaymentInfoVC: UITextFieldDelegate {
7658
8418
  viewTxtFieldNameOnCard.layer.borderColor = UIColor.systemGray.cgColor
7659
8419
  }
7660
8420
  }
8421
+ else if textField == txtFieldEmailCardView.textField {
8422
+ if let bodyBackgroundColor = UserStoreSingleton.shared.secondary_font_col,
8423
+ let borderColor = UIColor(hex: bodyBackgroundColor) {
8424
+ viewTxtFieldEmailCardView.layer.borderColor = borderColor.cgColor
8425
+ } else {
8426
+ viewTxtFieldEmailCardView.layer.borderColor = UIColor.systemGray.cgColor
8427
+ }
8428
+ }
7661
8429
  else if textField == txtFieldEmail {
7662
8430
  if let bodyBackgroundColor = UserStoreSingleton.shared.secondary_font_col,
7663
8431
  let borderColor = UIColor(hex: bodyBackgroundColor) {
@@ -7773,6 +8541,14 @@ extension PaymentInfoVC: UITextFieldDelegate {
7773
8541
  viewTextFieldConfirmAccount.layer.borderColor = UIColor.systemGray.cgColor
7774
8542
  }
7775
8543
  }
8544
+ else if textField == txtFieldEmailAccountView.textField {
8545
+ if let bodyBackgroundColor = UserStoreSingleton.shared.secondary_font_col,
8546
+ let borderColor = UIColor(hex: bodyBackgroundColor) {
8547
+ viewTxtFieldEmailAccountView.layer.borderColor = borderColor.cgColor
8548
+ } else {
8549
+ viewTxtFieldEmailAccountView.layer.borderColor = UIColor.systemGray.cgColor
8550
+ }
8551
+ }
7776
8552
  //New Bank Account View
7777
8553
  else if textField == txtFieldAccountNameNewAccountView {
7778
8554
  if let bodyBackgroundColor = UserStoreSingleton.shared.secondary_font_col,