From f49e4ea337ba13f2a07cb6626139c85d2658d748 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Thu, 4 Jun 2026 15:48:00 -0300 Subject: [PATCH] fix(payment): validate card expiry date month range and fix PaymentCardView character length --- .../PediFoods/Resources/Localizable.xcstrings | 43 ++++++++++++++----- .../PediFoods/Views/Main/CheckoutView.swift | 20 ++++++++- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/pedi-foods/Sources/PediFoods/Resources/Localizable.xcstrings b/pedi-foods/Sources/PediFoods/Resources/Localizable.xcstrings index d2957b8..29f9fe1 100644 --- a/pedi-foods/Sources/PediFoods/Resources/Localizable.xcstrings +++ b/pedi-foods/Sources/PediFoods/Resources/Localizable.xcstrings @@ -156,6 +156,9 @@ }, "Adicionar loja aos favoritos" : { + }, + "Adicionar novo cartão" : { + }, "Adicionar novo endereço" : { "comment" : "A button label that translates to \"Add new address\" in English.", @@ -181,10 +184,6 @@ "comment" : "A text that informs the user that registering implies acceptance of the app's terms of use and privacy policy. The text is clickable and navigates to the respective views when tapped.", "isCommentAutoGenerated" : true }, - "Apenas Pagar" : { - "comment" : "A button that allows the user to pay without saving the payment information.", - "isCommentAutoGenerated" : true - }, "Aplicar" : { "comment" : "A button that applies a coupon code to a cart.", "isCommentAutoGenerated" : true @@ -311,6 +310,9 @@ "Carrinho" : { "comment" : "A label for the cart section of the app.", "isCommentAutoGenerated" : true + }, + "Cartão de Crédito" : { + }, "Categorias" : { "comment" : "A heading for the list of available categories in the filters modal.", @@ -376,10 +378,6 @@ "comment" : "A section header for entering and applying a discount coupon.", "isCommentAutoGenerated" : true }, - "Dados do Cartão" : { - "comment" : "The header text for the section that collects payment card information.", - "isCommentAutoGenerated" : true - }, "Dark" : { "comment" : "Menu item indicating that the appearance should be in dark mode", "extractionState" : "stale", @@ -789,6 +787,12 @@ } } } + }, + "Novo cartão" : { + + }, + "Novo Cartão" : { + }, "Novo endereço" : { "comment" : "A label for a form to add a new address.", @@ -829,9 +833,8 @@ "comment" : "A label describing the sorting options available in the filters modal.", "isCommentAutoGenerated" : true }, - "Pagamento" : { - "comment" : "The title of the screen where a user can enter and save payment information.", - "isCommentAutoGenerated" : true + "ou" : { + }, "Pagamento via PIX" : { "comment" : "The navigation title for the PaymentPixView.", @@ -842,6 +845,9 @@ }, "Pagar Pelo App" : { + }, + "Pague mais rápido nas próximas compras" : { + }, "Pedido #%@" : { @@ -990,6 +996,9 @@ "Salvar Alterações" : { "comment" : "The text on a button that saves changes to a user's profile.", "isCommentAutoGenerated" : true + }, + "Salvar cartão" : { + }, "Save" : { "comment" : "Button title indicating that the current contents should be saved", @@ -1020,6 +1029,9 @@ } } } + }, + "Selecionar Cartão" : { + }, "Sem adicionais" : { @@ -1177,6 +1189,9 @@ "Total do Pedido" : { "comment" : "The label above the total price of the order in the payment card view.", "isCommentAutoGenerated" : true + }, + "Trocar" : { + }, "Trocar de loja?" : { "comment" : "A title for an alert that prompts the user to switch stores.", @@ -1185,10 +1200,16 @@ "Trocar Foto" : { "comment" : "A button label that allows a user to change their profile picture.", "isCommentAutoGenerated" : true + }, + "Trocar Pagamento" : { + }, "UPLOAD" : { "comment" : "A button label that says \"UPLOAD\".", "isCommentAutoGenerated" : true + }, + "Vence %@" : { + }, "Ver Detalhes" : { "comment" : "A button that shows order details when pressed.", diff --git a/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift b/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift index 58d65ab..bed7a3f 100644 --- a/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift +++ b/pedi-foods/Sources/PediFoods/Views/Main/CheckoutView.swift @@ -1116,10 +1116,18 @@ struct PaymentCardView: View { _cpf = State(initialValue: appState.wrappedValue.profile.cpf) } + private func validateExpiryDate(digits: Int) -> Bool { + let clean = expiry.filter(\.isNumber) + guard clean.count == digits else { return false } + let monthStr = String(clean.prefix(2)) + guard let month = Int(monthStr), (1...12).contains(month) else { return false } + return true + } + var canSubmit: Bool { holderName.trimmingCharacters(in: .whitespaces).isEmpty == false && cardNumber.filter(\.isNumber).count >= 13 && - expiry.filter(\.isNumber).count == 6 && + validateExpiryDate(digits: 4) && cvv.filter(\.isNumber).count >= 3 && cpf.filter(\.isNumber).count == 11 } @@ -1665,10 +1673,18 @@ struct ChangePaymentNewCardView: View { _cpf = State(initialValue: appState.wrappedValue.profile.cpf) } + private func validateExpiryDate(digits: Int) -> Bool { + let clean = expiry.filter(\.isNumber) + guard clean.count == digits else { return false } + let monthStr = String(clean.prefix(2)) + guard let month = Int(monthStr), (1...12).contains(month) else { return false } + return true + } + var canSubmit: Bool { holderName.trimmingCharacters(in: .whitespaces).isEmpty == false && cardNumber.filter(\.isNumber).count >= 13 && - expiry.filter(\.isNumber).count == 6 && + validateExpiryDate(digits: 6) && cvv.filter(\.isNumber).count >= 3 && cpf.filter(\.isNumber).count == 11 }