rentease/internal/server/handle_stripe_webhook.go
2025-11-02 21:45:37 +01:00

74 lines
1.9 KiB
Go

package server
import (
"context"
"encoding/json"
"io"
"net/http"
"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
}
var constructEvent = webhook.ConstructEvent
func handleStripeWebhook(bs stripeEventService, secret string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if secret == "" {
http.Error(w, "stripe webhook secret not configured", http.StatusServiceUnavailable)
return
}
payload, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "unable to read request body", http.StatusBadRequest)
return
}
sig := r.Header.Get("Stripe-Signature")
if sig == "" {
http.Error(w, "missing Stripe-Signature header", http.StatusBadRequest)
return
}
event, err := constructEvent(payload, sig, secret)
if err != nil {
http.Error(w, "invalid webhook signature", http.StatusBadRequest)
return
}
switch event.Type {
case stripe.EventTypePaymentIntentSucceeded:
var pi stripe.PaymentIntent
if err := json.Unmarshal(event.Data.Raw, &pi); err != nil {
http.Error(w, "invalid payment intent payload", http.StatusBadRequest)
return
}
if err := bs.HandlePaymentIntentSucceeded(r.Context(), &pi); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
case stripe.EventTypeChargeRefunded:
var ch stripe.Charge
if err := json.Unmarshal(event.Data.Raw, &ch); err != nil {
http.Error(w, "invalid charge payload", http.StatusBadRequest)
return
}
if err := bs.HandleChargeRefunded(r.Context(), &ch); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
default:
// Acknowledge events we don't actively process.
}
w.WriteHeader(http.StatusOK)
}
}