diff --git a/pkg/shippo/shippo.go b/pkg/shippo/shippo.go index ab6bab7..121e029 100644 --- a/pkg/shippo/shippo.go +++ b/pkg/shippo/shippo.go @@ -121,13 +121,12 @@ func (c *Client) ValidateAddress(addr Address) (*Address, error) { return nil, fmt.Errorf("failed to parse response: %w", err) } - // TODO: Re-add !result.Test bypass before production if needed. - // Removed test mode bypass so we can verify Shippo validation in development. - // - // Shippo returns empty validation_results (no is_valid field) for international - // addresses. We only reject when is_valid is explicitly false. - if result.ValidationResults != nil && result.ValidationResults.IsValid != nil && !*result.ValidationResults.IsValid { - return nil, fmt.Errorf("address is invalid") + // Only accept addresses that Shippo explicitly validates as valid. + // International addressees where Shippo cannot validate are rejected. + // Address validation for addresses in other countries need to be added on a per-country API basis. + // Going to add Bring validation for Norway next + if result.ValidationResults == nil || result.ValidationResults.IsValid == nil || !*result.ValidationResults.IsValid { + return nil, fmt.Errorf("invalid address") } // Return the normalized address from Shippo diff --git a/pkg/tui/model.go b/pkg/tui/model.go index 82ea7a1..6e2014d 100644 --- a/pkg/tui/model.go +++ b/pkg/tui/model.go @@ -238,11 +238,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case AddressSavedMsg: if msg.Err != nil { + log.Printf("address validation failed: %v", msg.Err) if m.ShippingForm != nil { m.ShippingForm.submitting = false m.ShippingForm.form = m.buildShippingForm(m.ShippingForm) } - m.ErrorMsg = fmt.Sprintf("Invalid address: %v", msg.Err) + m.ErrorMsg = "Invalid address. Currently only US addresses are supported." return m, m.ShippingForm.form.Init() } m.ShippingInfo = &msg.Address @@ -432,11 +433,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case AddressSavedMsg: if msg.Err != nil { + log.Printf("address validation failed: %v", msg.Err) if m.ShippingForm != nil { m.ShippingForm.submitting = false m.ShippingForm.form = m.buildShippingForm(m.ShippingForm) } - m.ErrorMsg = fmt.Sprintf("Invalid address: %v", msg.Err) + m.ErrorMsg = "Invalid address. Currently only US addresses are supported." if m.ShippingForm != nil { return m, m.ShippingForm.form.Init() } diff --git a/pkg/tui/shipping_form.go b/pkg/tui/shipping_form.go index 171e3cc..059e8f7 100644 --- a/pkg/tui/shipping_form.go +++ b/pkg/tui/shipping_form.go @@ -9,8 +9,8 @@ import ( "github.com/charmbracelet/lipgloss" ) -// scandinavianCountries defines the available countries with their dial codes. -var scandinavianCountries = []struct { +// countries defines the available countries with their dial codes. +var countries = []struct { Name string Code string DialCode string @@ -20,6 +20,7 @@ var scandinavianCountries = []struct { {Name: "Iceland", Code: "IS", DialCode: "+354"}, {Name: "Norway", Code: "NO", DialCode: "+47"}, {Name: "Sweden", Code: "SE", DialCode: "+46"}, + {Name: "USA", Code: "US", DialCode: "+1"}, } // countryDialCodes maps country ISO codes to their dialing prefixes. @@ -29,6 +30,7 @@ var countryDialCodes = map[string]string{ "IS": "+354", "NO": "+47", "SE": "+46", + "US": "+1", } // ShippingFormState holds the shipping form state. @@ -49,14 +51,14 @@ type ShippingFormState struct { // ShippingFormCompleteMsg is sent when the shipping form is completed type ShippingFormCompleteMsg struct { - Name string - Street1 string - Street2 string - City string - State string - Country string - Zip string - Phone string + Name string + Street1 string + Street2 string + City string + State string + Country string + Zip string + Phone string } // ShippingFormErrorMsg is sent when form validation fails at submission time @@ -68,8 +70,8 @@ type ShippingFormErrorMsg struct { // No field-level validators so Tab/Enter move freely between fields. // Validation happens at submission time instead. func (m Model) buildShippingForm(state *ShippingFormState) *huh.Form { - countryOptions := make([]huh.Option[string], 0, len(scandinavianCountries)) - for _, c := range scandinavianCountries { + countryOptions := make([]huh.Option[string], 0, len(countries)) + for _, c := range countries { label := fmt.Sprintf("%s (%s)", c.Name, c.DialCode) countryOptions = append(countryOptions, huh.NewOption(label, c.Code)) } @@ -218,14 +220,14 @@ func (m Model) UpdateShippingForm(msg tea.Msg, state *ShippingFormState) tea.Cmd phone := formatPhoneWithDialCode(state.Phone, state.Country) return func() tea.Msg { return ShippingFormCompleteMsg{ - Name: state.Name, - Street1: state.Street1, - Street2: state.Street2, - City: state.City, - State: state.State, - Country: state.Country, - Zip: state.Zip, - Phone: phone, + Name: state.Name, + Street1: state.Street1, + Street2: state.Street2, + City: state.City, + State: state.State, + Country: state.Country, + Zip: state.Zip, + Phone: phone, } } } diff --git a/pkg/validate/validate.go b/pkg/validate/validate.go index d8b97d6..f771fad 100644 --- a/pkg/validate/validate.go +++ b/pkg/validate/validate.go @@ -81,7 +81,7 @@ func MustBeLen(length int, name string) ErrorHandler { return nil } if len(str) != length { - return fmt.Errorf("Expected %s to be length %d but got %d", name, length, len(str)) + return fmt.Errorf("expected %s to be length %d but got %d", name, length, len(str)) } return nil }