git commit -m "refactor: rename ussd package to application (#24)

- Rename internal/handlers/ussd directory to application
- Update all imports and references to use new package name
This commit is contained in:
konstantinmds 2025-01-08 13:34:08 +01:00
parent 80b96e9bf6
commit 5812654a14
15 changed files with 309 additions and 108 deletions

View File

@ -55,6 +55,8 @@ const (
DATA_ACTIVE_DECIMAL DATA_ACTIVE_DECIMAL
// EVM address of the currently active voucher // EVM address of the currently active voucher
DATA_ACTIVE_ADDRESS DATA_ACTIVE_ADDRESS
//Holds count of the number of incorrect PIN attempts
DATA_INCORRECT_PIN_ATTEMPTS
) )
const ( const (

View File

@ -6,9 +6,13 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
// Define the regex pattern as a constant
const ( const (
// Define the regex pattern as a constant
pinPattern = `^\d{4}$` pinPattern = `^\d{4}$`
//Allowed incorrect PIN attempts
AllowedPINAttempts = uint8(3)
) )
// checks whether the given input is a 4 digit number // checks whether the given input is a 4 digit number

View File

@ -1,4 +1,4 @@
package ussd package application
import ( import (
"bytes" "bytes"
@ -734,11 +734,23 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res
if h.st.MatchFlag(flag_account_authorized, false) { if h.st.MatchFlag(flag_account_authorized, false) {
res.FlagReset = append(res.FlagReset, flag_incorrect_pin) res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized) res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized)
err := h.resetIncorrectPINAttempts(ctx, sessionId)
if err != nil {
return res, err
}
} else { } else {
res.FlagSet = append(res.FlagSet, flag_allow_update) res.FlagSet = append(res.FlagSet, flag_allow_update)
res.FlagReset = append(res.FlagReset, flag_account_authorized) res.FlagReset = append(res.FlagReset, flag_account_authorized)
err := h.resetIncorrectPINAttempts(ctx, sessionId)
if err != nil {
return res, err
}
} }
} else { } else {
err := h.incrementIncorrectPINAttempts(ctx, sessionId)
if err != nil {
return res, err
}
res.FlagSet = append(res.FlagSet, flag_incorrect_pin) res.FlagSet = append(res.FlagSet, flag_incorrect_pin)
res.FlagReset = append(res.FlagReset, flag_account_authorized) res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil return res, nil
@ -752,8 +764,34 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res
// ResetIncorrectPin resets the incorrect pin flag after a new PIN attempt. // ResetIncorrectPin resets the incorrect pin flag after a new PIN attempt.
func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
store := h.userdataStore
flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
flag_account_blocked, _ := h.flagManager.GetFlag("flag_account_blocked")
sessionId, ok := ctx.Value("SessionId").(string)
if !ok {
return res, fmt.Errorf("missing session")
}
res.FlagReset = append(res.FlagReset, flag_incorrect_pin) res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS)
if err != nil {
if !db.IsNotFound(err) {
return res, err
}
}
pinAttemptsValue, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64)
remainingPINAttempts := common.AllowedPINAttempts - uint8(pinAttemptsValue)
if remainingPINAttempts == 0 {
res.FlagSet = append(res.FlagSet, flag_account_blocked)
return res, nil
}
if remainingPINAttempts < common.AllowedPINAttempts {
res.Content = strconv.Itoa(int(remainingPINAttempts))
}
return res, nil return res, nil
} }
@ -835,11 +873,21 @@ func (h *Handlers) QuitWithHelp(ctx context.Context, sym string, input []byte) (
l := gotext.NewLocale(translationDir, code) l := gotext.NewLocale(translationDir, code)
l.AddDomain("default") l.AddDomain("default")
res.Content = l.Get("For more help,please call: 0757628885") res.Content = l.Get("For more help, please call: 0757628885")
res.FlagReset = append(res.FlagReset, flag_account_authorized) res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil return res, nil
} }
// ShowBlockedAccount displays a message after an account has been blocked and how to reach support.
func (h *Handlers) ShowBlockedAccount(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
res.Content = l.Get("Your account has been locked. For help on how to unblock your account, contact support at: 0757628885")
return res, nil
}
// VerifyYob verifies the length of the given input. // VerifyYob verifies the length of the given input.
func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
@ -2075,3 +2123,53 @@ func (h *Handlers) UpdateAllProfileItems(ctx context.Context, sym string, input
} }
return res, nil return res, nil
} }
// incrementIncorrectPINAttempts keeps track of the number of incorrect PIN attempts
func (h *Handlers) incrementIncorrectPINAttempts(ctx context.Context, sessionId string) error {
var pinAttemptsCount uint8
store := h.userdataStore
currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS)
if err != nil {
if db.IsNotFound(err) {
//First time Wrong PIN attempt: initialize with a count of 1
pinAttemptsCount = 1
err = store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(strconv.Itoa(int(pinAttemptsCount))))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write incorrect PIN attempts ", "key", common.DATA_INCORRECT_PIN_ATTEMPTS, "value", currentWrongPinAttempts, "error", err)
return err
}
return nil
}
}
pinAttemptsValue, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64)
pinAttemptsCount = uint8(pinAttemptsValue) + 1
err = store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(strconv.Itoa(int(pinAttemptsCount))))
if err != nil {
logg.ErrorCtxf(ctx, "failed to write incorrect PIN attempts ", "key", common.DATA_INCORRECT_PIN_ATTEMPTS, "value", pinAttemptsCount, "error", err)
return err
}
return nil
}
// resetIncorrectPINAttempts resets the number of incorrect PIN attempts after a correct PIN entry
func (h *Handlers) resetIncorrectPINAttempts(ctx context.Context, sessionId string) error {
store := h.userdataStore
currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS)
if err != nil {
if db.IsNotFound(err) {
return nil
}
return err
}
currentWrongPinAttemptsCount, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64)
if currentWrongPinAttemptsCount <= uint64(common.AllowedPINAttempts) {
err = store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("0")))
if err != nil {
logg.ErrorCtxf(ctx, "failed to reset incorrect PIN attempts ", "key", common.DATA_INCORRECT_PIN_ATTEMPTS, "value", common.AllowedPINAttempts, "error", err)
return err
}
}
return nil
}

View File

@ -1,10 +1,11 @@
package ussd package application
import ( import (
"context" "context"
"fmt" "fmt"
"log" "log"
"path" "path"
"strconv"
"strings" "strings"
"testing" "testing"
@ -907,37 +908,79 @@ func TestResetAccountAuthorized(t *testing.T) {
} }
func TestIncorrectPinReset(t *testing.T) { func TestIncorrectPinReset(t *testing.T) {
sessionId := "session123"
ctx, store := InitializeTestStore(t)
fm, err := NewFlagManager(flagsPath) fm, err := NewFlagManager(flagsPath)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin") flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin")
flag_account_blocked, _ := fm.parser.GetFlag("flag_account_blocked")
ctx = context.WithValue(ctx, "SessionId", sessionId)
// Define test cases // Define test cases
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
attempts uint8
expectedResult resource.Result expectedResult resource.Result
}{ }{
{ {
name: "Test incorrect pin reset", name: "Test when incorrect PIN attempts is 2",
input: []byte(""), input: []byte(""),
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagReset: []uint32{flag_incorrect_pin}, FlagReset: []uint32{flag_incorrect_pin},
Content: "1", //Expected remaining PIN attempts
}, },
attempts: 2,
},
{
name: "Test incorrect pin reset when incorrect PIN attempts is 1",
input: []byte(""),
expectedResult: resource.Result{
FlagReset: []uint32{flag_incorrect_pin},
Content: "2", //Expected remaining PIN attempts
},
attempts: 1,
},
{
name: "Test incorrect pin reset when incorrect PIN attempts is 1",
input: []byte(""),
expectedResult: resource.Result{
FlagReset: []uint32{flag_incorrect_pin},
Content: "2", //Expected remaining PIN attempts
},
attempts: 1,
},
{
name: "Test incorrect pin reset when incorrect PIN attempts is 3(account expected to be blocked)",
input: []byte(""),
expectedResult: resource.Result{
FlagReset: []uint32{flag_incorrect_pin},
FlagSet: []uint32{flag_account_blocked},
},
attempts: 3,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if err := store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(strconv.Itoa(int(tt.attempts)))); err != nil {
t.Fatal(err)
}
// Create the Handlers instance with the mock flag manager // Create the Handlers instance with the mock flag manager
h := &Handlers{ h := &Handlers{
flagManager: fm.parser, flagManager: fm.parser,
userdataStore: store,
} }
// Call the method // Call the method
res, err := h.ResetIncorrectPin(context.Background(), "reset_incorrect_pin", tt.input) res, err := h.ResetIncorrectPin(ctx, "reset_incorrect_pin", tt.input)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -2190,3 +2233,55 @@ func TestGetVoucherDetails(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, expectedResult, res) assert.Equal(t, expectedResult, res)
} }
func TestCountIncorrectPINAttempts(t *testing.T) {
ctx, store := InitializeTestStore(t)
sessionId := "session123"
ctx = context.WithValue(ctx, "SessionId", sessionId)
attempts := uint8(2)
h := &Handlers{
userdataStore: store,
}
err := store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(strconv.Itoa(int(attempts))))
if err != nil {
t.Logf(err.Error())
}
err = h.incrementIncorrectPINAttempts(ctx, sessionId)
if err != nil {
t.Logf(err.Error())
}
attemptsAfterCount, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS)
if err != nil {
t.Logf(err.Error())
}
pinAttemptsValue, _ := strconv.ParseUint(string(attemptsAfterCount), 0, 64)
pinAttemptsCount := uint8(pinAttemptsValue)
expectedAttempts := attempts + 1
assert.Equal(t, pinAttemptsCount, expectedAttempts)
}
func TestResetIncorrectPINAttempts(t *testing.T) {
ctx, store := InitializeTestStore(t)
sessionId := "session123"
ctx = context.WithValue(ctx, "SessionId", sessionId)
err := store.WriteEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("2")))
if err != nil {
t.Logf(err.Error())
}
h := &Handlers{
userdataStore: store,
}
h.resetIncorrectPINAttempts(ctx, sessionId)
incorrectAttempts, err := store.ReadEntry(ctx, sessionId, common.DATA_INCORRECT_PIN_ATTEMPTS)
if err != nil {
t.Logf(err.Error())
}
assert.Equal(t, "0", string(incorrectAttempts))
}

View File

@ -6,7 +6,7 @@ import (
"git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/internal/handlers/ussd" "git.grassecon.net/urdt/ussd/internal/handlers/application"
"git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/storage"
) )
@ -14,11 +14,11 @@ type BaseSessionHandler struct {
cfgTemplate engine.Config cfgTemplate engine.Config
rp RequestParser rp RequestParser
rs resource.Resource rs resource.Resource
hn *ussd.Handlers hn *application.Handlers
provider storage.StorageProvider provider storage.StorageProvider
} }
func NewBaseSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, hn *ussd.Handlers) *BaseSessionHandler { func NewBaseSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, hn *application.Handlers) *BaseSessionHandler {
return &BaseSessionHandler{ return &BaseSessionHandler{
cfgTemplate: cfg, cfgTemplate: cfg,
rs: rs, rs: rs,
@ -28,20 +28,20 @@ func NewBaseSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.D
} }
} }
func(f* BaseSessionHandler) Shutdown() { func (f *BaseSessionHandler) Shutdown() {
err := f.provider.Close() err := f.provider.Close()
if err != nil { if err != nil {
logg.Errorf("handler shutdown error", "err", err) logg.Errorf("handler shutdown error", "err", err)
} }
} }
func(f *BaseSessionHandler) GetEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) engine.Engine { func (f *BaseSessionHandler) GetEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) engine.Engine {
en := engine.NewEngine(cfg, rs) en := engine.NewEngine(cfg, rs)
en = en.WithPersister(pr) en = en.WithPersister(pr)
return en return en
} }
func(f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error) { func (f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error) {
var r bool var r bool
var err error var err error
var ok bool var ok bool
@ -88,21 +88,21 @@ func(f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error)
return rqs, nil return rqs, nil
} }
func(f *BaseSessionHandler) Output(rqs RequestSession) (RequestSession, error) { func (f *BaseSessionHandler) Output(rqs RequestSession) (RequestSession, error) {
var err error var err error
_, err = rqs.Engine.Flush(rqs.Ctx, rqs.Writer) _, err = rqs.Engine.Flush(rqs.Ctx, rqs.Writer)
return rqs, err return rqs, err
} }
func(f *BaseSessionHandler) Reset(rqs RequestSession) (RequestSession, error) { func (f *BaseSessionHandler) Reset(rqs RequestSession) (RequestSession, error) {
defer f.provider.Put(rqs.Config.SessionId, rqs.Storage) defer f.provider.Put(rqs.Config.SessionId, rqs.Storage)
return rqs, rqs.Engine.Finish() return rqs, rqs.Engine.Finish()
} }
func(f *BaseSessionHandler) GetConfig() engine.Config { func (f *BaseSessionHandler) GetConfig() engine.Config {
return f.cfgTemplate return f.cfgTemplate
} }
func(f *BaseSessionHandler) GetRequestParser() RequestParser { func (f *BaseSessionHandler) GetRequestParser() RequestParser {
return f.rp return f.rp
} }

View File

@ -10,13 +10,13 @@ import (
"git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/persist"
"git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/resource"
"git.grassecon.net/urdt/ussd/internal/handlers/ussd" "git.grassecon.net/urdt/ussd/internal/handlers/application"
"git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/internal/utils"
"git.grassecon.net/urdt/ussd/remote" "git.grassecon.net/urdt/ussd/remote"
) )
type HandlerService interface { type HandlerService interface {
GetHandler() (*ussd.Handlers, error) GetHandler() (*application.Handlers, error)
} }
func getParser(fp string, debug bool) (*asm.FlagParser, error) { func getParser(fp string, debug bool) (*asm.FlagParser, error) {
@ -64,72 +64,72 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
ls.UserdataStore = db ls.UserdataStore = db
} }
func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*ussd.Handlers, error) { func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*application.Handlers, error) {
replaceSeparatorFunc := func(input string) string { replaceSeparatorFunc := func(input string) string {
return strings.ReplaceAll(input, ":", ls.Cfg.MenuSeparator) return strings.ReplaceAll(input, ":", ls.Cfg.MenuSeparator)
} }
ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService, replaceSeparatorFunc) appHandlers, err := application.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService, replaceSeparatorFunc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ussdHandlers = ussdHandlers.WithPersister(ls.Pe) appHandlers = appHandlers.WithPersister(ls.Pe)
ls.DbRs.AddLocalFunc("set_language", ussdHandlers.SetLanguage) ls.DbRs.AddLocalFunc("set_language", appHandlers.SetLanguage)
ls.DbRs.AddLocalFunc("create_account", ussdHandlers.CreateAccount) ls.DbRs.AddLocalFunc("create_account", appHandlers.CreateAccount)
ls.DbRs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin) ls.DbRs.AddLocalFunc("save_temporary_pin", appHandlers.SaveTemporaryPin)
ls.DbRs.AddLocalFunc("verify_create_pin", ussdHandlers.VerifyCreatePin) ls.DbRs.AddLocalFunc("verify_create_pin", appHandlers.VerifyCreatePin)
ls.DbRs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier) ls.DbRs.AddLocalFunc("check_identifier", appHandlers.CheckIdentifier)
ls.DbRs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus) ls.DbRs.AddLocalFunc("check_account_status", appHandlers.CheckAccountStatus)
ls.DbRs.AddLocalFunc("authorize_account", ussdHandlers.Authorize) ls.DbRs.AddLocalFunc("authorize_account", appHandlers.Authorize)
ls.DbRs.AddLocalFunc("quit", ussdHandlers.Quit) ls.DbRs.AddLocalFunc("quit", appHandlers.Quit)
ls.DbRs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance) ls.DbRs.AddLocalFunc("check_balance", appHandlers.CheckBalance)
ls.DbRs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient) ls.DbRs.AddLocalFunc("validate_recipient", appHandlers.ValidateRecipient)
ls.DbRs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset) ls.DbRs.AddLocalFunc("transaction_reset", appHandlers.TransactionReset)
ls.DbRs.AddLocalFunc("invite_valid_recipient", ussdHandlers.InviteValidRecipient) ls.DbRs.AddLocalFunc("invite_valid_recipient", appHandlers.InviteValidRecipient)
ls.DbRs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount) ls.DbRs.AddLocalFunc("max_amount", appHandlers.MaxAmount)
ls.DbRs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount) ls.DbRs.AddLocalFunc("validate_amount", appHandlers.ValidateAmount)
ls.DbRs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount) ls.DbRs.AddLocalFunc("reset_transaction_amount", appHandlers.ResetTransactionAmount)
ls.DbRs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient) ls.DbRs.AddLocalFunc("get_recipient", appHandlers.GetRecipient)
ls.DbRs.AddLocalFunc("get_sender", ussdHandlers.GetSender) ls.DbRs.AddLocalFunc("get_sender", appHandlers.GetSender)
ls.DbRs.AddLocalFunc("get_amount", ussdHandlers.GetAmount) ls.DbRs.AddLocalFunc("get_amount", appHandlers.GetAmount)
ls.DbRs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin) ls.DbRs.AddLocalFunc("reset_incorrect", appHandlers.ResetIncorrectPin)
ls.DbRs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname) ls.DbRs.AddLocalFunc("save_firstname", appHandlers.SaveFirstname)
ls.DbRs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname) ls.DbRs.AddLocalFunc("save_familyname", appHandlers.SaveFamilyname)
ls.DbRs.AddLocalFunc("save_gender", ussdHandlers.SaveGender) ls.DbRs.AddLocalFunc("save_gender", appHandlers.SaveGender)
ls.DbRs.AddLocalFunc("save_location", ussdHandlers.SaveLocation) ls.DbRs.AddLocalFunc("save_location", appHandlers.SaveLocation)
ls.DbRs.AddLocalFunc("save_yob", ussdHandlers.SaveYob) ls.DbRs.AddLocalFunc("save_yob", appHandlers.SaveYob)
ls.DbRs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings) ls.DbRs.AddLocalFunc("save_offerings", appHandlers.SaveOfferings)
ls.DbRs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized) ls.DbRs.AddLocalFunc("reset_account_authorized", appHandlers.ResetAccountAuthorized)
ls.DbRs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate) ls.DbRs.AddLocalFunc("reset_allow_update", appHandlers.ResetAllowUpdate)
ls.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo) ls.DbRs.AddLocalFunc("get_profile_info", appHandlers.GetProfileInfo)
ls.DbRs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob) ls.DbRs.AddLocalFunc("verify_yob", appHandlers.VerifyYob)
ls.DbRs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob) ls.DbRs.AddLocalFunc("reset_incorrect_date_format", appHandlers.ResetIncorrectYob)
ls.DbRs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction) ls.DbRs.AddLocalFunc("initiate_transaction", appHandlers.InitiateTransaction)
ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("verify_new_pin", appHandlers.VerifyNewPin)
ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("confirm_pin_change", appHandlers.ConfirmPinChange)
ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) ls.DbRs.AddLocalFunc("quit_with_help", appHandlers.QuitWithHelp)
ls.DbRs.AddLocalFunc("fetch_community_balance", ussdHandlers.FetchCommunityBalance) ls.DbRs.AddLocalFunc("fetch_community_balance", appHandlers.FetchCommunityBalance)
ls.DbRs.AddLocalFunc("set_default_voucher", ussdHandlers.SetDefaultVoucher) ls.DbRs.AddLocalFunc("set_default_voucher", appHandlers.SetDefaultVoucher)
ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers) ls.DbRs.AddLocalFunc("check_vouchers", appHandlers.CheckVouchers)
ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("get_vouchers", appHandlers.GetVoucherList)
ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher)
ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher) ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher)
ls.DbRs.AddLocalFunc("get_voucher_details", ussdHandlers.GetVoucherDetails) ls.DbRs.AddLocalFunc("get_voucher_details", appHandlers.GetVoucherDetails)
ls.DbRs.AddLocalFunc("reset_valid_pin", ussdHandlers.ResetValidPin) ls.DbRs.AddLocalFunc("reset_valid_pin", appHandlers.ResetValidPin)
ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckBlockedNumPinMisMatch) ls.DbRs.AddLocalFunc("check_pin_mismatch", appHandlers.CheckBlockedNumPinMisMatch)
ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) ls.DbRs.AddLocalFunc("validate_blocked_number", appHandlers.ValidateBlockedNumber)
ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber)
ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber) ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber)
ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin) ls.DbRs.AddLocalFunc("reset_others_pin", appHandlers.ResetOthersPin)
ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin) ls.DbRs.AddLocalFunc("save_others_temporary_pin", appHandlers.SaveOthersTemporaryPin)
ls.DbRs.AddLocalFunc("get_current_profile_info", ussdHandlers.GetCurrentProfileInfo) ls.DbRs.AddLocalFunc("get_current_profile_info", appHandlers.GetCurrentProfileInfo)
ls.DbRs.AddLocalFunc("check_transactions", ussdHandlers.CheckTransactions) ls.DbRs.AddLocalFunc("check_transactions", appHandlers.CheckTransactions)
ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactionsList) ls.DbRs.AddLocalFunc("get_transactions", appHandlers.GetTransactionsList)
ls.DbRs.AddLocalFunc("view_statement", ussdHandlers.ViewTransactionStatement) ls.DbRs.AddLocalFunc("view_statement", appHandlers.ViewTransactionStatement)
ls.DbRs.AddLocalFunc("update_all_profile_items", ussdHandlers.UpdateAllProfileItems) ls.DbRs.AddLocalFunc("update_all_profile_items", appHandlers.UpdateAllProfileItems)
ls.DbRs.AddLocalFunc("set_back", ussdHandlers.SetBack) ls.DbRs.AddLocalFunc("set_back", appHandlers.SetBack)
return ussdHandlers, nil return appHandlers, nil
} }
// TODO: enable setting of sessionId on engine init time // TODO: enable setting of sessionId on engine init time

View File

@ -81,7 +81,8 @@ func (arp *ATRequestParser) GetInput(rq any) ([]byte, error) {
return nil, fmt.Errorf("no input found") return nil, fmt.Errorf("no input found")
} }
return []byte(parts[len(parts)-1]), nil trimmedInput := strings.TrimSpace(parts[len(parts)-1])
return []byte(trimmedInput), nil
} }
func parseQueryParams(query string) map[string]string { func parseQueryParams(query string) map[string]string {

View File

@ -54,7 +54,7 @@
}, },
{ {
"input": "1235", "input": "1235",
"expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" "expectedContent": "Incorrect PIN. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit"
}, },
{ {
"input": "1", "input": "1",
@ -95,7 +95,7 @@
}, },
{ {
"input": "1235", "input": "1235",
"expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" "expectedContent": "Incorrect PIN. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit"
}, },
{ {
"input": "1", "input": "1",
@ -108,7 +108,6 @@
{ {
"input": "0", "input": "0",
"expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back"
}, },
{ {
"input": "0", "input": "0",
@ -141,7 +140,7 @@
}, },
{ {
"input": "1235", "input": "1235",
"expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" "expectedContent": "Incorrect PIN. You have: 2 remaining attempt(s).\n1:Retry\n9:Quit"
}, },
{ {
"input": "1", "input": "1",
@ -154,7 +153,6 @@
{ {
"input": "0", "input": "0",
"expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back" "expectedContent": "Balances:\n1:My balance\n2:Community balance\n0:Back"
}, },
{ {
"input": "0", "input": "0",
@ -258,7 +256,6 @@
"input": "0", "input": "0",
"expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit"
} }
] ]
}, },
{ {
@ -444,9 +441,3 @@
} }
] ]
} }

View File

@ -0,0 +1,2 @@
LOAD show_blocked_account 0
HALT

View File

@ -1 +1 @@
Incorrect PIN Incorrect PIN. You have: {{.reset_incorrect}} remaining attempt(s).

View File

@ -1,5 +1,7 @@
LOAD reset_incorrect 0 LOAD reset_incorrect 0
RELOAD reset_incorrect RELOAD reset_incorrect
MAP reset_incorrect
CATCH blocked_account flag_account_blocked 1
MOUT retry 1 MOUT retry 1
MOUT quit 9 MOUT quit 9
HALT HALT

View File

@ -1 +1 @@
PIN ulioeka sio sahihi PIN ulioeka sio sahihi, una majaribio: {{.reset_incorrect}} yaliyobaki

View File

@ -7,8 +7,11 @@ msgstr "Ombi lako limetumwa. %s atapokea %s %s kutoka kwa %s."
msgid "Thank you for using Sarafu. Goodbye!" msgid "Thank you for using Sarafu. Goodbye!"
msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!" msgstr "Asante kwa kutumia huduma ya Sarafu. Kwaheri!"
msgid "For more help,please call: 0757628885" msgid "For more help, please call: 0757628885"
msgstr "Kwa usaidizi zaidi,piga: 0757628885" msgstr "Kwa usaidizi zaidi, piga: 0757628885"
msgid "Your account has been locked. For help on how to unblock your account, contact support at: 0757628885"
msgstr "Akaunti yako imefungwa. Kwa usaidizi wa jinsi ya kufungua akaunti yako, wasiliana na usaidizi kwa: 0757628885"
msgid "Balance: %s\n" msgid "Balance: %s\n"
msgstr "Salio: %s\n" msgstr "Salio: %s\n"

View File

@ -28,3 +28,5 @@ flag,flag_gender_set,34,this is set when the gender of the profile is set
flag,flag_location_set,35,this is set when the location of the profile is set flag,flag_location_set,35,this is set when the location of the profile is set
flag,flag_offerings_set,36,this is set when the offerings of the profile is set flag,flag_offerings_set,36,this is set when the offerings of the profile is set
flag,flag_back_set,37,this is set when it is a back navigation flag,flag_back_set,37,this is set when it is a back navigation
flag,flag_account_blocked,38,this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded

1 flag flag_language_set 8 checks whether the user has set their prefered language
28 flag flag_location_set 35 this is set when the location of the profile is set
29 flag flag_offerings_set 36 this is set when the offerings of the profile is set
30 flag flag_back_set 37 this is set when it is a back navigation
31 flag flag_account_blocked 38 this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded
32

View File

@ -1,3 +1,4 @@
CATCH blocked_account flag_account_blocked 1
CATCH select_language flag_language_set 0 CATCH select_language flag_language_set 0
CATCH terms flag_account_created 0 CATCH terms flag_account_created 0
LOAD check_account_status 0 LOAD check_account_status 0