Compare commits
	
		
			33 Commits
		
	
	
		
			master
			...
			menu-optio
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| fd54b4e0df | |||
| 0f9fc6c51b | |||
| 6f36b2352d | |||
| 6d3ec5306c | |||
| bb28112f47 | |||
| 5b5352f569 | |||
| e3e8bfe85c | |||
| 10c917b6da | |||
| 7c7150b103 | |||
| 99fcb9706e | |||
| 67062a41ad | |||
| 123fdec009 | |||
| 20694d956b | |||
| 10abad9e59 | |||
| ca366ee2bc | |||
| c7f0ddec9b | |||
| 2fe4ada5d3 | |||
| b0342936e1 | |||
| 63d060afe2 | |||
| 92d212f891 | |||
| b25288db2c | |||
| 5aed7c647f | |||
| 8765077177 | |||
| 4a6e4ebe55 | |||
| 525eee93d4 | |||
| 0c3ef357df | |||
| c2d2bd250a | |||
| cb2254664d | |||
| f010d097ed | |||
| c5fcb79e9e | |||
| cb77e44cbd | |||
| 4d7c584394 | |||
| 5ff06e8626 | 
| @ -71,6 +71,9 @@ func getHandler(appFlags *asm.FlagParser, rs *resource.DbResource, pe *persist.P | ||||
| 	rs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob) | ||||
| 	rs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit) | ||||
| 	rs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction) | ||||
| 	rs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) | ||||
| 	rs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin) | ||||
| 	rs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) | ||||
| 
 | ||||
| 	return ussdHandlers, nil | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
								
							| @ -4,22 +4,14 @@ go 1.22.6 | ||||
| 
 | ||||
| require ( | ||||
| 	git.defalsify.org/vise.git v0.1.0-rc.3.0.20240911162138-1f2af8672dc7 | ||||
| 	github.com/alecthomas/assert/v2 v2.2.2 | ||||
| 	gopkg.in/leonelquinteros/gotext.v1 v1.3.1 | ||||
| ) | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/alecthomas/participle/v2 v2.0.0 // indirect | ||||
| 	github.com/alecthomas/repr v0.2.0 // indirect | ||||
| 	github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect | ||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
| 	github.com/fxamacker/cbor/v2 v2.4.0 // indirect | ||||
| 	github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect | ||||
| 	github.com/hexops/gotextdiff v1.0.3 // indirect | ||||
| 	github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||
| 	github.com/stretchr/objx v0.5.2 // indirect | ||||
| 	github.com/stretchr/testify v1.9.0 | ||||
| 	github.com/x448/float16 v0.8.4 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|  | ||||
							
								
								
									
										12
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								go.sum
									
									
									
									
									
								
							| @ -8,8 +8,6 @@ github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk | ||||
| github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= | ||||
| github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE= | ||||
| github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= | ||||
| github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= | ||||
| github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo= | ||||
| @ -20,17 +18,7 @@ github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM | ||||
| github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk= | ||||
| github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I= | ||||
| github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= | ||||
| github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= | ||||
| github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||
| github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= | ||||
| github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc= | ||||
| gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
|  | ||||
| @ -16,10 +16,9 @@ type AccountServiceInterface interface { | ||||
| } | ||||
| 
 | ||||
| type AccountService struct { | ||||
| 	Client *http.Client | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID.
 | ||||
| //
 | ||||
| // Parameters:
 | ||||
| @ -27,14 +26,12 @@ type AccountService struct { | ||||
| //     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(trackingId string) (string, error) { | ||||
| 	resp, err := http.Get(config.TrackStatusURL + trackingId) | ||||
| 	resp, err := as.Client.Get(config.TrackStatusURL + trackingId) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| @ -50,18 +47,15 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (string, error) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 
 | ||||
| 	status := trackResp.Result.Transaction.Status | ||||
| 
 | ||||
| 	return status, 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(publicKey string) (string, error) { | ||||
| 
 | ||||
| 	resp, err := http.Get(config.BalanceURL + publicKey) | ||||
| 	if err != nil { | ||||
| 		return "0.0", err | ||||
| @ -83,15 +77,14 @@ func (as *AccountService) CheckBalance(publicKey string) (string, error) { | ||||
| 	return balance, nil | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //CreateAccount creates a new account in the custodial system.
 | ||||
| // 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() (*models.AccountResponse, error) { | ||||
| 	resp, err := http.Post(config.CreateAccountURL, "application/json", nil) | ||||
| 	resp, err := as.Client.Post(config.CreateAccountURL, "application/json", nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -107,6 +100,5 @@ func (as *AccountService) CreateAccount() (*models.AccountResponse, error) { | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &accountResp, nil | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,7 @@ import ( | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"path" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| @ -27,6 +28,8 @@ var ( | ||||
| 	logg           = logging.NewVanilla().WithDomain("ussdmenuhandler") | ||||
| 	scriptDir      = path.Join("services", "registration") | ||||
| 	translationDir = path.Join(scriptDir, "locale") | ||||
| 	validPin =  4 | ||||
| 	validYOB = 4 | ||||
| ) | ||||
| 
 | ||||
| type FSData struct { | ||||
| @ -73,10 +76,13 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db) (*Handlers, erro | ||||
| 	userDb := &utils.UserDataStore{ | ||||
| 		Db: userdataStore, | ||||
| 	} | ||||
| 	client := &http.Client{} | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore:      userDb, | ||||
| 		flagManager:        appFlags, | ||||
| 		accountService:     &server.AccountService{}, | ||||
| 		userdataStore: userDb, | ||||
| 		flagManager:   appFlags, | ||||
| 		accountService: &server.AccountService{ | ||||
| 			Client: client, | ||||
| 		}, | ||||
| 	} | ||||
| 	return h, nil | ||||
| } | ||||
| @ -188,6 +194,48 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| 	var err error | ||||
| 
 | ||||
| 	sessionId, ok := ctx.Value("SessionId").(string) | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") | ||||
| 
 | ||||
| 	accountPIN := string(input) | ||||
| 	// Validate that the PIN is a 4-digit number
 | ||||
| 	if !isValidPIN(accountPIN) { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_incorrect_pin) | ||||
| 		return res, nil | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| func (h *Handlers) ConfirmPinChange(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 | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // SavePin persists the user's PIN choice into the filesystem
 | ||||
| func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| @ -256,15 +304,19 @@ func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (res | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 
 | ||||
| 	//AccountPin, _ := utils.ReadEntry(ctx, h.userdataStore, sessionId, utils.DATA_ACCOUNT_PIN)
 | ||||
| 	store := h.userdataStore | ||||
| 	AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	TemporaryPIn, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) | ||||
| 	if err != nil { | ||||
| 		if !db.IsNotFound(err) { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if bytes.Equal(input, AccountPin) { | ||||
| 	if bytes.Equal(input, AccountPin) || bytes.Equal(input, TemporaryPIn) { | ||||
| 		res.FlagSet = []uint32{flag_valid_pin} | ||||
| 		res.FlagReset = []uint32{flag_pin_mismatch} | ||||
| 		res.FlagSet = append(res.FlagSet, flag_pin_set) | ||||
| @ -322,9 +374,6 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return res, nil | ||||
| 		} | ||||
| 	} else { | ||||
| 		return res, fmt.Errorf("a family name cannot be less than one character") | ||||
| 	} | ||||
| @ -341,7 +390,7 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 
 | ||||
| 	if len(input) == 4 { | ||||
| 	if len(input) == validPin { | ||||
| 		yob := string(input) | ||||
| 		store := h.userdataStore | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(yob)) | ||||
| @ -481,22 +530,19 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err == nil { | ||||
| 		if len(input) == 4 { | ||||
| 			if bytes.Equal(input, AccountPin) { | ||||
| 				if h.st.MatchFlag(flag_account_authorized, false) { | ||||
| 					res.FlagReset = append(res.FlagReset, flag_incorrect_pin) | ||||
| 					res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized) | ||||
| 				} else { | ||||
| 					res.FlagSet = append(res.FlagSet, flag_allow_update) | ||||
| 					res.FlagReset = append(res.FlagReset, flag_account_authorized) | ||||
| 				} | ||||
| 	if len(input) == validPin { | ||||
| 		if bytes.Equal(input, AccountPin) { | ||||
| 			if h.st.MatchFlag(flag_account_authorized, false) { | ||||
| 				res.FlagReset = append(res.FlagReset, flag_incorrect_pin) | ||||
| 				res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized) | ||||
| 			} else { | ||||
| 				res.FlagSet = append(res.FlagSet, flag_incorrect_pin) | ||||
| 				res.FlagSet = append(res.FlagSet, flag_allow_update) | ||||
| 				res.FlagReset = append(res.FlagReset, flag_account_authorized) | ||||
| 				return res, nil | ||||
| 			} | ||||
| 		} else { | ||||
| 			res.FlagSet = append(res.FlagSet, flag_incorrect_pin) | ||||
| 			res.FlagReset = append(res.FlagReset, flag_account_authorized) | ||||
| 			return res, nil | ||||
| 		} | ||||
| 	} else { | ||||
| 		return res, nil | ||||
| @ -543,7 +589,6 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if status == "SUCCESS" { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_account_success) | ||||
| 		res.FlagReset = append(res.FlagReset, flag_account_pending) | ||||
| @ -569,6 +614,21 @@ func (h *Handlers) Quit(ctx context.Context, sym string, input []byte) (resource | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // QuitWithHelp displays helpline information then exits the menu
 | ||||
| func (h *Handlers) QuitWithHelp(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| 
 | ||||
| 	flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") | ||||
| 
 | ||||
| 	code := codeFromCtx(ctx) | ||||
| 	l := gotext.NewLocale(translationDir, code) | ||||
| 	l.AddDomain("default") | ||||
| 
 | ||||
| 	res.Content = l.Get("For more help,please call: 0757628885") | ||||
| 	res.FlagReset = append(res.FlagReset, flag_account_authorized) | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // VerifyYob verifies the length of the given input
 | ||||
| func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| @ -584,7 +644,7 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if len(date) == 4 { | ||||
| 	if len(date) == validYOB { | ||||
| 		res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) | ||||
| 	} else { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_incorrect_date_format) | ||||
| @ -748,7 +808,8 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 
 | ||||
| 	flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") | ||||
| 
 | ||||
| 	publicKey, _ := utils.ReadEntry(ctx, h.userdataStore, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 
 | ||||
| 	amountStr := string(input) | ||||
| 
 | ||||
| @ -757,6 +818,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	res.Content = balanceStr | ||||
| 
 | ||||
| 	// Parse the balance
 | ||||
| @ -764,6 +826,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 	if len(balanceParts) != 2 { | ||||
| 		return res, fmt.Errorf("unexpected balance format: %s", balanceStr) | ||||
| 	} | ||||
| 
 | ||||
| 	balanceValue, err := strconv.ParseFloat(balanceParts[0], 64) | ||||
| 	if err != nil { | ||||
| 		return res, fmt.Errorf("failed to parse balance: %v", err) | ||||
| @ -773,6 +836,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 	re := regexp.MustCompile(`^(\d+(\.\d+)?)\s*(?:CELO)?$`) | ||||
| 	matches := re.FindStringSubmatch(strings.TrimSpace(amountStr)) | ||||
| 	if len(matches) < 2 { | ||||
| 
 | ||||
| 		res.FlagSet = append(res.FlagSet, flag_invalid_amount) | ||||
| 		res.Content = amountStr | ||||
| 		return res, nil | ||||
| @ -780,6 +844,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 
 | ||||
| 	inputAmount, err := strconv.ParseFloat(matches[1], 64) | ||||
| 	if err != nil { | ||||
| 
 | ||||
| 		res.FlagSet = append(res.FlagSet, flag_invalid_amount) | ||||
| 		res.Content = amountStr | ||||
| 		return res, nil | ||||
| @ -792,7 +857,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 	} | ||||
| 
 | ||||
| 	res.Content = fmt.Sprintf("%.3f", inputAmount) // Format to 3 decimal places
 | ||||
| 	store := h.userdataStore | ||||
| 	store = h.userdataStore | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(amountStr)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
|  | ||||
| @ -1,320 +0,0 @@ | ||||
| package ussd | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/ussd/mocks" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/utils" | ||||
| 	"github.com/alecthomas/assert/v2" | ||||
| ) | ||||
| 
 | ||||
| func TestSaveFirstname(t *testing.T) { | ||||
| 	// Create a new instance of MockMyDataStore
 | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	sessionId := "session123" | ||||
| 	firstName := "John" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 
 | ||||
| 	// Set up the expected behavior of the mock
 | ||||
| 	mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)).Return(nil) | ||||
| 
 | ||||
| 	// Create the Handlers instance with the mock store
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore: mockStore, | ||||
| 	} | ||||
| 
 | ||||
| 	// Call the method
 | ||||
| 	res, err := h.SaveFirstname(ctx, "save_firstname", []byte(firstName)) | ||||
| 
 | ||||
| 	// Assert results
 | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Assert all expectations were met
 | ||||
| 	mockStore.AssertExpectations(t) | ||||
| } | ||||
| 
 | ||||
| func TestSaveFamilyname(t *testing.T) { | ||||
| 	// Create a new instance of UserDataStore
 | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	sessionId := "session123" | ||||
| 	familyName := "Doeee" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 
 | ||||
| 	// Set up the expected behavior of the mock
 | ||||
| 	mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)).Return(nil) | ||||
| 
 | ||||
| 	// Create the Handlers instance with the mock store
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore: mockStore, | ||||
| 	} | ||||
| 
 | ||||
| 	// Call the method
 | ||||
| 	res, err := h.SaveFamilyname(ctx, "save_familyname", []byte(familyName)) | ||||
| 
 | ||||
| 	// Assert results
 | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Assert all expectations were met
 | ||||
| 	mockStore.AssertExpectations(t) | ||||
| } | ||||
| 
 | ||||
| func TestSaveYoB(t *testing.T) { | ||||
| 	// Create a new instance of MockMyDataStore
 | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	sessionId := "session123" | ||||
| 	yob := "1980" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 
 | ||||
| 	// Set up the expected behavior of the mock
 | ||||
| 	mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_YOB, []byte(yob)).Return(nil) | ||||
| 
 | ||||
| 	// Create the Handlers instance with the mock store
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore: mockStore, | ||||
| 	} | ||||
| 
 | ||||
| 	// Call the method
 | ||||
| 	res, err := h.SaveYob(ctx, "save_yob", []byte(yob)) | ||||
| 
 | ||||
| 	// Assert results
 | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Assert all expectations were met
 | ||||
| 	mockStore.AssertExpectations(t) | ||||
| } | ||||
| 
 | ||||
| func TestSaveLocation(t *testing.T) { | ||||
| 	// Create a new instance of MockMyDataStore
 | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	sessionId := "session123" | ||||
| 	yob := "Kilifi" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 
 | ||||
| 	// Set up the expected behavior of the mock
 | ||||
| 	mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_LOCATION, []byte(yob)).Return(nil) | ||||
| 
 | ||||
| 	// Create the Handlers instance with the mock store
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore: mockStore, | ||||
| 	} | ||||
| 
 | ||||
| 	// Call the method
 | ||||
| 	res, err := h.SaveLocation(ctx, "save_location", []byte(yob)) | ||||
| 
 | ||||
| 	// Assert results
 | ||||
| 	assert.NoError(t, err) | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Assert all expectations were met
 | ||||
| 	mockStore.AssertExpectations(t) | ||||
| } | ||||
| 
 | ||||
| func TestSaveGender(t *testing.T) { | ||||
| 	// Create a new instance of MockMyDataStore
 | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 
 | ||||
| 	// Define the session ID and context
 | ||||
| 	sessionId := "session123" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 
 | ||||
| 	// Define test cases
 | ||||
| 	tests := []struct { | ||||
| 		name           string | ||||
| 		input          []byte | ||||
| 		expectedGender string | ||||
| 		expectCall     bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:           "Valid Male Input", | ||||
| 			input:          []byte("1"), | ||||
| 			expectedGender: "Male", | ||||
| 			expectCall:     true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "Valid Female Input", | ||||
| 			input:          []byte("2"), | ||||
| 			expectedGender: "Female", | ||||
| 			expectCall:     true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "Valid Unspecified Input", | ||||
| 			input:          []byte("3"), | ||||
| 			expectedGender: "Unspecified", | ||||
| 			expectCall:     true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:           "Empty Input", | ||||
| 			input:          []byte(""), | ||||
| 			expectedGender: "", | ||||
| 			expectCall:     false, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			// Set up expectations for the mock database
 | ||||
| 			if tt.expectCall { | ||||
| 				expectedKey := utils.DATA_GENDER | ||||
| 				mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) | ||||
| 			} else { | ||||
| 				mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)).Return(nil) | ||||
| 			} | ||||
| 
 | ||||
| 			// Create the Handlers instance with the mock store
 | ||||
| 			h := &Handlers{ | ||||
| 				userdataStore: mockStore, | ||||
| 			} | ||||
| 
 | ||||
| 			// Call the method
 | ||||
| 			_, err := h.SaveGender(ctx, "someSym", tt.input) | ||||
| 
 | ||||
| 			// Assert no error
 | ||||
| 			assert.NoError(t, err) | ||||
| 
 | ||||
| 			// Verify expectations
 | ||||
| 			if tt.expectCall { | ||||
| 				mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) | ||||
| 			} else { | ||||
| 				mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestCheckIdentifier(t *testing.T) { | ||||
| 	// Create a new instance of MockMyDataStore
 | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 
 | ||||
| 	// Define the session ID and context
 | ||||
| 	sessionId := "session123" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 
 | ||||
| 	// Define test cases
 | ||||
| 	tests := []struct { | ||||
| 		name            string | ||||
| 		mockPublicKey   []byte | ||||
| 		mockErr         error | ||||
| 		expectedContent string | ||||
| 		expectError     bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:            "Saved public Key", | ||||
| 			mockPublicKey:   []byte("0xa8363"), | ||||
| 			mockErr:         nil, | ||||
| 			expectedContent: "0xa8363", | ||||
| 			expectError:     false, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			// Set up expectations for the mock database
 | ||||
| 			mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.mockPublicKey, tt.mockErr) | ||||
| 
 | ||||
| 			// Create the Handlers instance with the mock store
 | ||||
| 			h := &Handlers{ | ||||
| 				userdataStore: mockStore, | ||||
| 			} | ||||
| 
 | ||||
| 			// Call the method
 | ||||
| 			res, err := h.CheckIdentifier(ctx, "check_identifier", nil) | ||||
| 
 | ||||
| 			// Assert results
 | ||||
| 
 | ||||
| 			assert.NoError(t, err) | ||||
| 			assert.Equal(t, tt.expectedContent, res.Content) | ||||
| 
 | ||||
| 			// Verify expectations
 | ||||
| 			mockStore.AssertExpectations(t) | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestMaxAmount(t *testing.T) { | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 	mockCreateAccountService := new(mocks.MockAccountService) | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	sessionId := "session123" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 	publicKey := "0xcasgatweksalw1018221" | ||||
| 	expectedBalance := "0.003CELO" | ||||
| 
 | ||||
| 	// Set up the expected behavior of the mock
 | ||||
| 	mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) | ||||
| 	mockCreateAccountService.On("CheckBalance", publicKey).Return(expectedBalance, nil) | ||||
| 
 | ||||
| 	// Create the Handlers instance with the mock store
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore:  mockStore, | ||||
| 		accountService: mockCreateAccountService, | ||||
| 	} | ||||
| 
 | ||||
| 	// Call the method
 | ||||
| 	res, _ := h.MaxAmount(ctx, "max_amount", []byte("check_balance")) | ||||
| 
 | ||||
| 	//Assert that the balance that was set as the result content is what was returned by  Check Balance
 | ||||
| 	assert.Equal(t, expectedBalance, res.Content) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestGetSender(t *testing.T) { | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	sessionId := "session123" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 	publicKey := "0xcasgatweksalw1018221" | ||||
| 
 | ||||
| 	// Set up the expected behavior of the mock
 | ||||
| 	mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) | ||||
| 
 | ||||
| 	// Create the Handlers instance with the mock store
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore: mockStore, | ||||
| 	} | ||||
| 
 | ||||
| 	// Call the method
 | ||||
| 	res, _ := h.GetSender(ctx, "max_amount", []byte("check_balance")) | ||||
| 
 | ||||
| 	//Assert that the public key from readentry operation  is what was set as the result content.
 | ||||
| 	assert.Equal(t, publicKey, res.Content) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func TestGetAmount(t *testing.T) { | ||||
| 	mockStore := new(mocks.MockUserDataStore) | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	sessionId := "session123" | ||||
| 	ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 	Amount := "0.03CELO" | ||||
| 
 | ||||
| 	// Set up the expected behavior of the mock
 | ||||
| 	mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return([]byte(Amount), nil) | ||||
| 
 | ||||
| 	// Create the Handlers instance with the mock store
 | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore: mockStore, | ||||
| 	} | ||||
| 
 | ||||
| 	// Call the method
 | ||||
| 	res, _ := h.GetAmount(ctx, "get_amount", []byte("Getting amount...")) | ||||
| 
 | ||||
| 	//Assert that the retrieved amount is what was set as the content
 | ||||
| 	assert.Equal(t, Amount, res.Content) | ||||
| 
 | ||||
| } | ||||
| @ -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) | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| package mocks | ||||
| 
 | ||||
| import ( | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"github.com/stretchr/testify/mock" | ||||
| ) | ||||
| 
 | ||||
| // MockAccountService implements AccountServiceInterface for testing
 | ||||
| type MockAccountService struct { | ||||
| 	mock.Mock | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { | ||||
| 	args := m.Called() | ||||
| 	return args.Get(0).(*models.AccountResponse), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CheckBalance(publicKey string) (string, error) { | ||||
| 	args := m.Called(publicKey) | ||||
| 	return args.String(0), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CheckAccountStatus(trackingId string) (string, error) { | ||||
| 	args := m.Called(trackingId) | ||||
| 	return args.String(0), args.Error(1) | ||||
| } | ||||
| @ -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) | ||||
| } | ||||
| @ -1,10 +1,7 @@ | ||||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/binary" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/db" | ||||
| ) | ||||
| 
 | ||||
| type DataTyp uint16 | ||||
| @ -25,6 +22,7 @@ const ( | ||||
| 	DATA_OFFERINGS | ||||
| 	DATA_RECIPIENT | ||||
| 	DATA_AMOUNT | ||||
| 	DATA_TEMPORARY_PIN | ||||
| ) | ||||
| 
 | ||||
| func typToBytes(typ DataTyp) []byte { | ||||
| @ -37,21 +35,3 @@ func PackKey(typ DataTyp, data []byte) []byte { | ||||
| 	v := typToBytes(typ) | ||||
| 	return append(v, data...) | ||||
| } | ||||
| 
 | ||||
| func ReadEntry(ctx context.Context, store db.Db, sessionId string, typ DataTyp) ([]byte, error) { | ||||
| 	store.SetPrefix(db.DATATYPE_USERDATA) | ||||
| 	store.SetSession(sessionId) | ||||
| 	k := PackKey(typ, []byte(sessionId)) | ||||
| 	b, err := store.Get(ctx, k) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return b, nil | ||||
| } | ||||
| 
 | ||||
| func WriteEntry(ctx context.Context, store db.Db, sessionId string, typ DataTyp, value []byte) error { | ||||
| 	store.SetPrefix(db.DATATYPE_USERDATA) | ||||
| 	store.SetSession(sessionId) | ||||
| 	k := PackKey(typ, []byte(sessionId)) | ||||
| 	return store.Put(ctx, k, value) | ||||
| } | ||||
|  | ||||
							
								
								
									
										1
									
								
								services/registration/confirm_pin_change
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/confirm_pin_change
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| Confirm your new PIN: | ||||
							
								
								
									
										7
									
								
								services/registration/confirm_pin_change.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								services/registration/confirm_pin_change.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| LOAD verify_pin 0 | ||||
| MOUT back 0 | ||||
| HALT | ||||
| RELOAD verify_pin | ||||
| CATCH  create_pin_mismatch flag_pin_mismatch 1 | ||||
| MOVE pin_reset_success | ||||
| INCMP _ 0 | ||||
							
								
								
									
										2
									
								
								services/registration/help.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/help.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| LOAD quit_with_help 0 | ||||
| HALT | ||||
							
								
								
									
										1
									
								
								services/registration/invalid_pin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/invalid_pin
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| The PIN you entered is invalid.The PIN must be different from your current PIN.For help call +254757628885 | ||||
							
								
								
									
										4
									
								
								services/registration/invalid_pin.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								services/registration/invalid_pin.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| MOUT back 0 | ||||
| HALT | ||||
| INCMP _ 0 | ||||
| 
 | ||||
| @ -6,3 +6,6 @@ msgstr "Ombi lako limetumwa. %s atapokea %s kutoka kwa %s." | ||||
| 
 | ||||
| msgid "Thank you for using Sarafu. Goodbye!" | ||||
| msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!" | ||||
| 
 | ||||
| msgid "For more help,please call: 0757628885" | ||||
| msgstr "Kwa usaidizi zaidi,piga: 0757628885" | ||||
|  | ||||
| @ -10,6 +10,6 @@ HALT | ||||
| INCMP send 1 | ||||
| INCMP quit 2 | ||||
| INCMP my_account 3 | ||||
| INCMP quit 4 | ||||
| INCMP help 4 | ||||
| INCMP quit 9 | ||||
| INCMP . * | ||||
|  | ||||
							
								
								
									
										1
									
								
								services/registration/new_pin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/new_pin
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| Enter a new four number pin | ||||
							
								
								
									
										7
									
								
								services/registration/new_pin.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								services/registration/new_pin.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| LOAD save_temporary_pin 0 | ||||
| MOUT back 0 | ||||
| HALT | ||||
| RELOAD save_temporary_pin | ||||
| CATCH invalid_pin flag_incorrect_pin 1 | ||||
| INCMP _ 0 | ||||
| MOVE confirm_pin_change  | ||||
							
								
								
									
										1
									
								
								services/registration/old_pin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/old_pin
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| Enter your old PIN | ||||
							
								
								
									
										9
									
								
								services/registration/old_pin.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								services/registration/old_pin.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| LOAD authorize_account 6 | ||||
| MOUT back 0 | ||||
| HALT | ||||
| RELOAD authorize_account | ||||
| CATCH incorrect_pin flag_incorrect_pin 1 | ||||
| MOVE new_pin | ||||
| INCMP _ 0 | ||||
| 
 | ||||
| 
 | ||||
| @ -4,3 +4,4 @@ MOUT guard_pin 3 | ||||
| MOUT back 0 | ||||
| HALT | ||||
| INCMP _ 0 | ||||
| INCMP old_pin  1 | ||||
|  | ||||
							
								
								
									
										1
									
								
								services/registration/pin_reset_success
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/pin_reset_success
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| Your PIN change request has been successful | ||||
							
								
								
									
										6
									
								
								services/registration/pin_reset_success.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								services/registration/pin_reset_success.vis
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| LOAD confirm_pin_change 0 | ||||
| MOUT back 0 | ||||
| MOUT quit 9 | ||||
| HALT | ||||
| INCMP _ 0 | ||||
| INCMP quit 9 | ||||
| @ -1,3 +1,4 @@ | ||||
| LOAD authorize_account 6 | ||||
| MAP validate_amount | ||||
| RELOAD get_recipient | ||||
| MAP get_recipient | ||||
| @ -6,9 +7,9 @@ MAP get_sender | ||||
| MOUT back 0 | ||||
| MOUT quit 9 | ||||
| HALT | ||||
| LOAD authorize_account 6 | ||||
| RELOAD authorize_account | ||||
| CATCH incorrect_pin flag_incorrect_pin 1 | ||||
| CATCH transaction_initiated flag_account_authorized 1 | ||||
| INCMP _ 0 | ||||
| INCMP quit 9 | ||||
| INCMP transaction_initiated * | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user