@jimrising/easymerchantsdk-react-native 1.3.4 → 1.3.6
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.
- package/README.md +11 -4
- package/android/.gradle/8.10/checksums/checksums.lock +0 -0
- package/android/.gradle/8.10/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.10/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.10/fileHashes/fileHashes.bin +0 -0
- package/android/.gradle/8.10/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.10/gc.properties +0 -0
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/nb-cache/trust/0B5D6BE682AD6AEE9815EC13516BF075752CAE5AD5BECDCC00315C37622C2FD3 +1 -0
- package/android/.gradle/nb-cache/trust/23843E1876B2E51C07E80AB52D1E797E5D8053D8097EEEB15FB63DD903195C14 +1 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/.settings/org.eclipse.buildship.core.prefs +2 -0
- package/ios/Classes/EasyMerchantSdk.m +43 -28
- package/ios/Classes/EasyMerchantSdk.swift +68 -9
- package/ios/CustomComponents/PlanSelector.swift +28 -30
- package/ios/EnvironmentConfig.swift +30 -30
- package/ios/Example/ViewController.swift +47 -51
- package/ios/Models/AdditionalInfo.swift +43 -6
- package/ios/Models/BillingInfo.swift +50 -6
- package/ios/Models/RecurringIntervals.swift +34 -0
- package/ios/Models/RecurringStartDateType.swift +13 -0
- package/ios/Models/Request.swift +120 -11
- package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +609 -79
- package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +72 -25
- package/ios/Pods/ViewControllers/EmailVerificationVC.swift +14 -0
- package/ios/Pods/ViewControllers/OTPVerificationVC.swift +459 -42
- package/ios/Pods/ViewControllers/PaymentDoneVC.swift +16 -2
- package/ios/Pods/ViewControllers/PaymentErrorVC.swift +0 -2
- package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +1553 -372
- package/ios/Pods/ViewControllers/PaymentInformation/SavedAccountsTVC/SavedAccountTVC.swift +6 -2
- package/ios/Pods/ViewControllers/PaymentInformation/SavedCardsTVC/SavedCardsTVC.swift +6 -1
- package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +105 -0
- package/ios/easymerchantsdk.podspec +1 -1
- package/ios/easymerchantsdk.storyboard +554 -57
- package/package.json +2 -2
- package/.idea/caches/deviceStreaming.xml +0 -571
- package/.idea/em-MobileCheckoutSDK-ReactNative.iml +0 -9
- package/.idea/misc.xml +0 -5
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -6
|
@@ -196,6 +196,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
196
196
|
@IBOutlet weak var imgGrailPayAbandonError: UIImageView!
|
|
197
197
|
@IBOutlet weak var lblGrailPayAabandonError: UILabel!
|
|
198
198
|
|
|
199
|
+
@IBOutlet weak var txtFieldChosePlanGrailPayBankView: TextFieldStackView!
|
|
200
|
+
@IBOutlet weak var txtFieldSelectDateGrailPayBankView: TextFieldStackView!
|
|
201
|
+
@IBOutlet weak var bankIconGrailPayBankView: UIImageView!
|
|
202
|
+
|
|
199
203
|
//New GrailPay Account
|
|
200
204
|
@IBOutlet weak var viewAddNewGrailPayAccount: UIView!
|
|
201
205
|
@IBOutlet weak var lblGrailPayNewAccount: UILabel!
|
|
@@ -216,6 +220,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
216
220
|
@IBOutlet weak var lblTermsAndConditionNewGrailPayAccountView: UILabel!
|
|
217
221
|
@IBOutlet weak var btnChangeNewGrailPayAccountView: UIButton!
|
|
218
222
|
|
|
223
|
+
@IBOutlet weak var txtFieldChosePlanNewGrailPayBankView: TextFieldStackView!
|
|
224
|
+
@IBOutlet weak var txtFieldSelectDateNewGrailPayBankView: TextFieldStackView!
|
|
225
|
+
@IBOutlet weak var imgViewBankIconGrailPayNewBank: UIImageView!
|
|
226
|
+
|
|
219
227
|
//SavedBank
|
|
220
228
|
@IBOutlet weak var viewSingleSavedAccount: UIView!
|
|
221
229
|
@IBOutlet weak var viewTermAndConditionsSingleAccountView: UIStackView!
|
|
@@ -345,7 +353,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
345
353
|
var isFrom = String()
|
|
346
354
|
|
|
347
355
|
//Blink Card
|
|
348
|
-
//
|
|
356
|
+
// var blinkCardRecognizer: MBCBlinkCardRecognizer!
|
|
349
357
|
|
|
350
358
|
// Variables for Card Scanning Data Back
|
|
351
359
|
var cardNumber: String?
|
|
@@ -378,8 +386,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
378
386
|
var selectedNewGrailPayAccountName: String?
|
|
379
387
|
var grailPayNewAccountCustomerID: String?
|
|
380
388
|
|
|
381
|
-
// let planOptions = ["Weekly", "Monthly"]
|
|
382
|
-
|
|
383
389
|
var startDatePickerHandler: DatePickerHandler?
|
|
384
390
|
|
|
385
391
|
//MARK: - View Did Load
|
|
@@ -518,33 +524,50 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
518
524
|
func viewAppearanceOn() {
|
|
519
525
|
viewTermAndConditionsSingleAccountView.isHidden = true
|
|
520
526
|
btnPayNowSingleAccountView.isHidden = true
|
|
521
|
-
// viewSingleAccountViewHeight.constant = 68
|
|
522
|
-
|
|
523
527
|
viewNewBankAccount.isHidden = true
|
|
524
|
-
|
|
525
528
|
viewTxtFieldCVVSingleCard.isHidden = true
|
|
526
529
|
btnPayNowSingleCard.isHidden = true
|
|
527
|
-
|
|
528
530
|
self.viewCrypto.isHidden = true
|
|
529
531
|
|
|
530
|
-
if request.
|
|
532
|
+
if request.is_recurring == true {
|
|
531
533
|
self.txtFieldChosePlanCard.isHidden = false
|
|
532
|
-
|
|
534
|
+
if request.recurringStartDateType == .custom {
|
|
535
|
+
self.txtFieldStartDateCard.isHidden = false
|
|
536
|
+
self.txtFieldSelectDateSingleSavedCard.isHidden = false
|
|
537
|
+
self.txtFieldSelectDateNewCardView.isHidden = false
|
|
538
|
+
self.txtFieldSelectDateViewBankFields.isHidden = false
|
|
539
|
+
self.txtFieldSelectDateSingleSavedBankView.isHidden = false
|
|
540
|
+
self.txtFieldSelectDateNewAccountView.isHidden = false
|
|
541
|
+
self.txtFieldSelectDateGrailPayBankView.isHidden = false
|
|
542
|
+
self.txtFieldSelectDateNewGrailPayBankView.isHidden = false
|
|
543
|
+
|
|
544
|
+
self.heightViewBankFields.constant = 696
|
|
545
|
+
self.heightViewNewCardFields.constant = 624
|
|
546
|
+
self.heightViewNewAccountFields.constant = 808
|
|
547
|
+
self.heightSubViewNewAccountFields.constant = 738
|
|
548
|
+
} else {
|
|
549
|
+
self.txtFieldStartDateCard.isHidden = true
|
|
550
|
+
self.txtFieldSelectDateSingleSavedCard.isHidden = true
|
|
551
|
+
self.txtFieldSelectDateNewCardView.isHidden = true
|
|
552
|
+
self.txtFieldSelectDateViewBankFields.isHidden = true
|
|
553
|
+
self.txtFieldSelectDateSingleSavedBankView.isHidden = true
|
|
554
|
+
self.txtFieldSelectDateNewAccountView.isHidden = true
|
|
555
|
+
self.txtFieldSelectDateGrailPayBankView.isHidden = true
|
|
556
|
+
self.txtFieldSelectDateNewGrailPayBankView.isHidden = true
|
|
557
|
+
|
|
558
|
+
self.heightViewBankFields.constant = 600
|
|
559
|
+
self.heightViewNewCardFields.constant = 529
|
|
560
|
+
self.heightViewNewAccountFields.constant = 713
|
|
561
|
+
self.heightSubViewNewAccountFields.constant = 643
|
|
562
|
+
}
|
|
563
|
+
|
|
533
564
|
self.txtFieldSelectPlanSingleSavedCard.isHidden = false
|
|
534
|
-
self.txtFieldSelectDateSingleSavedCard.isHidden = false
|
|
535
565
|
self.txtFieldSelectPlanNewCardView.isHidden = false
|
|
536
|
-
self.txtFieldSelectDateNewCardView.isHidden = false
|
|
537
566
|
self.txtFieldSelectPlanViewBankFields.isHidden = false
|
|
538
|
-
self.txtFieldSelectDateViewBankFields.isHidden = false
|
|
539
567
|
self.txtFieldSelectPlanSingleSavedBankView.isHidden = false
|
|
540
|
-
self.txtFieldSelectDateSingleSavedBankView.isHidden = false
|
|
541
568
|
self.txtFieldSelectPlanNewAccountView.isHidden = false
|
|
542
|
-
self.
|
|
543
|
-
|
|
544
|
-
self.heightViewBankFields.constant = 696
|
|
545
|
-
self.heightViewNewCardFields.constant = 624
|
|
546
|
-
self.heightViewNewAccountFields.constant = 808
|
|
547
|
-
self.heightSubViewNewAccountFields.constant = 738
|
|
569
|
+
self.txtFieldChosePlanGrailPayBankView.isHidden = false
|
|
570
|
+
self.txtFieldChosePlanNewGrailPayBankView.isHidden = false
|
|
548
571
|
}
|
|
549
572
|
else {
|
|
550
573
|
self.txtFieldChosePlanCard.isHidden = true
|
|
@@ -559,6 +582,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
559
582
|
self.txtFieldSelectDateSingleSavedBankView.isHidden = true
|
|
560
583
|
self.txtFieldSelectPlanNewAccountView.isHidden = true
|
|
561
584
|
self.txtFieldSelectDateNewAccountView.isHidden = true
|
|
585
|
+
self.txtFieldChosePlanGrailPayBankView.isHidden = true
|
|
586
|
+
self.txtFieldChosePlanNewGrailPayBankView.isHidden = true
|
|
587
|
+
self.txtFieldSelectDateGrailPayBankView.isHidden = true
|
|
588
|
+
self.txtFieldSelectDateNewGrailPayBankView.isHidden = true
|
|
562
589
|
|
|
563
590
|
self.heightViewBankFields.constant = 506
|
|
564
591
|
self.heightViewNewCardFields.constant = 434
|
|
@@ -612,9 +639,14 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
612
639
|
btnNext.isHidden = true
|
|
613
640
|
btnNextHeight.constant = 0
|
|
614
641
|
btnNextTopCon.constant = 0
|
|
615
|
-
if request.
|
|
642
|
+
if request.is_recurring == true {
|
|
616
643
|
txtFieldSelectPlanSingleSavedCard.isHidden = false
|
|
617
|
-
|
|
644
|
+
if request.recurringStartDateType == .custom {
|
|
645
|
+
txtFieldSelectDateSingleSavedCard.isHidden = false
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
txtFieldSelectDateSingleSavedCard.isHidden = true
|
|
649
|
+
}
|
|
618
650
|
}
|
|
619
651
|
else {
|
|
620
652
|
txtFieldSelectPlanSingleSavedCard.isHidden = true
|
|
@@ -687,9 +719,14 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
687
719
|
if self.isSelectForPayBank {
|
|
688
720
|
self.viewTermAndConditionsSingleAccountView.isHidden = false
|
|
689
721
|
self.btnPayNowSingleAccountView.isHidden = false
|
|
690
|
-
if request.
|
|
722
|
+
if request.is_recurring == true {
|
|
691
723
|
self.txtFieldSelectPlanSingleSavedBankView.isHidden = false
|
|
692
|
-
|
|
724
|
+
if request.recurringStartDateType == .custom {
|
|
725
|
+
self.txtFieldSelectDateSingleSavedBankView.isHidden = false
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
self.txtFieldSelectDateSingleSavedBankView.isHidden = true
|
|
729
|
+
}
|
|
693
730
|
}
|
|
694
731
|
else {
|
|
695
732
|
self.txtFieldSelectPlanSingleSavedBankView.isHidden = true
|
|
@@ -734,8 +771,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
734
771
|
}
|
|
735
772
|
}
|
|
736
773
|
else {
|
|
737
|
-
//
|
|
738
|
-
//
|
|
774
|
+
// self.viewBankFields.isHidden = false
|
|
775
|
+
// self.viewTermsAndConditions.isHidden = false
|
|
739
776
|
self.grailPayBankLinkView.isHidden = true
|
|
740
777
|
}
|
|
741
778
|
}
|
|
@@ -799,8 +836,8 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
799
836
|
tblViewAccountTypes.backgroundColor = uiColor
|
|
800
837
|
viewAccountTypeNewAccountView.backgroundColor = uiColor
|
|
801
838
|
tblViewAccountTypeNewAccountView.backgroundColor = uiColor
|
|
802
|
-
//
|
|
803
|
-
//
|
|
839
|
+
// viewTblViewRecurring.backgroundColor = uiColor
|
|
840
|
+
// tblViewRecurring.backgroundColor = uiColor
|
|
804
841
|
}
|
|
805
842
|
|
|
806
843
|
if let bodyBackGroundColor = UserStoreSingleton.shared.body_bg_col,
|
|
@@ -970,7 +1007,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
970
1007
|
if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
|
|
971
1008
|
let uiColor = UIColor(hex: primaryFontColor) {
|
|
972
1009
|
lblChosePaymentMethod.textColor = uiColor
|
|
973
|
-
lblGrailPayNewAccount.
|
|
1010
|
+
lblGrailPayNewAccount.textColor = uiColor
|
|
974
1011
|
subLblGrailPayNewAccount.textColor = uiColor
|
|
975
1012
|
lblSavedInfoEmailView.textColor = uiColor
|
|
976
1013
|
subLblSavedEmailInfo.textColor = uiColor
|
|
@@ -989,6 +1026,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
989
1026
|
lblCreateNewCard.textColor = uiColor
|
|
990
1027
|
lblAccountNumberSingleAccountView.textColor = uiColor
|
|
991
1028
|
bankIconImgView.tintColor = uiColor
|
|
1029
|
+
imgViewSigleSavedCard.tintColor = uiColor
|
|
1030
|
+
imgViewSigleSavedCard.tintColor = uiColor
|
|
1031
|
+
bankIconGrailPayBankView.tintColor = uiColor
|
|
1032
|
+
imgViewBankIconGrailPayNewBank.tintColor = uiColor
|
|
1033
|
+
imgViewCardUpdateCardView.tintColor = uiColor
|
|
992
1034
|
lblChangeAccount.textColor = uiColor
|
|
993
1035
|
subLblChangeAccount.textColor = uiColor
|
|
994
1036
|
lblNewAccount.textColor = uiColor
|
|
@@ -1505,7 +1547,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1505
1547
|
// self.viewTblViewRecurring.isHidden.toggle()
|
|
1506
1548
|
// }
|
|
1507
1549
|
|
|
1508
|
-
|
|
1550
|
+
guard let plans = request?.recurringIntervals, !plans.isEmpty else {
|
|
1551
|
+
print("No recurring intervals available.")
|
|
1552
|
+
return
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
PlanSelector.presentPlanOptions(from: self, sourceView: sender, allowedPlans: plans) { selectedPlan in
|
|
1509
1556
|
self.txtFieldChosePlanCard.text = selectedPlan
|
|
1510
1557
|
}
|
|
1511
1558
|
}
|
|
@@ -1519,7 +1566,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1519
1566
|
}
|
|
1520
1567
|
|
|
1521
1568
|
@IBAction func actionBtnSelectPlanSingleSavedCard(_ sender: UIButton) {
|
|
1522
|
-
|
|
1569
|
+
guard let plans = request?.recurringIntervals, !plans.isEmpty else {
|
|
1570
|
+
print("No recurring intervals available.")
|
|
1571
|
+
return
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
PlanSelector.presentPlanOptions(from: self, sourceView: sender, allowedPlans: plans) { selectedPlan in
|
|
1523
1575
|
self.txtFieldSelectPlanSingleSavedCard.text = selectedPlan
|
|
1524
1576
|
}
|
|
1525
1577
|
}
|
|
@@ -1533,7 +1585,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1533
1585
|
}
|
|
1534
1586
|
|
|
1535
1587
|
@IBAction func actionBtnSelectPlanNewCardView(_ sender: UIButton) {
|
|
1536
|
-
|
|
1588
|
+
guard let plans = request?.recurringIntervals, !plans.isEmpty else {
|
|
1589
|
+
print("No recurring intervals available.")
|
|
1590
|
+
return
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
PlanSelector.presentPlanOptions(from: self, sourceView: sender, allowedPlans: plans) { selectedPlan in
|
|
1537
1594
|
self.txtFieldSelectPlanNewCardView.text = selectedPlan
|
|
1538
1595
|
}
|
|
1539
1596
|
}
|
|
@@ -1547,7 +1604,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1547
1604
|
}
|
|
1548
1605
|
|
|
1549
1606
|
@IBAction func actionBtnSelectPlanViewBankFields(_ sender: UIButton) {
|
|
1550
|
-
|
|
1607
|
+
guard let plans = request?.recurringIntervals, !plans.isEmpty else {
|
|
1608
|
+
print("No recurring intervals available.")
|
|
1609
|
+
return
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
PlanSelector.presentPlanOptions(from: self, sourceView: sender, allowedPlans: plans) { selectedPlan in
|
|
1551
1613
|
self.txtFieldSelectPlanViewBankFields.text = selectedPlan
|
|
1552
1614
|
}
|
|
1553
1615
|
}
|
|
@@ -1561,7 +1623,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1561
1623
|
}
|
|
1562
1624
|
|
|
1563
1625
|
@IBAction func actionBtnSelectPlanSingleSavedAccount(_ sender: UIButton) {
|
|
1564
|
-
|
|
1626
|
+
guard let plans = request?.recurringIntervals, !plans.isEmpty else {
|
|
1627
|
+
print("No recurring intervals available.")
|
|
1628
|
+
return
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
PlanSelector.presentPlanOptions(from: self, sourceView: sender, allowedPlans: plans) { selectedPlan in
|
|
1565
1632
|
self.txtFieldSelectPlanSingleSavedBankView.text = selectedPlan
|
|
1566
1633
|
}
|
|
1567
1634
|
}
|
|
@@ -1575,7 +1642,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1575
1642
|
}
|
|
1576
1643
|
|
|
1577
1644
|
@IBAction func actionBtnSelectPlanNewAccountView(_ sender: UIButton) {
|
|
1578
|
-
|
|
1645
|
+
guard let plans = request?.recurringIntervals, !plans.isEmpty else {
|
|
1646
|
+
print("No recurring intervals available.")
|
|
1647
|
+
return
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
PlanSelector.presentPlanOptions(from: self, sourceView: sender, allowedPlans: plans) { selectedPlan in
|
|
1579
1651
|
self.txtFieldSelectPlanNewAccountView.text = selectedPlan
|
|
1580
1652
|
}
|
|
1581
1653
|
}
|
|
@@ -1591,6 +1663,11 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1591
1663
|
@IBAction func actionBtnClose(_ sender: UIButton) {
|
|
1592
1664
|
UserStoreSingleton.shared.clearUserData()
|
|
1593
1665
|
UserStoreSingleton.shared.isLoggedIn = false
|
|
1666
|
+
|
|
1667
|
+
// Notify delegate about cancellation with custom message
|
|
1668
|
+
let result = Result(type: .cancelled, chargeData: ["message": "You’ve exited the payment screen. No transaction was made."])
|
|
1669
|
+
self.delegate?.easyPayController(self.navigationController as! EasyPayViewController, didFinishWith: result)
|
|
1670
|
+
|
|
1594
1671
|
self.dismiss(animated: true)
|
|
1595
1672
|
}
|
|
1596
1673
|
|
|
@@ -1620,6 +1697,12 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1620
1697
|
}
|
|
1621
1698
|
|
|
1622
1699
|
@IBAction func actionBtnSelectSingleSavedCard(_ sender: UIButton) {
|
|
1700
|
+
// Use the currently selectedCardIndex instead of sender.tag
|
|
1701
|
+
guard let index = selectedCardIndex, index < savedCards.count else {
|
|
1702
|
+
showAlert(title: "No Card Selected", message: "Please select a card from the list first.")
|
|
1703
|
+
return
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1623
1706
|
// Toggle selection state
|
|
1624
1707
|
isSelectForPay.toggle()
|
|
1625
1708
|
|
|
@@ -1635,13 +1718,15 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1635
1718
|
btnNextTopCon.constant = isSelectForPay ? 0 : 8
|
|
1636
1719
|
|
|
1637
1720
|
// Handle recurring fields visibility
|
|
1638
|
-
let showRecurringFields = isSelectForPay && request.
|
|
1721
|
+
let showRecurringFields = isSelectForPay && request.is_recurring == true
|
|
1722
|
+
let isCustomRecurring = request.recurringStartDateType == .custom
|
|
1639
1723
|
txtFieldSelectPlanSingleSavedCard.isHidden = !showRecurringFields
|
|
1640
|
-
txtFieldSelectDateSingleSavedCard.isHidden = !showRecurringFields
|
|
1724
|
+
txtFieldSelectDateSingleSavedCard.isHidden = !(showRecurringFields && isCustomRecurring)
|
|
1641
1725
|
|
|
1642
|
-
// Set selected card
|
|
1643
|
-
if isSelectForPay,
|
|
1644
|
-
selectedCard =
|
|
1726
|
+
// Set selected card based on tapped index
|
|
1727
|
+
if isSelectForPay, index < savedCards.count {
|
|
1728
|
+
selectedCard = savedCards[index]
|
|
1729
|
+
selectedCardIndex = index // Optional: track selected index
|
|
1645
1730
|
}
|
|
1646
1731
|
}
|
|
1647
1732
|
|
|
@@ -1678,8 +1763,25 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1678
1763
|
|
|
1679
1764
|
if cvvText.isEmpty {
|
|
1680
1765
|
self.showAlert(title: "Missing CVV", message: "Please enter the CVV to proceed.")
|
|
1766
|
+
return
|
|
1681
1767
|
} else if cvvText.count < 3 {
|
|
1682
1768
|
self.showAlert(title: "Invalid CVV", message: "CVV must be at least 3 digits.")
|
|
1769
|
+
return
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
1773
|
+
if let req = self.request, req.is_recurring == true {
|
|
1774
|
+
if txtFieldSelectPlanSingleSavedCard.text.isEmpty {
|
|
1775
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
1776
|
+
return
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1780
|
+
if txtFieldSelectDateSingleSavedCard.text.isEmpty {
|
|
1781
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
1782
|
+
return
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1683
1785
|
}
|
|
1684
1786
|
|
|
1685
1787
|
// Instantiate BillingInfoVC and pass the selected card data and CVV text
|
|
@@ -1698,6 +1800,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1698
1800
|
billingInfoVC.billingInfoData = jsonDict
|
|
1699
1801
|
}
|
|
1700
1802
|
|
|
1803
|
+
billingInfoVC.request = self.request
|
|
1804
|
+
billingInfoVC.chosenPlan = txtFieldSelectPlanSingleSavedCard.text
|
|
1805
|
+
billingInfoVC.startDate = txtFieldSelectDateSingleSavedCard.text
|
|
1806
|
+
|
|
1701
1807
|
// Navigate to BillingInfoVC
|
|
1702
1808
|
self.navigationController?.pushViewController(billingInfoVC, animated: true)
|
|
1703
1809
|
}
|
|
@@ -1707,13 +1813,29 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1707
1813
|
|
|
1708
1814
|
if cvvText.isEmpty {
|
|
1709
1815
|
self.showAlert(title: "Missing CVV", message: "Please enter the CVV to proceed.")
|
|
1710
|
-
|
|
1711
|
-
else if cvvText.count < 3 {
|
|
1816
|
+
return
|
|
1817
|
+
} else if cvvText.count < 3 {
|
|
1712
1818
|
self.showAlert(title: "Invalid CVV", message: "CVV must be at least 3 digits.")
|
|
1819
|
+
return
|
|
1713
1820
|
}
|
|
1714
|
-
|
|
1715
|
-
|
|
1821
|
+
|
|
1822
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
1823
|
+
if let req = self.request, req.is_recurring == true {
|
|
1824
|
+
if txtFieldSelectPlanSingleSavedCard.text.isEmpty {
|
|
1825
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
1826
|
+
return
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1830
|
+
if txtFieldSelectDateSingleSavedCard.text.isEmpty {
|
|
1831
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
1832
|
+
return
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1716
1835
|
}
|
|
1836
|
+
|
|
1837
|
+
// All validations passed, call the API
|
|
1838
|
+
paymentIntentFromShowSavedCardApi()
|
|
1717
1839
|
}
|
|
1718
1840
|
}
|
|
1719
1841
|
|
|
@@ -1812,6 +1934,21 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1812
1934
|
return
|
|
1813
1935
|
}
|
|
1814
1936
|
|
|
1937
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
1938
|
+
if let req = self.request, req.is_recurring == true {
|
|
1939
|
+
if txtFieldSelectPlanNewCardView.text.isEmpty {
|
|
1940
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
1941
|
+
return
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
1945
|
+
if txtFieldSelectDateNewCardView.text.isEmpty {
|
|
1946
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
1947
|
+
return
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1815
1952
|
// Instantiate BillingInfoVC and pass the card details
|
|
1816
1953
|
let billingInfoVC = UIStoryboard(name: "easymerchantsdk", bundle: .easyPayBundle).instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
|
|
1817
1954
|
|
|
@@ -1833,11 +1970,20 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1833
1970
|
billingInfoVC.billingInfoData = jsonDict
|
|
1834
1971
|
}
|
|
1835
1972
|
|
|
1973
|
+
billingInfoVC.request = self.request
|
|
1974
|
+
billingInfoVC.chosenPlan = txtFieldSelectPlanNewCardView.text
|
|
1975
|
+
billingInfoVC.startDate = txtFieldSelectDateNewCardView.text
|
|
1976
|
+
|
|
1836
1977
|
// Navigate to BillingInfoVC
|
|
1837
1978
|
self.navigationController?.pushViewController(billingInfoVC, animated: true)
|
|
1838
1979
|
}
|
|
1839
1980
|
else {
|
|
1840
|
-
|
|
1981
|
+
if request.enable3DS == true {
|
|
1982
|
+
threeDSecurePaymentNewCardApi(customerId: UserStoreSingleton.shared.customerId ?? "")
|
|
1983
|
+
}
|
|
1984
|
+
else {
|
|
1985
|
+
paymentIntentFromAddNewCardApi(customerId: UserStoreSingleton.shared.customerId)
|
|
1986
|
+
}
|
|
1841
1987
|
}
|
|
1842
1988
|
}
|
|
1843
1989
|
}
|
|
@@ -1894,6 +2040,44 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1894
2040
|
self.viewSingleSavedAccount.isHidden = false
|
|
1895
2041
|
}
|
|
1896
2042
|
|
|
2043
|
+
@IBAction func actionBtnChosePlanGrailPayBankView(_ sender: UIButton) {
|
|
2044
|
+
guard let plans = request?.recurringIntervals, !plans.isEmpty else {
|
|
2045
|
+
print("No recurring intervals available.")
|
|
2046
|
+
return
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
PlanSelector.presentPlanOptions(from: self, sourceView: sender, allowedPlans: plans) { selectedPlan in
|
|
2050
|
+
self.txtFieldChosePlanGrailPayBankView.text = selectedPlan
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
@IBAction func actionBtnSelectDateGrailPayBankView(_ sender: UIButton) {
|
|
2055
|
+
startDatePickerHandler = DatePickerHandler(textField: txtFieldSelectDateGrailPayBankView.textField)
|
|
2056
|
+
startDatePickerHandler?.onDateSelected = { selectedDate in
|
|
2057
|
+
|
|
2058
|
+
}
|
|
2059
|
+
txtFieldSelectDateGrailPayBankView.textField.becomeFirstResponder()
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
@IBAction func actionBtnSelectPlanNewGrailPayBankView(_ sender: UIButton) {
|
|
2063
|
+
guard let plans = request?.recurringIntervals, !plans.isEmpty else {
|
|
2064
|
+
print("No recurring intervals available.")
|
|
2065
|
+
return
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
PlanSelector.presentPlanOptions(from: self, sourceView: sender, allowedPlans: plans) { selectedPlan in
|
|
2069
|
+
self.txtFieldChosePlanNewGrailPayBankView.text = selectedPlan
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
@IBAction func actionBtnSelectDateNewGrailPayBankView(_ sender: UIButton) {
|
|
2074
|
+
startDatePickerHandler = DatePickerHandler(textField: txtFieldSelectDateNewGrailPayBankView.textField)
|
|
2075
|
+
startDatePickerHandler?.onDateSelected = { selectedDate in
|
|
2076
|
+
|
|
2077
|
+
}
|
|
2078
|
+
txtFieldSelectDateNewGrailPayBankView.textField.becomeFirstResponder()
|
|
2079
|
+
}
|
|
2080
|
+
|
|
1897
2081
|
//MARK: - Account Connect Api
|
|
1898
2082
|
func accountConnectApi(account: [String: Any]) {
|
|
1899
2083
|
showLoadingIndicator()
|
|
@@ -1906,15 +2090,15 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1906
2090
|
return
|
|
1907
2091
|
}
|
|
1908
2092
|
|
|
1909
|
-
var
|
|
1910
|
-
|
|
1911
|
-
|
|
2093
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
2094
|
+
uRLRequest.httpMethod = "POST"
|
|
2095
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
1912
2096
|
|
|
1913
2097
|
// Add API headers
|
|
1914
2098
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
1915
2099
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
1916
|
-
|
|
1917
|
-
|
|
2100
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
2101
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
1918
2102
|
}
|
|
1919
2103
|
|
|
1920
2104
|
// Prepare parameters
|
|
@@ -1937,7 +2121,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1937
2121
|
|
|
1938
2122
|
do {
|
|
1939
2123
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
1940
|
-
|
|
2124
|
+
uRLRequest.httpBody = jsonData
|
|
1941
2125
|
|
|
1942
2126
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
1943
2127
|
print("🔍 JSON Payload:\n\(jsonString)")
|
|
@@ -1948,7 +2132,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1948
2132
|
return
|
|
1949
2133
|
}
|
|
1950
2134
|
|
|
1951
|
-
let task = URLSession.shared.dataTask(with:
|
|
2135
|
+
let task = URLSession.shared.dataTask(with: uRLRequest) { data, response, error in
|
|
1952
2136
|
DispatchQueue.main.async { self.hideLoadingIndicator() }
|
|
1953
2137
|
|
|
1954
2138
|
if let error = error {
|
|
@@ -1986,6 +2170,16 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
1986
2170
|
self.viewGrailPaySavedBank.isHidden = false
|
|
1987
2171
|
self.viewGrailPaySavedAccount.isHidden = false
|
|
1988
2172
|
|
|
2173
|
+
if self.request.is_recurring == true {
|
|
2174
|
+
self.txtFieldChosePlanGrailPayBankView.isHidden = false
|
|
2175
|
+
if self.request.recurringStartDateType == .custom {
|
|
2176
|
+
self.txtFieldSelectDateGrailPayBankView.isHidden = false
|
|
2177
|
+
}
|
|
2178
|
+
else {
|
|
2179
|
+
self.txtFieldSelectDateGrailPayBankView.isHidden = true
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
|
|
1989
2183
|
if let accountNumber = account["account_number"] as? String {
|
|
1990
2184
|
let last4 = String(accountNumber.suffix(4))
|
|
1991
2185
|
self.lblGrailPaySavedAccountNumber.text = "****\(last4)"
|
|
@@ -2131,6 +2325,16 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2131
2325
|
self.grailPayNewSavedAccountView.isHidden = false
|
|
2132
2326
|
self.viewGrailPayNewAccountDetail.isHidden = false
|
|
2133
2327
|
|
|
2328
|
+
if self.request.is_recurring == true {
|
|
2329
|
+
self.txtFieldChosePlanNewGrailPayBankView.isHidden = false
|
|
2330
|
+
if self.request.recurringStartDateType == .custom {
|
|
2331
|
+
self.txtFieldSelectDateNewGrailPayBankView.isHidden = false
|
|
2332
|
+
}
|
|
2333
|
+
else {
|
|
2334
|
+
self.txtFieldSelectDateNewGrailPayBankView.isHidden = true
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2134
2338
|
if let accountNumber = account["account_number"] as? String {
|
|
2135
2339
|
let last4 = String(accountNumber.suffix(4))
|
|
2136
2340
|
self.lblGrailPayNewAccountNumber.text = "****\(last4)"
|
|
@@ -2177,6 +2381,21 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2177
2381
|
|
|
2178
2382
|
@IBAction private func actionBtnLinkGrailPayBankAccount(_ sender: UIButton) {
|
|
2179
2383
|
if isBankAccountConnected {
|
|
2384
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
2385
|
+
if let req = self.request, req.is_recurring == true {
|
|
2386
|
+
if self.txtFieldChosePlanGrailPayBankView.text.isEmpty {
|
|
2387
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
2388
|
+
return
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2392
|
+
if self.txtFieldSelectDateGrailPayBankView.text.isEmpty {
|
|
2393
|
+
self.showAlert(title: "Missing Information", message: "Please select start date.")
|
|
2394
|
+
return
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2180
2399
|
guard agreeTermsAndCondtition else {
|
|
2181
2400
|
self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
|
|
2182
2401
|
return
|
|
@@ -2203,6 +2422,21 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2203
2422
|
|
|
2204
2423
|
@IBAction func actionLinkNewGrailPayAccount(_ sender: UIButton) {
|
|
2205
2424
|
if isNewBankAccountConnected {
|
|
2425
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
2426
|
+
if let req = self.request, req.is_recurring == true {
|
|
2427
|
+
if self.txtFieldChosePlanNewGrailPayBankView.text.isEmpty {
|
|
2428
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
2429
|
+
return
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2433
|
+
if self.txtFieldSelectDateNewGrailPayBankView.text.isEmpty {
|
|
2434
|
+
self.showAlert(title: "Missing Information", message: "Please select start date.")
|
|
2435
|
+
return
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2206
2440
|
guard agreeTermsAndCondtition else {
|
|
2207
2441
|
self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
|
|
2208
2442
|
return
|
|
@@ -2316,22 +2550,22 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2316
2550
|
return
|
|
2317
2551
|
}
|
|
2318
2552
|
|
|
2319
|
-
var
|
|
2320
|
-
|
|
2321
|
-
|
|
2553
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
2554
|
+
uRLRequest.httpMethod = "POST"
|
|
2555
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
2322
2556
|
|
|
2323
2557
|
let token = UserStoreSingleton.shared.clientToken
|
|
2324
2558
|
print("Setting clientToken header: \(token ?? "None")")
|
|
2325
|
-
|
|
2559
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
2326
2560
|
|
|
2327
2561
|
if let apiKey = EnvironmentConfig.apiKey {
|
|
2328
|
-
|
|
2562
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
|
|
2329
2563
|
}
|
|
2330
2564
|
if let apiSecret = EnvironmentConfig.apiSecret {
|
|
2331
|
-
|
|
2565
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
|
|
2332
2566
|
}
|
|
2333
2567
|
|
|
2334
|
-
|
|
2568
|
+
var params: [String: Any] = [
|
|
2335
2569
|
"account_id": self.grailPayAccountID ?? "",
|
|
2336
2570
|
"account_type": self.selectedGrailPayAccountType ?? "",
|
|
2337
2571
|
"name": self.selectedGrailPayAccountName ?? "",
|
|
@@ -2339,11 +2573,34 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2339
2573
|
"email": "newmerchantadminuser12@test.com"
|
|
2340
2574
|
]
|
|
2341
2575
|
|
|
2576
|
+
// Add these if recurring is enabled
|
|
2577
|
+
if let req = request, req.is_recurring == true {
|
|
2578
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2579
|
+
// Only send start_date if type is .custom and field is not empty
|
|
2580
|
+
if let startDateText = txtFieldSelectDateGrailPayBankView?.text, !startDateText.isEmpty {
|
|
2581
|
+
let inputFormatter = DateFormatter()
|
|
2582
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
2583
|
+
|
|
2584
|
+
let outputFormatter = DateFormatter()
|
|
2585
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
2586
|
+
|
|
2587
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
2588
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
2589
|
+
params["start_date"] = apiFormattedDate
|
|
2590
|
+
} else {
|
|
2591
|
+
print("Invalid date format in startDateText")
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
params["interval"] = txtFieldChosePlanGrailPayBankView.text.lowercased()
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2342
2599
|
print(params)
|
|
2343
2600
|
|
|
2344
2601
|
do {
|
|
2345
2602
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
2346
|
-
|
|
2603
|
+
uRLRequest.httpBody = jsonData
|
|
2347
2604
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
2348
2605
|
print("JSON Payload: \(jsonString)")
|
|
2349
2606
|
}
|
|
@@ -2354,7 +2611,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2354
2611
|
}
|
|
2355
2612
|
|
|
2356
2613
|
let session = URLSession.shared
|
|
2357
|
-
let task = session.dataTask(with:
|
|
2614
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
2358
2615
|
|
|
2359
2616
|
DispatchQueue.main.async {
|
|
2360
2617
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -2424,22 +2681,22 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2424
2681
|
return
|
|
2425
2682
|
}
|
|
2426
2683
|
|
|
2427
|
-
var
|
|
2428
|
-
|
|
2429
|
-
|
|
2684
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
2685
|
+
uRLRequest.httpMethod = "POST"
|
|
2686
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
2430
2687
|
|
|
2431
2688
|
let token = UserStoreSingleton.shared.clientToken
|
|
2432
2689
|
print("Setting clientToken header: \(token ?? "None")")
|
|
2433
|
-
|
|
2690
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
2434
2691
|
|
|
2435
2692
|
if let apiKey = EnvironmentConfig.apiKey {
|
|
2436
|
-
|
|
2693
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
|
|
2437
2694
|
}
|
|
2438
2695
|
if let apiSecret = EnvironmentConfig.apiSecret {
|
|
2439
|
-
|
|
2696
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
|
|
2440
2697
|
}
|
|
2441
2698
|
|
|
2442
|
-
|
|
2699
|
+
var params: [String: Any] = [
|
|
2443
2700
|
"account_id": self.newGrailPayAccountID ?? "",
|
|
2444
2701
|
"account_type": self.selectedNewGrailPayAccountType ?? "",
|
|
2445
2702
|
"name": self.selectedNewGrailPayAccountName ?? "",
|
|
@@ -2447,11 +2704,34 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2447
2704
|
"customer_id": self.grailPayNewAccountCustomerID ?? ""
|
|
2448
2705
|
]
|
|
2449
2706
|
|
|
2707
|
+
// Add these if recurring is enabled
|
|
2708
|
+
if let req = request, req.is_recurring == true {
|
|
2709
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2710
|
+
// Only send start_date if type is .custom and field is not empty
|
|
2711
|
+
if let startDateText = txtFieldSelectDateNewGrailPayBankView?.text, !startDateText.isEmpty {
|
|
2712
|
+
let inputFormatter = DateFormatter()
|
|
2713
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
2714
|
+
|
|
2715
|
+
let outputFormatter = DateFormatter()
|
|
2716
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
2717
|
+
|
|
2718
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
2719
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
2720
|
+
params["start_date"] = apiFormattedDate
|
|
2721
|
+
} else {
|
|
2722
|
+
print("Invalid date format in startDateText")
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
params["interval"] = txtFieldChosePlanNewGrailPayBankView.text.lowercased()
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2450
2730
|
print(params)
|
|
2451
2731
|
|
|
2452
2732
|
do {
|
|
2453
2733
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
2454
|
-
|
|
2734
|
+
uRLRequest.httpBody = jsonData
|
|
2455
2735
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
2456
2736
|
print("JSON Payload: \(jsonString)")
|
|
2457
2737
|
}
|
|
@@ -2462,7 +2742,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2462
2742
|
}
|
|
2463
2743
|
|
|
2464
2744
|
let session = URLSession.shared
|
|
2465
|
-
let task = session.dataTask(with:
|
|
2745
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
2466
2746
|
|
|
2467
2747
|
DispatchQueue.main.async {
|
|
2468
2748
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -2532,22 +2812,22 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2532
2812
|
return
|
|
2533
2813
|
}
|
|
2534
2814
|
|
|
2535
|
-
var
|
|
2536
|
-
|
|
2537
|
-
|
|
2815
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
2816
|
+
uRLRequest.httpMethod = "POST"
|
|
2817
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
2538
2818
|
|
|
2539
2819
|
let token = UserStoreSingleton.shared.clientToken
|
|
2540
2820
|
print("Setting clientToken header: \(token ?? "None")")
|
|
2541
|
-
|
|
2821
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
2542
2822
|
|
|
2543
2823
|
if let apiKey = EnvironmentConfig.apiKey {
|
|
2544
|
-
|
|
2824
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
|
|
2545
2825
|
}
|
|
2546
2826
|
if let apiSecret = EnvironmentConfig.apiSecret {
|
|
2547
|
-
|
|
2827
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
|
|
2548
2828
|
}
|
|
2549
2829
|
|
|
2550
|
-
|
|
2830
|
+
var params: [String: Any] = [
|
|
2551
2831
|
"account_id": self.newGrailPayAccountID ?? "",
|
|
2552
2832
|
"account_type": self.selectedNewGrailPayAccountType ?? "",
|
|
2553
2833
|
"name": self.selectedNewGrailPayAccountName ?? "",
|
|
@@ -2557,11 +2837,34 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2557
2837
|
"is_default": 1
|
|
2558
2838
|
]
|
|
2559
2839
|
|
|
2840
|
+
// Add these if recurring is enabled
|
|
2841
|
+
if let req = request, req.is_recurring == true {
|
|
2842
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2843
|
+
// Only send start_date if type is .custom and field is not empty
|
|
2844
|
+
if let startDateText = txtFieldSelectDateNewGrailPayBankView?.text, !startDateText.isEmpty {
|
|
2845
|
+
let inputFormatter = DateFormatter()
|
|
2846
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
2847
|
+
|
|
2848
|
+
let outputFormatter = DateFormatter()
|
|
2849
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
2850
|
+
|
|
2851
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
2852
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
2853
|
+
params["start_date"] = apiFormattedDate
|
|
2854
|
+
} else {
|
|
2855
|
+
print("Invalid date format in startDateText")
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
params["interval"] = txtFieldChosePlanNewGrailPayBankView.text.lowercased()
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2560
2863
|
print(params)
|
|
2561
2864
|
|
|
2562
2865
|
do {
|
|
2563
2866
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
2564
|
-
|
|
2867
|
+
uRLRequest.httpBody = jsonData
|
|
2565
2868
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
2566
2869
|
print("JSON Payload: \(jsonString)")
|
|
2567
2870
|
}
|
|
@@ -2572,7 +2875,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2572
2875
|
}
|
|
2573
2876
|
|
|
2574
2877
|
let session = URLSession.shared
|
|
2575
|
-
let task = session.dataTask(with:
|
|
2878
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
2576
2879
|
|
|
2577
2880
|
DispatchQueue.main.async {
|
|
2578
2881
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -2643,33 +2946,56 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2643
2946
|
return
|
|
2644
2947
|
}
|
|
2645
2948
|
|
|
2646
|
-
var
|
|
2647
|
-
|
|
2648
|
-
|
|
2949
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
2950
|
+
uRLRequest.httpMethod = "POST"
|
|
2951
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
2649
2952
|
|
|
2650
2953
|
let token = UserStoreSingleton.shared.clientToken
|
|
2651
2954
|
print("Setting clientToken header: \(token ?? "None")")
|
|
2652
|
-
|
|
2955
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
2653
2956
|
|
|
2654
2957
|
if let apiKey = EnvironmentConfig.apiKey {
|
|
2655
|
-
|
|
2958
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "X-Api-Key")
|
|
2656
2959
|
}
|
|
2657
2960
|
if let apiSecret = EnvironmentConfig.apiSecret {
|
|
2658
|
-
|
|
2961
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "X-Api-Secret")
|
|
2659
2962
|
}
|
|
2660
2963
|
|
|
2661
|
-
|
|
2964
|
+
var params: [String: Any] = [
|
|
2662
2965
|
"account_id": selectedbankAccounts?.account_id ?? "",
|
|
2663
2966
|
"customer": selectedbankAccounts?.customer_id ?? "",
|
|
2664
2967
|
"description": "payment checkout",
|
|
2665
2968
|
"currency": "usd",
|
|
2666
2969
|
]
|
|
2667
2970
|
|
|
2971
|
+
// Add these if recurring is enabled
|
|
2972
|
+
if let req = request, req.is_recurring == true {
|
|
2973
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
2974
|
+
// Only send start_date if type is .custom and field is not empty
|
|
2975
|
+
if let startDateText = txtFieldSelectDateSingleSavedBankView?.text, !startDateText.isEmpty {
|
|
2976
|
+
let inputFormatter = DateFormatter()
|
|
2977
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
2978
|
+
|
|
2979
|
+
let outputFormatter = DateFormatter()
|
|
2980
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
2981
|
+
|
|
2982
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
2983
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
2984
|
+
params["start_date"] = apiFormattedDate
|
|
2985
|
+
} else {
|
|
2986
|
+
print("Invalid date format in startDateText")
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
params["interval"] = txtFieldSelectPlanSingleSavedBankView.text.lowercased()
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2668
2994
|
print(params)
|
|
2669
2995
|
|
|
2670
2996
|
do {
|
|
2671
2997
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
2672
|
-
|
|
2998
|
+
uRLRequest.httpBody = jsonData
|
|
2673
2999
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
2674
3000
|
print("JSON Payload: \(jsonString)")
|
|
2675
3001
|
}
|
|
@@ -2680,7 +3006,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2680
3006
|
}
|
|
2681
3007
|
|
|
2682
3008
|
let session = URLSession.shared
|
|
2683
|
-
let task = session.dataTask(with:
|
|
3009
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
2684
3010
|
|
|
2685
3011
|
DispatchQueue.main.async {
|
|
2686
3012
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -2773,71 +3099,145 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2773
3099
|
// Validation checks
|
|
2774
3100
|
if cardNumber.isEmpty {
|
|
2775
3101
|
self.showAlert(title: "Missing Information", message: "Card Number is required.")
|
|
3102
|
+
return
|
|
2776
3103
|
} else if sanitizedCardNumber.count != 16 {
|
|
2777
3104
|
self.showAlert(title: "Invalid Card Number", message: "Card Number must be 16 digits.")
|
|
3105
|
+
return
|
|
2778
3106
|
} else if expiryDate.isEmpty {
|
|
2779
3107
|
self.showAlert(title: "Missing Information", message: "Card Expiry Date is required.")
|
|
2780
|
-
|
|
3108
|
+
return
|
|
3109
|
+
}
|
|
3110
|
+
else if !expiryDate.matches(expiryDateRegex) {
|
|
2781
3111
|
self.showAlert(title: "Invalid Expiry Date", message: "Expiry Date must be in format MM/yyyy (e.g., 02/2026).")
|
|
2782
|
-
|
|
3112
|
+
return
|
|
3113
|
+
}
|
|
3114
|
+
else {
|
|
3115
|
+
// Check if expiry date is past tomorrow
|
|
3116
|
+
let dateFormatter = DateFormatter()
|
|
3117
|
+
dateFormatter.dateFormat = "MM/yyyy"
|
|
3118
|
+
dateFormatter.timeZone = TimeZone.current
|
|
3119
|
+
if let expDate = dateFormatter.date(from: expiryDate) {
|
|
3120
|
+
let calendar = Calendar.current
|
|
3121
|
+
// Get start of the month following expiry
|
|
3122
|
+
let expiryMonthStart = calendar.date(from: calendar.dateComponents([.year, .month], from: expDate))!
|
|
3123
|
+
let endOfMonth = calendar.date(byAdding: DateComponents(month: 1, day: -1), to: expiryMonthStart)!
|
|
3124
|
+
|
|
3125
|
+
let tomorrow = calendar.date(byAdding: .day, value: 1, to: Date())!
|
|
3126
|
+
if endOfMonth < tomorrow {
|
|
3127
|
+
self.showAlert(title: "Expired Card", message: "Card is expired or please enter a valid expiry date.")
|
|
3128
|
+
return
|
|
3129
|
+
}
|
|
3130
|
+
} else {
|
|
3131
|
+
self.showAlert(title: "Invalid Expiry Date", message: "Unable to parse expiry date.")
|
|
3132
|
+
return
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
if cvv.isEmpty {
|
|
2783
3137
|
self.showAlert(title: "Missing Information", message: "Card CVV Number is required.")
|
|
3138
|
+
return
|
|
2784
3139
|
} else if cvv.count < 3 {
|
|
2785
3140
|
self.showAlert(title: "Invalid CVV", message: "CVV must be at least 3 digits.")
|
|
3141
|
+
return
|
|
2786
3142
|
} else if cardName.isEmpty {
|
|
2787
3143
|
self.showAlert(title: "Missing Information", message: "Card Name is required.")
|
|
3144
|
+
return
|
|
2788
3145
|
} else if !cardName.matches(nameRegex) {
|
|
2789
3146
|
self.showAlert(title: "Invalid Name", message: "Name must contain only letters and spaces.")
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
3147
|
+
return
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
3151
|
+
if let req = self.request, req.is_recurring == true {
|
|
3152
|
+
if self.txtFieldChosePlanCard.text.isEmpty {
|
|
3153
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3154
|
+
return
|
|
3155
|
+
}
|
|
3156
|
+
|
|
3157
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3158
|
+
if self.txtFieldStartDateCard.text.isEmpty {
|
|
3159
|
+
self.showAlert(title: "Missing Information", message: "Please select start date.")
|
|
3160
|
+
return
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
2801
3163
|
}
|
|
3164
|
+
|
|
3165
|
+
// Proceed to BillingInfoVC
|
|
3166
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
|
|
3167
|
+
vc.billingInfoData = jsonDict
|
|
3168
|
+
vc.cardNumber = cardNumber
|
|
3169
|
+
vc.expiryDate = expiryDate
|
|
3170
|
+
vc.cvv = cvv
|
|
3171
|
+
vc.nameOnCard = cardName
|
|
3172
|
+
vc.selectedPaymentMethod = self.selectedPaymentMethod
|
|
3173
|
+
vc.isSavedForFuture = self.isSavedForFuture
|
|
3174
|
+
vc.request = self.request
|
|
3175
|
+
vc.chosenPlan = self.txtFieldChosePlanCard.text
|
|
3176
|
+
vc.startDate = self.txtFieldStartDateCard.text
|
|
3177
|
+
self.navigationController?.pushViewController(vc, animated: true)
|
|
2802
3178
|
}
|
|
2803
3179
|
else if self.selectedPaymentMethod == "Bank" {
|
|
2804
3180
|
// Bank Case
|
|
2805
3181
|
if self.txtFieldAccountName.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2806
3182
|
self.showAlert(title: "Missing Information", message: "Bank account name is required.")
|
|
3183
|
+
return
|
|
2807
3184
|
} else if self.txtFieldRoutingNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2808
3185
|
self.showAlert(title: "Missing Information", message: "Routing number is required.")
|
|
3186
|
+
return
|
|
2809
3187
|
}
|
|
2810
3188
|
else if let routingNumber = self.txtFieldRoutingNumber.text?.replacingOccurrences(of: " ", with: ""), routingNumber.count != 9 || !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: routingNumber)) {
|
|
2811
3189
|
self.showAlert(title: "Invalid Routing Number", message: "Routing number is not correct. It must be exactly 9 digits.")
|
|
3190
|
+
return
|
|
2812
3191
|
}
|
|
2813
3192
|
else if self.txtFieldAccountType.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2814
3193
|
self.showAlert(title: "Missing Information", message: "Bank account type is required.")
|
|
3194
|
+
return
|
|
2815
3195
|
} else if self.txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2816
3196
|
self.showAlert(title: "Missing Information", message: "Bank account number is required.")
|
|
3197
|
+
return
|
|
2817
3198
|
}
|
|
2818
3199
|
else if self.txtFieldConfirmBankAccount.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2819
3200
|
self.showAlert(title: "Missing Information", message: "Please confirm your bank account number.")
|
|
3201
|
+
return
|
|
2820
3202
|
}
|
|
2821
3203
|
else if self.txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines) != self.txtFieldConfirmBankAccount.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
|
|
2822
3204
|
self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
|
|
3205
|
+
return
|
|
2823
3206
|
}
|
|
2824
3207
|
else if !self.agreeTermsAndCondtition {
|
|
2825
3208
|
self.showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
|
|
2826
3209
|
return
|
|
2827
3210
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
3211
|
+
|
|
3212
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
3213
|
+
if let req = self.request, req.is_recurring == true {
|
|
3214
|
+
if self.txtFieldSelectPlanViewBankFields.text.isEmpty {
|
|
3215
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3216
|
+
return
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3220
|
+
if self.txtFieldSelectDateViewBankFields.text.isEmpty {
|
|
3221
|
+
self.showAlert(title: "Missing Information", message: "Please select start date.")
|
|
3222
|
+
return
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
2840
3225
|
}
|
|
3226
|
+
|
|
3227
|
+
// Proceed to BillingInfoVC
|
|
3228
|
+
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
|
|
3229
|
+
vc.accountName = self.txtFieldAccountName.text
|
|
3230
|
+
vc.routingNumber = self.txtFieldRoutingNumber.text
|
|
3231
|
+
vc.accountType = self.txtFieldAccountType.text
|
|
3232
|
+
vc.accountNumber = self.txtFieldAccountNumber.text
|
|
3233
|
+
vc.billingInfoData = jsonDict
|
|
3234
|
+
vc.selectedPaymentMethod = self.selectedPaymentMethod
|
|
3235
|
+
vc.isSavedForFuture = self.isSavedForFuture
|
|
3236
|
+
vc.isFrom = "NormalBankPayWithoutSave"
|
|
3237
|
+
vc.chosenPlan = self.txtFieldSelectPlanViewBankFields.text
|
|
3238
|
+
vc.startDate = self.txtFieldSelectDateViewBankFields.text
|
|
3239
|
+
vc.request = self.request
|
|
3240
|
+
self.navigationController?.pushViewController(vc, animated: true)
|
|
2841
3241
|
}
|
|
2842
3242
|
}
|
|
2843
3243
|
}
|
|
@@ -2864,27 +3264,79 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2864
3264
|
// Validation checks
|
|
2865
3265
|
if cardNumber.isEmpty {
|
|
2866
3266
|
self.showAlert(title: "Missing Information", message: "Card Number is required.")
|
|
3267
|
+
return
|
|
2867
3268
|
} else if sanitizedCardNumber.count != 16 {
|
|
2868
3269
|
self.showAlert(title: "Invalid Card Number", message: "Card Number must be 16 digits.")
|
|
3270
|
+
return
|
|
2869
3271
|
} else if expiryDate.isEmpty {
|
|
2870
3272
|
self.showAlert(title: "Missing Information", message: "Card Expiry Date is required.")
|
|
2871
|
-
|
|
3273
|
+
return
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
else if !expiryDate.matches(expiryDateRegex) {
|
|
2872
3277
|
self.showAlert(title: "Invalid Expiry Date", message: "Expiry Date must be in format MM/yyyy (e.g., 02/2026).")
|
|
2873
|
-
|
|
2874
|
-
self.showAlert(title: "Missing Information", message: "Card CVV Number is required.")
|
|
2875
|
-
} else if cvv.count < 3 {
|
|
2876
|
-
self.showAlert(title: "Invalid CVV", message: "CVV must be at least 3 digits.")
|
|
2877
|
-
} else if cardName.isEmpty {
|
|
2878
|
-
self.showAlert(title: "Missing Information", message: "Card Name is required.")
|
|
2879
|
-
} else if !cardName.matches(nameRegex) {
|
|
2880
|
-
self.showAlert(title: "Invalid Name", message: "Name must contain only letters and spaces.")
|
|
3278
|
+
return
|
|
2881
3279
|
}
|
|
2882
3280
|
else {
|
|
2883
|
-
//
|
|
2884
|
-
|
|
2885
|
-
|
|
3281
|
+
// Check if expiry date is past tomorrow
|
|
3282
|
+
let dateFormatter = DateFormatter()
|
|
3283
|
+
dateFormatter.dateFormat = "MM/yyyy"
|
|
3284
|
+
dateFormatter.timeZone = TimeZone.current
|
|
3285
|
+
if let expDate = dateFormatter.date(from: expiryDate) {
|
|
3286
|
+
let calendar = Calendar.current
|
|
3287
|
+
// Get start of the month following expiry
|
|
3288
|
+
let expiryMonthStart = calendar.date(from: calendar.dateComponents([.year, .month], from: expDate))!
|
|
3289
|
+
let endOfMonth = calendar.date(byAdding: DateComponents(month: 1, day: -1), to: expiryMonthStart)!
|
|
3290
|
+
|
|
3291
|
+
let tomorrow = calendar.date(byAdding: .day, value: 1, to: Date())!
|
|
3292
|
+
if endOfMonth < tomorrow {
|
|
3293
|
+
self.showAlert(title: "Expired Card", message: "Card is expired or please enter a valid expiry date.")
|
|
3294
|
+
return
|
|
3295
|
+
}
|
|
2886
3296
|
} else {
|
|
2887
|
-
|
|
3297
|
+
self.showAlert(title: "Invalid Expiry Date", message: "Unable to parse expiry date.")
|
|
3298
|
+
return
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
if cvv.isEmpty {
|
|
3303
|
+
self.showAlert(title: "Missing Information", message: "Card CVV Number is required.")
|
|
3304
|
+
return
|
|
3305
|
+
} else if cvv.count < 3 {
|
|
3306
|
+
self.showAlert(title: "Invalid CVV", message: "CVV must be at least 3 digits.")
|
|
3307
|
+
return
|
|
3308
|
+
} else if cardName.isEmpty {
|
|
3309
|
+
self.showAlert(title: "Missing Information", message: "Card Name is required.")
|
|
3310
|
+
return
|
|
3311
|
+
} else if !cardName.matches(nameRegex) {
|
|
3312
|
+
self.showAlert(title: "Invalid Name", message: "Name must contain only letters and spaces.")
|
|
3313
|
+
return
|
|
3314
|
+
}
|
|
3315
|
+
|
|
3316
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
3317
|
+
if let req = self.request, req.is_recurring == true {
|
|
3318
|
+
if self.txtFieldChosePlanCard.text.isEmpty {
|
|
3319
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3320
|
+
return
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3324
|
+
if self.txtFieldStartDateCard.text.isEmpty {
|
|
3325
|
+
self.showAlert(title: "Missing Information", message: "Please select start date.")
|
|
3326
|
+
return
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
|
|
3331
|
+
// Navigate to EmailVerificationVC if isSavedForFuture is true, else call paymentIntentApi
|
|
3332
|
+
if isSavedForFuture {
|
|
3333
|
+
navigateCardDataToEmailVerificationVC()
|
|
3334
|
+
} else {
|
|
3335
|
+
if request.enable3DS == true {
|
|
3336
|
+
threeDSecurePaymentApi()
|
|
3337
|
+
}
|
|
3338
|
+
else {
|
|
3339
|
+
paymentIntentApi()
|
|
2888
3340
|
}
|
|
2889
3341
|
}
|
|
2890
3342
|
}
|
|
@@ -2892,36 +3344,57 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2892
3344
|
// Bank Case
|
|
2893
3345
|
if txtFieldAccountName.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2894
3346
|
showAlert(title: "Missing Information", message: "Bank account name is required.")
|
|
3347
|
+
return
|
|
2895
3348
|
}
|
|
2896
3349
|
else if txtFieldRoutingNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2897
3350
|
showAlert(title: "Missing Information", message: "Routing number is required.")
|
|
3351
|
+
return
|
|
2898
3352
|
}
|
|
2899
3353
|
else if let routingNumber = self.txtFieldRoutingNumber.text?.replacingOccurrences(of: " ", with: ""), routingNumber.count != 9 || !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: routingNumber)) {
|
|
2900
3354
|
self.showAlert(title: "Invalid Routing Number", message: "Routing number is not correct. It must be exactly 9 digits.")
|
|
3355
|
+
return
|
|
2901
3356
|
}
|
|
2902
3357
|
else if txtFieldAccountType.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2903
3358
|
showAlert(title: "Missing Information", message: "Bank account type is required.")
|
|
3359
|
+
return
|
|
2904
3360
|
}
|
|
2905
3361
|
else if txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2906
3362
|
showAlert(title: "Missing Information", message: "Bank account number is required.")
|
|
3363
|
+
return
|
|
2907
3364
|
}
|
|
2908
3365
|
else if self.txtFieldConfirmBankAccount.text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == true {
|
|
2909
3366
|
self.showAlert(title: "Missing Information", message: "Please confirm your bank account number.")
|
|
3367
|
+
return
|
|
2910
3368
|
}
|
|
2911
3369
|
else if self.txtFieldAccountNumber.text?.trimmingCharacters(in: .whitespacesAndNewlines) != self.txtFieldConfirmBankAccount.text?.trimmingCharacters(in: .whitespacesAndNewlines) {
|
|
2912
3370
|
self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
|
|
3371
|
+
return
|
|
2913
3372
|
}
|
|
2914
3373
|
else if !agreeTermsAndCondtition {
|
|
2915
3374
|
showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
|
|
2916
3375
|
return
|
|
2917
3376
|
}
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
3377
|
+
|
|
3378
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
3379
|
+
if let req = self.request, req.is_recurring == true {
|
|
3380
|
+
if self.txtFieldSelectPlanViewBankFields.text.isEmpty {
|
|
3381
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3382
|
+
return
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3386
|
+
if self.txtFieldSelectDateViewBankFields.text.isEmpty {
|
|
3387
|
+
self.showAlert(title: "Missing Information", message: "Please select start date.")
|
|
3388
|
+
return
|
|
3389
|
+
}
|
|
2923
3390
|
}
|
|
2924
3391
|
}
|
|
3392
|
+
|
|
3393
|
+
if isSavedForFuture {
|
|
3394
|
+
navigateBankDataToEmailVerificationVC()
|
|
3395
|
+
} else {
|
|
3396
|
+
accountChargeApi()
|
|
3397
|
+
}
|
|
2925
3398
|
}
|
|
2926
3399
|
}
|
|
2927
3400
|
}
|
|
@@ -2936,6 +3409,9 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2936
3409
|
emailVerificationVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
2937
3410
|
emailVerificationVC.isSavedForFuture = isSavedForFuture
|
|
2938
3411
|
emailVerificationVC.easyPayDelegate = self.easyPayDelegate // Pass delegate if needed
|
|
3412
|
+
emailVerificationVC.request = self.request
|
|
3413
|
+
emailVerificationVC.chosenPlan = self.txtFieldChosePlanCard.text
|
|
3414
|
+
emailVerificationVC.startDate = self.txtFieldStartDateCard.text
|
|
2939
3415
|
self.navigationController?.pushViewController(emailVerificationVC, animated: true)
|
|
2940
3416
|
}
|
|
2941
3417
|
}
|
|
@@ -2949,6 +3425,9 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
2949
3425
|
emailVerificationVC.selectedPaymentMethod = self.selectedPaymentMethod
|
|
2950
3426
|
emailVerificationVC.isSavedForFuture = isSavedForFuture
|
|
2951
3427
|
emailVerificationVC.easyPayDelegate = self.easyPayDelegate // Pass delegate if needed
|
|
3428
|
+
emailVerificationVC.chosenPlan = self.txtFieldSelectPlanViewBankFields.text
|
|
3429
|
+
emailVerificationVC.startDate = self.txtFieldSelectDateViewBankFields.text
|
|
3430
|
+
emailVerificationVC.request = self.request
|
|
2952
3431
|
self.navigationController?.pushViewController(emailVerificationVC, animated: true)
|
|
2953
3432
|
}
|
|
2954
3433
|
}
|
|
@@ -3010,6 +3489,23 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3010
3489
|
self.viewCrypto.isHidden = true
|
|
3011
3490
|
self.viewSingleSavedAccount.isHidden = true
|
|
3012
3491
|
self.viewChangedAccount.isHidden = true
|
|
3492
|
+
|
|
3493
|
+
if request?.authenticatedACH == true {
|
|
3494
|
+
self.viewBankFields.isHidden = true
|
|
3495
|
+
self.viewBtnShowSavedCards.isHidden = true
|
|
3496
|
+
self.btnNext.isHidden = true
|
|
3497
|
+
self.viewTermsAndConditions.isHidden = true
|
|
3498
|
+
self.btnNextHeight.constant = 0
|
|
3499
|
+
self.btnNextTopCon.constant = 0
|
|
3500
|
+
|
|
3501
|
+
self.viewBtnShowSavedCards.isHidden = false
|
|
3502
|
+
self.lblBtnShowSaveCard.text = "Show Saved Accounts"
|
|
3503
|
+
self.viewBtnShowSavedCardHeight.constant = 50
|
|
3504
|
+
self.viewBtnShowSavedCardTopCon.constant = 20
|
|
3505
|
+
grailPayBankLinkView.isHidden = false
|
|
3506
|
+
viewAddNewGrailPayAccount.isHidden = true
|
|
3507
|
+
}
|
|
3508
|
+
|
|
3013
3509
|
}
|
|
3014
3510
|
}
|
|
3015
3511
|
|
|
@@ -3052,9 +3548,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3052
3548
|
|
|
3053
3549
|
if isSelectForPayBank {
|
|
3054
3550
|
// Show or hide recurring plan fields based on request.enableRecurring
|
|
3055
|
-
if request?.
|
|
3551
|
+
if request?.is_recurring == true {
|
|
3056
3552
|
txtFieldSelectPlanSingleSavedBankView.isHidden = false
|
|
3057
|
-
|
|
3553
|
+
if request.recurringStartDateType == .custom {
|
|
3554
|
+
txtFieldSelectDateSingleSavedBankView.isHidden = false
|
|
3555
|
+
} else {
|
|
3556
|
+
txtFieldSelectDateSingleSavedBankView.isHidden = true
|
|
3557
|
+
}
|
|
3058
3558
|
} else {
|
|
3059
3559
|
txtFieldSelectPlanSingleSavedBankView.isHidden = true
|
|
3060
3560
|
txtFieldSelectDateSingleSavedBankView.isHidden = true
|
|
@@ -3083,7 +3583,31 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3083
3583
|
}
|
|
3084
3584
|
|
|
3085
3585
|
@IBAction func actionBtnPayNowSingleAccountView(_ sender: UIButton) {
|
|
3586
|
+
guard let selectedIndex = selectedBankIndex else {
|
|
3587
|
+
showAlert(message: "Please select a bank account")
|
|
3588
|
+
return
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
let selectedBank = bankAccounts[selectedIndex]
|
|
3592
|
+
// Log for debugging
|
|
3593
|
+
print("Pay Now tapped with account: ****\(selectedBank.account_number_last_4 ?? "")")
|
|
3594
|
+
|
|
3086
3595
|
if request.authenticatedACH == true {
|
|
3596
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
3597
|
+
if let req = self.request, req.is_recurring == true {
|
|
3598
|
+
if txtFieldSelectPlanSingleSavedBankView.text.isEmpty {
|
|
3599
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3600
|
+
return
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3604
|
+
if txtFieldSelectDateSingleSavedBankView.text.isEmpty {
|
|
3605
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
3606
|
+
return
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
}
|
|
3610
|
+
|
|
3087
3611
|
// Check if the terms and conditions are agreed
|
|
3088
3612
|
if !agreeTermsAndCondtition {
|
|
3089
3613
|
showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
|
|
@@ -3096,12 +3620,26 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3096
3620
|
let json = try? JSONSerialization.jsonObject(with: billingInfoData, options: []),
|
|
3097
3621
|
let jsonDict = json as? [String: Any], !jsonDict.isEmpty {
|
|
3098
3622
|
|
|
3623
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
3624
|
+
if let req = self.request, req.is_recurring == true {
|
|
3625
|
+
if txtFieldSelectPlanSingleSavedBankView.text.isEmpty {
|
|
3626
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3627
|
+
return
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3631
|
+
if txtFieldSelectDateSingleSavedBankView.text.isEmpty {
|
|
3632
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
3633
|
+
return
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3099
3638
|
// Check if the terms and conditions are agreed
|
|
3100
3639
|
if !agreeTermsAndCondtition {
|
|
3101
3640
|
showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
|
|
3102
3641
|
return
|
|
3103
3642
|
}
|
|
3104
|
-
|
|
3105
3643
|
// Proceed with the Pay Now action
|
|
3106
3644
|
let vc = easymerchantsdk.instantiateViewController(withIdentifier: "BillingInfoVC") as! BillingInfoVC
|
|
3107
3645
|
// Pass the customer_id and account_id to BillingInfoVC
|
|
@@ -3110,14 +3648,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3110
3648
|
vc.billingInfoData = jsonDict
|
|
3111
3649
|
vc.isFrom = "SavedBank"
|
|
3112
3650
|
vc.selectedPaymentMethod = "Bank"
|
|
3651
|
+
vc.chosenPlan = self.txtFieldSelectPlanSingleSavedBankView.text
|
|
3652
|
+
vc.startDate = self.txtFieldSelectDateSingleSavedBankView.text
|
|
3653
|
+
vc.request = self.request
|
|
3113
3654
|
self.navigationController?.pushViewController(vc, animated: true)
|
|
3114
3655
|
}
|
|
3115
3656
|
else {
|
|
3116
3657
|
//If Billing info is nil or empty
|
|
3658
|
+
if let req = self.request, req.is_recurring == true {
|
|
3659
|
+
if txtFieldSelectPlanSingleSavedBankView.text.isEmpty {
|
|
3660
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3661
|
+
return
|
|
3662
|
+
}
|
|
3663
|
+
|
|
3664
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3665
|
+
if txtFieldSelectDateSingleSavedBankView.text.isEmpty {
|
|
3666
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
3667
|
+
return
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3117
3672
|
if !agreeTermsAndCondtition {
|
|
3118
3673
|
showAlert(title: "Terms and Conditions", message: "Please agree to the terms and conditions")
|
|
3119
3674
|
return
|
|
3120
3675
|
}
|
|
3676
|
+
|
|
3121
3677
|
accountChargeSavedBankAccountApi()
|
|
3122
3678
|
}
|
|
3123
3679
|
}
|
|
@@ -3175,6 +3731,21 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3175
3731
|
return
|
|
3176
3732
|
}
|
|
3177
3733
|
|
|
3734
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
3735
|
+
if let req = self.request, req.is_recurring == true {
|
|
3736
|
+
if txtFieldSelectPlanNewAccountView.text.isEmpty {
|
|
3737
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3738
|
+
return
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3742
|
+
if txtFieldSelectDateNewAccountView.text.isEmpty {
|
|
3743
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
3744
|
+
return
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
|
|
3178
3749
|
// Determine the vc.isFrom value based on isSavedNewAccount
|
|
3179
3750
|
let isSavedNewAccount = isSavedNewAccount
|
|
3180
3751
|
let isFromValue = isSavedNewAccount ? "AddNewAccountWithSave" : "AddNewAccountWithoutSave"
|
|
@@ -3190,10 +3761,56 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3190
3761
|
vc.isFrom = isFromValue
|
|
3191
3762
|
vc.isSavedNewAccount = isSavedNewAccount
|
|
3192
3763
|
vc.delegate = self
|
|
3764
|
+
vc.chosenPlan = self.txtFieldSelectPlanNewAccountView.text
|
|
3765
|
+
vc.startDate = self.txtFieldSelectDateNewAccountView.text
|
|
3766
|
+
vc.request = self.request
|
|
3193
3767
|
self.navigationController?.pushViewController(vc, animated: true)
|
|
3194
3768
|
}
|
|
3195
3769
|
else {
|
|
3196
3770
|
//If Billing info is nil or empty
|
|
3771
|
+
|
|
3772
|
+
// Retrieve and trim text field values (removing leading and trailing whitespace)
|
|
3773
|
+
let accountName = txtFieldAccountNameNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
3774
|
+
let routingNumber = txtFieldRoutingNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
3775
|
+
let accountType = txtFieldAccountTypeNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
3776
|
+
let accountNumber = txtFieldAccountNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
3777
|
+
let confirmAccountNumber = txtFieldConfirmAccountNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
3778
|
+
|
|
3779
|
+
// Check if any field is empty
|
|
3780
|
+
if accountName.isEmpty || routingNumber.isEmpty || accountType.isEmpty || accountNumber.isEmpty || confirmAccountNumber.isEmpty {
|
|
3781
|
+
let alert = UIAlertController(title: "Missing Information", message: "Please fill in all bank details.", preferredStyle: .alert)
|
|
3782
|
+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
|
3783
|
+
self.present(alert, animated: true, completion: nil)
|
|
3784
|
+
return
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3787
|
+
// Check if routing number is exactly 9 digits and numeric
|
|
3788
|
+
if routingNumber.count != 9 || !CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: routingNumber)) {
|
|
3789
|
+
self.showAlert(title: "Invalid Routing Number", message: "Routing number is not correct. It must be exactly 9 digits.")
|
|
3790
|
+
return
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
// Check if account number and confirmation match
|
|
3794
|
+
if accountNumber != confirmAccountNumber {
|
|
3795
|
+
self.showAlert(title: "Account Mismatch", message: "Bank account number and confirmation do not match.")
|
|
3796
|
+
return
|
|
3797
|
+
}
|
|
3798
|
+
|
|
3799
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
3800
|
+
if let req = self.request, req.is_recurring == true {
|
|
3801
|
+
if txtFieldSelectPlanNewAccountView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
3802
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
3803
|
+
return
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
3807
|
+
if txtFieldSelectDateNewAccountView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
3808
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
3809
|
+
return
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
|
|
3197
3814
|
let isSavedNewAccount = isSavedNewAccount
|
|
3198
3815
|
// If Billing info is nil or empty, call the appropriate API
|
|
3199
3816
|
if isSavedNewAccount {
|
|
@@ -3614,38 +4231,40 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3614
4231
|
// MARK: - GET Show Cards API
|
|
3615
4232
|
func getShowCardsApi() {
|
|
3616
4233
|
showLoadingIndicator()
|
|
3617
|
-
|
|
4234
|
+
|
|
3618
4235
|
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.getCards.path()
|
|
3619
|
-
|
|
4236
|
+
|
|
3620
4237
|
guard let serviceURL = URL(string: fullURL) else {
|
|
3621
4238
|
print("❌ Invalid URL: \(fullURL)")
|
|
3622
4239
|
hideLoadingIndicator()
|
|
3623
4240
|
return
|
|
3624
4241
|
}
|
|
3625
|
-
|
|
3626
|
-
var
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
4242
|
+
|
|
4243
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
4244
|
+
uRLRequest.httpMethod = "GET"
|
|
4245
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
4246
|
+
|
|
3630
4247
|
let token = UserStoreSingleton.shared.customerToken
|
|
3631
4248
|
print("🔑 Setting customerToken header: \(token ?? "None")")
|
|
3632
|
-
|
|
3633
|
-
|
|
4249
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Customer-Token")
|
|
4250
|
+
|
|
3634
4251
|
// Add API headers
|
|
3635
4252
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
3636
4253
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
3637
|
-
|
|
3638
|
-
|
|
4254
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
4255
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
3639
4256
|
}
|
|
3640
|
-
|
|
4257
|
+
|
|
3641
4258
|
let session = URLSession.shared
|
|
3642
|
-
let task = session.dataTask(with:
|
|
4259
|
+
let task = session.dataTask(with: uRLRequest) { [weak self] (data, response, error) in
|
|
3643
4260
|
guard let self = self else { return }
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
4261
|
+
|
|
4262
|
+
defer {
|
|
4263
|
+
DispatchQueue.main.async {
|
|
4264
|
+
self.hideLoadingIndicator()
|
|
4265
|
+
}
|
|
3647
4266
|
}
|
|
3648
|
-
|
|
4267
|
+
|
|
3649
4268
|
if let error = error {
|
|
3650
4269
|
print("❌ Error in getShowCardsApi: \(error.localizedDescription)")
|
|
3651
4270
|
if let httpResponse = response as? HTTPURLResponse {
|
|
@@ -3653,23 +4272,23 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3653
4272
|
}
|
|
3654
4273
|
return
|
|
3655
4274
|
}
|
|
3656
|
-
|
|
4275
|
+
|
|
3657
4276
|
guard let data = data else {
|
|
3658
4277
|
print("❌ No data received in getShowCardsApi.")
|
|
3659
4278
|
return
|
|
3660
4279
|
}
|
|
3661
|
-
|
|
4280
|
+
|
|
3662
4281
|
do {
|
|
3663
4282
|
// Print raw JSON string for inspection
|
|
3664
4283
|
if let rawJSON = String(data: data, encoding: .utf8) {
|
|
3665
4284
|
print("📦 Raw JSON Response:\n\(rawJSON)")
|
|
3666
4285
|
}
|
|
3667
|
-
|
|
4286
|
+
|
|
3668
4287
|
if let jsonResponse = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
3669
4288
|
let cards = jsonResponse["Cards"] as? [[String: Any]] {
|
|
3670
|
-
|
|
4289
|
+
|
|
3671
4290
|
print("✅ Parsed JSON Response:\n\(jsonResponse)")
|
|
3672
|
-
|
|
4291
|
+
|
|
3673
4292
|
// Map JSON response to CardModel array
|
|
3674
4293
|
self.savedCards = cards.compactMap { cardDict in
|
|
3675
4294
|
return CardModel(
|
|
@@ -3697,25 +4316,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3697
4316
|
--------------------------
|
|
3698
4317
|
""")
|
|
3699
4318
|
}
|
|
3700
|
-
|
|
4319
|
+
|
|
3701
4320
|
DispatchQueue.main.async {
|
|
3702
4321
|
if let firstCard = self.savedCards.first {
|
|
3703
4322
|
self.lblCardNumberSigleSavedCard.text = "****\(firstCard.ccLast4)"
|
|
3704
4323
|
self.lblExpireDateSingelSavedCard.text = "Expiry: \(firstCard.ccValidThru)"
|
|
4324
|
+
|
|
4325
|
+
self.selectedCardIndex = 0
|
|
4326
|
+
self.selectedCard = self.savedCards[0]
|
|
4327
|
+
self.updateSingleCardView(for: 0)
|
|
4328
|
+
|
|
3705
4329
|
}
|
|
3706
|
-
|
|
4330
|
+
|
|
3707
4331
|
self.tblViewSavedCardsList.reloadData()
|
|
3708
|
-
|
|
4332
|
+
|
|
3709
4333
|
if self.isSelectForPay {
|
|
3710
4334
|
self.viewTxtFieldCVVSingleCard.isHidden = false
|
|
3711
4335
|
self.btnPayNowSingleCard.isHidden = false
|
|
3712
4336
|
self.btnNext.isHidden = true
|
|
3713
4337
|
self.btnNextHeight.constant = 0
|
|
3714
4338
|
self.btnNextTopCon.constant = 0
|
|
3715
|
-
|
|
3716
|
-
if self.request.
|
|
4339
|
+
|
|
4340
|
+
if self.request.is_recurring == true {
|
|
3717
4341
|
self.txtFieldSelectPlanSingleSavedCard.isHidden = false
|
|
3718
|
-
self.
|
|
4342
|
+
if self.request.recurringStartDateType == .custom {
|
|
4343
|
+
self.txtFieldSelectDateSingleSavedCard.isHidden = false
|
|
4344
|
+
}
|
|
3719
4345
|
} else {
|
|
3720
4346
|
self.txtFieldSelectPlanSingleSavedCard.isHidden = true
|
|
3721
4347
|
self.txtFieldSelectDateSingleSavedCard.isHidden = true
|
|
@@ -3744,7 +4370,10 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3744
4370
|
func paymentIntentApi() {
|
|
3745
4371
|
showLoadingIndicator()
|
|
3746
4372
|
|
|
3747
|
-
|
|
4373
|
+
///Old Using URL Endpoints
|
|
4374
|
+
// let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.creditCharges.path()
|
|
4375
|
+
|
|
4376
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
|
|
3748
4377
|
|
|
3749
4378
|
guard let serviceURL = URL(string: fullURL) else {
|
|
3750
4379
|
print("Invalid URL")
|
|
@@ -3752,37 +4381,62 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3752
4381
|
return
|
|
3753
4382
|
}
|
|
3754
4383
|
|
|
3755
|
-
var
|
|
3756
|
-
|
|
3757
|
-
|
|
4384
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
4385
|
+
uRLRequest.httpMethod = "POST"
|
|
4386
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
3758
4387
|
|
|
3759
4388
|
let token = UserStoreSingleton.shared.clientToken
|
|
3760
4389
|
print("Setting clientToken header: \(token ?? "None")")
|
|
3761
|
-
|
|
4390
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
3762
4391
|
|
|
3763
4392
|
// Add API headers
|
|
3764
4393
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
3765
4394
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
3766
|
-
|
|
3767
|
-
|
|
4395
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
4396
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
3768
4397
|
}
|
|
3769
4398
|
|
|
3770
|
-
|
|
4399
|
+
var params: [String: Any] = [
|
|
3771
4400
|
"name": cardNameTextField.text,
|
|
3772
|
-
"email":
|
|
4401
|
+
"email": UserStoreSingleton.shared.merchantEmail ?? "",
|
|
3773
4402
|
"card_number": cardNumberTextField.text.replacingOccurrences(of: " ", with: ""),
|
|
3774
4403
|
"cardholder_name": cardNameTextField.text,
|
|
3775
4404
|
"exp_month": cardExpiryTextField.text.components(separatedBy: "/").first ?? "",
|
|
3776
4405
|
"exp_year": cardExpiryTextField.text.components(separatedBy: "/").last ?? "",
|
|
3777
4406
|
"cvc": cardCvvTextField.text,
|
|
3778
|
-
"description": "
|
|
3779
|
-
"currency": "usd"
|
|
3780
|
-
"payment_method": "card"
|
|
4407
|
+
"description": "payment checkout",
|
|
4408
|
+
"currency": "usd"
|
|
4409
|
+
// "payment_method": "card"
|
|
3781
4410
|
]
|
|
3782
4411
|
|
|
4412
|
+
// Add these if recurring is enabled
|
|
4413
|
+
if let req = request, req.is_recurring == true {
|
|
4414
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
4415
|
+
// Only send start_date if type is .custom and field is not empty
|
|
4416
|
+
if let startDateText = txtFieldStartDateCard?.text, !startDateText.isEmpty {
|
|
4417
|
+
let inputFormatter = DateFormatter()
|
|
4418
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
4419
|
+
|
|
4420
|
+
let outputFormatter = DateFormatter()
|
|
4421
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
4422
|
+
|
|
4423
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
4424
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
4425
|
+
params["start_date"] = apiFormattedDate
|
|
4426
|
+
} else {
|
|
4427
|
+
print("Invalid date format in startDateText")
|
|
4428
|
+
}
|
|
4429
|
+
}
|
|
4430
|
+
}
|
|
4431
|
+
|
|
4432
|
+
params["interval"] = txtFieldChosePlanCard.text.lowercased()
|
|
4433
|
+
}
|
|
4434
|
+
|
|
4435
|
+
print(params)
|
|
4436
|
+
|
|
3783
4437
|
do {
|
|
3784
4438
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
3785
|
-
|
|
4439
|
+
uRLRequest.httpBody = jsonData
|
|
3786
4440
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
3787
4441
|
print("JSON Payload: \(jsonString)")
|
|
3788
4442
|
}
|
|
@@ -3793,7 +4447,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3793
4447
|
}
|
|
3794
4448
|
|
|
3795
4449
|
let session = URLSession.shared
|
|
3796
|
-
let task = session.dataTask(with:
|
|
4450
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
3797
4451
|
|
|
3798
4452
|
DispatchQueue.main.async {
|
|
3799
4453
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -3854,54 +4508,89 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3854
4508
|
// MARK: - Credit Card Charge API (Saved Cards, No Billing Info, Logged In)
|
|
3855
4509
|
func paymentIntentFromShowSavedCardApi() {
|
|
3856
4510
|
showLoadingIndicator()
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
4511
|
+
|
|
4512
|
+
///Old Using URL Endpoints
|
|
4513
|
+
// let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.creditCharges.path()
|
|
4514
|
+
|
|
4515
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()
|
|
4516
|
+
|
|
3860
4517
|
guard let serviceURL = URL(string: fullURL) else {
|
|
3861
4518
|
print("Invalid URL")
|
|
3862
4519
|
hideLoadingIndicator()
|
|
3863
4520
|
return
|
|
3864
4521
|
}
|
|
3865
|
-
|
|
3866
|
-
var
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
4522
|
+
|
|
4523
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
4524
|
+
uRLRequest.httpMethod = "POST"
|
|
4525
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
4526
|
+
|
|
3870
4527
|
// Add client token
|
|
3871
4528
|
let token = UserStoreSingleton.shared.clientToken
|
|
3872
4529
|
print("Setting clientToken header: \(token ?? "None")")
|
|
3873
|
-
|
|
3874
|
-
|
|
4530
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
4531
|
+
|
|
3875
4532
|
// Add API headers
|
|
3876
4533
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
3877
4534
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
3878
|
-
|
|
3879
|
-
|
|
4535
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
4536
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
3880
4537
|
}
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
let
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
"
|
|
3889
|
-
"
|
|
4538
|
+
|
|
4539
|
+
///OLD Params
|
|
4540
|
+
// // Prepare parameters
|
|
4541
|
+
// let email = UserStoreSingleton.shared.verificationEmail ?? ""
|
|
4542
|
+
// let namePart = email.split(separator: "@").first.map(String.init) ?? ""
|
|
4543
|
+
//
|
|
4544
|
+
// var params: [String: Any] = [
|
|
4545
|
+
// "name": namePart,
|
|
4546
|
+
// "email": email,
|
|
4547
|
+
// "description": "TestDescription",
|
|
4548
|
+
// "currency": "usd",
|
|
4549
|
+
// "payment_method": "card",
|
|
4550
|
+
// "save_card": 0,
|
|
4551
|
+
// "customer": selectedCard?.customerId ?? "",
|
|
4552
|
+
// "card_id": selectedCard?.cardId ?? "",
|
|
4553
|
+
// "cvc": txtFieldCVVSingleSavedCard.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "",
|
|
4554
|
+
// "customer_id": selectedCard?.customerId ?? ""
|
|
4555
|
+
// ]
|
|
4556
|
+
|
|
4557
|
+
var params: [String: Any] = [
|
|
4558
|
+
"description": "payment checkout",
|
|
3890
4559
|
"currency": "usd",
|
|
3891
|
-
"payment_method": "card",
|
|
3892
|
-
"save_card": 0,
|
|
3893
|
-
"customer": selectedCard?.customerId ?? "",
|
|
3894
4560
|
"card_id": selectedCard?.cardId ?? "",
|
|
3895
4561
|
"cvc": txtFieldCVVSingleSavedCard.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "",
|
|
3896
4562
|
"customer_id": selectedCard?.customerId ?? ""
|
|
3897
4563
|
]
|
|
3898
|
-
|
|
4564
|
+
|
|
4565
|
+
// Add these if recurring is enabled
|
|
4566
|
+
if let req = request, req.is_recurring == true {
|
|
4567
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
4568
|
+
// Only send start_date if type is .custom and field is not empty
|
|
4569
|
+
if let startDateText = txtFieldSelectDateSingleSavedCard?.text, !startDateText.isEmpty {
|
|
4570
|
+
let inputFormatter = DateFormatter()
|
|
4571
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
4572
|
+
|
|
4573
|
+
let outputFormatter = DateFormatter()
|
|
4574
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
4575
|
+
|
|
4576
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
4577
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
4578
|
+
params["start_date"] = apiFormattedDate
|
|
4579
|
+
} else {
|
|
4580
|
+
print("Invalid date format in startDateText")
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
|
|
4585
|
+
params["interval"] = txtFieldSelectPlanSingleSavedCard.text.lowercased()
|
|
4586
|
+
}
|
|
4587
|
+
|
|
3899
4588
|
print("Request Params: \(params)")
|
|
3900
|
-
|
|
4589
|
+
|
|
3901
4590
|
do {
|
|
3902
4591
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
3903
|
-
|
|
3904
|
-
|
|
4592
|
+
uRLRequest.httpBody = jsonData
|
|
4593
|
+
|
|
3905
4594
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
3906
4595
|
print("JSON Payload: \(jsonString)")
|
|
3907
4596
|
}
|
|
@@ -3910,29 +4599,29 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3910
4599
|
hideLoadingIndicator()
|
|
3911
4600
|
return
|
|
3912
4601
|
}
|
|
3913
|
-
|
|
4602
|
+
|
|
3914
4603
|
// API Call
|
|
3915
|
-
let task = URLSession.shared.dataTask(with:
|
|
4604
|
+
let task = URLSession.shared.dataTask(with: uRLRequest) { (data, response, error) in
|
|
3916
4605
|
DispatchQueue.main.async {
|
|
3917
4606
|
self.hideLoadingIndicator()
|
|
3918
4607
|
}
|
|
3919
|
-
|
|
4608
|
+
|
|
3920
4609
|
if let error = error {
|
|
3921
4610
|
print("Error: \(error.localizedDescription)")
|
|
3922
4611
|
return
|
|
3923
4612
|
}
|
|
3924
|
-
|
|
4613
|
+
|
|
3925
4614
|
guard let httpResponse = response as? HTTPURLResponse else {
|
|
3926
4615
|
print("Invalid response")
|
|
3927
4616
|
return
|
|
3928
4617
|
}
|
|
3929
|
-
|
|
4618
|
+
|
|
3930
4619
|
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
3931
4620
|
if let data = data {
|
|
3932
4621
|
do {
|
|
3933
4622
|
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
3934
4623
|
print("Response Data: \(responseObject)")
|
|
3935
|
-
|
|
4624
|
+
|
|
3936
4625
|
if let status = responseObject["status"] as? Int, status == 0 {
|
|
3937
4626
|
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
3938
4627
|
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
@@ -3970,31 +4659,6 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
3970
4659
|
|
|
3971
4660
|
//MARK: - Credit Card Charge Api from Add new cards If Billing info is nil and Logged in.
|
|
3972
4661
|
func paymentIntentFromAddNewCardApi(customerId: String?) {
|
|
3973
|
-
showLoadingIndicator()
|
|
3974
|
-
|
|
3975
|
-
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.creditCharges.path()
|
|
3976
|
-
|
|
3977
|
-
guard let serviceURL = URL(string: fullURL) else {
|
|
3978
|
-
print("Invalid URL")
|
|
3979
|
-
hideLoadingIndicator()
|
|
3980
|
-
return
|
|
3981
|
-
}
|
|
3982
|
-
|
|
3983
|
-
var request = URLRequest(url: serviceURL)
|
|
3984
|
-
request.httpMethod = "POST"
|
|
3985
|
-
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
3986
|
-
|
|
3987
|
-
let token = UserStoreSingleton.shared.clientToken
|
|
3988
|
-
print("Setting clientToken header: \(token ?? "None")")
|
|
3989
|
-
request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
3990
|
-
|
|
3991
|
-
// Add API headers
|
|
3992
|
-
if let apiKey = EnvironmentConfig.apiKey,
|
|
3993
|
-
let apiSecret = EnvironmentConfig.apiSecret {
|
|
3994
|
-
request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
3995
|
-
request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
3996
|
-
}
|
|
3997
|
-
|
|
3998
4662
|
// Get the text fields from the selected cell and trim whitespace
|
|
3999
4663
|
let nameText = txtFieldNameOnCardNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
4000
4664
|
let cvvText = txtFieldCVVNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
@@ -4044,6 +4708,48 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4044
4708
|
return
|
|
4045
4709
|
}
|
|
4046
4710
|
|
|
4711
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
4712
|
+
if let req = self.request, req.is_recurring == true {
|
|
4713
|
+
let planText = txtFieldSelectPlanNewCardView.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
4714
|
+
let dateText = txtFieldSelectDateNewCardView.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
4715
|
+
|
|
4716
|
+
if planText.isEmpty {
|
|
4717
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
4718
|
+
return
|
|
4719
|
+
}
|
|
4720
|
+
|
|
4721
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
4722
|
+
if dateText.isEmpty {
|
|
4723
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
4724
|
+
return
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
|
|
4729
|
+
showLoadingIndicator()
|
|
4730
|
+
|
|
4731
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.creditCharges.path()
|
|
4732
|
+
|
|
4733
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
4734
|
+
print("Invalid URL")
|
|
4735
|
+
hideLoadingIndicator()
|
|
4736
|
+
return
|
|
4737
|
+
}
|
|
4738
|
+
|
|
4739
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
4740
|
+
uRLRequest.httpMethod = "POST"
|
|
4741
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
4742
|
+
|
|
4743
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
4744
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
4745
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
4746
|
+
|
|
4747
|
+
// Add API headers
|
|
4748
|
+
if let apiKey = EnvironmentConfig.apiKey,
|
|
4749
|
+
let apiSecret = EnvironmentConfig.apiSecret {
|
|
4750
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
4751
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
4752
|
+
}
|
|
4047
4753
|
|
|
4048
4754
|
let emailPrefix = UserStoreSingleton.shared.verificationEmail?.components(separatedBy: "@").first ?? ""
|
|
4049
4755
|
|
|
@@ -4073,11 +4779,34 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4073
4779
|
params["username"] = emailPrefix
|
|
4074
4780
|
}
|
|
4075
4781
|
|
|
4782
|
+
// Add these if recurring is enabled
|
|
4783
|
+
if let req = request, req.is_recurring == true {
|
|
4784
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
4785
|
+
// Only send start_date if type is .custom and field is not empty
|
|
4786
|
+
if let startDateText = txtFieldSelectDateNewCardView?.text, !startDateText.isEmpty {
|
|
4787
|
+
let inputFormatter = DateFormatter()
|
|
4788
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
4789
|
+
|
|
4790
|
+
let outputFormatter = DateFormatter()
|
|
4791
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
4792
|
+
|
|
4793
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
4794
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
4795
|
+
params["start_date"] = apiFormattedDate
|
|
4796
|
+
} else {
|
|
4797
|
+
print("Invalid date format in startDateText")
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
params["interval"] = txtFieldSelectPlanNewCardView.text.lowercased()
|
|
4803
|
+
}
|
|
4804
|
+
|
|
4076
4805
|
print(params)
|
|
4077
4806
|
|
|
4078
4807
|
do {
|
|
4079
4808
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
4080
|
-
|
|
4809
|
+
uRLRequest.httpBody = jsonData
|
|
4081
4810
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
4082
4811
|
print("JSON Payload: \(jsonString)")
|
|
4083
4812
|
}
|
|
@@ -4088,7 +4817,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4088
4817
|
}
|
|
4089
4818
|
|
|
4090
4819
|
let session = URLSession.shared
|
|
4091
|
-
let task = session.dataTask(with:
|
|
4820
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
4092
4821
|
|
|
4093
4822
|
DispatchQueue.main.async {
|
|
4094
4823
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -4504,9 +5233,13 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4504
5233
|
self.viewTermAndConditionsSingleAccountView.isHidden = false
|
|
4505
5234
|
self.btnPayNowSingleAccountView.isHidden = false
|
|
4506
5235
|
|
|
4507
|
-
if self.request.
|
|
5236
|
+
if self.request.is_recurring == true {
|
|
4508
5237
|
self.txtFieldSelectPlanSingleSavedBankView.isHidden = false
|
|
4509
|
-
self.
|
|
5238
|
+
if self.request.recurringStartDateType == .custom {
|
|
5239
|
+
self.txtFieldSelectDateSingleSavedBankView.isHidden = false
|
|
5240
|
+
} else {
|
|
5241
|
+
self.txtFieldSelectDateSingleSavedBankView.isHidden = true
|
|
5242
|
+
}
|
|
4510
5243
|
}
|
|
4511
5244
|
else {
|
|
4512
5245
|
self.txtFieldSelectPlanSingleSavedBankView.isHidden = true
|
|
@@ -4553,22 +5286,22 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4553
5286
|
return
|
|
4554
5287
|
}
|
|
4555
5288
|
|
|
4556
|
-
var
|
|
4557
|
-
|
|
4558
|
-
|
|
5289
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
5290
|
+
uRLRequest.httpMethod = "POST"
|
|
5291
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
4559
5292
|
|
|
4560
5293
|
let token = UserStoreSingleton.shared.clientToken
|
|
4561
5294
|
print("Setting clientToken header: \(token ?? "None")")
|
|
4562
|
-
|
|
5295
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
4563
5296
|
|
|
4564
5297
|
// Add API headers
|
|
4565
5298
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
4566
5299
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
4567
|
-
|
|
4568
|
-
|
|
5300
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
5301
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
4569
5302
|
}
|
|
4570
5303
|
|
|
4571
|
-
|
|
5304
|
+
var params: [String: Any] = [
|
|
4572
5305
|
"name": txtFieldAccountName.text ?? "",
|
|
4573
5306
|
"email": "test@gmail.com",
|
|
4574
5307
|
"description": "TestDescription",
|
|
@@ -4581,9 +5314,32 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4581
5314
|
"levelIndicator": 1,
|
|
4582
5315
|
]
|
|
4583
5316
|
|
|
5317
|
+
// Add these if recurring is enabled
|
|
5318
|
+
if let req = request, req.is_recurring == true {
|
|
5319
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
5320
|
+
// Only send start_date if type is .custom and field is not empty
|
|
5321
|
+
if let startDateText = txtFieldSelectDateViewBankFields?.text, !startDateText.isEmpty {
|
|
5322
|
+
let inputFormatter = DateFormatter()
|
|
5323
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
5324
|
+
|
|
5325
|
+
let outputFormatter = DateFormatter()
|
|
5326
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
5327
|
+
|
|
5328
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
5329
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
5330
|
+
params["start_date"] = apiFormattedDate
|
|
5331
|
+
} else {
|
|
5332
|
+
print("Invalid date format in startDateText")
|
|
5333
|
+
}
|
|
5334
|
+
}
|
|
5335
|
+
}
|
|
5336
|
+
|
|
5337
|
+
params["interval"] = txtFieldSelectPlanViewBankFields.text.lowercased()
|
|
5338
|
+
}
|
|
5339
|
+
|
|
4584
5340
|
do {
|
|
4585
5341
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
4586
|
-
|
|
5342
|
+
uRLRequest.httpBody = jsonData
|
|
4587
5343
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
4588
5344
|
print("JSON Payload: \(jsonString)")
|
|
4589
5345
|
}
|
|
@@ -4594,7 +5350,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4594
5350
|
}
|
|
4595
5351
|
|
|
4596
5352
|
let session = URLSession.shared
|
|
4597
|
-
let task = session.dataTask(with:
|
|
5353
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
4598
5354
|
|
|
4599
5355
|
DispatchQueue.main.async {
|
|
4600
5356
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -4665,22 +5421,22 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4665
5421
|
return
|
|
4666
5422
|
}
|
|
4667
5423
|
|
|
4668
|
-
var
|
|
4669
|
-
|
|
4670
|
-
|
|
5424
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
5425
|
+
uRLRequest.httpMethod = "POST"
|
|
5426
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
4671
5427
|
|
|
4672
5428
|
let token = UserStoreSingleton.shared.clientToken
|
|
4673
5429
|
print("Setting clientToken header: \(token ?? "None")")
|
|
4674
|
-
|
|
5430
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
4675
5431
|
|
|
4676
5432
|
// Add API headers
|
|
4677
5433
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
4678
5434
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
4679
|
-
|
|
4680
|
-
|
|
5435
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
5436
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
4681
5437
|
}
|
|
4682
5438
|
|
|
4683
|
-
|
|
5439
|
+
var params: [String: Any] = [
|
|
4684
5440
|
"account_id": selectedbankAccounts?.account_id ?? "",
|
|
4685
5441
|
"customer": selectedbankAccounts?.customer_id ?? "",
|
|
4686
5442
|
"payment_method": "ach",
|
|
@@ -4688,11 +5444,34 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4688
5444
|
"currency": "usd",
|
|
4689
5445
|
]
|
|
4690
5446
|
|
|
5447
|
+
// Add these if recurring is enabled
|
|
5448
|
+
if let req = request, req.is_recurring == true {
|
|
5449
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
5450
|
+
// Only send start_date if type is .custom and field is not empty
|
|
5451
|
+
if let startDateText = txtFieldSelectDateSingleSavedBankView?.text, !startDateText.isEmpty {
|
|
5452
|
+
let inputFormatter = DateFormatter()
|
|
5453
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
5454
|
+
|
|
5455
|
+
let outputFormatter = DateFormatter()
|
|
5456
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
5457
|
+
|
|
5458
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
5459
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
5460
|
+
params["start_date"] = apiFormattedDate
|
|
5461
|
+
} else {
|
|
5462
|
+
print("Invalid date format in startDateText")
|
|
5463
|
+
}
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5467
|
+
params["interval"] = txtFieldSelectPlanSingleSavedBankView.text.lowercased()
|
|
5468
|
+
}
|
|
5469
|
+
|
|
4691
5470
|
print(params)
|
|
4692
5471
|
|
|
4693
5472
|
do {
|
|
4694
5473
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
4695
|
-
|
|
5474
|
+
uRLRequest.httpBody = jsonData
|
|
4696
5475
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
4697
5476
|
print("JSON Payload: \(jsonString)")
|
|
4698
5477
|
}
|
|
@@ -4703,7 +5482,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4703
5482
|
}
|
|
4704
5483
|
|
|
4705
5484
|
let session = URLSession.shared
|
|
4706
|
-
let task = session.dataTask(with:
|
|
5485
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
4707
5486
|
|
|
4708
5487
|
DispatchQueue.main.async {
|
|
4709
5488
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -4774,19 +5553,19 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4774
5553
|
return
|
|
4775
5554
|
}
|
|
4776
5555
|
|
|
4777
|
-
var
|
|
4778
|
-
|
|
4779
|
-
|
|
5556
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
5557
|
+
uRLRequest.httpMethod = "POST"
|
|
5558
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
4780
5559
|
|
|
4781
5560
|
let token = UserStoreSingleton.shared.clientToken
|
|
4782
5561
|
print("Setting clientToken header: \(token ?? "None")")
|
|
4783
|
-
|
|
5562
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
4784
5563
|
|
|
4785
5564
|
// Add API headers
|
|
4786
5565
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
4787
5566
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
4788
|
-
|
|
4789
|
-
|
|
5567
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
5568
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
4790
5569
|
}
|
|
4791
5570
|
|
|
4792
5571
|
let accountName = txtFieldAccountNameNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
@@ -4794,7 +5573,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4794
5573
|
let accountType = txtFieldAccountTypeNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
4795
5574
|
let accountNumber = txtFieldAccountNumberNewAccountView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
4796
5575
|
|
|
4797
|
-
|
|
5576
|
+
var params: [String: Any] = [
|
|
4798
5577
|
"name": accountName,
|
|
4799
5578
|
"email": UserStoreSingleton.shared.verificationEmail ?? "",
|
|
4800
5579
|
"description": "Test Description",
|
|
@@ -4807,11 +5586,34 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4807
5586
|
"levelIndicator": 1,
|
|
4808
5587
|
]
|
|
4809
5588
|
|
|
5589
|
+
// Add these if recurring is enabled
|
|
5590
|
+
if let req = request, req.is_recurring == true {
|
|
5591
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
5592
|
+
// Only send start_date if type is .custom and field is not empty
|
|
5593
|
+
if let startDateText = txtFieldSelectDateNewAccountView?.text, !startDateText.isEmpty {
|
|
5594
|
+
let inputFormatter = DateFormatter()
|
|
5595
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
5596
|
+
|
|
5597
|
+
let outputFormatter = DateFormatter()
|
|
5598
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
5599
|
+
|
|
5600
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
5601
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
5602
|
+
params["start_date"] = apiFormattedDate
|
|
5603
|
+
} else {
|
|
5604
|
+
print("Invalid date format in startDateText")
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5608
|
+
|
|
5609
|
+
params["interval"] = txtFieldSelectPlanNewAccountView.text.lowercased()
|
|
5610
|
+
}
|
|
5611
|
+
|
|
4810
5612
|
print(params)
|
|
4811
5613
|
|
|
4812
5614
|
do {
|
|
4813
5615
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
4814
|
-
|
|
5616
|
+
uRLRequest.httpBody = jsonData
|
|
4815
5617
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
4816
5618
|
print("JSON Payload: \(jsonString)")
|
|
4817
5619
|
}
|
|
@@ -4822,7 +5624,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4822
5624
|
}
|
|
4823
5625
|
|
|
4824
5626
|
let session = URLSession.shared
|
|
4825
|
-
let task = session.dataTask(with:
|
|
5627
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
4826
5628
|
|
|
4827
5629
|
DispatchQueue.main.async {
|
|
4828
5630
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -4893,19 +5695,19 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4893
5695
|
return
|
|
4894
5696
|
}
|
|
4895
5697
|
|
|
4896
|
-
var
|
|
4897
|
-
|
|
4898
|
-
|
|
5698
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
5699
|
+
uRLRequest.httpMethod = "POST"
|
|
5700
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
4899
5701
|
|
|
4900
5702
|
let token = UserStoreSingleton.shared.clientToken
|
|
4901
5703
|
print("Setting clientToken header: \(token ?? "None")")
|
|
4902
|
-
|
|
5704
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
4903
5705
|
|
|
4904
5706
|
// Add API headers
|
|
4905
5707
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
4906
5708
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
4907
|
-
|
|
4908
|
-
|
|
5709
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
5710
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
4909
5711
|
}
|
|
4910
5712
|
|
|
4911
5713
|
// Retrieve and trim text field values (removing leading and trailing whitespace)
|
|
@@ -4937,11 +5739,34 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4937
5739
|
params["username"] = emailPrefix
|
|
4938
5740
|
}
|
|
4939
5741
|
|
|
5742
|
+
// Add these if recurring is enabled
|
|
5743
|
+
if let req = request, req.is_recurring == true {
|
|
5744
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
5745
|
+
// Only send start_date if type is .custom and field is not empty
|
|
5746
|
+
if let startDateText = txtFieldSelectDateNewAccountView?.text, !startDateText.isEmpty {
|
|
5747
|
+
let inputFormatter = DateFormatter()
|
|
5748
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
5749
|
+
|
|
5750
|
+
let outputFormatter = DateFormatter()
|
|
5751
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
5752
|
+
|
|
5753
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
5754
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
5755
|
+
params["start_date"] = apiFormattedDate
|
|
5756
|
+
} else {
|
|
5757
|
+
print("Invalid date format in startDateText")
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
}
|
|
5761
|
+
|
|
5762
|
+
params["interval"] = txtFieldSelectPlanNewAccountView.text.lowercased()
|
|
5763
|
+
}
|
|
5764
|
+
|
|
4940
5765
|
print(params)
|
|
4941
5766
|
|
|
4942
5767
|
do {
|
|
4943
5768
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
4944
|
-
|
|
5769
|
+
uRLRequest.httpBody = jsonData
|
|
4945
5770
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
4946
5771
|
print("JSON Payload: \(jsonString)")
|
|
4947
5772
|
}
|
|
@@ -4952,7 +5777,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
4952
5777
|
}
|
|
4953
5778
|
|
|
4954
5779
|
let session = URLSession.shared
|
|
4955
|
-
let task = session.dataTask(with:
|
|
5780
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
4956
5781
|
|
|
4957
5782
|
DispatchQueue.main.async {
|
|
4958
5783
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -5110,31 +5935,270 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
5110
5935
|
|
|
5111
5936
|
let token = UserStoreSingleton.shared.clientToken
|
|
5112
5937
|
print("Setting clientToken header: \(token ?? "None")")
|
|
5113
|
-
request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
5938
|
+
request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
5939
|
+
|
|
5940
|
+
// Add API headers
|
|
5941
|
+
if let apiKey = EnvironmentConfig.apiKey,
|
|
5942
|
+
let apiSecret = EnvironmentConfig.apiSecret {
|
|
5943
|
+
request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
5944
|
+
request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
5945
|
+
}
|
|
5946
|
+
|
|
5947
|
+
let params: [String: Any] = [
|
|
5948
|
+
"name": UserStoreSingleton.shared.merchantName ?? "",
|
|
5949
|
+
"email": UserStoreSingleton.shared.merchantEmail ?? "",
|
|
5950
|
+
"description": "Payment checkout",
|
|
5951
|
+
"amount": amount ?? "",
|
|
5952
|
+
"currency": "satoshi",
|
|
5953
|
+
"payment_account": UserStoreSingleton.shared.paymentAccount ?? "",
|
|
5954
|
+
"success_url": "https://stage-api.stage-easymerchant.io/webhook/crypto",
|
|
5955
|
+
"callback_url": "https://stage-api.stage-easymerchant.io/webhook/crypto"
|
|
5956
|
+
]
|
|
5957
|
+
|
|
5958
|
+
print(params)
|
|
5959
|
+
|
|
5960
|
+
do {
|
|
5961
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
5962
|
+
request.httpBody = jsonData
|
|
5963
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
5964
|
+
print("JSON Payload: \(jsonString)")
|
|
5965
|
+
}
|
|
5966
|
+
} catch let error {
|
|
5967
|
+
print("Error creating JSON data: \(error)")
|
|
5968
|
+
hideLoadingIndicator()
|
|
5969
|
+
return
|
|
5970
|
+
}
|
|
5971
|
+
|
|
5972
|
+
let session = URLSession.shared
|
|
5973
|
+
let task = session.dataTask(with: request) { (serviceData, serviceResponse, error) in
|
|
5974
|
+
|
|
5975
|
+
DispatchQueue.main.async {
|
|
5976
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
5977
|
+
}
|
|
5978
|
+
|
|
5979
|
+
if let error = error {
|
|
5980
|
+
print("Error: \(error.localizedDescription)")
|
|
5981
|
+
return
|
|
5982
|
+
}
|
|
5983
|
+
|
|
5984
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
5985
|
+
print("Invalid response")
|
|
5986
|
+
return
|
|
5987
|
+
}
|
|
5988
|
+
|
|
5989
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
5990
|
+
if let data = serviceData {
|
|
5991
|
+
do {
|
|
5992
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
5993
|
+
print("Response Data: \(responseObject)")
|
|
5994
|
+
|
|
5995
|
+
// Check if status is 0 and handle the error
|
|
5996
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
5997
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
5998
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
5999
|
+
} else {
|
|
6000
|
+
// Extract charge_id
|
|
6001
|
+
if let chargeId = responseObject["charge_id"] as? String {
|
|
6002
|
+
self.chargeId = chargeId
|
|
6003
|
+
print("Charge ID: \(chargeId)")
|
|
6004
|
+
}
|
|
6005
|
+
|
|
6006
|
+
// Extract chain_invoice address and lightning invoice fields
|
|
6007
|
+
if let data = responseObject["data"] as? [String: Any] {
|
|
6008
|
+
self.chainInvoiceAddress = (data["chain_invoice"] as? [String: Any])?["address"] as? String
|
|
6009
|
+
self.lightningURI = (data["lightning_invoice"] as? [String: Any])?["payreq"] as? String
|
|
6010
|
+
|
|
6011
|
+
// Default to OnChain QR code initially
|
|
6012
|
+
DispatchQueue.main.async {
|
|
6013
|
+
if let address = self.chainInvoiceAddress {
|
|
6014
|
+
self.qrImageView.image = self.generateQRCode(from: address)
|
|
6015
|
+
self.lblBTCAddress.text = "BTC Address: \(address)"
|
|
6016
|
+
|
|
6017
|
+
// Extract the amount from the URI
|
|
6018
|
+
if let uri = data["uri"] as? String {
|
|
6019
|
+
if let amountString = self.extractAmount(from: uri) {
|
|
6020
|
+
// Update the label with the extracted amount
|
|
6021
|
+
DispatchQueue.main.async {
|
|
6022
|
+
self.lblAmmoutCrypto.text = "\(amountString) BTC = $\(self.amount ?? 0) USD"
|
|
6023
|
+
self.lblCryptoAmmountViewTryAgain.text = "\(amountString) BTC = $\(self.amount ?? 0) USD"
|
|
6024
|
+
}
|
|
6025
|
+
}
|
|
6026
|
+
}
|
|
6027
|
+
}
|
|
6028
|
+
|
|
6029
|
+
self.viewCryptoQRCode.isHidden = false
|
|
6030
|
+
self.viewCryptoTryAgain.isHidden = true
|
|
6031
|
+
}
|
|
6032
|
+
} else {
|
|
6033
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid response format")
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
} else {
|
|
6037
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
6038
|
+
}
|
|
6039
|
+
} catch let jsonError {
|
|
6040
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
6041
|
+
}
|
|
6042
|
+
} else {
|
|
6043
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
6044
|
+
}
|
|
6045
|
+
} else {
|
|
6046
|
+
if let data = serviceData,
|
|
6047
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
6048
|
+
let message = responseObj["message"] as? String {
|
|
6049
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
6050
|
+
} else {
|
|
6051
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
task.resume()
|
|
6056
|
+
}
|
|
6057
|
+
|
|
6058
|
+
// Helper function to extract the amount from the URI
|
|
6059
|
+
func extractAmount(from uri: String) -> String? {
|
|
6060
|
+
let regex = try? NSRegularExpression(pattern: "amount=([\\d.]+)", options: [])
|
|
6061
|
+
let matches = regex?.matches(in: uri, options: [], range: NSRange(location: 0, length: uri.count))
|
|
6062
|
+
|
|
6063
|
+
if let match = matches?.first, let amountRange = Range(match.range(at: 1), in: uri) {
|
|
6064
|
+
return String(uri[amountRange])
|
|
6065
|
+
}
|
|
6066
|
+
return nil
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
//MARK: - GET Crypto Payment Info API
|
|
6070
|
+
func getCryptoPaymentInfoApi(chargeId: String) {
|
|
6071
|
+
|
|
6072
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.charges.path()+"/\(chargeId)"
|
|
6073
|
+
|
|
6074
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
6075
|
+
print("Invalid URL")
|
|
6076
|
+
hideLoadingIndicator()
|
|
6077
|
+
return
|
|
6078
|
+
}
|
|
6079
|
+
|
|
6080
|
+
var request = URLRequest(url: serviceURL)
|
|
6081
|
+
request.httpMethod = "GET"
|
|
6082
|
+
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
6083
|
+
|
|
6084
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
6085
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
6086
|
+
request.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
6087
|
+
|
|
6088
|
+
// Add API headers
|
|
6089
|
+
if let apiKey = EnvironmentConfig.apiKey,
|
|
6090
|
+
let apiSecret = EnvironmentConfig.apiSecret {
|
|
6091
|
+
request.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
6092
|
+
request.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6095
|
+
let session = URLSession.shared
|
|
6096
|
+
let task = session.dataTask(with: request) { [weak self] (data, response, error) in
|
|
6097
|
+
guard let self = self else { return }
|
|
6098
|
+
|
|
6099
|
+
if let error = error {
|
|
6100
|
+
print("Error: \(error.localizedDescription)")
|
|
6101
|
+
return
|
|
6102
|
+
}
|
|
6103
|
+
|
|
6104
|
+
guard let data = data else { return }
|
|
6105
|
+
|
|
6106
|
+
do {
|
|
6107
|
+
if let jsonResponse = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
6108
|
+
let responseData = jsonResponse["data"] as? [String: Any] {
|
|
6109
|
+
|
|
6110
|
+
// Extract required data
|
|
6111
|
+
let amount = responseData["amount"] as? String ?? ""
|
|
6112
|
+
let dateCreated = responseData["date_created"] as? String ?? ""
|
|
6113
|
+
let status = responseData["status"] as? String ?? ""
|
|
6114
|
+
let transactionId = responseData["transaction_id"] as? String ?? ""
|
|
6115
|
+
|
|
6116
|
+
print("Amount: \(amount), Date Created: \(dateCreated), Status: \(status), Transaction ID: \(transactionId)")
|
|
6117
|
+
|
|
6118
|
+
// Check if the status is "completed"
|
|
6119
|
+
if status.lowercased() == "completed" {
|
|
6120
|
+
DispatchQueue.main.async {
|
|
6121
|
+
// Navigate to PaymentDoneVC and pass data
|
|
6122
|
+
self.navigateToPaymentDoneVC(amount: amount, dateCreated: dateCreated, transactionId: transactionId)
|
|
6123
|
+
}
|
|
6124
|
+
}
|
|
6125
|
+
}
|
|
6126
|
+
} catch {
|
|
6127
|
+
print("Failed to parse response: \(error.localizedDescription)")
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
task.resume()
|
|
6131
|
+
}
|
|
6132
|
+
|
|
6133
|
+
// MARK: - 3DS Functionality
|
|
6134
|
+
|
|
6135
|
+
// MARK: - Credit Card Charge Api If Billing info is nil and Without Login.
|
|
6136
|
+
func threeDSecurePaymentApi() {
|
|
6137
|
+
showLoadingIndicator()
|
|
6138
|
+
|
|
6139
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecure.path()
|
|
6140
|
+
|
|
6141
|
+
guard let serviceURL = URL(string: fullURL) else {
|
|
6142
|
+
print("Invalid URL")
|
|
6143
|
+
hideLoadingIndicator()
|
|
6144
|
+
return
|
|
6145
|
+
}
|
|
6146
|
+
|
|
6147
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
6148
|
+
uRLRequest.httpMethod = "POST"
|
|
6149
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
6150
|
+
|
|
6151
|
+
let token = UserStoreSingleton.shared.clientToken
|
|
6152
|
+
print("Setting clientToken header: \(token ?? "None")")
|
|
6153
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
5114
6154
|
|
|
5115
6155
|
// Add API headers
|
|
5116
6156
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
5117
6157
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
5118
|
-
|
|
5119
|
-
|
|
6158
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
6159
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
5120
6160
|
}
|
|
5121
6161
|
|
|
5122
|
-
|
|
5123
|
-
"name":
|
|
6162
|
+
var params: [String: Any] = [
|
|
6163
|
+
"name": cardNameTextField.text,
|
|
5124
6164
|
"email": UserStoreSingleton.shared.merchantEmail ?? "",
|
|
5125
|
-
"
|
|
5126
|
-
"
|
|
5127
|
-
"
|
|
5128
|
-
"
|
|
5129
|
-
"
|
|
5130
|
-
"
|
|
6165
|
+
"card_number": cardNumberTextField.text.replacingOccurrences(of: " ", with: ""),
|
|
6166
|
+
"cardholder_name": cardNameTextField.text,
|
|
6167
|
+
"exp_month": cardExpiryTextField.text.components(separatedBy: "/").first ?? "",
|
|
6168
|
+
"exp_year": cardExpiryTextField.text.components(separatedBy: "/").last ?? "",
|
|
6169
|
+
"cvc": cardCvvTextField.text,
|
|
6170
|
+
"description": "payment checkout",
|
|
6171
|
+
"currency": "usd",
|
|
6172
|
+
"payment_method": "card",
|
|
6173
|
+
"tokenize": request.tokenOnly ?? false,
|
|
5131
6174
|
]
|
|
5132
6175
|
|
|
5133
|
-
|
|
6176
|
+
// Add these if recurring is enabled
|
|
6177
|
+
if let req = request, req.is_recurring == true {
|
|
6178
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
6179
|
+
// Only send start_date if type is .custom and field is not empty
|
|
6180
|
+
if let startDateText = txtFieldStartDateCard?.text, !startDateText.isEmpty {
|
|
6181
|
+
let inputFormatter = DateFormatter()
|
|
6182
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
6183
|
+
|
|
6184
|
+
let outputFormatter = DateFormatter()
|
|
6185
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
6186
|
+
|
|
6187
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
6188
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
6189
|
+
params["start_date"] = apiFormattedDate
|
|
6190
|
+
} else {
|
|
6191
|
+
print("Invalid date format in startDateText")
|
|
6192
|
+
}
|
|
6193
|
+
}
|
|
6194
|
+
}
|
|
6195
|
+
|
|
6196
|
+
params["interval"] = txtFieldChosePlanCard.text.lowercased()
|
|
6197
|
+
}
|
|
5134
6198
|
|
|
5135
6199
|
do {
|
|
5136
6200
|
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
5137
|
-
|
|
6201
|
+
uRLRequest.httpBody = jsonData
|
|
5138
6202
|
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
5139
6203
|
print("JSON Payload: \(jsonString)")
|
|
5140
6204
|
}
|
|
@@ -5145,7 +6209,7 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
5145
6209
|
}
|
|
5146
6210
|
|
|
5147
6211
|
let session = URLSession.shared
|
|
5148
|
-
let task = session.dataTask(with:
|
|
6212
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
5149
6213
|
|
|
5150
6214
|
DispatchQueue.main.async {
|
|
5151
6215
|
self.hideLoadingIndicator() // Stop loader when response is received
|
|
@@ -5172,40 +6236,16 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
5172
6236
|
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
5173
6237
|
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
5174
6238
|
} else {
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
self.chargeId = chargeId
|
|
5178
|
-
print("Charge ID: \(chargeId)")
|
|
5179
|
-
}
|
|
5180
|
-
|
|
5181
|
-
// Extract chain_invoice address and lightning invoice fields
|
|
5182
|
-
if let data = responseObject["data"] as? [String: Any] {
|
|
5183
|
-
self.chainInvoiceAddress = (data["chain_invoice"] as? [String: Any])?["address"] as? String
|
|
5184
|
-
self.lightningURI = (data["lightning_invoice"] as? [String: Any])?["payreq"] as? String
|
|
5185
|
-
|
|
5186
|
-
// Default to OnChain QR code initially
|
|
5187
|
-
DispatchQueue.main.async {
|
|
5188
|
-
if let address = self.chainInvoiceAddress {
|
|
5189
|
-
self.qrImageView.image = self.generateQRCode(from: address)
|
|
5190
|
-
self.lblBTCAddress.text = "BTC Address: \(address)"
|
|
5191
|
-
|
|
5192
|
-
// Extract the amount from the URI
|
|
5193
|
-
if let uri = data["uri"] as? String {
|
|
5194
|
-
if let amountString = self.extractAmount(from: uri) {
|
|
5195
|
-
// Update the label with the extracted amount
|
|
5196
|
-
DispatchQueue.main.async {
|
|
5197
|
-
self.lblAmmoutCrypto.text = "\(amountString) BTC = $\(self.amount ?? 0) USD"
|
|
5198
|
-
self.lblCryptoAmmountViewTryAgain.text = "\(amountString) BTC = $\(self.amount ?? 0) USD"
|
|
5199
|
-
}
|
|
5200
|
-
}
|
|
5201
|
-
}
|
|
5202
|
-
}
|
|
6239
|
+
DispatchQueue.main.async {
|
|
6240
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
|
|
5203
6241
|
|
|
5204
|
-
|
|
5205
|
-
|
|
6242
|
+
let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
|
|
6243
|
+
paymentDoneVC.redirectURL = urlString
|
|
6244
|
+
paymentDoneVC.chargeData = responseObject
|
|
6245
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
6246
|
+
|
|
6247
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
5206
6248
|
}
|
|
5207
|
-
} else {
|
|
5208
|
-
self.presentPaymentErrorVC(errorMessage: "Invalid response format")
|
|
5209
6249
|
}
|
|
5210
6250
|
}
|
|
5211
6251
|
} else {
|
|
@@ -5230,21 +6270,77 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
5230
6270
|
task.resume()
|
|
5231
6271
|
}
|
|
5232
6272
|
|
|
5233
|
-
//
|
|
5234
|
-
func
|
|
5235
|
-
|
|
5236
|
-
let
|
|
6273
|
+
// MARK: - Credit Card Charge Api If Billing info is nil from New Card.
|
|
6274
|
+
func threeDSecurePaymentNewCardApi(customerId: String) {
|
|
6275
|
+
// Get the text fields from the selected cell and trim whitespace
|
|
6276
|
+
let nameText = txtFieldNameOnCardNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
6277
|
+
let cvvText = txtFieldCVVNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
6278
|
+
let cardNumberText = (txtFieldCardNumberNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "").replacingOccurrences(of: " ", with: "")
|
|
6279
|
+
let expiryText = txtFieldExpiryDateNewCardView.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
|
5237
6280
|
|
|
5238
|
-
|
|
5239
|
-
|
|
6281
|
+
// Check if any field is empty
|
|
6282
|
+
if cardNumberText.isEmpty || expiryText.isEmpty || cvvText.isEmpty || nameText.isEmpty {
|
|
6283
|
+
let alert = UIAlertController(title: "Missing Information", message: "Please fill in all card details.", preferredStyle: .alert)
|
|
6284
|
+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
|
6285
|
+
self.present(alert, animated: true, completion: nil)
|
|
6286
|
+
return
|
|
5240
6287
|
}
|
|
5241
|
-
return nil
|
|
5242
|
-
}
|
|
5243
|
-
|
|
5244
|
-
//MARK: - GET Crypto Payment Info API
|
|
5245
|
-
func getCryptoPaymentInfoApi(chargeId: String) {
|
|
5246
6288
|
|
|
5247
|
-
|
|
6289
|
+
// Validate expiry date format
|
|
6290
|
+
let expiryDateFormat = "MM/yyyy"
|
|
6291
|
+
let dateFormatter = DateFormatter()
|
|
6292
|
+
dateFormatter.dateFormat = expiryDateFormat
|
|
6293
|
+
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
|
|
6294
|
+
|
|
6295
|
+
// Split the expiry date and validate its format
|
|
6296
|
+
let exp = expiryText.split(separator: "/")
|
|
6297
|
+
if exp.count != 2 || exp[0].count != 2 || exp[1].count != 4 {
|
|
6298
|
+
let alert = UIAlertController(title: "Invalid Expiry Date", message: "Please enter the expiry date in the format MM/yyyy.", preferredStyle: .alert)
|
|
6299
|
+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
|
6300
|
+
self.present(alert, animated: true, completion: nil)
|
|
6301
|
+
return
|
|
6302
|
+
}
|
|
6303
|
+
|
|
6304
|
+
// Check if the expiry date is valid
|
|
6305
|
+
if let expiryDateObj = dateFormatter.date(from: expiryText) {
|
|
6306
|
+
// Check if the expiry date is in the past
|
|
6307
|
+
let currentDate = Date()
|
|
6308
|
+
let calendar = Calendar.current
|
|
6309
|
+
let currentMonthYear = calendar.date(from: calendar.dateComponents([.year, .month], from: currentDate))
|
|
6310
|
+
if expiryDateObj < currentMonthYear! {
|
|
6311
|
+
let alert = UIAlertController(title: "Expired Card", message: "The expiry date cannot be in the past. Please enter a valid expiry date.", preferredStyle: .alert)
|
|
6312
|
+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
|
6313
|
+
self.present(alert, animated: true, completion: nil)
|
|
6314
|
+
return
|
|
6315
|
+
}
|
|
6316
|
+
} else {
|
|
6317
|
+
let alert = UIAlertController(title: "Invalid Expiry Date", message: "Please enter the expiry date in the format MM/yyyy.", preferredStyle: .alert)
|
|
6318
|
+
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
|
6319
|
+
self.present(alert, animated: true, completion: nil)
|
|
6320
|
+
return
|
|
6321
|
+
}
|
|
6322
|
+
|
|
6323
|
+
// Recurring Plan + Date Validation (only if is_recurring is true)
|
|
6324
|
+
if let req = self.request, req.is_recurring == true {
|
|
6325
|
+
let planText = txtFieldSelectPlanNewCardView.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
6326
|
+
let dateText = txtFieldSelectDateNewCardView.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
6327
|
+
|
|
6328
|
+
if planText.isEmpty {
|
|
6329
|
+
self.showAlert(title: "Missing Information", message: "Please choose your plan.")
|
|
6330
|
+
return
|
|
6331
|
+
}
|
|
6332
|
+
|
|
6333
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
6334
|
+
if dateText.isEmpty {
|
|
6335
|
+
self.showAlert(title: "Missing Information", message: "Please select a plan start date.")
|
|
6336
|
+
return
|
|
6337
|
+
}
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
6340
|
+
|
|
6341
|
+
showLoadingIndicator()
|
|
6342
|
+
|
|
6343
|
+
let fullURL = EnvironmentConfig.baseURL + EnvironmentConfig.Endpoints.threeDSecure.path()
|
|
5248
6344
|
|
|
5249
6345
|
guard let serviceURL = URL(string: fullURL) else {
|
|
5250
6346
|
print("Invalid URL")
|
|
@@ -5252,54 +6348,133 @@ class PaymentInfoVC: BaseVC, BillingInfoVCDelegate {
|
|
|
5252
6348
|
return
|
|
5253
6349
|
}
|
|
5254
6350
|
|
|
5255
|
-
var
|
|
5256
|
-
|
|
5257
|
-
|
|
6351
|
+
var uRLRequest = URLRequest(url: serviceURL)
|
|
6352
|
+
uRLRequest.httpMethod = "POST"
|
|
6353
|
+
uRLRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
5258
6354
|
|
|
5259
6355
|
let token = UserStoreSingleton.shared.clientToken
|
|
5260
6356
|
print("Setting clientToken header: \(token ?? "None")")
|
|
5261
|
-
|
|
6357
|
+
uRLRequest.addValue(token ?? "", forHTTPHeaderField: "Client-Token")
|
|
5262
6358
|
|
|
5263
6359
|
// Add API headers
|
|
5264
6360
|
if let apiKey = EnvironmentConfig.apiKey,
|
|
5265
6361
|
let apiSecret = EnvironmentConfig.apiSecret {
|
|
5266
|
-
|
|
5267
|
-
|
|
6362
|
+
uRLRequest.addValue(apiKey, forHTTPHeaderField: "x-api-key")
|
|
6363
|
+
uRLRequest.addValue(apiSecret, forHTTPHeaderField: "x-api-secret")
|
|
6364
|
+
}
|
|
6365
|
+
|
|
6366
|
+
var params: [String: Any] = [
|
|
6367
|
+
"name": nameText,
|
|
6368
|
+
"email": txtFieldEmail.text ?? "",
|
|
6369
|
+
"card_number": cardNumberText,
|
|
6370
|
+
"cardholder_name": nameText,
|
|
6371
|
+
"exp_month": expiryText.components(separatedBy: "/").first ?? "",
|
|
6372
|
+
"exp_year": expiryText.components(separatedBy: "/").last ?? "",
|
|
6373
|
+
"cvc": cvvText,
|
|
6374
|
+
"description": "payment checkout",
|
|
6375
|
+
"currency": "usd",
|
|
6376
|
+
"tokenize": request.tokenOnly ?? false,
|
|
6377
|
+
"customer_id": customerId,
|
|
6378
|
+
"save_card": isSavedNewCardForFuture ? 1 : 0,
|
|
6379
|
+
]
|
|
6380
|
+
|
|
6381
|
+
// Add is_default parameter if save_card is 1
|
|
6382
|
+
if isSavedNewCardForFuture {
|
|
6383
|
+
params["is_default"] = "1"
|
|
6384
|
+
}
|
|
6385
|
+
|
|
6386
|
+
// Add these if recurring is enabled
|
|
6387
|
+
if let req = request, req.is_recurring == true {
|
|
6388
|
+
if let recurringType = req.recurringStartDateType, recurringType == .custom {
|
|
6389
|
+
// Only send start_date if type is .custom and field is not empty
|
|
6390
|
+
if let startDateText = txtFieldSelectDateNewCardView?.text, !startDateText.isEmpty {
|
|
6391
|
+
let inputFormatter = DateFormatter()
|
|
6392
|
+
inputFormatter.dateFormat = "dd/MM/yyyy"
|
|
6393
|
+
|
|
6394
|
+
let outputFormatter = DateFormatter()
|
|
6395
|
+
outputFormatter.dateFormat = "MM/dd/yyyy"
|
|
6396
|
+
|
|
6397
|
+
if let date = inputFormatter.date(from: startDateText) {
|
|
6398
|
+
let apiFormattedDate = outputFormatter.string(from: date)
|
|
6399
|
+
params["start_date"] = apiFormattedDate
|
|
6400
|
+
} else {
|
|
6401
|
+
print("Invalid date format in startDateText")
|
|
6402
|
+
}
|
|
6403
|
+
}
|
|
6404
|
+
}
|
|
6405
|
+
|
|
6406
|
+
params["interval"] = txtFieldSelectPlanNewCardView.text.lowercased()
|
|
6407
|
+
}
|
|
6408
|
+
|
|
6409
|
+
do {
|
|
6410
|
+
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
|
|
6411
|
+
uRLRequest.httpBody = jsonData
|
|
6412
|
+
if let jsonString = String(data: jsonData, encoding: .utf8) {
|
|
6413
|
+
print("JSON Payload: \(jsonString)")
|
|
6414
|
+
}
|
|
6415
|
+
} catch let error {
|
|
6416
|
+
print("Error creating JSON data: \(error)")
|
|
6417
|
+
hideLoadingIndicator()
|
|
6418
|
+
return
|
|
5268
6419
|
}
|
|
5269
6420
|
|
|
5270
6421
|
let session = URLSession.shared
|
|
5271
|
-
let task = session.dataTask(with:
|
|
5272
|
-
|
|
6422
|
+
let task = session.dataTask(with: uRLRequest) { (serviceData, serviceResponse, error) in
|
|
6423
|
+
|
|
6424
|
+
DispatchQueue.main.async {
|
|
6425
|
+
self.hideLoadingIndicator() // Stop loader when response is received
|
|
6426
|
+
}
|
|
5273
6427
|
|
|
5274
6428
|
if let error = error {
|
|
5275
6429
|
print("Error: \(error.localizedDescription)")
|
|
5276
6430
|
return
|
|
5277
6431
|
}
|
|
5278
6432
|
|
|
5279
|
-
guard let
|
|
6433
|
+
guard let httpResponse = serviceResponse as? HTTPURLResponse else {
|
|
6434
|
+
print("Invalid response")
|
|
6435
|
+
return
|
|
6436
|
+
}
|
|
5280
6437
|
|
|
5281
|
-
|
|
5282
|
-
if let
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
6438
|
+
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 {
|
|
6439
|
+
if let data = serviceData {
|
|
6440
|
+
do {
|
|
6441
|
+
if let responseObject = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
6442
|
+
print("Response Data: \(responseObject)")
|
|
6443
|
+
|
|
6444
|
+
// Check if status is 0 and handle the error
|
|
6445
|
+
if let status = responseObject["status"] as? Int, status == 0 {
|
|
6446
|
+
let errorMessage = responseObject["message"] as? String ?? "Unknown error"
|
|
6447
|
+
self.presentPaymentErrorVC(errorMessage: errorMessage)
|
|
6448
|
+
} else {
|
|
6449
|
+
DispatchQueue.main.async {
|
|
6450
|
+
if let paymentDoneVC = self.storyboard?.instantiateViewController(withIdentifier: "ThreeDSecurePaymentDoneVC") as? ThreeDSecurePaymentDoneVC {
|
|
6451
|
+
|
|
6452
|
+
let urlString = responseObject["redirect_url"] as? String ?? responseObject["location_url"] as? String ?? ""
|
|
6453
|
+
paymentDoneVC.redirectURL = urlString
|
|
6454
|
+
paymentDoneVC.chargeData = responseObject
|
|
6455
|
+
paymentDoneVC.easyPayDelegate = self.easyPayDelegate
|
|
6456
|
+
|
|
6457
|
+
self.navigationController?.pushViewController(paymentDoneVC, animated: true)
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6461
|
+
} else {
|
|
6462
|
+
self.presentPaymentErrorVC(errorMessage: "Invalid JSON format")
|
|
5298
6463
|
}
|
|
6464
|
+
} catch let jsonError {
|
|
6465
|
+
self.presentPaymentErrorVC(errorMessage: "Error parsing JSON: \(jsonError)")
|
|
5299
6466
|
}
|
|
6467
|
+
} else {
|
|
6468
|
+
self.presentPaymentErrorVC(errorMessage: "No data received")
|
|
6469
|
+
}
|
|
6470
|
+
} else {
|
|
6471
|
+
if let data = serviceData,
|
|
6472
|
+
let responseObj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
|
|
6473
|
+
let message = responseObj["message"] as? String {
|
|
6474
|
+
self.presentPaymentErrorVC(errorMessage: message)
|
|
6475
|
+
} else {
|
|
6476
|
+
self.presentPaymentErrorVC(errorMessage: "HTTP Status Code: \(httpResponse.statusCode)")
|
|
5300
6477
|
}
|
|
5301
|
-
} catch {
|
|
5302
|
-
print("Failed to parse response: \(error.localizedDescription)")
|
|
5303
6478
|
}
|
|
5304
6479
|
}
|
|
5305
6480
|
task.resume()
|
|
@@ -5341,9 +6516,17 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
|
|
|
5341
6516
|
cell.lblPayment.textColor = uiColor
|
|
5342
6517
|
}
|
|
5343
6518
|
} else {
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
6519
|
+
if let primaryFontColor = UserStoreSingleton.shared.primary_font_col,
|
|
6520
|
+
let uiColor = UIColor(hex: primaryFontColor) {
|
|
6521
|
+
cell.vwMain.layer.borderColor = uiColor.cgColor
|
|
6522
|
+
cell.imgVwPayments.tintColor = uiColor
|
|
6523
|
+
cell.lblPayment.textColor = uiColor
|
|
6524
|
+
}
|
|
6525
|
+
else {
|
|
6526
|
+
cell.vwMain.layer.borderColor = UIColor.gray.cgColor
|
|
6527
|
+
cell.imgVwPayments.tintColor = .systemGray
|
|
6528
|
+
cell.lblPayment.textColor = .systemGray
|
|
6529
|
+
}
|
|
5347
6530
|
}
|
|
5348
6531
|
|
|
5349
6532
|
return cell
|
|
@@ -5446,9 +6629,11 @@ extension PaymentInfoVC: UICollectionViewDelegate, UICollectionViewDataSource, U
|
|
|
5446
6629
|
self.btnNext.isHidden = true
|
|
5447
6630
|
self.btnNextHeight.constant = 0
|
|
5448
6631
|
self.btnNextTopCon.constant = 0
|
|
5449
|
-
if self.request.
|
|
6632
|
+
if self.request.is_recurring == true {
|
|
5450
6633
|
self.txtFieldSelectPlanSingleSavedCard.isHidden = false
|
|
5451
|
-
|
|
6634
|
+
if request.recurringStartDateType == .custom {
|
|
6635
|
+
self.txtFieldSelectDateSingleSavedCard.isHidden = false
|
|
6636
|
+
}
|
|
5452
6637
|
}
|
|
5453
6638
|
else {
|
|
5454
6639
|
self.txtFieldSelectPlanSingleSavedCard.isHidden = true
|
|
@@ -5620,10 +6805,10 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
|
|
|
5620
6805
|
cell.btnRemoveBankAccount.removeTarget(nil, action: nil, for: .allEvents)
|
|
5621
6806
|
|
|
5622
6807
|
if indexPath.row == bankAccounts.count {
|
|
5623
|
-
print("Showing 'Add New Account' cell")
|
|
6808
|
+
// print("Showing 'Add New Account' cell")
|
|
5624
6809
|
configureAddNewAccountCell(cell)
|
|
5625
6810
|
} else {
|
|
5626
|
-
print("Configuring saved account cell at row: \(indexPath.row)")
|
|
6811
|
+
// print("Configuring saved account cell at row: \(indexPath.row)")
|
|
5627
6812
|
configureSavedAccountCell(cell, at: indexPath)
|
|
5628
6813
|
}
|
|
5629
6814
|
|
|
@@ -5962,11 +7147,9 @@ extension PaymentInfoVC: UITableViewDelegate, UITableViewDataSource {
|
|
|
5962
7147
|
if isSelectForPayBank {
|
|
5963
7148
|
viewTermAndConditionsSingleAccountView.isHidden = false
|
|
5964
7149
|
btnPayNowSingleAccountView.isHidden = false
|
|
5965
|
-
// viewSingleAccountViewHeight.constant = 170
|
|
5966
7150
|
} else {
|
|
5967
7151
|
viewTermAndConditionsSingleAccountView.isHidden = true
|
|
5968
7152
|
btnPayNowSingleAccountView.isHidden = true
|
|
5969
|
-
// viewSingleAccountViewHeight.constant = 68
|
|
5970
7153
|
btnNext.isHidden = true
|
|
5971
7154
|
btnNextHeight.constant = 0
|
|
5972
7155
|
btnNextTopCon.constant = 0
|
|
@@ -6069,7 +7252,7 @@ extension PaymentInfoVC: UITextFieldDelegate {
|
|
|
6069
7252
|
}
|
|
6070
7253
|
|
|
6071
7254
|
// Handle card CVV input for both old and new card fields
|
|
6072
|
-
else if textField == cardCvvTextField.textField || textField == txtFieldCVVNewCardView || textField == txtFieldCVVUpdateCardView {
|
|
7255
|
+
else if textField == cardCvvTextField.textField || textField == txtFieldCVVNewCardView || textField == txtFieldCVVUpdateCardView || textField == txtFieldCVVSingleSavedCard {
|
|
6073
7256
|
let maxLength = 3
|
|
6074
7257
|
let currentString = (textField.text ?? "") as NSString
|
|
6075
7258
|
let newString = currentString.replacingCharacters(in: range, with: string)
|
|
@@ -6675,11 +7858,9 @@ extension PaymentInfoVC: UITextFieldDelegate {
|
|
|
6675
7858
|
//
|
|
6676
7859
|
// func blinkCardOverlayViewControllerDidFinishEditing(_ blinkCardOverlayViewController: MBCBlinkCardOverlayViewController, editResult: MBCBlinkCardEditResult) {
|
|
6677
7860
|
// blinkCardOverlayViewController.recognizerRunnerViewController?.pauseScanning()
|
|
6678
|
-
//
|
|
6679
7861
|
// /** Needs to be called on main thread beacuse everything prior is on background thread */
|
|
6680
7862
|
// DispatchQueue.main.async {
|
|
6681
7863
|
// // present the alert view with scanned results
|
|
6682
|
-
//
|
|
6683
7864
|
// let alertController: UIAlertController = UIAlertController.init(title: "BlinkCard Edit Results", message: editResult.description, preferredStyle: .alert)
|
|
6684
7865
|
//
|
|
6685
7866
|
// let okAction: UIAlertAction = UIAlertAction.init(title: "OK", style: .default,
|
|
@@ -6692,7 +7873,7 @@ extension PaymentInfoVC: UITextFieldDelegate {
|
|
|
6692
7873
|
// }
|
|
6693
7874
|
//
|
|
6694
7875
|
//}
|
|
6695
|
-
|
|
7876
|
+
|
|
6696
7877
|
//extension PaymentInfoVC: CustomOverlayDelegate {
|
|
6697
7878
|
// func didFinishScanningCard(cardNumber: String, expiryDate: String, cvv: String, nameOnCard: String) {
|
|
6698
7879
|
// // Update the text fields with scanned details
|