fix payment stuff

sync3
Razmig Sarkissian 1 month ago
parent 4acb1e8ea4
commit d1435688eb
  1. 24
      PadelClub.xcodeproj/project.pbxproj
  2. 41
      PadelClub/Utils/Network/PaymentService.swift
  3. 4
      PadelClub/Views/Player/PlayerDetailView.swift
  4. 4
      PadelClub/Views/Team/EditingTeamView.swift
  5. 51
      PadelClub/Views/Team/PaymentRequestButton.swift
  6. 24
      PadelClub/Views/Team/PaymentService.swift
  7. 99
      PadelClub/Views/Tournament/Screen/InscriptionManagerView.swift

@ -154,6 +154,12 @@
FF2B51612C7E302C00FFF126 /* local.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = FF2B51602C7E302C00FFF126 /* local.sqlite */; };
FF2B6F5E2C036A1500835EE7 /* EventLinksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */; };
FF2EFBF02BDE295E0049CE3B /* SendToAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */; };
FF30ACED2E8D700B008B6006 /* PaymentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACEC2E8D700B008B6006 /* PaymentService.swift */; };
FF30ACEE2E8D700B008B6006 /* PaymentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACEC2E8D700B008B6006 /* PaymentService.swift */; };
FF30ACEF2E8D700B008B6006 /* PaymentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACEC2E8D700B008B6006 /* PaymentService.swift */; };
FF30ACF12E8D7078008B6006 /* PaymentRequestButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */; };
FF30ACF22E8D7078008B6006 /* PaymentRequestButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */; };
FF30ACF32E8D7078008B6006 /* PaymentRequestButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */; };
FF3795622B9396D0004EA093 /* PadelClubApp.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FF3795602B9396D0004EA093 /* PadelClubApp.xcdatamodeld */; };
FF3795662B9399AA004EA093 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3795652B9399AA004EA093 /* Persistence.swift */; };
FF39B6152DC8825E004E10CE /* PadelClubData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C49770202DC25A23005CD239 /* PadelClubData.framework */; };
@ -717,6 +723,9 @@
FFA97C9E2E8A7C080089EA22 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97C9D2E8A7C080089EA22 /* View+Extensions.swift */; };
FFA97C9F2E8A7C080089EA22 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97C9D2E8A7C080089EA22 /* View+Extensions.swift */; };
FFA97CA02E8A7C080089EA22 /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97C9D2E8A7C080089EA22 /* View+Extensions.swift */; };
FFA97CA22E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */; };
FFA97CA32E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */; };
FFA97CA42E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */; };
FFB0FF672E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; };
FFB0FF682E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; };
FFB0FF692E81B671009EDEAC /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB0FF662E81B671009EDEAC /* OnboardingView.swift */; };
@ -1019,6 +1028,8 @@
FF2B51622C7F073100FFF126 /* Model_1_1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model_1_1.xcdatamodel; sourceTree = "<group>"; };
FF2B6F5D2C036A1400835EE7 /* EventLinksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventLinksView.swift; sourceTree = "<group>"; };
FF2EFBEF2BDE295E0049CE3B /* SendToAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendToAllView.swift; sourceTree = "<group>"; };
FF30ACEC2E8D700B008B6006 /* PaymentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentService.swift; sourceTree = "<group>"; };
FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentRequestButton.swift; sourceTree = "<group>"; };
FF3795612B9396D0004EA093 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
FF3795652B9399AA004EA093 /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
FF39B60F2DC87FEB004E10CE /* PadelClubData.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PadelClubData.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1126,6 +1137,7 @@
FFA6D78A2BB0BEB3003A31F3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
FFA97C8C2E8A59D00089EA22 /* TournamentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentPickerView.swift; sourceTree = "<group>"; };
FFA97C9D2E8A7C080089EA22 /* View+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = "<group>"; };
FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankingGroupStageSetupView.swift; sourceTree = "<group>"; };
FFB0FF662E81B671009EDEAC /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
FFB0FF722E841042009EDEAC /* WeekdaySelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekdaySelectionView.swift; sourceTree = "<group>"; };
FFB1C98A2C10255100B154A7 /* TournamentBroadcastRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TournamentBroadcastRowView.swift; sourceTree = "<group>"; };
@ -1762,6 +1774,7 @@
FF4AB6B42B9248200002987F /* NetworkManager.swift */,
FFC1E1092BAC2A77008D6F59 /* NetworkFederalService.swift */,
FFE8B5BA2DA9896800BDE966 /* RefundService.swift */,
FF30ACEC2E8D700B008B6006 /* PaymentService.swift */,
FFE8B5C62DAA390000BDE966 /* StripeValidationService.swift */,
FFE8B5CA2DAA429E00BDE966 /* XlsToCsvService.swift */,
FFE8B6392DACEAEC00BDE966 /* ConfigurationService.swift */,
@ -1814,6 +1827,7 @@
FF967CFB2BAEE13900A9A3BD /* GroupStagesView.swift */,
FF5DA18E2BB9268800A33061 /* GroupStagesSettingsView.swift */,
FF6525C22C8C61B400B9498E /* LoserBracketFromGroupStageView.swift */,
FFA97CA12E8BAC880089EA22 /* RankingGroupStageSetupView.swift */,
FF9AC3932BE3625D00C2E883 /* Components */,
FF9AC3922BE3625200C2E883 /* Shared */,
);
@ -1842,6 +1856,7 @@
FF1162862BD004AD000C4809 /* EditingTeamView.swift */,
FF17CA562CC02FEA003C7323 /* CoachListView.swift */,
FF7DCD382CC330260041110C /* TeamRestingView.swift */,
FF30ACF02E8D7078008B6006 /* PaymentRequestButton.swift */,
FF025AD62BD0C0FB00A86CF8 /* Components */,
);
path = Team;
@ -2281,6 +2296,7 @@
C4A47DB32B86387500ADC637 /* AccountView.swift in Sources */,
FFCEDA4C2C2C08EA00F8C0F2 /* PlayersWithoutContactView.swift in Sources */,
FF17CA4F2CB9243E003C7323 /* FollowUpMatchView.swift in Sources */,
FF30ACEF2E8D700B008B6006 /* PaymentService.swift in Sources */,
FFCD16B32C3E5E590092707B /* TeamsCallingView.swift in Sources */,
FF1162832BCFBE4E000C4809 /* EditablePlayerView.swift in Sources */,
FF1162852BD00279000C4809 /* PlayerDetailView.swift in Sources */,
@ -2315,6 +2331,7 @@
FF025AED2BD1513700A86CF8 /* AppScreen.swift in Sources */,
FFC91B032BD85E2400B29808 /* CourtView.swift in Sources */,
FFCFC00E2BBC3D4600B82851 /* PointSelectionView.swift in Sources */,
FF30ACF22E8D7078008B6006 /* PaymentRequestButton.swift in Sources */,
FF089EB62BB00A3800F0AEC7 /* TeamRowView.swift in Sources */,
C40CD2F32C412681000DBD9A /* AppDelegate.swift in Sources */,
FFCFC00C2BBC3D1E00B82851 /* EditScoreView.swift in Sources */,
@ -2451,6 +2468,7 @@
FF967D032BAEF0C000A9A3BD /* MatchDetailView.swift in Sources */,
FFE8B5B32DA848D300BDE966 /* OnlineWaitingListFaqSheetView.swift in Sources */,
FF967D0F2BAF63B000A9A3BD /* PlayerBlockView.swift in Sources */,
FFA97CA32E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */,
FFCFC0122BBC3E1A00B82851 /* PointView.swift in Sources */,
FF1CBC232BB53E590036DAAB /* ClubHolder.swift in Sources */,
FFBF41862BF75FDA001B24CB /* EventSettingsView.swift in Sources */,
@ -2550,6 +2568,7 @@
FFA252AF2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */,
FF4CBF6F2C996C0600151637 /* ListRowViewModifier.swift in Sources */,
FF4CBF702C996C0600151637 /* PresentationContext.swift in Sources */,
FF30ACEE2E8D700B008B6006 /* PaymentService.swift in Sources */,
FF4CBF722C996C0600151637 /* SwiftParser.swift in Sources */,
FF4CBF732C996C0600151637 /* ChangePasswordView.swift in Sources */,
FF4CBF742C996C0600151637 /* TournamentSubscriptionView.swift in Sources */,
@ -2584,6 +2603,7 @@
FF4CBF882C996C0600151637 /* TeamRowView.swift in Sources */,
FF4CBF8A2C996C0600151637 /* AppDelegate.swift in Sources */,
FF4CBF8C2C996C0600151637 /* EditScoreView.swift in Sources */,
FF30ACF12E8D7078008B6006 /* PaymentRequestButton.swift in Sources */,
FF4CBF8D2C996C0600151637 /* TournamentOrganizerView.swift in Sources */,
FF4CBF8F2C996C0600151637 /* TournamentRunningView.swift in Sources */,
FF4CBF902C996C0600151637 /* TournamentDatePickerView.swift in Sources */,
@ -2720,6 +2740,7 @@
FF4CC0182C996C0600151637 /* ClubHolder.swift in Sources */,
FF4CC0192C996C0600151637 /* EventSettingsView.swift in Sources */,
C49771E72DC25F04005CD239 /* Color+Extensions.swift in Sources */,
FFA97CA22E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */,
C49771E82DC25F04005CD239 /* Badge+Extensions.swift in Sources */,
C49771E92DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */,
FF4CC01A2C996C0600151637 /* InscriptionInfoView.swift in Sources */,
@ -2797,6 +2818,7 @@
FFA252AD2CDB734A0074E63F /* UmpireStatisticView.swift in Sources */,
FF70FAEE2C90584900129CC2 /* ListRowViewModifier.swift in Sources */,
FF70FAEF2C90584900129CC2 /* PresentationContext.swift in Sources */,
FF30ACED2E8D700B008B6006 /* PaymentService.swift in Sources */,
FF70FAF12C90584900129CC2 /* SwiftParser.swift in Sources */,
FF70FAF22C90584900129CC2 /* ChangePasswordView.swift in Sources */,
FF70FAF32C90584900129CC2 /* TournamentSubscriptionView.swift in Sources */,
@ -2831,6 +2853,7 @@
FF70FB072C90584900129CC2 /* TeamRowView.swift in Sources */,
FF70FB092C90584900129CC2 /* AppDelegate.swift in Sources */,
FF70FB0B2C90584900129CC2 /* EditScoreView.swift in Sources */,
FF30ACF32E8D7078008B6006 /* PaymentRequestButton.swift in Sources */,
FF70FB0C2C90584900129CC2 /* TournamentOrganizerView.swift in Sources */,
FF70FB0E2C90584900129CC2 /* TournamentRunningView.swift in Sources */,
FF70FB0F2C90584900129CC2 /* TournamentDatePickerView.swift in Sources */,
@ -2967,6 +2990,7 @@
FF70FB972C90584900129CC2 /* ClubHolder.swift in Sources */,
FF70FB982C90584900129CC2 /* EventSettingsView.swift in Sources */,
C49771E42DC25F04005CD239 /* Color+Extensions.swift in Sources */,
FFA97CA42E8BAC880089EA22 /* RankingGroupStageSetupView.swift in Sources */,
C49771E52DC25F04005CD239 /* Badge+Extensions.swift in Sources */,
C49771E62DC25F04005CD239 /* SpinDrawable+Extensions.swift in Sources */,
FF70FB992C90584900129CC2 /* InscriptionInfoView.swift in Sources */,

@ -0,0 +1,41 @@
//
// PaymentService.swift
// PadelClub
//
// Created by Razmig Sarkissian on 01/10/2025.
//
import Foundation
import LeStorage
import PadelClubData
class PaymentService {
static func resendPaymentEmail(teamRegistrationId: String) async throws -> SimpleResponse {
let service = try StoreCenter.main.service()
let urlRequest = try service._baseRequest(
servicePath: "resend-payment-email/\(teamRegistrationId)/",
method: .post,
requiresToken: true
)
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw PaymentError.requestFailed
}
return try JSON.decoder.decode(SimpleResponse.self, from: data)
}
}
enum PaymentError: Error {
case requestFailed
case unauthorized
case unknown
}
struct SimpleResponse: Codable {
let success: Bool
let message: String
}

@ -286,7 +286,7 @@ struct PlayerDetailView: View {
}
LabeledContent {
TextField("Téléphone contact", text: $contactPhoneNumber)
TextField("Téléphone", text: $contactPhoneNumber)
.focused($focusedField, equals: ._contactPhoneNumber)
.keyboardType(.namePhonePad)
.textContentType(nil)
@ -320,7 +320,7 @@ struct PlayerDetailView: View {
}
LabeledContent {
TextField("Email contact", text: $contactEmail)
TextField("Email", text: $contactEmail)
.focused($focusedField, equals: ._contactEmail)
.keyboardType(.emailAddress)
.textContentType(nil)

@ -140,6 +140,10 @@ struct EditingTeamView: View {
} label: {
Text("Payé en ligne")
}
if team.hasPaidOnline() == false {
PaymentRequestButton(teamRegistration: team)
}
}
if let refundMessage, refundMessage.isEmpty == false {

@ -0,0 +1,51 @@
//
// PaymentRequestButton.swift
// PadelClub
//
// Created by Razmig Sarkissian on 01/10/2025.
//
import SwiftUI
import PadelClubData
struct PaymentRequestButton: View {
let teamRegistration: TeamRegistration
@State private var isLoading = false
@State private var showAlert = false
@State private var alertMessage = ""
var body: some View {
Button("Renvoyer email de paiement") {
resendEmail()
}
.disabled(isLoading)
.alert("Résultat", isPresented: $showAlert) {
Button("OK") { }
} message: {
Text(alertMessage)
}
}
private func resendEmail() {
isLoading = true
Task {
do {
let response = try await PaymentService.resendPaymentEmail(
teamRegistrationId: teamRegistration.id
)
await MainActor.run {
isLoading = false
alertMessage = response.message
showAlert = true
}
} catch {
await MainActor.run {
isLoading = false
alertMessage = "Erreur lors de l'envoi"
showAlert = true
}
}
}
}
}

@ -0,0 +1,24 @@
class PaymentService {
static func resendPaymentEmail(teamRegistrationId: String) async throws -> SimpleResponse {
let service = try StoreCenter.main.service()
let urlRequest = try service._baseRequest(
servicePath: "resend-payment-email/\(teamRegistrationId)/",
method: .post,
requiresToken: true
)
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw PaymentError.requestFailed
}
return try JSON.decoder.decode(SimpleResponse.self, from: data)
}
}
struct SimpleResponse: Codable {
let success: Bool
let message: String
}

@ -54,7 +54,10 @@ struct InscriptionManagerView: View {
@State private var refreshResult: String? = nil
@State private var refreshStatus: Bool?
@State private var showLegendView: Bool = false
@State private var isLoading = false
@State private var showAlert = false
@State private var alertMessage = ""
var tournamentStore: TournamentStore? {
return self.tournament.tournamentStore
}
@ -335,6 +338,11 @@ struct InscriptionManagerView: View {
}
.tint(.master)
}
.alert("Requête de paiement", isPresented: $showAlert) {
Button("OK") { }
} message: {
Text(alertMessage)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
@ -373,6 +381,40 @@ struct InscriptionManagerView: View {
ToolbarItem(placement: .navigationBarTrailing) {
Menu {
#if PRODTEST
if tournament.enableOnlinePayment {
Button {
isLoading = true
Task {
do {
try await selectedSortedTeams.filter { team in
team.hasPaidOnline() == false && team.hasPaid() == false
}.concurrentForEach { team in
_ = try await PaymentService.resendPaymentEmail(teamRegistrationId: team.id)
}
await MainActor.run {
isLoading = false
alertMessage = "Relance effectuée avec succès"
showAlert = true
}
} catch {
Logger.error(error)
await MainActor.run {
isLoading = false
alertMessage = "Erreur lors de la requête"
showAlert = true
}
}
}
} label: {
Text("Requête de paiement")
}
.disabled(isLoading)
}
#endif
if tournament.inscriptionClosed() == false {
Menu {
_sortingTypePickerView()
@ -396,7 +438,7 @@ struct InscriptionManagerView: View {
if tournament.isAnimation() == false {
Divider()
Section {
Menu {
Button("+1 en tableau") {
tournament.addWildCard(1, .bracket)
_setHash()
@ -408,7 +450,7 @@ struct InscriptionManagerView: View {
_setHash()
}
}
} header: {
} label: {
Text("Ajout de wildcards")
}
@ -421,21 +463,29 @@ struct InscriptionManagerView: View {
}
Divider()
Button {
presentImportView = true
Menu {
Button {
presentImportView = true
} label: {
Label("Importer un fichier", systemImage: "square.and.arrow.down")
}
Link(destination: URLs.beachPadel.url) {
Label("beach-padel.app.fft.fr", systemImage: "safari")
}
} label: {
Label("Importer beach-padel", systemImage: "square.and.arrow.down")
}
Link(destination: URLs.beachPadel.url) {
Label("beach-padel.app.fft.fr", systemImage: "safari")
Text("Beach Padel")
}
if tournament.inscriptionClosed() == false {
Divider()
_importTeamsMenuView(title: "Importer des paires")
_sharingTeamsMenuView()
Menu {
_importTeamsMenuView(title: "Importer des paires d'un autre tournoi")
_sharingTeamsMenuView()
} label: {
Text("Importer / Exporter")
}
} else {
_sharingTeamsMenuView()
@ -459,19 +509,20 @@ struct InscriptionManagerView: View {
}
//rankingDateSourcePickerView(showDateInLabel: true)
Divider()
_sharingTeamsMenuView()
Divider()
_importTeamsMenuView(title: "Importer des paires")
Button {
presentImportView = true
Menu {
_sharingTeamsMenuView()
_importTeamsMenuView(title: "Importer des paires d'un autre tournoi")
Button {
presentImportView = true
} label: {
Label("Importer un fichier", systemImage: "square.and.arrow.down")
}
} label: {
Label("Importer un fichier", systemImage: "square.and.arrow.down")
Text("Importer / Exporter")
}
}
} label: {
@ -741,7 +792,7 @@ struct InscriptionManagerView: View {
.pickerStyle(.menu)
if currentRankSourceDate == SourceFileManager.shared.mostRecentDateAvailable {
Button("Rafraîchir") {
Button("Rafraîchir le rang des joueurs") {
confirmUpdateRank = true
}
}

Loading…
Cancel
Save