Compare commits

..

No commits in common. "518baceee587ebcf7dd1690da077a9bef63f8829" and "3ba891a18472805f8fe5ef227d1807392e9df0cc" have entirely different histories.

6 changed files with 40 additions and 47 deletions

View File

@ -32,8 +32,10 @@ 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,6 +93,15 @@ 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)
@ -111,6 +120,15 @@ 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.20251202085112-45469d4ba326 git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20251128071248-bfdeef125576
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,8 +4,6 @@ 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,15 +45,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, err return res, err
} }
// call the mpesa rates API to get the rates rate := config.MpesaRate()
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()
@ -92,7 +84,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*rates.Buy) ksh := fmt.Sprintf("%f", activeFloat*rate)
kshFormatted, _ := store.TruncateDecimalString(ksh, 0) kshFormatted, _ := store.TruncateDecimalString(ksh, 0)
@ -166,7 +158,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [
return res, err return res, err
} }
maxKsh := maxFloat * rates.Buy maxKsh := maxFloat * rate
kshStr := fmt.Sprintf("%f", maxKsh) kshStr := fmt.Sprintf("%f", maxKsh)
kshFormatted, _ := store.TruncateDecimalString(kshStr, 0) kshFormatted, _ := store.TruncateDecimalString(kshStr, 0)
@ -200,15 +192,7 @@ 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)
@ -218,8 +202,8 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input []
return res, nil return res, nil
} }
// divide by the buy rate // divide by the rate
inputAmount := kshAmount / rates.Buy inputAmount := kshAmount / rate
// 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))
@ -268,7 +252,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,
) )
@ -327,7 +311,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,
) )
@ -386,7 +370,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
@ -434,7 +418,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
} }
@ -484,22 +468,13 @@ 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)
@ -527,12 +502,12 @@ func (h *MenuHandlers) SendMpesaPreview(ctx context.Context, sym string, input [
return res, err return res, err
} }
estimateValue := kshAmount / rates.Sell estimateValue := kshAmount / sendRate
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"