How a Stripe event becomes one message
Between Stripe and your channel there are five decisions. Each one is written in the journal, so you can always tell why a message was, or was not, sent.
Stripe Paybell your channel | | | | event + signature -----> | | | | 1. signature checked | | <--- 400, not from Stripe | (wrong? nothing is sent) | | | 2. already handled? stop | | | 3. your rules: kind, amount | | | 4. same payment as an event | | | already announced? stop | | | 5. message --------------> | | | <------------- 200 -------- | | <--- 200, handled ------- | | | | channel down? Stripe is | | <--- 503, send it again - | asked to send it again | | | journal: what came in, | | | what was done with it, why |
1. Is it really Stripe?
Stripe signs every event: it computes an HMAC-SHA256 of the timestamp and the exact body with the signing secret of your endpoint, and sends the result in the Stripe-Signature header. Paybell recomputes it. A different result, a missing header or a timestamp older than five minutes means the request is refused with a 400 and your channel hears nothing. To understand a failure in your own code, see the signature checker. Until you have pasted the signing secret, every event is refused for that reason, and Stripe sends it again later.
2. Has it been handled already?
Stripe delivers "at least once": the same event can arrive twice. Each event id is remembered per connection, and a repeat is answered with a 200 without a second message.
3. Do you want to hear about it?
Your rules decide: the kinds of events that are switched on, a minimum amount for payment messages, test mode on or off. A payment under your minimum stays silent but still counts in the daily summary.
4. Is it the same payment again?
One card payment through Checkout produces checkout.session.completed, payment_intent.succeeded and charge.succeeded, plus invoice.paid for a subscription. They share a payment intent or an invoice. The first one to arrive is announced; the others are marked "merged" in the journal.
5. Did the channel take it?
If the channel answers with an error that may pass (too many requests, a server error, no answer in time), Stripe is answered 503 and will send the event again, for up to three days. If the channel says the webhook no longer exists, retrying would not help: the event is marked "refused" and the dashboard tells you to check the address.
What runs underneath
Paybell is a small app on Qu4zr: one HTTP endpoint that verifies and forwards, a database for the journal, a vault for the key that encrypts your secrets, and a clock for the daily summary. If you need the same thing between two other systems, or with your own rules, that is what Qu4zr is for.