Files
sendico/api/notification/internal/server/notificationimp/confirmation_request.go

151 lines
5.3 KiB
Go

package notificationimp
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/tech/sendico/pkg/merrors"
confirmations "github.com/tech/sendico/pkg/messaging/notifications/confirmations"
"github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mservice"
"go.uber.org/zap"
)
func (a *NotificationAPI) onConfirmationRequest(ctx context.Context, request *model.ConfirmationRequest) error {
if request == nil {
return merrors.InvalidArgument("confirmation request is nil", "request")
}
if a == nil || a.tg == nil {
return merrors.Internal("telegram client is not configured")
}
req := normalizeConfirmationRequest(*request)
if req.RequestID == "" {
return merrors.InvalidArgument("confirmation request_id is required", "request_id")
}
if req.TargetChatID == "" {
return merrors.InvalidArgument("confirmation target_chat_id is required", "target_chat_id")
}
if req.RequestedMoney == nil || strings.TrimSpace(req.RequestedMoney.Amount) == "" || strings.TrimSpace(req.RequestedMoney.Currency) == "" {
return merrors.InvalidArgument("confirmation requested_money is required", "requested_money")
}
if req.SourceService == "" {
return merrors.InvalidArgument("confirmation source_service is required", "source_service")
}
prompt := confirmationPrompt(&req)
sent, err := a.tg.SendText(ctx, req.TargetChatID, prompt, "")
if err != nil {
a.logger.Warn("Failed to send confirmation prompt to Telegram", zap.Error(err), zap.String("request_id", req.RequestID), zap.String("chat_id", req.TargetChatID))
return err
}
if sent == nil || strings.TrimSpace(sent.MessageID) == "" {
return merrors.Internal("telegram confirmation message id is missing")
}
a.logger.Info("Telegram confirmation prompt sent",
zap.String("request_id", req.RequestID),
zap.String("chat_id", req.TargetChatID),
zap.String("message_id", strings.TrimSpace(sent.MessageID)))
if a.producer == nil {
return merrors.Internal("messaging producer is not configured")
}
dispatch := &model.ConfirmationRequestDispatch{
RequestID: req.RequestID,
ChatID: req.TargetChatID,
MessageID: strings.TrimSpace(sent.MessageID),
SourceService: req.SourceService,
Rail: req.Rail,
}
env := confirmations.ConfirmationDispatch(string(mservice.Notifications), dispatch, req.SourceService, req.Rail)
if err := a.producer.SendMessage(env); err != nil {
a.logger.Warn("Failed to publish confirmation dispatch", zap.Error(err), zap.String("request_id", req.RequestID), zap.String("message_id", dispatch.MessageID))
return err
}
return nil
}
func (a *NotificationAPI) onTelegramText(ctx context.Context, request *model.TelegramTextRequest) error {
if request == nil {
return merrors.InvalidArgument("telegram text request is nil", "request")
}
if a == nil || a.tg == nil {
return merrors.Internal("telegram client is not configured")
}
request.ChatID = strings.TrimSpace(request.ChatID)
request.ReplyToMessageID = strings.TrimSpace(request.ReplyToMessageID)
request.Text = strings.TrimSpace(request.Text)
if request.ChatID == "" {
return merrors.InvalidArgument("telegram chat_id is required", "chat_id")
}
if request.Text == "" {
return merrors.InvalidArgument("telegram text is required", "text")
}
if _, err := a.tg.SendText(ctx, request.ChatID, request.Text, request.ReplyToMessageID); err != nil {
a.logger.Warn("Failed to send telegram text", zap.Error(err),
zap.String("request_id", request.RequestID),
zap.String("chat_id", request.ChatID),
zap.String("reply_to_message_id", request.ReplyToMessageID))
return err
}
return nil
}
func normalizeConfirmationRequest(request model.ConfirmationRequest) model.ConfirmationRequest {
request.RequestID = strings.TrimSpace(request.RequestID)
request.TargetChatID = strings.TrimSpace(request.TargetChatID)
request.PaymentIntentID = strings.TrimSpace(request.PaymentIntentID)
request.QuoteRef = strings.TrimSpace(request.QuoteRef)
request.SourceService = strings.TrimSpace(request.SourceService)
request.Rail = strings.TrimSpace(request.Rail)
request.AcceptedUserIDs = normalizeStringList(request.AcceptedUserIDs)
if request.RequestedMoney != nil {
request.RequestedMoney.Amount = strings.TrimSpace(request.RequestedMoney.Amount)
request.RequestedMoney.Currency = strings.TrimSpace(request.RequestedMoney.Currency)
}
return request
}
func normalizeStringList(values []string) []string {
if len(values) == 0 {
return nil
}
result := make([]string, 0, len(values))
seen := map[string]struct{}{}
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
if len(result) == 0 {
return nil
}
return result
}
func confirmationPrompt(req *model.ConfirmationRequest) string {
var builder strings.Builder
builder.WriteString("Payment confirmation required\n")
if req.QuoteRef != "" {
builder.WriteString("Quote ref: ")
builder.WriteString(req.QuoteRef)
builder.WriteString("\n")
}
if req.RequestedMoney != nil {
amountFloat, err := strconv.ParseFloat(req.RequestedMoney.Amount, 64)
if err != nil {
amountFloat = 0
}
builder.WriteString(fmt.Sprintf("\n*Requested: %.2f %s*\n\n", amountFloat, req.RequestedMoney.Currency))
}
builder.WriteString("Reply with \"<amount> <currency>\" (e.g., 12.34 USD).")
return builder.String()
}