Webhooks
Stashly uses Stripe webhooks for billing events. Configure your webhook endpoint in the Stripe dashboard and verify incoming events using the Stripe signature header.
Stripe-powered billing webhooks
Stashly billing is powered by Stripe. Webhook payloads come directly from Stripe and are forwarded to your registered endpoint.
Stripe Webhook Endpoint#
/webhooks/stripeReceive Stripe events. Stashly verifies the Stripe-Signature header before processing.
Raw body required
Stripe signature verification requires the raw (un-parsed) request body. Do not use JSON body-parser middleware on this route.
Verified Events#
Stashly processes the following Stripe events:
| Event | Description |
|---|---|
checkout.session.completed | A new subscription checkout completed |
customer.subscription.updated | Subscription plan changed |
customer.subscription.deleted | Subscription cancelled or expired |
invoice.payment_succeeded | Payment processed successfully |
invoice.payment_failed | Payment failed — tenant enters grace period |
Verifying Webhook Signatures#
All incoming webhooks include a Stripe-Signature header. Verify it using the Stripe SDK:
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
app.post("/webhooks/stripe", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["stripe-signature"];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case "invoice.payment_succeeded":
// handle success
break;
case "customer.subscription.deleted":
// handle cancellation
break;
}
res.json({ received: true });
});
Webhook Payload Format#
Stripe event payloads follow the standard Stripe event structure:
{
"id": "evt_01j9xk2m",
"type": "invoice.payment_succeeded",
"created": 1748476800,
"data": {
"object": {
"id": "in_01j9xk2m",
"customer": "cus_abc123",
"amount_paid": 2900,
"currency": "usd",
"status": "paid"
}
}
}
Refer to the Stripe documentation for the full event object schemas.