package handler import ( "net/http" "strings" "github.com/gofrs/uuid/v5" "github.com/saveinmed/backend-go/internal/domain" ) // CreatePaymentPreference godoc // @Summary Cria preferência de pagamento Mercado Pago com split nativo // @Tags Pagamentos // @Security BearerAuth // @Produce json // @Param id path string true "Order ID" // @Success 201 {object} domain.PaymentPreference // @Router /api/v1/orders/{id}/payment [post] func (h *Handler) CreatePaymentPreference(w http.ResponseWriter, r *http.Request) { if !strings.HasSuffix(r.URL.Path, "/payment") { http.NotFound(w, r) return } id, err := parseUUIDFromPath(r.URL.Path) if err != nil { writeError(w, http.StatusBadRequest, err) return } pref, err := h.svc.CreatePaymentPreference(r.Context(), id) if err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusCreated, pref) } // CreateShipment godoc // @Summary Gera guia de postagem/transporte // @Tags Logistica // @Security BearerAuth // @Accept json // @Produce json // @Param shipment body createShipmentRequest true "Dados de envio" // @Success 201 {object} domain.Shipment // @Router /api/v1/shipments [post] func (h *Handler) CreateShipment(w http.ResponseWriter, r *http.Request) { var req createShipmentRequest if err := decodeJSON(r.Context(), r, &req); err != nil { writeError(w, http.StatusBadRequest, err) return } shipment := &domain.Shipment{ OrderID: req.OrderID, Carrier: req.Carrier, TrackingCode: req.TrackingCode, ExternalTracking: req.ExternalTracking, } if err := h.svc.CreateShipment(r.Context(), shipment); err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusCreated, shipment) } // GetShipmentByOrderID godoc // @Summary Rastreia entrega // @Tags Logistica // @Security BearerAuth // @Produce json // @Param order_id path string true "Order ID" // @Success 200 {object} domain.Shipment // @Router /api/v1/shipments/{order_id} [get] func (h *Handler) GetShipmentByOrderID(w http.ResponseWriter, r *http.Request) { orderID, err := parseUUIDFromPath(r.URL.Path) if err != nil { writeError(w, http.StatusBadRequest, err) return } shipment, err := h.svc.GetShipmentByOrderID(r.Context(), orderID) if err != nil { writeError(w, http.StatusNotFound, err) return } writeJSON(w, http.StatusOK, shipment) } // HandlePaymentWebhook godoc // @Summary Recebe notificações do Mercado Pago // @Tags Pagamentos // @Accept json // @Produce json // @Param notification body domain.PaymentWebhookEvent true "Evento do gateway" // @Success 200 {object} domain.PaymentSplitResult // @Router /api/v1/payments/webhook [post] func (h *Handler) HandlePaymentWebhook(w http.ResponseWriter, r *http.Request) { var event domain.PaymentWebhookEvent if err := decodeJSON(r.Context(), r, &event); err != nil { writeError(w, http.StatusBadRequest, err) return } summary, err := h.svc.HandlePaymentWebhook(r.Context(), event) if err != nil { writeError(w, http.StatusInternalServerError, err) return } writeJSON(w, http.StatusOK, summary) } // HandleStripeWebhook godoc // @Summary Recebe notificações do Stripe // @Tags Pagamentos // @Accept json // @Produce json // @Success 200 {object} map[string]string // @Router /api/v1/payments/webhook/stripe [post] func (h *Handler) HandleStripeWebhook(w http.ResponseWriter, r *http.Request) { // In production: Verify Stripe-Signature header using the raw body // signature := r.Header.Get("Stripe-Signature") var payload struct { Type string `json:"type"` Data struct { Object struct { ID string `json:"id"` Metadata struct { OrderID string `json:"order_id"` } `json:"metadata"` Status string `json:"status"` AmountReceived int64 `json:"amount_received"` ApplicationFee int64 `json:"application_fee_amount"` TransferData struct { Amount int64 `json:"amount"` // Seller amount } `json:"transfer_data"` } `json:"object"` } `json:"data"` } if err := decodeJSON(r.Context(), r, &payload); err != nil { writeError(w, http.StatusBadRequest, err) return } // Map Stripe event to generic domain event if payload.Type == "payment_intent.succeeded" { orderID, _ := uuid.FromString(payload.Data.Object.Metadata.OrderID) event := domain.PaymentWebhookEvent{ PaymentID: payload.Data.Object.ID, OrderID: orderID, Status: "approved", // Stripe succeeded = approved TotalPaidAmount: payload.Data.Object.AmountReceived, MarketplaceFee: payload.Data.Object.ApplicationFee, SellerAmount: payload.Data.Object.TransferData.Amount, } if _, err := h.svc.HandlePaymentWebhook(r.Context(), event); err != nil { writeError(w, http.StatusInternalServerError, err) return } } writeJSON(w, http.StatusOK, map[string]string{"received": "true"}) } // HandleAsaasWebhook godoc // @Summary Recebe notificações do Asaas // @Tags Pagamentos // @Accept json // @Produce json // @Success 200 {object} map[string]string // @Router /api/v1/payments/webhook/asaas [post] func (h *Handler) HandleAsaasWebhook(w http.ResponseWriter, r *http.Request) { // In production: Verify asaas-access-token header // token := r.Header.Get("asaas-access-token") var payload struct { Event string `json:"event"` Payment struct { ID string `json:"id"` ExternalRef string `json:"externalReference"` // We store OrderID here Status string `json:"status"` NetValue int64 `json:"netValue"` // value after fees? Need to check docs. Value float64 `json:"value"` // Asaas sends float Split []struct { WalletID string `json:"walletId"` FixedValue float64 `json:"fixedValue"` } `json:"split"` } `json:"payment"` } if err := decodeJSON(r.Context(), r, &payload); err != nil { writeError(w, http.StatusBadRequest, err) return } if payload.Event == "PAYMENT_RECEIVED" || payload.Event == "PAYMENT_CONFIRMED" { orderID, _ := uuid.FromString(payload.Payment.ExternalRef) // Convert Asaas float value to cents totalCents := int64(payload.Payment.Value * 100) // Find split part for marketplace/seller // This logic depends on how we set up the split. // For now, simpler mapping: event := domain.PaymentWebhookEvent{ PaymentID: payload.Payment.ID, OrderID: orderID, Status: "approved", TotalPaidAmount: totalCents, // MarketplaceFee and SellerAmount might need calculation or extraction from split array } if _, err := h.svc.HandlePaymentWebhook(r.Context(), event); err != nil { writeError(w, http.StatusInternalServerError, err) return } } writeJSON(w, http.StatusOK, map[string]string{"received": "true"}) }