fix online payment

online_payment
Raz 7 months ago
parent c1ac3ed998
commit 430af36802
  1. 3
      PadelClub/Data/CustomUser.swift
  2. 44
      PadelClub/Utils/Network/ConfigurationService.swift
  3. 20
      PadelClub/Views/Navigation/Agenda/EventListView.swift
  4. 19
      PadelClub/Views/Navigation/Umpire/UmpireView.swift
  5. 20
      PadelClub/Views/Shared/OnlineWaitingListFaqSheetView.swift
  6. 68
      PadelClub/Views/Shared/PaymentInfoSheetView.swift
  7. 10
      PadelClub/Views/Tournament/Screen/RegistrationSetupView.swift

@ -20,6 +20,9 @@ enum RegistrationPaymentMode: Int, Codable {
case noFee = 2 case noFee = 2
case stripe = 3 case stripe = 3
static let stripeFixedFee = 0.25 // Fixed fee in euros
static let stripePercentageFee = 0.014 // 1.4%
func canEnableOnlinePayment() -> Bool { func canEnableOnlinePayment() -> Bool {
switch self { switch self {
case .disabled: case .disabled:

@ -28,13 +28,13 @@ struct TimeToConfirmConfig: Codable {
let timeProximityRules: [String: Int] let timeProximityRules: [String: Int]
let waitingListRules: [String: Int] let waitingListRules: [String: Int]
let businessRules: BusinessRules let businessRules: BusinessRules
let urgencyOverride: UrgencyOverride let minimumResponseTime: Int
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case timeProximityRules = "time_proximity_rules" case timeProximityRules = "time_proximity_rules"
case waitingListRules = "waiting_list_rules" case waitingListRules = "waiting_list_rules"
case businessRules = "business_rules" case businessRules = "business_rules"
case urgencyOverride = "urgency_override" case minimumResponseTime = "minimum_response_time"
} }
// Default configuration // Default configuration
@ -54,61 +54,25 @@ struct TimeToConfirmConfig: Codable {
businessRules: BusinessRules( businessRules: BusinessRules(
hours: Hours( hours: Hours(
start: 8, start: 8,
end: 21, end: 21
defaultConfirmationHour: 8
),
days: Days(
workingDays: [0, 1, 2, 3, 4, 5, 6],
weekend: []
) )
), ),
urgencyOverride: UrgencyOverride( minimumResponseTime: 30
thresholds: [
"24": true,
"12": true
],
minimumResponseTime: 30
)
) )
} }
struct BusinessRules: Codable { struct BusinessRules: Codable {
let hours: Hours let hours: Hours
let days: Days
} }
struct Hours: Codable { struct Hours: Codable {
let start: Int let start: Int
let end: Int let end: Int
let defaultConfirmationHour: Int
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {
case start case start
case end 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"
} }
} }

@ -206,17 +206,29 @@ struct EventListView: View {
if dataStore.user.canEnableOnlinePayment() { if dataStore.user.canEnableOnlinePayment() {
Menu { Menu {
Button { let templateTournament = Tournament.getTemplateTournament()
if 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 pcTournaments.forEach { tournament in
if tournament.onlineRegistrationCanBeEnabled() { if tournament.onlineRegistrationCanBeEnabled() {
tournament.setupRegistrationSettings(templateTournament: templateTournament) tournament.setupRegistrationSettings(templateTournament: templateTournament)
} }
} }
dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments) dataStore.tournaments.addOrUpdate(contentOfs: pcTournaments)
} label: {
Text("Utiliser ces réglages par défaut")
} }
} label: {
Text("Utiliser les réglages par défaut")
} }
} label: { } label: {
Text("Inscription et paiement en ligne") Text("Inscription et paiement en ligne")

@ -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 { Section {
@Bindable var user = dataStore.user @Bindable var user = dataStore.user
Toggle(isOn: $user.disableRankingFederalRuling) { Toggle(isOn: $user.disableRankingFederalRuling) {

@ -32,9 +32,8 @@ struct OnlineWaitingListFaqSheetView: View {
// Get waiting list rules // Get waiting list rules
let waitingRules = timeToConfirmConfig.waitingListRules let waitingRules = timeToConfirmConfig.waitingListRules
// Get urgency thresholds // Get minimum response time
let urgencyThresholds = timeToConfirmConfig.urgencyOverride.thresholds let minResponseTime = timeToConfirmConfig.minimumResponseTime
let minResponseTime = timeToConfirmConfig.urgencyOverride.minimumResponseTime
return """ return """
FAQ pour les Arbitres - Confirmation des Équipes FAQ pour les Arbitres - Confirmation des Équipes
@ -53,23 +52,20 @@ struct OnlineWaitingListFaqSheetView: View {
- Tournoi dans plus de 72h \(formatMinutes(defaultTime)) - Tournoi dans plus de 72h \(formatMinutes(defaultTime))
Ces délais peuvent être raccourcis en fonction du nombre d'équipes en liste d'attente : 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["30"] ?? 30)+ équipes en attente \(formatMinutes(30))
- \(waitingRules["20"] ?? 20)+ équipes en attente \(formatMinutes(waitingRules["20"] ?? 60)) - \(waitingRules["20"] ?? 20)+ équipes en attente \(formatMinutes(60))
- \(waitingRules["10"] ?? 10)+ équipes en attente \(formatMinutes(waitingRules["10"] ?? 120)) - \(waitingRules["10"] ?? 10)+ équipes en attente \(formatMinutes(120))
Y a-t-il des exceptions à ces règles ? Y a-t-il des exceptions à ces règles ?
Oui, dans les situations urgentes : 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 24h, les heures ouvrables s'étendent de 7h à 22h
- 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 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 ? Comment les délais sont-ils arrondis ?
Les délais sont toujours arrondis à la demi-heure supérieure pour plus de simplicité. 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.
""" """
} }

@ -9,43 +9,51 @@ import SwiftUI
struct PaymentInfoSheetView: View { struct PaymentInfoSheetView: View {
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
let paymentInfoText: String = let paymentConfig: PaymentConfig
"""
Comment fonctionnent les paiements 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 : 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)))
Options de paiement : return """
- Le paiement en ligne est activé à la discrétion de l'organisateur Comment fonctionnent les paiements en ligne ?
- 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
Remboursements : Les paiements en ligne permettent aux joueurs de régler les frais de tournoi directement via la plateforme. Voici les informations importantes à connaître :
- 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
Commissions et frais : Options de paiement :
- Padel Club prélève une commission de 0,75% sur chaque transaction - Le paiement en ligne est activé à la discrétion de l'organisateur
- Cette commission couvre les frais de service et de maintenance de la plateforme - L'organisateur peut rendre le paiement en ligne obligatoire ou optionnel
- Des frais supplémentaires de Stripe peuvent s'appliquer (environ 1,4% + 0,25 par transaction) - Si le paiement n'est pas obligatoire, il est possible de s'inscrire sans payer immédiatement
- Le montant total des frais est indiqué clairement avant validation du paiement - Tous les paiements sont traités via Stripe, une plateforme sécurisée de paiement en ligne
Exigences pour les organisateurs : Remboursements :
- L'organisateur doit avoir un compte Stripe valide pour recevoir les paiements - Les remboursements peuvent être activés ou désactivés par l'organisateur
- Le compte Stripe doit être vérifié et connecté à Padel Club - Si activés, une date limite de remboursement peut être définie
- Sans compte Stripe connecté, l'option de paiement en ligne ne peut pas être activée - Aucun remboursement n'est possible après cette date limite
- Les fonds sont directement versés sur le compte bancaire associé au compte Stripe de l'organisateur - Les remboursements sont automatiquement traités via la même méthode de paiement utilisée
Sécurité : Commissions et frais :
- Toutes les transactions sont sécurisées et chiffrées - Padel Club prélève une commission de \(stripePlatformFee)% sur chaque transaction
- Padel Club ne stocke pas les informations de carte bancaire - Cette commission couvre les frais de service et de maintenance de la plateforme
- La conformité RGPD et PCI-DSS est assurée par Stripe - Des frais supplémentaires de Stripe s'appliquent (\(stripePercentageFee)% + \(stripeFixedFee) par transaction)
- Le montant total des frais est indiqué clairement avant validation du paiement
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 { var body: some View {
NavigationView { NavigationView {
ScrollView { ScrollView {

@ -177,7 +177,7 @@ struct RegistrationSetupView: View {
do { do {
self.timeToConfirmConfig = try await ConfigurationService.fetchTournamentConfig() self.timeToConfirmConfig = try await ConfigurationService.fetchTournamentConfig()
} catch { } catch {
print("Error fetching configuration: \(error)") print("Error fetching timeToConfirmConfig: \(error)")
} }
} }
} }
@ -290,7 +290,7 @@ struct RegistrationSetupView: View {
OnlineWaitingListFaqSheetView(timeToConfirmConfig: timeToConfirmConfig ?? TimeToConfirmConfig.defaultConfig) OnlineWaitingListFaqSheetView(timeToConfirmConfig: timeToConfirmConfig ?? TimeToConfirmConfig.defaultConfig)
} }
.sheet(isPresented: $showMorePaymentInfos) { .sheet(isPresented: $showMorePaymentInfos) {
PaymentInfoSheetView() PaymentInfoSheetView(paymentConfig: paymentConfig ?? PaymentConfig.defaultConfig)
} }
.toolbar(content: { .toolbar(content: {
@ -460,7 +460,7 @@ struct RegistrationSetupView: View {
do { do {
self.paymentConfig = try await ConfigurationService.fetchPaymentConfig() self.paymentConfig = try await ConfigurationService.fetchPaymentConfig()
} catch { } catch {
print("Error fetching configuration: \(error)") print("Error fetching paymentConfig: \(error)")
} }
} }
.onChange(of: [enableOnlinePayment, onlinePaymentIsMandatory, enableOnlinePaymentRefund]) { .onChange(of: [enableOnlinePayment, onlinePaymentIsMandatory, enableOnlinePaymentRefund]) {
@ -480,8 +480,8 @@ struct RegistrationSetupView: View {
if dataStore.user.registrationPaymentMode.requiresStripe() { if dataStore.user.registrationPaymentMode.requiresStripe() {
Section { Section {
let fixedFee = 0.25 // Fixed fee in euros let fixedFee = RegistrationPaymentMode.stripeFixedFee // Fixed fee in euros
let percentageFee = 0.012 // 1.2% let percentageFee = RegistrationPaymentMode.stripePercentageFee
let totalStripeFee = fixedFee + (entryFee * percentageFee) let totalStripeFee = fixedFee + (entryFee * percentageFee)
LabeledContent { LabeledContent {

Loading…
Cancel
Save