|
|
|
|
@ -30,6 +30,7 @@ struct RegistrationSetupView: View { |
|
|
|
|
@State private var enableTimeToConfirm: Bool |
|
|
|
|
@State private var isTemplate: Bool |
|
|
|
|
@State private var isCorporateTournament: Bool |
|
|
|
|
@State private var isValidating = false |
|
|
|
|
|
|
|
|
|
// Online Payment |
|
|
|
|
@State private var enableOnlinePayment: Bool |
|
|
|
|
@ -297,31 +298,21 @@ struct RegistrationSetupView: View { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
.toolbar(content: { |
|
|
|
|
if focusedField != nil { |
|
|
|
|
ToolbarItem(placement: .topBarLeading) { |
|
|
|
|
Button("Annuler", role: .cancel) { |
|
|
|
|
focusedField = nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
.toolbar { |
|
|
|
|
if focusedField != nil { |
|
|
|
|
if focusedField == ._stripeAccountId, stripeAccountId.isEmpty == false { |
|
|
|
|
ToolbarItem(placement: .keyboard) { |
|
|
|
|
HStack { |
|
|
|
|
if focusedField == ._stripeAccountId, stripeAccountId.isEmpty == false { |
|
|
|
|
Button("Effacer") { |
|
|
|
|
stripeAccountId = "" |
|
|
|
|
tournament.stripeAccountId = nil |
|
|
|
|
} |
|
|
|
|
.buttonStyle(.borderless) |
|
|
|
|
Spacer() |
|
|
|
|
Button("Valider") { |
|
|
|
|
focusedField = nil |
|
|
|
|
} |
|
|
|
|
.buttonStyle(.bordered) |
|
|
|
|
Button("Effacer") { |
|
|
|
|
stripeAccountId = "" |
|
|
|
|
stripeAccountIdIsInvalid = false |
|
|
|
|
tournament.stripeAccountId = nil |
|
|
|
|
} |
|
|
|
|
.buttonStyle(.borderless) |
|
|
|
|
Spacer() |
|
|
|
|
Button("Valider") { |
|
|
|
|
focusedField = nil |
|
|
|
|
} |
|
|
|
|
.buttonStyle(.bordered) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -418,12 +409,24 @@ struct RegistrationSetupView: View { |
|
|
|
|
|
|
|
|
|
if isCorporateTournament == false, dataStore.user.registrationPaymentMode.requiresStripe() { |
|
|
|
|
VStack(alignment: .leading) { |
|
|
|
|
TextField("Identifiant du compte Stripe", text: $stripeAccountId) |
|
|
|
|
.frame(maxWidth: .infinity) |
|
|
|
|
.keyboardType(.default) |
|
|
|
|
.focused($focusedField, equals: ._stripeAccountId) |
|
|
|
|
LabeledContent { |
|
|
|
|
if isValidating { |
|
|
|
|
ProgressView() |
|
|
|
|
} else if focusedField == nil, stripeAccountIdIsInvalid == false, stripeAccountId.isEmpty == false, isValidating == false { |
|
|
|
|
Image(systemName: "checkmark.circle.fill").foregroundStyle(.green) |
|
|
|
|
} |
|
|
|
|
} label: { |
|
|
|
|
TextField("Identifiant du compte Stripe", text: $stripeAccountId) |
|
|
|
|
.frame(maxWidth: .infinity) |
|
|
|
|
.keyboardType(.default) |
|
|
|
|
.focused($focusedField, equals: ._stripeAccountId) |
|
|
|
|
.disabled(isValidating) |
|
|
|
|
} |
|
|
|
|
if stripeAccountIdIsInvalid { |
|
|
|
|
Text("Format d'identifiant Stripe invalide.").foregroundStyle(.logoRed) |
|
|
|
|
Text("Identifiant Stripe invalide. Vous ne pouvez pas activer le paiement en ligne.").foregroundStyle(.logoRed) |
|
|
|
|
Button("Ré-essayer") { |
|
|
|
|
_confirmStripeAccountId() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -450,7 +453,7 @@ struct RegistrationSetupView: View { |
|
|
|
|
} |
|
|
|
|
.onChange(of: focusedField) { old, new in |
|
|
|
|
if old == ._stripeAccountId { |
|
|
|
|
_hasChanged() |
|
|
|
|
_confirmStripeAccountId() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -461,12 +464,86 @@ struct RegistrationSetupView: View { |
|
|
|
|
if stripeAccountId.isEmpty { |
|
|
|
|
tournament.stripeAccountId = nil |
|
|
|
|
} else if stripeAccountId.count >= 5, stripeAccountId.starts(with: "acct_") { |
|
|
|
|
tournament.stripeAccountId = stripeAccountId.prefixMultilineTrimmed(255) |
|
|
|
|
_checkStripeAccount(stripeAccountId.prefixMultilineTrimmed(255)) |
|
|
|
|
} else { |
|
|
|
|
stripeAccountIdIsInvalid = true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func _checkStripeAccount(_ accId: String) { |
|
|
|
|
Task { |
|
|
|
|
isValidating = true |
|
|
|
|
do { |
|
|
|
|
let response = try await _validateStripeAccountID(accId) |
|
|
|
|
stripeAccountId = accId |
|
|
|
|
stripeAccountIdIsInvalid = response.valid == false |
|
|
|
|
enableOnlinePayment = response.valid |
|
|
|
|
} catch { |
|
|
|
|
stripeAccountIdIsInvalid = true |
|
|
|
|
enableOnlinePayment = false |
|
|
|
|
} |
|
|
|
|
isValidating = false |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func _validateStripeAccountID(_ accountID: String) async throws -> ValidationResponse { |
|
|
|
|
let apiPath = "\(URLs.activationHost.rawValue)/utils/validate-stripe-account/" |
|
|
|
|
let url = URL(string: apiPath)! |
|
|
|
|
var request = URLRequest(url: url) |
|
|
|
|
request.httpMethod = "POST" |
|
|
|
|
request.setValue("application/json", forHTTPHeaderField: "Content-Type") |
|
|
|
|
|
|
|
|
|
let body = ["account_id": accountID] |
|
|
|
|
request.httpBody = try JSONEncoder().encode(body) |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
let (data, response) = try await URLSession.shared.data(for: request) |
|
|
|
|
|
|
|
|
|
guard let httpResponse = response as? HTTPURLResponse else { |
|
|
|
|
throw ValidationError.invalidResponse |
|
|
|
|
} |
|
|
|
|
switch httpResponse.statusCode { |
|
|
|
|
case 200...299: |
|
|
|
|
let decodedResponse = try JSONDecoder().decode(ValidationResponse.self, from: data) |
|
|
|
|
return decodedResponse |
|
|
|
|
case 400: |
|
|
|
|
// Handle bad request |
|
|
|
|
let errorResponse = try JSONDecoder().decode(ValidationResponse.self, from: data) |
|
|
|
|
return errorResponse |
|
|
|
|
case 403: |
|
|
|
|
// Handle permission error |
|
|
|
|
let errorResponse = try JSONDecoder().decode(ValidationResponse.self, from: data) |
|
|
|
|
return errorResponse |
|
|
|
|
default: |
|
|
|
|
throw ValidationError.invalidResponse |
|
|
|
|
} |
|
|
|
|
} catch let error as ValidationError { |
|
|
|
|
throw error |
|
|
|
|
} catch { |
|
|
|
|
throw ValidationError.networkError(error) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct ValidationResponse: Codable { |
|
|
|
|
let valid: Bool |
|
|
|
|
let account: AccountDetails? |
|
|
|
|
let error: String? |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct AccountDetails: Codable { |
|
|
|
|
let id: String |
|
|
|
|
|
|
|
|
|
enum CodingKeys: String, CodingKey { |
|
|
|
|
case id |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
enum ValidationError: Error { |
|
|
|
|
case invalidResponse |
|
|
|
|
case networkError(Error) |
|
|
|
|
case invalidData |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func _hasChanged() { |
|
|
|
|
hasChanges = true |
|
|
|
|
} |
|
|
|
|
@ -496,7 +573,11 @@ struct RegistrationSetupView: View { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tournament.enableTimeToConfirm = enableTimeToConfirm |
|
|
|
|
_confirmStripeAccountId() |
|
|
|
|
if stripeAccountIdIsInvalid == false { |
|
|
|
|
tournament.stripeAccountId = stripeAccountId |
|
|
|
|
} else { |
|
|
|
|
tournament.stripeAccountId = nil |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
tournament.accountIsRequired = true |
|
|
|
|
tournament.licenseIsRequired = true |
|
|
|
|
|