Webhooks

A webhook is Stripe telling your site that something happened. Without one, your site only ever learns about payments the reader’s browser was present for.

That gap matters more than it sounds. Cancellations, failed renewals and successful renewals all happen at Stripe with nobody on your site. A site without a working webhook never records a renewal at all, and lags behind cancellations. Access does eventually catch up — the plugin refreshes a subscription from Stripe once its cached copy is an hour old — but a renewal that was never recorded stays unrecorded, and campaign totals never see it.

Setting it up

If WooCommerce Stripe is installed and configured, there is nothing to set up. It already receives and verifies Stripe’s events, and Monetization Kit listens to the ones it owns as WooCommerce Stripe hands them on. Do not add a second endpoint for this plugin: both plugins read the same signing-secret field, so a second endpoint’s secret would displace the one WooCommerce Stripe verifies with and stop its webhook working. Skip to Events to subscribe to and make sure WooCommerce Stripe’s own endpoint carries them.

Otherwise the plugin owns the endpoint, and your webhook URL is your site address followed by its REST route:

https://example.com/wp-json/fabrica-monetization-kit/v1/stripe-webhook

In your Stripe dashboard, go to Developers → Webhooks, add an endpoint with that URL, and subscribe it to the events below. Stripe then shows a signing secret starting whsec_ — paste it into the Webhook secret field on the Gateways tab, which also shows the URL and the exact event list.

The secret is what proves an incoming request really came from Stripe. Without it the plugin rejects everything, which is the correct thing to do but looks like nothing working.

Events to subscribe to

Subscribe to all of these. Each one does a job, and a missing one fails quietly.

On a WooCommerce Stripe site these all belong on WooCommerce Stripe’s endpoint, which it creates for itself when you connect your account. Monetization Kit acts on three of them as WooCommerce Stripe passes them on — invoice.payment_succeeded, charge.refunded and payment_intent.succeeded — and leaves the rest to WooCommerce. Check that endpoint carries all of the events below: the three the plugin needs are easy to miss, because nothing visibly breaks without them until a renewal or a refund goes unrecorded.

Subscription lifecycle

Event Why it matters
customer.subscription.created A new subscriber.
customer.subscription.updated Upgrades, downgrades, cancel-at-period-end.
customer.subscription.deleted The subscription has ended. Without this, access never expires.
customer.subscription.paused Access should stop.
customer.subscription.resumed Access should return.

Billing

Event Why it matters
invoice.payment_succeeded Records each renewal. Recurring donations depend on this.
invoice.payment_failed An expired or declined card. Lets you prompt for new details.

One-off payments

Event Why it matters
payment_intent.succeeded Records a contribution whose donor closed the tab before returning to the site. Without it, Stripe takes the money and the site never hears about it. Abandoned article and author purchases are not recovered this way.

Refunds

Event Why it matters
charge.refunded Withdraws access on a full refund, and adjusts campaign totals. A partial refund adjusts the totals but leaves access in place.

Checking it works

Check the delivery itself, in Stripe’s webhook page, which lists every attempt with its response. 200 is good. 400 almost always means the signing secret does not match. Repeated 500s mean something is failing on the site — the response body is a generic message, so turn on WP_DEBUG and read the [FMK] Webhook error: line in your PHP error log for the real one.

Cancelling a test subscription and reloading as that reader is a weaker test than it looks. On a WooCommerce site the cancellation is WooCommerce’s to handle, so it proves nothing about this plugin. And where the plugin owns the subscription, the hourly refresh removes access eventually even with the webhook broken — so if you do test this way, check within the hour.

A better test on a WooCommerce Stripe site is a refund: refund a contribution in Stripe and watch the campaign total drop. That exercises the whole path — WooCommerce Stripe receiving and verifying the event, and this plugin acting on it.

Local development

Stripe cannot reach a site on your own machine. Use the Stripe CLI to forward events:

stripe listen --skip-verify --forward-to https://example.localhost/wp-json/fabrica-monetization-kit/v1/stripe-webhook

It prints its own signing secret when it starts. Put that wherever your secret lives — the Gateways tab, or WooCommerce Stripe — for as long as you are testing, and remember it is not the one your live site uses.

Handling events yourself

Every event this endpoint processes fires fabrica_monetization_kit_stripe_webhook, after the plugin has finished with it. The signature has already been checked, so the event can be trusted.

It does not fire for events the plugin skips — a duplicate delivery, or an event for a different Stripe account. On a WooCommerce Stripe site it fires for the three events the plugin acts on, as they arrive by way of WooCommerce Stripe.

For the common cases there are friendlier actions: fabrica_monetization_kit_subscription_created, _subscription_renewed, _subscription_payment_failed, _subscription_deleted and _refund_processed. Each hands you the user ID directly instead of a Stripe customer to look up. See the hooks reference.

If you change the REST namespace

fabrica_monetization_kit_rest_namespace changes the URL of every route, webhook included. Where the plugin owns the endpoint, update it in Stripe to match or payments stop being recorded. On a WooCommerce Stripe site there is no endpoint of ours to update.