PhoenixKitBilling.Web.WebhookController (PhoenixKitBilling v0.3.1)

Copy Markdown View Source

Handles webhooks from payment providers (Stripe, PayPal, Razorpay, EveryPay).

This controller receives webhook events from payment providers, verifies their signatures, and processes them through the WebhookProcessor.

Webhook URLs

Configure these URLs in your payment provider dashboards:

  • Stripe: https://yourdomain.com/phoenix_kit/webhooks/billing/stripe
  • PayPal: https://yourdomain.com/phoenix_kit/webhooks/billing/paypal
  • Razorpay: https://yourdomain.com/phoenix_kit/webhooks/billing/razorpay
  • EveryPay: https://yourdomain.com/phoenix_kit/webhooks/billing/everypay

Security

Stripe, PayPal and Razorpay webhooks verify signatures to ensure they come from legitimate sources; invalid signatures result in 401 Unauthorized responses.

EveryPay v4 callbacks are not signed. The EveryPay handler instead re-fetches the authoritative payment record from the EveryPay API and acts only on that, so a forged callback body cannot change billing state.

Idempotency

Events are logged in the phoenix_kit_webhook_events table with their event IDs. Duplicate events are detected and ignored to prevent double-processing.

Summary

Functions

Handles EveryPay callbacks.

Handles PayPal webhooks.

Handles Razorpay webhooks.

Handles Stripe webhooks.

Functions

everypay(conn, params)

Handles EveryPay callbacks.

EveryPay v4 callbacks are not signed. Rather than trust the callback body, this reads the payment_reference, re-fetches the authoritative payment record from the EveryPay API, normalizes it, and processes that.

Always returns 200 for well-formed callbacks (including duplicates and non-final payment states) so EveryPay does not keep retrying.

paypal(conn, params)

Handles PayPal webhooks.

PayPal verification requires multiple headers for signature verification.

razorpay(conn, params)

Handles Razorpay webhooks.

Signature is read from the x-razorpay-signature header.

stripe(conn, params)

Handles Stripe webhooks.

Expects the raw body in conn.assigns.raw_body (set by a custom Plug). Signature is read from the stripe-signature header.