diff --git a/PadelClub/Data/CustomUser.swift b/PadelClub/Data/CustomUser.swift index 8a1be03..6b27a25 100644 --- a/PadelClub/Data/CustomUser.swift +++ b/PadelClub/Data/CustomUser.swift @@ -20,6 +20,9 @@ enum RegistrationPaymentMode: Int, Codable { case noFee = 2 case stripe = 3 + static let stripeFixedFee = 0.25 // Fixed fee in euros + static let stripePercentageFee = 0.014 // 1.4% + func canEnableOnlinePayment() -> Bool { switch self { case .disabled: diff --git a/PadelClub/Utils/Network/ConfigurationService.swift b/PadelClub/Utils/Network/ConfigurationService.swift index c012a8e..911c445 100644 --- a/PadelClub/Utils/Network/ConfigurationService.swift +++ b/PadelClub/Utils/Network/ConfigurationService.swift @@ -28,13 +28,13 @@ struct TimeToConfirmConfig: Codable { let timeProximityRules: [String: Int] let waitingListRules: [String: Int] let businessRules: BusinessRules - let urgencyOverride: UrgencyOverride - + let minimumResponseTime: Int + private enum CodingKeys: String, CodingKey { case timeProximityRules = "time_proximity_rules" case waitingListRules = "waiting_list_rules" case businessRules = "business_rules" - case urgencyOverride = "urgency_override" + case minimumResponseTime = "minimum_response_time" } // Default configuration @@ -54,61 +54,25 @@ struct TimeToConfirmConfig: Codable { businessRules: BusinessRules( hours: Hours( start: 8, - end: 21, - defaultConfirmationHour: 8 - ), - days: Days( - workingDays: [0, 1, 2, 3, 4, 5, 6], - weekend: [] + end: 21 ) ), - urgencyOverride: UrgencyOverride( - thresholds: [ - "24": true, - "12": true - ], - minimumResponseTime: 30 - ) + minimumResponseTime: 30 ) } struct BusinessRules: Codable { let hours: Hours - let days: Days } struct Hours: Codable { let start: Int let end: Int - let defaultConfirmationHour: Int private enum CodingKeys: String, CodingKey { case start case end - case defaultConfirmationHour = "default_confirmation_hour" - } - -} - -struct Days: Codable { - let workingDays: [Int] - let weekend: [Int] - - private enum CodingKeys: String, CodingKey { - case workingDays = "working_days" - case weekend - } - -} - -struct UrgencyOverride: Codable { - let thresholds: [String: Bool] - let minimumResponseTime: Int - - private enum CodingKeys: String, CodingKey { - case thresholds - case minimumResponseTime = "minimum_response_time" } } diff --git a/PadelClub/Views/Navigation/Agenda/EventListView.swift b/PadelClub/Views/Navigation/Agenda/EventListView.swift index c39e0c2..4fda5c0 100644 --- a/PadelClub/Views/Navigation/Agenda/EventListView.swift +++ b/PadelClub/Views/Navigation/Agenda/EventListView.swift @@ -206,17 +206,29 @@ struct EventListView: View { if dataStore.user.canEnableOnlinePayment() { Menu { - Button { - if let templateTournament = Tournament.getTemplateTournament() { + let templateTournament = Tournament.getTemplateTournament() + + if let templateTournament { + NavigationLink { + RegistrationSetupView(tournament: templateTournament) + } label: { + Text("Voir le tournoi référence") + } + } else { + Text("Aucun tournoi référence") + } + + if let templateTournament { + Button { pcTournaments.forEach { tournament in if tournament.onlineRegistrationCanBeEnabled() { tournament.setupRegistrationSettings(templateTournament: templateTournament) } } dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments) + } label: { + Text("Utiliser ces réglages par défaut") } - } label: { - Text("Utiliser les réglages par défaut") } } label: { Text("Inscription et paiement en ligne") diff --git a/PadelClub/Views/Navigation/Umpire/UmpireView.swift b/PadelClub/Views/Navigation/Umpire/UmpireView.swift index 0588566..ef144bb 100644 --- a/PadelClub/Views/Navigation/Umpire/UmpireView.swift +++ b/PadelClub/Views/Navigation/Umpire/UmpireView.swift @@ -177,6 +177,25 @@ struct UmpireView: View { } } + if dataStore.user.canEnableOnlinePayment() { + Section { + if let tournamentTemplate = Tournament.getTemplateTournament() { + NavigationLink { + RegistrationSetupView(tournament: tournamentTemplate) + } label: { + Text("Référence") + Text(tournamentTemplate.tournamentTitle()).foregroundStyle(.secondary) + } + } else { + Text("Aucun tournoi référence. Choisissez-en un dans la liste d'activité") + } + } header: { + Text("Inscription et paiement en ligne") + } footer: { + Text("Tournoi référence utilisé pour les réglages des inscriptions en ligne") + } + } + Section { @Bindable var user = dataStore.user Toggle(isOn: $user.disableRankingFederalRuling) { diff --git a/PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift b/PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift index ff2630a..d143d9b 100644 --- a/PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift +++ b/PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift @@ -32,9 +32,8 @@ struct OnlineWaitingListFaqSheetView: View { // Get waiting list rules let waitingRules = timeToConfirmConfig.waitingListRules - // Get urgency thresholds - let urgencyThresholds = timeToConfirmConfig.urgencyOverride.thresholds - let minResponseTime = timeToConfirmConfig.urgencyOverride.minimumResponseTime + // Get minimum response time + let minResponseTime = timeToConfirmConfig.minimumResponseTime return """ FAQ pour les Arbitres - Confirmation des Équipes @@ -53,23 +52,20 @@ struct OnlineWaitingListFaqSheetView: View { - Tournoi dans plus de 72h → \(formatMinutes(defaultTime)) Ces délais peuvent être raccourcis en fonction du nombre d'équipes en liste d'attente : - - \(waitingRules["30"] ?? 30)+ équipes en attente → \(formatMinutes(waitingRules["30"] ?? 30)) - - \(waitingRules["20"] ?? 20)+ équipes en attente → \(formatMinutes(waitingRules["20"] ?? 60)) - - \(waitingRules["10"] ?? 10)+ équipes en attente → \(formatMinutes(waitingRules["10"] ?? 120)) + - \(waitingRules["30"] ?? 30)+ équipes en attente → \(formatMinutes(30)) + - \(waitingRules["20"] ?? 20)+ équipes en attente → \(formatMinutes(60)) + - \(waitingRules["10"] ?? 10)+ équipes en attente → \(formatMinutes(120)) Y a-t-il des exceptions à ces règles ? Oui, dans les situations urgentes : - - Si le tournoi commence dans moins de 24h, les restrictions d'heures ouvrables sont ignorées - - Si le tournoi commence dans moins de 12h, toutes les restrictions sont assouplies avec un minimum de \(minResponseTime) minutes de délai + - Si le tournoi commence dans moins de 24h, les heures ouvrables s'étendent de 7h à 22h + - Si le tournoi commence dans moins de 12h, les heures ouvrables s'étendent de 6h à 1h du matin + - Un délai minimum de \(minResponseTime) minutes est toujours respecté Comment les délais sont-ils arrondis ? Les délais sont toujours arrondis à la demi-heure supérieure pour plus de simplicité. - - Que se passe-t-il si le délai tombe en dehors des heures ouvrables ? - - Si le délai calculé tombe en dehors des heures ouvrables (avant \(startHour)h ou après \(endHour)h), il est automatiquement reporté au jour ouvrable suivant à \(timeToConfirmConfig.businessRules.hours.defaultConfirmationHour)h du matin. """ } diff --git a/PadelClub/Views/Shared/PaymentInfoSheetView.swift b/PadelClub/Views/Shared/PaymentInfoSheetView.swift index 11e734f..1a79c5e 100644 --- a/PadelClub/Views/Shared/PaymentInfoSheetView.swift +++ b/PadelClub/Views/Shared/PaymentInfoSheetView.swift @@ -9,43 +9,51 @@ import SwiftUI struct PaymentInfoSheetView: View { @Environment(\.dismiss) private var dismiss - let paymentInfoText: String = - """ - Comment fonctionnent les paiements en ligne ? + let paymentConfig: PaymentConfig - Les paiements en ligne permettent aux joueurs de régler les frais de tournoi directement via la plateforme. Voici les informations importantes à connaître : + var paymentInfoText: String { + let stripePlatformFee = (paymentConfig.stripeFee * 100).formatted(.number.precision(.fractionLength(2))) + let stripePercentageFee = (RegistrationPaymentMode.stripePercentageFee * 100).formatted(.number.precision(.fractionLength(1))) + let stripeFixedFee = RegistrationPaymentMode.stripeFixedFee.formatted(.number.precision(.fractionLength(2))) + + return """ + Comment fonctionnent les paiements en ligne ? - Options de paiement : - - Le paiement en ligne est activé à la discrétion de l'organisateur - - L'organisateur peut rendre le paiement en ligne obligatoire ou optionnel - - Si le paiement n'est pas obligatoire, il est possible de s'inscrire sans payer immédiatement - - Tous les paiements sont traités via Stripe, une plateforme sécurisée de paiement en ligne + Les paiements en ligne permettent aux joueurs de régler les frais de tournoi directement via la plateforme. Voici les informations importantes à connaître : - Remboursements : - - Les remboursements peuvent être activés ou désactivés par l'organisateur - - Si activés, une date limite de remboursement peut être définie - - Aucun remboursement n'est possible après cette date limite - - Les remboursements sont automatiquement traités via la même méthode de paiement utilisée + Options de paiement : + - Le paiement en ligne est activé à la discrétion de l'organisateur + - L'organisateur peut rendre le paiement en ligne obligatoire ou optionnel + - Si le paiement n'est pas obligatoire, il est possible de s'inscrire sans payer immédiatement + - Tous les paiements sont traités via Stripe, une plateforme sécurisée de paiement en ligne - Commissions et frais : - - Padel Club prélève une commission de 0,75% sur chaque transaction - - Cette commission couvre les frais de service et de maintenance de la plateforme - - Des frais supplémentaires de Stripe peuvent s'appliquer (environ 1,4% + 0,25€ par transaction) - - Le montant total des frais est indiqué clairement avant validation du paiement + Remboursements : + - Les remboursements peuvent être activés ou désactivés par l'organisateur + - Si activés, une date limite de remboursement peut être définie + - Aucun remboursement n'est possible après cette date limite + - Les remboursements sont automatiquement traités via la même méthode de paiement utilisée - Exigences pour les organisateurs : - - L'organisateur doit avoir un compte Stripe valide pour recevoir les paiements - - Le compte Stripe doit être vérifié et connecté à Padel Club - - Sans compte Stripe connecté, l'option de paiement en ligne ne peut pas être activée - - Les fonds sont directement versés sur le compte bancaire associé au compte Stripe de l'organisateur + Commissions et frais : + - Padel Club prélève une commission de \(stripePlatformFee)% sur chaque transaction + - Cette commission couvre les frais de service et de maintenance de la plateforme + - Des frais supplémentaires de Stripe s'appliquent (\(stripePercentageFee)% + \(stripeFixedFee)€ par transaction) + - Le montant total des frais est indiqué clairement avant validation du paiement - Sécurité : - - Toutes les transactions sont sécurisées et chiffrées - - Padel Club ne stocke pas les informations de carte bancaire - - La conformité RGPD et PCI-DSS est assurée par Stripe + Exigences pour les organisateurs : + - L'organisateur doit avoir un compte Stripe valide pour recevoir les paiements + - Le compte Stripe doit être vérifié et connecté à Padel Club + - Sans compte Stripe connecté, l'option de paiement en ligne ne peut pas être activée + - Les fonds sont directement versés sur le compte bancaire associé au compte Stripe de l'organisateur + + Sécurité : + - Toutes les transactions sont sécurisées et chiffrées + - Padel Club ne stocke pas les informations de carte bancaire + - La conformité RGPD et PCI-DSS est assurée par Stripe + + En cas de problème avec un paiement, veuillez contacter l'organisateur du tournoi ou le support Padel Club. + """ + } - En cas de problème avec un paiement, veuillez contacter l'organisateur du tournoi ou le support Padel Club. - """ var body: some View { NavigationView { ScrollView { diff --git a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift index 3c89a32..a724c65 100644 --- a/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift +++ b/PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift @@ -177,7 +177,7 @@ struct RegistrationSetupView: View { do { self.timeToConfirmConfig = try await ConfigurationService.fetchTournamentConfig() } catch { - print("Error fetching configuration: \(error)") + print("Error fetching timeToConfirmConfig: \(error)") } } } @@ -290,7 +290,7 @@ struct RegistrationSetupView: View { OnlineWaitingListFaqSheetView(timeToConfirmConfig: timeToConfirmConfig ?? TimeToConfirmConfig.defaultConfig) } .sheet(isPresented: $showMorePaymentInfos) { - PaymentInfoSheetView() + PaymentInfoSheetView(paymentConfig: paymentConfig ?? PaymentConfig.defaultConfig) } .toolbar(content: { @@ -460,7 +460,7 @@ struct RegistrationSetupView: View { do { self.paymentConfig = try await ConfigurationService.fetchPaymentConfig() } catch { - print("Error fetching configuration: \(error)") + print("Error fetching paymentConfig: \(error)") } } .onChange(of: [enableOnlinePayment, onlinePaymentIsMandatory, enableOnlinePaymentRefund]) { @@ -480,8 +480,8 @@ struct RegistrationSetupView: View { if dataStore.user.registrationPaymentMode.requiresStripe() { Section { - let fixedFee = 0.25 // Fixed fee in euros - let percentageFee = 0.012 // 1.2% + let fixedFee = RegistrationPaymentMode.stripeFixedFee // Fixed fee in euros + let percentageFee = RegistrationPaymentMode.stripePercentageFee let totalStripeFee = fixedFee + (entryFee * percentageFee) LabeledContent {