@jimrising/easymerchantsdk-react-native 1.3.1 → 1.3.3

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.
@@ -6,12 +6,18 @@
6
6
  //
7
7
 
8
8
  import UIKit
9
+ //import BlinkCard
9
10
 
10
11
  struct PaymentsData {
11
12
  var name = String()
12
13
  var image = String()
13
14
  }
14
15
 
16
+ enum GrailPaySource {
17
+ case existingAccount
18
+ case newAccount
19
+ }
20
+
15
21
  let easymerchantsdk = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle)
16
22
 
17
23
  class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
@@ -155,6 +161,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
155
161
  @IBOutlet weak var viewAccountType: UIView!
156
162
  @IBOutlet weak var tblViewAccountTypes: UITableView!
157
163
  @IBOutlet weak var btnCheckSavedAccountForFuture: UIButton!
164
+ @IBOutlet weak var txtFieldConfirmBankAccount: UITextField!
165
+ @IBOutlet weak var viewTextFieldConfirmAccount: UIView!
158
166
 
159
167
  //GrailPay
160
168
  @IBOutlet weak var grailPayBankLinkView: UIView!
@@ -173,6 +181,26 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
173
181
  @IBOutlet weak var imgGrailPayAbandonError: UIImageView!
174
182
  @IBOutlet weak var lblGrailPayAabandonError: UILabel!
175
183
 
184
+ //New GrailPay Account
185
+ @IBOutlet weak var viewAddNewGrailPayAccount: UIView!
186
+ @IBOutlet weak var lblGrailPayNewAccount: UILabel!
187
+ @IBOutlet weak var subLblGrailPayNewAccount: UILabel!
188
+ @IBOutlet weak var btnCloseGrailPayNewAccountView: UIButton!
189
+ @IBOutlet weak var btnLinkGrailPayAddNewAccount: FilledButton!
190
+ @IBOutlet weak var viewAbandonGrailPayNewAccountView: UIView!
191
+ @IBOutlet weak var lblAbandonGrailPayNewAccountView: UILabel!
192
+ @IBOutlet weak var grailPayNewSavedAccountView: UIStackView!
193
+ @IBOutlet weak var viewGrailPayNewAccountDetail: UIView!
194
+ @IBOutlet weak var btnSelectGrailPayNewAccount: UIButton!
195
+ @IBOutlet weak var lblGrailPayNewAccountNumber: UILabel!
196
+ @IBOutlet weak var lblGrailPayNewAccountType: UILabel!
197
+ @IBOutlet weak var btnCheckBoxGrailPayNewAccountSaveAccount: UIButton!
198
+ @IBOutlet weak var lblGrailPayNewAccountSaveForFuture: UILabel!
199
+ @IBOutlet weak var btnCheckBoxGrailPayNewAccountTerms: UIButton!
200
+ @IBOutlet weak var lblIagreeGrailPayNewAccountView: UILabel!
201
+ @IBOutlet weak var lblTermsAndConditionNewGrailPayAccountView: UILabel!
202
+ @IBOutlet weak var btnChangeNewGrailPayAccountView: UIButton!
203
+
176
204
  //SavedBank
177
205
  @IBOutlet weak var viewSingleSavedAccount: UIView!
178
206
  @IBOutlet weak var viewTermAndConditionsSingleAccountView: UIStackView!
@@ -183,7 +211,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
183
211
  @IBOutlet weak var lblAccountNumberSingleAccountView: UILabel!
184
212
  @IBOutlet weak var lblAccountTypeSingleAccountView: UILabel!
185
213
  @IBOutlet weak var btnPayNowSingleAccountView: FilledButton!
186
- @IBOutlet weak var viewSingleAccountViewHeight: NSLayoutConstraint!
214
+ // @IBOutlet weak var viewSingleAccountViewHeight: NSLayoutConstraint!
187
215
  @IBOutlet weak var viewChangedAccount: UIView!
188
216
  @IBOutlet weak var tblViewSavedBankAccounts: UITableView!
189
217
  @IBOutlet weak var tblViewSavedBankAccountsHeight: NSLayoutConstraint!
@@ -206,6 +234,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
206
234
  @IBOutlet weak var tblViewAccountTypeNewAccountView: UITableView!
207
235
  @IBOutlet weak var btnPayNowNewAccountView: FilledButton!
208
236
  @IBOutlet weak var lblSavedNewAccountForFuture: UILabel!
237
+ @IBOutlet weak var txtFieldConfirmAccountNewAccountView: UITextField!
238
+ @IBOutlet weak var viewTxtFieldConfirmNewAccountView: UIView!
209
239
 
210
240
  //Crypto
211
241
  @IBOutlet weak var viewCrypto: UIView!
@@ -276,7 +306,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
276
306
  var selectedBank: BankAccountModel?
277
307
  var isSelectForPayBank: Bool = false
278
308
  var selectedBankIndex: Int? = nil
279
- var arrAccountType = ["Saving","Current"]
309
+ var arrAccountType = ["Saving", "Checking"]
280
310
 
281
311
  var agreeTermsAndCondtition: Bool = false
282
312
 
@@ -305,6 +335,24 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
305
335
  var isBankAccountConnected = false
306
336
  var defaultBankAccountID: String?
307
337
 
338
+ var isNewBankAccountConnected = false
339
+ var defaultNewBankAccountID: String?
340
+
341
+ var isSavedNewGrailPayAccount: Bool = false
342
+
343
+ var grailPaySource: GrailPaySource?
344
+
345
+ //GrailPay Params
346
+ var grailPayAccountID: String?
347
+ var selectedGrailPayAccountType: String?
348
+ var selectedGrailPayAccountName: String?
349
+
350
+ //New GrailPay Account Params
351
+ var newGrailPayAccountID: String?
352
+ var selectedNewGrailPayAccountType: String?
353
+ var selectedNewGrailPayAccountName: String?
354
+ var grailPayNewAccountCustomerID: String?
355
+
308
356
  //MARK: - View Did Load
309
357
  override func viewDidLoad() {
310
358
  super.viewDidLoad()
@@ -422,6 +470,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
422
470
  setUpTermAndConditionsButton()
423
471
  setUpTermAndConditionsForSingleSavedAccountButton()
424
472
  setUpTermAndConditionsForGrailPaySavedAccountButton()
473
+ setUpTermAndConditionsForGrailPayNewAccountButton()
425
474
 
426
475
  settingsView.clipsToBounds = false
427
476
  settingsView.layer.shadowColor = UIColor.black.cgColor
@@ -469,7 +518,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
469
518
  func viewAppearanceOn() {
470
519
  viewTermAndConditionsSingleAccountView.isHidden = true
471
520
  btnPayNowSingleAccountView.isHidden = true
472
- viewSingleAccountViewHeight.constant = 68
521
+ // viewSingleAccountViewHeight.constant = 68
473
522
 
474
523
  viewNewBankAccount.isHidden = true
475
524
 
@@ -479,8 +528,15 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
479
528
 
480
529
  self.viewCrypto.isHidden = true
481
530
 
531
+ // self.viewAddNewGrailPayAccount.isHidden = true
532
+ // self.viewAbandonGrailPayNewAccountView.isHidden = true
533
+
482
534
  if selectedPaymentMethod == "Card" {
483
535
  self.grailPayBankLinkView.isHidden = true
536
+
537
+ self.viewAddNewGrailPayAccount.isHidden = true
538
+ self.viewAbandonGrailPayNewAccountView.isHidden = true
539
+
484
540
  if UserStoreSingleton.shared.isLoggedIn == true {
485
541
  if UserStoreSingleton.shared.customerId == nil {
486
542
  self.OTPView.isHidden = true
@@ -589,30 +645,41 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
589
645
  if self.isSelectForPayBank {
590
646
  self.viewTermAndConditionsSingleAccountView.isHidden = false
591
647
  self.btnPayNowSingleAccountView.isHidden = false
592
- self.viewSingleAccountViewHeight.constant = 170
648
+ // self.viewSingleAccountViewHeight.constant = 170
593
649
  } else {
594
650
  self.viewTermAndConditionsSingleAccountView.isHidden = true
595
651
  self.btnPayNowSingleAccountView.isHidden = true
596
- self.viewSingleAccountViewHeight.constant = 68
652
+ // self.viewSingleAccountViewHeight.constant = 68
597
653
  self.btnNext.isHidden = true
598
654
  self.btnNextHeight.constant = 0
599
655
  self.btnNextTopCon.constant = 0
600
656
  }
601
657
 
602
658
  if request?.authenticatedACH == true {
603
- self.viewBankFields.isHidden = true
604
- self.viewTermsAndConditions.isHidden = true
605
- self.grailPayBankLinkView.isHidden = false
606
- self.btnNext.isHidden = true
607
- self.btnNextHeight.constant = 0
608
- self.btnNextTopCon.constant = 0
609
- self.viewGrailPayAbandon.isHidden = true
610
-
611
- if isBankAccountConnected {
612
- self.viewBtnShowSavedCards.isHidden = true
613
- self.btnShowSavedCard.isHidden = true
614
- self.viewBtnShowSavedCardHeight.constant = 0
615
- self.viewBtnShowSavedCardTopCon.constant = 0
659
+ if isSavedNewGrailPayAccount == true {
660
+ updateUIForNewGrailPayAccount()
661
+ } else {
662
+ self.viewBankFields.isHidden = true
663
+ self.viewTermsAndConditions.isHidden = true
664
+
665
+ if self.grailPaySource == .existingAccount {
666
+ self.grailPayBankLinkView.isHidden = false
667
+ } else if self.grailPaySource == .newAccount {
668
+ self.grailPayBankLinkView.isHidden = true
669
+ self.viewSingleSavedAccount.isHidden = true
670
+ }
671
+ // self.grailPayBankLinkView.isHidden = false
672
+ self.btnNext.isHidden = true
673
+ self.btnNextHeight.constant = 0
674
+ self.btnNextTopCon.constant = 0
675
+ self.viewGrailPayAbandon.isHidden = true
676
+
677
+ if isBankAccountConnected {
678
+ self.viewBtnShowSavedCards.isHidden = true
679
+ self.btnShowSavedCard.isHidden = true
680
+ self.viewBtnShowSavedCardHeight.constant = 0
681
+ self.viewBtnShowSavedCardTopCon.constant = 0
682
+ }
616
683
  }
617
684
  }
618
685
  else {
@@ -649,7 +716,14 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
649
716
  } else {
650
717
  self.viewBankFields.isHidden = true
651
718
  self.viewTermsAndConditions.isHidden = true
652
- self.grailPayBankLinkView.isHidden = false
719
+
720
+ if self.grailPaySource == .existingAccount {
721
+ self.grailPayBankLinkView.isHidden = false
722
+ } else if self.grailPaySource == .newAccount {
723
+ self.grailPayBankLinkView.isHidden = true
724
+ self.viewSingleSavedAccount.isHidden = true
725
+ }
726
+ // self.grailPayBankLinkView.isHidden = false
653
727
  self.btnNext.isHidden = true
654
728
  self.btnNextHeight.constant = 0
655
729
  self.btnNextTopCon.constant = 0
@@ -682,6 +756,9 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
682
756
  viewSingelBankAccount.backgroundColor = uiColor
683
757
  viewGrailPaySavedAccount.backgroundColor = uiColor
684
758
  viewGrailPayAbandon.backgroundColor = uiColor
759
+ settingsView.backgroundColor = uiColor
760
+ viewAbandonGrailPayNewAccountView.backgroundColor = uiColor
761
+ viewGrailPayNewAccountDetail.backgroundColor = uiColor
685
762
  }
686
763
 
687
764
  if let primaryBtnBackGroundColor = UserStoreSingleton.shared.primary_btn_bg_col,
@@ -691,6 +768,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
691
768
  btnResendOTP.tintColor = uiColor
692
769
  btnSelectSingleSavedCard.tintColor = uiColor
693
770
  btnChangeSavedCard.tintColor = uiColor
771
+ btnChangeNewGrailPayAccountView.tintColor = uiColor
694
772
  btnSelectUpdateCardView.tintColor = uiColor
695
773
  btnCheckSavedAccountForFuture.tintColor = uiColor
696
774
  btnAgreeTermsConditions.tintColor = uiColor
@@ -708,6 +786,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
708
786
  btnCheckBoxGrailPayAgreeTerms.tintColor = uiColor
709
787
  lblGrailPayTermsAndCondition.textColor = uiColor
710
788
  lblGrailPayAabandonError.textColor = uiColor
789
+ btnSelectGrailPayNewAccount.tintColor = uiColor
790
+ btnCheckBoxGrailPayNewAccountSaveAccount.tintColor = uiColor
791
+ btnCheckBoxGrailPayNewAccountTerms.tintColor = uiColor
792
+ lblAbandonGrailPayNewAccountView.textColor = uiColor
793
+ lblTermsAndConditionNewGrailPayAccountView.textColor = uiColor
711
794
 
712
795
  btnScanCard.tintColor = uiColor // Set color for the image
713
796
  btnScanCard.setTitleColor(uiColor, for: .normal) // Set color for the text
@@ -727,6 +810,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
727
810
  txtFieldAccountType.tintColor = uiColor
728
811
  txtFieldAccountNumber.tintColor = uiColor
729
812
  txtFieldRoutingNumber.tintColor = uiColor
813
+ txtFieldConfirmBankAccount.tintColor = uiColor
730
814
  txtFieldCVVNewCardView.tintColor = uiColor
731
815
  txtFieldCVVUpdateCardView.tintColor = uiColor
732
816
  txtFieldCVVSingleSavedCard.tintColor = uiColor
@@ -743,6 +827,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
743
827
  cardExpiryTextField.tintColor = uiColor
744
828
  cardCvvTextField.tintColor = uiColor
745
829
  cardNameTextField.tintColor = uiColor
830
+ txtFieldConfirmAccountNewAccountView.tintColor = uiColor
746
831
  }
747
832
 
748
833
  if let secondaryBtnBackgroundColor = UserStoreSingleton.shared.primary_btn_font_col,
@@ -820,11 +905,15 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
820
905
  btnSettings.tintColor = placeholderColor
821
906
  lblGrailPaySaveAccountForFuture.textColor = placeholderColor
822
907
  lblGrailPayAgreeTo.textColor = placeholderColor
908
+ lblGrailPayNewAccountSaveForFuture.textColor = placeholderColor
909
+ lblIagreeGrailPayNewAccountView.textColor = placeholderColor
823
910
  }
824
911
 
825
912
  if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
826
913
  let uiColor = UIColor(hex: primaryFontColor) {
827
914
  lblChosePaymentMethod.textColor = uiColor
915
+ lblGrailPayNewAccount.tintColor = uiColor
916
+ subLblGrailPayNewAccount.textColor = uiColor
828
917
  lblSavedInfoEmailView.textColor = uiColor
829
918
  subLblSavedEmailInfo.textColor = uiColor
830
919
  lblSavedInfoOTPView.textColor = uiColor
@@ -852,8 +941,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
852
941
  lblSetting.textColor = uiColor
853
942
  btnChangeLanguage.tintColor = uiColor
854
943
  btnLogout.tintColor = uiColor
855
- lblGrailPaySavedAccountNumber.tintColor = uiColor
856
- lblGrailPaySavedAccountType.tintColor = uiColor
944
+ lblGrailPaySavedAccountNumber.textColor = uiColor
945
+ lblGrailPaySavedAccountType.textColor = uiColor
946
+ lblGrailPayNewAccountNumber.textColor = uiColor
947
+ lblGrailPayNewAccountType.textColor = uiColor
857
948
 
858
949
  txtFieldEmail.textColor = uiColor
859
950
  txtFieldOTPText1.textColor = uiColor
@@ -937,6 +1028,14 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
937
1028
  lblGrailPayTermsAndCondition.font = UIFont.systemFont(ofSize: fontSize)
938
1029
  lblGrailPayAgreeTo.font = UIFont.systemFont(ofSize: fontSize)
939
1030
  lblGrailPaySaveAccountForFuture.font = UIFont.systemFont(ofSize: fontSize)
1031
+ lblIagreeGrailPayNewAccountView.font = UIFont.systemFont(ofSize: fontSize)
1032
+ lblGrailPayNewAccountSaveForFuture.font = UIFont.systemFont(ofSize: fontSize)
1033
+ lblTermsAndConditionNewGrailPayAccountView.font = UIFont.systemFont(ofSize: fontSize)
1034
+ subLblGrailPayNewAccount.font = UIFont.systemFont(ofSize: fontSize)
1035
+ lblAbandonGrailPayNewAccountView.font = UIFont.systemFont(ofSize: fontSize)
1036
+ lblGrailPayNewAccountNumber.font = UIFont.systemFont(ofSize: fontSize)
1037
+ lblGrailPayNewAccountType.font = UIFont.systemFont(ofSize: fontSize)
1038
+
940
1039
  lblGrailPaySavedAccountType.font = UIFont.systemFont(ofSize: fontSize)
941
1040
  lblGrailPaySavedAccountNumber.font = UIFont.systemFont(ofSize: fontSize)
942
1041
 
@@ -984,6 +1083,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
984
1083
  txtFieldRoutingNumberNewAccountView.delegate = self
985
1084
  txtFieldAccountTypeNewAccountView.delegate = self
986
1085
  txtFieldAccountNumber.delegate = self
1086
+ txtFieldConfirmBankAccount.delegate = self
987
1087
  }
988
1088
 
989
1089
  private func updateSaveButtons() {
@@ -1071,11 +1171,39 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1071
1171
  lblGrailPayTermsAndCondition.attributedText = attributedString
1072
1172
  lblGrailPayTermsAndCondition.isUserInteractionEnabled = true
1073
1173
  // Add tap gesture recognizer
1074
- let tap = UITapGestureRecognizer(target: self, action: #selector(aboutTermsAndGrailPayConditions))
1174
+ let tap = UITapGestureRecognizer(target: self, action: #selector(aboutTermsGrailPayConditions))
1075
1175
  lblGrailPayTermsAndCondition.addGestureRecognizer(tap)
1076
1176
  }
1077
1177
 
1078
- @objc func aboutTermsAndGrailPayConditions(sender: UITapGestureRecognizer) {
1178
+ @objc func aboutTermsGrailPayConditions(sender: UITapGestureRecognizer) {
1179
+ guard let vc = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
1180
+ print("Error: Could not find TermAndConditionsVC in easymerchantsdk storyboard.")
1181
+ return
1182
+ }
1183
+ vc.modalPresentationStyle = .overFullScreen
1184
+ present(vc, animated: true, completion: nil)
1185
+ }
1186
+
1187
+ //MARK: - Term & Conditions setup for GrailPay saved bank account view
1188
+ func setUpTermAndConditionsForGrailPayNewAccountButton() {
1189
+ let string = "TERMS & CONDITIONS"
1190
+ let attributedString = NSMutableAttributedString(string: string)
1191
+
1192
+ attributedString.addAttribute(
1193
+ .font,
1194
+ value: UIFont.systemFont(ofSize: 12, weight: .medium),
1195
+ range: NSRange(location: 0, length: string.count)
1196
+ )
1197
+
1198
+ // Assign the attributed string to the label
1199
+ lblTermsAndConditionNewGrailPayAccountView.attributedText = attributedString
1200
+ lblTermsAndConditionNewGrailPayAccountView.isUserInteractionEnabled = true
1201
+ // Add tap gesture recognizer
1202
+ let tap = UITapGestureRecognizer(target: self, action: #selector(aboutTermsGrailPayNewAccountConditions))
1203
+ lblTermsAndConditionNewGrailPayAccountView.addGestureRecognizer(tap)
1204
+ }
1205
+
1206
+ @objc func aboutTermsGrailPayNewAccountConditions(sender: UITapGestureRecognizer) {
1079
1207
  guard let vc = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
1080
1208
  print("Error: Could not find TermAndConditionsVC in easymerchantsdk storyboard.")
1081
1209
  return
@@ -1538,72 +1666,41 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1538
1666
  }
1539
1667
 
1540
1668
  @IBAction func actionBtnGrailPayCheckBoxSaveAccountForFuture(_ sender: UIButton) {
1541
-
1669
+ isSavedForFuture.toggle()
1670
+ // Change the image based on the state
1671
+ let imageName = isSavedForFuture ? "checkmark.square.fill" : "square"
1672
+ btnCheckBoxGrailPaySaveAccount.setImage(UIImage(systemName: imageName), for: .normal)
1542
1673
  }
1543
1674
 
1544
1675
  @IBAction func actionBtnGrailPayCheckAgreeTerms(_ sender: UIButton) {
1545
-
1676
+ agreeTermsAndCondtition.toggle()
1677
+ // Change the image based on the state
1678
+ let imageName = agreeTermsAndCondtition ? "checkmark.square.fill" : "square"
1679
+ btnCheckBoxGrailPayAgreeTerms.setImage(UIImage(systemName: imageName), for: .normal)
1546
1680
  }
1547
1681
 
1548
- @IBAction private func actionBtnLinkGrailPayBankAccount(_ sender: UIButton) {
1549
- if isBankAccountConnected {
1550
- print("Proceed with bank-connected actions.")
1551
- } else {
1552
- guard let grailPayParams = request.grailPayParams else {
1553
- print("GrailPay parameters not available in request")
1554
- return
1555
- }
1556
-
1557
- GrailPayHelper.presentGrailPay(from: self, request: grailPayParams) { [weak self] result in
1558
- guard let self = self else { return }
1559
-
1560
- switch result.type {
1561
- case .success:
1562
- print("📦 Raw chargeData type: \(type(of: result.chargeData)) value: \(String(describing: result.chargeData))")
1563
-
1564
- // Safely unwrap the chargeData dictionary
1565
- if let chargeData = result.chargeData,
1566
- let dataArray = chargeData["data"] as? [[String: Any]],
1567
- let firstAccount = dataArray.first {
1568
-
1569
- print("✅ Bank connected: \(firstAccount)")
1570
- // 🚀 Call Account Connect API
1571
- self.accountConnectApi(account: firstAccount)
1572
- // ✅ Optional UI updates
1573
- self.lblGrailPayAabandonError.text = "Default account successfully set"
1574
- self.viewGrailPayAbandon.isHidden = false
1575
-
1576
- if selectedPaymentMethod == "Bank" {
1577
- self.viewBtnShowSavedCards.isHidden = true
1578
- }
1579
-
1580
- let amountText = String(format: "$%.2f", request.amount)
1581
- let submitText = request.submitButtonText
1582
-
1583
- let defaultTitle = (submitText?.isEmpty == false)
1584
- ? "\(submitText!) (\(amountText))"
1585
- : "Pay Now (\(amountText))"
1586
-
1587
- btnLinkBankAccount.setTitle(defaultTitle, for: .normal)
1588
- } else {
1589
- print("⚠️ chargeData does not contain valid account data")
1590
- self.lblGrailPayAabandonError.text = "Failed to retrieve account info"
1591
- self.lblGrailPayAabandonError.textColor = .systemRed
1592
- self.viewGrailPayAbandon.isHidden = false
1593
- }
1594
- case .cancelled:
1595
- print("⚠️ GrailPay cancelled")
1596
- self.viewGrailPayAbandon.isHidden = false
1597
- self.lblGrailPayAabandonError.text = "User has abandoned the action"
1598
- case .error:
1599
- if let error = result.error {
1600
- print("❌ GrailPay error: \(error.localizedDescription)")
1601
- } else {
1602
- print("❌ GrailPay unknown error")
1603
- }
1604
- }
1605
- }
1606
- }
1682
+ @IBAction func actionBtnChangeGrailPayNewAccount(_ sender: UIButton) {
1683
+ self.viewChangedAccount.isHidden = false
1684
+ self.viewAddNewGrailPayAccount.isHidden = true
1685
+ }
1686
+
1687
+ @IBAction func actionBtnCheckBoxGrailPayNewAccountSavedForFuture(_ sender: UIButton) {
1688
+ isSavedForFuture.toggle()
1689
+ // Change the image based on the state
1690
+ let imageName = isSavedForFuture ? "checkmark.square.fill" : "square"
1691
+ btnCheckBoxGrailPayNewAccountSaveAccount.setImage(UIImage(systemName: imageName), for: .normal)
1692
+ }
1693
+
1694
+ @IBAction func actionBtnCheckBoxTermsGrailPayNewAccount(_ sender: UIButton) {
1695
+ agreeTermsAndCondtition.toggle()
1696
+ // Change the image based on the state
1697
+ let imageName = agreeTermsAndCondtition ? "checkmark.square.fill" : "square"
1698
+ btnCheckBoxGrailPayNewAccountTerms.setImage(UIImage(systemName: imageName), for: .normal)
1699
+ }
1700
+
1701
+ @IBAction func actionBtnCloseGrailPayNewAccountView(_ sender: UIButton) {
1702
+ self.viewAddNewGrailPayAccount.isHidden = true
1703
+ self.viewSingleSavedAccount.isHidden = false
1607
1704
  }
1608
1705
 
1609
1706
  //MARK: - Account Connect Api
@@ -1686,9 +1783,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1686
1783
  if let status = responseObject["status"] as? Int, status == 1 {
1687
1784
  self.isBankAccountConnected = true
1688
1785
  self.defaultBankAccountID = responseObject["default_account"] as? String
1786
+ self.grailPayAccountID = responseObject["account_id"] as? String
1689
1787
  } else {
1690
1788
  self.isBankAccountConnected = false
1691
1789
  self.defaultBankAccountID = nil
1790
+ self.grailPayAccountID = nil
1692
1791
  }
1693
1792
 
1694
1793
  DispatchQueue.main.async {
@@ -1723,14 +1822,693 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1723
1822
  else {
1724
1823
  print("❌ Invalid JSON format")
1725
1824
  }
1726
- } catch {
1727
- print("❌ JSON Parsing Error: \(error)")
1825
+ } catch {
1826
+ print("❌ JSON Parsing Error: \(error)")
1827
+ }
1828
+ } else {
1829
+ print("❌ HTTP Status Code: \(httpResponse.statusCode)")
1830
+ }
1831
+ }
1832
+
1833
+ task.resume()
1834
+ }
1835
+
1836
+ //MARK: - Account Connect Api New GrailPay Account Add
1837
+ func newGrailPayAccountConnectApi(account: [String: Any]) {
1838
+ showLoadingIndicator()
1839
+
1840
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.accountConnect.path()
1841
+
1842
+ guard let serviceURL = URL(string: fullURL) else {
1843
+ print("Invalid URL")
1844
+ hideLoadingIndicator()
1845
+ return
1846
+ }
1847
+
1848
+ var request = URLRequest(url: serviceURL)
1849
+ request.httpMethod = "POST"
1850
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
1851
+
1852
+ // Add API headers
1853
+ if let apiKey = EnvironmentConfig.apiKey,
1854
+ let apiSecret = EnvironmentConfig.apiSecret {
1855
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
1856
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
1857
+ }
1858
+
1859
+ // Save customer_id for use in charge API
1860
+ let customerID = UserStoreSingleton.shared.customerId ?? ""
1861
+ self.grailPayNewAccountCustomerID = customerID
1862
+
1863
+ // Prepare parameters
1864
+ let params: [String: Any] = [
1865
+ "account_uuid": account["account_uuid"] ?? "",
1866
+ "user_uuid": account["user_uuid"] ?? "",
1867
+ "aggregator_type": account["aggregator_type"] ?? "bank_link",
1868
+ "vendor_id": account["vendor_id"] ?? "",
1869
+ "created_at": account["created_at"] ?? "",
1870
+ "updated_at": account["updated_at"] ?? "",
1871
+ "account_id": account["account_id"] ?? "",
1872
+ "account_number": account["account_number"] ?? "",
1873
+ "provider_name": account["provider_name"] ?? "",
1874
+ "routing_number": account["routing_number"] ?? "",
1875
+ "name": account["name"] ?? "",
1876
+ "account_type": account["account_type"] ?? "",
1877
+ "account_status": account["account_status"] ?? "",
1878
+ "customer_id": customerID // add if needed
1879
+ ]
1880
+
1881
+ do {
1882
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
1883
+ request.httpBody = jsonData
1884
+
1885
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
1886
+ print("🔍 JSON Payload:\n\(jsonString)")
1887
+ }
1888
+ } catch {
1889
+ print("❌ JSON Serialization Error: \(error)")
1890
+ hideLoadingIndicator()
1891
+ return
1892
+ }
1893
+
1894
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
1895
+ DispatchQueue.main.async { self.hideLoadingIndicator() }
1896
+
1897
+ if let error = error {
1898
+ print("❌ Request Error: \(error.localizedDescription)")
1899
+ return
1900
+ }
1901
+
1902
+ guard let httpResponse = response as? HTTPURLResponse else {
1903
+ print("❌ Invalid response")
1904
+ return
1905
+ }
1906
+
1907
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
1908
+ guard let data = data else {
1909
+ print("❌ No data received")
1910
+ return
1911
+ }
1912
+
1913
+ do {
1914
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
1915
+ print("✅ Account Connected Successfully: \(responseObject)")
1916
+
1917
+ if let status = responseObject["status"] as? Int, status == 1 {
1918
+ DispatchQueue.main.async {
1919
+ self.isNewBankAccountConnected = true
1920
+ self.defaultNewBankAccountID = responseObject["default_account"] as? String
1921
+ self.newGrailPayAccountID = responseObject["account_id"] as? String
1922
+ self.isSavedNewGrailPayAccount = true
1923
+ self.updateUIForNewGrailPayAccount()
1924
+ }
1925
+ }
1926
+ else {
1927
+ self.isNewBankAccountConnected = false
1928
+ self.defaultNewBankAccountID = nil
1929
+ self.newGrailPayAccountID = nil
1930
+ }
1931
+
1932
+ DispatchQueue.main.async {
1933
+ // ✅ Show saved bank UI
1934
+ self.grailPayNewSavedAccountView.isHidden = false
1935
+ self.viewGrailPayNewAccountDetail.isHidden = false
1936
+
1937
+ if let accountNumber = account["account_number"] as? String {
1938
+ let last4 = String(accountNumber.suffix(4))
1939
+ self.lblGrailPayNewAccountNumber.text = "****\(last4)"
1940
+ } else {
1941
+ self.lblGrailPayNewAccountNumber.text = "****----" // Default/fallback text
1942
+ }
1943
+
1944
+ // ✅ Set account type
1945
+ if let accountType = account["account_type"] as? String {
1946
+ self.lblGrailPayNewAccountType.text = "Type: \(accountType.capitalized)"
1947
+ }
1948
+
1949
+ // ✅ Optionally hide abandon view if visible
1950
+ self.viewAbandonGrailPayNewAccountView.isHidden = true
1951
+
1952
+ if self.selectedPaymentMethod == "Bank" {
1953
+ self.viewBtnShowSavedCards.isHidden = true
1954
+ self.btnShowSavedCard.isHidden = true
1955
+ self.viewBtnShowSavedCardHeight.constant = 0
1956
+ self.viewBtnShowSavedCardTopCon.constant = 0
1957
+ }
1958
+ }
1959
+ }
1960
+
1961
+ else {
1962
+ print("❌ Invalid JSON format")
1963
+ }
1964
+ } catch {
1965
+ print("❌ JSON Parsing Error: \(error)")
1966
+ }
1967
+ } else {
1968
+ print("❌ HTTP Status Code: \(httpResponse.statusCode)")
1969
+ }
1970
+ }
1971
+
1972
+ task.resume()
1973
+ }
1974
+
1975
+ @IBAction private func actionBtnLinkGrailPayBankAccount(_ sender: UIButton) {
1976
+ if isBankAccountConnected {
1977
+ guard agreeTermsAndCondtition else {
1978
+ self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
1979
+ return
1980
+ }
1981
+
1982
+ if isSavedForFuture {
1983
+ // Navigate to next screen without calling the API
1984
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
1985
+ vc.selectedPaymentMethod = "GrailPay"
1986
+ vc.easyPayDelegate = self.easyPayDelegate
1987
+ vc.grailPayAccountID = self.grailPayAccountID
1988
+ vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
1989
+ vc.selectedGrailPayAccountName = self.selectedGrailPayAccountName
1990
+ self.navigationController?.pushViewController(vc, animated: true)
1991
+ } else {
1992
+ // Proceed to call API if not saving for future
1993
+ grailPayAccountChargeApi()
1994
+ }
1995
+ } else {
1996
+ grailPaySource = .existingAccount
1997
+ launchGrailPay()
1998
+ }
1999
+ }
2000
+
2001
+ @IBAction func actionLinkNewGrailPayAccount(_ sender: UIButton) {
2002
+ if isNewBankAccountConnected {
2003
+ guard agreeTermsAndCondtition else {
2004
+ self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
2005
+ return
2006
+ }
2007
+
2008
+ if isSavedForFuture {
2009
+ grailPayNewAccountSavingChargeApi()
2010
+ }
2011
+ else {
2012
+ grailPayNewAccountChargeApi()
2013
+ }
2014
+ } else {
2015
+ grailPaySource = .newAccount
2016
+ launchGrailPay()
2017
+ }
2018
+ }
2019
+
2020
+ func launchGrailPay() {
2021
+ guard let grailPayParams = request.grailPayParams else {
2022
+ print("GrailPay parameters not available in request")
2023
+ return
2024
+ }
2025
+
2026
+ GrailPayHelper.presentGrailPay(from: self, request: grailPayParams) { [weak self] result in
2027
+ guard let self = self else { return }
2028
+
2029
+ switch result.type {
2030
+ case .success:
2031
+ if let chargeData = result.chargeData,
2032
+ let dataArray = chargeData["data"] as? [[String: Any]],
2033
+ let firstAccount = dataArray.first {
2034
+
2035
+ print("✅ Bank connected: \(firstAccount)")
2036
+ self.selectedGrailPayAccountType = firstAccount["account_type"] as? String
2037
+ self.selectedGrailPayAccountName = firstAccount["name"] as? String
2038
+
2039
+ if self.grailPaySource == .existingAccount {
2040
+ self.accountConnectApi(account: firstAccount)
2041
+ self.lblGrailPayAabandonError.text = "Default account successfully set"
2042
+ self.viewGrailPayAbandon.isHidden = false
2043
+ } else if self.grailPaySource == .newAccount {
2044
+ self.newGrailPayAccountConnectApi(account: firstAccount)
2045
+ self.lblAbandonGrailPayNewAccountView.text = "Default account successfully set"
2046
+ self.viewAbandonGrailPayNewAccountView.isHidden = false
2047
+
2048
+ self.selectedNewGrailPayAccountType = firstAccount["account_type"] as? String
2049
+ self.selectedNewGrailPayAccountName = firstAccount["name"] as? String
2050
+ }
2051
+
2052
+ if self.selectedPaymentMethod == "Bank" {
2053
+ self.viewBtnShowSavedCards.isHidden = true
2054
+ }
2055
+
2056
+ let amountText = String(format: "$%.2f", self.request.amount)
2057
+ let submitText = self.request.submitButtonText
2058
+ let defaultTitle = (submitText?.isEmpty == false)
2059
+ ? "\(submitText!) (\(amountText))"
2060
+ : "Pay Now (\(amountText))"
2061
+
2062
+ if self.grailPaySource == .existingAccount {
2063
+ self.btnLinkBankAccount.setTitle(defaultTitle, for: .normal)
2064
+ } else if self.grailPaySource == .newAccount {
2065
+ self.btnLinkGrailPayAddNewAccount.setTitle(defaultTitle, for: .normal)
2066
+ }
2067
+
2068
+ } else {
2069
+ print("⚠️ chargeData does not contain valid account data")
2070
+ self.showGrailPayError(message: "Failed to retrieve account info")
2071
+ }
2072
+
2073
+ case .cancelled:
2074
+ print("⚠️ GrailPay cancelled")
2075
+ self.showGrailPayError(message: "User has abandoned the action")
2076
+
2077
+ case .error:
2078
+ let errorMsg = result.error?.localizedDescription ?? "Unknown error"
2079
+ print("❌ GrailPay error: \(errorMsg)")
2080
+ self.showGrailPayError(message: errorMsg)
2081
+ }
2082
+ }
2083
+ }
2084
+
2085
+ func updateUIForNewGrailPayAccount() {
2086
+ self.viewAddNewGrailPayAccount.isHidden = false
2087
+ self.viewAbandonGrailPayNewAccountView.isHidden = false
2088
+ self.viewSingleSavedAccount.isHidden = true
2089
+ self.grailPayBankLinkView.isHidden = true
2090
+ }
2091
+
2092
+ func showGrailPayError(message: String) {
2093
+ if grailPaySource == .existingAccount {
2094
+ lblGrailPayAabandonError.text = message
2095
+ lblGrailPayAabandonError.textColor = .systemRed
2096
+ viewGrailPayAbandon.isHidden = false
2097
+ }
2098
+ else if grailPaySource == .newAccount {
2099
+ lblAbandonGrailPayNewAccountView.text = message
2100
+ lblAbandonGrailPayNewAccountView.textColor = .systemRed
2101
+ viewAbandonGrailPayNewAccountView.isHidden = false
2102
+ }
2103
+ }
2104
+
2105
+ //MARK: - Grail Pay Banking Payment Charge Api without saving bank and without Login
2106
+ func grailPayAccountChargeApi() {
2107
+ showLoadingIndicator()
2108
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
2109
+
2110
+ guard let serviceURL = URL(string: fullURL) else {
2111
+ print("Invalid URL")
2112
+ hideLoadingIndicator()
2113
+ return
2114
+ }
2115
+
2116
+ var request = URLRequest(url: serviceURL)
2117
+ request.httpMethod = "POST"
2118
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
2119
+
2120
+ let token = UserStoreSingleton.shared.clientToken
2121
+ print("Setting clientToken header: \(token ?? "None")")
2122
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2123
+
2124
+ if let apiKey = EnvironmentConfig.apiKey {
2125
+ request.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
2126
+ }
2127
+ if let apiSecret = EnvironmentConfig.apiSecret {
2128
+ request.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
2129
+ }
2130
+
2131
+ let params: [String: Any] = [
2132
+ "account_id": self.grailPayAccountID ?? "",
2133
+ "account_type": self.selectedGrailPayAccountType ?? "",
2134
+ "name": self.selectedGrailPayAccountName ?? "",
2135
+ "description": "payment checkout",
2136
+ "email": "newmerchantadminuser12@test.com"
2137
+ ]
2138
+
2139
+ print(params)
2140
+
2141
+ do {
2142
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
2143
+ request.httpBody = jsonData
2144
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
2145
+ print("JSON Payload: \(jsonString)")
2146
+ }
2147
+ } catch let error {
2148
+ print("Error creating JSON data: \(error)")
2149
+ hideLoadingIndicator()
2150
+ return
2151
+ }
2152
+
2153
+ let session = URLSession.shared
2154
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
2155
+
2156
+ DispatchQueue.main.async {
2157
+ self.hideLoadingIndicator() // Stop loader when response is received
2158
+ }
2159
+
2160
+ if let error = error {
2161
+ print("Error: \(error.localizedDescription)")
2162
+ return
2163
+ }
2164
+
2165
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
2166
+ print("Invalid response")
2167
+ return
2168
+ }
2169
+
2170
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
2171
+ if let data = serviceData {
2172
+ do {
2173
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
2174
+ print("Response Data: \(responseObject)")
2175
+
2176
+ // Check if status is 0 and handle the error
2177
+ if let status = responseObject["status"] as? Int, status == 0 {
2178
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
2179
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
2180
+ } else {
2181
+ DispatchQueue.main.async {
2182
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
2183
+ paymentDoneVC.chargeData = responseObject
2184
+ // Pass the selected payment method
2185
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
2186
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
2187
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
2188
+ }
2189
+ }
2190
+ }
2191
+ } else {
2192
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
2193
+ }
2194
+ } catch let jsonError {
2195
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
2196
+ }
2197
+ } else {
2198
+ self.presentPaymentErrorVC(errorMessage: "No data received")
2199
+ }
2200
+ } else {
2201
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
2202
+ }
2203
+ }
2204
+ task.resume()
2205
+ }
2206
+
2207
+ //MARK: - GrailPay New Account Banking Payment Charge Api without saving bank.
2208
+ func grailPayNewAccountChargeApi() {
2209
+ showLoadingIndicator()
2210
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
2211
+
2212
+ guard let serviceURL = URL(string: fullURL) else {
2213
+ print("Invalid URL")
2214
+ hideLoadingIndicator()
2215
+ return
2216
+ }
2217
+
2218
+ var request = URLRequest(url: serviceURL)
2219
+ request.httpMethod = "POST"
2220
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
2221
+
2222
+ let token = UserStoreSingleton.shared.clientToken
2223
+ print("Setting clientToken header: \(token ?? "None")")
2224
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2225
+
2226
+ if let apiKey = EnvironmentConfig.apiKey {
2227
+ request.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
2228
+ }
2229
+ if let apiSecret = EnvironmentConfig.apiSecret {
2230
+ request.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
2231
+ }
2232
+
2233
+ let params: [String: Any] = [
2234
+ "account_id": self.newGrailPayAccountID ?? "",
2235
+ "account_type": self.selectedNewGrailPayAccountType ?? "",
2236
+ "name": self.selectedNewGrailPayAccountName ?? "",
2237
+ "description": "payment checkout",
2238
+ "customer_id": self.grailPayNewAccountCustomerID ?? ""
2239
+ ]
2240
+
2241
+ print(params)
2242
+
2243
+ do {
2244
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
2245
+ request.httpBody = jsonData
2246
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
2247
+ print("JSON Payload: \(jsonString)")
2248
+ }
2249
+ } catch let error {
2250
+ print("Error creating JSON data: \(error)")
2251
+ hideLoadingIndicator()
2252
+ return
2253
+ }
2254
+
2255
+ let session = URLSession.shared
2256
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
2257
+
2258
+ DispatchQueue.main.async {
2259
+ self.hideLoadingIndicator() // Stop loader when response is received
2260
+ }
2261
+
2262
+ if let error = error {
2263
+ print("Error: \(error.localizedDescription)")
2264
+ return
2265
+ }
2266
+
2267
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
2268
+ print("Invalid response")
2269
+ return
2270
+ }
2271
+
2272
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
2273
+ if let data = serviceData {
2274
+ do {
2275
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
2276
+ print("Response Data: \(responseObject)")
2277
+
2278
+ // Check if status is 0 and handle the error
2279
+ if let status = responseObject["status"] as? Int, status == 0 {
2280
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
2281
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
2282
+ } else {
2283
+ DispatchQueue.main.async {
2284
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
2285
+ paymentDoneVC.chargeData = responseObject
2286
+ // Pass the selected payment method
2287
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
2288
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
2289
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
2290
+ }
2291
+ }
2292
+ }
2293
+ } else {
2294
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
2295
+ }
2296
+ } catch let jsonError {
2297
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
2298
+ }
2299
+ } else {
2300
+ self.presentPaymentErrorVC(errorMessage: "No data received")
2301
+ }
2302
+ } else {
2303
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
2304
+ }
2305
+ }
2306
+ task.resume()
2307
+ }
2308
+
2309
+ //MARK: - GrailPay New Account Banking Payment Charge Api with saving bank.
2310
+ func grailPayNewAccountSavingChargeApi() {
2311
+ showLoadingIndicator()
2312
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
2313
+
2314
+ guard let serviceURL = URL(string: fullURL) else {
2315
+ print("Invalid URL")
2316
+ hideLoadingIndicator()
2317
+ return
2318
+ }
2319
+
2320
+ var request = URLRequest(url: serviceURL)
2321
+ request.httpMethod = "POST"
2322
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
2323
+
2324
+ let token = UserStoreSingleton.shared.clientToken
2325
+ print("Setting clientToken header: \(token ?? "None")")
2326
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2327
+
2328
+ if let apiKey = EnvironmentConfig.apiKey {
2329
+ request.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
2330
+ }
2331
+ if let apiSecret = EnvironmentConfig.apiSecret {
2332
+ request.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
2333
+ }
2334
+
2335
+ let params: [String: Any] = [
2336
+ "account_id": self.newGrailPayAccountID ?? "",
2337
+ "account_type": self.selectedNewGrailPayAccountType ?? "",
2338
+ "name": self.selectedNewGrailPayAccountName ?? "",
2339
+ "description": "payment checkout",
2340
+ "customer_id": self.grailPayNewAccountCustomerID ?? "",
2341
+ "save_account" : 1,
2342
+ "is_default": 1
2343
+ ]
2344
+
2345
+ print(params)
2346
+
2347
+ do {
2348
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
2349
+ request.httpBody = jsonData
2350
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
2351
+ print("JSON Payload: \(jsonString)")
2352
+ }
2353
+ } catch let error {
2354
+ print("Error creating JSON data: \(error)")
2355
+ hideLoadingIndicator()
2356
+ return
2357
+ }
2358
+
2359
+ let session = URLSession.shared
2360
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
2361
+
2362
+ DispatchQueue.main.async {
2363
+ self.hideLoadingIndicator() // Stop loader when response is received
2364
+ }
2365
+
2366
+ if let error = error {
2367
+ print("Error: \(error.localizedDescription)")
2368
+ return
2369
+ }
2370
+
2371
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
2372
+ print("Invalid response")
2373
+ return
2374
+ }
2375
+
2376
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
2377
+ if let data = serviceData {
2378
+ do {
2379
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
2380
+ print("Response Data: \(responseObject)")
2381
+
2382
+ // Check if status is 0 and handle the error
2383
+ if let status = responseObject["status"] as? Int, status == 0 {
2384
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
2385
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
2386
+ } else {
2387
+ DispatchQueue.main.async {
2388
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
2389
+ paymentDoneVC.chargeData = responseObject
2390
+ // Pass the selected payment method
2391
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
2392
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
2393
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
2394
+ }
2395
+ }
2396
+ }
2397
+ } else {
2398
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
2399
+ }
2400
+ } catch let jsonError {
2401
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
2402
+ }
2403
+ } else {
2404
+ self.presentPaymentErrorVC(errorMessage: "No data received")
2405
+ }
2406
+ } else {
2407
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
2408
+ }
2409
+ }
2410
+ task.resume()
2411
+ }
2412
+
2413
+ //MARK: - GrailPay Banking Account Charge Api from single saved bank account.
2414
+ func grailPayAaccountChargeSingleSavedAccountApi() {
2415
+ showLoadingIndicator()
2416
+
2417
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
2418
+
2419
+ guard let serviceURL = URL(string: fullURL) else {
2420
+ print("Invalid URL")
2421
+ hideLoadingIndicator()
2422
+ return
2423
+ }
2424
+
2425
+ var request = URLRequest(url: serviceURL)
2426
+ request.httpMethod = "POST"
2427
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
2428
+
2429
+ let token = UserStoreSingleton.shared.clientToken
2430
+ print("Setting clientToken header: \(token ?? "None")")
2431
+ request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2432
+
2433
+ if let apiKey = EnvironmentConfig.apiKey {
2434
+ request.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
2435
+ }
2436
+ if let apiSecret = EnvironmentConfig.apiSecret {
2437
+ request.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
2438
+ }
2439
+
2440
+ let params: [String: Any] = [
2441
+ "account_id": selectedbankAccounts?.account_id ?? "",
2442
+ "customer": selectedbankAccounts?.customer_id ?? "",
2443
+ "description": "payment checkout",
2444
+ "currency": "usd",
2445
+ ]
2446
+
2447
+ print(params)
2448
+
2449
+ do {
2450
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
2451
+ request.httpBody = jsonData
2452
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
2453
+ print("JSON Payload: \(jsonString)")
2454
+ }
2455
+ } catch let error {
2456
+ print("Error creating JSON data: \(error)")
2457
+ hideLoadingIndicator()
2458
+ return
2459
+ }
2460
+
2461
+ let session = URLSession.shared
2462
+ let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
2463
+
2464
+ DispatchQueue.main.async {
2465
+ self.hideLoadingIndicator() // Stop loader when response is received
2466
+ }
2467
+
2468
+ if let error = error {
2469
+ print("Error: \(error.localizedDescription)")
2470
+ return
2471
+ }
2472
+
2473
+ guard let httpResponse = serviceResponse as? HTTPURLResponse else {
2474
+ print("Invalid response")
2475
+ return
2476
+ }
2477
+
2478
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
2479
+ if let data = serviceData {
2480
+ do {
2481
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
2482
+ print("Response Data: \(responseObject)")
2483
+
2484
+ // Check if status is 0 and handle the error
2485
+ if let status = responseObject["status"] as? Int, status == 0 {
2486
+ let errorMessage = responseObject["message"] as? String ?? "Unknown error"
2487
+ self.presentPaymentErrorVC(errorMessage: errorMessage)
2488
+ } else {
2489
+ DispatchQueue.main.async {
2490
+ if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "PaymentDoneVC") as? PaymentDoneVC {
2491
+ paymentDoneVC.chargeData = responseObject
2492
+ // Pass the selected payment method
2493
+ paymentDoneVC.selectedPaymentMethod = self.selectedPaymentMethod
2494
+ paymentDoneVC.easyPayDelegate = self.easyPayDelegate // Pass the delegate
2495
+ self.navigationController?.pushViewController(paymentDoneVC, animated: true)
2496
+ }
2497
+ }
2498
+ }
2499
+ } else {
2500
+ self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
2501
+ }
2502
+ } catch let jsonError {
2503
+ self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
2504
+ }
2505
+ } else {
2506
+ self.presentPaymentErrorVC(errorMessage: "No data received")
1728
2507
  }
1729
2508
  } else {
1730
- print("HTTP Status Code: \(httpResponse.statusCode)")
2509
+ self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
1731
2510
  }
1732
2511
  }
1733
-
1734
2512
  task.resume()
1735
2513
  }
1736
2514
 
@@ -1801,11 +2579,21 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1801
2579
  self.showAlert(title: "Missing Information", message: "Bank account name is required.")
1802
2580
  } else if self.txtFieldRoutingNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
1803
2581
  self.showAlert(title: "Missing Information", message: "Routing number is required.")
1804
- } else if self.txtFieldAccountType.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
2582
+ }
2583
+ else if let routingNumber = self.txtFieldRoutingNumber.text?.replacingOccurrences(of: " ", with: ""), routingNumber.count != 9 || !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: routingNumber)) {
2584
+ self.showAlert(title: "Invalid Routing Number", message: "Routing number is not correct. It must be exactly 9 digits.")
2585
+ }
2586
+ else if self.txtFieldAccountType.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
1805
2587
  self.showAlert(title: "Missing Information", message: "Bank account type is required.")
1806
2588
  } else if self.txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
1807
2589
  self.showAlert(title: "Missing Information", message: "Bank account number is required.")
1808
2590
  }
2591
+ else if self.txtFieldConfirmBankAccount.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
2592
+ self.showAlert(title: "Missing Information", message: "Please confirm your bank account number.")
2593
+ }
2594
+ else if self.txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines) != self.txtFieldConfirmBankAccount.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
2595
+ self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
2596
+ }
1809
2597
  else if !self.agreeTermsAndCondtition {
1810
2598
  self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
1811
2599
  return
@@ -1877,13 +2665,25 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1877
2665
  // Bank Case
1878
2666
  if txtFieldAccountName.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
1879
2667
  showAlert(title: "Missing Information", message: "Bank account name is required.")
1880
- } else if txtFieldRoutingNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
2668
+ }
2669
+ else if txtFieldRoutingNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
1881
2670
  showAlert(title: "Missing Information", message: "Routing number is required.")
1882
- } else if txtFieldAccountType.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
2671
+ }
2672
+ else if let routingNumber = self.txtFieldRoutingNumber.text?.replacingOccurrences(of: " ", with: ""), routingNumber.count != 9 || !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: routingNumber)) {
2673
+ self.showAlert(title: "Invalid Routing Number", message: "Routing number is not correct. It must be exactly 9 digits.")
2674
+ }
2675
+ else if txtFieldAccountType.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
1883
2676
  showAlert(title: "Missing Information", message: "Bank account type is required.")
1884
- } else if txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
2677
+ }
2678
+ else if txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
1885
2679
  showAlert(title: "Missing Information", message: "Bank account number is required.")
1886
2680
  }
2681
+ else if self.txtFieldConfirmBankAccount.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
2682
+ self.showAlert(title: "Missing Information", message: "Please confirm your bank account number.")
2683
+ }
2684
+ else if self.txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines) != self.txtFieldConfirmBankAccount.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
2685
+ self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
2686
+ }
1887
2687
  else if !agreeTermsAndCondtition {
1888
2688
  showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
1889
2689
  return
@@ -2021,11 +2821,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2021
2821
  if isSelectForPayBank {
2022
2822
  viewTermAndConditionsSingleAccountView.isHidden = false
2023
2823
  btnPayNowSingleAccountView.isHidden = false
2024
- viewSingleAccountViewHeight.constant = 170
2824
+ // viewSingleAccountViewHeight.constant = 170
2025
2825
  } else {
2026
2826
  viewTermAndConditionsSingleAccountView.isHidden = true
2027
2827
  btnPayNowSingleAccountView.isHidden = true
2028
- viewSingleAccountViewHeight.constant = 68
2828
+ // viewSingleAccountViewHeight.constant = 68
2029
2829
  btnNext.isHidden = true
2030
2830
  btnNextHeight.constant = 0
2031
2831
  btnNextTopCon.constant = 0
@@ -2046,35 +2846,38 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2046
2846
  }
2047
2847
 
2048
2848
  @IBAction func actionBtnPayNowSingleAccountView(_ sender: UIButton) {
2049
- // Check if billingInfoData is not nil or empty
2050
- if let billingInfoData = request.billingInfoData,
2051
- let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
2052
- let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
2053
-
2054
- // Check if the terms and conditions are agreed
2055
- if !agreeTermsAndCondtition {
2056
- showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
2057
- return
2849
+ if request.authenticatedACH == true {
2850
+ grailPayAaccountChargeSingleSavedAccountApi()
2851
+ } else {
2852
+ // Check if billingInfoData is not nil or empty
2853
+ if let billingInfoData = request.billingInfoData,
2854
+ let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
2855
+ let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
2856
+
2857
+ // Check if the terms and conditions are agreed
2858
+ if !agreeTermsAndCondtition {
2859
+ showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
2860
+ return
2861
+ }
2862
+
2863
+ // Proceed with the Pay Now action
2864
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2865
+ // Pass the customer_id and account_id to BillingInfoVC
2866
+ vc.customerID = selectedbankAccounts?.customer_id
2867
+ vc.accountID = selectedbankAccounts?.account_id
2868
+ vc.billingInfoData = jsonDict
2869
+ vc.isFrom = "SavedBank"
2870
+ vc.selectedPaymentMethod = "Bank"
2871
+ self.navigationController?.pushViewController(vc, animated: true)
2058
2872
  }
2059
-
2060
- // Proceed with the Pay Now action
2061
- let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2062
- // Pass the customer_id and account_id to BillingInfoVC
2063
- vc.customerID = selectedbankAccounts?.customer_id
2064
- vc.accountID = selectedbankAccounts?.account_id
2065
- vc.billingInfoData = jsonDict
2066
- vc.isFrom = "SavedBank"
2067
- vc.selectedPaymentMethod = "Bank"
2068
- self.navigationController?.pushViewController(vc, animated: true)
2069
- }
2070
- else {
2071
- //If Billing info is nil or empty
2072
- if !agreeTermsAndCondtition {
2073
- showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
2074
- return
2873
+ else {
2874
+ //If Billing info is nil or empty
2875
+ if !agreeTermsAndCondtition {
2876
+ showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
2877
+ return
2878
+ }
2879
+ accountChargeSavedBankAccountApi()
2075
2880
  }
2076
-
2077
- accountChargeSavedBankAccountApi()
2078
2881
  }
2079
2882
  }
2080
2883
 
@@ -2108,15 +2911,28 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2108
2911
  let routingNumber = txtFieldRoutingNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2109
2912
  let accountType = txtFieldAccountTypeNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2110
2913
  let accountNumber = txtFieldAccountNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2914
+ let confirmAccountNumber = txtFieldConfirmAccountNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2111
2915
 
2112
2916
  // Check if any field is empty
2113
- if accountName.isEmpty || routingNumber.isEmpty || accountType.isEmpty || accountNumber.isEmpty {
2917
+ if accountName.isEmpty || routingNumber.isEmpty || accountType.isEmpty || accountNumber.isEmpty || confirmAccountNumber.isEmpty {
2114
2918
  let alert = UIAlertController(title: "Missing Information", message: "Please fill in all bank details.", preferredStyle: .alert)
2115
2919
  alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
2116
2920
  self.present(alert, animated: true, completion: nil)
2117
2921
  return
2118
2922
  }
2119
2923
 
2924
+ // Check if routing number is exactly 9 digits and numeric
2925
+ if routingNumber.count != 9 || !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: routingNumber)) {
2926
+ self.showAlert(title: "Invalid Routing Number", message: "Routing number is not correct. It must be exactly 9 digits.")
2927
+ return
2928
+ }
2929
+
2930
+ // Check if account number and confirmation match
2931
+ if accountNumber != confirmAccountNumber {
2932
+ self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
2933
+ return
2934
+ }
2935
+
2120
2936
  // Determine the vc.isFrom value based on isSavedNewAccount
2121
2937
  let isSavedNewAccount = isSavedNewAccount
2122
2938
  let isFromValue = isSavedNewAccount ? "AddNewAccountWithSave" : "AddNewAccountWithoutSave"
@@ -2317,7 +3133,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2317
3133
  //MARK: - Send OTP Email Verification Api
2318
3134
  func emailVerificationApi() {
2319
3135
  showLoadingIndicator()
2320
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3136
+
2321
3137
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.emailVerification.path()
2322
3138
 
2323
3139
  guard let serviceURL = URL(string: fullURL) else {
@@ -2335,6 +3151,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2335
3151
  print("Setting clientToken header: \(token ?? "None")")
2336
3152
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2337
3153
 
3154
+ // Add API headers
3155
+ if let apiKey = EnvironmentConfig.apiKey,
3156
+ let apiSecret = EnvironmentConfig.apiSecret {
3157
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
3158
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
3159
+ }
3160
+
2338
3161
  // Define the parameters for the request
2339
3162
  let params: [String: Any] = [
2340
3163
  "card_search_value": txtFieldEmail.text ?? "",
@@ -2415,6 +3238,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2415
3238
  print("Setting clientToken header: \(token ?? "None")")
2416
3239
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2417
3240
 
3241
+ // Add API headers
3242
+ if let apiKey = EnvironmentConfig.apiKey,
3243
+ let apiSecret = EnvironmentConfig.apiSecret {
3244
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
3245
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
3246
+ }
3247
+
2418
3248
  // ✅ Safe access from main thread (assumes you call this from main thread)
2419
3249
  let email = self.txtFieldEmail.text ?? ""
2420
3250
  let otp = getCombinedOTP()
@@ -2538,18 +3368,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2538
3368
  func getShowCardsApi() {
2539
3369
  showLoadingIndicator()
2540
3370
 
2541
- // var components = URLComponents()
2542
- // components.scheme = "https"
2543
- // components.host = "stage-api.stage-easymerchant.io"
2544
- // components.path = "/api/v1/card"
2545
- //
2546
- // guard let serviceURL = components.url else {
2547
- // print("Invalid URL")
2548
- // hideLoadingIndicator()
2549
- // return
2550
- // }
2551
-
2552
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
2553
3371
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.getCards.path()
2554
3372
 
2555
3373
  guard let serviceURL = URL(string: fullURL) else {
@@ -2566,6 +3384,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2566
3384
  print("Setting customerToken header: \(token ?? "None")")
2567
3385
  request.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
2568
3386
 
3387
+ // Add API headers
3388
+ if let apiKey = EnvironmentConfig.apiKey,
3389
+ let apiSecret = EnvironmentConfig.apiSecret {
3390
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
3391
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
3392
+ }
3393
+
2569
3394
  let session = URLSession.shared
2570
3395
  let task = session.dataTask(with: request) { [weak self] (data, response, error) in
2571
3396
  guard let self = self else { return }
@@ -2618,18 +3443,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2618
3443
  func paymentIntentApi() {
2619
3444
  showLoadingIndicator()
2620
3445
 
2621
- // var components = URLComponents()
2622
- // components.scheme = "https"
2623
- // components.host = "stage-api.stage-easymerchant.io"
2624
- // components.path = "/api/v1/charges"
2625
- //
2626
- // guard let serviceURL = components.url else {
2627
- // print("Invalid URL")
2628
- // hideLoadingIndicator()
2629
- // return
2630
- // }
2631
-
2632
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
2633
3446
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.creditCharges.path()
2634
3447
 
2635
3448
  guard let serviceURL = URL(string: fullURL) else {
@@ -2646,6 +3459,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2646
3459
  print("Setting clientToken header: \(token ?? "None")")
2647
3460
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2648
3461
 
3462
+ // Add API headers
3463
+ if let apiKey = EnvironmentConfig.apiKey,
3464
+ let apiSecret = EnvironmentConfig.apiSecret {
3465
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
3466
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
3467
+ }
3468
+
2649
3469
  let params: [String: Any] = [
2650
3470
  "name": cardNameTextField.text,
2651
3471
  "email": "test@gmail.com",
@@ -2728,18 +3548,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2728
3548
  func paymentIntentFromShowSavedCardApi() {
2729
3549
  showLoadingIndicator()
2730
3550
 
2731
- // var components = URLComponents()
2732
- // components.scheme = "https"
2733
- // components.host = "stage-api.stage-easymerchant.io"
2734
- // components.path = "/api/v1/charges"
2735
- //
2736
- // guard let serviceURL = components.url else {
2737
- // print("Invalid URL")
2738
- // hideLoadingIndicator()
2739
- // return
2740
- // }
2741
-
2742
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
2743
3551
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.creditCharges.path()
2744
3552
 
2745
3553
  guard let serviceURL = URL(string: fullURL) else {
@@ -2756,6 +3564,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2756
3564
  print("Setting clientToken header: \(token ?? "None")")
2757
3565
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2758
3566
 
3567
+ // Add API headers
3568
+ if let apiKey = EnvironmentConfig.apiKey,
3569
+ let apiSecret = EnvironmentConfig.apiSecret {
3570
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
3571
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
3572
+ }
3573
+
2759
3574
  // Prepare parameters for the API request
2760
3575
  let params: [String: Any] = [
2761
3576
  "name": UserStoreSingleton.shared.verificationEmail?.split(separator: "@").first ?? "",
@@ -2841,18 +3656,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2841
3656
  func paymentIntentFromAddNewCardApi(customerId: String?) {
2842
3657
  showLoadingIndicator()
2843
3658
 
2844
- // var components = URLComponents()
2845
- // components.scheme = "https"
2846
- // components.host = "stage-api.stage-easymerchant.io"
2847
- // components.path = "/api/v1/charges"
2848
- //
2849
- // guard let serviceURL = components.url else {
2850
- // print("Invalid URL")
2851
- // hideLoadingIndicator()
2852
- // return
2853
- // }
2854
-
2855
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
2856
3659
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.creditCharges.path()
2857
3660
 
2858
3661
  guard let serviceURL = URL(string: fullURL) else {
@@ -2869,6 +3672,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2869
3672
  print("Setting clientToken header: \(token ?? "None")")
2870
3673
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2871
3674
 
3675
+ // Add API headers
3676
+ if let apiKey = EnvironmentConfig.apiKey,
3677
+ let apiSecret = EnvironmentConfig.apiSecret {
3678
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
3679
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
3680
+ }
3681
+
2872
3682
  // Get the text fields from the selected cell and trim whitespace
2873
3683
  let nameText = txtFieldNameOnCardNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
2874
3684
  let cvvText = txtFieldCVVNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
@@ -3018,18 +3828,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3018
3828
  func updateCardApi(cardID: String) {
3019
3829
  showLoadingIndicator()
3020
3830
 
3021
- // var components = URLComponents()
3022
- // components.scheme = "https"
3023
- // components.host = "stage-api.stage-easymerchant.io"
3024
- // components.path = "/api/v1/card/\(cardID)" // Append the card_id to the path
3025
- //
3026
- // guard let serviceURL = components.url else {
3027
- // print("Invalid URL")
3028
- // hideLoadingIndicator()
3029
- // return
3030
- // }
3031
-
3032
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3033
3831
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.getCards.path()+"/\(cardID)"
3034
3832
 
3035
3833
  guard let serviceURL = URL(string: fullURL) else {
@@ -3046,6 +3844,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3046
3844
  print("Setting customerToken header: \(token ?? "None")")
3047
3845
  request.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
3048
3846
 
3847
+ // Add API headers
3848
+ if let apiKey = EnvironmentConfig.apiKey,
3849
+ let apiSecret = EnvironmentConfig.apiSecret {
3850
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
3851
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
3852
+ }
3853
+
3049
3854
  // Get the text fields from the selected cell and trim whitespace
3050
3855
  let nameText = txtFieldNameOnCardUpdateCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3051
3856
  let cvvText = txtFieldCVVUpdateCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
@@ -3180,18 +3985,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3180
3985
  func deleteCardApi(cardID: String, at index: Int) {
3181
3986
  showLoadingIndicator()
3182
3987
 
3183
- // var components = URLComponents()
3184
- // components.scheme = "https"
3185
- // components.host = "stage-api.stage-easymerchant.io"
3186
- // components.path = "/api/v1/card/\(cardID)" // Append the card_id to the path
3187
- //
3188
- // guard let serviceURL = components.url else {
3189
- // print("Invalid URL")
3190
- // hideLoadingIndicator()
3191
- // return
3192
- // }
3193
-
3194
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3195
3988
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.getCards.path()+"/\(cardID)"
3196
3989
 
3197
3990
  guard let serviceURL = URL(string: fullURL) else {
@@ -3208,6 +4001,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3208
4001
  print("Setting customerToken header: \(token ?? "None")")
3209
4002
  request.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
3210
4003
 
4004
+ // Add API headers
4005
+ if let apiKey = EnvironmentConfig.apiKey,
4006
+ let apiSecret = EnvironmentConfig.apiSecret {
4007
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
4008
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4009
+ }
4010
+
3211
4011
  let session = URLSession.shared
3212
4012
  let task = session.dataTask(with: request) { [weak self] (serviceData, serviceResponse, error) in
3213
4013
  guard let self = self else { return }
@@ -3279,9 +4079,9 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3279
4079
  return
3280
4080
  }
3281
4081
 
3282
- var request = URLRequest(url: serviceURL)
3283
- request.httpMethod = "GET"
3284
- request.addValue("application/json", forHTTPHeaderField: "Content-Type")
4082
+ var urlRequest = URLRequest(url: serviceURL)
4083
+ urlRequest.httpMethod = "GET"
4084
+ urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
3285
4085
 
3286
4086
  // Retrieve and validate customer token
3287
4087
  guard let token = UserStoreSingleton.shared.customerToken, !token.isEmpty else {
@@ -3292,10 +4092,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3292
4092
  }
3293
4093
 
3294
4094
  print("Setting customerToken header: \(token)")
3295
- request.addValue(token, forHTTPHeaderField: "Customer-Token")
4095
+ urlRequest.addValue(token, forHTTPHeaderField: "Customer-Token")
3296
4096
 
3297
4097
  let session = URLSession.shared
3298
- let task = session.dataTask(with: request) { [weak self] (data, response, error) in
4098
+ let task = session.dataTask(with: urlRequest) { [weak self] (data, response, error) in
3299
4099
  guard let self = self else { return }
3300
4100
 
3301
4101
  DispatchQueue.main.async {
@@ -3369,15 +4169,17 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3369
4169
  if self.isSelectForPayBank {
3370
4170
  self.viewTermAndConditionsSingleAccountView.isHidden = false
3371
4171
  self.btnPayNowSingleAccountView.isHidden = false
3372
- self.viewSingleAccountViewHeight.constant = 170
4172
+ // self.viewSingleAccountViewHeight.constant = 170
3373
4173
  } else {
3374
4174
  self.viewTermAndConditionsSingleAccountView.isHidden = true
3375
4175
  self.btnPayNowSingleAccountView.isHidden = true
3376
- self.viewSingleAccountViewHeight.constant = 68
4176
+ // self.viewSingleAccountViewHeight.constant = 68
3377
4177
  self.btnNext.isHidden = true
3378
4178
  self.btnNextHeight.constant = 0
3379
4179
  self.btnNextTopCon.constant = 0
3380
4180
  }
4181
+
4182
+ self.grailPayBankLinkView.isHidden = true
3381
4183
  }
3382
4184
  } else {
3383
4185
  print("Error from server: \(bankAccountModel.message ?? "Unknown error")")
@@ -3412,6 +4214,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3412
4214
  print("Setting clientToken header: \(token ?? "None")")
3413
4215
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
3414
4216
 
4217
+ // Add API headers
4218
+ if let apiKey = EnvironmentConfig.apiKey,
4219
+ let apiSecret = EnvironmentConfig.apiSecret {
4220
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
4221
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4222
+ }
4223
+
3415
4224
  let params: [String: Any] = [
3416
4225
  "name": txtFieldAccountName.text ?? "",
3417
4226
  "email": "test@gmail.com",
@@ -3511,6 +4320,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3511
4320
  print("Setting clientToken header: \(token ?? "None")")
3512
4321
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
3513
4322
 
4323
+ // Add API headers
4324
+ if let apiKey = EnvironmentConfig.apiKey,
4325
+ let apiSecret = EnvironmentConfig.apiSecret {
4326
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
4327
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4328
+ }
4329
+
3514
4330
  let params: [String: Any] = [
3515
4331
  "account_id": selectedbankAccounts?.account_id ?? "",
3516
4332
  "customer": selectedbankAccounts?.customer_id ?? "",
@@ -3607,6 +4423,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3607
4423
  print("Setting clientToken header: \(token ?? "None")")
3608
4424
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
3609
4425
 
4426
+ // Add API headers
4427
+ if let apiKey = EnvironmentConfig.apiKey,
4428
+ let apiSecret = EnvironmentConfig.apiSecret {
4429
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
4430
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4431
+ }
4432
+
3610
4433
  let accountName = txtFieldAccountNameNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3611
4434
  let routingNumber = txtFieldRoutingNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3612
4435
  let accountType = txtFieldAccountTypeNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
@@ -3713,6 +4536,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3713
4536
  print("Setting clientToken header: \(token ?? "None")")
3714
4537
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
3715
4538
 
4539
+ // Add API headers
4540
+ if let apiKey = EnvironmentConfig.apiKey,
4541
+ let apiSecret = EnvironmentConfig.apiSecret {
4542
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
4543
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4544
+ }
4545
+
3716
4546
  // Retrieve and trim text field values (removing leading and trailing whitespace)
3717
4547
  let accountName = txtFieldAccountNameNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
3718
4548
  let routingNumber = txtFieldRoutingNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
@@ -3829,6 +4659,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3829
4659
  print("Setting customerToken header: \(token ?? "None")")
3830
4660
  request.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
3831
4661
 
4662
+ // Add API headers
4663
+ if let apiKey = EnvironmentConfig.apiKey,
4664
+ let apiSecret = EnvironmentConfig.apiSecret {
4665
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
4666
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4667
+ }
4668
+
3832
4669
  let session = URLSession.shared
3833
4670
  let task = session.dataTask(with: request) { [weak self] (serviceData, serviceResponse, error) in
3834
4671
  guard let self = self else { return }
@@ -3898,6 +4735,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3898
4735
  print("Setting clientToken header: \(token ?? "None")")
3899
4736
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
3900
4737
 
4738
+ // Add API headers
4739
+ if let apiKey = EnvironmentConfig.apiKey,
4740
+ let apiSecret = EnvironmentConfig.apiSecret {
4741
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
4742
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4743
+ }
4744
+
3901
4745
  let params: [String: Any] = [
3902
4746
  "name": UserStoreSingleton.shared.merchantName ?? "",
3903
4747
  "email": UserStoreSingleton.shared.merchantEmail ?? "",
@@ -4033,6 +4877,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
4033
4877
  print("Setting clientToken header: \(token ?? "None")")
4034
4878
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
4035
4879
 
4880
+ // Add API headers
4881
+ if let apiKey = EnvironmentConfig.apiKey,
4882
+ let apiSecret = EnvironmentConfig.apiSecret {
4883
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
4884
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
4885
+ }
4886
+
4036
4887
  let session = URLSession.shared
4037
4888
  let task = session.dataTask(with: request) { [weak self] (data, response, error) in
4038
4889
  guard let self = self else { return }
@@ -4126,10 +4977,10 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
4126
4977
  imageName = "creditcard"
4127
4978
  case "Crypto":
4128
4979
  imageName = "bitcoinsign.circle.fill"
4129
- // case "Wallet":
4130
- // imageName = "Wallet"
4980
+ case "Wallet":
4981
+ imageName = "wallet.bifold"
4131
4982
  default:
4132
- imageName = "Wallet"
4983
+ imageName = ""
4133
4984
  }
4134
4985
  return PaymentsData(name: method, image: imageName)
4135
4986
  }
@@ -4181,6 +5032,7 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
4181
5032
  self.viewTermsAndConditions.isHidden = true
4182
5033
  self.viewCrypto.isHidden = true
4183
5034
  self.grailPayBankLinkView.isHidden = true
5035
+ self.viewAddNewGrailPayAccount.isHidden = true
4184
5036
  } else {
4185
5037
  getShowCardsApi()
4186
5038
  self.viewSingleSavedAccount.isHidden = true
@@ -4203,6 +5055,7 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
4203
5055
  self.viewNewBankAccount.isHidden = true
4204
5056
  self.viewCrypto.isHidden = true
4205
5057
  self.grailPayBankLinkView.isHidden = true
5058
+ self.viewAddNewGrailPayAccount.isHidden = true
4206
5059
  }
4207
5060
 
4208
5061
  case "Bank":
@@ -4231,6 +5084,7 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
4231
5084
  self.viewTermsAndConditions.isHidden = false
4232
5085
  self.grailPayBankLinkView.isHidden = true
4233
5086
  self.viewGrailPayAbandon.isHidden = true
5087
+
4234
5088
  } else {
4235
5089
  self.viewBankFields.isHidden = true
4236
5090
  self.viewTermsAndConditions.isHidden = true
@@ -4246,6 +5100,7 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
4246
5100
  self.viewBtnShowSavedCardHeight.constant = 0
4247
5101
  self.viewBtnShowSavedCardTopCon.constant = 0
4248
5102
  }
5103
+
4249
5104
  }
4250
5105
 
4251
5106
  } else {
@@ -4297,6 +5152,8 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
4297
5152
  self.viewNewBankAccount.isHidden = true
4298
5153
 
4299
5154
  self.grailPayBankLinkView.isHidden = true
5155
+ self.viewAddNewGrailPayAccount.isHidden = true
5156
+ self.viewAbandonGrailPayNewAccountView.isHidden = true
4300
5157
 
4301
5158
  default:
4302
5159
  break
@@ -4316,7 +5173,6 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
4316
5173
  return savedCards.count + 1
4317
5174
  }
4318
5175
  else if tableView == tblViewSavedBankAccounts {
4319
- print("Number of bank accounts: \(bankAccounts.count)")
4320
5176
  return bankAccounts.count + 1
4321
5177
  }
4322
5178
  else if tableView == tblViewAccountTypes {
@@ -4610,23 +5466,49 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
4610
5466
 
4611
5467
  //Bank Cell
4612
5468
  @objc func addNewBankAccountTapped(_ sender: UIButton) {
4613
- self.viewNewBankAccount.isHidden = false
4614
- self.viewSingleSavedAccount.isHidden = true
4615
- self.viewBankFields.isHidden = true
4616
- self.viewBtnShowSavedCards.isHidden = true
4617
- self.viewBtnShowSavedCardHeight.constant = 0
4618
- self.viewBtnShowSavedCardTopCon.constant = 0
4619
- self.OTPView.isHidden = true
4620
- self.emailView.isHidden = true
4621
- self.viewCardFields.isHidden = true
4622
- self.viewSingleSavedCard.isHidden = true
4623
- self.viewChangeCard.isHidden = true
4624
- self.viewUpdateCard.isHidden = true
4625
- self.viewAddNewCard.isHidden = true
4626
- self.btnNext.isHidden = true
4627
- self.btnNextHeight.constant = 0
4628
- self.btnNextTopCon.constant = 8
4629
- self.viewChangedAccount.isHidden = true
5469
+ if request.authenticatedACH == true {
5470
+ self.viewAddNewGrailPayAccount.isHidden = false
5471
+
5472
+ self.viewNewBankAccount.isHidden = true
5473
+ self.viewSingleSavedAccount.isHidden = true
5474
+ self.viewBankFields.isHidden = true
5475
+ self.viewBtnShowSavedCards.isHidden = true
5476
+ self.viewBtnShowSavedCardHeight.constant = 0
5477
+ self.viewBtnShowSavedCardTopCon.constant = 0
5478
+ self.OTPView.isHidden = true
5479
+ self.emailView.isHidden = true
5480
+ self.viewCardFields.isHidden = true
5481
+ self.viewSingleSavedCard.isHidden = true
5482
+ self.viewChangeCard.isHidden = true
5483
+ self.viewUpdateCard.isHidden = true
5484
+ self.viewAddNewCard.isHidden = true
5485
+ self.btnNext.isHidden = true
5486
+ self.btnNextHeight.constant = 0
5487
+ self.btnNextTopCon.constant = 8
5488
+ self.viewChangedAccount.isHidden = true
5489
+ }
5490
+ else {
5491
+ self.viewAddNewGrailPayAccount.isHidden = true
5492
+
5493
+ self.viewNewBankAccount.isHidden = false
5494
+ self.viewSingleSavedAccount.isHidden = true
5495
+ self.viewBankFields.isHidden = true
5496
+ self.viewBtnShowSavedCards.isHidden = true
5497
+ self.viewBtnShowSavedCardHeight.constant = 0
5498
+ self.viewBtnShowSavedCardTopCon.constant = 0
5499
+ self.OTPView.isHidden = true
5500
+ self.emailView.isHidden = true
5501
+ self.viewCardFields.isHidden = true
5502
+ self.viewSingleSavedCard.isHidden = true
5503
+ self.viewChangeCard.isHidden = true
5504
+ self.viewUpdateCard.isHidden = true
5505
+ self.viewAddNewCard.isHidden = true
5506
+ self.btnNext.isHidden = true
5507
+ self.btnNextHeight.constant = 0
5508
+ self.btnNextTopCon.constant = 8
5509
+ self.viewChangedAccount.isHidden = true
5510
+ }
5511
+
4630
5512
  }
4631
5513
 
4632
5514
  @objc func btnSelectAccountTapped(_ sender: UIButton) {
@@ -4663,11 +5545,11 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
4663
5545
  if isSelectForPayBank {
4664
5546
  viewTermAndConditionsSingleAccountView.isHidden = false
4665
5547
  btnPayNowSingleAccountView.isHidden = false
4666
- viewSingleAccountViewHeight.constant = 170
5548
+ // viewSingleAccountViewHeight.constant = 170
4667
5549
  } else {
4668
5550
  viewTermAndConditionsSingleAccountView.isHidden = true
4669
5551
  btnPayNowSingleAccountView.isHidden = true
4670
- viewSingleAccountViewHeight.constant = 68
5552
+ // viewSingleAccountViewHeight.constant = 68
4671
5553
  btnNext.isHidden = true
4672
5554
  btnNextHeight.constant = 0
4673
5555
  btnNextTopCon.constant = 0
@@ -4856,6 +5738,25 @@ extension PaymentInfoVC: UITextFieldDelegate {
4856
5738
  return textField.text?.count == 0
4857
5739
  }
4858
5740
 
5741
+ else if textField == txtFieldRoutingNumber {
5742
+ let allowedCharacters = CharacterSet.decimalDigits
5743
+ let characterSet = CharacterSet(charactersIn: string)
5744
+
5745
+ let currentText = textField.text ?? ""
5746
+ let newLength = (currentText as NSString).replacingCharacters(in: range, with: string).count
5747
+
5748
+ return allowedCharacters.isSuperset(of: characterSet) && newLength <= 9
5749
+ }
5750
+ else if textField == txtFieldRoutingNumberNewAccountView {
5751
+ let allowedCharacters = CharacterSet.decimalDigits
5752
+ let characterSet = CharacterSet(charactersIn: string)
5753
+
5754
+ let currentText = textField.text ?? ""
5755
+ let newLength = (currentText as NSString).replacingCharacters(in: range, with: string).count
5756
+
5757
+ return allowedCharacters.isSuperset(of: characterSet) && newLength <= 9
5758
+ }
5759
+
4859
5760
  return true
4860
5761
  }
4861
5762
 
@@ -4933,7 +5834,7 @@ extension PaymentInfoVC: UITextFieldDelegate {
4933
5834
  return otp1 + otp2 + otp3 + otp4 + otp5 + otp6
4934
5835
  }
4935
5836
 
4936
- // UITextFieldDelegate methods
5837
+ //MARK: - UITextFieldDelegate methods
4937
5838
  func textFieldDidBeginEditing(_ textField: UITextField) {
4938
5839
  if textField == cardNumberTextField.textField {
4939
5840
  if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
@@ -5073,6 +5974,14 @@ extension PaymentInfoVC: UITextFieldDelegate {
5073
5974
  viewtxtFieldAccountNumber.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
5074
5975
  }
5075
5976
  }
5977
+ else if textField == txtFieldConfirmBankAccount {
5978
+ if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
5979
+ let uiColor = UIColor(hex: primaryBtnFontColor) {
5980
+ viewTextFieldConfirmAccount.layer.borderColor = uiColor.cgColor
5981
+ } else {
5982
+ viewTextFieldConfirmAccount.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
5983
+ }
5984
+ }
5076
5985
  //New Bank Account View
5077
5986
  else if textField == txtFieldAccountNameNewAccountView {
5078
5987
  if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
@@ -5106,6 +6015,14 @@ extension PaymentInfoVC: UITextFieldDelegate {
5106
6015
  viewtxtFieldAccountNumberNewAccountView.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
5107
6016
  }
5108
6017
  }
6018
+ else if textField == txtFieldConfirmAccountNewAccountView {
6019
+ if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
6020
+ let uiColor = UIColor(hex: primaryBtnFontColor) {
6021
+ viewTxtFieldConfirmNewAccountView.layer.borderColor = uiColor.cgColor
6022
+ } else {
6023
+ viewTxtFieldConfirmNewAccountView.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
6024
+ }
6025
+ }
5109
6026
  }
5110
6027
 
5111
6028
  func textFieldDidEndEditing(_ textField: UITextField) {
@@ -5248,6 +6165,14 @@ extension PaymentInfoVC: UITextFieldDelegate {
5248
6165
  viewtxtFieldAccountNumber.layer.borderColor = UIColor.systemGray.cgColor
5249
6166
  }
5250
6167
  }
6168
+ else if textField == txtFieldConfirmBankAccount {
6169
+ if let bodyBackgroundColor = UserStoreSingleton.shared.secondary_font_col,
6170
+ let borderColor = UIColor(hex: bodyBackgroundColor) {
6171
+ viewTextFieldConfirmAccount.layer.borderColor = borderColor.cgColor
6172
+ } else {
6173
+ viewTextFieldConfirmAccount.layer.borderColor = UIColor.systemGray.cgColor
6174
+ }
6175
+ }
5251
6176
  //New Bank Account View
5252
6177
  else if textField == txtFieldAccountNameNewAccountView {
5253
6178
  if let bodyBackgroundColor = UserStoreSingleton.shared.secondary_font_col,
@@ -5281,12 +6206,19 @@ extension PaymentInfoVC: UITextFieldDelegate {
5281
6206
  viewtxtFieldAccountNumberNewAccountView.layer.borderColor = UIColor.systemGray.cgColor
5282
6207
  }
5283
6208
  }
6209
+ else if textField == txtFieldConfirmAccountNewAccountView {
6210
+ if let bodyBackgroundColor = UserStoreSingleton.shared.secondary_font_col,
6211
+ let borderColor = UIColor(hex: bodyBackgroundColor) {
6212
+ viewTxtFieldConfirmNewAccountView.layer.borderColor = borderColor.cgColor
6213
+ } else {
6214
+ viewTxtFieldConfirmNewAccountView.layer.borderColor = UIColor.systemGray.cgColor
6215
+ }
6216
+ }
5284
6217
  }
5285
6218
 
5286
6219
  }
5287
6220
 
5288
6221
  //MARK: - BlinkCard
5289
-
5290
6222
  //extension PaymentInfoVC: MBCBlinkCardOverlayViewControllerDelegate {
5291
6223
  // func blinkCardOverlayViewControllerDidFinishScanning(_ blinkCardOverlayViewController: MBCBlinkCardOverlayViewController, state: MBCRecognizerResultState) {
5292
6224
  // /** This is done on background thread */
@@ -5343,7 +6275,7 @@ extension PaymentInfoVC: UITextFieldDelegate {
5343
6275
  // }
5344
6276
  //
5345
6277
  //}
5346
-
6278
+ //
5347
6279
  //extension PaymentInfoVC: CustomOverlayDelegate {
5348
6280
  // func didFinishScanningCard(cardNumber: String, expiryDate: String, cvv: String, nameOnCard: String) {
5349
6281
  // // Update the text fields with scanned details