rentease/internal/server/handle_stripe_webhook.go

65 lines
1.9 KiB
Go

package server
import (
"context"
"encoding/json"
"io"
"net/http"
"github.com/labstack/echo/v4"
"github.com/stripe/stripe-go/v83"
"github.com/stripe/stripe-go/v83/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)
}
}