diff --git a/handlers/application/mpesa.go b/handlers/application/mpesa.go index 695c0e2..467adc8 100644 --- a/handlers/application/mpesa.go +++ b/handlers/application/mpesa.go @@ -28,20 +28,36 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [ flag_api_call_error, _ := h.flagManager.GetFlag("flag_api_call_error") flag_low_swap_amount, _ := h.flagManager.GetFlag("flag_low_swap_amount") flag_incorrect_pool, _ := h.flagManager.GetFlag("flag_incorrect_pool") + flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") inputStr := string(input) - if inputStr == "0" || inputStr == "9" { + if inputStr == "0" || inputStr == "99" || inputStr == "88" || inputStr == "98" { + res.FlagReset = append(res.FlagReset, flag_low_swap_amount, flag_api_call_error, flag_incorrect_voucher) return res, nil } userStore := h.userdataStore + metadata, err := store.GetOrderedVoucherData(ctx, userStore, sessionId, inputStr) + if err != nil { + return res, fmt.Errorf("failed to retrieve swap to voucher data: %v", err) + } + if metadata == nil { + res.FlagSet = append(res.FlagSet, flag_incorrect_voucher) + return res, nil + } + + // Store the active transaction voucher data + if err := store.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { + logg.ErrorCtxf(ctx, "failed on StoreTemporaryVoucher", "error", err) + return res, err + } // Fetch session data - _, activeBal, _, activeAddress, publicKey, activeDecimal, err := h.getSessionData(ctx, sessionId) + _, _, _, _, publicKey, _, err := h.getSessionData(ctx, sessionId) if err != nil { return res, err } @@ -83,8 +99,12 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [ return res, err } + // Fetch min withdrawal amount from config/env + minksh := fmt.Sprintf("%f", config.MinMpesaWithdrawAmount()) + minKshFormatted, _ := store.TruncateDecimalString(minksh, 0) + // If RAT is the same as SAT, return early with KSH format - if string(activeAddress) == string(recipientActiveAddress) { + if string(metadata.TokenAddress) == string(recipientActiveAddress) { txType = "normal" // Save the transaction type if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_SEND_TRANSACTION_TYPE, []byte(txType)); err != nil { @@ -92,14 +112,15 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [ return res, err } - activeFloat, _ := strconv.ParseFloat(string(activeBal), 64) + activeFloat, _ := strconv.ParseFloat(string(metadata.Balance), 64) ksh := fmt.Sprintf("%f", activeFloat*rates.Buy) - kshFormatted, _ := store.TruncateDecimalString(ksh, 0) + maxKshFormatted, _ := store.TruncateDecimalString(ksh, 0) res.Content = l.Get( - "Enter the amount of Mpesa to get: (Max %s Ksh)\n", - kshFormatted, + "Enter the amount of Mpesa to withdraw: (Min: Ksh %s, Max %s Ksh)\n", + minKshFormatted, + maxKshFormatted, ) return res, nil @@ -112,7 +133,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [ } // Check if sender token is swappable - canSwap, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(activeAddress)) + canSwap, err := h.accountService.CheckTokenInPool(ctx, string(activePoolAddress), string(metadata.TokenAddress)) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_call_error) logg.ErrorCtxf(ctx, "failed on CheckTokenInPool", "error", err) @@ -125,7 +146,7 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [ } // retrieve the max credit send amounts - _, maxRAT, err := h.calculateSendCreditLimits(ctx, activePoolAddress, activeAddress, recipientActiveAddress, publicKey, activeDecimal, recipientActiveDecimal) + _, maxRAT, err := h.calculateSendCreditLimits(ctx, activePoolAddress, []byte(metadata.TokenAddress), recipientActiveAddress, publicKey, []byte(metadata.TokenDecimals), recipientActiveDecimal) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_call_error) logg.ErrorCtxf(ctx, "failed on calculateSendCreditLimits", "error", err) @@ -155,25 +176,26 @@ func (h *MenuHandlers) GetMpesaMaxLimit(ctx context.Context, sym string, input [ } // save swap related data for the swap preview - metadata := &dataserviceapi.TokenHoldings{ + swapMetadata := &dataserviceapi.TokenHoldings{ TokenAddress: string(recipientActiveAddress), TokenSymbol: string(recipientActiveSym), TokenDecimals: string(recipientActiveDecimal), } // Store the active swap_to data - if err := store.UpdateSwapToVoucherData(ctx, userStore, sessionId, metadata); err != nil { + if err := store.UpdateSwapToVoucherData(ctx, userStore, sessionId, swapMetadata); err != nil { logg.ErrorCtxf(ctx, "failed on UpdateSwapToVoucherData", "error", err) return res, err } maxKsh := maxFloat * rates.Buy kshStr := fmt.Sprintf("%f", maxKsh) - kshFormatted, _ := store.TruncateDecimalString(kshStr, 0) + maxKshFormatted, _ := store.TruncateDecimalString(kshStr, 0) res.Content = l.Get( - "Enter the amount of Mpesa to get: (Max %s Ksh)\n", - kshFormatted, + "Enter the amount of Mpesa to withdraw: (Min: Ksh %s, Max %s Ksh)\n", + minKshFormatted, + maxKshFormatted, ) return res, nil @@ -189,7 +211,7 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input [] // INPUT IN RAT Ksh inputStr := string(input) - if inputStr == "9" { + if inputStr == "0" || inputStr == "9" { return res, nil } @@ -219,16 +241,18 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input [] return res, nil } + min := config.MinMpesaWithdrawAmount() + + if kshAmount < min { + // if the input is below the minimum + res.FlagSet = append(res.FlagSet, flag_invalid_amount) + res.Content = inputStr + return res, nil + } + // divide by the buy rate inputAmount := kshAmount / rates.Buy - // store the user's raw input amount in the temporary value - err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(inputStr)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporary inputStr entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", inputStr, "error", err) - return res, err - } - swapData, err := store.ReadSwapPreviewData(ctx, userStore, sessionId) if err != nil { return res, err @@ -239,19 +263,21 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input [] return res, err } - if string(transactionType) == "normal" { - activeBal, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err) - return res, err - } - balanceValue, err := strconv.ParseFloat(string(activeBal), 64) - if err != nil { - logg.ErrorCtxf(ctx, "Failed to convert the activeBal to a float", "error", err) - return res, err - } + // get the selected voucher + mpesaWithdrawalVoucher, err := store.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) + if err != nil { + logg.ErrorCtxf(ctx, "failed on GetTemporaryVoucherData", "error", err) + return res, err + } - if inputAmount > balanceValue { + maxValue, err := strconv.ParseFloat(mpesaWithdrawalVoucher.Balance, 64) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to convert the swapMaxAmount to a float", "error", err) + return res, err + } + + if string(transactionType) == "normal" { + if inputAmount > maxValue { res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.Content = inputStr return res, nil @@ -270,7 +296,7 @@ func (h *MenuHandlers) GetMpesaPreview(ctx context.Context, sym string, input [] res.Content = l.Get( "You are sending %s %s in order to receive ~ %s ksh", - qouteInputAmount, swapData.ActiveSwapFromSym, inputStr, + qouteInputAmount, mpesaWithdrawalVoucher.TokenSymbol, inputStr, ) return res, nil @@ -365,6 +391,12 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [ return res, err } + mpesaWithdrawalVoucher, err := store.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) + if err != nil { + logg.ErrorCtxf(ctx, "failed on GetTemporaryVoucherData", "error", err) + return res, err + } + if string(transactionType) == "normal" { // Call TokenTransfer for the normal transaction data, err := store.ReadTransactionData(ctx, h.userdataStore, sessionId) @@ -372,12 +404,12 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [ return res, err } - finalAmountStr, err := store.ParseAndScaleAmount(data.Amount, data.ActiveDecimal) + finalAmountStr, err := store.ParseAndScaleAmount(data.Amount, mpesaWithdrawalVoucher.TokenDecimals) if err != nil { return res, err } - tokenTransfer, err := h.accountService.TokenTransfer(ctx, finalAmountStr, data.PublicKey, mpesaAddress, data.ActiveAddress) + tokenTransfer, err := h.accountService.TokenTransfer(ctx, finalAmountStr, data.PublicKey, mpesaAddress, mpesaWithdrawalVoucher.TokenAddress) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_call_error) res.Content = l.Get("Your request failed. Please try again later.") @@ -416,11 +448,6 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [ // TODO: remove this temporary time delay and replace with a swap and send endpoint time.Sleep(1 * time.Second) - finalKshStr, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) - if err != nil { - return res, err - } - amount, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_AMOUNT) if err != nil { return res, err @@ -437,7 +464,7 @@ func (h *MenuHandlers) InitiateGetMpesa(ctx context.Context, sym string, input [ 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. Please await confirmation") res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } diff --git a/services/registration/get_mpesa b/services/registration/get_mpesa index 3ba5d3c..90039ed 100644 --- a/services/registration/get_mpesa +++ b/services/registration/get_mpesa @@ -1 +1 @@ -{{.get_mpesa_max_limit}} \ No newline at end of file +{{.get_ordered_vouchers}} \ No newline at end of file diff --git a/services/registration/get_mpesa.vis b/services/registration/get_mpesa.vis index bf536d3..f9a9b42 100644 --- a/services/registration/get_mpesa.vis +++ b/services/registration/get_mpesa.vis @@ -1,10 +1,15 @@ CATCH no_voucher flag_no_active_voucher 1 +LOAD get_ordered_vouchers 0 +MAP get_ordered_vouchers +MOUT back 0 +MOUT quit 99 +MNEXT next 88 +MPREV prev 98 +HALT +INCMP > 88 +INCMP < 98 +INCMP _ 0 +INCMP quit 99 LOAD get_mpesa_max_limit 0 RELOAD get_mpesa_max_limit -MAP get_mpesa_max_limit -MOUT back 0 -MOUT quit 9 -HALT -INCMP _ 0 -INCMP quit 9 -INCMP get_mpesa_confirmation * +INCMP mpesa_max_limit * diff --git a/services/registration/get_mpesa_confirmation.vis b/services/registration/get_mpesa_confirmation.vis index eccee90..5514765 100644 --- a/services/registration/get_mpesa_confirmation.vis +++ b/services/registration/get_mpesa_confirmation.vis @@ -1,7 +1,4 @@ -LOAD get_mpesa_preview 0 MAP get_mpesa_preview -CATCH api_failure flag_api_call_error 1 -CATCH invalid_credit_send_amount flag_invalid_amount 1 MOUT back 0 MOUT quit 9 HALT diff --git a/services/registration/invalid_get_mpesa_amount b/services/registration/invalid_get_mpesa_amount new file mode 100644 index 0000000..7f5fd35 --- /dev/null +++ b/services/registration/invalid_get_mpesa_amount @@ -0,0 +1 @@ +Amount {{.get_mpesa_preview}} is invalid, please try again: \ No newline at end of file diff --git a/services/registration/invalid_get_mpesa_amount.vis b/services/registration/invalid_get_mpesa_amount.vis new file mode 100644 index 0000000..3cba646 --- /dev/null +++ b/services/registration/invalid_get_mpesa_amount.vis @@ -0,0 +1,7 @@ +MAP get_mpesa_preview +RELOAD reset_transaction_amount +MOUT retry 1 +MOUT quit 9 +HALT +INCMP _ 1 +INCMP quit 9 diff --git a/services/registration/invalid_get_mpesa_amount_swa b/services/registration/invalid_get_mpesa_amount_swa new file mode 100644 index 0000000..27afab9 --- /dev/null +++ b/services/registration/invalid_get_mpesa_amount_swa @@ -0,0 +1 @@ +Kiwango {{.get_mpesa_preview}} sio sahihi, tafadhali weka tena: \ No newline at end of file diff --git a/services/registration/mpesa_max_limit b/services/registration/mpesa_max_limit new file mode 100644 index 0000000..3ba5d3c --- /dev/null +++ b/services/registration/mpesa_max_limit @@ -0,0 +1 @@ +{{.get_mpesa_max_limit}} \ No newline at end of file diff --git a/services/registration/mpesa_max_limit.vis b/services/registration/mpesa_max_limit.vis new file mode 100644 index 0000000..d3573ca --- /dev/null +++ b/services/registration/mpesa_max_limit.vis @@ -0,0 +1,13 @@ +LOAD reset_transaction_amount 10 +RELOAD reset_transaction_amount +MAP get_mpesa_max_limit +MOUT back 0 +MOUT quit 9 +HALT +INCMP _ 0 +INCMP quit 9 +LOAD get_mpesa_preview 90 +RELOAD get_mpesa_preview +CATCH api_failure flag_api_call_error 1 +CATCH invalid_get_mpesa_amount flag_invalid_amount 1 +INCMP get_mpesa_confirmation *