Monitoring of billing events and anomalies for SaaS products. Use when: billing observability, billing monitoring, billing event tracking, billing anomaly detection, billing alert, stripe webhook monitoring, payment failure alert, revenue metric, MRR monitoring, churn alert, failed payment rate, billing event log, invoice monitoring, subscription event metrics, billing error alert, billing event anomaly, dunning monitoring, payment success rate, billing pipeline health, subscription event tracking, billing event tracing, mrr drop alert, payment spike alert, stripe event monitoring.
Invoke this skill when you need to:
// Every billing event (inbound from Stripe or outbound state change) is logged at INFO level
// with structured fields for queryability
func (h *StripeWebhookHandler) HandleSubscriptionUpdated(ctx context.Context, event stripe.Event) error {
var sub stripe.Subscription
if err := json.Unmarshal(event.Data.Raw, &sub); err != nil {
return fmt.Errorf("%w: parse subscription", ErrUnrecoverable)
}
tenantID := sub.Metadata["tenant_id"]
ctx = logctx.WithTenantID(ctx, tenantID)
slog.InfoContext(ctx, "billing event received",
"event_id", event.ID,
"event_type", string(event.Type),
"tenant_id", tenantID,
"subscription_id", sub.ID,
"status", string(sub.Status),
"plan_id", sub.Items.Data[0].Price.ID,
"current_period_end", sub.CurrentPeriodEnd,
)
// Process the event...
slog.InfoContext(ctx, "billing event processed",
"event_id", event.ID,
"event_type", string(event.Type),
"tenant_id", tenantID,
"duration_ms", time.Since(start).Milliseconds(),
)
return nil
}
package billing_metrics
import "github.com/prometheus/client_golang/prometheus"
var (
// Operational health: webhook processing rate
WebhookEventsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "saas_billing_webhook_events_total",
Help: "Total Stripe webhook events received, by type and outcome.",
},
[]string{"event_type", "outcome"}, // outcome: "processed" | "duplicate" | "failed"
)
// Payment outcomes
PaymentOutcomesTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "saas_billing_payment_outcomes_total",
Help: "Payment success and failure counts.",
},
[]string{"outcome", "failure_code"}, // outcome: "succeeded" | "failed"
)
// Subscription state transitions
SubscriptionTransitionsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "saas_billing_subscription_transitions_total",
Help: "Total subscription state transitions.",
},
[]string{"from_status", "to_status"},
)
// Active subscription gauge by plan
ActiveSubscriptionsByPlan = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "saas_billing_active_subscriptions",
Help: "Current count of active subscriptions per plan.",
},
[]string{"plan_id", "plan_name"},
)
// Webhook processing latency
WebhookProcessingDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "saas_billing_webhook_duration_seconds",
Help: "Time to process a Stripe webhook event.",
Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5},
},
[]string{"event_type"},
)
)
func init() {
prometheus.MustRegister(
WebhookEventsTotal,
PaymentOutcomesTotal,
SubscriptionTransitionsTotal,
ActiveSubscriptionsByPlan,
WebhookProcessingDuration,
)
}
func (h *BillingMetricsMiddleware) Wrap(handler BillingWebhookHandler) BillingWebhookHandler {
return BillingWebhookHandlerFunc(func(ctx context.Context, event stripe.Event) error {
start := time.Now()
err := handler.Handle(ctx, event)
duration := time.Since(start).Seconds()
outcome := "processed"
if errors.Is(err, ErrDuplicateEvent) {
outcome = "duplicate"
} else if err != nil {
outcome = "failed"
}
billing_metrics.WebhookEventsTotal.WithLabelValues(string(event.Type), outcome).Inc()
billing_metrics.WebhookProcessingDuration.WithLabelValues(string(event.Type)).Observe(duration)
return err
})
}
func (h *PaymentHandler) HandleInvoicePaymentSucceeded(ctx context.Context, event stripe.Event) error {
billing_metrics.PaymentOutcomesTotal.WithLabelValues("succeeded", "").Inc()
// ...
return nil
}
func (h *PaymentHandler) HandleInvoicePaymentFailed(ctx context.Context, event stripe.Event) error {
var invoice stripe.Invoice
json.Unmarshal(event.Data.Raw, &invoice)
failureCode := ""
if invoice.PaymentIntent != nil && invoice.PaymentIntent.LastPaymentError != nil {
failureCode = string(invoice.PaymentIntent.LastPaymentError.Code)
}
billing_metrics.PaymentOutcomesTotal.WithLabelValues("failed", failureCode).Inc()
// ...
return nil
}
# Prometheus alerting rules — billing pipeline health