Skip to main content
Version: 0.8

Webhooks

WebhookProvider.VerifyWebhook verifies signatures and parses events into a unified WebhookEvent. All three providers implement it — pass the raw request body and the relevant signature header(s):

event, err := client.VerifyWebhook(ctx, payload, map[string]string{
"Stripe-Signature": signatureHeader, // for Stripe
// "X-Razorpay-Signature": sig, // for Razorpay
// "PAYPAL-TRANSMISSION-SIG": sig, // for PayPal (+ other PAYPAL-* headers)
})
if err != nil {
// signature verification failed
}

fmt.Println(event.Type) // e.g. "payment_intent.succeeded"
fmt.Println(event.Provider) // e.g. "stripe"

Configure each provider's webhook secret at construction time — stripe.DefaultConfig().WithWebhookSecret(...), razorpay.DefaultConfig().WithWebhookSecret(...), paypal.DefaultConfig().WithWebhookID(...).

Acting on normalized events

Beyond the raw Type/Raw, every event carries provider-normalized fields so you can act without parsing the raw payload — switch on event.Kind:

switch event.Kind {
case payment.WebhookPaymentSucceeded:
// event.PaymentID and event.OrderID are populated; event.Amount may be nil
if event.Amount != nil {
markPaid(event.PaymentID, event.Amount)
}
case payment.WebhookPaymentFailed:
markFailed(event.PaymentID)
case payment.WebhookRefundSucceeded:
recordRefund(event.RefundID, event.PaymentID, event.Amount)
case payment.WebhookSetupSucceeded:
// event.SetupIntentID is populated; the payment method is ready for reuse
activateSavedCard(event.SetupIntentID)
case payment.WebhookSetupFailed:
notifySetupFailed(event.SetupIntentID)
case payment.WebhookInvoicePaymentSucceeded:
// recurring charge paid; event.SubscriptionID/InvoiceID and Amount are set
extendSubscription(event.SubscriptionID, event.Amount)
case payment.WebhookInvoicePaymentFailed:
flagPastDue(event.SubscriptionID)
case payment.WebhookSubscriptionCanceled:
revokeAccess(event.SubscriptionID)
case payment.WebhookUnknown:
// not a normalized event; fall back to event.Type / event.Raw
}

WebhookEventKind values

WebhookPaymentCreated, WebhookPaymentSucceeded, WebhookPaymentFailed, WebhookPaymentCanceled, WebhookRefundSucceeded, WebhookRefundFailed, WebhookSetupSucceeded, WebhookSetupFailed, WebhookSubscriptionCreated, WebhookSubscriptionUpdated, WebhookSubscriptionCanceled, WebhookInvoicePaymentSucceeded, WebhookInvoicePaymentFailed, and WebhookUnknown for anything that doesn't map to a normalized category.

WebhookEvent fields

ID, Type (raw provider event type), Provider, Raw (raw payload bytes), Kind, and the normalized identifiers that apply to the event — PaymentID, OrderID, RefundID, SetupIntentID, SubscriptionID, InvoiceID — each empty when it doesn't apply. Amount is the normalized amount in integer minor units (nil when the event carries no amount). PayPal's major-unit decimal strings (e.g. "10.00") are converted to minor units automatically via ParseMajorUnitAmount.

Debugging: ParseWebhook

PayPal and Razorpay each export a ParseWebhook that parses an event without verifying its signature. It exists for local debugging only — never call it in production, since it accepts an unsigned, unauthenticated payload.

Stripe's ParseWebhook is different: despite the shared name, it still verifies the signature — it takes signature and webhookSecret as explicit parameters instead of reading them from Config and a headers map the way VerifyWebhook does. It's a convenience for callers who already have the raw Stripe-Signature header value in hand, not an unverified debugging path.