From 97eff686bef52e796561556aa363a5694e55a418 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 01:19:14 +0000 Subject: [PATCH 1/3] Initial plan From 4817e7b259abd86d991a1c02dee573792010e314 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 01:30:18 +0000 Subject: [PATCH 2/3] Add support for French phone numbers and other international formats Co-authored-by: TahiryRa <43411767+TahiryRa@users.noreply.github.com> --- README.md | 19 +++++++++++++++++++ api/pkg/requests/request.go | 26 ++++++++++++++++++++++---- api/pkg/validators/validator.go | 6 +++--- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fa387b9e..1b02dbd7 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Quick Start Guide 👉 [https://docs.httpsms.com](https://docs.httpsms.com) - [Webhook](#webhook) - [Back Pressure](#back-pressure) - [Message Expiration](#message-expiration) + - [International Phone Number Support](#international-phone-number-support) - [API Clients](#api-clients) - [Flows](#flows) - [Sending an SMS Message](#sending-an-sms-message) @@ -112,6 +113,24 @@ Sometimes it happens that the phone doesn't get the push notification in time an possible to set a timeout for which a message is valid and if a message becomes expired after the timeout elapses, you will be notified. +### International Phone Number Support + +httpSMS supports phone numbers from multiple countries and regions. Phone numbers can be provided in the following formats: + +- **International E.164 format** (recommended): `+33612345678`, `+18005550100`, `+237671234567` +- **National format**: `0612345678` (France), `0123456789` (France) +- **Without leading zeros**: Numbers without country code prefix will be automatically detected if they match common formats + +The system automatically converts phone numbers to the standard E.164 format for consistent handling across different regions. + +**Supported countries include:** +- 🇫🇷 France (e.g., `+33612345678` or `0612345678`) +- 🇺🇸 United States (e.g., `+18005550100`) +- 🇬🇧 United Kingdom (e.g., `+441234567890`) +- 🇩🇪 Germany, 🇪🇸 Spain, 🇮🇹 Italy +- 🇨🇲 Cameroon, 🇳🇬 Nigeria, 🇿🇦 South Africa +- And many more... + ## API Clients - [x] Go: https://github.com/NdoleStudio/httpsms-go diff --git a/api/pkg/requests/request.go b/api/pkg/requests/request.go index 851137d1..d6fe37c6 100644 --- a/api/pkg/requests/request.go +++ b/api/pkg/requests/request.go @@ -23,12 +23,30 @@ func (input *request) sanitizeAddresses(value []string) []string { func (input *request) sanitizeAddress(value string) string { value = strings.TrimSpace(value) - if !strings.HasPrefix(value, "+") && input.isDigits(value) && len(value) > 9 { - value = "+" + value - } - + + // Try parsing with UNKNOWN_REGION first (works for international format) if number, err := phonenumbers.Parse(value, phonenumbers.UNKNOWN_REGION); err == nil { value = phonenumbers.Format(number, phonenumbers.E164) + return value + } + + // If value doesn't start with + and has more than 9 digits, try adding + + if !strings.HasPrefix(value, "+") && input.isDigits(value) && len(value) > 9 && !strings.HasPrefix(value, "0") { + testValue := "+" + value + if number, err := phonenumbers.Parse(testValue, phonenumbers.UNKNOWN_REGION); err == nil { + value = phonenumbers.Format(number, phonenumbers.E164) + return value + } + } + + // If UNKNOWN_REGION fails, try common region codes for national formats + // This supports phone numbers without country code (e.g., French "0612345678") + commonRegions := []string{"FR", "US", "GB", "DE", "ES", "IT", "CM", "NG", "ZA"} + for _, region := range commonRegions { + if number, err := phonenumbers.Parse(value, region); err == nil && phonenumbers.IsValidNumber(number) { + value = phonenumbers.Format(number, phonenumbers.E164) + break + } } return value diff --git a/api/pkg/validators/validator.go b/api/pkg/validators/validator.go index bc7111e8..57da57fd 100644 --- a/api/pkg/validators/validator.go +++ b/api/pkg/validators/validator.go @@ -29,12 +29,12 @@ func init() { govalidator.AddCustomRule(phoneNumberRule, func(field string, rule string, message string, value interface{}) error { phoneNumber, ok := value.(string) if !ok { - return fmt.Errorf("The %s field must be a valid E.164 phone number in the international format e.g +18005550100", field) + return fmt.Errorf("The %s field must be a valid E.164 phone number in the international format e.g +18005550100 or +33612345678", field) } _, err := phonenumbers.Parse(phoneNumber, phonenumbers.UNKNOWN_REGION) if err != nil { - return fmt.Errorf("The %s field must be a valid E.164 phone number in the international format e.g +18005550100", field) + return fmt.Errorf("The %s field must be a valid E.164 phone number in the international format e.g +18005550100 or +33612345678", field) } return nil @@ -49,7 +49,7 @@ func init() { for index, number := range phoneNumbers { _, err := phonenumbers.Parse(number, phonenumbers.UNKNOWN_REGION) if err != nil { - return fmt.Errorf("The %s field in index [%d] must be a valid E.164 phone number in the international format e.g +18005550100", field, index) + return fmt.Errorf("The %s field in index [%d] must be a valid E.164 phone number in the international format e.g +18005550100 or +33612345678", field, index) } } From 76734a460d73e185a2e4da5f958b791300938483 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 13 Dec 2025 01:33:23 +0000 Subject: [PATCH 3/3] Extract region codes to package-level constant for maintainability Co-authored-by: TahiryRa <43411767+TahiryRa@users.noreply.github.com> --- api/pkg/requests/request.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/api/pkg/requests/request.go b/api/pkg/requests/request.go index d6fe37c6..a9c34362 100644 --- a/api/pkg/requests/request.go +++ b/api/pkg/requests/request.go @@ -11,6 +11,10 @@ import ( "github.com/nyaruka/phonenumbers" ) +// commonRegionCodes are the region codes tried when parsing phone numbers in national format +// These regions are attempted in order when UNKNOWN_REGION parsing fails +var commonRegionCodes = []string{"FR", "US", "GB", "DE", "ES", "IT", "CM", "NG", "ZA"} + type request struct{} func (input *request) sanitizeAddresses(value []string) []string { @@ -41,8 +45,7 @@ func (input *request) sanitizeAddress(value string) string { // If UNKNOWN_REGION fails, try common region codes for national formats // This supports phone numbers without country code (e.g., French "0612345678") - commonRegions := []string{"FR", "US", "GB", "DE", "ES", "IT", "CM", "NG", "ZA"} - for _, region := range commonRegions { + for _, region := range commonRegionCodes { if number, err := phonenumbers.Parse(value, region); err == nil && phonenumbers.IsValidNumber(number) { value = phonenumbers.Format(number, phonenumbers.E164) break