forked from grassrootseconomics/visedriver
		
	Merge branch 'master' into swahili-templates-menu
This commit is contained in:
		
						commit
						7676cfd40c
					
				| @ -16,7 +16,3 @@ CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create | ||||
| TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/ | ||||
| BALANCE_URL=https://custodial.sarafu.africa/api/account/status/ | ||||
| TRACK_URL=http://localhost:5003/api/v2/account/status | ||||
| 
 | ||||
| 
 | ||||
| #numbers with privileges to reset others pin | ||||
| ADMIN_NUMBERS=254051722XXX,255012221XXX | ||||
| @ -19,9 +19,9 @@ import ( | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	httpserver "git.grassecon.net/urdt/ussd/internal/http" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -139,7 +139,7 @@ func main() { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	accountService := server.AccountService{} | ||||
| 	accountService := remote.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
|  | ||||
| @ -16,8 +16,8 @@ import ( | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -106,8 +106,7 @@ func main() { | ||||
| 
 | ||||
| 	lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) | ||||
| 	lhs.SetDataStore(&userdataStore) | ||||
| 
 | ||||
| 	accountService := server.AccountService{} | ||||
| 	accountService := remote.AccountService{} | ||||
| 
 | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
|  | ||||
| @ -18,9 +18,9 @@ import ( | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	httpserver "git.grassecon.net/urdt/ussd/internal/http" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -100,7 +100,7 @@ func main() { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	accountService := server.AccountService{} | ||||
| 	accountService := remote.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
|  | ||||
| @ -13,8 +13,8 @@ import ( | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -97,7 +97,7 @@ func main() { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	accountService := server.AccountService{} | ||||
| 	accountService := remote.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
|  | ||||
| @ -1,7 +1,9 @@ | ||||
| package utils | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/logging" | ||||
| ) | ||||
| 
 | ||||
| type DataTyp uint16 | ||||
| @ -23,14 +25,17 @@ const ( | ||||
| 	DATA_RECIPIENT | ||||
| 	DATA_AMOUNT | ||||
| 	DATA_TEMPORARY_VALUE | ||||
| 	DATA_VOUCHER_LIST | ||||
| 	DATA_ACTIVE_SYM | ||||
| 	DATA_ACTIVE_BAL | ||||
| 	DATA_BLOCKED_NUMBER | ||||
| 	DATA_PUBLIC_KEY_REVERSE | ||||
| 	DATA_ACTIVE_DECIMAL | ||||
| 	DATA_ACTIVE_ADDRESS | ||||
| 	DATA_TRANSACTIONS | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	logg = logging.NewVanilla().WithDomain("urdt-common") | ||||
| ) | ||||
| 
 | ||||
| func typToBytes(typ DataTyp) []byte { | ||||
| @ -2,6 +2,7 @@ package common | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/hex" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func NormalizeHex(s string) (string, error) { | ||||
| @ -16,3 +17,15 @@ func NormalizeHex(s string) (string, error) { | ||||
| 	} | ||||
| 	return hex.EncodeToString(r), nil | ||||
| } | ||||
| 
 | ||||
| func IsSameHex(left string, right string) bool { | ||||
| 	bl, err := NormalizeHex(left) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	br, err := NormalizeHex(left) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return strings.Compare(bl, br) == 0 | ||||
| } | ||||
|  | ||||
							
								
								
									
										52
									
								
								common/storage.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								common/storage.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/db" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.defalsify.org/vise.git/persist" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| ) | ||||
| 
 | ||||
| func StoreToDb(store *UserDataStore) db.Db { | ||||
| 	return store.Db | ||||
| } | ||||
| 
 | ||||
| func StoreToPrefixDb(store *UserDataStore, pfx []byte) storage.PrefixDb { | ||||
| 	return storage.NewSubPrefixDb(store.Db, pfx)	 | ||||
| } | ||||
| 
 | ||||
| type StorageServices interface { | ||||
| 	GetPersister(ctx context.Context) (*persist.Persister, error) | ||||
| 	GetUserdataDb(ctx context.Context) (db.Db, error) | ||||
| 	GetResource(ctx context.Context) (resource.Resource, error) | ||||
| 	EnsureDbDir() error | ||||
| } | ||||
| 
 | ||||
| type StorageService struct { | ||||
| 	svc *storage.MenuStorageService | ||||
| } | ||||
| 
 | ||||
| func NewStorageService(dbDir string) *StorageService { | ||||
| 	return &StorageService{ | ||||
| 		svc: storage.NewMenuStorageService(dbDir, ""), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func(ss *StorageService) GetPersister(ctx context.Context) (*persist.Persister, error) { | ||||
| 	return ss.svc.GetPersister(ctx) | ||||
| } | ||||
| 	 | ||||
| func(ss *StorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { | ||||
| 	return ss.svc.GetUserdataDb(ctx) | ||||
| } | ||||
| 
 | ||||
| func(ss *StorageService) GetResource(ctx context.Context) (resource.Resource, error) { | ||||
| 	return nil, errors.New("not implemented") | ||||
| } | ||||
| 
 | ||||
| func(ss *StorageService) EnsureDbDir() error { | ||||
| 	return ss.svc.EnsureDbDir() | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package utils | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| @ -16,7 +16,7 @@ type UserDataStore struct { | ||||
| 	db.Db | ||||
| } | ||||
| 
 | ||||
| // ReadEntry retrieves an entry from the store based on the provided parameters.
 | ||||
| // ReadEntry retrieves an entry to the userdata store.
 | ||||
| func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) { | ||||
| 	store.SetPrefix(db.DATATYPE_USERDATA) | ||||
| 	store.SetSession(sessionId) | ||||
| @ -24,6 +24,8 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ | ||||
| 	return store.Get(ctx, k) | ||||
| } | ||||
| 
 | ||||
| // WriteEntry adds an entry to the userdata store.
 | ||||
| // BUG: this uses sessionId twice
 | ||||
| func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { | ||||
| 	store.SetPrefix(db.DATATYPE_USERDATA) | ||||
| 	store.SetSession(sessionId) | ||||
| @ -1,4 +1,4 @@ | ||||
| package utils | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| @ -37,6 +37,15 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { | ||||
| 	return data | ||||
| } | ||||
| 
 | ||||
| //func StoreVouchers(db storage.PrefixDb, data VoucherMetadata) {
 | ||||
| //		value, err := db.Put(ctx, []byte(key))
 | ||||
| //		if err != nil {
 | ||||
| //			return nil, fmt.Errorf("failed to get %s: %v", key, err)
 | ||||
| //		}
 | ||||
| //		data[key] = string(value)
 | ||||
| //	}
 | ||||
| //}
 | ||||
| 
 | ||||
| // GetVoucherData retrieves and matches voucher data
 | ||||
| func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { | ||||
| 	keys := []string{"sym", "bal", "deci", "addr"} | ||||
| @ -75,6 +84,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, | ||||
| 	decList := strings.Split(decimals, "\n") | ||||
| 	addrList := strings.Split(addresses, "\n") | ||||
| 
 | ||||
| 	logg.Tracef("found" , "symlist", symList, "syms", symbols, "input", input) | ||||
| 	for i, sym := range symList { | ||||
| 		parts := strings.SplitN(sym, ":", 2) | ||||
| 
 | ||||
| @ -125,8 +135,9 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str | ||||
| 	return data, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateVoucherData sets the active voucher data in the DataStore.
 | ||||
| // UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore.
 | ||||
| func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { | ||||
| 	logg.TraceCtxf(ctx, "dtal", "data", data) | ||||
| 	// Active voucher data entries
 | ||||
| 	activeEntries := map[DataTyp][]byte{ | ||||
| 		DATA_ACTIVE_SYM:     []byte(data.TokenSymbol), | ||||
| @ -1,14 +1,14 @@ | ||||
| package utils | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"github.com/alecthomas/assert/v2" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	memdb "git.defalsify.org/vise.git/db/mem" | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| ) | ||||
| @ -132,7 +132,6 @@ func TestStoreTemporaryVoucher(t *testing.T) { | ||||
| 	storedValue, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE) | ||||
| 	require.NoError(t, err) | ||||
| 	require.Equal(t, expectedData, string(storedValue), "Mismatch for key %v", DATA_TEMPORARY_VALUE) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestGetTemporaryVoucherData(t *testing.T) { | ||||
| @ -1,18 +1,70 @@ | ||||
| package config | ||||
| 
 | ||||
| import "git.grassecon.net/urdt/ussd/initializers" | ||||
| import ( | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	createAccountPath = "/api/v2/account/create" | ||||
| 	trackStatusPath = "/api/track" | ||||
| 	balancePathPrefix = "/api/account" | ||||
| 	trackPath = "/api/v2/account/status" | ||||
| 	voucherHoldingsPathPrefix = "/api/v1/holdings" | ||||
| 	voucherTransfersPathPrefix = "/api/v1/transfers/last10" | ||||
| 	voucherDataPathPrefix = "/api/v1/token" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	custodialURLBase string | ||||
| 	dataURLBase string | ||||
| 	CustodialAPIKey string | ||||
| 	DataAPIKey string | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	CreateAccountURL string | ||||
| 	TrackStatusURL   string | ||||
| 	BalanceURL       string | ||||
|     TrackURL         string | ||||
| 	BalanceURL	string | ||||
| 	TrackURL         string | ||||
| 	VoucherHoldingsURL	string | ||||
| 	VoucherTransfersURL	string | ||||
| 	VoucherDataURL	string | ||||
| ) | ||||
| 
 | ||||
| // LoadConfig initializes the configuration values after environment variables are loaded.
 | ||||
| func LoadConfig() { | ||||
| 	CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/create") | ||||
| 	TrackStatusURL = initializers.GetEnv("TRACK_STATUS_URL", "https://custodial.sarafu.africa/api/track/") | ||||
| 	BalanceURL = initializers.GetEnv("BALANCE_URL", "https://custodial.sarafu.africa/api/account/status/") | ||||
|     TrackURL = initializers.GetEnv("TRACK_URL", "http://localhost:5003/api/v2/account/status") | ||||
| func setBase() error { | ||||
| 	var err error | ||||
| 
 | ||||
| 	custodialURLBase = initializers.GetEnv("CUSTODIAL_URL_BASE", "http://localhost:5003") | ||||
| 	dataURLBase = initializers.GetEnv("DATA_URL_BASE", "http://localhost:5006") | ||||
| 	CustodialAPIKey = initializers.GetEnv("CUSTODIAL_API_KEY", "xd") | ||||
| 	DataAPIKey = initializers.GetEnv("DATA_API_KEY", "xd") | ||||
| 
 | ||||
| 	_, err = url.JoinPath(custodialURLBase, "/foo") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	_, err = url.JoinPath(dataURLBase, "/bar") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // LoadConfig initializes the configuration values after environment variables are loaded.
 | ||||
| func LoadConfig() error { | ||||
| 	err := setBase() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	CreateAccountURL, _  = url.JoinPath(custodialURLBase, createAccountPath) | ||||
| 	TrackStatusURL, _ = url.JoinPath(custodialURLBase, trackStatusPath) | ||||
| 	BalanceURL, _ = url.JoinPath(custodialURLBase, balancePathPrefix) | ||||
| 	TrackURL, _ = url.JoinPath(custodialURLBase, trackPath) | ||||
| 	VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) | ||||
| 	VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) | ||||
| 	VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -8,9 +8,10 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/engine" | ||||
| 	"git.defalsify.org/vise.git/persist" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/ussd" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/utils" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| type HandlerService interface { | ||||
| @ -62,7 +63,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { | ||||
| 	ls.UserdataStore = db | ||||
| } | ||||
| 
 | ||||
| func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { | ||||
| func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*ussd.Handlers, error) { | ||||
| 	ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|  | ||||
| @ -1,185 +0,0 @@ | ||||
| package server | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	okResponse  api.OKResponse | ||||
| 	errResponse api.ErrResponse | ||||
| ) | ||||
| 
 | ||||
| type AccountServiceInterface interface { | ||||
| 	CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) | ||||
| 	CreateAccount(ctx context.Context) (*api.OKResponse, error) | ||||
| 	CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) | ||||
| 	TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) | ||||
| 	FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) | ||||
| } | ||||
| 
 | ||||
| type AccountService struct { | ||||
| } | ||||
| 
 | ||||
| // Parameters:
 | ||||
| //   - trackingId: A unique identifier for the account.This should be obtained from a previous call to
 | ||||
| //     CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the
 | ||||
| //     AccountResponse struct can be used here to check the account status during a transaction.
 | ||||
| //
 | ||||
| // Returns:
 | ||||
| //   - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string.
 | ||||
| //   - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
 | ||||
| //     If no error occurs, this will be nil
 | ||||
| func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { | ||||
| 	resp, err := http.Get(config.BalanceURL + trackingId) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	var trackResp models.TrackStatusResponse | ||||
| 	err = json.Unmarshal(body, &trackResp) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &trackResp, nil | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { | ||||
| 	var err error | ||||
| 	// Construct the URL with the path parameter
 | ||||
| 	url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) | ||||
| 	req, err := http.NewRequest("GET", url, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 	req.Header.Set("X-GE-KEY", "xd") | ||||
| 
 | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		errResponse.Description = err.Error() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode >= http.StatusBadRequest { | ||||
| 		err := json.Unmarshal([]byte(body), &errResponse) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors.New(errResponse.Description) | ||||
| 	} | ||||
| 	err = json.Unmarshal([]byte(body), &okResponse) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(okResponse.Result) == 0 { | ||||
| 		return nil, errors.New("Empty api result") | ||||
| 	} | ||||
| 	return &okResponse, nil | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account whose balance needs to be checked.
 | ||||
| func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { | ||||
| 	resp, err := http.Get(config.BalanceURL + publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var balanceResp models.BalanceResponse | ||||
| 	err = json.Unmarshal(body, &balanceResp) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &balanceResp, nil | ||||
| } | ||||
| 
 | ||||
| // CreateAccount creates a new account in the custodial system.
 | ||||
| // Returns:
 | ||||
| //   - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account.
 | ||||
| //     If there is an error during the request or processing, this will be nil.
 | ||||
| //   - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
 | ||||
| //     If no error occurs, this will be nil.
 | ||||
| func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { | ||||
| 	var err error | ||||
| 
 | ||||
| 	// Create a new request
 | ||||
| 	req, err := http.NewRequest("POST", config.CreateAccountURL, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 	req.Header.Set("X-GE-KEY", "xd") | ||||
| 
 | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		errResponse.Description = err.Error() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode >= http.StatusBadRequest { | ||||
| 		err := json.Unmarshal([]byte(body), &errResponse) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors.New(errResponse.Description) | ||||
| 	} | ||||
| 	err = json.Unmarshal([]byte(body), &okResponse) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(okResponse.Result) == 0 { | ||||
| 		return nil, errors.New("Empty api result") | ||||
| 	} | ||||
| 	return &okResponse, nil | ||||
| } | ||||
| 
 | ||||
| // FetchVouchers retrieves the token holdings for a given public key from the custodial holdings API endpoint
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account.
 | ||||
| func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { | ||||
| 	file, err := os.Open("sample_tokens.json") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer file.Close() | ||||
| 	var holdings models.VoucherHoldingResponse | ||||
| 
 | ||||
| 	if err := json.NewDecoder(file).Decode(&holdings); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &holdings, nil | ||||
| } | ||||
| @ -20,8 +20,8 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.defalsify.org/vise.git/state" | ||||
| 	"git.grassecon.net/urdt/ussd/common" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/utils" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| 	"gopkg.in/leonelquinteros/gotext.v1" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| @ -68,18 +68,18 @@ type Handlers struct { | ||||
| 	pe             *persist.Persister | ||||
| 	st             *state.State | ||||
| 	ca             cache.Memory | ||||
| 	userdataStore  utils.DataStore | ||||
| 	userdataStore  common.DataStore | ||||
| 	adminstore     *utils.AdminStore | ||||
| 	flagManager    *asm.FlagParser | ||||
| 	accountService server.AccountServiceInterface | ||||
| 	accountService remote.AccountServiceInterface | ||||
| 	prefixDb       storage.PrefixDb | ||||
| } | ||||
| 
 | ||||
| func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService server.AccountServiceInterface) (*Handlers, error) { | ||||
| func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService remote.AccountServiceInterface) (*Handlers, error) { | ||||
| 	if userdataStore == nil { | ||||
| 		return nil, fmt.Errorf("cannot create handler with nil userdata store") | ||||
| 	} | ||||
| 	userDb := &utils.UserDataStore{ | ||||
| 	userDb := &common.UserDataStore{ | ||||
| 		Db: userdataStore, | ||||
| 	} | ||||
| 	// Instantiate the SubPrefixDb with "vouchers" prefix
 | ||||
| @ -170,16 +170,16 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r | ||||
| 
 | ||||
| func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { | ||||
| 	flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") | ||||
| 	okResponse, err := h.accountService.CreateAccount(ctx) | ||||
| 	r, err := h.accountService.CreateAccount(ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	trackingId := okResponse.Result["trackingId"].(string) | ||||
| 	publicKey := okResponse.Result["publicKey"].(string) | ||||
| 	trackingId := r.TrackingId | ||||
| 	publicKey := r.PublicKey | ||||
| 
 | ||||
| 	data := map[utils.DataTyp]string{ | ||||
| 		utils.DATA_TRACKING_ID: trackingId, | ||||
| 		utils.DATA_PUBLIC_KEY:  publicKey, | ||||
| 	data := map[common.DataTyp]string{ | ||||
| 		common.DATA_TRACKING_ID: trackingId, | ||||
| 		common.DATA_PUBLIC_KEY:  publicKey, | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	for key, value := range data { | ||||
| @ -192,7 +192,7 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) | ||||
| 	err = store.WriteEntry(ctx, publicKeyNormalized, common.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @ -212,7 +212,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	_, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_CREATED) | ||||
| 	_, err = store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_CREATED) | ||||
| 	if err != nil { | ||||
| 		if db.IsNotFound(err) { | ||||
| 			logg.Printf(logging.LVL_INFO, "Creating an account because it doesn't exist") | ||||
| @ -233,11 +233,11 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) | ||||
| 	blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE) | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -289,7 +289,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt | ||||
| 	} | ||||
| 	res.FlagReset = append(res.FlagReset, flag_incorrect_pin) | ||||
| 	store := h.userdataStore | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(accountPIN)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -307,12 +307,12 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	temporaryPin := string(input) | ||||
| 	blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) | ||||
| 	blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) | ||||
| 	err = store.WriteEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -329,7 +329,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt | ||||
| 	flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -338,7 +338,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt | ||||
| 	} else { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_pin_mismatch) | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -360,7 +360,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -372,7 +372,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte | ||||
| 		res.FlagSet = []uint32{flag_pin_mismatch} | ||||
| 	} | ||||
| 
 | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -403,13 +403,13 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) | ||||
| 	flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") | ||||
| 	allowUpdate := h.st.MatchFlag(flag_allow_update, true) | ||||
| 	if allowUpdate { | ||||
| 		temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName)) | ||||
| 		temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(temporaryFirstName)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -426,6 +426,7 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	familyName := string(input) | ||||
| 
 | ||||
| @ -433,13 +434,13 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) | ||||
| 	allowUpdate := h.st.MatchFlag(flag_allow_update, true) | ||||
| 
 | ||||
| 	if allowUpdate { | ||||
| 		temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) | ||||
| 		temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -461,13 +462,13 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou | ||||
| 	allowUpdate := h.st.MatchFlag(flag_allow_update, true) | ||||
| 
 | ||||
| 	if allowUpdate { | ||||
| 		temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(temporaryYob)) | ||||
| 		temporaryYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(temporaryYob)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -491,13 +492,13 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( | ||||
| 	allowUpdate := h.st.MatchFlag(flag_allow_update, true) | ||||
| 
 | ||||
| 	if allowUpdate { | ||||
| 		temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation)) | ||||
| 		temporaryLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(temporaryLocation)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -521,13 +522,13 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re | ||||
| 	allowUpdate := h.st.MatchFlag(flag_allow_update, true) | ||||
| 
 | ||||
| 	if allowUpdate { | ||||
| 		temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender)) | ||||
| 		temporaryGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(temporaryGender)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(gender)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -544,6 +545,7 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 
 | ||||
| 	offerings := string(input) | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| @ -551,13 +553,13 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) | ||||
| 	allowUpdate := h.st.MatchFlag(flag_allow_update, true) | ||||
| 
 | ||||
| 	if allowUpdate { | ||||
| 		temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings)) | ||||
| 		temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(temporaryOfferings)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -601,7 +603,7 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	publicKey, _ := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 
 | ||||
| 	res.Content = string(publicKey) | ||||
| 
 | ||||
| @ -622,7 +624,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res | ||||
| 	flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN) | ||||
| 	AccountPin, err := store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -669,22 +671,21 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b | ||||
| 	} | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey)) | ||||
| 
 | ||||
| 	okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) | ||||
| 	if err != nil { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_api_error) | ||||
| 		return res, err | ||||
| 	} | ||||
| 	res.FlagReset = append(res.FlagReset, flag_api_error) | ||||
| 	isActive := okResponse.Result["active"].(bool) | ||||
| 	if !ok { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	if isActive { | ||||
| 	if r.Active { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_account_success) | ||||
| 		res.FlagReset = append(res.FlagReset, flag_account_pending) | ||||
| 	} else { | ||||
| @ -774,7 +775,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	// get the active sym and active balance
 | ||||
| 	activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) | ||||
| 	activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) | ||||
| 	if err != nil { | ||||
| 		if db.IsNotFound(err) { | ||||
| 			balance := "0.00" | ||||
| @ -785,7 +786,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -797,6 +798,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( | ||||
| 
 | ||||
| func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| 
 | ||||
| 	flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") | ||||
| 
 | ||||
| 	sessionId, ok := ctx.Value("SessionId").(string) | ||||
| @ -807,21 +809,19 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input | ||||
| 	balanceType := strings.Split(symbol, "_")[0] | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey)) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| 	if !balanceResponse.Ok { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_api_error) | ||||
| 		return res, nil | ||||
| 	} | ||||
| 	res.FlagReset = append(res.FlagReset, flag_api_error) | ||||
| 	balance := balanceResponse.Result.Balance | ||||
| 
 | ||||
| 	balance := balanceResponse.Balance | ||||
| 
 | ||||
| 	switch balanceType { | ||||
| 	case "my": | ||||
| @ -841,15 +841,15 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) | ||||
| 	blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_VALUE) | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), common.DATA_TEMPORARY_VALUE) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, string(blockedPhonenumber), utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| @ -874,7 +874,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	blockedNumber := string(input) | ||||
| 	_, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) | ||||
| 	_, err = store.ReadEntry(ctx, blockedNumber, common.DATA_PUBLIC_KEY) | ||||
| 	if !isValidPhoneNumber(blockedNumber) { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_unregistered_number) | ||||
| 		return res, nil | ||||
| @ -888,7 +888,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| @ -918,7 +918,7 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by | ||||
| 			return res, nil | ||||
| 		} | ||||
| 		store := h.userdataStore | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(recipient)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recipient)) | ||||
| 		if err != nil { | ||||
| 			return res, nil | ||||
| 		} | ||||
| @ -941,12 +941,12 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt | ||||
| 	flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") | ||||
| 	flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") | ||||
| 	store := h.userdataStore | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte("")) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("")) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte("")) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte("")) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| @ -968,7 +968,7 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input | ||||
| 
 | ||||
| 	flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") | ||||
| 	store := h.userdataStore | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte("")) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("")) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| @ -990,7 +990,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -1015,7 +1015,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 	var balanceValue float64 | ||||
| 
 | ||||
| 	// retrieve the active balance
 | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -1041,7 +1041,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 
 | ||||
| 	// Format the amount with 2 decimal places before saving
 | ||||
| 	formattedAmount := fmt.Sprintf("%.2f", inputAmount) | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(formattedAmount)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(formattedAmount)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -1059,7 +1059,7 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) | ||||
| 	recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) | ||||
| 
 | ||||
| 	res.Content = string(recipient) | ||||
| 
 | ||||
| @ -1075,7 +1075,7 @@ func (h *Handlers) RetrieveBlockedNumber(ctx context.Context, sym string, input | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	blockedNumber, _ := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) | ||||
| 	blockedNumber, _ := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) | ||||
| 
 | ||||
| 	res.Content = string(blockedNumber) | ||||
| 
 | ||||
| @ -1107,12 +1107,12 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	// retrieve the active symbol
 | ||||
| 	activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) | ||||
| 	activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) | ||||
| 	amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) | ||||
| 
 | ||||
| 	res.Content = fmt.Sprintf("%s %s", string(amount), string(activeSym)) | ||||
| 
 | ||||
| @ -1136,11 +1136,11 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] | ||||
| 	// Use the amount, recipient and sender to call the API and initialize the transaction
 | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) | ||||
| 	amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) | ||||
| 
 | ||||
| 	recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) | ||||
| 	recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) | ||||
| 
 | ||||
| 	activeSym, _ := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) | ||||
| 	activeSym, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) | ||||
| 
 | ||||
| 	res.Content = l.Get("Your request has been sent. %s will receive %s %s from %s.", string(recipient), string(amount), string(activeSym), string(sessionId)) | ||||
| 
 | ||||
| @ -1180,12 +1180,12 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	// Retrieve user data as strings with fallback to defaultValue
 | ||||
| 	firstName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_FIRST_NAME)) | ||||
| 	familyName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_FAMILY_NAME)) | ||||
| 	yob := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_YOB)) | ||||
| 	gender := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_GENDER)) | ||||
| 	location := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_LOCATION)) | ||||
| 	offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_OFFERINGS)) | ||||
| 	firstName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME)) | ||||
| 	familyName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME)) | ||||
| 	yob := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_YOB)) | ||||
| 	gender := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_GENDER)) | ||||
| 	location := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_LOCATION)) | ||||
| 	offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS)) | ||||
| 
 | ||||
| 	// Construct the full name
 | ||||
| 	name := defaultValue | ||||
| @ -1242,11 +1242,11 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by | ||||
| 	flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher") | ||||
| 
 | ||||
| 	// check if the user has an active sym
 | ||||
| 	_, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) | ||||
| 	_, err = store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		if db.IsNotFound(err) { | ||||
| 			publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 			publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 			if err != nil { | ||||
| 				return res, err | ||||
| 			} | ||||
| @ -1254,27 +1254,28 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by | ||||
| 			// Fetch vouchers from the API using the public key
 | ||||
| 			vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) | ||||
| 			if err != nil { | ||||
| 				return res, err | ||||
| 				res.FlagSet = append(res.FlagSet, flag_no_active_voucher) | ||||
| 				return res, nil | ||||
| 			} | ||||
| 
 | ||||
| 			// Return if there is no voucher
 | ||||
| 			if len(vouchersResp.Result.Holdings) == 0 { | ||||
| 			if len(vouchersResp) == 0 { | ||||
| 				res.FlagSet = append(res.FlagSet, flag_no_active_voucher) | ||||
| 				return res, nil | ||||
| 			} | ||||
| 
 | ||||
| 			// Use only the first voucher
 | ||||
| 			firstVoucher := vouchersResp.Result.Holdings[0] | ||||
| 			firstVoucher := vouchersResp[0] | ||||
| 			defaultSym := firstVoucher.TokenSymbol | ||||
| 			defaultBal := firstVoucher.Balance | ||||
| 
 | ||||
| 			// set the active symbol
 | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(defaultSym)) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(defaultSym)) | ||||
| 			if err != nil { | ||||
| 				return res, err | ||||
| 			} | ||||
| 			// set the active balance
 | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(defaultBal)) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(defaultBal)) | ||||
| 			if err != nil { | ||||
| 				return res, err | ||||
| 			} | ||||
| @ -1300,7 +1301,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) | ||||
| 	} | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| @ -1311,7 +1312,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	data := utils.ProcessVouchers(vouchersResp.Result.Holdings) | ||||
| 	data := common.ProcessVouchers(vouchersResp) | ||||
| 
 | ||||
| 	// Store all voucher data
 | ||||
| 	dataMap := map[string]string{ | ||||
| @ -1361,7 +1362,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr) | ||||
| 	metadata, err := common.GetVoucherData(ctx, h.prefixDb, inputStr) | ||||
| 	if err != nil { | ||||
| 		return res, fmt.Errorf("failed to retrieve voucher data: %v", err) | ||||
| 	} | ||||
| @ -1371,7 +1372,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { | ||||
| 	if err := common.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| @ -1391,13 +1392,13 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re | ||||
| 	} | ||||
| 
 | ||||
| 	// Get temporary data
 | ||||
| 	tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) | ||||
| 	tempData, err := common.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Set as active and clear temporary data
 | ||||
| 	if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { | ||||
| 	if err := common.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,10 +0,0 @@ | ||||
| package models | ||||
| 
 | ||||
| type AccountResponse struct { | ||||
| 	Ok          bool   `json:"ok"` | ||||
| 	Description string `json:"description"` // Include the description field
 | ||||
| 	Result      struct { | ||||
| 		PublicKey  string `json:"publicKey"` | ||||
| 		TrackingId string `json:"trackingId"` | ||||
| 	} `json:"result"` | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| package models | ||||
| 
 | ||||
| import "encoding/json" | ||||
| 
 | ||||
| 
 | ||||
| type BalanceResponse struct { | ||||
| 	Ok     bool `json:"ok"` | ||||
| 	Result struct { | ||||
| 		Balance string      `json:"balance"` | ||||
| 		Nonce   json.Number `json:"nonce"` | ||||
| 	} `json:"result"` | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| package models | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| ) | ||||
| type Transaction struct { | ||||
| 	CreatedAt     time.Time   `json:"createdAt"` | ||||
| 	Status        string      `json:"status"` | ||||
| 	TransferValue json.Number `json:"transferValue"` | ||||
| 	TxHash        string      `json:"txHash"` | ||||
| 	TxType        string      `json:"txType"` | ||||
| } | ||||
| 
 | ||||
| type TrackStatusResponse struct { | ||||
| 	Ok     bool `json:"ok"` | ||||
| 	Result struct { | ||||
| 		Transaction struct { | ||||
| 			CreatedAt     time.Time   `json:"createdAt"` | ||||
| 			Status        string      `json:"status"` | ||||
| 			TransferValue json.Number `json:"transferValue"` | ||||
| 			TxHash        string      `json:"txHash"` | ||||
| 			TxType        string      `json:"txType"` | ||||
| 		} | ||||
| 	} `json:"result"` | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| package models | ||||
| 
 | ||||
| import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| 
 | ||||
| type VoucherHoldingResponse struct { | ||||
| 	Ok          bool          `json:"ok"` | ||||
| 	Description string        `json:"description"` | ||||
| 	Result      VoucherResult `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // VoucherResult holds the list of token holdings
 | ||||
| type VoucherResult struct { | ||||
| 	Holdings []dataserviceapi.TokenHoldings `json:"holdings"` | ||||
| } | ||||
| @ -11,11 +11,11 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/logging" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/testutil/testservice" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/testutil/testtag" | ||||
| 	testdataloader "github.com/peteole/testdata-loader" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -83,7 +83,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { | ||||
| 	} | ||||
| 
 | ||||
| 	if testtag.AccountService == nil { | ||||
| 		testtag.AccountService = &server.AccountService{} | ||||
| 		testtag.AccountService = &remote.AccountService{} | ||||
| 	} | ||||
| 
 | ||||
| 	switch testtag.AccountService.(type) { | ||||
| @ -91,7 +91,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { | ||||
| 		go func() { | ||||
| 			eventChannel <- false | ||||
| 		}() | ||||
| 	case *server.AccountService: | ||||
| 	case *remote.AccountService: | ||||
| 		go func() { | ||||
| 			time.Sleep(5 * time.Second) // Wait for 5 seconds
 | ||||
| 			eventChannel <- true | ||||
|  | ||||
| @ -1,59 +0,0 @@ | ||||
| package mocks | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/lang" | ||||
| 	"github.com/stretchr/testify/mock" | ||||
| ) | ||||
| 
 | ||||
| type MockDb struct { | ||||
| 	mock.Mock | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) SetPrefix(prefix uint8) { | ||||
| 	m.Called(prefix) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) Prefix() uint8 { | ||||
| 	args := m.Called() | ||||
| 	return args.Get(0).(uint8) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) Safe() bool { | ||||
| 	args := m.Called() | ||||
| 	return args.Get(0).(bool) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) SetLanguage(language *lang.Language) { | ||||
| 	m.Called(language) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) SetLock(uint8, bool) error { | ||||
| 	args := m.Called() | ||||
| 	return args.Error(0) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) Connect(ctx context.Context, connectionStr string) error { | ||||
| 	args := m.Called(ctx, connectionStr) | ||||
| 	return args.Error(0) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) SetSession(sessionId string) { | ||||
| 	m.Called(sessionId) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) Put(ctx context.Context, key, value []byte) error { | ||||
| 	args := m.Called(ctx, key, value) | ||||
| 	return args.Error(0) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) Get(ctx context.Context, key []byte) ([]byte, error) { | ||||
| 	args := m.Called(ctx, key) | ||||
| 	return nil, args.Error(0) | ||||
| } | ||||
| 
 | ||||
| func (m *MockDb) Close() error { | ||||
| 	args := m.Called(nil) | ||||
| 	return args.Error(0) | ||||
| } | ||||
| @ -3,8 +3,8 @@ package mocks | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| 	"git.grassecon.net/urdt/ussd/models" | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| 	"github.com/stretchr/testify/mock" | ||||
| ) | ||||
| 
 | ||||
| @ -13,28 +13,33 @@ type MockAccountService struct { | ||||
| 	mock.Mock | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { | ||||
| func (m *MockAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { | ||||
| 	args := m.Called() | ||||
| 	return args.Get(0).(*api.OKResponse), args.Error(1) | ||||
| 	return args.Get(0).(*models.AccountResult), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { | ||||
| func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { | ||||
| 	args := m.Called(publicKey) | ||||
| 	return args.Get(0).(*models.BalanceResponse), args.Error(1) | ||||
| 	return args.Get(0).(*models.BalanceResult), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { | ||||
| func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { | ||||
| 	args := m.Called(trackingId) | ||||
| 	return args.Get(0).(*models.TrackStatusResponse), args.Error(1) | ||||
| 	return args.Get(0).(*models.TrackStatusResult), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) TrackAccountStatus(ctx context.Context,publicKey string) (*api.OKResponse, error) { | ||||
| 
 | ||||
| func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { | ||||
| 	args := m.Called(publicKey) | ||||
| 	return args.Get(0).(*api.OKResponse), args.Error(1) | ||||
| 	return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { | ||||
| func (m *MockAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { | ||||
| 	args := m.Called(publicKey) | ||||
| 	return args.Get(0).(*models.VoucherHoldingResponse), args.Error(1) | ||||
| 	return args.Get(0).([]dataserviceapi.Last10TxResponse), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func(m MockAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { | ||||
| 	args := m.Called(address) | ||||
| 	return args.Get(0).(*models.VoucherDataResult), args.Error(1) | ||||
| } | ||||
|  | ||||
| @ -1,21 +0,0 @@ | ||||
| package mocks | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/mock" | ||||
| ) | ||||
| 
 | ||||
| type MockSubPrefixDb struct { | ||||
| 	mock.Mock | ||||
| } | ||||
| 
 | ||||
| func (m *MockSubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { | ||||
| 	args := m.Called(ctx, key) | ||||
| 	return args.Get(0).([]byte), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockSubPrefixDb) Put(ctx context.Context, key, val []byte) error { | ||||
| 	args := m.Called(ctx, key, val) | ||||
| 	return args.Error(0) | ||||
| } | ||||
| @ -1,24 +0,0 @@ | ||||
| package mocks | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/db" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/utils" | ||||
| 	"github.com/stretchr/testify/mock" | ||||
| ) | ||||
| 
 | ||||
| type MockUserDataStore struct { | ||||
| 	db.Db | ||||
| 	mock.Mock | ||||
| } | ||||
| 
 | ||||
| func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ utils.DataTyp) ([]byte, error) { | ||||
| 	args := m.Called(ctx, sessionId, typ) | ||||
| 	return args.Get(0).([]byte), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockUserDataStore) WriteEntry(ctx context.Context, sessionId string, typ utils.DataTyp, value []byte) error { | ||||
| 	args := m.Called(ctx, sessionId, typ, value) | ||||
| 	return args.Error(0) | ||||
| } | ||||
| @ -3,88 +3,50 @@ package testservice | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| 	"git.grassecon.net/urdt/ussd/models" | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| ) | ||||
| 
 | ||||
| type TestAccountService struct { | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { | ||||
| 	return &api.OKResponse{ | ||||
| 		Ok:          true, | ||||
| 		Description: "Account creation succeeded", | ||||
| 		Result: map[string]any{ | ||||
| 			"trackingId": "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", | ||||
| 			"publicKey":  "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", | ||||
| 		}, | ||||
| func (tas *TestAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { | ||||
| 	return &models.AccountResult { | ||||
| 		TrackingId: "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", | ||||
| 		PublicKey: "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { | ||||
| 	balanceResponse := &models.BalanceResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: struct { | ||||
| 			Balance string      `json:"balance"` | ||||
| 			Nonce   json.Number `json:"nonce"` | ||||
| 		}{ | ||||
| 			Balance: "0.003 CELO", | ||||
| 			Nonce:   json.Number("0"), | ||||
| 		}, | ||||
| func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { | ||||
| 	balanceResponse := &models.BalanceResult { | ||||
| 		Balance: "0.003 CELO", | ||||
| 		Nonce:   json.Number("0"), | ||||
| 	} | ||||
| 
 | ||||
| 	return balanceResponse, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { | ||||
| 	trackResponse := &models.TrackStatusResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: struct { | ||||
| 			Transaction struct { | ||||
| 				CreatedAt     time.Time   "json:\"createdAt\"" | ||||
| 				Status        string      "json:\"status\"" | ||||
| 				TransferValue json.Number "json:\"transferValue\"" | ||||
| 				TxHash        string      "json:\"txHash\"" | ||||
| 				TxType        string      "json:\"txType\"" | ||||
| 			} | ||||
| 		}{ | ||||
| 			Transaction: models.Transaction{ | ||||
| 				CreatedAt:     time.Now(), | ||||
| 				Status:        "SUCCESS", | ||||
| 				TransferValue: json.Number("0.5"), | ||||
| 				TxHash:        "0x123abc456def", | ||||
| 				TxType:        "transfer", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return trackResponse, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { | ||||
| 	return &api.OKResponse{ | ||||
| 		Ok:          true, | ||||
| 		Description: "Account creation succeeded", | ||||
| 		Result: map[string]any{ | ||||
| 			"active": true, | ||||
| 		}, | ||||
| func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { | ||||
| 	return &models.TrackStatusResult { | ||||
| 		Active: true, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { | ||||
| 	return &models.VoucherHoldingResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: models.VoucherResult{ | ||||
| 			Holdings: []dataserviceapi.TokenHoldings{ | ||||
| 				{ | ||||
| 					ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", | ||||
| 					TokenSymbol:     "SRF", | ||||
| 					TokenDecimals:   "6", | ||||
| 					Balance:         "2745987", | ||||
| 				}, | ||||
| 			}, | ||||
| func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { | ||||
| 	return []dataserviceapi.TokenHoldings { | ||||
| 		dataserviceapi.TokenHoldings { | ||||
| 			ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", | ||||
| 			TokenSymbol:     "SRF", | ||||
| 			TokenDecimals:   "6", | ||||
| 			Balance:         "2745987", | ||||
| 		}, | ||||
| 	}, nil | ||||
| 	}, nil  | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { | ||||
| 	return []dataserviceapi.Last10TxResponse{}, nil | ||||
| } | ||||
| 
 | ||||
| func(m TestAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { | ||||
| 	return &models.VoucherDataResult{}, nil | ||||
| } | ||||
|  | ||||
| @ -3,10 +3,10 @@ | ||||
| package testtag | ||||
| 
 | ||||
| import ( | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| 	accountservice "git.grassecon.net/urdt/ussd/internal/testutil/testservice" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	AccountService server.AccountServiceInterface = &accountservice.TestAccountService{} | ||||
| 	AccountService remote.AccountServiceInterface = &accountservice.TestAccountService{} | ||||
| ) | ||||
|  | ||||
							
								
								
									
										6
									
								
								models/accountresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								models/accountresponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| package models | ||||
| 
 | ||||
| type AccountResult struct { | ||||
| 	PublicKey string `json:"publicKey"` | ||||
| 	TrackingId string `json:"trackingId"` | ||||
| } | ||||
							
								
								
									
										9
									
								
								models/balanceresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								models/balanceresponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| package models | ||||
| 
 | ||||
| import "encoding/json" | ||||
| 
 | ||||
| 
 | ||||
| type BalanceResult struct { | ||||
| 	Balance string      `json:"balance"` | ||||
| 	Nonce   json.Number `json:"nonce"` | ||||
| } | ||||
							
								
								
									
										18
									
								
								models/trackstatusresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								models/trackstatusresponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| package models | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type Transaction struct { | ||||
| 	CreatedAt     time.Time   `json:"createdAt"` | ||||
| 	Status        string      `json:"status"` | ||||
| 	TransferValue json.Number `json:"transferValue"` | ||||
| 	TxHash        string      `json:"txHash"` | ||||
| 	TxType        string      `json:"txType"` | ||||
| } | ||||
| 
 | ||||
| type TrackStatusResult struct { | ||||
| 	Active	bool `json:"active"` | ||||
| } | ||||
							
								
								
									
										21
									
								
								models/vouchersresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								models/vouchersresponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package models | ||||
| 
 | ||||
| import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| 
 | ||||
| //type VoucherHoldingResponse struct {
 | ||||
| //	Ok          bool          `json:"ok"`
 | ||||
| //	Description string        `json:"description"`
 | ||||
| //	Result      VoucherResult `json:"result"`
 | ||||
| //}
 | ||||
| 
 | ||||
| // VoucherResult holds the list of token holdings
 | ||||
| type VoucherResult struct { | ||||
| 	Holdings []dataserviceapi.TokenHoldings `json:"holdings"` | ||||
| } | ||||
| 
 | ||||
| type VoucherDataResult struct { | ||||
| 	TokenName	string `json:"tokenName"` | ||||
| 	TokenSymbol     string `json:"tokenSymbol"` | ||||
| 	TokenDecimals   string `json:"tokenDecimals"` | ||||
| 	SinkAddress	string `json:"sinkAddress"` | ||||
| } | ||||
							
								
								
									
										223
									
								
								remote/accountservice.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								remote/accountservice.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,223 @@ | ||||
| package remote | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/models" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| ) | ||||
| 
 | ||||
| type AccountServiceInterface interface { | ||||
| 	CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) | ||||
| 	CreateAccount(ctx context.Context) (*models.AccountResult, error) | ||||
| 	TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) | ||||
| 	FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) | ||||
| 	FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) | ||||
| 	VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) | ||||
| } | ||||
| 
 | ||||
| type AccountService struct { | ||||
| } | ||||
| 
 | ||||
| // Parameters:
 | ||||
| //   - trackingId: A unique identifier for the account.This should be obtained from a previous call to
 | ||||
| //     CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the
 | ||||
| //     AccountResponse struct can be used here to check the account status during a transaction.
 | ||||
| //
 | ||||
| // Returns:
 | ||||
| //   - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string.
 | ||||
| //   - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
 | ||||
| //     If no error occurs, this will be nil
 | ||||
| func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { | ||||
| 	var r models.TrackStatusResult | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.TrackURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err =  doCustodialRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &r, nil | ||||
| } | ||||
| 
 | ||||
| // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account whose balance needs to be checked.
 | ||||
| func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { | ||||
| 	var balanceResult models.BalanceResult | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.BalanceURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doCustodialRequest(ctx, req, &balanceResult) | ||||
| 	return &balanceResult, err | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // CreateAccount creates a new account in the custodial system.
 | ||||
| // Returns:
 | ||||
| //   - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account.
 | ||||
| //     If there is an error during the request or processing, this will be nil.
 | ||||
| //   - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
 | ||||
| //     If no error occurs, this will be nil.
 | ||||
| func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { | ||||
| 	var r models.AccountResult | ||||
| 	// Create a new request
 | ||||
| 	req, err := http.NewRequest("POST", config.CreateAccountURL, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err =  doCustodialRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &r, nil | ||||
| } | ||||
| 
 | ||||
| // FetchVouchers retrieves the token holdings for a given public key from the data indexer API endpoint
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account.
 | ||||
| func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { | ||||
| 	var r []dataserviceapi.TokenHoldings | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherHoldingsURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err =  doDataRequest(ctx, req, r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account.
 | ||||
| func (as *AccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { | ||||
| 	var r []dataserviceapi.Last10TxResponse | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherTransfersURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err =  doDataRequest(ctx, req, r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // VoucherData retrieves voucher metadata from the data indexer API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - address: The voucher address.
 | ||||
| func (as *AccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { | ||||
| 	var voucherDataResult models.VoucherDataResult | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherDataURL, address) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doCustodialRequest(ctx, req, &voucherDataResult) | ||||
| 	return &voucherDataResult, err | ||||
| } | ||||
| 
 | ||||
| func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { | ||||
| 	var okResponse  api.OKResponse | ||||
| 	var errResponse api.ErrResponse | ||||
| 
 | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		errResponse.Description = err.Error() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode >= http.StatusBadRequest { | ||||
| 		err := json.Unmarshal([]byte(body), &errResponse) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors.New(errResponse.Description) | ||||
| 	} | ||||
| 	err = json.Unmarshal([]byte(body), &okResponse) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(okResponse.Result) == 0 { | ||||
| 		return nil, errors.New("Empty api result") | ||||
| 	} | ||||
| 
 | ||||
| 	v, err := json.Marshal(okResponse.Result) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	err = json.Unmarshal(v, &rcpt) | ||||
| 	return &okResponse, err | ||||
| } | ||||
| 
 | ||||
| func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { | ||||
| 	req.Header.Set("X-GE-KEY", config.CustodialAPIKey) | ||||
| 	return doRequest(ctx, req, rcpt) | ||||
| } | ||||
| 
 | ||||
| func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { | ||||
| 	req.Header.Set("X-GE-KEY", config.DataAPIKey) | ||||
| 	return doRequest(ctx, req, rcpt) | ||||
| } | ||||
| @ -1 +1 @@ | ||||
| You need a voucher to send | ||||
| You need a voucher to proceed | ||||
| @ -1 +1 @@ | ||||
| Unahitaji sarafu kutuma | ||||
| Unahitaji sarafu kuendelea | ||||
| @ -1,3 +1,4 @@ | ||||
| CATCH no_voucher flag_no_active_voucher 1 | ||||
| LOAD get_vouchers 0 | ||||
| MAP get_vouchers | ||||
| MOUT back 0 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user