package server import ( "context" "encoding/json" "io" "net/http" "github.com/labstack/echo/v4" stripe "github.com/stripe/stripe-go/v79" "github.com/stripe/stripe-go/v79/webhook" ) type stripeEventService interface { HandlePaymentIntentSucceeded(ctx context.Context, pi *stripe.PaymentIntent) error HandleChargeRefunded(ctx context.Context, ch *stripe.Charge) error } func handleStripeWebhook(bs stripeEventService, secret string) echo.HandlerFunc { return func(c echo.Context) error { if secret == "" { return echo.NewHTTPError(http.StatusServiceUnavailable, "stripe webhook secret not configured") } payload, err := io.ReadAll(c.Request().Body) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "unable to read request body") } sig := c.Request().Header.Get("Stripe-Signature") if sig == "" { return echo.NewHTTPError(http.StatusBadRequest, "missing Stripe-Signature header") } event, err := webhook.ConstructEvent(payload, sig, secret) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "invalid webhook signature") } switch event.Type { case stripe.EventTypePaymentIntentSucceeded: var pi stripe.PaymentIntent if err := json.Unmarshal(event.Data.Raw, &pi); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "invalid payment intent payload") } if err := bs.HandlePaymentIntentSucceeded(c.Request().Context(), &pi); err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } case stripe.EventTypeChargeRefunded: var ch stripe.Charge if err := json.Unmarshal(event.Data.Raw, &ch); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "invalid charge payload") } if err := bs.HandleChargeRefunded(c.Request().Context(), &ch); err != nil { return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) } default: // Acknowledge events we don't actively process. } return c.NoContent(http.StatusOK) } }