package usecase import ( "context" "fmt" "strings" "github.com/gofrs/uuid/v5" "github.com/saveinmed/backend-go/internal/domain" ) // CreatePaymentPreference builds a checkout preference (e.g. Mercado Pago) for an order. func (s *Service) CreatePaymentPreference(ctx context.Context, id uuid.UUID) (*domain.PaymentPreference, error) { order, err := s.repo.GetOrder(ctx, id) if err != nil { return nil, err } buyer, err := s.repo.GetUser(ctx, order.BuyerID) if err != nil { return nil, fmt.Errorf("failed to fetch buyer: %w", err) } sellerAcc, err := s.repo.GetSellerPaymentAccount(ctx, order.SellerID) if err != nil { sellerAcc = nil // Proceed without split if not configured } return s.pay.CreatePreference(ctx, order, buyer, sellerAcc) } // ProcessOrderPayment processes a direct card payment for an order. // It resolves the buyer (user or B2B company) and delegates to the payment gateway. func (s *Service) ProcessOrderPayment(ctx context.Context, id uuid.UUID, token, issuerID, paymentMethodID string, installments int, payerEmail, payerDoc string) (*domain.PaymentResult, error) { order, err := s.repo.GetOrder(ctx, id) if err != nil { return nil, err } var buyer *domain.User user, err := s.repo.GetUser(ctx, order.BuyerID) if err == nil { buyer = user } else { // B2B fallback: resolve company as payer company, errComp := s.repo.GetCompany(ctx, order.BuyerID) if errComp != nil { return nil, fmt.Errorf("failed to fetch buyer (user or company): %v / %v", err, errComp) } email := payerEmail if email == "" { email = "financeiro@saveinmed.com" } buyer = &domain.User{ ID: company.ID, Name: company.CorporateName, Email: email, CPF: payerDoc, } if buyer.CPF == "" { buyer.CPF = company.CNPJ } } sellerAcc, err := s.repo.GetSellerPaymentAccount(ctx, order.SellerID) if err != nil { sellerAcc = nil } res, err := s.pay.CreatePayment(ctx, order, token, issuerID, paymentMethodID, installments, buyer, sellerAcc) if err != nil { return nil, err } if res.Status == "approved" { if err := s.UpdateOrderStatus(ctx, order.ID, domain.OrderStatusPaid); err != nil { // Payment succeeded; status update failure should be reconciled async. fmt.Printf("WARN: payment approved but status update failed: %v\n", err) } } return res, nil } // HandlePaymentWebhook processes payment gateway callbacks, updates the order status // and records the split in the financial ledger. func (s *Service) HandlePaymentWebhook(ctx context.Context, event domain.PaymentWebhookEvent) (*domain.PaymentSplitResult, error) { order, err := s.repo.GetOrder(ctx, event.OrderID) if err != nil { return nil, err } expectedMarketplaceFee := int64(float64(order.TotalCents) * (s.marketplaceCommission / 100)) marketplaceFee := event.MarketplaceFee if marketplaceFee == 0 { marketplaceFee = expectedMarketplaceFee } sellerReceivable := order.TotalCents - marketplaceFee if event.SellerAmount > 0 { sellerReceivable = event.SellerAmount } if strings.EqualFold(event.Status, "approved") || strings.EqualFold(event.Status, "paid") { if err := s.repo.UpdateOrderStatus(ctx, order.ID, domain.OrderStatusPaid); err != nil { return nil, err } _ = s.repo.RecordLedgerEntry(ctx, &domain.LedgerEntry{ ID: uuid.Must(uuid.NewV7()), CompanyID: order.SellerID, AmountCents: order.TotalCents, Type: "SALE", Description: "Order #" + order.ID.String(), ReferenceID: &order.ID, }) _ = s.repo.RecordLedgerEntry(ctx, &domain.LedgerEntry{ ID: uuid.Must(uuid.NewV7()), CompanyID: order.SellerID, AmountCents: -marketplaceFee, Type: "FEE", Description: "Marketplace Fee #" + order.ID.String(), ReferenceID: &order.ID, }) } return &domain.PaymentSplitResult{ OrderID: order.ID, PaymentID: event.PaymentID, Status: event.Status, MarketplaceFee: marketplaceFee, SellerReceivable: sellerReceivable, TotalPaidAmount: event.TotalPaidAmount, }, nil }