Compare commits

...

5 Commits

Author SHA1 Message Date
518baceee5
update the translations to add the approximation sign ~
Some checks failed
release / docker (push) Has been cancelled
2025-12-02 12:28:55 +03:00
1cb82e9099
call the mpesa rates API to get the rates 2025-12-02 12:28:28 +03:00
efc93397b2
use the updated sarafu-api 2025-12-02 12:26:52 +03:00
5b19b3409b
remove the mpesa rates configs 2025-12-02 12:26:36 +03:00
2db97cde81
remove the hard-coded rates 2025-12-02 12:26:13 +03:00
6 changed files with 47 additions and 40 deletions

View File

@ -32,10 +32,8 @@ INCLUDE_STABLES_PARAM=false
#Mpesa #Mpesa
DEFAULT_MPESA_ADDRESS=0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e DEFAULT_MPESA_ADDRESS=0x48a953cA5cf5298bc6f6Af3C608351f537AAcb9e
MPESA_RATE=129.5
MIN_MPESA_SEND_AMOUNT=100 MIN_MPESA_SEND_AMOUNT=100
MAX_MPESA_SEND_AMOUNT=250000 MAX_MPESA_SEND_AMOUNT=250000
MPESA_SEND_RATE=130.2
DEFAULT_MPESA_ASSET=cUSD DEFAULT_MPESA_ASSET=cUSD
MPESA_BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr MPESA_BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr
MPESA_ONRAMP_BASE=https://pretium.v1.grassecon.net MPESA_ONRAMP_BASE=https://pretium.v1.grassecon.net

View File

@ -93,15 +93,6 @@ func DefaultMpesaAddress() string {
return env.GetEnv("DEFAULT_MPESA_ADDRESS", "") return env.GetEnv("DEFAULT_MPESA_ADDRESS", "")
} }
func MpesaRate() float64 {
v := env.GetEnv("MPESA_RATE", "129.5")
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return 129.5 // fallback default
}
return f
}
func MinMpesaSendAmount() float64 { func MinMpesaSendAmount() float64 {
v := env.GetEnv("MIN_MPESA_SEND_AMOUNT", "100") v := env.GetEnv("MIN_MPESA_SEND_AMOUNT", "100")
f, err := strconv.ParseFloat(v, 64) f, err := strconv.ParseFloat(v, 64)
@ -120,15 +111,6 @@ func MaxMpesaSendAmount() float64 {
return f return f
} }
func MpesaSendRate() float64 {
v := env.GetEnv("MPESA_SEND_RATE", "130.2")
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return 130.2 // fallback default
}
return f
}
func DefaultMpesaAsset() string { func DefaultMpesaAsset() string {
return env.GetEnv("DEFAULT_MPESA_ASSET", "") return env.GetEnv("DEFAULT_MPESA_ASSET", "")
} }

2
go.mod
View File

@ -7,7 +7,7 @@ toolchain go1.24.10
require ( require (
git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66 git.defalsify.org/vise.git v0.3.2-0.20250528124150-03bf7bfc1b66
git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20251127132814-8ceadabbc215 git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20251127132814-8ceadabbc215
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251128071248-bfdeef125576 git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251202085112-45469d4ba326
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694
github.com/alecthomas/assert/v2 v2.2.2 github.com/alecthomas/assert/v2 v2.2.2

2
go.sum
View File

@ -4,6 +4,8 @@ git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20251127132814-8cea
git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20251127132814-8ceadabbc215/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20251127132814-8ceadabbc215/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251128071248-bfdeef125576 h1:Ov4zENfEnzuU4ZpsNGbFjog9NUM0h1A7RYwWkmHRJWo= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251128071248-bfdeef125576 h1:Ov4zENfEnzuU4ZpsNGbFjog9NUM0h1A7RYwWkmHRJWo=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251128071248-bfdeef125576/go.mod h1:h/y/lJNJAVTcIzAxCMXXw8Dh2aoLxBFZ6F1nTB8C0nU= git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251128071248-bfdeef125576/go.mod h1:h/y/lJNJAVTcIzAxCMXXw8Dh2aoLxBFZ6F1nTB8C0nU=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251202085112-45469d4ba326 h1:qH4QulgncvAD7b/YeHGPxcDJTBIychPeoZJACefYryI=
git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251202085112-45469d4ba326/go.mod h1:h/y/lJNJAVTcIzAxCMXXw8Dh2aoLxBFZ6F1nTB8C0nU=
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 h1:Jo+yWysWw/N5BJQtAyEMN8ePVvAyPHv+JG4lQti+1N4= git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 h1:Jo+yWysWw/N5BJQtAyEMN8ePVvAyPHv+JG4lQti+1N4=
git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306/go.mod h1:FdLwYtzsjOIcDiW4uDgDYnB4Wdzq12uJUe0QHSSPbSo= git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306/go.mod h1:FdLwYtzsjOIcDiW4uDgDYnB4Wdzq12uJUe0QHSSPbSo=
git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E=

View File

@ -45,7 +45,15 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, err return res, err
} }
rate := config.MpesaRate() // call the mpesa rates API to get the rates
rates, err := h.accountService.GetMpesaOnrampRates(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on GetMpesaOnrampRates", "error", err)
return res, nil
}
txType := "swap" txType := "swap"
mpesaAddress := config.DefaultMpesaAddress() mpesaAddress := config.DefaultMpesaAddress()
@ -84,7 +92,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
} }
activeFloat, _ := strconv.ParseFloat(string(activeBal), 64) activeFloat, _ := strconv.ParseFloat(string(activeBal), 64)
ksh := fmt.Sprintf("%f", activeFloat*rate) ksh := fmt.Sprintf("%f", activeFloat*rates.Buy)
kshFormatted, _ := store.TruncateDecimalString(ksh, 0) kshFormatted, _ := store.TruncateDecimalString(ksh, 0)
@ -158,7 +166,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, err return res, err
} }
maxKsh := maxFloat * rate maxKsh := maxFloat * rates.Buy
kshStr := fmt.Sprintf("%f", maxKsh) kshStr := fmt.Sprintf("%f", maxKsh)
kshFormatted, _ := store.TruncateDecimalString(kshStr, 0) kshFormatted, _ := store.TruncateDecimalString(kshStr, 0)
@ -192,7 +200,15 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
l.AddDomain("default") l.AddDomain("default")
userStore := h.userdataStore userStore := h.userdataStore
rate := config.MpesaRate()
// call the mpesa rates API to get the rates
rates, err := h.accountService.GetMpesaOnrampRates(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on GetMpesaOnrampRates", "error", err)
return res, nil
}
// Input in Ksh // Input in Ksh
kshAmount, err := strconv.ParseFloat(inputStr, 64) kshAmount, err := strconv.ParseFloat(inputStr, 64)
@ -202,8 +218,8 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
return res, nil return res, nil
} }
// divide by the rate // divide by the buy rate
inputAmount := kshAmount / rate inputAmount := kshAmount / rates.Buy
// store the user's raw input amount in the temporary value // store the user's raw input amount in the temporary value
err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr)) err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr))
@ -252,7 +268,7 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
} }
res.Content = l.Get( res.Content = l.Get(
"You are sending %s %s in order to receive %s ksh", "You are sending %s %s in order to receive ~ %s ksh",
qouteInputAmount, swapData.ActiveSwapFromSym, inputStr, qouteInputAmount, swapData.ActiveSwapFromSym, inputStr,
) )
@ -311,7 +327,7 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
qouteInputAmount, _ := store.TruncateDecimalString(quoteInputStr, 2) qouteInputAmount, _ := store.TruncateDecimalString(quoteInputStr, 2)
res.Content = l.Get( res.Content = l.Get(
"You are sending %s %s in order to receive %s ksh", "You are sending %s %s in order to receive ~ %s ksh",
qouteInputAmount, swapData.ActiveSwapFromSym, inputStr, qouteInputAmount, swapData.ActiveSwapFromSym, inputStr,
) )
@ -370,7 +386,7 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [
logg.InfoCtxf(ctx, "TokenTransfer normal", "trackingId", tokenTransfer.TrackingId) logg.InfoCtxf(ctx, "TokenTransfer normal", "trackingId", tokenTransfer.TrackingId)
res.Content = l.Get("Your request has been sent. You will receive %s ksh", data.TemporaryValue) res.Content = l.Get("Your request has been sent. You will receive ~ %s ksh", data.TemporaryValue)
res.FlagReset = append(res.FlagReset, flag_account_authorized) res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil return res, nil
@ -418,7 +434,7 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [
logg.InfoCtxf(ctx, "final TokenTransfer after swap", "trackingId", tokenTransfer.TrackingId) logg.InfoCtxf(ctx, "final TokenTransfer after swap", "trackingId", tokenTransfer.TrackingId)
res.Content = l.Get("Your request has been sent. You will receive %s ksh", finalKshStr) res.Content = l.Get("Your request has been sent. You will receive ~ %s ksh", finalKshStr)
res.FlagReset = append(res.FlagReset, flag_account_authorized) res.FlagReset = append(res.FlagReset, flag_account_authorized)
return res, nil return res, nil
} }
@ -468,13 +484,22 @@ func (h *MenuHandlers) SendMpesaPreview(ctx context.Context, sym string, input [
} }
flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount")
flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error")
code := codeFromCtx(ctx) code := codeFromCtx(ctx)
l := gotext.NewLocale(translationDir, code) l := gotext.NewLocale(translationDir, code)
l.AddDomain("default") l.AddDomain("default")
userStore := h.userdataStore userStore := h.userdataStore
sendRate := config.MpesaSendRate()
// call the mpesa rates API to get the rates
rates, err := h.accountService.GetMpesaOnrampRates(ctx)
if err != nil {
res.FlagSet = append(res.FlagSet, flag_api_call_error)
res.Content = l.Get("Your request failed. Please try again later.")
logg.ErrorCtxf(ctx, "failed on GetMpesaOnrampRates", "error", err)
return res, nil
}
// Input in Ksh // Input in Ksh
kshAmount, err := strconv.ParseFloat(inputStr, 64) kshAmount, err := strconv.ParseFloat(inputStr, 64)
@ -502,12 +527,12 @@ func (h *MenuHandlers) SendMpesaPreview(ctx context.Context, sym string, input [
return res, err return res, err
} }
estimateValue := kshAmount / sendRate estimateValue := kshAmount / rates.Sell
estimateStr := fmt.Sprintf("%f", estimateValue) estimateStr := fmt.Sprintf("%f", estimateValue)
estimateFormatted, _ := store.TruncateDecimalString(estimateStr, 2) estimateFormatted, _ := store.TruncateDecimalString(estimateStr, 2)
res.Content = l.Get( res.Content = l.Get(
"You will get a prompt for your M-Pesa PIN shortly to send %s ksh and receive %s cUSD", "You will get a prompt for your M-Pesa PIN shortly to send %s ksh and receive ~ %s cUSD",
inputStr, estimateFormatted, inputStr, estimateFormatted,
) )

View File

@ -58,17 +58,17 @@ msgstr "%s atapokea %s %s"
msgid "Enter the amount of M-Pesa to get: (Max %s Ksh)\n" msgid "Enter the amount of M-Pesa to get: (Max %s Ksh)\n"
msgstr "Weka kiasi cha M-Pesa cha kupata: (Kikomo %s Ksh)\n" msgstr "Weka kiasi cha M-Pesa cha kupata: (Kikomo %s Ksh)\n"
msgid "You are sending %s %s in order to receive %s ksh" msgid "You are sending %s %s in order to receive ~ %s ksh"
msgstr "Unatuma %s %s ili upoke %s ksh" msgstr "Unatuma ~ %s %s ili upoke %s ksh"
msgid "Your request has been sent. You will receive %s ksh" msgid "Your request has been sent. You will receive ~ %s ksh"
msgstr "Ombi lako limetumwa. Utapokea %s ksh" msgstr "Ombi lako limetumwa. Utapokea ~ %s ksh"
msgid "Enter the amount of M-Pesa to send: (Minimum %s Ksh)\n" msgid "Enter the amount of M-Pesa to send: (Minimum %s Ksh)\n"
msgstr "Weka kiasi cha M-Pesa cha kutuma: (Kima cha chini %s Ksh)\n" msgstr "Weka kiasi cha M-Pesa cha kutuma: (Kima cha chini %s Ksh)\n"
msgid "You will get a prompt for your M-Pesa PIN shortly to send %s ksh and receive %s cUSD" msgid "You will get a prompt for your M-Pesa PIN shortly to send %s ksh and receive ~ %s cUSD"
msgstr "Utapokea kidokezo cha PIN yako ya M-Pesa hivi karibuni kutuma %s ksh na kupokea %s cUSD" msgstr "Utapokea kidokezo cha PIN yako ya M-Pesa hivi karibuni kutuma %s ksh na kupokea ~ %s cUSD"
msgid "Your request has been sent. Thank you for using Sarafu" msgid "Your request has been sent. Thank you for using Sarafu"
msgstr "Ombi lako limetumwa. Asante kwa kutumia huduma ya Sarafu" msgstr "Ombi lako limetumwa. Asante kwa kutumia huduma ya Sarafu"