What Should a Payment Alert Include?
A great payment alert answers who paid, how much you actually keep, what it means for MRR, and what to do next. Here's the field-by-field anatomy — and why raw Stripe webhooks can't give you it.
A great payment alert answers who paid, how much you actually keep, what it means for MRR, and what to do next. Here's the field-by-field anatomy — and why raw Stripe webhooks can't give you it.
What a payment alert should include, at minimum, is five things: how much came in and how much you actually keep after fees, who paid (name and email, not an ID), what happened (the event type), what it means for MRR, and what to do next (a deep link plus an urgency cue). Miss any of those and you have a notification you still have to investigate. The catch is that a raw Stripe webhook physically can't give you most of them — the net amount and the customer's name live somewhere else.
Short answer
A great payment alert reads like a sentence a human wrote: $249.00 ($241.02 net after $7.98 fee) from Acme Corp — Pro plan, MRR +$249 — view in Stripe. A bad one is a wall of JSON with an amount in cents and a cus_… ID. The difference isn't formatting; it's real enrichment — fee math, ID-to-identity resolution, and MRR classification.
Here is the same event two ways. First, a raw Stripe webhook forwarded straight to Slack. An engineer can decode it; a founder or ops teammate sees an integer in cents, a customer ID with no name, and no idea what the payment is worth after fees:
🧩 payment_intent.succeeded
{ "amount": 24900, "currency": "usd", "status": "succeeded",
"customer": "cus_Qx9aB2c" }
Now the same payment as an alert a person can act on without opening a tab. Plain English, gross and net, a real customer, the plan, the MRR delta, and a link straight to the record:
🔔 New payment — $249.00 from Acme Corp
Net after fees: $241.02 ($7.98 Stripe fee)
billing@acme.com · Pro plan, monthly · MRR +$249
View in Stripe →
The second one is not just prettier. To produce it you have to do work the raw payload doesn't do for you: compute the net, turn a customer ID into a name and email, classify the subscription movement, and attach a deep link. That's the whole anatomy of a good alert — so let's take it field by field.
Every actionable payment alert answers five questions in order: how much, who, what, so what, and what next. Map each to a concrete field and you have a template that works for a sale, a failed charge, a refund, or a churn.
Gross alone is a vanity number. A raw charge.succeeded or payment_intent.succeeded event carries the gross amount, currency, and status — but not the processing fee or the net that actually lands in your balance. Stripe exposes those on a separate balance_transaction object, which you have to fetch or expand to read the fee and the net. There is no webhook for the balance transaction itself.
Why it matters: US card pricing is 2.9% + $0.30 per successful charge, so a $100 sale nets about $96.80 — a raw alert saying "$100.00" overstates real revenue by roughly 3.2%. And you can't just hardcode 2.9%: international cards add 1.5%, currency conversion adds another 1%, and ACH is 0.8% capped at $5. The fee varies per payment, so a correct alert reads the actual fee off the balance transaction rather than guessing.
Show both numbers
Always display gross and net ("$249.00 · $241.02 net after $7.98 fee"). Gross tells you the sale size; net tells you what you can spend. Founders reconcile against net, so that's the number that belongs in the alert.
The raw event gives you a bare customer ID like cus_Qx9aB2c — no name, no email. A cus_… string tells you nothing you can act on; you can't recognize a key account, spot a churned whale, or reply to support without resolving it first. A good alert has already done that lookup and shows the customer's name and email inline. This is basic to any Stripe payment notification in Slack worth reading.
"Something happened in Stripe" is not an alert. The reader needs to know instantly whether this is a new sale, a failed charge, a refund, a dispute, or a cancellation — because each one routes to a different person and a different response. Lead with a labeled event type and an emoji marker so the category is legible at a glance, before anyone reads a single number.
For anything subscription-related, the single most useful field is the MRR delta. MRR moves four ways — new, expansion, contraction, and churn — and "subscription updated" tells you none of them. Worse, cancellations in Stripe usually surface as a generic customer.subscription.updated event (triggered when someone cancels in the customer portal), not a dedicated "churn" event, so a useful alert has to interpret the event to classify the movement.
A good subscription alert states the movement category and the delta: "Upgrade · MRR +$40" or "Canceled · MRR −$249." In practice, movement alerts include the customer, prior MRR, current MRR, the delta, and the category (new / expansion / contraction / churn). That's the difference between a number that scrolls past and one that makes someone open Slack. If subscriptions are your lifeblood, see how to monitor Stripe subscriptions in Slack.
An actionable notification lets the reader act from the message. That means a deep link to the exact Stripe record — the specific payment or customer, not the Dashboard home page — so investigating is one click, not a search. Pair it with an urgency cue: match the interruption to the stakes. A routine sale can be quiet; a failed payment or a dispute should look and route differently, because those need a human now. A failed charge that recovers on its own doesn't warrant the same volume as one that's about to churn a key account.
The best notification-UX guidance is simple: the higher the risk, the stronger the interruption. A quiet acknowledgment for routine events; a loud, prominent marker for high-value or destructive ones. In alerting terms, prioritize events that combine business impact with a defined response path, route by severity to the right people, and remember that not every alert needs to wake someone at 3 a.m. Low-urgency events belong in a digest, not a ping.
So a payment alert should also carry cues that answer "how much does this matter, and who owns it?" In practice that's three things: a severity marker (color or emoji), a routing target (which channel or person), and — for failures — a reason and next step. Here's a high-urgency example, deliberately built to look different from a routine sale:
⚠️ Payment failed — $1,200.00 from Globex Inc
Reason: insufficient_funds · Enterprise plan
MRR at risk: −$1,200 · Stripe retries in 3 days
ap@globex.com · Open customer →
This one earns a louder marker, a decline reason you can act on, an at-risk MRR figure, the retry timeline, and a link to the customer — and it's routed to founders, not the general wins channel. That's the anatomy of a failed-payment alert done right; the full playbook is in how to send failed payment alerts to Slack, and deciding who gets what is covered in which Stripe alerts go to finance, support, and founders.
Volume is a field too
One Slack ping per webhook is how a channel becomes noise nobody reads. Grouping related events and batching low-urgency ones into a digest is a core noise-reduction technique. An alert system's design includes deciding what *doesn't* fire in real time — see how to avoid noisy Stripe notifications.
It's worth being precise about the gap, because it explains why "just forward the webhook" never produces a good alert. A raw Stripe Event wraps everything in envelope fields (id, object, api_version, created, type) and nests the real data under data.object. Amounts are integers in the smallest currency unit — amount: 2000 means $20.00 — so unformatted JSON is unreadable to a non-engineer and easy to misread.
More fundamentally, three of the five fields a good alert needs are not in the payload at all:
balance_transaction object you must expand or fetch — the charge webhook doesn't carry the fee or the net.cus_… ID and have to resolve it against the customer record.subscription.updated.Stripe's own default Slack app and Stripe Workflows for Slack let you pick events, choose a channel, and optionally include a Dashboard link — which is genuinely useful and costs nothing. But users describe the out-of-the-box output as "mostly a direct transformation of data": it doesn't compute net-after-fees, doesn't resolve a bare customer ID into a name, doesn't classify MRR movement, and sends one ping per event with no grouping. Raw forwarders (Zapier, direct webhooks, Hookdeck) can pass any field, but they leave every bit of that enrichment to you. If you're weighing building it yourself, ChargeBell vs custom Stripe webhook code walks through exactly what that enrichment costs to maintain.
Use this as a spec whether you're building alerts yourself or evaluating a tool. A payment alert should include:
ChargeBell is a purpose-built Stripe-to-Slack alert product, and its alerts are the anatomy above, assembled for you. Each message is plain English with the numbers already computed: the net amount after Stripe fees, the customer name and email when Stripe provides them, and the MRR impact for subscription changes — not raw webhook JSON.
The urgency and routing cues are built in too. Payment failed and dispute created are treated as critical — they bypass quiet hours and can add an @channel prefix — while routine wins can go to a #wins channel and problems to #founders via per-alert routing. Quiet hours hold low-priority alerts and combine them into one "while you were away" message, and daily and weekly digests batch the small stuff so your channel stays signal, not noise. It connects through official read-only Stripe OAuth, so it can see payment events to build these alerts but can never move money or change anything.
Net
After-fees amount computed on every payment alert
Name + email
Customer identity resolved, not a raw ID
MRR Δ
Movement classified for subscription changes
Key takeaways
Connect Stripe and Slack, pick a channel, and send a test alert. Net after fees, real customer identity, MRR impact, and deep links, out of the box. Free plan, no card needed.
At minimum: the amount and net after fees, the customer's name and email, the event type in plain English, the MRR impact for subscription changes, and a deep link to the exact Stripe record. Add an urgency cue so failures and disputes stand out from routine sales.
A charge or payment_intent webhook only carries the gross amount, currency, and status. Stripe puts the fee and net on a separate balance_transaction object, which you have to expand or fetch — there's no webhook for the balance transaction itself, so a raw forward can't show net.
No. That's the US card rate, but international cards add 1.5%, currency conversion adds another 1%, and ACH is 0.8% capped at $5. The fee varies per payment, so a correct alert reads the actual fee from the balance transaction rather than hardcoding a percentage.
The raw event gives you a bare ID like cus_Qx9aB2c with no name or email. You can't recognize a key account, spot a churn risk, or reply to support from an ID. A good alert resolves the ID into a name and email so the reader can act without a lookup.
Match interruption to stakes: the higher the risk, the stronger the alert. A routine sale can be quiet, but a failed payment or dispute should carry a louder marker, a reason and next step, and route to the right people. Low-urgency events belong in a digest, not a real-time ping.
Yes. ChargeBell's alerts are plain English with the numbers already worked out — net after Stripe fees, the customer's name and email when Stripe provides them, and the MRR delta for subscription changes. It connects through read-only Stripe OAuth, so it can build these alerts but never touches money.
There are three free ways to pipe Stripe payments into Slack, and each stops at a bare 'you got paid' message. Here's how each works — and the fast path to alerts that actually show net, customer, and MRR.
July 6, 2026
A payments channel that pings for every $9 charge gets muted — and then nobody sees the $312 dispute. Here's a five-lever framework to keep Stripe alerts signal, not noise.
July 6, 2026
A DIY webhook handler is flexible and feels free — until you own signature verification, dedup, retries, formatting, hosting, and on-call. Here's the honest build-vs-buy math.
July 6, 2026