From f36847d9668b92ead9267fd8d20321eba9ab0046 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 14:51:17 +0300 Subject: [PATCH 01/14] Added a placeholder function to get transactions --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 708139b..c291268 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -94,6 +94,7 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) ls.DbRs.AddLocalFunc("fetch_custodial_balances", ussdHandlers.FetchCustodialBalances) + ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactions) return ussdHandlers, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 36d1ad5..ec27fea 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1045,3 +1045,12 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) return res, nil } + +// GetTransactions retrieves the transactions from the API using the "PublicKey" +func (h *Handlers) GetTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + + res.Content = "Transaction list" + + return res, nil +} \ No newline at end of file From d41ba79ae4f04c527565e3ba2dd3fbcfbbf01247 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 14:51:59 +0300 Subject: [PATCH 02/14] Adde the check_statement node --- services/registration/check_statement | 1 + services/registration/check_statement.vis | 9 +++++++++ services/registration/check_statement_swa | 1 + services/registration/my_account.vis | 1 + 4 files changed, 12 insertions(+) create mode 100644 services/registration/check_statement create mode 100644 services/registration/check_statement.vis create mode 100644 services/registration/check_statement_swa diff --git a/services/registration/check_statement b/services/registration/check_statement new file mode 100644 index 0000000..0e989db --- /dev/null +++ b/services/registration/check_statement @@ -0,0 +1 @@ +Please enter your PIN to view statement: \ No newline at end of file diff --git a/services/registration/check_statement.vis b/services/registration/check_statement.vis new file mode 100644 index 0000000..596dcab --- /dev/null +++ b/services/registration/check_statement.vis @@ -0,0 +1,9 @@ +LOAD authorize_account 6 +MOUT back 0 +MOUT quit 9 +HALT +RELOAD authorize_account +CATCH incorrect_pin flag_incorrect_pin 1 +INCMP _ 0 +INCMP quit 9 +INCMP transactions * diff --git a/services/registration/check_statement_swa b/services/registration/check_statement_swa new file mode 100644 index 0000000..468364f --- /dev/null +++ b/services/registration/check_statement_swa @@ -0,0 +1 @@ +Tafadhali weka PIN yako kuona taarifa ya matumizi: \ No newline at end of file diff --git a/services/registration/my_account.vis b/services/registration/my_account.vis index 43ee6a2..2b6289e 100644 --- a/services/registration/my_account.vis +++ b/services/registration/my_account.vis @@ -11,5 +11,6 @@ INCMP main 0 INCMP edit_profile 1 INCMP change_language 2 INCMP balances 3 +INCMP check_statement 4 INCMP pin_management 5 INCMP address 6 From fc0043e3f6a8b2945abd3899fce035ec233fd91e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 14:52:15 +0300 Subject: [PATCH 03/14] Adde the transactions node --- services/registration/transactions | 1 + services/registration/transactions.vis | 11 +++++++++++ services/registration/transactions_swa | 1 + 3 files changed, 13 insertions(+) create mode 100644 services/registration/transactions create mode 100644 services/registration/transactions.vis create mode 100644 services/registration/transactions_swa diff --git a/services/registration/transactions b/services/registration/transactions new file mode 100644 index 0000000..8152c42 --- /dev/null +++ b/services/registration/transactions @@ -0,0 +1 @@ +{{.get_transactions}} \ No newline at end of file diff --git a/services/registration/transactions.vis b/services/registration/transactions.vis new file mode 100644 index 0000000..2317ff0 --- /dev/null +++ b/services/registration/transactions.vis @@ -0,0 +1,11 @@ +LOAD get_transactions 0 +MAP get_transactions +MOUT back 0 +MOUT quit 99 +MNEXT next 11 +MPREV prev 22 +HALT +INCMP _ 0 +INCMP quit 99 +INCMP > 11 +INCMP < 22 diff --git a/services/registration/transactions_swa b/services/registration/transactions_swa new file mode 100644 index 0000000..8152c42 --- /dev/null +++ b/services/registration/transactions_swa @@ -0,0 +1 @@ +{{.get_transactions}} \ No newline at end of file From 1d9f4fc13e2e44c3b2e058e255da1e433cd56b17 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 15:50:19 +0300 Subject: [PATCH 04/14] added statement related flags --- services/registration/pp.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index 406cc22..cd6a633 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -19,3 +19,5 @@ flag,flag_api_call_error,25,this is set when communication to an external servic flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher flag,flag_admin_privilege,27,this is set when a user has admin privileges. flag,flag_unregistered_number,28,this is set when an unregistered phonenumber tries to perform an action +flag,flag_no_transfers,29,this is set when a user does not have any transactions +flag,flag_incorrect_statement,30,this is set when the selected statement is invalid From d49b68597c6b928a78e38e2bca79af9e6ffc42cd Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:03:54 +0300 Subject: [PATCH 05/14] check transactions and catch when none exist --- services/registration/check_statement.vis | 3 +++ services/registration/no_transfers | 1 + services/registration/no_transfers.vis | 5 +++++ services/registration/no_transfers_swa | 1 + 4 files changed, 10 insertions(+) create mode 100644 services/registration/no_transfers create mode 100644 services/registration/no_transfers.vis create mode 100644 services/registration/no_transfers_swa diff --git a/services/registration/check_statement.vis b/services/registration/check_statement.vis index 596dcab..d79b5ca 100644 --- a/services/registration/check_statement.vis +++ b/services/registration/check_statement.vis @@ -1,3 +1,6 @@ +LOAD check_transactions 0 +RELOAD check_transactions +CATCH no_transfers flag_no_transfers 1 LOAD authorize_account 6 MOUT back 0 MOUT quit 9 diff --git a/services/registration/no_transfers b/services/registration/no_transfers new file mode 100644 index 0000000..3439806 --- /dev/null +++ b/services/registration/no_transfers @@ -0,0 +1 @@ +No transfers history \ No newline at end of file diff --git a/services/registration/no_transfers.vis b/services/registration/no_transfers.vis new file mode 100644 index 0000000..832ef22 --- /dev/null +++ b/services/registration/no_transfers.vis @@ -0,0 +1,5 @@ +MOUT back 0 +MOUT quit 9 +HALT +INCMP ^ 0 +INCMP quit 9 diff --git a/services/registration/no_transfers_swa b/services/registration/no_transfers_swa new file mode 100644 index 0000000..1f82e82 --- /dev/null +++ b/services/registration/no_transfers_swa @@ -0,0 +1 @@ +Hakuna historia kwa akaunti yako \ No newline at end of file From bfcdd79f336c6fb51c5ae3163ee978952d21ada2 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:04:46 +0300 Subject: [PATCH 06/14] view single statement --- services/registration/transactions.vis | 4 ++++ services/registration/view_statement | 1 + services/registration/view_statement.vis | 10 ++++++++++ 3 files changed, 15 insertions(+) create mode 100644 services/registration/view_statement create mode 100644 services/registration/view_statement.vis diff --git a/services/registration/transactions.vis b/services/registration/transactions.vis index 2317ff0..33ccc89 100644 --- a/services/registration/transactions.vis +++ b/services/registration/transactions.vis @@ -5,7 +5,11 @@ MOUT quit 99 MNEXT next 11 MPREV prev 22 HALT +LOAD view_statement 0 +RELOAD view_statement +CATCH . flag_incorrect_statement 1 INCMP _ 0 INCMP quit 99 INCMP > 11 INCMP < 22 +INCMP view_statement * diff --git a/services/registration/view_statement b/services/registration/view_statement new file mode 100644 index 0000000..1285cf9 --- /dev/null +++ b/services/registration/view_statement @@ -0,0 +1 @@ +{{.view_statement}} \ No newline at end of file diff --git a/services/registration/view_statement.vis b/services/registration/view_statement.vis new file mode 100644 index 0000000..4dde523 --- /dev/null +++ b/services/registration/view_statement.vis @@ -0,0 +1,10 @@ +MAP view_statement +MOUT back 0 +MOUT quit 9 +MNEXT next 11 +MPREV prev 22 +HALT +INCMP _ 0 +INCMP quit 9 +INCMP > 11 +INCMP < 22 From 1d82bc2261e6a1540e74aaf293fcf34d3bf68d0a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:07:28 +0300 Subject: [PATCH 07/14] added transactions functions --- common/transfer_statements.go | 115 ++++++++++++++++++ internal/handlers/handlerservice.go | 4 +- internal/handlers/ussd/menuhandler.go | 166 +++++++++++++++++++++++++- 3 files changed, 281 insertions(+), 4 deletions(-) create mode 100644 common/transfer_statements.go diff --git a/common/transfer_statements.go b/common/transfer_statements.go new file mode 100644 index 0000000..cc75e78 --- /dev/null +++ b/common/transfer_statements.go @@ -0,0 +1,115 @@ +package common + +import ( + "context" + "fmt" + "strings" + "time" + + "git.grassecon.net/urdt/ussd/internal/storage" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" +) + +// TransferMetadata helps organize data fields +type TransferMetadata struct { + Senders string + Recipients string + TransferValues string + Addresses string + TxHashes string + Dates string + Symbols string + Decimals string +} + +// ProcessTransfers converts transfers into formatted strings +func ProcessTransfers(transfers []dataserviceapi.Last10TxResponse) TransferMetadata { + var data TransferMetadata + var senders, recipients, transferValues, addresses, txHashes, dates, symbols, decimals []string + + for _, t := range transfers { + senders = append(senders, t.Sender) + recipients = append(recipients, t.Recipient) + + // Scale down the amount + scaledBalance := ScaleDownBalance(t.TransferValue, t.TokenDecimals) + transferValues = append(transferValues, scaledBalance) + + addresses = append(addresses, t.ContractAddress) + txHashes = append(txHashes, t.TxHash) + dates = append(dates, fmt.Sprintf("%s", t.DateBlock)) + symbols = append(symbols, t.TokenSymbol) + decimals = append(decimals, t.TokenDecimals) + } + + data.Senders = strings.Join(senders, "\n") + data.Recipients = strings.Join(recipients, "\n") + data.TransferValues = strings.Join(transferValues, "\n") + data.Addresses = strings.Join(addresses, "\n") + data.TxHashes = strings.Join(txHashes, "\n") + data.Dates = strings.Join(dates, "\n") + data.Symbols = strings.Join(symbols, "\n") + data.Decimals = strings.Join(decimals, "\n") + + return data +} + +// GetTransferData retrieves and matches transfer data +// returns a formatted string of the full transaction/statement +func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, index int) (string, error) { + keys := []string{"txfrom", "txto", "txval", "txaddr", "txhash", "txdate", "txsym"} + data := make(map[string]string) + + for _, key := range keys { + value, err := db.Get(ctx, []byte(key)) + if err != nil { + return "", fmt.Errorf("failed to get %s: %v", key, err) + } + data[key] = string(value) + } + + // Split the data + senders := strings.Split(string(data["txfrom"]), "\n") + recipients := strings.Split(string(data["txto"]), "\n") + values := strings.Split(string(data["txval"]), "\n") + addresses := strings.Split(string(data["txaddr"]), "\n") + hashes := strings.Split(string(data["txhash"]), "\n") + dates := strings.Split(string(data["txdate"]), "\n") + syms := strings.Split(string(data["txsym"]), "\n") + + // Adjust for 0-based indexing + i := index - 1 + transactionType := "received" + party := fmt.Sprintf("from: %s", strings.TrimSpace(senders[i])) + if strings.TrimSpace(senders[i]) == publicKey { + transactionType = "sent" + party = fmt.Sprintf("to: %s", strings.TrimSpace(recipients[i])) + } + + formattedDate := formatDate(strings.TrimSpace(dates[i])) + + // Build the full transaction detail + detail := fmt.Sprintf( + "%s %s %s\n%s\ncontract address: %s\ntxhash: %s\ndate: %s", + transactionType, + strings.TrimSpace(values[i]), + strings.TrimSpace(syms[i]), + party, + strings.TrimSpace(addresses[i]), + strings.TrimSpace(hashes[i]), + formattedDate, + ) + + return detail, nil +} + +// Helper function to format date in desired output +func formatDate(dateStr string) string { + fmt.Println(dateStr) + parsedDate, err := time.Parse("2006-01-02 15:04:05 -0700 MST", dateStr) + if err != nil { + fmt.Println("Error parsing date:", err) + return "" + } + return parsedDate.Format("2006-01-02 03:04:05 PM") +} diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 6d21d88..c77e82c 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -118,7 +118,9 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin) ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin) ls.DbRs.AddLocalFunc("get_current_profile_info", ussdHandlers.GetCurrentProfileInfo) - ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactions) + ls.DbRs.AddLocalFunc("check_transactions", ussdHandlers.CheckTransactions) + ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactionsList) + ls.DbRs.AddLocalFunc("view_statement", ussdHandlers.ViewTransactionStatement) return ussdHandlers, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8b1ceb4..5896b90 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1634,11 +1634,171 @@ func (h *Handlers) GetVoucherDetails(ctx context.Context, sym string, input []by return res, nil } -// GetTransactions retrieves the transactions from the API using the "PublicKey" -func (h *Handlers) GetTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { +// CheckTransactions retrieves the transactions from the API using the "PublicKey" and stores to prefixDb +func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } - res.Content = "Transaction list" + flag_no_transfers, _ := h.flagManager.GetFlag("flag_no_transfers") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_error") + + store := h.userdataStore + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err + } + + // Fetch transactions from the API using the public key + transactionsResp, err := h.accountService.FetchTransactions(ctx, string(publicKey)) + if err != nil { + res.FlagSet = append(res.FlagSet, flag_api_error) + logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) + return res, err + } + + // Return if there are no transactions + if len(transactionsResp) == 0 { + res.FlagSet = append(res.FlagSet, flag_no_transfers) + return res, nil + } + + data := common.ProcessTransfers(transactionsResp) + + // Store all transaction data + dataMap := map[string]string{ + "txfrom": data.Senders, + "txto": data.Recipients, + "txval": data.TransferValues, + "txaddr": data.Addresses, + "txhash": data.TxHashes, + "txdate": data.Dates, + "txsym": data.Symbols, + "txdeci": data.Decimals, + } + + for key, value := range dataMap { + if err := h.prefixDb.Put(ctx, []byte(key), []byte(value)); err != nil { + logg.ErrorCtxf(ctx, "failed to write to prefixDb", "error", err) + return res, err + } + } + + res.FlagReset = append(res.FlagReset, flag_no_transfers) + + return res, nil +} + +// GetTransactionsList fetches the list of transactions and formats them +func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + store := h.userdataStore + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err + } + + // Read transactions from the store and format them + TransactionSenders, err := h.prefixDb.Get(ctx, []byte("txfrom")) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read the TransactionSenders from prefixDb", "error", err) + return res, err + } + TransactionSyms, err := h.prefixDb.Get(ctx, []byte("txsym")) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read the TransactionSyms from prefixDb", "error", err) + return res, err + } + TransactionValues, err := h.prefixDb.Get(ctx, []byte("txval")) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read the TransactionValues from prefixDb", "error", err) + return res, err + } + TransactionDates, err := h.prefixDb.Get(ctx, []byte("txdate")) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read the TransactionDates from prefixDb", "error", err) + return res, err + } + + // Parse the data + senders := strings.Split(string(TransactionSenders), "\n") + syms := strings.Split(string(TransactionSyms), "\n") + values := strings.Split(string(TransactionValues), "\n") + dates := strings.Split(string(TransactionDates), "\n") + + var formattedTransactions []string + for i := 0; i < len(senders); i++ { + sender := strings.TrimSpace(senders[i]) + sym := strings.TrimSpace(syms[i]) + value := strings.TrimSpace(values[i]) + date := strings.Split(strings.TrimSpace(dates[i]), " ")[0] + + status := "received" + if sender == string(publicKey) { + status = "sent" + } + + formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d:%s %s %s %s", i+1, status, value, sym, date)) + } + + res.Content = strings.Join(formattedTransactions, "\n") + + return res, nil +} + +// ViewTransactionStatement retrieves the token holding and balance from the subprefixDB +// and displays it to the user for them to select it +func (h *Handlers) ViewTransactionStatement(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + store := h.userdataStore + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err + } + + flag_incorrect_statement, _ := h.flagManager.GetFlag("flag_incorrect_statement") + + inputStr := string(input) + if inputStr == "0" || inputStr == "99" || inputStr == "11" || inputStr == "22" { + res.FlagReset = append(res.FlagReset, flag_incorrect_statement) + return res, nil + } + + // Convert input string to integer + index, err := strconv.Atoi(strings.TrimSpace(inputStr)) + if err != nil { + return res, fmt.Errorf("invalid input: must be a number between 1 and 10") + } + + if index < 1 || index > 10 { + return res, fmt.Errorf("invalid input: index must be between 1 and 10") + } + + statement, err := common.GetTransferData(ctx, h.prefixDb, string(publicKey), index) + if err != nil { + return res, fmt.Errorf("failed to retrieve transfer data: %v", err) + } + + if statement == "" { + res.FlagSet = append(res.FlagSet, flag_incorrect_statement) + return res, nil + } + + res.FlagReset = append(res.FlagReset, flag_incorrect_statement) + res.Content = statement return res, nil } From 6159686a8e64131393fac364f6b11ccfa160909b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:21:55 +0300 Subject: [PATCH 08/14] update function comment --- internal/handlers/ussd/menuhandler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 5896b90..065da85 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1754,8 +1754,8 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] return res, nil } -// ViewTransactionStatement retrieves the token holding and balance from the subprefixDB -// and displays it to the user for them to select it +// ViewTransactionStatement retrieves the transaction statement +// and displays it to the user func (h *Handlers) ViewTransactionStatement(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) From eab6dbd74c1a7bb961b1ba2aadbc782831e19481 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:23:35 +0300 Subject: [PATCH 09/14] Check if index is within range --- common/transfer_statements.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/transfer_statements.go b/common/transfer_statements.go index cc75e78..eec7e11 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -77,6 +77,11 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, dates := strings.Split(string(data["txdate"]), "\n") syms := strings.Split(string(data["txsym"]), "\n") + // Check if index is within range + if index < 1 || index > len(senders) { + return "", fmt.Errorf("transaction not found: index %d out of range", index) + } + // Adjust for 0-based indexing i := index - 1 transactionType := "received" From 9498242901804bd918a783612fbae9490a8724cd Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:27:19 +0300 Subject: [PATCH 10/14] removed debug statement --- common/transfer_statements.go | 1 - 1 file changed, 1 deletion(-) diff --git a/common/transfer_statements.go b/common/transfer_statements.go index eec7e11..4dd409c 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -110,7 +110,6 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, // Helper function to format date in desired output func formatDate(dateStr string) string { - fmt.Println(dateStr) parsedDate, err := time.Parse("2006-01-02 15:04:05 -0700 MST", dateStr) if err != nil { fmt.Println("Error parsing date:", err) From df89fe69e18096946fe977b3aed95a0d6b0e042d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 17:09:59 +0300 Subject: [PATCH 11/14] return to the main node --- services/registration/transactions.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/transactions.vis b/services/registration/transactions.vis index 33ccc89..b21f1dc 100644 --- a/services/registration/transactions.vis +++ b/services/registration/transactions.vis @@ -8,7 +8,7 @@ HALT LOAD view_statement 0 RELOAD view_statement CATCH . flag_incorrect_statement 1 -INCMP _ 0 +INCMP ^ 0 INCMP quit 99 INCMP > 11 INCMP < 22 From a748c1b6b2f1ece736cbaf23ccf18524d769121b Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Fri, 22 Nov 2024 11:53:13 +0300 Subject: [PATCH 12/14] chore: fix old reference --- internal/testutil/testtag/onlinetest.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/testutil/testtag/onlinetest.go b/internal/testutil/testtag/onlinetest.go index 92cbb14..835ba0d 100644 --- a/internal/testutil/testtag/onlinetest.go +++ b/internal/testutil/testtag/onlinetest.go @@ -2,8 +2,8 @@ package testtag -import "git.grassecon.net/urdt/ussd/internal/handlers/server" +import "git.grassecon.net/urdt/ussd/remote" var ( - AccountService server.AccountServiceInterface + AccountService remote.AccountServiceInterface ) From 2d6c434bde0763e3187c1eac5cb8a7ba64ab92c7 Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Mon, 25 Nov 2024 09:56:24 +0300 Subject: [PATCH 13/14] feat: upgrade go-vise dep, minor cleanups to go.mod * added stub for Dump() --- cmd/africastalking/main.go | 10 +++++----- go.mod | 36 +++++++++++++++--------------------- go.sum | 8 ++++---- internal/storage/gdbm.go | 7 ++++++- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 9f5b501..7eb0497 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -44,14 +44,14 @@ type atRequestParser struct{} func (arp *atRequestParser) GetSessionId(rq any) (string, error) { rqv, ok := rq.(*http.Request) if !ok { - log.Println("got an invalid request:", rq) + log.Printf("got an invalid request:", rq) return "", handlers.ErrInvalidRequest } // Capture body (if any) for logging body, err := io.ReadAll(rqv.Body) if err != nil { - log.Println("failed to read request body:", err) + log.Printf("failed to read request body:", err) return "", fmt.Errorf("failed to read request body: %v", err) } // Reset the body for further reading @@ -61,13 +61,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) { bodyLog := map[string]string{"body": string(body)} logBytes, err := json.Marshal(bodyLog) if err != nil { - log.Println("failed to marshal request body:", err) + log.Printf("failed to marshal request body:", err) } else { - log.Println("Received request:", string(logBytes)) + log.Printf("Received request:", string(logBytes)) } if err := rqv.ParseForm(); err != nil { - log.Println("failed to parse form data: %v", err) + log.Printf("failed to parse form data: %v", err) return "", fmt.Errorf("failed to parse form data: %v", err) } diff --git a/go.mod b/go.mod index 391c1a5..d89f0f3 100644 --- a/go.mod +++ b/go.mod @@ -2,29 +2,16 @@ module git.grassecon.net/urdt/ussd go 1.23.0 -toolchain go1.23.2 - require ( - git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b + git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a github.com/alecthomas/assert/v2 v2.2.2 + github.com/gofrs/uuid v4.4.0+incompatible github.com/grassrootseconomics/eth-custodial v1.3.0-beta - github.com/peteole/testdata-loader v0.3.0 - gopkg.in/leonelquinteros/gotext.v1 v1.3.1 -) - -require github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a // indirect - -require ( - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.7.1 // indirect - github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/grassrootseconomics/ussd-data-service v1.2.0-beta github.com/joho/godotenv v1.5.1 - github.com/kr/text v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.18.0 // indirect + github.com/peteole/testdata-loader v0.3.0 + github.com/stretchr/testify v1.9.0 + gopkg.in/leonelquinteros/gotext.v1 v1.3.1 ) require ( @@ -33,13 +20,20 @@ require ( github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/gofrs/uuid v4.4.0+incompatible github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/stretchr/testify v1.9.0 github.com/x448/float16 v0.8.4 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.18.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0ba38c1..5532c1c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b h1:dxBplsIlzJHV+5EH+gzB+w08Blt7IJbb2jeRe1OEjLU= -git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a h1:LvGKktk0kUnuRN3nF9r15D8OoV0sFaMmQr52kGq2gtE= +git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= @@ -18,8 +18,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1 github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU= 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= diff --git a/internal/storage/gdbm.go b/internal/storage/gdbm.go index 49de570..11979e1 100644 --- a/internal/storage/gdbm.go +++ b/internal/storage/gdbm.go @@ -4,8 +4,8 @@ import ( "context" "git.defalsify.org/vise.git/db" - "git.defalsify.org/vise.git/lang" gdbmdb "git.defalsify.org/vise.git/db/gdbm" + "git.defalsify.org/vise.git/lang" ) var ( @@ -114,3 +114,8 @@ func(tdb *ThreadGdbmDb) Close() error { tdb.db = nil return err } + +func(tdb *ThreadGdbmDb) Dump(_ context.Context, _ []byte) (*db.Dumper, error) { + logg.Warnf("method not implemented for thread gdbm db") + return nil, nil +} From 08ff1056d774433b6f72935751941aa993ba267a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 26 Nov 2024 07:24:57 +0100 Subject: [PATCH 14/14] Validate aliases, addresses and phone numbers in the send menu (#176) - Update the phone number regex - Check whether the recipient is a valid phone number, alias or address Reviewed-on: https://git.grassecon.net/urdt/ussd/pulls/176 Co-authored-by: alfred-mk Co-committed-by: alfred-mk --- cmd/africastalking/main.go | 9 +- common/recipient.go | 73 +++++++++++++ config/config.go | 3 + internal/handlers/ussd/menuhandler.go | 102 ++++++++++++------ internal/testutil/mocks/servicemock.go | 5 + .../testservice/TestAccountService.go | 8 +- menutraversal_test/test_setup.json | 4 +- remote/accountservice.go | 21 ++++ services/registration/send | 2 +- 9 files changed, 186 insertions(+), 41 deletions(-) create mode 100644 common/recipient.go diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 7eb0497..7d1caa8 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 new file mode 100644 index 0000000..f463a32 --- /dev/null +++ b/common/recipient.go @@ -0,0 +1,73 @@ +package common + +import ( + "errors" + "fmt" + "regexp" + "strings" +) + +// 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]+$` +) + +// 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") +} + +// 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, " ", "") + + // Replace leading "0" with "254" if present + if strings.HasPrefix(phone, "0") { + phone = "254" + phone[1:] + } + + // Add "+" if not already present + if !strings.HasPrefix(phone, "254") { + return "", errors.New("unexpected format") + } + + return "+" + phone, nil +} diff --git a/config/config.go b/config/config.go index edb33fe..3a8e8ed 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(dataURLBase, AliasPrefix) return nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 065da85..8d3b928 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 = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}` 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,61 @@ 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") - - res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite) - res.Content = recipient - - return res, nil + 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 } - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) - return res, err - } + // Check if the phone number is registered + 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) + res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite) + res.Content = recipient + return res, nil + } - 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 + 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 + } + + 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 + } } } 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/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/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 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