Compare commits

...

24 Commits

Author SHA1 Message Date
Carlosokumu
bee9ad5ff5 Merge remote-tracking branch 'refs/remotes/origin/api-context' into api-context 2024-10-24 17:39:11 +03:00
Carlosokumu
6e7b46666e ensure mod match with master 2024-10-24 17:34:42 +03:00
Carlosokumu
383f074cae update method signatures 2024-10-24 17:32:08 +03:00
Carlosokumu
d678a639b8 Merge branch 'master' into api-context 2024-10-24 17:07:11 +03:00
453fea569a Merge pull request 'api-structs' (#117) from api-structs into master
Reviewed-on: urdt/ussd#117
2024-10-24 15:53:46 +02:00
carlos
5c75e35fe0 Delete coverage.html
delete cover.html
2024-10-24 15:45:52 +02:00
carlos
cff50538fa Delete cover.out
delete cover.out
2024-10-24 15:45:29 +02:00
carlos
69a4530269 Delete services/registration/locale/swa/default.mo
remove dev file
2024-10-24 15:41:47 +02:00
Carlosokumu
db19e38717 Merge remote-tracking branch 'refs/remotes/origin/api-structs' into api-structs 2024-10-24 16:37:07 +03:00
Carlosokumu
c796bbdcfc correct create endpoint 2024-10-24 16:36:09 +03:00
Carlosokumu
2b34a0900c check for status code and unmarshal on errResponse 2024-10-24 16:35:53 +03:00
Carlosokumu
57a49819f4 Merge branch 'master' into api-structs 2024-10-24 16:22:41 +03:00
Carlosokumu
abc01b7cee change to local setup endpoint 2024-10-24 16:21:34 +03:00
d19c20a9d7 Merge branch 'master' into api-structs 2024-10-24 15:19:06 +02:00
0c6144d262 Merge pull request 'send-menu-update' (#131) from send-menu-update into master
Reviewed-on: urdt/ussd#131
2024-10-24 15:15:11 +02:00
alfred-mk
ec4e44a27c Merge branch 'master' into send-menu-update 2024-10-24 15:58:59 +03:00
69eb57f794 Merge branch 'master' into api-context 2024-10-24 14:51:25 +02:00
2347d64acc Merge pull request 'check-balance-update' (#132) from check-balance-update into master
Reviewed-on: urdt/ussd#132
2024-10-24 14:50:59 +02:00
alfred-mk
39c0560abe Updated the test 2024-10-24 15:21:48 +03:00
alfred-mk
75459f852b Return the full balance string 2024-10-24 15:12:57 +03:00
alfred-mk
6200728435 Updated the menuhander test 2024-10-24 14:45:15 +03:00
alfred-mk
579b46db65 Replace the public key with the sessionId 2024-10-24 14:34:12 +03:00
Carlosokumu
b41e52af63 pass context.Context 2024-10-23 12:45:54 +03:00
Carlosokumu
fb32dde136 run go mod tidy 2024-10-23 12:45:10 +03:00
8 changed files with 65 additions and 66 deletions

2
go.mod
View File

@@ -44,3 +44,5 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View File

@@ -1,6 +1,7 @@
package server package server
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@@ -16,14 +17,13 @@ import (
var ( var (
okResponse api.OKResponse okResponse api.OKResponse
errResponse api.ErrResponse errResponse api.ErrResponse
EMPTY_RESPONSE = 0
) )
type AccountServiceInterface interface { type AccountServiceInterface interface {
CheckBalance(publicKey string) (*models.BalanceResponse, error) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error)
CreateAccount() (*api.OKResponse, error) CreateAccount(ctx context.Context) (*api.OKResponse, error)
CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error)
TrackAccountStatus(publicKey string) (*api.OKResponse, error) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error)
} }
type AccountService struct { type AccountService struct {
@@ -41,7 +41,7 @@ type TestAccountService struct {
// - 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. // - 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. // - 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 // If no error occurs, this will be nil
func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) {
resp, err := http.Get(config.BalanceURL + trackingId) resp, err := http.Get(config.BalanceURL + trackingId)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -62,7 +62,7 @@ func (as *AccountService) CheckAccountStatus(trackingId string) (*models.TrackSt
} }
func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) {
var err error var err error
// Construct the URL with the path parameter // Construct the URL with the path parameter
url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey)
@@ -85,15 +85,18 @@ func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse,
errResponse.Description = err.Error() errResponse.Description = err.Error()
return nil, err return nil, err
} }
err = json.Unmarshal([]byte(body), &okResponse) if resp.StatusCode >= http.StatusBadRequest {
if err != nil {
err := json.Unmarshal([]byte(body), &errResponse) err := json.Unmarshal([]byte(body), &errResponse)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return nil, errors.New(errResponse.Description) return nil, errors.New(errResponse.Description)
} }
if len(okResponse.Result) == EMPTY_RESPONSE { 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 nil, errors.New("Empty api result")
} }
return &okResponse, nil return &okResponse, nil
@@ -103,7 +106,7 @@ func (as *AccountService) TrackAccountStatus(publicKey string) (*api.OKResponse,
// CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
// Parameters: // Parameters:
// - publicKey: The public key associated with the account whose balance needs to be checked. // - publicKey: The public key associated with the account whose balance needs to be checked.
func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) {
resp, err := http.Get(config.BalanceURL + publicKey) resp, err := http.Get(config.BalanceURL + publicKey)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -127,7 +130,7 @@ func (as *AccountService) CheckBalance(publicKey string) (*models.BalanceRespons
// If there is an error during the request or processing, this will be nil. // 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. // - 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. // If no error occurs, this will be nil.
func (as *AccountService) CreateAccount() (*api.OKResponse, error) { func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) {
var err error var err error
// Create a new request // Create a new request
@@ -149,21 +152,24 @@ func (as *AccountService) CreateAccount() (*api.OKResponse, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = json.Unmarshal([]byte(body), &okResponse) if resp.StatusCode >= http.StatusBadRequest {
if err != nil {
err := json.Unmarshal([]byte(body), &errResponse) err := json.Unmarshal([]byte(body), &errResponse)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return nil, errors.New(errResponse.Description) return nil, errors.New(errResponse.Description)
} }
if len(okResponse.Result) == EMPTY_RESPONSE { 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 nil, errors.New("Empty api result")
} }
return &okResponse, nil return &okResponse, nil
} }
func (tas *TestAccountService) CreateAccount() (*api.OKResponse, error) { func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) {
return &api.OKResponse{ return &api.OKResponse{
Ok: true, Ok: true,
Description: "Account creation request received successfully", Description: "Account creation request received successfully",
@@ -172,7 +178,7 @@ func (tas *TestAccountService) CreateAccount() (*api.OKResponse, error) {
} }
func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) {
balanceResponse := &models.BalanceResponse{ balanceResponse := &models.BalanceResponse{
Ok: true, Ok: true,
Result: struct { Result: struct {
@@ -186,7 +192,7 @@ func (tas *TestAccountService) CheckBalance(publicKey string) (*models.BalanceRe
return balanceResponse, nil return balanceResponse, nil
} }
func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) {
return &api.OKResponse{ return &api.OKResponse{
Ok: true, Ok: true,
Description: "Account creation succeeded", Description: "Account creation succeeded",
@@ -196,7 +202,7 @@ func (tas *TestAccountService) TrackAccountStatus(publicKey string) (*api.OKResp
}, nil }, nil
} }
func (tas *TestAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) {
trackResponse := &models.TrackStatusResponse{ trackResponse := &models.TrackStatusResponse{
Ok: true, Ok: true,
Result: struct { Result: struct {

View File

@@ -140,7 +140,7 @@ 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 { func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error {
flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") flag_account_created, _ := h.flagManager.GetFlag("flag_account_created")
okResponse, err := h.accountService.CreateAccount() okResponse, err := h.accountService.CreateAccount(ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -544,7 +544,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b
if err != nil { if err != nil {
return res, err return res, err
} }
okResponse, err = h.accountService.TrackAccountStatus(string(publicKey)) okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey))
if err != nil { if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_error) res.FlagSet = append(res.FlagSet, flag_api_error)
return res, err return res, err
@@ -641,13 +641,17 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) (
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code)
l.AddDomain("default")
store := h.userdataStore store := h.userdataStore
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
if err != nil { if err != nil {
return res, err return res, err
} }
balanceResponse, err := h.accountService.CheckBalance(string(publicKey)) balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey))
if err != nil { if err != nil {
return res, nil return res, nil
} }
@@ -657,7 +661,8 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) (
} }
res.FlagReset = append(res.FlagReset, flag_api_error) res.FlagReset = append(res.FlagReset, flag_api_error)
balance := balanceResponse.Result.Balance balance := balanceResponse.Result.Balance
res.Content = balance
res.Content = l.Get("Balance: %s\n", balance)
return res, nil return res, nil
} }
@@ -679,7 +684,7 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input
return res, err return res, err
} }
balanceResponse, err := h.accountService.CheckBalance(string(publicKey)) balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey))
if err != nil { if err != nil {
return res, nil return res, nil
} }
@@ -797,7 +802,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res
store := h.userdataStore store := h.userdataStore
publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
balanceResp, err := h.accountService.CheckBalance(string(publicKey)) balanceResp, err := h.accountService.CheckBalance(ctx, string(publicKey))
if err != nil { if err != nil {
return res, nil return res, nil
} }
@@ -827,7 +832,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte)
amountStr := string(input) amountStr := string(input)
balanceRes, err := h.accountService.CheckBalance(string(publicKey)) balanceRes, err := h.accountService.CheckBalance(ctx, string(publicKey))
balanceStr := balanceRes.Result.Balance balanceStr := balanceRes.Result.Balance
if !balanceRes.Ok { if !balanceRes.Ok {
@@ -897,7 +902,7 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) (
return res, nil return res, nil
} }
// GetSender retrieves the public key from the Gdbm Db // GetSender returns the sessionId (phoneNumber)
func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
@@ -906,10 +911,7 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res
return res, fmt.Errorf("missing session") return res, fmt.Errorf("missing session")
} }
store := h.userdataStore res.Content = string(sessionId)
publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
res.Content = string(publicKey)
return res, nil return res, nil
} }
@@ -946,13 +948,12 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []
// TODO // TODO
// Use the amount, recipient and sender to call the API and initialize the transaction // Use the amount, recipient and sender to call the API and initialize the transaction
store := h.userdataStore store := h.userdataStore
publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT)
recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT)
res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(publicKey)) res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(sessionId))
account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized")
if err != nil { if err != nil {

View File

@@ -516,12 +516,8 @@ func TestGetSender(t *testing.T) {
mockStore := new(mocks.MockUserDataStore) mockStore := new(mocks.MockUserDataStore)
// Define test data // Define test data
sessionId := "session123" sessionId := "254712345678"
ctx := context.WithValue(context.Background(), "SessionId", sessionId) 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 // Create the Handlers instance with the mock store
h := &Handlers{ h := &Handlers{
@@ -529,11 +525,10 @@ func TestGetSender(t *testing.T) {
} }
// Call the method // Call the method
res, _ := h.GetSender(ctx, "max_amount", []byte("check_balance")) res, _ := h.GetSender(ctx, "get_sender", []byte(""))
//Assert that the public key from readentry operation is what was set as the result content.
assert.Equal(t, publicKey, res.Content)
//Assert that the sessionId is what was set as the result content.
assert.Equal(t, sessionId, res.Content)
} }
func TestGetAmount(t *testing.T) { func TestGetAmount(t *testing.T) {
@@ -1297,7 +1292,7 @@ func TestResetInvalidAmount(t *testing.T) {
} }
func TestInitiateTransaction(t *testing.T) { func TestInitiateTransaction(t *testing.T) {
sessionId := "session123" sessionId := "254712345678"
fm, err := NewFlagManager(flagsPath) fm, err := NewFlagManager(flagsPath)
@@ -1320,30 +1315,26 @@ func TestInitiateTransaction(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
input []byte input []byte
PublicKey []byte
Recipient []byte Recipient []byte
Amount []byte Amount []byte
status string status string
expectedResult resource.Result expectedResult resource.Result
}{ }{
{ {
name: "Test amount reset", name: "Test initiate transaction",
PublicKey: []byte("0x1241527192"),
Amount: []byte("0.002 CELO"), Amount: []byte("0.002 CELO"),
Recipient: []byte("0x12415ass27192"), Recipient: []byte("0x12415ass27192"),
expectedResult: resource.Result{ expectedResult: resource.Result{
FlagReset: []uint32{account_authorized_flag}, FlagReset: []uint32{account_authorized_flag},
Content: "Your request has been sent. 0x12415ass27192 will receive 0.002CELO from 0x1241527192.", Content: "Your request has been sent. 0x12415ass27192 will receive 0.002 CELO from 254712345678.",
}, },
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// Define expected interactions with the mock // Define expected interactions with the mock
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.PublicKey, nil)
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil)
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil)
//mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil)
// Call the method under test // Call the method under test
res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input)
@@ -1356,10 +1347,8 @@ func TestInitiateTransaction(t *testing.T) {
// Assert that expectations were met // Assert that expectations were met
mockDataStore.AssertExpectations(t) mockDataStore.AssertExpectations(t)
}) })
} }
} }
func TestQuit(t *testing.T) { func TestQuit(t *testing.T) {
@@ -1638,7 +1627,6 @@ func TestValidateRecipient(t *testing.T) {
} }
func TestCheckBalance(t *testing.T) { func TestCheckBalance(t *testing.T) {
sessionId := "session123" sessionId := "session123"
publicKey := "0X13242618721" publicKey := "0X13242618721"
fm, _ := NewFlagManager(flagsPath) fm, _ := NewFlagManager(flagsPath)
@@ -1680,7 +1668,7 @@ func TestCheckBalance(t *testing.T) {
}, },
}, },
expectedResult: resource.Result{ expectedResult: resource.Result{
Content: "0.003 CELO", Content: "Balance: 0.003 CELO\n",
FlagReset: []uint32{flag_api_error}, FlagReset: []uint32{flag_api_error},
}, },
}, },
@@ -1713,10 +1701,8 @@ func TestCheckBalance(t *testing.T) {
//Assert that the result set to content is what was expected //Assert that the result set to content is what was expected
assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input") assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input")
}) })
} }
} }
func TestGetProfile(t *testing.T) { func TestGetProfile(t *testing.T) {

View File

@@ -1,6 +1,8 @@
package mocks package mocks
import ( import (
"context"
"git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/models"
"github.com/grassrootseconomics/eth-custodial/pkg/api" "github.com/grassrootseconomics/eth-custodial/pkg/api"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
@@ -11,22 +13,22 @@ type MockAccountService struct {
mock.Mock mock.Mock
} }
func (m *MockAccountService) CreateAccount() (*api.OKResponse, error) { func (m *MockAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) {
args := m.Called() args := m.Called()
return args.Get(0).(*api.OKResponse), args.Error(1) return args.Get(0).(*api.OKResponse), args.Error(1)
} }
func (m *MockAccountService) CheckBalance(publicKey string) (*models.BalanceResponse, error) { func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) {
args := m.Called(publicKey) args := m.Called(publicKey)
return args.Get(0).(*models.BalanceResponse), args.Error(1) return args.Get(0).(*models.BalanceResponse), args.Error(1)
} }
func (m *MockAccountService) CheckAccountStatus(trackingId string) (*models.TrackStatusResponse, error) { func (m *MockAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) {
args := m.Called(trackingId) args := m.Called(trackingId)
return args.Get(0).(*models.TrackStatusResponse), args.Error(1) return args.Get(0).(*models.TrackStatusResponse), args.Error(1)
} }
func (m *MockAccountService) TrackAccountStatus(publicKey string) (*api.OKResponse, error) { func (m *MockAccountService) TrackAccountStatus(ctx context.Context,publicKey string) (*api.OKResponse, error) {
args := m.Called(publicKey) args := m.Called(publicKey)
return args.Get(0).(*api.OKResponse), args.Error(1) return args.Get(0).(*api.OKResponse), args.Error(1)
} }

View File

@@ -7,6 +7,8 @@ msgstr "Ombi lako limetumwa. %s atapokea %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 "Balance: %s\n"
msgstr "Salio: %s\n"

View File

@@ -1 +1 @@
Balance: {{.check_balance}} {{.check_balance}}

View File

@@ -1 +1 @@
Salio: {{.check_balance}} {{.check_balance}}