From 8421feb7573f3bf9a5dbaf3ad61d72a3d7033882 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 20 Nov 2024 14:59:33 +0300 Subject: [PATCH 01/12] updated the regex to match all Kenyan numbers --- internal/handlers/ussd/menuhandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d8e6fa0..ba9a4c6 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -37,7 +37,7 @@ var ( // Define the regex patterns as constants const ( - phoneRegex = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}` + phoneRegex = `^(?:\+254|254|0)?((?:7[0-9]{8})|(?:1[01][0-9]{7}))$` pinPattern = `^\d{4}$` ) -- 2.45.2 From ec0bbc8aaa07940d1b926a8be1830ecff8f207d9 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 20 Nov 2024 18:32:14 +0300 Subject: [PATCH 02/12] use the latest updates from ussd-data-service --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 391c1a5..e66cd6f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( gopkg.in/leonelquinteros/gotext.v1 v1.3.1 ) -require github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a // indirect +require github.com/grassrootseconomics/ussd-data-service v1.2.0-beta // indirect require ( github.com/jackc/pgpassfile v1.0.0 // indirect diff --git a/go.sum b/go.sum index 0ba38c1..087fd82 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQ github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo= github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a h1:q/YH7nE2j8epNmFnTu0tU1vwtCxtQ6nH+d7hRVV5krU= github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a/go.mod h1:hdKaKwqiW6/kphK4j/BhmuRlZDLo1+DYo3gYw5O0siw= +github.com/grassrootseconomics/ussd-data-service v1.2.0-beta h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk= +github.com/grassrootseconomics/ussd-data-service v1.2.0-beta/go.mod h1:omfI0QtUwIdpu9gMcUqLMCG8O1XWjqJGBx1qUMiGWC0= github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo= github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -- 2.45.2 From 1f830657f9e8e087b8fff7456e139c892c44242b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 20 Nov 2024 18:33:12 +0300 Subject: [PATCH 03/12] added the alias endpoint --- config/config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/config.go b/config/config.go index edb33fe..255971c 100644 --- a/config/config.go +++ b/config/config.go @@ -15,6 +15,7 @@ const ( voucherHoldingsPathPrefix = "/api/v1/holdings" voucherTransfersPathPrefix = "/api/v1/transfers/last10" voucherDataPathPrefix = "/api/v1/token" + AliasPrefix = "api/v1/alias" ) var ( @@ -32,6 +33,7 @@ var ( VoucherHoldingsURL string VoucherTransfersURL string VoucherDataURL string + CheckAliasURL string ) func setBase() error { @@ -66,6 +68,7 @@ func LoadConfig() error { VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix) + CheckAliasURL, _ = url.JoinPath(custodialURLBase, AliasPrefix) return nil } -- 2.45.2 From 9ba8c040c0227711cc4474909265676ed9a19777 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 20 Nov 2024 18:34:48 +0300 Subject: [PATCH 04/12] added the CheckAliasAddress function to the account service and test mocks --- internal/testutil/mocks/servicemock.go | 5 +++++ .../testservice/TestAccountService.go | 8 +++++-- remote/accountservice.go | 21 +++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index a25f1ae..521cfa0 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -47,3 +47,8 @@ func (m *MockAccountService) TokenTransfer(ctx context.Context, amount, from, to args := m.Called() return args.Get(0).(*models.TokenTransferResponse), args.Error(1) } + +func (m *MockAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) { + args := m.Called() + return args.Get(0).(*dataserviceapi.AliasAddress), args.Error(1) +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 7c486f3..96dacbc 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -33,8 +33,8 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey } func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { - return []dataserviceapi.TokenHoldings { - dataserviceapi.TokenHoldings { + return []dataserviceapi.TokenHoldings{ + dataserviceapi.TokenHoldings{ ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", TokenSymbol: "SRF", TokenDecimals: "6", @@ -56,3 +56,7 @@ func (tas *TestAccountService) TokenTransfer(ctx context.Context, amount, from, TrackingId: "e034d147-747d-42ea-928d-b5a7cb3426af", }, nil } + +func (m TestAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) { + return &dataserviceapi.AliasAddress{}, nil +} diff --git a/remote/accountservice.go b/remote/accountservice.go index 23b62ca..b0e9eb4 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -24,6 +24,7 @@ type AccountServiceInterface interface { FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) + CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) } type AccountService struct { @@ -209,6 +210,26 @@ func (as *AccountService) TokenTransfer(ctx context.Context, amount, from, to, t return &r, nil } +// CheckAliasAddress retrieves the address of an alias from the API endpoint. +// Parameters: +// - alias: The alias of the user. +func (as *AccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) { + var r dataserviceapi.AliasAddress + + ep, err := url.JoinPath(config.CheckAliasURL, alias) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) + if err != nil { + return nil, err + } + + _, err = doRequest(ctx, req, &r) + return &r, err +} + func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse -- 2.45.2 From 1a3426e7294098b082d8512b899b4a980cee064a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 20 Nov 2024 18:36:32 +0300 Subject: [PATCH 05/12] added helper functions to validate aliases, addresses and phone numbers --- common/recipient.go | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 common/recipient.go diff --git a/common/recipient.go b/common/recipient.go new file mode 100644 index 0000000..c5b6a4b --- /dev/null +++ b/common/recipient.go @@ -0,0 +1,48 @@ +package common + +import ( + "fmt" + "regexp" +) + +// Define the regex patterns as constants +const ( + phoneRegex = `^(?:\+254|254|0)?((?:7[0-9]{8})|(?:1[01][0-9]{7}))$` + addressRegex = `^0x[a-fA-F0-9]{40}$` + aliasRegex = `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+$` +) + +// IsValidPhoneNumber checks if the given number is a valid phone number +func IsValidPhoneNumber(phonenumber string) bool { + match, _ := regexp.MatchString(phoneRegex, phonenumber) + return match +} + +// IsValidAddress checks if the given address is a valid Ethereum address +func IsValidAddress(address string) bool { + match, _ := regexp.MatchString(addressRegex, address) + return match +} + +// IsValidAlias checks if the alias is a valid alias format +func IsValidAlias(alias string) bool { + match, _ := regexp.MatchString(aliasRegex, alias) + return match +} + +// CheckRecipient validates the recipient format based on the criteria +func CheckRecipient(recipient string) (string, error) { + if IsValidPhoneNumber(recipient) { + return "phone number", nil + } + + if IsValidAddress(recipient) { + return "address", nil + } + + if IsValidAlias(recipient) { + return "alias", nil + } + + return "", fmt.Errorf("invalid recipient: must be a phone number, address or alias") +} -- 2.45.2 From a766f0c01fbed4065cc39cadc509233d5ff08c0b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 20 Nov 2024 18:37:35 +0300 Subject: [PATCH 06/12] improved the ValidateRecipient to check phone numbers, aliases and addresses --- internal/handlers/ussd/menuhandler.go | 93 +++++++++++++++++---------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ba9a4c6..6311444 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -35,12 +35,17 @@ var ( errResponse *api.ErrResponse ) -// Define the regex patterns as constants +// Define the regex patterns as constants const ( - phoneRegex = `^(?:\+254|254|0)?((?:7[0-9]{8})|(?:1[01][0-9]{7}))$` pinPattern = `^\d{4}$` ) +// isValidPIN checks whether the given input is a 4 digit number +func isValidPIN(pin string) bool { + match, _ := regexp.MatchString(pinPattern, pin) + return match +} + // FlagManager handles centralized flag management type FlagManager struct { parser *asm.FlagParser @@ -95,17 +100,6 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util return h, nil } -// isValidPIN checks whether the given input is a 4 digit number -func isValidPIN(pin string) bool { - match, _ := regexp.MatchString(pinPattern, pin) - return match -} - -func isValidPhoneNumber(phonenumber string) bool { - match, _ := regexp.MatchString(phoneRegex, phonenumber) - return match -} - func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { if h.pe != nil { panic("persister already set") @@ -877,7 +871,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input } blockedNumber := string(input) _, err = store.ReadEntry(ctx, blockedNumber, common.DATA_PUBLIC_KEY) - if !isValidPhoneNumber(blockedNumber) { + if !common.IsValidPhoneNumber(blockedNumber) { res.FlagSet = append(res.FlagSet, flag_unregistered_number) return res, nil } @@ -898,10 +892,9 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input return res, nil } -// ValidateRecipient validates that the given input is a valid phone number. +// ValidateRecipient validates that the given input is valid. func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) @@ -909,13 +902,16 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by return res, fmt.Errorf("missing session") } - recipient := string(input) - flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + + recipient := string(input) if recipient != "0" { - if !isValidPhoneNumber(recipient) { + recipientType, err := common.CheckRecipient(recipient) + if err != nil { + // Invalid recipient format (not a phone number, address, or valid alias format) res.FlagSet = append(res.FlagSet, flag_invalid_recipient) res.Content = recipient @@ -929,25 +925,54 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by return res, err } - publicKey, err := store.ReadEntry(ctx, recipient, common.DATA_PUBLIC_KEY) - if err != nil { - if db.IsNotFound(err) { - logg.InfoCtxf(ctx, "Unregistered number") + switch recipientType { + case "phone number": + // Check if the phone number is registered + publicKey, err := store.ReadEntry(ctx, recipient, common.DATA_PUBLIC_KEY) + if err != nil { + if db.IsNotFound(err) { + logg.InfoCtxf(ctx, "Unregistered phone number: %s", recipient) + res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite) + res.Content = recipient + return res, nil + } - res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite) - res.Content = recipient - - return res, nil + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err } - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) - return res, err - } + // Save the publicKey as the recipient + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, publicKey) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", string(publicKey), "error", err) + return res, err + } - err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, publicKey) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", string(publicKey), "error", err) - return res, nil + case "address": + // Save the valid Ethereum address as the recipient + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recipient)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", recipient, "error", err) + return res, err + } + + case "alias": + // Call the API to validate and retrieve the address for the alias + r, aliasErr := h.accountService.CheckAliasAddress(ctx, recipient) + if aliasErr != nil { + res.FlagSet = append(res.FlagSet, flag_api_error) + res.Content = recipient + + logg.ErrorCtxf(ctx, "failed on CheckAliasAddress", "error", aliasErr) + return res, err + } + + // Alias validation succeeded, save the Ethereum address + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(r.Address)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", r.Address, "error", err) + return res, err + } } } -- 2.45.2 From 7c346e2af0edfd2ff021cf6a02e6bf8d80b5d1c5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 20 Nov 2024 18:45:37 +0300 Subject: [PATCH 07/12] use the correct base URL --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 255971c..3a8e8ed 100644 --- a/config/config.go +++ b/config/config.go @@ -68,7 +68,7 @@ func LoadConfig() error { VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix) - CheckAliasURL, _ = url.JoinPath(custodialURLBase, AliasPrefix) + CheckAliasURL, _ = url.JoinPath(dataURLBase, AliasPrefix) return nil } -- 2.45.2 From 1f089ec27bc549d493ac6b5e8a3958bb3f90c467 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 20 Nov 2024 18:55:58 +0300 Subject: [PATCH 08/12] updated the template to include the alias and address --- menutraversal_test/test_setup.json | 4 ++-- services/registration/send | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index a554939..4d8414f 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -61,7 +61,7 @@ }, { "input": "1", - "expectedContent": "Enter recipient's phone number:\n0:Back" + "expectedContent": "Enter recipient's phone number/address/alias:\n0:Back" }, { "input": "000", @@ -69,7 +69,7 @@ }, { "input": "1", - "expectedContent": "Enter recipient's phone number:\n0:Back" + "expectedContent": "Enter recipient's phone number/address/alias:\n0:Back" }, { "input": "0712345678", diff --git a/services/registration/send b/services/registration/send index d124026..306466c 100644 --- a/services/registration/send +++ b/services/registration/send @@ -1 +1 @@ -Enter recipient's phone number: \ No newline at end of file +Enter recipient's phone number/address/alias: \ No newline at end of file -- 2.45.2 From f666b1e96053ab8b6f3c982440f5cd2861d5bfa5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 25 Nov 2024 11:30:09 +0300 Subject: [PATCH 09/12] format the number to a standard version --- cmd/africastalking/main.go | 9 ++++++++- common/recipient.go | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 9f5b501..81a750e 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -20,6 +20,7 @@ import ( "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/common" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" @@ -76,7 +77,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) { return "", fmt.Errorf("no phone number found") } - return phoneNumber, nil + formattedNumber, err := common.FormatPhoneNumber(phoneNumber) + if err != nil { + fmt.Printf("Error: %v\n", err) + return "", fmt.Errorf("failed to format number") + } + + return formattedNumber, nil } func (arp *atRequestParser) GetInput(rq any) ([]byte, error) { diff --git a/common/recipient.go b/common/recipient.go index c5b6a4b..9b44926 100644 --- a/common/recipient.go +++ b/common/recipient.go @@ -3,6 +3,7 @@ package common import ( "fmt" "regexp" + "strings" ) // Define the regex patterns as constants @@ -46,3 +47,17 @@ func CheckRecipient(recipient string) (string, error) { return "", fmt.Errorf("invalid recipient: must be a phone number, address or alias") } + +// FormatPhoneNumber formats a Kenyan phone number to "254xxxxxxxx". +func FormatPhoneNumber(phone string) (string, error) { + // Remove any leading "+" and spaces + phone = strings.TrimPrefix(phone, "+") + phone = strings.ReplaceAll(phone, " ", "") + + // Replace leading "0" with "254" if present + if strings.HasPrefix(phone, "0") { + phone = "254" + phone[1:] + } + + return phone, nil +} \ No newline at end of file -- 2.45.2 From b919bfbfc85ac4b409eb1a9847e2000fe9164578 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 25 Nov 2024 11:59:51 +0300 Subject: [PATCH 10/12] use the formatted number when checking if it is registered --- internal/handlers/ussd/menuhandler.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f4edb5d..8d3b928 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -927,8 +927,15 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by switch recipientType { case "phone number": + // format the phone number + formattedNumber, err := common.FormatPhoneNumber(recipient) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to format the phone number: %s", recipient, "error", err) + return res, err + } + // Check if the phone number is registered - publicKey, err := store.ReadEntry(ctx, recipient, common.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, formattedNumber, common.DATA_PUBLIC_KEY) if err != nil { if db.IsNotFound(err) { logg.InfoCtxf(ctx, "Unregistered phone number: %s", recipient) -- 2.45.2 From 2a36b31750b93bd3c1124ea708dacf43a70d61bb Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 25 Nov 2024 14:37:37 +0300 Subject: [PATCH 11/12] update the format to be +254xxxxxxxxx --- common/recipient.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/common/recipient.go b/common/recipient.go index 9b44926..708d405 100644 --- a/common/recipient.go +++ b/common/recipient.go @@ -1,6 +1,7 @@ package common import ( + "errors" "fmt" "regexp" "strings" @@ -48,8 +49,12 @@ func CheckRecipient(recipient string) (string, error) { return "", fmt.Errorf("invalid recipient: must be a phone number, address or alias") } -// FormatPhoneNumber formats a Kenyan phone number to "254xxxxxxxx". +// FormatPhoneNumber formats a Kenyan phone number to "+254xxxxxxxx". func FormatPhoneNumber(phone string) (string, error) { + if !IsValidPhoneNumber(phone) { + return "", errors.New("invalid phone number") + } + // Remove any leading "+" and spaces phone = strings.TrimPrefix(phone, "+") phone = strings.ReplaceAll(phone, " ", "") @@ -59,5 +64,10 @@ func FormatPhoneNumber(phone string) (string, error) { phone = "254" + phone[1:] } - return phone, nil -} \ No newline at end of file + // Add "+" if not already present + if !strings.HasPrefix(phone, "254") { + return "", errors.New("unexpected format") + } + + return "+" + phone, nil +} -- 2.45.2 From e8bed3b07b2c3035cebe9e3c22b430d87d7c205e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 25 Nov 2024 15:02:28 +0300 Subject: [PATCH 12/12] update the alias regex to be alphanumeric --- common/recipient.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/recipient.go b/common/recipient.go index 708d405..f463a32 100644 --- a/common/recipient.go +++ b/common/recipient.go @@ -11,7 +11,7 @@ import ( const ( phoneRegex = `^(?:\+254|254|0)?((?:7[0-9]{8})|(?:1[01][0-9]{7}))$` addressRegex = `^0x[a-fA-F0-9]{40}$` - aliasRegex = `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+$` + aliasRegex = `^[a-zA-Z0-9]+$` ) // IsValidPhoneNumber checks if the given number is a valid phone number -- 2.45.2