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 \" \" (e.g., 12.34 USD).") return builder.String() }