@jimrising/easymerchantsdk-react-native 1.2.8 → 1.3.0

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.
@@ -156,6 +156,23 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
156
156
  @IBOutlet weak var tblViewAccountTypes: UITableView!
157
157
  @IBOutlet weak var btnCheckSavedAccountForFuture: UIButton!
158
158
 
159
+ //GrailPay
160
+ @IBOutlet weak var grailPayBankLinkView: UIView!
161
+ @IBOutlet weak var viewGrailPaySavedBank: UIStackView!
162
+ @IBOutlet weak var btnLinkBankAccount: FilledButton!
163
+ @IBOutlet weak var viewGrailPaySavedAccount: UIView!
164
+ @IBOutlet weak var btnCheckGrailPaySavedAccount: UIButton!
165
+ @IBOutlet weak var lblGrailPaySavedAccountNumber: UILabel!
166
+ @IBOutlet weak var lblGrailPaySavedAccountType: UILabel!
167
+ @IBOutlet weak var btnCheckBoxGrailPaySaveAccount: UIButton!
168
+ @IBOutlet weak var lblGrailPaySaveAccountForFuture: UILabel!
169
+ @IBOutlet weak var btnCheckBoxGrailPayAgreeTerms: UIButton!
170
+ @IBOutlet weak var lblGrailPayAgreeTo: UILabel!
171
+ @IBOutlet weak var lblGrailPayTermsAndCondition: UILabel!
172
+ @IBOutlet weak var viewGrailPayAbandon: UIView!
173
+ @IBOutlet weak var imgGrailPayAbandonError: UIImageView!
174
+ @IBOutlet weak var lblGrailPayAabandonError: UILabel!
175
+
159
176
  //SavedBank
160
177
  @IBOutlet weak var viewSingleSavedAccount: UIView!
161
178
  @IBOutlet weak var viewTermAndConditionsSingleAccountView: UIStackView!
@@ -212,7 +229,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
212
229
  @IBOutlet weak var btnTryAgain: UIButton!
213
230
  @IBOutlet weak var lblTimerTryAgainView: UILabel!
214
231
 
215
-
216
232
  //Setting View
217
233
  @IBOutlet weak var btnChangeLanguage: UIButton!
218
234
  @IBOutlet weak var btnLogout: UIButton!
@@ -238,13 +254,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
238
254
 
239
255
  public var amount: Int?
240
256
 
241
- // let arrPaymentMethods = [
242
- // PaymentsData(name: "Card",image: "creditcard"),
243
- // PaymentsData(name: "Bank",image: "building.columns.fill"),
244
- // PaymentsData(name: "Crypto",image: "bitcoinsign.circle.fill"),
245
- // // PaymentsData(name: "Wallet",image: "bag.fill")
246
- // ]
247
-
248
257
  var arrPaymentMethods: [PaymentsData] = []
249
258
 
250
259
  private var selectedIndex: IndexPath?
@@ -281,7 +290,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
281
290
  var isFrom = String()
282
291
 
283
292
  //Blink Card
284
- // var blinkCardRecognizer: MBCBlinkCardRecognizer!
293
+ // var blinkCardRecognizer: MBCBlinkCardRecognizer!
285
294
 
286
295
  // Variables for Card Scanning Data Back
287
296
  var cardNumber: String?
@@ -289,20 +298,26 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
289
298
  var cvv: String?
290
299
  var nameOnCard: String?
291
300
 
301
+ private var grailPayChargeData: [String: Any]?
302
+
303
+ var grailPayAccount: [String: Any]?
304
+
305
+ var isBankAccountConnected = false
306
+ var defaultBankAccountID: String?
307
+
292
308
  //MARK: - View Did Load
293
309
  override func viewDidLoad() {
294
310
  super.viewDidLoad()
295
311
 
312
+ viewAppearanceOn()
313
+
296
314
  uiFinishingTouchElements()
297
315
 
298
316
  setUpTextFieldsDelegates()
299
317
 
300
318
  collVwPayment.delegate = self
301
319
  collVwPayment.dataSource = self
302
- // Set the first cell as selected by default
303
- // selectedIndex = IndexPath(row: 0, section: 0)
304
- // selectedPaymentMethod = arrPaymentMethods[selectedIndex!.row].name
305
- // collVwPayment.reloadData()
320
+
306
321
  loadPaymentMethods()
307
322
 
308
323
  updateSaveButtons()
@@ -312,13 +327,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
312
327
  tapGesture.cancelsTouchesInView = false
313
328
  self.view.addGestureRecognizer(tapGesture)
314
329
 
315
- // Check if billingInfoData is available
330
+ //// Check if billingInfoData is available
316
331
  if let billingInfoData = request.billingInfoData,
317
332
  let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
318
333
  let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
319
- print("Billing Info Data: \(jsonDict)")
334
+
320
335
  // If `submitButtonText` is not empty, use it; otherwise, default to "Next (Billing Info)"
321
- let buttonText = request?.submitButtonText?.isEmpty == false ? request?.submitButtonText : "Next (Billing Info)"
336
+ let buttonText = (request?.submitButtonText?.isEmpty == false ? request!.submitButtonText! + " (Billing Info)" : "Next (Billing Info)")
322
337
 
323
338
  btnNext.setTitle(buttonText, for: .normal)
324
339
  btnPayNowNewCardView.setTitle(buttonText, for: .normal)
@@ -326,12 +341,18 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
326
341
  btnPayNowSingleCard.setTitle(buttonText, for: .normal)
327
342
  btnPayNowSingleAccountView.setTitle(buttonText, for: .normal)
328
343
  } else {
329
- // If billingInfoData is nil or empty, set the button title to "Pay Now"
330
- btnNext.setTitle("Pay Now ($\(amount ?? 0))", for: .normal)
331
- btnPayNowNewCardView.setTitle("Pay Now ($\(amount ?? 0))", for: .normal)
332
- btnPayNowNewAccountView.setTitle("Pay Now ($\(amount ?? 0))", for: .normal)
333
- btnPayNowSingleCard.setTitle("Pay Now ($\(amount ?? 0))", for: .normal)
334
- btnPayNowSingleAccountView.setTitle("Pay Now ($\(amount ?? 0))", for: .normal)
344
+ let amountText = String(format: "$%.2f", amount ?? 0)
345
+ let submitText = request?.submitButtonText
346
+
347
+ let defaultTitle = (submitText?.isEmpty == false)
348
+ ? "\(submitText!) (\(amountText))"
349
+ : "Pay Now (\(amountText))"
350
+
351
+ btnNext.setTitle(defaultTitle, for: .normal)
352
+ btnPayNowNewCardView.setTitle(defaultTitle, for: .normal)
353
+ btnPayNowNewAccountView.setTitle(defaultTitle, for: .normal)
354
+ btnPayNowSingleCard.setTitle(defaultTitle, for: .normal)
355
+ btnPayNowSingleAccountView.setTitle(defaultTitle, for: .normal)
335
356
  }
336
357
 
337
358
  emailView.isHidden = true
@@ -400,6 +421,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
400
421
 
401
422
  setUpTermAndConditionsButton()
402
423
  setUpTermAndConditionsForSingleSavedAccountButton()
424
+ setUpTermAndConditionsForGrailPaySavedAccountButton()
403
425
 
404
426
  settingsView.clipsToBounds = false
405
427
  settingsView.layer.shadowColor = UIColor.black.cgColor
@@ -418,20 +440,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
418
440
  btnSettings.isHidden = true
419
441
  }
420
442
 
421
- // if let request = request {
422
- // print("Token Only: \(request.tokenOnly)")
423
- // print("Save Card: \(request.saveCard)")
424
- // print("Save Account: \(request.saveAccount)")
425
- // print("Submit Button Text: \(request.submitButtonText)")
426
- // }
427
443
  }
428
444
 
429
445
  //MARK: - View Wiil Appear
430
446
  override func viewWillAppear(_ animated: Bool) {
431
-
432
447
  uiFinishingTouchElements()
433
448
 
434
- viewAppearanceOnClick()
449
+ viewAppearanceOn()
435
450
 
436
451
  keyboardObserver.animateChanges({ [self] height in
437
452
  let newConstant = CGFloat.maximum(height - self.view.safeAreaInsets.bottom + 8, 8)
@@ -451,7 +466,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
451
466
  }
452
467
 
453
468
  //MARK: - View Appearances on clicks
454
- func viewAppearanceOnClick() {
469
+ func viewAppearanceOn() {
455
470
  viewTermAndConditionsSingleAccountView.isHidden = true
456
471
  btnPayNowSingleAccountView.isHidden = true
457
472
  viewSingleAccountViewHeight.constant = 68
@@ -465,6 +480,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
465
480
  self.viewCrypto.isHidden = true
466
481
 
467
482
  if selectedPaymentMethod == "Card" {
483
+ self.grailPayBankLinkView.isHidden = true
468
484
  if UserStoreSingleton.shared.isLoggedIn == true {
469
485
  if UserStoreSingleton.shared.customerId == nil {
470
486
  self.OTPView.isHidden = true
@@ -583,11 +599,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
583
599
  self.btnNextTopCon.constant = 0
584
600
  }
585
601
 
602
+ 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
616
+ }
617
+ }
618
+ else {
619
+ self.viewBankFields.isHidden = false
620
+ self.viewTermsAndConditions.isHidden = false
621
+ self.grailPayBankLinkView.isHidden = true
622
+ }
586
623
  }
587
624
  else {
588
- self.viewBankFields.isHidden = false
625
+ // self.viewBankFields.isHidden = false
589
626
  self.viewBtnShowSavedCards.isHidden = false
590
- self.lblBtnShowSaveCard.text = "Use Saved Accounts"
627
+ self.lblBtnShowSaveCard.text = "Show Saved Accounts"
591
628
  self.viewBtnShowSavedCardHeight.constant = 50
592
629
  self.viewBtnShowSavedCardTopCon.constant = 20
593
630
  self.OTPView.isHidden = true
@@ -600,12 +637,30 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
600
637
  self.btnNext.isHidden = false
601
638
  self.btnNextHeight.constant = 50
602
639
  self.btnNextTopCon.constant = 20
603
- self.viewTermsAndConditions.isHidden = false
640
+ // self.viewTermsAndConditions.isHidden = false
604
641
  self.viewCrypto.isHidden = true
605
642
  self.viewSingleSavedAccount.isHidden = true
606
643
  self.viewChangedAccount.isHidden = true
644
+
645
+ if request?.authenticatedACH == false {
646
+ self.viewBankFields.isHidden = false
647
+ self.viewTermsAndConditions.isHidden = false
648
+ self.grailPayBankLinkView.isHidden = true
649
+ } else {
650
+ self.viewBankFields.isHidden = true
651
+ self.viewTermsAndConditions.isHidden = true
652
+ self.grailPayBankLinkView.isHidden = false
653
+ self.btnNext.isHidden = true
654
+ self.btnNextHeight.constant = 0
655
+ self.btnNextTopCon.constant = 0
656
+ }
607
657
  }
608
658
  }
659
+
660
+ DispatchQueue.main.async {
661
+ self.view.setNeedsLayout()
662
+ self.view.layoutIfNeeded()
663
+ }
609
664
  }
610
665
 
611
666
  //MARK: - Ui Colors Setup.
@@ -625,6 +680,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
625
680
  bgViewSingleSavedCard.backgroundColor = uiColor
626
681
  bgViewUpdateCard.backgroundColor = uiColor
627
682
  viewSingelBankAccount.backgroundColor = uiColor
683
+ viewGrailPaySavedAccount.backgroundColor = uiColor
684
+ viewGrailPayAbandon.backgroundColor = uiColor
628
685
  }
629
686
 
630
687
  if let primaryBtnBackGroundColor = UserStoreSingleton.shared.primary_btn_bg_col,
@@ -646,6 +703,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
646
703
  btnOnChain.tintColor = uiColor
647
704
  btnLightning.tintColor = uiColor
648
705
  btnTryAgain.backgroundColor = uiColor
706
+ btnCheckGrailPaySavedAccount.tintColor = uiColor
707
+ btnCheckBoxGrailPaySaveAccount.tintColor = uiColor
708
+ btnCheckBoxGrailPayAgreeTerms.tintColor = uiColor
709
+ lblGrailPayTermsAndCondition.textColor = uiColor
710
+ lblGrailPayAabandonError.textColor = uiColor
649
711
 
650
712
  btnScanCard.tintColor = uiColor // Set color for the image
651
713
  btnScanCard.setTitleColor(uiColor, for: .normal) // Set color for the text
@@ -699,6 +761,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
699
761
  btnTryAgain.layer.cornerRadius = CGFloat(borderRadius)
700
762
  viewCryptoTryAgain.layer.cornerRadius = CGFloat(borderRadius)
701
763
  viewCryptoQRCode.layer.cornerRadius = CGFloat(borderRadius)
764
+ viewGrailPaySavedAccount.layer.cornerRadius = CGFloat(borderRadius)
765
+ viewGrailPayAbandon.layer.cornerRadius = CGFloat(borderRadius)
702
766
 
703
767
  viewTextOTP1.layer.cornerRadius = CGFloat(borderRadius)
704
768
  viewTextOTP2.layer.cornerRadius = CGFloat(borderRadius)
@@ -716,6 +780,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
716
780
  btnTryAgain.layer.cornerRadius = 8
717
781
  viewCryptoTryAgain.layer.cornerRadius = 8
718
782
  viewCryptoQRCode.layer.cornerRadius = 8
783
+ viewGrailPaySavedAccount.layer.cornerRadius = 8
784
+ viewGrailPayAbandon.layer.cornerRadius = 8
719
785
  }
720
786
  viewBtnShowSavedCards.layer.masksToBounds = true // Ensure the corners are clipped properly
721
787
  bgViewSingleSavedCard.layer.masksToBounds = true
@@ -752,6 +818,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
752
818
  subLblPriceChanged.textColor = placeholderColor
753
819
  lblEasyMerchantOne.textColor = placeholderColor
754
820
  btnSettings.tintColor = placeholderColor
821
+ lblGrailPaySaveAccountForFuture.textColor = placeholderColor
822
+ lblGrailPayAgreeTo.textColor = placeholderColor
755
823
  }
756
824
 
757
825
  if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
@@ -784,6 +852,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
784
852
  lblSetting.textColor = uiColor
785
853
  btnChangeLanguage.tintColor = uiColor
786
854
  btnLogout.tintColor = uiColor
855
+ lblGrailPaySavedAccountNumber.tintColor = uiColor
856
+ lblGrailPaySavedAccountType.tintColor = uiColor
787
857
 
788
858
  txtFieldEmail.textColor = uiColor
789
859
  txtFieldOTPText1.textColor = uiColor
@@ -863,6 +933,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
863
933
  lblTermsAndConditions.font = UIFont.systemFont(ofSize: fontSize)
864
934
  lblTermsAndCondtionsSingleAccount.font = UIFont.systemFont(ofSize: fontSize)
865
935
  lblBtnShowSaveCard.font = UIFont.systemFont(ofSize: fontSize)
936
+ lblGrailPayAabandonError.font = UIFont.systemFont(ofSize: fontSize)
937
+ lblGrailPayTermsAndCondition.font = UIFont.systemFont(ofSize: fontSize)
938
+ lblGrailPayAgreeTo.font = UIFont.systemFont(ofSize: fontSize)
939
+ lblGrailPaySaveAccountForFuture.font = UIFont.systemFont(ofSize: fontSize)
940
+ lblGrailPaySavedAccountType.font = UIFont.systemFont(ofSize: fontSize)
941
+ lblGrailPaySavedAccountNumber.font = UIFont.systemFont(ofSize: fontSize)
866
942
 
867
943
  btnShowSavedCard.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
868
944
  btnResendOTP.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
@@ -879,7 +955,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
879
955
  btnCheckBoxSavedCard.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
880
956
  btnSavedCardForFutureNewCardView.titleLabel?.font = UIFont.systemFont(ofSize: fontSize)
881
957
  }
882
-
883
958
  }
884
959
 
885
960
  func setUpTextFieldsDelegates() {
@@ -909,32 +984,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
909
984
  txtFieldRoutingNumberNewAccountView.delegate = self
910
985
  txtFieldAccountTypeNewAccountView.delegate = self
911
986
  txtFieldAccountNumber.delegate = self
912
-
913
- viewTextFieldCardNumber.layer.borderColor = UIColor.systemGray.cgColor
914
- viewTxtFieldExpiryDate.layer.borderColor = UIColor.systemGray.cgColor
915
- viewTxtFieldCVV.layer.borderColor = UIColor.systemGray.cgColor
916
- viewTxtFieldNameOnCard.layer.borderColor = UIColor.systemGray.cgColor
917
- viewTxtFieldEmail.layer.borderColor = UIColor.systemGray.cgColor
918
- viewTxtFieldCVVSingleSavedCard.layer.borderColor = UIColor.systemGray.cgColor
919
- //Update Card View
920
- viewTxtFieldExpireDateUpdateCardView.layer.borderColor = UIColor.systemGray.cgColor
921
- viewtxtFieldCVVUpdateCardView.layer.borderColor = UIColor.systemGray.cgColor
922
- viewTxtFieldNameOnCardUpdateCardView.layer.borderColor = UIColor.systemGray.cgColor
923
- //NEW Card View
924
- viewtxtFieldCardNumberNewCardView.layer.borderColor = UIColor.systemGray.cgColor
925
- viewtxtFieldExpiryDateNewCardView.layer.borderColor = UIColor.systemGray.cgColor
926
- viewtxtFieldCVVNewCardView.layer.borderColor = UIColor.systemGray.cgColor
927
- viewtxtFieldNameOnCardNewCardView.layer.borderColor = UIColor.systemGray.cgColor
928
- //Bank View
929
- viewtxtFieldAccountName.layer.borderColor = UIColor.systemGray.cgColor
930
- viewtxtFieldRoutingNumber.layer.borderColor = UIColor.systemGray.cgColor
931
- viewtxtFieldAccountType.layer.borderColor = UIColor.systemGray.cgColor
932
- viewtxtFieldAccountNumber.layer.borderColor = UIColor.systemGray.cgColor
933
- //New Bank Account View
934
- viewtxtFieldAccountNameNewAccountView.layer.borderColor = UIColor.systemGray.cgColor
935
- viewtxtFieldRoutingNumberNewAccountView.layer.borderColor = UIColor.systemGray.cgColor
936
- viewtxtFieldAccountTypeNewAccountView.layer.borderColor = UIColor.systemGray.cgColor
937
- viewtxtFieldAccountNumberNewAccountView.layer.borderColor = UIColor.systemGray.cgColor
938
987
  }
939
988
 
940
989
  private func updateSaveButtons() {
@@ -943,7 +992,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
943
992
 
944
993
  let cardImageName = isSavedCard ? "checkmark.square.fill" : "square"
945
994
  let accountImageName = isSavedAccount ? "checkmark.square.fill" : "square"
946
-
995
+
947
996
  btnCheckBoxSavedCard.setImage(UIImage(systemName: cardImageName), for: .normal)
948
997
  btnSavedCardForFutureNewCardView.setImage(UIImage(systemName: cardImageName), for: .normal)
949
998
 
@@ -979,7 +1028,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
979
1028
  present(vc, animated: true, completion: nil)
980
1029
  }
981
1030
 
982
- //MARK: - Term & Conditions setup for Signle saved bank account view
1031
+ //MARK: - Term & Conditions setup for Single saved bank account view
983
1032
  func setUpTermAndConditionsForSingleSavedAccountButton() {
984
1033
  let string = "TERMS & CONDITIONS"
985
1034
  let attributedString = NSMutableAttributedString(string: string)
@@ -1007,6 +1056,34 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1007
1056
  present(vc, animated: true, completion: nil)
1008
1057
  }
1009
1058
 
1059
+ //MARK: - Term & Conditions setup for GrailPay saved bank account view
1060
+ func setUpTermAndConditionsForGrailPaySavedAccountButton() {
1061
+ let string = "TERMS & CONDITIONS"
1062
+ let attributedString = NSMutableAttributedString(string: string)
1063
+
1064
+ attributedString.addAttribute(
1065
+ .font,
1066
+ value: UIFont.systemFont(ofSize: 12, weight: .medium),
1067
+ range: NSRange(location: 0, length: string.count)
1068
+ )
1069
+
1070
+ // Assign the attributed string to the label
1071
+ lblGrailPayTermsAndCondition.attributedText = attributedString
1072
+ lblGrailPayTermsAndCondition.isUserInteractionEnabled = true
1073
+ // Add tap gesture recognizer
1074
+ let tap = UITapGestureRecognizer(target: self, action: #selector(aboutTermsAndGrailPayConditions))
1075
+ lblGrailPayTermsAndCondition.addGestureRecognizer(tap)
1076
+ }
1077
+
1078
+ @objc func aboutTermsAndGrailPayConditions(sender: UITapGestureRecognizer) {
1079
+ guard let vc = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
1080
+ print("Error: Could not find TermAndConditionsVC in easymerchantsdk storyboard.")
1081
+ return
1082
+ }
1083
+ vc.modalPresentationStyle = .overFullScreen
1084
+ present(vc, animated: true, completion: nil)
1085
+ }
1086
+
1010
1087
  func didPassTextBack(_ text: String) {
1011
1088
  print("Received text: \(text)")
1012
1089
  isFrom = text
@@ -1081,26 +1158,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1081
1158
  }
1082
1159
 
1083
1160
  //MARK: - Crypto Functions
1084
-
1085
- // Method to generate QR code from a string
1086
- // private func generateQRCode(from string: String) -> UIImage? {
1087
- // // Get data from the string
1088
- // guard let data = string.data(using: .ascii) else { return nil }
1089
- // // Get a QR CIFilter
1090
- // guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
1091
- // qrFilter.setValue(data, forKey: "inputMessage")
1092
- // // Get the output image
1093
- // guard let qrImage = qrFilter.outputImage else { return nil }
1094
- // // Scale the image
1095
- // let transform = CGAffineTransform(scaleX: 10, y: 10)
1096
- // let scaledQrImage = qrImage.transformed(by: transform)
1097
- // // Do some processing to get the UIImage
1098
- // let context = CIContext()
1099
- // guard let cgImage = context.createCGImage(scaledQrImage, from: scaledQrImage.extent) else { return nil }
1100
- //
1101
- // return UIImage(cgImage: cgImage)
1102
- // }
1103
-
1104
1161
  private func generateQRCode(from string: String) -> UIImage? {
1105
1162
  // Convert string to data
1106
1163
  guard let data = string.data(using: .ascii) else { return nil }
@@ -1300,14 +1357,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1300
1357
 
1301
1358
  let cvvText = txtFieldCVVSingleSavedCard.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
1302
1359
 
1303
- // Show an alert if the CVV field is empty
1304
- // if cvvText.isEmpty {
1305
- // let alert = UIAlertController(title: "Missing CVV", message: "Please enter the CVV to proceed.", preferredStyle: .alert)
1306
- // alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
1307
- // self.present(alert, animated: true, completion: nil)
1308
- // return
1309
- // }
1310
-
1311
1360
  if cvvText.isEmpty {
1312
1361
  self.showAlert(title: "Missing CVV", message: "Please enter the CVV to proceed.")
1313
1362
  } else if cvvText.count < 3 {
@@ -1337,14 +1386,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1337
1386
  // If billingInfoData is nil or empty, set the button title to "Pay Now"
1338
1387
  let cvvText = txtFieldCVVSingleSavedCard.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
1339
1388
 
1340
- // Show an alert if the CVV field is empty
1341
- // if cvvText.isEmpty {
1342
- // let alert = UIAlertController(title: "Missing CVV", message: "Please enter the CVV to proceed.", preferredStyle: .alert)
1343
- // alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
1344
- // self.present(alert, animated: true, completion: nil)
1345
- // return
1346
- // }
1347
-
1348
1389
  if cvvText.isEmpty {
1349
1390
  self.showAlert(title: "Missing CVV", message: "Please enter the CVV to proceed.")
1350
1391
  }
@@ -1496,6 +1537,203 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1496
1537
  btnCheckBoxSavedCard.setImage(UIImage(systemName: imageName), for: .normal)
1497
1538
  }
1498
1539
 
1540
+ @IBAction func actionBtnGrailPayCheckBoxSaveAccountForFuture(_ sender: UIButton) {
1541
+
1542
+ }
1543
+
1544
+ @IBAction func actionBtnGrailPayCheckAgreeTerms(_ sender: UIButton) {
1545
+
1546
+ }
1547
+
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
+ }
1607
+ }
1608
+
1609
+ //MARK: - Account Connect Api
1610
+ func accountConnectApi(account: [String: Any]) {
1611
+ showLoadingIndicator()
1612
+
1613
+ let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.accountConnect.path()
1614
+
1615
+ guard let serviceURL = URL(string: fullURL) else {
1616
+ print("Invalid URL")
1617
+ hideLoadingIndicator()
1618
+ return
1619
+ }
1620
+
1621
+ var request = URLRequest(url: serviceURL)
1622
+ request.httpMethod = "POST"
1623
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
1624
+
1625
+ // Add API headers
1626
+ if let apiKey = EnvironmentConfig.apiKey,
1627
+ let apiSecret = EnvironmentConfig.apiSecret {
1628
+ request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
1629
+ request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
1630
+ }
1631
+
1632
+ // Prepare parameters
1633
+ let params: [String: Any] = [
1634
+ "account_uuid": account["account_uuid"] ?? "",
1635
+ "user_uuid": account["user_uuid"] ?? "",
1636
+ "aggregator_type": account["aggregator_type"] ?? "bank_link",
1637
+ "vendor_id": account["vendor_id"] ?? "",
1638
+ "created_at": account["created_at"] ?? "",
1639
+ "updated_at": account["updated_at"] ?? "",
1640
+ "account_id": account["account_id"] ?? "",
1641
+ "account_number": account["account_number"] ?? "",
1642
+ "provider_name": account["provider_name"] ?? "",
1643
+ "routing_number": account["routing_number"] ?? "",
1644
+ "name": account["name"] ?? "",
1645
+ "account_type": account["account_type"] ?? "",
1646
+ "account_status": account["account_status"] ?? "",
1647
+ "customer_id": "" // add if needed
1648
+ ]
1649
+
1650
+ do {
1651
+ let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
1652
+ request.httpBody = jsonData
1653
+
1654
+ if let jsonString = String(data: jsonData, encoding: .utf8) {
1655
+ print("🔍 JSON Payload:\n\(jsonString)")
1656
+ }
1657
+ } catch {
1658
+ print("❌ JSON Serialization Error: \(error)")
1659
+ hideLoadingIndicator()
1660
+ return
1661
+ }
1662
+
1663
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
1664
+ DispatchQueue.main.async { self.hideLoadingIndicator() }
1665
+
1666
+ if let error = error {
1667
+ print("❌ Request Error: \(error.localizedDescription)")
1668
+ return
1669
+ }
1670
+
1671
+ guard let httpResponse = response as? HTTPURLResponse else {
1672
+ print("❌ Invalid response")
1673
+ return
1674
+ }
1675
+
1676
+ if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
1677
+ guard let data = data else {
1678
+ print("❌ No data received")
1679
+ return
1680
+ }
1681
+
1682
+ do {
1683
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
1684
+ print("✅ Account Connected Successfully: \(responseObject)")
1685
+
1686
+ if let status = responseObject["status"] as? Int, status == 1 {
1687
+ self.isBankAccountConnected = true
1688
+ self.defaultBankAccountID = responseObject["default_account"] as? String
1689
+ } else {
1690
+ self.isBankAccountConnected = false
1691
+ self.defaultBankAccountID = nil
1692
+ }
1693
+
1694
+ DispatchQueue.main.async {
1695
+ // ✅ Show saved bank UI
1696
+ self.viewGrailPaySavedBank.isHidden = false
1697
+ self.viewGrailPaySavedAccount.isHidden = false
1698
+
1699
+ if let accountNumber = account["account_number"] as? String {
1700
+ let last4 = String(accountNumber.suffix(4))
1701
+ self.lblGrailPaySavedAccountNumber.text = "****\(last4)"
1702
+ } else {
1703
+ self.lblGrailPaySavedAccountNumber.text = "****----" // Default/fallback text
1704
+ }
1705
+
1706
+ // ✅ Set account type
1707
+ if let accountType = account["account_type"] as? String {
1708
+ self.lblGrailPaySavedAccountType.text = "Type: \(accountType.capitalized)"
1709
+ }
1710
+
1711
+ // ✅ Optionally hide abandon view if visible
1712
+ self.viewGrailPayAbandon.isHidden = true
1713
+
1714
+ if self.selectedPaymentMethod == "Bank" {
1715
+ self.viewBtnShowSavedCards.isHidden = true
1716
+ self.btnShowSavedCard.isHidden = true
1717
+ self.viewBtnShowSavedCardHeight.constant = 0
1718
+ self.viewBtnShowSavedCardTopCon.constant = 0
1719
+ }
1720
+ }
1721
+ }
1722
+
1723
+ else {
1724
+ print("❌ Invalid JSON format")
1725
+ }
1726
+ } catch {
1727
+ print("❌ JSON Parsing Error: \(error)")
1728
+ }
1729
+ } else {
1730
+ print("❌ HTTP Status Code: \(httpResponse.statusCode)")
1731
+ }
1732
+ }
1733
+
1734
+ task.resume()
1735
+ }
1736
+
1499
1737
  // MARK: - Action Button Next
1500
1738
  @IBAction func actionBtnNext(_ sender: UIButton) {
1501
1739
  // Check if billing info data exists and is valid JSON
@@ -1592,15 +1830,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1592
1830
  // Billing info is nil or empty
1593
1831
  if selectedPaymentMethod == "Card" {
1594
1832
  /// Card Case
1595
- // if cardNumberTextField.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
1596
- // showAlert(title: "Missing Information", message: "Card Number is required.")
1597
- // } else if cardExpiryTextField.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
1598
- // showAlert(title: "Missing Information", message: "Card Expiry Date is required.")
1599
- // } else if cardCvvTextField.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
1600
- // showAlert(title: "Missing Information", message: "Card CVV Number is required.")
1601
- // } else if cardNameTextField.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
1602
- // showAlert(title: "Missing Information", message: "Card Name is required.")
1603
- // }
1604
1833
 
1605
1834
  // Trim whitespace and newlines
1606
1835
  let cardNumber = self.cardNumberTextField.text.trimmingCharacters(in: .whitespacesAndNewlines)
@@ -1737,7 +1966,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1737
1966
  else if selectedPaymentMethod == "Bank" {
1738
1967
  self.viewBankFields.isHidden = false
1739
1968
  self.viewBtnShowSavedCards.isHidden = false
1740
- self.lblBtnShowSaveCard.text = "Use Saved Accounts"
1969
+ self.lblBtnShowSaveCard.text = "Show Saved Accounts"
1741
1970
  self.viewBtnShowSavedCardHeight.constant = 50
1742
1971
  self.viewBtnShowSavedCardTopCon.constant = 20
1743
1972
  self.OTPView.isHidden = true
@@ -2045,50 +2274,49 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2045
2274
 
2046
2275
  //MARK: - Card Scan Button Action
2047
2276
  @IBAction func actionButtonScanCard(_ sender: UIButton) {
2048
- // // Validate BlinkCard License
2049
- // let licenseErrorMessage = BlinkCardHelper.setupLicense()
2050
- // if !licenseErrorMessage.isEmpty {
2051
- // // Show alert if the license is invalid or expired
2052
- // showLicenseErrorAlert(message: licenseErrorMessage)
2053
- // return
2054
- // }
2055
- //
2056
- // // Proceed with card scanning if the license is valid
2057
- // blinkCardRecognizer = MBCBlinkCardRecognizer()
2058
- // blinkCardRecognizer.returnFullDocumentImage = true
2059
- //
2060
- // let recognizerList = [blinkCardRecognizer!]
2061
- // let recognizerCollection = MBCRecognizerCollection(recognizers: recognizerList)
2062
- //
2063
- // let customOverlayViewController: CustomOverlay = CustomOverlay.initFromStoryboard()
2064
- //
2065
- // // Reconfigure recognizers with the overlay
2066
- // customOverlayViewController.reconfigureRecognizers(recognizerCollection)
2067
- //
2068
- // // Create the recognizer runner view controller with custom overlay
2069
- // guard let recognizerRunnerViewController =
2070
- // MBCViewControllerFactory.recognizerRunnerViewController(withOverlayViewController: customOverlayViewController) else {
2071
- // showLicenseErrorAlert(message: "Failed to initialize the recognizer view controller.")
2072
- // return
2073
- // }
2074
- //
2075
- // customOverlayViewController.delegate = self
2076
- // recognizerRunnerViewController.modalPresentationStyle = .fullScreen
2077
- //
2078
- // // Present the recognizer runner view controller
2079
- // self.present(recognizerRunnerViewController, animated: true, completion: nil)
2277
+ // // Validate BlinkCard License
2278
+ // let licenseErrorMessage = BlinkCardHelper.setupLicense()
2279
+ // if !licenseErrorMessage.isEmpty {
2280
+ // // Show alert if the license is invalid or expired
2281
+ // showLicenseErrorAlert(message: licenseErrorMessage)
2282
+ // return
2283
+ // }
2284
+ //
2285
+ // // Proceed with card scanning if the license is valid
2286
+ // blinkCardRecognizer = MBCBlinkCardRecognizer()
2287
+ // blinkCardRecognizer.returnFullDocumentImage = true
2288
+ //
2289
+ // let recognizerList = [blinkCardRecognizer!]
2290
+ // let recognizerCollection = MBCRecognizerCollection(recognizers: recognizerList)
2291
+ //
2292
+ // let customOverlayViewController: CustomOverlay = CustomOverlay.initFromStoryboard()
2293
+ //
2294
+ // // Reconfigure recognizers with the overlay
2295
+ // customOverlayViewController.reconfigureRecognizers(recognizerCollection)
2296
+ //
2297
+ // // Create the recognizer runner view controller with custom overlay
2298
+ // guard let recognizerRunnerViewController =
2299
+ // MBCViewControllerFactory.recognizerRunnerViewController(withOverlayViewController: customOverlayViewController) else {
2300
+ // showLicenseErrorAlert(message: "Failed to initialize the recognizer view controller.")
2301
+ // return
2302
+ // }
2303
+ //
2304
+ // customOverlayViewController.delegate = self
2305
+ // recognizerRunnerViewController.modalPresentationStyle = .fullScreen
2306
+ //
2307
+ // // Present the recognizer runner view controller
2308
+ // self.present(recognizerRunnerViewController, animated: true, completion: nil)
2080
2309
  }
2081
2310
 
2082
- // private func showLicenseErrorAlert(message: String) {
2083
- // let alert = UIAlertController(title: "License Error", message: message, preferredStyle: .alert)
2084
- // alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
2085
- // self.present(alert, animated: true, completion: nil)
2086
- // }
2311
+ private func showLicenseErrorAlert(message: String) {
2312
+ let alert = UIAlertController(title: "License Error", message: message, preferredStyle: .alert)
2313
+ alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
2314
+ self.present(alert, animated: true, completion: nil)
2315
+ }
2087
2316
 
2088
2317
  //MARK: - Send OTP Email Verification Api
2089
2318
  func emailVerificationApi() {
2090
2319
  showLoadingIndicator()
2091
-
2092
2320
  // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
2093
2321
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.emailVerification.path()
2094
2322
 
@@ -2171,7 +2399,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2171
2399
  func otpVerificationApi() {
2172
2400
  showLoadingIndicator()
2173
2401
 
2174
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
2175
2402
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.verifyOtp.path()
2176
2403
 
2177
2404
  guard let serviceURL = URL(string: fullURL) else {
@@ -2188,10 +2415,14 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2188
2415
  print("Setting clientToken header: \(token ?? "None")")
2189
2416
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
2190
2417
 
2418
+ // ✅ Safe access from main thread (assumes you call this from main thread)
2419
+ let email = self.txtFieldEmail.text ?? ""
2420
+ let otp = getCombinedOTP()
2421
+
2191
2422
  let params: [String: Any] = [
2192
- "card_search_value": txtFieldEmail.text ?? "",
2423
+ "card_search_value": email,
2193
2424
  "card_search_key": "email",
2194
- "otp": getCombinedOTP()
2425
+ "otp": otp
2195
2426
  ]
2196
2427
 
2197
2428
  do {
@@ -2200,14 +2431,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2200
2431
  if let jsonString = String(data: jsonData, encoding: .utf8) {
2201
2432
  print("JSON Payload: \(jsonString)")
2202
2433
  }
2203
- } catch let error {
2434
+ } catch {
2204
2435
  print("Error creating JSON data: \(error)")
2205
2436
  hideLoadingIndicator()
2206
2437
  return
2207
2438
  }
2208
2439
 
2209
- let session = URLSession.shared
2210
- let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
2440
+ let task = URLSession.shared.dataTask(with: request) { serviceData, serviceResponse, error in
2211
2441
 
2212
2442
  DispatchQueue.main.async {
2213
2443
  self.hideLoadingIndicator()
@@ -2224,86 +2454,83 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2224
2454
  }
2225
2455
 
2226
2456
  if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
2227
- if let data = serviceData {
2228
- do {
2229
- if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
2230
-
2457
+ guard let data = serviceData else {
2458
+ print("No data received")
2459
+ return
2460
+ }
2461
+
2462
+ do {
2463
+ if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
2464
+ print("Response Data: \(responseObject)")
2465
+
2466
+ guard let responseData = responseObject["data"] as? [String: Any],
2467
+ let nestedData = responseData["data"] as? [String: Any] else {
2468
+ print("Invalid response structure or missing keys")
2469
+ return
2470
+ }
2471
+
2472
+ let customerId = nestedData["customer_id"] as? String ?? ""
2473
+ let customerToken = nestedData["customer_token"] as? String ?? ""
2474
+
2475
+ DispatchQueue.main.async {
2476
+ UserStoreSingleton.shared.customerId = customerId
2477
+ UserStoreSingleton.shared.customerToken = customerToken
2478
+ UserStoreSingleton.shared.verificationEmail = email
2231
2479
  UserStoreSingleton.shared.isLoggedIn = true
2232
2480
 
2233
- print("Response Data: \(responseObject)")
2481
+ print("Saved Customer ID: \(UserStoreSingleton.shared.customerId ?? "nil")")
2482
+ print("Saved Customer Token: \(UserStoreSingleton.shared.customerToken ?? "nil")")
2483
+ print("Saved Verification Email: \(UserStoreSingleton.shared.verificationEmail ?? "nil")")
2234
2484
 
2235
- UserStoreSingleton.shared.isLoggedIn = true
2485
+ self.btnSettings.isHidden = false
2236
2486
 
2237
- if let responseData = responseObject["data"] as? [String: Any],
2238
- let data = responseData["data"] as? [String: Any],
2239
- let customerId = data["customer_id"] as? String,
2240
- let customerToken = data["customer_token"] as? String {
2241
-
2242
- // Save customer ID and token
2243
- UserStoreSingleton.shared.customerId = customerId
2244
- UserStoreSingleton.shared.customerToken = customerToken
2245
-
2246
- // Update UI-related or main thread operations
2247
- DispatchQueue.main.async {
2248
- UserStoreSingleton.shared.verificationEmail = self.txtFieldEmail.text
2249
-
2250
- print("Customer ID successfully saved: \(UserStoreSingleton.shared.customerId ?? "None")")
2251
- print("Customer Token successfully saved: \(UserStoreSingleton.shared.customerToken ?? "None")")
2252
- print("Customer email successfully saved: \(UserStoreSingleton.shared.verificationEmail ?? "None")")
2253
-
2254
- self.btnSettings.isHidden = false
2487
+ if self.selectedPaymentMethod == "Card" {
2488
+ if customerId.isEmpty {
2489
+ self.OTPView.isHidden = true
2490
+ self.viewCardFields.isHidden = false
2491
+ self.viewSingleSavedCard.isHidden = true
2492
+ self.viewBtnShowSavedCards.isHidden = true
2493
+ self.viewBtnShowSavedCardHeight.constant = 0
2494
+ self.viewBtnShowSavedCardTopCon.constant = 0
2495
+ self.viewChangeCard.isHidden = true
2496
+ self.viewUpdateCard.isHidden = true
2497
+ self.viewAddNewCard.isHidden = true
2498
+ } else {
2499
+ self.getShowCardsApi()
2500
+ self.OTPView.isHidden = true
2501
+ self.viewCardFields.isHidden = true
2502
+ self.viewSingleSavedCard.isHidden = false
2503
+ self.viewBtnShowSavedCards.isHidden = true
2504
+ self.viewBtnShowSavedCardHeight.constant = 0
2505
+ self.viewBtnShowSavedCardTopCon.constant = 0
2506
+ self.viewChangeCard.isHidden = true
2507
+ self.viewUpdateCard.isHidden = true
2508
+ self.viewAddNewCard.isHidden = true
2255
2509
 
2256
- // Check if customerId is nil or empty and update UI
2257
- if self.selectedPaymentMethod == "Card" {
2258
- if customerId.isEmpty {
2259
- self.OTPView.isHidden = true
2260
- self.viewCardFields.isHidden = false
2261
- self.viewSingleSavedCard.isHidden = true
2262
- self.viewBtnShowSavedCards.isHidden = true
2263
- self.viewBtnShowSavedCardHeight.constant = 0
2264
- self.viewBtnShowSavedCardTopCon.constant = 0
2265
- self.viewChangeCard.isHidden = true
2266
- self.viewUpdateCard.isHidden = true
2267
- self.viewAddNewCard.isHidden = true
2268
- }
2269
- else {
2270
- self.getShowCardsApi()
2271
- self.OTPView.isHidden = true
2272
- self.viewCardFields.isHidden = true
2273
- self.viewSingleSavedCard.isHidden = false
2274
- self.viewBtnShowSavedCards.isHidden = true
2275
- self.viewBtnShowSavedCardHeight.constant = 0
2276
- self.viewBtnShowSavedCardTopCon.constant = 0
2277
- self.viewChangeCard.isHidden = true
2278
- self.viewUpdateCard.isHidden = true
2279
- self.viewAddNewCard.isHidden = true
2280
-
2281
- self.viewTxtFieldCVVSingleCard.isHidden = true
2282
- self.btnPayNowSingleCard.isHidden = true
2283
- self.viewSingleSavedCardHeight.constant = 60
2284
- self.btnNext.isHidden = true
2285
- self.btnNextHeight.constant = 0
2286
- self.btnNextTopCon.constant = 8
2287
- }
2288
- }
2289
- else if self.selectedPaymentMethod == "Bank" {
2290
- self.getShowBankAccountsApi()
2291
- }
2510
+ self.viewTxtFieldCVVSingleCard.isHidden = true
2511
+ self.btnPayNowSingleCard.isHidden = true
2512
+ self.viewSingleSavedCardHeight.constant = 60
2513
+ self.btnNext.isHidden = true
2514
+ self.btnNextHeight.constant = 0
2515
+ self.btnNextTopCon.constant = 8
2292
2516
  }
2517
+ } else if self.selectedPaymentMethod == "Bank" {
2518
+ self.getShowBankAccountsApi()
2293
2519
  }
2294
- } else {
2295
- print("Invalid JSON format")
2296
2520
  }
2297
- } catch let jsonError {
2298
- print("Error parsing JSON: \(jsonError)")
2521
+
2522
+ } else {
2523
+ print("Invalid JSON format")
2299
2524
  }
2300
- } else {
2301
- print("No data received")
2525
+ } catch {
2526
+ print("Error parsing JSON: \(error)")
2302
2527
  }
2528
+
2303
2529
  } else {
2304
2530
  print("HTTP Status Code: \(httpResponse.statusCode)")
2305
2531
  }
2306
2532
  }
2533
+
2307
2534
  task.resume()
2308
2535
  }
2309
2536
 
@@ -3040,23 +3267,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3040
3267
  }
3041
3268
 
3042
3269
  //MARK: - Bank Api's
3043
-
3044
- //MARK: - GET Show Bank Accounts API
3270
+ // MARK: - GET Show Bank Accounts API
3045
3271
  func getShowBankAccountsApi() {
3046
3272
  showLoadingIndicator()
3047
3273
 
3048
- // var components = URLComponents()
3049
- // components.scheme = "https"
3050
- // components.host = "stage-api.stage-easymerchant.io"
3051
- // components.path = "/api/v1/ach/account"
3052
- //
3053
- // guard let serviceURL = components.url else {
3054
- // print("Invalid URL")
3055
- // hideLoadingIndicator()
3056
- // return
3057
- // }
3058
-
3059
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3060
3274
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.account.path()
3061
3275
 
3062
3276
  guard let serviceURL = URL(string: fullURL) else {
@@ -3069,16 +3283,23 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3069
3283
  request.httpMethod = "GET"
3070
3284
  request.addValue("application/json", forHTTPHeaderField: "Content-Type")
3071
3285
 
3072
- let token = UserStoreSingleton.shared.customerToken
3073
- print("Setting customerToken header: \(token ?? "None")")
3074
- request.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
3286
+ // Retrieve and validate customer token
3287
+ guard let token = UserStoreSingleton.shared.customerToken, !token.isEmpty else {
3288
+ print("Customer token is missing or empty")
3289
+ hideLoadingIndicator()
3290
+ showAlert(message: "Session expired. Please log in again.")
3291
+ return
3292
+ }
3293
+
3294
+ print("Setting customerToken header: \(token)")
3295
+ request.addValue(token, forHTTPHeaderField: "Customer-Token")
3075
3296
 
3076
3297
  let session = URLSession.shared
3077
3298
  let task = session.dataTask(with: request) { [weak self] (data, response, error) in
3078
3299
  guard let self = self else { return }
3079
3300
 
3080
3301
  DispatchQueue.main.async {
3081
- self.hideLoadingIndicator() // Stop loader when response is received
3302
+ self.hideLoadingIndicator()
3082
3303
  }
3083
3304
 
3084
3305
  if let error = error {
@@ -3086,21 +3307,36 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3086
3307
  return
3087
3308
  }
3088
3309
 
3089
- guard let data = data else { return }
3310
+ if let httpResponse = response as? HTTPURLResponse {
3311
+ print("Status Code: \(httpResponse.statusCode)")
3312
+ print("Headers: \(httpResponse.allHeaderFields)")
3313
+ }
3314
+
3315
+ guard let data = data else {
3316
+ print("No data received")
3317
+ return
3318
+ }
3319
+
3320
+ if let responseString = String(data: data, encoding: .utf8) {
3321
+ print("Response Body: \(responseString)")
3322
+ }
3090
3323
 
3091
3324
  do {
3092
3325
  let decoder = JSONDecoder()
3093
3326
  let bankAccountModel = try decoder.decode(BankAccountModel.self, from: data)
3094
3327
 
3095
3328
  if bankAccountModel.status == true {
3096
- // Update bankAccounts array on the main thread
3097
3329
  DispatchQueue.main.async {
3098
3330
  self.bankAccounts = bankAccountModel.accounts ?? []
3331
+ print("Number of bank accounts: \(self.bankAccounts.count)")
3332
+
3333
+ if self.bankAccounts.isEmpty {
3334
+ print("No bank accounts found. Please add a new one.")
3335
+ return
3336
+ }
3099
3337
 
3100
3338
  if let firstAccount = self.bankAccounts.first {
3101
- // Set the account number and account type in the labels
3102
3339
  self.selectedbankAccounts = firstAccount
3103
-
3104
3340
  self.lblAccountNumberSingleAccountView.text = "****\(firstAccount.account_number_last_4 ?? "")"
3105
3341
  self.lblAccountTypeSingleAccountView.text = "Type: \(firstAccount.account_type ?? "")"
3106
3342
  }
@@ -3109,9 +3345,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3109
3345
 
3110
3346
  self.viewSingleSavedAccount.isHidden = false
3111
3347
  self.viewBankFields.isHidden = true
3348
+
3112
3349
  self.viewBtnShowSavedCards.isHidden = true
3113
3350
  self.viewBtnShowSavedCardHeight.constant = 0
3114
3351
  self.viewBtnShowSavedCardTopCon.constant = 0
3352
+
3115
3353
  self.OTPView.isHidden = true
3116
3354
  self.emailView.isHidden = true
3117
3355
  self.viewCardFields.isHidden = true
@@ -3125,9 +3363,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3125
3363
 
3126
3364
  self.viewNewBankAccount.isHidden = true
3127
3365
  self.viewCrypto.isHidden = true
3128
-
3129
3366
  self.viewTermsAndConditions.isHidden = true
3130
-
3131
3367
  self.viewChangedAccount.isHidden = true
3132
3368
 
3133
3369
  if self.isSelectForPayBank {
@@ -3137,15 +3373,17 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3137
3373
  } else {
3138
3374
  self.viewTermAndConditionsSingleAccountView.isHidden = true
3139
3375
  self.btnPayNowSingleAccountView.isHidden = true
3140
- self.viewSingleAccountViewHeight.constant = 68
3376
+ self.viewSingleAccountViewHeight.constant = 68
3141
3377
  self.btnNext.isHidden = true
3142
3378
  self.btnNextHeight.constant = 0
3143
3379
  self.btnNextTopCon.constant = 0
3144
3380
  }
3145
3381
  }
3146
3382
  } else {
3147
- // Handle the error or show a message
3148
- print("Error: \(bankAccountModel.message ?? "Unknown error")")
3383
+ print("Error from server: \(bankAccountModel.message ?? "Unknown error")")
3384
+ DispatchQueue.main.async {
3385
+ self.showAlert(message: bankAccountModel.message ?? "Something went wrong.")
3386
+ }
3149
3387
  }
3150
3388
  } catch {
3151
3389
  print("Failed to parse response: \(error.localizedDescription)")
@@ -3158,19 +3396,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3158
3396
  //MARK: - Banking Account Charge Api If Billing info is nil and without Login
3159
3397
  func accountChargeApi() {
3160
3398
  showLoadingIndicator()
3161
-
3162
- // var components = URLComponents()
3163
- // components.scheme = "https"
3164
- // components.host = "stage-api.stage-easymerchant.io"
3165
- // components.path = "/api/v1/ach/charge"
3166
- //
3167
- // guard let serviceURL = components.url else {
3168
- // print("Invalid URL")
3169
- // hideLoadingIndicator()
3170
- // return
3171
- // }
3172
-
3173
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3174
3399
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
3175
3400
 
3176
3401
  guard let serviceURL = URL(string: fullURL) else {
@@ -3270,18 +3495,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3270
3495
  func accountChargeSavedBankAccountApi() {
3271
3496
  showLoadingIndicator()
3272
3497
 
3273
- // var components = URLComponents()
3274
- // components.scheme = "https"
3275
- // components.host = "stage-api.stage-easymerchant.io"
3276
- // components.path = "/api/v1/ach/charge"
3277
- //
3278
- // guard let serviceURL = components.url else {
3279
- // print("Invalid URL")
3280
- // hideLoadingIndicator()
3281
- // return
3282
- // }
3283
-
3284
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3285
3498
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
3286
3499
 
3287
3500
  guard let serviceURL = URL(string: fullURL) else {
@@ -3378,18 +3591,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3378
3591
  func accountChargeAddNewAccountApi() {
3379
3592
  showLoadingIndicator()
3380
3593
 
3381
- // var components = URLComponents()
3382
- // components.scheme = "https"
3383
- // components.host = "stage-api.stage-easymerchant.io"
3384
- // components.path = "/api/v1/ach/charge"
3385
- //
3386
- // guard let serviceURL = components.url else {
3387
- // print("Invalid URL")
3388
- // hideLoadingIndicator()
3389
- // return
3390
- // }
3391
-
3392
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3393
3594
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
3394
3595
 
3395
3596
  guard let serviceURL = URL(string: fullURL) else {
@@ -3496,18 +3697,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3496
3697
  func accountChargeApi(customerId: String?) {
3497
3698
  showLoadingIndicator()
3498
3699
 
3499
- // var components = URLComponents()
3500
- // components.scheme = "https"
3501
- // components.host = "stage-api.stage-easymerchant.io"
3502
- // components.path = "/api/v1/ach/charge"
3503
- //
3504
- // guard let serviceURL = components.url else {
3505
- // print("Invalid URL")
3506
- // hideLoadingIndicator()
3507
- // return
3508
- // }
3509
-
3510
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3511
3700
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()
3512
3701
 
3513
3702
  guard let serviceURL = URL(string: fullURL) else {
@@ -3624,18 +3813,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3624
3813
  func deleteAccountApi(accountID: String, at index: Int) {
3625
3814
  showLoadingIndicator()
3626
3815
 
3627
- // var components = URLComponents()
3628
- // components.scheme = "https"
3629
- // components.host = "stage-api.stage-easymerchant.io"
3630
- // components.path = "/api/v1/ach/account/\(accountID)" // Append the account_id to the path
3631
- //
3632
- // guard let serviceURL = components.url else {
3633
- // print("Invalid URL")
3634
- // hideLoadingIndicator()
3635
- // return
3636
- // }
3637
-
3638
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3639
3816
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.achCharge.path()+"/\(accountID)"
3640
3817
 
3641
3818
  guard let serviceURL = URL(string: fullURL) else {
@@ -3704,18 +3881,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3704
3881
  // MARK: - Crypto Charge API
3705
3882
  func cryptoChargeApi() {
3706
3883
  showLoadingIndicator()
3707
- // var components = URLComponents()
3708
- // components.scheme = "https"
3709
- // components.host = "stage-api.stage-easymerchant.io"
3710
- // components.path = "/api/v1/charges"
3711
- //
3712
- // guard let serviceURL = components.url else {
3713
- // print("Invalid URL")
3714
- // hideLoadingIndicator()
3715
- // return
3716
- // }
3717
3884
 
3718
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3719
3885
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
3720
3886
 
3721
3887
  guard let serviceURL = URL(string: fullURL) else {
@@ -3851,17 +4017,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3851
4017
  //MARK: - GET Crypto Payment Info API
3852
4018
  func getCryptoPaymentInfoApi(chargeId: String) {
3853
4019
 
3854
- // var components = URLComponents()
3855
- // components.scheme = "https"
3856
- // components.host = "stage-api.stage-easymerchant.io"
3857
- // components.path = "/api/v1/charges/\(chargeId)" // Append chargeId to the path
3858
- //
3859
- // guard let serviceURL = components.url else {
3860
- // print("Invalid URL")
3861
- // return
3862
- // }
3863
-
3864
- // Construct the full URL using baseURL from EnvironmentConfig and path from the endpoint
3865
4020
  let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()+"/\(chargeId)"
3866
4021
 
3867
4022
  guard let serviceURL = URL(string: fullURL) else {
@@ -3971,8 +4126,8 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
3971
4126
  imageName = "creditcard"
3972
4127
  case "Crypto":
3973
4128
  imageName = "bitcoinsign.circle.fill"
3974
- // case "Wallet":
3975
- // imageName = "Wallet"
4129
+ // case "Wallet":
4130
+ // imageName = "Wallet"
3976
4131
  default:
3977
4132
  imageName = "Wallet"
3978
4133
  }
@@ -3995,111 +4150,154 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
3995
4150
 
3996
4151
  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
3997
4152
  selectedIndex = indexPath
3998
- selectedPaymentMethod = arrPaymentMethods[indexPath.row].name
4153
+ let method = arrPaymentMethods[indexPath.row].name
4154
+ updatePaymentMethodUI(for: method)
3999
4155
  collectionView.reloadData()
4156
+ }
4157
+
4158
+ func updatePaymentMethodUI(for method: String) {
4159
+ selectedPaymentMethod = method
4000
4160
 
4001
- // Show the appropriate view based on the selected payment method
4002
- switch arrPaymentMethods[indexPath.row].name {
4161
+ switch method {
4003
4162
  case "Card":
4004
- if selectedPaymentMethod == "Card" {
4005
- if UserStoreSingleton.shared.isLoggedIn == false {
4006
- self.viewSingleSavedAccount.isHidden = true
4007
- self.viewBankFields.isHidden = true
4008
- self.viewBtnShowSavedCards.isHidden = false
4009
- self.lblBtnShowSaveCard.text = "Show Saved Cards"
4010
- self.viewBtnShowSavedCardHeight.constant = 50
4011
- self.viewBtnShowSavedCardTopCon.constant = 20
4012
- self.OTPView.isHidden = true
4013
- self.emailView.isHidden = true
4014
- self.viewCardFields.isHidden = false
4015
- self.viewSingleSavedCard.isHidden = true
4016
- self.viewChangeCard.isHidden = true
4017
- self.viewUpdateCard.isHidden = true
4018
- self.viewAddNewCard.isHidden = true
4019
- self.btnNext.isHidden = false
4020
- self.btnNextHeight.constant = 50
4021
- self.btnNextTopCon.constant = 20
4022
- self.viewTermsAndConditions.isHidden = true
4023
- self.viewCrypto.isHidden = true
4024
- }
4025
- else {
4026
- getShowCardsApi()
4027
- self.viewSingleSavedAccount.isHidden = true
4028
- self.viewBankFields.isHidden = true
4029
- self.viewBtnShowSavedCards.isHidden = false
4030
- self.viewBtnShowSavedCards.isHidden = true
4031
- self.viewBtnShowSavedCardHeight.constant = 0
4032
- self.viewBtnShowSavedCardTopCon.constant = 0
4033
- self.OTPView.isHidden = true
4034
- self.emailView.isHidden = true
4035
- self.viewCardFields.isHidden = true
4036
- self.viewSingleSavedCard.isHidden = false
4037
- self.viewChangeCard.isHidden = true
4038
- self.viewUpdateCard.isHidden = true
4039
- self.viewAddNewCard.isHidden = true
4040
- self.btnNext.isHidden = true
4041
- self.btnNextHeight.constant = 0
4042
- self.btnNextTopCon.constant = 8
4043
- self.viewChangedAccount.isHidden = true
4044
- self.viewNewBankAccount.isHidden = true
4045
- self.viewCrypto.isHidden = true
4046
- }
4047
- }
4048
- case "Bank":
4049
- if selectedPaymentMethod == "Bank" {
4050
- if UserStoreSingleton.shared.isLoggedIn == false {
4051
- self.viewBankFields.isHidden = false
4052
- self.viewBtnShowSavedCards.isHidden = false
4053
- self.lblBtnShowSaveCard.text = "Use Saved Accounts"
4054
- self.viewBtnShowSavedCardHeight.constant = 50
4055
- self.viewBtnShowSavedCardTopCon.constant = 20
4056
- self.OTPView.isHidden = true
4057
- self.emailView.isHidden = true
4058
- self.viewCardFields.isHidden = true
4059
- self.viewSingleSavedCard.isHidden = true
4060
- self.viewChangeCard.isHidden = true
4061
- self.viewUpdateCard.isHidden = true
4062
- self.viewAddNewCard.isHidden = true
4063
- self.btnNext.isHidden = false
4064
- self.btnNextHeight.constant = 50
4065
- self.btnNextTopCon.constant = 20
4066
- self.viewTermsAndConditions.isHidden = false
4067
- self.viewCrypto.isHidden = true
4068
- self.viewSingleSavedAccount.isHidden = true
4069
- }
4070
- else {
4071
- getShowBankAccountsApi()
4072
- }
4073
- }
4074
- case "Crypto":
4075
- if selectedPaymentMethod == "Crypto" {
4076
- self.cryptoChargeApi()
4077
- self.startCryptoTimer()
4078
- self.totalTimeInSeconds = 300
4079
-
4080
- self.viewCrypto.isHidden = false
4081
- self.viewQrCodeHeight.constant = 440
4082
- self.viewCryptoHeight.constant = 440
4163
+ if UserStoreSingleton.shared.isLoggedIn == false {
4164
+ self.viewSingleSavedAccount.isHidden = true
4165
+ self.viewBankFields.isHidden = true
4166
+ self.viewBtnShowSavedCards.isHidden = false
4167
+ self.lblBtnShowSaveCard.text = "Show Saved Cards"
4168
+ self.viewBtnShowSavedCardHeight.constant = 50
4169
+ self.viewBtnShowSavedCardTopCon.constant = 20
4170
+ self.btnShowSavedCard.isHidden = false
4171
+ self.OTPView.isHidden = true
4172
+ self.emailView.isHidden = true
4173
+ self.viewCardFields.isHidden = false
4174
+ self.viewSingleSavedCard.isHidden = true
4175
+ self.viewChangeCard.isHidden = true
4176
+ self.viewUpdateCard.isHidden = true
4177
+ self.viewAddNewCard.isHidden = true
4178
+ self.btnNext.isHidden = false
4179
+ self.btnNextHeight.constant = 50
4180
+ self.btnNextTopCon.constant = 20
4181
+ self.viewTermsAndConditions.isHidden = true
4182
+ self.viewCrypto.isHidden = true
4183
+ self.grailPayBankLinkView.isHidden = true
4184
+ } else {
4185
+ getShowCardsApi()
4083
4186
  self.viewSingleSavedAccount.isHidden = true
4084
4187
  self.viewBankFields.isHidden = true
4085
4188
  self.viewBtnShowSavedCards.isHidden = true
4086
4189
  self.viewBtnShowSavedCardHeight.constant = 0
4087
4190
  self.viewBtnShowSavedCardTopCon.constant = 0
4191
+ self.btnShowSavedCard.isHidden = true
4088
4192
  self.OTPView.isHidden = true
4089
4193
  self.emailView.isHidden = true
4090
4194
  self.viewCardFields.isHidden = true
4091
- self.viewSingleSavedCard.isHidden = true
4195
+ self.viewSingleSavedCard.isHidden = false
4092
4196
  self.viewChangeCard.isHidden = true
4093
4197
  self.viewUpdateCard.isHidden = true
4094
4198
  self.viewAddNewCard.isHidden = true
4095
4199
  self.btnNext.isHidden = true
4096
4200
  self.btnNextHeight.constant = 0
4097
4201
  self.btnNextTopCon.constant = 8
4098
- self.viewTermsAndConditions.isHidden = true
4099
4202
  self.viewChangedAccount.isHidden = true
4100
4203
  self.viewNewBankAccount.isHidden = true
4204
+ self.viewCrypto.isHidden = true
4205
+ self.grailPayBankLinkView.isHidden = true
4206
+ }
4207
+
4208
+ case "Bank":
4209
+ if UserStoreSingleton.shared.isLoggedIn == false {
4210
+ // self.viewBankFields.isHidden = false
4211
+ self.viewBtnShowSavedCards.isHidden = false
4212
+ self.lblBtnShowSaveCard.text = "Show Saved Accounts"
4213
+ self.viewBtnShowSavedCardHeight.constant = 50
4214
+ self.viewBtnShowSavedCardTopCon.constant = 20
4215
+ self.OTPView.isHidden = true
4216
+ self.emailView.isHidden = true
4217
+ self.viewCardFields.isHidden = true
4218
+ self.viewSingleSavedCard.isHidden = true
4219
+ self.viewChangeCard.isHidden = true
4220
+ self.viewUpdateCard.isHidden = true
4221
+ self.viewAddNewCard.isHidden = true
4222
+ self.btnNext.isHidden = false
4223
+ self.btnNextHeight.constant = 50
4224
+ self.btnNextTopCon.constant = 20
4225
+ // self.viewTermsAndConditions.isHidden = false
4226
+ self.viewCrypto.isHidden = true
4227
+ self.viewSingleSavedAccount.isHidden = true
4101
4228
 
4229
+ if request?.authenticatedACH == false {
4230
+ self.viewBankFields.isHidden = false
4231
+ self.viewTermsAndConditions.isHidden = false
4232
+ self.grailPayBankLinkView.isHidden = true
4233
+ self.viewGrailPayAbandon.isHidden = true
4234
+ } else {
4235
+ self.viewBankFields.isHidden = true
4236
+ self.viewTermsAndConditions.isHidden = true
4237
+ self.grailPayBankLinkView.isHidden = false
4238
+ self.btnNext.isHidden = true
4239
+ self.btnNextHeight.constant = 0
4240
+ self.btnNextTopCon.constant = 0
4241
+ self.viewGrailPayAbandon.isHidden = true
4242
+
4243
+ if isBankAccountConnected {
4244
+ self.viewBtnShowSavedCards.isHidden = true
4245
+ self.btnShowSavedCard.isHidden = true
4246
+ self.viewBtnShowSavedCardHeight.constant = 0
4247
+ self.viewBtnShowSavedCardTopCon.constant = 0
4248
+ }
4249
+ }
4250
+
4251
+ } else {
4252
+ if request?.authenticatedACH == true {
4253
+ self.viewBankFields.isHidden = true
4254
+ self.viewTermsAndConditions.isHidden = true
4255
+ self.grailPayBankLinkView.isHidden = false
4256
+ self.btnNext.isHidden = true
4257
+ self.btnNextHeight.constant = 0
4258
+ self.btnNextTopCon.constant = 0
4259
+ self.viewGrailPayAbandon.isHidden = true
4260
+
4261
+ if isBankAccountConnected {
4262
+ self.viewBtnShowSavedCards.isHidden = true
4263
+ self.btnShowSavedCard.isHidden = true
4264
+ self.viewBtnShowSavedCardHeight.constant = 0
4265
+ self.viewBtnShowSavedCardTopCon.constant = 0
4266
+ }
4267
+ }
4268
+
4269
+ getShowBankAccountsApi()
4102
4270
  }
4271
+
4272
+ case "Crypto":
4273
+ self.cryptoChargeApi()
4274
+ self.startCryptoTimer()
4275
+ self.totalTimeInSeconds = 300
4276
+
4277
+ self.viewCrypto.isHidden = false
4278
+ self.viewQrCodeHeight.constant = 440
4279
+ self.viewCryptoHeight.constant = 440
4280
+ self.viewSingleSavedAccount.isHidden = true
4281
+ self.viewBankFields.isHidden = true
4282
+ self.viewBtnShowSavedCards.isHidden = true
4283
+ self.viewBtnShowSavedCardHeight.constant = 0
4284
+ self.viewBtnShowSavedCardTopCon.constant = 0
4285
+ self.OTPView.isHidden = true
4286
+ self.emailView.isHidden = true
4287
+ self.viewCardFields.isHidden = true
4288
+ self.viewSingleSavedCard.isHidden = true
4289
+ self.viewChangeCard.isHidden = true
4290
+ self.viewUpdateCard.isHidden = true
4291
+ self.viewAddNewCard.isHidden = true
4292
+ self.btnNext.isHidden = true
4293
+ self.btnNextHeight.constant = 0
4294
+ self.btnNextTopCon.constant = 8
4295
+ self.viewTermsAndConditions.isHidden = true
4296
+ self.viewChangedAccount.isHidden = true
4297
+ self.viewNewBankAccount.isHidden = true
4298
+
4299
+ self.grailPayBankLinkView.isHidden = true
4300
+
4103
4301
  default:
4104
4302
  break
4105
4303
  }
@@ -4118,6 +4316,7 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
4118
4316
  return savedCards.count + 1
4119
4317
  }
4120
4318
  else if tableView == tblViewSavedBankAccounts {
4319
+ print("Number of bank accounts: \(bankAccounts.count)")
4121
4320
  return bankAccounts.count + 1
4122
4321
  }
4123
4322
  else if tableView == tblViewAccountTypes {
@@ -4154,10 +4353,11 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
4154
4353
  cell.btnThreeDotBank.removeTarget(nil, action: nil, for: .allEvents)
4155
4354
  cell.btnRemoveBankAccount.removeTarget(nil, action: nil, for: .allEvents)
4156
4355
 
4157
- // Check if it's the "Add New Card" cell
4158
4356
  if indexPath.row == bankAccounts.count {
4357
+ print("Showing 'Add New Account' cell")
4159
4358
  configureAddNewAccountCell(cell)
4160
4359
  } else {
4360
+ print("Configuring saved account cell at row: \(indexPath.row)")
4161
4361
  configureSavedAccountCell(cell, at: indexPath)
4162
4362
  }
4163
4363
 
@@ -4299,13 +4499,10 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
4299
4499
 
4300
4500
  @objc func btnSelectCardTapped(_ sender: UIButton) {
4301
4501
  let index = sender.tag
4302
-
4303
4502
  // Store the index of the previously selected card
4304
4503
  let previousSelectedIndex = selectedCardIndex
4305
-
4306
4504
  // Toggle the selection state: if the same card is selected, deselect it; otherwise, select the new one
4307
4505
  selectedCardIndex = (selectedCardIndex == index) ? nil : index
4308
-
4309
4506
  // Update the single card view
4310
4507
  if let selectedIndex = selectedCardIndex {
4311
4508
  updateSingleCardView(for: selectedIndex)
@@ -4314,12 +4511,10 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
4314
4511
  } else {
4315
4512
  viewSingleSavedCard.isHidden = true
4316
4513
  }
4317
-
4318
4514
  // Reload the previously selected row to update its state
4319
4515
  if let previousIndex = previousSelectedIndex {
4320
4516
  tblViewSavedCardsList.reloadRows(at: [IndexPath(row: previousIndex, section: 0)], with: .automatic)
4321
4517
  }
4322
-
4323
4518
  // Reload the newly selected row to update its state
4324
4519
  tblViewSavedCardsList.reloadRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
4325
4520
  }
@@ -4747,7 +4942,8 @@ extension PaymentInfoVC: UITextFieldDelegate {
4747
4942
  } else {
4748
4943
  viewTextFieldCardNumber.layer.borderColor = UIColor._1757D9.withAlphaComponent(1).cgColor
4749
4944
  }
4750
- } else if textField == cardExpiryTextField.textField {
4945
+ }
4946
+ else if textField == cardExpiryTextField.textField {
4751
4947
  if let primaryBtnFontColor = UserStoreSingleton.shared.primary_btn_bg_col,
4752
4948
  let uiColor = UIColor(hex: primaryBtnFontColor) {
4753
4949
  viewTxtFieldExpiryDate.layer.borderColor = uiColor.cgColor
@@ -5095,26 +5291,26 @@ extension PaymentInfoVC: UITextFieldDelegate {
5095
5291
  // func blinkCardOverlayViewControllerDidFinishScanning(_ blinkCardOverlayViewController: MBCBlinkCardOverlayViewController, state: MBCRecognizerResultState) {
5096
5292
  // /** This is done on background thread */
5097
5293
  // blinkCardOverlayViewController.recognizerRunnerViewController?.pauseScanning()
5098
- //
5294
+ //
5099
5295
  // var message: String = ""
5100
5296
  // var title: String = ""
5101
- //
5297
+ //
5102
5298
  // if blinkCardRecognizer.result.resultState == .valid {
5103
5299
  // title = "Payment card"
5104
- //
5300
+ //
5105
5301
  // let fullDocumentImage: UIImage! = blinkCardRecognizer.result.firstSideFullDocumentImage?.image
5106
5302
  // print("Got payment card image with width: \(fullDocumentImage.size.width), height: \(fullDocumentImage.size.height)")
5107
- //
5303
+ //
5108
5304
  // // Save the string representation of the code
5109
5305
  // message = blinkCardRecognizer.result.description
5110
5306
  // }
5111
- //
5307
+ //
5112
5308
  // /** Needs to be called on main thread beacuse everything prior is on background thread */
5113
5309
  // DispatchQueue.main.async {
5114
5310
  // // present the alert view with scanned results
5115
- //
5311
+ //
5116
5312
  // let alertController: UIAlertController = UIAlertController.init(title: title, message: message, preferredStyle: .alert)
5117
- //
5313
+ //
5118
5314
  // let okAction: UIAlertAction = UIAlertAction.init(title: "OK", style: .default,
5119
5315
  // handler: { (action) -> Void in
5120
5316
  // self.dismiss(animated: true, completion: nil)
@@ -5123,20 +5319,20 @@ extension PaymentInfoVC: UITextFieldDelegate {
5123
5319
  // blinkCardOverlayViewController.present(alertController, animated: true, completion: nil)
5124
5320
  // }
5125
5321
  // }
5126
- //
5322
+ //
5127
5323
  // func blinkCardOverlayViewControllerDidTapClose(_ blinkCardOverlayViewController: MBCBlinkCardOverlayViewController) {
5128
5324
  // self.dismiss(animated: true, completion: nil)
5129
5325
  // }
5130
- //
5326
+ //
5131
5327
  // func blinkCardOverlayViewControllerDidFinishEditing(_ blinkCardOverlayViewController: MBCBlinkCardOverlayViewController, editResult: MBCBlinkCardEditResult) {
5132
5328
  // blinkCardOverlayViewController.recognizerRunnerViewController?.pauseScanning()
5133
- //
5329
+ //
5134
5330
  // /** Needs to be called on main thread beacuse everything prior is on background thread */
5135
5331
  // DispatchQueue.main.async {
5136
5332
  // // present the alert view with scanned results
5137
- //
5333
+ //
5138
5334
  // let alertController: UIAlertController = UIAlertController.init(title: "BlinkCard Edit Results", message: editResult.description, preferredStyle: .alert)
5139
- //
5335
+ //
5140
5336
  // let okAction: UIAlertAction = UIAlertAction.init(title: "OK", style: .default,
5141
5337
  // handler: { (action) -> Void in
5142
5338
  // self.dismiss(animated: true, completion: nil)
@@ -5145,7 +5341,7 @@ extension PaymentInfoVC: UITextFieldDelegate {
5145
5341
  // blinkCardOverlayViewController.blinkCardEditViewController?.present(alertController, animated: true, completion: nil)
5146
5342
  // }
5147
5343
  // }
5148
- //
5344
+ //
5149
5345
  //}
5150
5346
 
5151
5347
  //extension PaymentInfoVC: CustomOverlayDelegate {
@@ -5156,5 +5352,5 @@ extension PaymentInfoVC: UITextFieldDelegate {
5156
5352
  // cardCvvTextField.text = cvv
5157
5353
  // cardNameTextField.text = nameOnCard
5158
5354
  // }
5159
- //
5355
+ //
5160
5356
  //}