@jimrising/easymerchantsdk-react-native 2.5.2 → 2.5.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.
@@ -17,7 +17,7 @@ enum GrailPaySource {
17
17
  case newAccount
18
18
  }
19
19
 
20
- let EasyPaySdk = UIStoryboard(name: "EasyPaySdk", bundle: Bundle.easyPayBundle)
20
+ let easymerchantsdk = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle)
21
21
 
22
22
  class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
23
23
 
@@ -693,6 +693,30 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
693
693
  setUpTermAndConditionsButton()
694
694
  setUpTermAndConditionsForSingleSavedAccountButton()
695
695
  setUpTermAndConditionsForGrailPaySavedAccountButton()
696
+ viewTermsAndConditions.isUserInteractionEnabled = true
697
+ viewTermsAndConditions.superview?.isUserInteractionEnabled = true
698
+ // Add spacing between terms row and submit button (code-only so layout stays within fixed height)
699
+ if let termsContainerStack = viewTermsAndConditions.superview as? UIStackView,
700
+ let mainBankStack = termsContainerStack.superview as? UIStackView {
701
+ mainBankStack.setCustomSpacing(24, after: termsContainerStack)
702
+ }
703
+ // Add spacing between bank form container and submit button (they are siblings)
704
+ if let parentStack = viewBankFields.superview as? UIStackView {
705
+ parentStack.setCustomSpacing(24, after: viewBankFields)
706
+ } else if let parent = viewBankFields.superview {
707
+ for c in parent.constraints {
708
+ if c.firstAttribute == .top && c.secondAttribute == .bottom,
709
+ (c.firstItem as? UIView) === grailPayBankLinkView, (c.secondItem as? UIView) === viewBankFields {
710
+ c.constant = 24
711
+ break
712
+ }
713
+ if c.firstAttribute == .bottom && c.secondAttribute == .top,
714
+ (c.firstItem as? UIView) === viewBankFields, (c.secondItem as? UIView) === grailPayBankLinkView {
715
+ c.constant = 24
716
+ break
717
+ }
718
+ }
719
+ }
696
720
  setUpOpenGrailPayAgainButton()
697
721
  lblGrailPaySavedAccountNumber.text = ""
698
722
  lblGrailPaySavedAccountType.text = ""
@@ -1009,10 +1033,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1009
1033
  self.txtFieldSelectDateNewGrailPayBankView.isHidden = false
1010
1034
 
1011
1035
  if request.saveAccount == false {
1012
- self.heightViewBankFields.constant = 790
1036
+ self.heightViewBankFields.constant = 790 + 24 // +24 spacing between terms row and submit button
1013
1037
  self.heightViewNewAccountFields.constant = 840
1014
1038
  } else {
1015
- self.heightViewBankFields.constant = 836
1039
+ self.heightViewBankFields.constant = 836 + 24 // +24 spacing between terms row and submit button
1016
1040
  self.heightViewNewAccountFields.constant = 890
1017
1041
  }
1018
1042
 
@@ -1104,10 +1128,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
1104
1128
  self.txtFieldSelectDateNewGrailPayBankView.isHidden = true
1105
1129
 
1106
1130
  if request.saveAccount == false {
1107
- self.heightViewBankFields.constant = 604
1131
+ self.heightViewBankFields.constant = 604 + 24 // +24 spacing between terms row and submit button
1108
1132
  self.heightViewNewAccountFields.constant = 644
1109
1133
  } else {
1110
- self.heightViewBankFields.constant = 644
1134
+ self.heightViewBankFields.constant = 644 + 24 // +24 spacing between terms row and submit button
1111
1135
  self.heightViewNewAccountFields.constant = 704
1112
1136
  }
1113
1137
 
@@ -2014,7 +2038,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2014
2038
  }
2015
2039
 
2016
2040
  @objc func aboutTerms(sender: UITapGestureRecognizer) {
2017
- guard let vc = UIStoryboard(name: "EasyPaySdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2041
+ guard let vc = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2042
+ print("Error: Could not find TermAndConditionsVC in easymerchantsdk storyboard.")
2018
2043
  return
2019
2044
  }
2020
2045
  vc.modalPresentationStyle = .overFullScreen
@@ -2041,7 +2066,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2041
2066
  }
2042
2067
 
2043
2068
  @objc func aboutTermsAndConditions(sender: UITapGestureRecognizer) {
2044
- guard let vc = UIStoryboard(name: "EasyPaySdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2069
+ guard let vc = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2045
2070
  return
2046
2071
  }
2047
2072
  vc.modalPresentationStyle = .overFullScreen
@@ -2052,6 +2077,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2052
2077
  func setUpOpenGrailPayAgainButton() {
2053
2078
  viewGrailPaySavedAccount.addSubview(btnOpenGrailPayAgain)
2054
2079
  let labelsStack = lblGrailPaySavedAccountNumber.superview!
2080
+ // Deactivate constraint pinning labels stack trailing to superview so we can add the button
2055
2081
  for c in viewGrailPaySavedAccount.constraints {
2056
2082
  let matches = (c.firstItem as? UIView) === labelsStack && c.firstAttribute == .trailing
2057
2083
  || (c.secondItem as? UIView) === labelsStack && c.secondAttribute == .trailing
@@ -2092,7 +2118,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2092
2118
  }
2093
2119
 
2094
2120
  @objc func aboutTermsGrailPayConditions(sender: UITapGestureRecognizer) {
2095
- guard let vc = UIStoryboard(name: "EasyPaySdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2121
+ guard let vc = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2096
2122
  return
2097
2123
  }
2098
2124
  vc.modalPresentationStyle = .overFullScreen
@@ -2119,7 +2145,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2119
2145
  }
2120
2146
 
2121
2147
  @objc func aboutTermsGrailPayNewAccountConditions(sender: UITapGestureRecognizer) {
2122
- guard let vc = UIStoryboard(name: "EasyPaySdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2148
+ guard let vc = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2123
2149
  return
2124
2150
  }
2125
2151
  vc.modalPresentationStyle = .overFullScreen
@@ -2146,7 +2172,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2146
2172
  }
2147
2173
 
2148
2174
  @objc func aboutTermsNewAccountViewConditions(sender: UITapGestureRecognizer) {
2149
- guard let vc = UIStoryboard(name: "EasyPaySdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2175
+ guard let vc = UIStoryboard(name: "easymerchantsdk", bundle: Bundle.easyPayBundle).instantiateViewController(withIdentifier: "TermAndConditionsVC") as? TermAndConditionsVC else {
2150
2176
  return
2151
2177
  }
2152
2178
  vc.modalPresentationStyle = .overFullScreen
@@ -2716,6 +2742,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2716
2742
  }
2717
2743
 
2718
2744
  @IBAction func actionBtnPayNowSingleSavedCard(_ sender: UIButton) {
2745
+ showDebugPayButtonAlert(message: "Pay for SAVED CARD — actionBtnPayNowSingleSavedCard")
2719
2746
  // Check if billingInfoData is not nil or empty
2720
2747
  if let billingInfoData = request.fields,
2721
2748
  let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
@@ -2758,7 +2785,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2758
2785
  }
2759
2786
  else if showBilling {
2760
2787
  // Push to BillingInfoVC
2761
- let billingInfoVC = UIStoryboard(name: "EasyPaySdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2788
+ let billingInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2762
2789
 
2763
2790
  billingInfoVC.isFrom = "SavedCards"
2764
2791
  billingInfoVC.selectedCard = self.selectedCard // Passing the selected card data
@@ -2784,7 +2811,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2784
2811
  }
2785
2812
  else {
2786
2813
  // Push to AdditionalInfoVC
2787
- let additionalInfoVC = UIStoryboard(name: "EasyPaySdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
2814
+ let additionalInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
2788
2815
  additionalInfoVC.isFrom = "SavedCards"
2789
2816
  additionalInfoVC.selectedCard = self.selectedCard // Passing the selected card data
2790
2817
  additionalInfoVC.cvvText = cvvText // Passing the CVV text
@@ -2882,7 +2909,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2882
2909
 
2883
2910
  @IBAction func actionBtnPayNowNewCardView(_ sender: UIButton) {
2884
2911
  NSLog("[PayButton] CARD FORM SUBMIT — actionBtnPayNowNewCardView")
2912
+ showDebugPayButtonAlert(message: "Pay for NEW CARD — actionBtnPayNowNewCardView")
2913
+ NSLog("%@", "[PayNowNewCard] isLoggedIn=\(UserStoreSingleton.shared.isLoggedIn), request.saveCard=\(request.saveCard), isSavedNewCardForFuture=\(isSavedNewCardForFuture)")
2885
2914
  if UserStoreSingleton.shared.isLoggedIn == true {
2915
+ NSLog("%@", "[PayNowNewCard] → taking LOGGED IN branch")
2886
2916
  //if billing info is availbale
2887
2917
  if let billingInfoData = request.fields,
2888
2918
  let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
@@ -2960,8 +2990,9 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2960
2990
 
2961
2991
  let showBilling = fieldSection.visibility.billing
2962
2992
  let showAdditional = fieldSection.visibility.additional
2963
-
2993
+ NSLog("%@", "[PayNowNewCard] LOGGED IN: showBilling=\(showBilling), showAdditional=\(showAdditional)")
2964
2994
  if !showBilling && !showAdditional {
2995
+ NSLog("%@", "[PayNowNewCard] LOGGED IN: no billing → calling payment API directly")
2965
2996
  if self.request.secureAuthentication == true {
2966
2997
  self.threeDSecurePaymentNewCardApi(customerId: UserStoreSingleton.shared.customerId ?? "")
2967
2998
  } else {
@@ -2970,7 +3001,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2970
3001
  }
2971
3002
  else if showBilling {
2972
3003
  // Push to BillingInfoVC
2973
- let billingInfoVC = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3004
+ let billingInfoVC = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
2974
3005
  // Pass the card details
2975
3006
  billingInfoVC.cardNumber = cardNumber
2976
3007
  billingInfoVC.expiryDate = expiryDate
@@ -2997,7 +3028,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
2997
3028
  }
2998
3029
  else {
2999
3030
  // Push to AdditionalInfoVC
3000
- let additionalInfoVC = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3031
+ let additionalInfoVC = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3001
3032
  // Pass the card details
3002
3033
  additionalInfoVC.cardNumber = cardNumber
3003
3034
  additionalInfoVC.expiryDate = expiryDate
@@ -3028,6 +3059,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3028
3059
  }
3029
3060
  }
3030
3061
  else {
3062
+ NSLog("%@", "[PayNowNewCard] LOGGED IN: no billingInfoData → calling payment API")
3031
3063
  if request.secureAuthentication == true {
3032
3064
  threeDSecurePaymentNewCardApi(customerId: UserStoreSingleton.shared.customerId ?? "")
3033
3065
  }
@@ -3036,6 +3068,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3036
3068
  }
3037
3069
  }
3038
3070
  } else {
3071
+ NSLog("%@", "[PayNowNewCard] → taking NOT LOGGED IN branch")
3039
3072
  // User not logged in — same check as ACH flow: if save card checked → verify (OTP) first
3040
3073
  if let billingInfoData = request.fields,
3041
3074
  let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
@@ -3097,10 +3130,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3097
3130
  }
3098
3131
  let showBilling = fieldSection.visibility.billing
3099
3132
  let showAdditional = fieldSection.visibility.additional
3133
+ NSLog("%@", "[PayNowNewCard] NOT LOGGED IN: showBilling=\(showBilling), showAdditional=\(showAdditional)")
3100
3134
  if !showBilling && !showAdditional {
3101
- let saveCardChecked = (self.request.saveCard == true) || self.isSavedNewCardForFuture
3135
+ // Use only the user's checkbox so we don't ask for OTP when they didn't tick "save card"
3136
+ let saveCardChecked = self.isSavedNewCardForFuture
3102
3137
  if saveCardChecked {
3103
- if let vc = EasyPaySdk.instantiateViewController(withIdentifier: "OTPVerificationVC") as? OTPVerificationVC {
3138
+ if let vc = easymerchantsdk.instantiateViewController(withIdentifier: "OTPVerificationVC") as? OTPVerificationVC {
3104
3139
  vc.easyPayDelegate = self.easyPayDelegate
3105
3140
  vc.cardNumber = cardNumber
3106
3141
  vc.expiryDate = expiryDate
@@ -3113,6 +3148,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3113
3148
  vc.userEmail = self.request.email
3114
3149
  vc.amount = self.request.amount ?? 0
3115
3150
  vc.isSavedForFuture = self.isSavedNewCardForFuture
3151
+ vc.isSavedNewCard = self.isSavedNewCardForFuture
3116
3152
  if let billingInfoData = self.request.fields {
3117
3153
  vc.billingInfoData = billingInfoData
3118
3154
  }
@@ -3121,8 +3157,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3121
3157
  vc.visibility = fieldSection.visibility
3122
3158
  vc.email = self.request.email
3123
3159
  self.navigationController?.pushViewController(vc, animated: true)
3160
+ } else {
3161
+ NSLog("%@", "[PayNowNewCard] NOT LOGGED IN: OTPVerificationVC instantiation failed")
3124
3162
  }
3125
3163
  } else {
3164
+ NSLog("%@", "[PayNowNewCard] NOT LOGGED IN: saveCard not checked → calling payment API (bypassing OTP)")
3126
3165
  if self.request.secureAuthentication == true {
3127
3166
  self.threeDSecurePaymentNewCardApi(customerId: UserStoreSingleton.shared.customerId ?? "")
3128
3167
  } else {
@@ -3130,7 +3169,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3130
3169
  }
3131
3170
  }
3132
3171
  } else if showBilling {
3133
- let billingInfoVC = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3172
+ NSLog("%@", "[PayNowNewCard] NOT LOGGED IN: pushing BillingInfoVC")
3173
+ let billingInfoVC = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3134
3174
  billingInfoVC.cardNumber = cardNumber
3135
3175
  billingInfoVC.expiryDate = expiryDate
3136
3176
  billingInfoVC.cvv = cvv
@@ -3150,7 +3190,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3150
3190
  billingInfoVC.visibility = fieldSection.visibility
3151
3191
  self.navigationController?.pushViewController(billingInfoVC, animated: true)
3152
3192
  } else {
3153
- let additionalInfoVC = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3193
+ NSLog("%@", "[PayNowNewCard] NOT LOGGED IN: pushing AdditionalInfoVC")
3194
+ let additionalInfoVC = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3154
3195
  additionalInfoVC.cardNumber = cardNumber
3155
3196
  additionalInfoVC.expiryDate = expiryDate
3156
3197
  additionalInfoVC.cvv = cvv
@@ -3172,8 +3213,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3172
3213
  }
3173
3214
  }
3174
3215
  } catch {
3216
+ NSLog("%@", "[PayNowNewCard] NOT LOGGED IN: decode fieldSection catch")
3175
3217
  }
3176
3218
  } else {
3219
+ NSLog("%@", "[PayNowNewCard] NOT LOGGED IN: no billingInfoData → calling payment API (bypassing OTP)")
3177
3220
  if request.secureAuthentication == true {
3178
3221
  threeDSecurePaymentNewCardApi(customerId: UserStoreSingleton.shared.customerId ?? "")
3179
3222
  } else {
@@ -3552,6 +3595,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3552
3595
  }
3553
3596
 
3554
3597
  @IBAction private func actionBtnLinkGrailPayBankAccount(_ sender: UIButton) {
3598
+ showDebugPayButtonAlert(message: "Pay for GRAILPAY (saved/linked bank) — actionBtnLinkGrailPayBankAccount")
3555
3599
  if isBankAccountConnected {
3556
3600
  let email = self.txtFieldEmailGrailPayView.text.trimmingCharacters(in: .whitespacesAndNewlines)
3557
3601
 
@@ -3605,8 +3649,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3605
3649
  }
3606
3650
  else {
3607
3651
  // Navigate directly to EmailVerificationVC
3608
- // let vc = EasyPaySdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
3609
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "OTPVerificationVC") as! OTPVerificationVC
3652
+ // let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
3653
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "OTPVerificationVC") as! OTPVerificationVC
3610
3654
  vc.easyPayDelegate = self.easyPayDelegate
3611
3655
  vc.grailPayAccountID = self.grailPayAccountID
3612
3656
  vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
@@ -3628,7 +3672,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3628
3672
  }
3629
3673
  else if showBilling {
3630
3674
  // Push to BillingInfoVC
3631
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3675
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3632
3676
  vc.billingInfoData = billingInfoData
3633
3677
  vc.request = self.request
3634
3678
  vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
@@ -3648,7 +3692,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3648
3692
  }
3649
3693
  else {
3650
3694
  // Push to AdditionalInfoVC
3651
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3695
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3652
3696
  vc.billingInfoData = billingInfoData
3653
3697
  vc.request = self.request
3654
3698
  vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
@@ -3678,8 +3722,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3678
3722
  grailPayAccountChargeWithSaveApi()
3679
3723
  } else {
3680
3724
  // Navigate to EmailVerificationVC directly
3681
- // let vc = EasyPaySdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
3682
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "OTPVerificationVC") as! OTPVerificationVC
3725
+ // let vc = easymerchantsdk.instantiateViewController(withIdentifier: "EmailVerificationVC") as! EmailVerificationVC
3726
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "OTPVerificationVC") as! OTPVerificationVC
3683
3727
  vc.easyPayDelegate = self.easyPayDelegate
3684
3728
  vc.grailPayAccountID = self.grailPayAccountID
3685
3729
  vc.selectedGrailPayAccountType = self.selectedGrailPayAccountType
@@ -3714,7 +3758,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3714
3758
  }
3715
3759
  else if showBilling {
3716
3760
  // Push to BillingInfoVC
3717
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3761
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3718
3762
  vc.billingInfoData = billingInfoData
3719
3763
  vc.request = self.request
3720
3764
  vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
@@ -3734,7 +3778,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3734
3778
  }
3735
3779
  else {
3736
3780
  // Push to AdditionalInfoVC
3737
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3781
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3738
3782
  vc.billingInfoData = billingInfoData
3739
3783
  vc.request = self.request
3740
3784
  vc.chosenPlan = self.txtFieldChosePlanGrailPayBankView.text
@@ -3771,6 +3815,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3771
3815
  }
3772
3816
 
3773
3817
  @IBAction func actionLinkNewGrailPayAccount(_ sender: UIButton) {
3818
+ showDebugPayButtonAlert(message: "Pay for NEW GRAILPAY ACCOUNT — actionLinkNewGrailPayAccount")
3774
3819
  if isNewBankAccountConnected {
3775
3820
  // Recurring Plan + Date Validation (only if is_recurring is true)
3776
3821
  if let req = self.request, req.is_recurring == true {
@@ -3813,7 +3858,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3813
3858
  }
3814
3859
  else if showBilling {
3815
3860
  // Push to BillingInfoVC
3816
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3861
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3817
3862
  vc.billingInfoData = billingInfoData
3818
3863
  vc.request = self.request
3819
3864
  vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
@@ -3832,7 +3877,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3832
3877
  }
3833
3878
  else {
3834
3879
  // Push to AdditionalInfoVC
3835
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3880
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3836
3881
  vc.billingInfoData = billingInfoData
3837
3882
  vc.request = self.request
3838
3883
  vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
@@ -3873,7 +3918,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3873
3918
  }
3874
3919
  else if showBilling {
3875
3920
  // Push to BillingInfoVC
3876
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3921
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3877
3922
  vc.billingInfoData = nil
3878
3923
  vc.request = self.request
3879
3924
  vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
@@ -3892,7 +3937,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3892
3937
  }
3893
3938
  else {
3894
3939
  // Push to AdditionalInfoVC
3895
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3940
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3896
3941
  vc.billingInfoData = nil
3897
3942
  vc.request = self.request
3898
3943
  vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
@@ -3933,7 +3978,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3933
3978
  }
3934
3979
  else if showBilling {
3935
3980
  // Push to BillingInfoVC
3936
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3981
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
3937
3982
  vc.billingInfoData = billingInfoData
3938
3983
  vc.request = self.request
3939
3984
  vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
@@ -3952,7 +3997,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
3952
3997
  }
3953
3998
  else {
3954
3999
  // Push to AdditionalInfoVC
3955
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
4000
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
3956
4001
  vc.billingInfoData = billingInfoData
3957
4002
  vc.request = self.request
3958
4003
  vc.chosenPlan = self.txtFieldChosePlanNewGrailPayBankView.text
@@ -5151,8 +5196,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5151
5196
 
5152
5197
  let showBilling = fieldSection.visibility.billing
5153
5198
  let showAdditional = fieldSection.visibility.additional
5154
- // Same as ACH: use request.saveCard OR checkbox (isSavedNewCardForFuture) card checkbox sets isSavedNewCardForFuture
5155
- let saveCardChecked = (self.request.saveCard == true) || self.isSavedNewCardForFuture
5199
+ // Use only the user's checkbox (isSavedNewCardForFuture); do not use request.saveCard so we don't ask for OTP when user didn't tick "save card"
5200
+ let saveCardChecked = self.isSavedNewCardForFuture
5156
5201
  if !showBilling && !showAdditional {
5157
5202
  if saveCardChecked {
5158
5203
  if UserStoreSingleton.shared.isLoggedIn == true {
@@ -5175,13 +5220,14 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5175
5220
  }
5176
5221
  else if showBilling {
5177
5222
  // Push to BillingInfoVC
5178
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
5223
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
5179
5224
  vc.cardNumber = cardNumber
5180
5225
  vc.expiryDate = expiryDate
5181
5226
  vc.cvv = cvv
5182
5227
  vc.nameOnCard = cardName
5183
5228
  vc.selectedPaymentMethod = self.selectedPaymentMethod
5184
5229
  vc.isSavedForFuture = self.isSavedNewAccount
5230
+ vc.isSavedNewCard = self.isSavedNewCardForFuture
5185
5231
  vc.request = self.request
5186
5232
  vc.chosenPlan = self.txtFieldChosePlanCard.text
5187
5233
  vc.startDate = self.txtFieldStartDateCard.text
@@ -5196,7 +5242,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5196
5242
  }
5197
5243
  else {
5198
5244
  // Push to AdditionalInfoVC
5199
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
5245
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
5200
5246
  vc.billingInfoData = billingInfoData
5201
5247
  vc.cardNumber = cardNumber
5202
5248
  vc.expiryDate = expiryDate
@@ -5204,6 +5250,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5204
5250
  vc.nameOnCard = cardName
5205
5251
  vc.selectedPaymentMethod = self.selectedPaymentMethod
5206
5252
  vc.isSavedForFuture = self.isSavedNewAccount
5253
+ vc.isSavedNewCard = self.isSavedNewCardForFuture
5207
5254
  vc.request = self.request
5208
5255
  vc.chosenPlan = self.txtFieldChosePlanCard.text
5209
5256
  vc.startDate = self.txtFieldStartDateCard.text
@@ -5294,7 +5341,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5294
5341
  }
5295
5342
  else if showBilling {
5296
5343
  // Push to BillingInfoVC
5297
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
5344
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
5298
5345
  vc.accountName = self.txtFieldAccountName.text
5299
5346
  vc.routingNumber = self.txtFieldRoutingNumber.text
5300
5347
  vc.accountType = self.txtFieldAccountType.text
@@ -5317,7 +5364,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5317
5364
  }
5318
5365
  else {
5319
5366
  // Push to AdditionalInfoVC
5320
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
5367
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
5321
5368
  vc.accountName = self.txtFieldAccountName.text
5322
5369
  vc.routingNumber = self.txtFieldRoutingNumber.text
5323
5370
  vc.accountType = self.txtFieldAccountType.text
@@ -5474,6 +5521,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5474
5521
  vc.nameOnCard = self.cardNameTextField.text
5475
5522
  vc.selectedPaymentMethod = self.selectedPaymentMethod
5476
5523
  vc.isSavedForFuture = isSavedForFuture
5524
+ vc.isSavedNewCard = isSavedForFuture
5477
5525
  vc.easyPayDelegate = self.easyPayDelegate // Pass delegate if needed
5478
5526
  vc.request = self.request
5479
5527
  vc.chosenPlan = self.txtFieldChosePlanCard.text
@@ -5584,6 +5632,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5584
5632
  emailVerificationVC.nameOnCard = self.cardNameTextField.text
5585
5633
  emailVerificationVC.selectedPaymentMethod = self.selectedPaymentMethod
5586
5634
  emailVerificationVC.isSavedForFuture = isSavedForFuture
5635
+ emailVerificationVC.isSavedNewCard = isSavedForFuture
5587
5636
  emailVerificationVC.easyPayDelegate = self.easyPayDelegate // Pass delegate if needed
5588
5637
  emailVerificationVC.request = self.request
5589
5638
  emailVerificationVC.chosenPlan = self.txtFieldChosePlanCard.text
@@ -5818,6 +5867,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5818
5867
  }
5819
5868
 
5820
5869
  @IBAction func actionBtnPayNowSingleAccountView(_ sender: UIButton) {
5870
+ showDebugPayButtonAlert(message: "Pay for SAVED BANK ACCOUNT — actionBtnPayNowSingleAccountView")
5821
5871
  guard let selectedIndex = selectedBankIndex else {
5822
5872
  showAlert(message: "Please select a bank account")
5823
5873
  return
@@ -5893,7 +5943,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5893
5943
  }
5894
5944
  else if showBilling {
5895
5945
  // Push to BillingInfoVC
5896
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
5946
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
5897
5947
  vc.customerID = self.selectedbankAccounts?.customer_id
5898
5948
  vc.accountID = self.selectedbankAccounts?.account_id
5899
5949
  vc.billingInfoData = billingInfoData
@@ -5911,7 +5961,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5911
5961
  }
5912
5962
  else {
5913
5963
  // Push to AdditionalInfoVC
5914
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
5964
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
5915
5965
  vc.customerID = self.selectedbankAccounts?.customer_id
5916
5966
  vc.accountID = self.selectedbankAccounts?.account_id
5917
5967
  vc.billingInfoData = billingInfoData
@@ -5979,6 +6029,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
5979
6029
  }
5980
6030
 
5981
6031
  @IBAction func actionBtnPayNowNewAccountView(_ sender: UIButton) {
6032
+ showDebugPayButtonAlert(message: "Pay for NEW BANK ACCOUNT (manual ACH) — actionBtnPayNowNewAccountView")
5982
6033
  // Check if billingInfoData is not nil or empty
5983
6034
  if let billingInfoData = request.fields {
5984
6035
  do {
@@ -6044,12 +6095,86 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6044
6095
  let showBilling = fieldSection.visibility.billing
6045
6096
  let showAdditional = fieldSection.visibility.additional
6046
6097
 
6047
- if !showBilling && !showAdditional {
6098
+ let customerId = UserStoreSingleton.shared.customerId ?? ""
6099
+ let needsOTP = isSavedNewAccount && customerId.isEmpty
6100
+
6101
+ if needsOTP {
6102
+ // Save account checked and no customer ID → always require OTP first (same as GrailPay / save card)
6103
+ if let vc = easymerchantsdk.instantiateViewController(withIdentifier: "OTPVerificationVC") as? OTPVerificationVC {
6104
+ vc.easyPayDelegate = self.easyPayDelegate
6105
+ vc.request = self.request
6106
+ vc.selectedPaymentMethod = "Bank"
6107
+ vc.isFrom = "AddNewAccountWithSave"
6108
+ vc.accountName = accountName
6109
+ vc.routingNumber = routingNumber
6110
+ vc.accountType = accountType
6111
+ vc.accountNumber = accountNumber
6112
+ vc.userEmail = self.request.email
6113
+ vc.email = self.request.email
6114
+ vc.isSavedNewAccount = true
6115
+ vc.amount = Double(self.request.amount ?? 0)
6116
+ vc.chosenPlan = self.txtFieldSelectPlanNewAccountView.text
6117
+ vc.startDate = self.txtFieldSelectDateNewAccountView.text
6118
+ if let billingInfoData = self.request.fields {
6119
+ vc.billingInfoData = billingInfoData
6120
+ }
6121
+ vc.billingInfo = fieldSection.billing
6122
+ vc.additionalInfo = fieldSection.additional
6123
+ vc.visibility = fieldSection.visibility
6124
+ vc.afterOTPFlow = showBilling ? "billing" : (showAdditional ? "additional" : "direct")
6125
+ self.navigationController?.pushViewController(vc, animated: true)
6126
+ } else {
6127
+ if !showBilling && !showAdditional {
6128
+ self.accountBankChargeAddNewAccountApi()
6129
+ } else if showBilling {
6130
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
6131
+ vc.accountName = accountName
6132
+ vc.routingNumber = routingNumber
6133
+ vc.accountType = accountType
6134
+ vc.accountNumber = accountNumber
6135
+ vc.billingInfoData = billingInfoData
6136
+ vc.selectedPaymentMethod = "Bank"
6137
+ vc.isFrom = isFromValue
6138
+ vc.isSavedNewAccount = isSavedNewAccount
6139
+ vc.delegate = self
6140
+ vc.chosenPlan = self.txtFieldSelectPlanNewAccountView.text
6141
+ vc.startDate = self.txtFieldSelectDateNewAccountView.text
6142
+ vc.request = self.request
6143
+ vc.selectedPaymentMethod = self.selectedPaymentMethod
6144
+ vc.amount = self.amount
6145
+ vc.billingInfo = fieldSection.billing
6146
+ vc.additionalInfo = fieldSection.additional
6147
+ vc.visibility = fieldSection.visibility
6148
+ vc.easyPayDelegate = self.easyPayDelegate
6149
+ self.navigationController?.pushViewController(vc, animated: true)
6150
+ } else {
6151
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
6152
+ vc.accountName = accountName
6153
+ vc.routingNumber = routingNumber
6154
+ vc.accountType = accountType
6155
+ vc.accountNumber = accountNumber
6156
+ vc.billingInfoData = billingInfoData
6157
+ vc.selectedPaymentMethod = "Bank"
6158
+ vc.isFrom = isFromValue
6159
+ vc.isSavedNewAccount = isSavedNewAccount
6160
+ vc.chosenPlan = self.txtFieldSelectPlanNewAccountView.text
6161
+ vc.startDate = self.txtFieldSelectDateNewAccountView.text
6162
+ vc.request = self.request
6163
+ vc.selectedPaymentMethod = self.selectedPaymentMethod
6164
+ vc.amount = self.amount
6165
+ vc.billingInfo = fieldSection.billing
6166
+ vc.additionalInfo = fieldSection.additional
6167
+ vc.visibility = fieldSection.visibility
6168
+ vc.easyPayDelegate = self.easyPayDelegate
6169
+ self.navigationController?.pushViewController(vc, animated: true)
6170
+ }
6171
+ }
6172
+ } else if !showBilling && !showAdditional {
6048
6173
  self.accountBankChargeAddNewAccountApi()
6049
6174
  }
6050
6175
  else if showBilling {
6051
6176
  // Push to BillingInfoVC
6052
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
6177
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
6053
6178
  vc.accountName = accountName
6054
6179
  vc.routingNumber = routingNumber
6055
6180
  vc.accountType = accountType
@@ -6072,7 +6197,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6072
6197
  }
6073
6198
  else {
6074
6199
  // Push to AdditionalInfoVC
6075
- let vc = EasyPaySdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
6200
+ let vc = easymerchantsdk.instantiateViewController(withIdentifier: "AdditionalInfoVC") as! AdditionalInfoVC
6076
6201
  vc.accountName = accountName
6077
6202
  vc.routingNumber = routingNumber
6078
6203
  vc.accountType = accountType
@@ -6081,7 +6206,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6081
6206
  vc.selectedPaymentMethod = "Bank"
6082
6207
  vc.isFrom = isFromValue
6083
6208
  vc.isSavedNewAccount = isSavedNewAccount
6084
- // vc.delegate = self
6085
6209
  vc.chosenPlan = self.txtFieldSelectPlanNewAccountView.text
6086
6210
  vc.startDate = self.txtFieldSelectDateNewAccountView.text
6087
6211
  vc.request = self.request
@@ -6149,7 +6273,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6149
6273
  }
6150
6274
  }
6151
6275
 
6152
- let isSavedNewAccount = isSavedNewAccount
6276
+ let isSavedNewAccount = self.isSavedNewAccount
6153
6277
  // If Billing info is nil or empty, call the appropriate API
6154
6278
  if isSavedNewAccount {
6155
6279
  // Call accountChargeApi if the account is saved
@@ -6281,6 +6405,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6281
6405
 
6282
6406
  }
6283
6407
 
6408
+ /// Debug: log which pay button was tapped (remove when done).
6409
+ private func showDebugPayButtonAlert(message: String) {
6410
+ NSLog("[PayButton] %@", message)
6411
+ }
6412
+
6284
6413
  private func showLicenseErrorAlert(message: String) {
6285
6414
  let alert = UIAlertController(title: "License Error", message: message, preferredStyle: .alert)
6286
6415
  alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
@@ -6305,6 +6434,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6305
6434
  // Use the clientToken from your singleton
6306
6435
  let token = UserStoreSingleton.shared.clientToken
6307
6436
  request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
6437
+ // Match Android: send_otp also needs X-Api-Key / X-Api-Secret for merchant auth
6438
+ if let apiKey = EnvironmentConfig.apiKey, let apiSecret = EnvironmentConfig.apiSecret {
6439
+ request.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
6440
+ request.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
6441
+ }
6308
6442
 
6309
6443
  // Define the parameters for the request
6310
6444
  let params: [String: Any] = [
@@ -6312,6 +6446,15 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6312
6446
  "card_search_key": "email"
6313
6447
  ]
6314
6448
 
6449
+ // Log send_otp request (headers masked for sharing)
6450
+ let clientTokenMask = (token ?? "").isEmpty ? "nil" : String((token ?? "").prefix(8)) + "..."
6451
+ let apiKeyMask = EnvironmentConfig.apiKey.map { $0.isEmpty ? "nil" : String($0.prefix(8)) + "..." } ?? "nil"
6452
+ let apiSecretMask = EnvironmentConfig.apiSecret.map { _ in "****" } ?? "nil"
6453
+ NSLog("[send_otp] ----- REQUEST -----")
6454
+ NSLog("[send_otp] URL: %@", fullURL)
6455
+ NSLog("[send_otp] Headers: Content-Type=application/json, Client-Token=%@, X-Api-Key=%@, X-Api-Secret=%@", clientTokenMask, apiKeyMask, apiSecretMask)
6456
+ NSLog("[send_otp] Body: %@", params.description)
6457
+
6315
6458
  do {
6316
6459
  // Convert the dictionary to JSON
6317
6460
  let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
@@ -6338,6 +6481,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6338
6481
  return
6339
6482
  }
6340
6483
 
6484
+ // Log send_otp response
6485
+ let statusCode = httpResponse.statusCode
6486
+ let bodyStr = serviceData.flatMap { String(data: $0, encoding: .utf8) } ?? ""
6487
+ NSLog("[send_otp] ----- RESPONSE -----")
6488
+ NSLog("[send_otp] Status: %d", statusCode)
6489
+ NSLog("[send_otp] Body: %@", bodyStr)
6490
+
6341
6491
  if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
6342
6492
  if let data = serviceData {
6343
6493
  do {
@@ -6377,9 +6527,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6377
6527
  urlRequest.httpMethod = "POST"
6378
6528
  urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
6379
6529
 
6380
- // Use customer_token instead of client_token for verify_otp API
6381
- let token = UserStoreSingleton.shared.customerToken
6382
- urlRequest.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
6530
+ // Verify_otp: match Android only Customer-Token + Content-Type (no X-Api-Key/X-Api-Secret)
6531
+ urlRequest.addValue(UserStoreSingleton.shared.customerToken ?? "", forHTTPHeaderField: "Customer-Token")
6383
6532
 
6384
6533
  // Safe access from main thread (assumes you call this from main thread)
6385
6534
  let email = self.txtFieldEmail.text ?? ""
@@ -6391,6 +6540,14 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6391
6540
  "otp": otp
6392
6541
  ]
6393
6542
 
6543
+ // Log verify_otp request (headers masked for sharing)
6544
+ let customerT = UserStoreSingleton.shared.customerToken ?? ""
6545
+ let customerTokenMask = customerT.isEmpty ? "nil" : String(customerT.prefix(8)) + "..."
6546
+ NSLog("[verify_otp] ----- REQUEST -----")
6547
+ NSLog("[verify_otp] URL: %@", fullURL)
6548
+ NSLog("[verify_otp] Headers: Customer-Token=%@, Content-Type=application/json", customerTokenMask)
6549
+ NSLog("[verify_otp] Body: card_search_value=%@, card_search_key=email, otp=%@", email, String(repeating: "*", count: otp.count))
6550
+
6394
6551
  do {
6395
6552
  let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
6396
6553
  urlRequest.httpBody = jsonData
@@ -6415,6 +6572,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6415
6572
  return
6416
6573
  }
6417
6574
 
6575
+ // Log verify_otp response
6576
+ let statusCode = httpResponse.statusCode
6577
+ let bodyStr = serviceData.flatMap { String(data: $0, encoding: .utf8) } ?? ""
6578
+ NSLog("[verify_otp] ----- RESPONSE -----")
6579
+ NSLog("[verify_otp] Status: %d", statusCode)
6580
+ NSLog("[verify_otp] Body: %@", bodyStr)
6581
+
6418
6582
  if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
6419
6583
  guard let data = serviceData else {
6420
6584
  return
@@ -6437,9 +6601,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6437
6601
  UserStoreSingleton.shared.verificationEmail = email
6438
6602
  UserStoreSingleton.shared.isLoggedIn = true
6439
6603
 
6440
-
6441
- // self.btnSettings.isHidden = false
6442
-
6443
6604
  if self.selectedPaymentMethod == "Card" {
6444
6605
  if customerId.isEmpty {
6445
6606
  self.OTPView.isHidden = true
@@ -6452,30 +6613,35 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6452
6613
  self.viewUpdateCard.isHidden = true
6453
6614
  self.viewAddNewCard.isHidden = true
6454
6615
  } else {
6455
- self.getShowCardsApi()
6456
6616
  self.OTPView.isHidden = true
6457
- self.viewCardFields.isHidden = true
6458
- self.viewSingleSavedCard.isHidden = false
6459
- self.viewBtnShowSavedCards.isHidden = true
6460
- self.viewBtnShowSavedCardHeight.constant = 0
6461
- self.viewBtnShowSavedCardTopCon.constant = 0
6462
- self.viewChangeCard.isHidden = true
6463
- self.viewUpdateCard.isHidden = true
6464
- self.viewAddNewCard.isHidden = true
6465
-
6466
- self.viewTxtFieldCVVSingleCard.isHidden = true
6467
- self.btnPayNowSingleCard.isHidden = true
6468
- self.btnNext.isHidden = true
6469
- self.btnNextHeight.constant = 0
6470
- self.btnNextTopCon.constant = 8
6617
+ // After verify_otp: run charge so card is saved (same pattern as OTPVerificationVC → paymentIntentWithSavedCardApi / paymentIntentApi)
6618
+ if self.isSavedNewCardForFuture {
6619
+ if self.request.secureAuthentication == true {
6620
+ self.threeDSecurePaymentNewCardApi(customerId: customerId)
6621
+ } else {
6622
+ self.paymentIntentAddNewCardApi(customerId: customerId)
6623
+ }
6624
+ } else {
6625
+ self.getShowCardsApi()
6626
+ self.viewCardFields.isHidden = true
6627
+ self.viewSingleSavedCard.isHidden = false
6628
+ self.viewBtnShowSavedCards.isHidden = true
6629
+ self.viewBtnShowSavedCardHeight.constant = 0
6630
+ self.viewBtnShowSavedCardTopCon.constant = 0
6631
+ self.viewChangeCard.isHidden = true
6632
+ self.viewUpdateCard.isHidden = true
6633
+ self.viewAddNewCard.isHidden = true
6634
+ self.viewTxtFieldCVVSingleCard.isHidden = true
6635
+ self.btnPayNowSingleCard.isHidden = true
6636
+ self.btnNext.isHidden = true
6637
+ self.btnNextHeight.constant = 0
6638
+ self.btnNextTopCon.constant = 8
6639
+ }
6471
6640
  }
6472
-
6473
6641
  self.txtFieldSelectPlanSingleSavedCard.isHidden = true
6474
6642
  self.txtFieldSelectDateSingleSavedCard.isHidden = true
6475
-
6476
6643
  self.txtFieldSelectPlanSingleSavedBankView.isHidden = true
6477
6644
  self.txtFieldSelectDateSingleSavedBankView.isHidden = true
6478
-
6479
6645
  } else if self.selectedPaymentMethod == "Bank" {
6480
6646
  self.getShowBankAccountsApi()
6481
6647
  }
@@ -6516,7 +6682,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6516
6682
  // uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
6517
6683
  //
6518
6684
  // let token = UserStoreSingleton.shared.customerToken
6519
- // // print("🔑 Setting customerToken header: \(token ?? "None")")
6520
6685
  // uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
6521
6686
  //
6522
6687
  // let session = URLSession.shared
@@ -6570,19 +6735,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6570
6735
  // )
6571
6736
  // }
6572
6737
  //
6573
- // self.savedCards.forEach { card in
6574
- // --------------------------
6575
- // Card Brand: \(card.cardBrandName)
6576
- // Card ID: \(card.cardId)
6577
- // Card Type: \(card.cardType)
6578
- // Last 4: ****\(card.ccLast4)
6579
- // Expiry: \(card.ccValidThru)
6580
- // Customer ID: \(card.customerId)
6581
- // Is Default: \(card.isDefault)
6582
- // --------------------------
6583
- // """)
6584
- // }
6585
- //
6586
6738
  // DispatchQueue.main.async {
6587
6739
  // if let firstCard = self.savedCards.first {
6588
6740
  // self.lblCardNumberSigleSavedCard.text = "****\(firstCard.ccLast4)"
@@ -6680,19 +6832,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
6680
6832
  )
6681
6833
  }
6682
6834
 
6683
- self.savedCards.forEach { card in
6684
- --------------------------
6685
- Card Brand: \(card.cardBrandName)
6686
- Card ID: \(card.cardId)
6687
- Card Type: \(card.cardType)
6688
- Last 4: ****\(card.ccLast4)
6689
- Expiry: \(card.ccValidThru)
6690
- Customer ID: \(card.customerId)
6691
- Is Default: \(card.isDefault)
6692
- --------------------------
6693
- """)
6694
- }
6695
-
6696
6835
  DispatchQueue.main.async {
6697
6836
  if self.savedCards.isEmpty {
6698
6837
  // self.showToast(message: "No saved card is available")
@@ -8145,7 +8284,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
8145
8284
  // request.addValue("application/json", forHTTPHeaderField: "Content-Type")
8146
8285
  //
8147
8286
  // let token = UserStoreSingleton.shared.customerToken
8148
- // // print("Setting customerToken header: \(token ?? "None")")
8149
8287
  // request.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
8150
8288
  //
8151
8289
  // // Get the text fields from the selected cell and trim whitespace
@@ -9819,6 +9957,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
9819
9957
  "account_number": accountNumber,
9820
9958
  "payment_mode": "auth_and_capture",
9821
9959
  "levelIndicator": 1,
9960
+ "save_account": isSavedNewAccount ? 1 : 0,
9961
+ "payment_method": "ach",
9822
9962
  ]
9823
9963
 
9824
9964
  if let billingInfoData = request.fields {
@@ -10270,7 +10410,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
10270
10410
  // request.addValue("application/json", forHTTPHeaderField: "Content-Type")
10271
10411
  //
10272
10412
  // let token = UserStoreSingleton.shared.customerToken
10273
- // // print("Setting customerToken header: \(token ?? "None")")
10274
10413
  // request.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
10275
10414
  //
10276
10415
  // let session = URLSession.shared
@@ -10830,7 +10969,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
10830
10969
  DispatchQueue.main.async {
10831
10970
  if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
10832
10971
 
10833
- let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
10972
+ // Use only redirect_url for 3DS WebView (match Android)
10973
+ let urlString = responseObject["redirect_url"] as? String ?? ""
10834
10974
  paymentDoneVC.redirectURL = urlString
10835
10975
  paymentDoneVC.chargeData = responseObject
10836
10976
  paymentDoneVC.easyPayDelegate = self.easyPayDelegate
@@ -11168,7 +11308,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
11168
11308
  DispatchQueue.main.async {
11169
11309
  if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
11170
11310
 
11171
- let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
11311
+ // Use only redirect_url for 3DS WebView (match Android)
11312
+ let urlString = responseObject["redirect_url"] as? String ?? ""
11172
11313
  paymentDoneVC.redirectURL = urlString
11173
11314
  paymentDoneVC.chargeData = responseObject
11174
11315
  paymentDoneVC.easyPayDelegate = self.easyPayDelegate
@@ -12053,35 +12194,32 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
12053
12194
  }
12054
12195
 
12055
12196
  @objc func addNewCardTapped(_ sender: UIButton) {
12056
- // Make the Add New Card view visible
12057
- viewAddNewCard.isHidden = false
12058
-
12059
- // Hide other conflicting views to prevent UI overlap
12197
+ // Go back to first card form (same as initial) instead of showing the separate "new card" UI
12198
+ viewAddNewCard.isHidden = true
12060
12199
  viewSingleSavedCard.isHidden = true
12061
12200
  viewChangeCard.isHidden = true
12062
12201
  viewUpdateCard.isHidden = true
12063
- viewCardFields.isHidden = true
12064
-
12065
- // Adjust visibility of any other related views if necessary
12066
- viewBtnShowSavedCards.isHidden = true
12067
- viewBtnShowSavedCardHeight.constant = 0
12068
- viewBtnShowSavedCardTopCon.constant = 0
12069
- btnNext.isHidden = true
12070
- btnNextHeight.constant = 0
12071
- btnNextTopCon.constant = 8
12202
+ viewCardFields.isHidden = false
12203
+ viewBtnShowSavedCards.isHidden = false
12204
+ viewBtnShowSavedCardHeight.constant = 50
12205
+ viewBtnShowSavedCardTopCon.constant = 20
12206
+ btnNext.isHidden = false
12207
+ btnNextHeight.constant = 50
12208
+ btnNextTopCon.constant = 20
12072
12209
  }
12073
12210
 
12074
12211
  //Bank Cell
12075
12212
  @objc func addNewBankAccountTapped(_ sender: UIButton) {
12213
+ // Go back to first bank form (same as initial) instead of showing the separate "new account" UI
12076
12214
  if request.authenticatedACH == true {
12077
- self.viewAddNewGrailPayAccount.isHidden = false
12078
-
12215
+ self.viewAddNewGrailPayAccount.isHidden = true
12079
12216
  self.viewNewBankAccount.isHidden = true
12080
12217
  self.viewSingleSavedAccount.isHidden = true
12081
12218
  self.viewBankFields.isHidden = true
12082
- self.viewBtnShowSavedCards.isHidden = true
12083
- self.viewBtnShowSavedCardHeight.constant = 0
12084
- self.viewBtnShowSavedCardTopCon.constant = 0
12219
+ self.grailPayBankLinkView.isHidden = false
12220
+ self.viewBtnShowSavedCards.isHidden = false
12221
+ self.viewBtnShowSavedCardHeight.constant = 50
12222
+ self.viewBtnShowSavedCardTopCon.constant = 20
12085
12223
  self.OTPView.isHidden = true
12086
12224
  self.emailView.isHidden = true
12087
12225
  self.viewCardFields.isHidden = true
@@ -12096,13 +12234,12 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
12096
12234
  }
12097
12235
  else {
12098
12236
  self.viewAddNewGrailPayAccount.isHidden = true
12099
-
12100
- self.viewNewBankAccount.isHidden = false
12237
+ self.viewNewBankAccount.isHidden = true
12101
12238
  self.viewSingleSavedAccount.isHidden = true
12102
- self.viewBankFields.isHidden = true
12103
- self.viewBtnShowSavedCards.isHidden = true
12104
- self.viewBtnShowSavedCardHeight.constant = 0
12105
- self.viewBtnShowSavedCardTopCon.constant = 0
12239
+ self.viewBankFields.isHidden = false
12240
+ self.viewBtnShowSavedCards.isHidden = false
12241
+ self.viewBtnShowSavedCardHeight.constant = 50
12242
+ self.viewBtnShowSavedCardTopCon.constant = 20
12106
12243
  self.OTPView.isHidden = true
12107
12244
  self.emailView.isHidden = true
12108
12245
  self.viewCardFields.isHidden = true
@@ -12110,12 +12247,11 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
12110
12247
  self.viewChangeCard.isHidden = true
12111
12248
  self.viewUpdateCard.isHidden = true
12112
12249
  self.viewAddNewCard.isHidden = true
12113
- self.btnNext.isHidden = true
12114
- self.btnNextHeight.constant = 0
12115
- self.btnNextTopCon.constant = 8
12250
+ self.btnNext.isHidden = false
12251
+ self.btnNextHeight.constant = 50
12252
+ self.btnNextTopCon.constant = 20
12116
12253
  self.viewChangedAccount.isHidden = true
12117
12254
  }
12118
-
12119
12255
  }
12120
12256
 
12121
12257
  @objc func btnSelectAccountTapped(_ sender: UIButton) {
@@ -12871,5 +13007,4 @@ extension PaymentInfoVC: UITextFieldDelegate {
12871
13007
  }
12872
13008
  }
12873
13009
 
12874
- }
12875
-
13010
+ }