Compare commits
No commits in common. "19ec8f0817ecbccc29ec7d90c27501b125eeae9d" and "ef3a3d6717c98a17516281e04f271c8fb6e712fd" have entirely different histories.
19ec8f0817
...
ef3a3d6717
@ -20,7 +20,6 @@ import (
|
|||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
"git.defalsify.org/vise.git/resource"
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/common"
|
|
||||||
"git.grassecon.net/urdt/ussd/config"
|
"git.grassecon.net/urdt/ussd/config"
|
||||||
"git.grassecon.net/urdt/ussd/initializers"
|
"git.grassecon.net/urdt/ussd/initializers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
@ -45,14 +44,14 @@ type atRequestParser struct{}
|
|||||||
func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
||||||
rqv, ok := rq.(*http.Request)
|
rqv, ok := rq.(*http.Request)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Printf("got an invalid request:", rq)
|
log.Println("got an invalid request:", rq)
|
||||||
return "", handlers.ErrInvalidRequest
|
return "", handlers.ErrInvalidRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture body (if any) for logging
|
// Capture body (if any) for logging
|
||||||
body, err := io.ReadAll(rqv.Body)
|
body, err := io.ReadAll(rqv.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to read request body:", err)
|
log.Println("failed to read request body:", err)
|
||||||
return "", fmt.Errorf("failed to read request body: %v", err)
|
return "", fmt.Errorf("failed to read request body: %v", err)
|
||||||
}
|
}
|
||||||
// Reset the body for further reading
|
// Reset the body for further reading
|
||||||
@ -62,13 +61,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
|||||||
bodyLog := map[string]string{"body": string(body)}
|
bodyLog := map[string]string{"body": string(body)}
|
||||||
logBytes, err := json.Marshal(bodyLog)
|
logBytes, err := json.Marshal(bodyLog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to marshal request body:", err)
|
log.Println("failed to marshal request body:", err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Received request:", string(logBytes))
|
log.Println("Received request:", string(logBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := rqv.ParseForm(); err != nil {
|
if err := rqv.ParseForm(); err != nil {
|
||||||
log.Printf("failed to parse form data: %v", err)
|
log.Println("failed to parse form data: %v", err)
|
||||||
return "", fmt.Errorf("failed to parse form data: %v", err)
|
return "", fmt.Errorf("failed to parse form data: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,13 +76,7 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
|||||||
return "", fmt.Errorf("no phone number found")
|
return "", fmt.Errorf("no phone number found")
|
||||||
}
|
}
|
||||||
|
|
||||||
formattedNumber, err := common.FormatPhoneNumber(phoneNumber)
|
return phoneNumber, nil
|
||||||
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) {
|
func (arp *atRequestParser) GetInput(rq any) ([]byte, error) {
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,119 +0,0 @@
|
|||||||
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")
|
|
||||||
|
|
||||||
// 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"
|
|
||||||
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 {
|
|
||||||
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")
|
|
||||||
}
|
|
@ -15,7 +15,6 @@ const (
|
|||||||
voucherHoldingsPathPrefix = "/api/v1/holdings"
|
voucherHoldingsPathPrefix = "/api/v1/holdings"
|
||||||
voucherTransfersPathPrefix = "/api/v1/transfers/last10"
|
voucherTransfersPathPrefix = "/api/v1/transfers/last10"
|
||||||
voucherDataPathPrefix = "/api/v1/token"
|
voucherDataPathPrefix = "/api/v1/token"
|
||||||
AliasPrefix = "api/v1/alias"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -33,7 +32,6 @@ var (
|
|||||||
VoucherHoldingsURL string
|
VoucherHoldingsURL string
|
||||||
VoucherTransfersURL string
|
VoucherTransfersURL string
|
||||||
VoucherDataURL string
|
VoucherDataURL string
|
||||||
CheckAliasURL string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setBase() error {
|
func setBase() error {
|
||||||
@ -68,7 +66,6 @@ func LoadConfig() error {
|
|||||||
VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix)
|
VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix)
|
||||||
VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix)
|
VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix)
|
||||||
VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix)
|
VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix)
|
||||||
CheckAliasURL, _ = url.JoinPath(dataURLBase, AliasPrefix)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
34
go.mod
34
go.mod
@ -2,38 +2,44 @@ module git.grassecon.net/urdt/ussd
|
|||||||
|
|
||||||
go 1.23.0
|
go 1.23.0
|
||||||
|
|
||||||
|
toolchain go1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a
|
git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b
|
||||||
github.com/alecthomas/assert/v2 v2.2.2
|
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/grassrootseconomics/eth-custodial v1.3.0-beta
|
||||||
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta
|
|
||||||
github.com/joho/godotenv v1.5.1
|
|
||||||
github.com/peteole/testdata-loader v0.3.0
|
github.com/peteole/testdata-loader v0.3.0
|
||||||
github.com/stretchr/testify v1.9.0
|
|
||||||
gopkg.in/leonelquinteros/gotext.v1 v1.3.1
|
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/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
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/participle/v2 v2.0.0 // indirect
|
github.com/alecthomas/participle/v2 v2.0.0 // indirect
|
||||||
github.com/alecthomas/repr v0.2.0 // indirect
|
github.com/alecthomas/repr v0.2.0 // indirect
|
||||||
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect
|
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/fxamacker/cbor/v2 v2.4.0 // 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/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect
|
||||||
github.com/hexops/gotextdiff v1.0.3 // 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/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 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/objx v0.5.2 // indirect
|
||||||
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
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
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
8
go.sum
8
go.sum
@ -1,5 +1,5 @@
|
|||||||
git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a h1:LvGKktk0kUnuRN3nF9r15D8OoV0sFaMmQr52kGq2gtE=
|
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.20241122120231-9e9ee5bdfa7a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
|
git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
|
||||||
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
|
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/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
|
||||||
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
|
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/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 h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU=
|
||||||
github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
|
github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
|
||||||
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk=
|
github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a h1:q/YH7nE2j8epNmFnTu0tU1vwtCxtQ6nH+d7hRVV5krU=
|
||||||
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta/go.mod h1:omfI0QtUwIdpu9gMcUqLMCG8O1XWjqJGBx1qUMiGWC0=
|
github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a/go.mod h1:hdKaKwqiW6/kphK4j/BhmuRlZDLo1+DYo3gYw5O0siw=
|
||||||
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo=
|
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/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=
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
|
@ -118,9 +118,6 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn
|
|||||||
ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin)
|
ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin)
|
||||||
ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin)
|
ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin)
|
||||||
ls.DbRs.AddLocalFunc("get_current_profile_info", ussdHandlers.GetCurrentProfileInfo)
|
ls.DbRs.AddLocalFunc("get_current_profile_info", ussdHandlers.GetCurrentProfileInfo)
|
||||||
ls.DbRs.AddLocalFunc("check_transactions", ussdHandlers.CheckTransactions)
|
|
||||||
ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactionsList)
|
|
||||||
ls.DbRs.AddLocalFunc("view_statement", ussdHandlers.ViewTransactionStatement)
|
|
||||||
|
|
||||||
return ussdHandlers, nil
|
return ussdHandlers, nil
|
||||||
}
|
}
|
||||||
|
@ -35,17 +35,12 @@ var (
|
|||||||
errResponse *api.ErrResponse
|
errResponse *api.ErrResponse
|
||||||
)
|
)
|
||||||
|
|
||||||
// Define the regex patterns as constants
|
// Define the regex patterns as constants
|
||||||
const (
|
const (
|
||||||
|
phoneRegex = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}`
|
||||||
pinPattern = `^\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
|
// FlagManager handles centralized flag management
|
||||||
type FlagManager struct {
|
type FlagManager struct {
|
||||||
parser *asm.FlagParser
|
parser *asm.FlagParser
|
||||||
@ -100,6 +95,17 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util
|
|||||||
return h, nil
|
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 {
|
func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers {
|
||||||
if h.pe != nil {
|
if h.pe != nil {
|
||||||
panic("persister already set")
|
panic("persister already set")
|
||||||
@ -871,7 +877,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input
|
|||||||
}
|
}
|
||||||
blockedNumber := string(input)
|
blockedNumber := string(input)
|
||||||
_, err = store.ReadEntry(ctx, blockedNumber, common.DATA_PUBLIC_KEY)
|
_, err = store.ReadEntry(ctx, blockedNumber, common.DATA_PUBLIC_KEY)
|
||||||
if !common.IsValidPhoneNumber(blockedNumber) {
|
if !isValidPhoneNumber(blockedNumber) {
|
||||||
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
|
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
@ -892,9 +898,10 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRecipient validates that the given input is valid.
|
// ValidateRecipient validates that the given input is a valid phone number.
|
||||||
func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
|
var err error
|
||||||
store := h.userdataStore
|
store := h.userdataStore
|
||||||
|
|
||||||
sessionId, ok := ctx.Value("SessionId").(string)
|
sessionId, ok := ctx.Value("SessionId").(string)
|
||||||
@ -902,16 +909,13 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by
|
|||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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")
|
||||||
|
|
||||||
if recipient != "0" {
|
if recipient != "0" {
|
||||||
recipientType, err := common.CheckRecipient(recipient)
|
if !isValidPhoneNumber(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.FlagSet = append(res.FlagSet, flag_invalid_recipient)
|
||||||
res.Content = recipient
|
res.Content = recipient
|
||||||
|
|
||||||
@ -925,61 +929,25 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by
|
|||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch recipientType {
|
publicKey, err := store.ReadEntry(ctx, recipient, common.DATA_PUBLIC_KEY)
|
||||||
case "phone number":
|
if err != nil {
|
||||||
// format the phone number
|
if db.IsNotFound(err) {
|
||||||
formattedNumber, err := common.FormatPhoneNumber(recipient)
|
logg.InfoCtxf(ctx, "Unregistered number")
|
||||||
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
|
res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite)
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
res.Content = recipient
|
||||||
|
|
||||||
logg.ErrorCtxf(ctx, "failed on CheckAliasAddress", "error", aliasErr)
|
return res, nil
|
||||||
return res, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alias validation succeeded, save the Ethereum address
|
logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err)
|
||||||
err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(r.Address))
|
return res, err
|
||||||
if err != nil {
|
}
|
||||||
logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", r.Address, "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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1664,172 +1632,3 @@ func (h *Handlers) GetVoucherDetails(ctx context.Context, sym string, input []by
|
|||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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)
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
@ -4,8 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/db"
|
"git.defalsify.org/vise.git/db"
|
||||||
gdbmdb "git.defalsify.org/vise.git/db/gdbm"
|
|
||||||
"git.defalsify.org/vise.git/lang"
|
"git.defalsify.org/vise.git/lang"
|
||||||
|
gdbmdb "git.defalsify.org/vise.git/db/gdbm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -114,8 +114,3 @@ func(tdb *ThreadGdbmDb) Close() error {
|
|||||||
tdb.db = nil
|
tdb.db = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func(tdb *ThreadGdbmDb) Dump(_ context.Context, _ []byte) (*db.Dumper, error) {
|
|
||||||
logg.Warnf("method not implemented for thread gdbm db")
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
@ -47,8 +47,3 @@ func (m *MockAccountService) TokenTransfer(ctx context.Context, amount, from, to
|
|||||||
args := m.Called()
|
args := m.Called()
|
||||||
return args.Get(0).(*models.TokenTransferResponse), args.Error(1)
|
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)
|
|
||||||
}
|
|
||||||
|
@ -33,8 +33,8 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
|
func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) {
|
||||||
return []dataserviceapi.TokenHoldings{
|
return []dataserviceapi.TokenHoldings {
|
||||||
dataserviceapi.TokenHoldings{
|
dataserviceapi.TokenHoldings {
|
||||||
ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
|
ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
|
||||||
TokenSymbol: "SRF",
|
TokenSymbol: "SRF",
|
||||||
TokenDecimals: "6",
|
TokenDecimals: "6",
|
||||||
@ -56,7 +56,3 @@ func (tas *TestAccountService) TokenTransfer(ctx context.Context, amount, from,
|
|||||||
TrackingId: "e034d147-747d-42ea-928d-b5a7cb3426af",
|
TrackingId: "e034d147-747d-42ea-928d-b5a7cb3426af",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m TestAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) {
|
|
||||||
return &dataserviceapi.AliasAddress{}, nil
|
|
||||||
}
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
package testtag
|
package testtag
|
||||||
|
|
||||||
import "git.grassecon.net/urdt/ussd/remote"
|
import "git.grassecon.net/urdt/ussd/internal/handlers/server"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
AccountService remote.AccountServiceInterface
|
AccountService server.AccountServiceInterface
|
||||||
)
|
)
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "1",
|
"input": "1",
|
||||||
"expectedContent": "Enter recipient's phone number/address/alias:\n0:Back"
|
"expectedContent": "Enter recipient's phone number:\n0:Back"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "000",
|
"input": "000",
|
||||||
@ -69,7 +69,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "1",
|
"input": "1",
|
||||||
"expectedContent": "Enter recipient's phone number/address/alias:\n0:Back"
|
"expectedContent": "Enter recipient's phone number:\n0:Back"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "0712345678",
|
"input": "0712345678",
|
||||||
|
@ -24,7 +24,6 @@ type AccountServiceInterface interface {
|
|||||||
FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error)
|
FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error)
|
||||||
VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error)
|
VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error)
|
||||||
TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, 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 {
|
type AccountService struct {
|
||||||
@ -210,26 +209,6 @@ func (as *AccountService) TokenTransfer(ctx context.Context, amount, from, to, t
|
|||||||
return &r, nil
|
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) {
|
func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) {
|
||||||
var okResponse api.OKResponse
|
var okResponse api.OKResponse
|
||||||
var errResponse api.ErrResponse
|
var errResponse api.ErrResponse
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Please enter your PIN to view statement:
|
|
@ -1,12 +0,0 @@
|
|||||||
LOAD check_transactions 0
|
|
||||||
RELOAD check_transactions
|
|
||||||
CATCH no_transfers flag_no_transfers 1
|
|
||||||
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 *
|
|
@ -1 +0,0 @@
|
|||||||
Tafadhali weka PIN yako kuona taarifa ya matumizi:
|
|
@ -11,6 +11,5 @@ INCMP main 0
|
|||||||
INCMP edit_profile 1
|
INCMP edit_profile 1
|
||||||
INCMP change_language 2
|
INCMP change_language 2
|
||||||
INCMP balances 3
|
INCMP balances 3
|
||||||
INCMP check_statement 4
|
|
||||||
INCMP pin_management 5
|
INCMP pin_management 5
|
||||||
INCMP address 6
|
INCMP address 6
|
||||||
|
@ -1 +0,0 @@
|
|||||||
No transfers history
|
|
@ -1,5 +0,0 @@
|
|||||||
MOUT back 0
|
|
||||||
MOUT quit 9
|
|
||||||
HALT
|
|
||||||
INCMP ^ 0
|
|
||||||
INCMP quit 9
|
|
@ -1 +0,0 @@
|
|||||||
Hakuna historia kwa akaunti yako
|
|
@ -19,5 +19,3 @@ 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_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_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_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
|
|
||||||
|
|
@ -1 +1 @@
|
|||||||
Enter recipient's phone number/address/alias:
|
Enter recipient's phone number:
|
@ -1 +0,0 @@
|
|||||||
{{.get_transactions}}
|
|
@ -1,15 +0,0 @@
|
|||||||
LOAD get_transactions 0
|
|
||||||
MAP get_transactions
|
|
||||||
MOUT back 0
|
|
||||||
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 *
|
|
@ -1 +0,0 @@
|
|||||||
{{.get_transactions}}
|
|
@ -1 +0,0 @@
|
|||||||
{{.view_statement}}
|
|
@ -1,10 +0,0 @@
|
|||||||
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
|
|
Loading…
Reference in New Issue
Block a user