How to Set Up Stripe Webhooks for Your SaaS Without Breaking Payments | Deployxa

Stripe webhooks are critical for SaaS payments, but they are easy to get wrong. Here is the founder's guide to reliable, secure, and tested webhook handling.

← Back to Dispatch Articles
Engineering Log

How to Set Up Stripe Webhooks for Your SaaS Without Breaking Payments

Stripe webhooks are critical for SaaS payments, but they are easy to get wrong. Here is the founder's guide to reliable, secure, and tested webhook handling.

How to Set Up Stripe Webhooks for Your SaaS Without Breaking Payments

Stripe webhooks are how your SaaS knows when a payment succeeds, a subscription is cancelled, or a refund is issued. They are critical for payment processing, but they are also easy to get wrong: without signature verification, an attacker can spoof webhooks; without idempotency, duplicate webhooks cause double charges; without proper error handling, failed webhooks cause missed payments. This article is the founder's guide to reliable, secure, and tested Stripe webhook handling.

The direct answer is that Stripe webhooks require four things: signature verification (confirm the webhook is from Stripe), idempotency (handle duplicate webhooks without double-processing), fast responses (respond within 30 seconds to avoid Stripe retries), and error handling (return 200 for permanent errors, 500 for temporary errors). For more on webhooks, see our article on the webhook reliability gap.

Why Stripe Webhooks Matter for SaaS

Stripe webhooks matter for three business reasons:

  • Payment accuracy. Without webhooks, your SaaS does not know when a payment succeeds (it only knows when the checkout is initiated). Webhooks update your database when the payment is completed, which ensures the customer gets access to the product.
  • Subscription management. Without webhooks, your SaaS does not know when a subscription is cancelled, upgraded, or past due. Webhooks update the subscription status, which ensures the customer's access is correctly managed.
  • Automated operations. Without webhooks, you would need to manually check Stripe for payment status, which is not scalable. Webhooks automate the process, which frees you to focus on the product.

The Four Requirements for Reliable Stripe Webhooks

Requirement 1: Signature Verification

Stripe signs each webhook with a secret (the webhook signing secret). Your webhook endpoint must verify the signature to confirm the webhook is from Stripe (not from an attacker). Without verification, an attacker can send a fake webhook and trigger actions (e.g., mark an invoice as paid without actually paying).

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

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, webhookSecret);
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Process the event
  // ...
});

For more on security, see our article on a practical security checklist for early-stage SaaS.

Requirement 2: Idempotency

Stripe retries webhooks if it does not receive a 200 response within 30 seconds. This means the same webhook can arrive multiple times. Without idempotency, duplicate webhooks cause duplicate actions (e.g., double charges, duplicate records).

// Check if the webhook has already been processed
const processed = await WebhookLog.findOne({ where: { eventId: event.id } });
if (processed) {
  return res.json({ received: true, duplicate: true });
}

// Log the webhook
await WebhookLog.create({ eventId: event.id, type: event.type });

// Process the webhook
// ...

Requirement 3: Fast Responses

Stripe has a 30-second timeout. If your webhook handler does not respond within 30 seconds, Stripe retries the webhook. If your handler does synchronous work (e.g., sends an email, generates a report), it might exceed the timeout.

The fix is to respond immediately (200) and process the webhook asynchronously (via a background job):

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
  // Verify signature
  // Check idempotency
  // Log the webhook

  // Respond immediately
  res.json({ received: true });

  // Process asynchronously
  try {
    switch (event.type) {
      case 'checkout.session.completed':
        await jobQueue.add('process-payment', event.data.object);
        break;
      case 'customer.subscription.deleted':
        await jobQueue.add('cancel-subscription', event.data.object);
        break;
    }
  } catch (err) {
    console.error('Webhook processing error:', err);
  }
});

For more on background jobs, see our article on the SaaS founder's guide to background jobs and workers.

Requirement 4: Error Handling

Handle errors correctly:

  • Permanent errors (e.g., malformed payload): return 200 (to stop Stripe from retrying).
  • Temporary errors (e.g., database timeout): return 500 (to trigger Stripe retries).
  • Unhandled errors: log them and return 200 (to avoid infinite retries).

How to Test Stripe Webhooks

Testing webhooks is essential. Use Stripe CLI to send test webhook events to your local development server:

# Install Stripe CLI
# https://stripe.com/docs/stripe-cli

# Listen for webhooks and forward to your local server
stripe listen --forward-to localhost:3000/webhooks/stripe

# Trigger a test event
stripe trigger checkout.session.completed

Verify your webhook handler processes the event correctly, logs it, and responds with 200.

Common Pitfalls and Troubleshooting

The first pitfall is not verifying the signature. Without verification, an attacker can spoof webhooks. The fix is to always verify the signature.

The second pitfall is using express.json() for the webhook route. Stripe's signature verification requires the raw body, but express.json() parses it into a JavaScript object, which changes the body. The fix is to use express.raw({ type: 'application/json' }) for the webhook route.

The third pitfall is not handling duplicates. Stripe retries webhooks, which means the same event can arrive multiple times. The fix is to check if the event has already been processed (by storing the event ID in a database).

The fourth pitfall is slow responses. If the handler takes more than 30 seconds, Stripe retries. The fix is to respond immediately and process asynchronously.

The fifth pitfall is not testing. Webhooks that are not tested might fail in production. The fix is to test with Stripe CLI before launching.

Common Pitfalls and Troubleshooting

When working with how to set up stripe webhooks for your saas without breaking payments, several common pitfalls can undermine effectiveness. The first is over-automation. Automating everything sounds appealing, but some tasks require human judgment. The fix is to automate repetitive tasks (monitoring, diagnosis, deployment) while keeping humans in the loop for decisions that affect customers, billing, or security. The second is not testing changes before applying them. Whether it is a configuration change, a code change, or an infrastructure change, untested changes can break production. The fix is to always test in staging before applying to production, and to have a rollback plan. The third is not monitoring the automation itself. If your automated system goes down, you are flying blind. The fix is to monitor the automation system (e.g., with a dead man's switch) and to alert if it stops running. The fourth is not documenting the process. If the process is in your head, it does not exist for anyone else. The fix is to document the process in a runbook that anyone can follow. For more on documentation, see our article on how to build a deployment process your future team can inherit. The fifth is not reviewing regularly. Processes that work today might not work tomorrow (as the product grows, the traffic changes, the team changes). The fix is to review the process monthly and to adjust as needed.

Advanced Patterns and Best Practices

Beyond the basics of how to set up stripe webhooks for your saas without breaking payments, several advanced patterns can improve outcomes. The first is incremental implementation. Rather than implementing everything at once, start with the minimum viable version and iterate. This reduces risk (smaller changes are easier to debug) and delivers value faster. The second is automation. Manual processes are error-prone and do not scale. The fix is to automate repetitive tasks (deployment, testing, monitoring) using CI/CD pipelines and automated tools. For more on CI/CD, see our article on how we built the CI/CD pipeline. The third is documentation. A process that is not documented does not exist for anyone else. The fix is to document processes in runbooks that anyone can follow. For more on documentation, see our article on how to build a deployment process your future team can inherit. The fourth is testing. Untested changes can break production. The fix is to write tests (unit, integration, end-to-end) and to run them in CI/CD before every deployment. For more on testing, see our article on the testing void. The fifth is continuous improvement. Processes that work today might not work tomorrow. The fix is to review processes regularly (monthly) and to adjust based on lessons learned from incidents, feedback, and changing requirements.

When This Approach Is Not the Right Choice

While how to set up stripe webhooks for your saas without breaking payments is a valuable practice, it is not always the right approach. For very small projects (hobby projects, prototypes), the overhead of implementing best practices might not be worth the effort. The fix is to implement the minimum viable version and to add more as the project grows. For teams with limited resources (solo founders, small teams), prioritizing features over infrastructure might be the right call in the short term. The fix is to implement the highest-impact practices first (security, backups) and to defer the rest until the team grows. For projects with strict compliance requirements (HIPAA, SOC 2), the standard approach might not be sufficient, and you might need to implement additional controls (audit logging, access reviews, penetration testing). The key is to match the approach to your project's stage, resources, and requirements. For more on prioritization, see our article on the production checklist before your SaaS takes its first customer. For more on compliance, see the SaaS founder's guide to compliance.

Additional Considerations and Best Practices

When working with how to set up stripe webhooks for your saas without breaking payments, there are several additional considerations that can significantly impact your success. The first is the importance of starting simple and iterating. Many teams try to implement everything at once, which leads to complexity, bugs, and delayed launches. The fix is to start with the minimum viable version, verify it works, and then add features incrementally. This approach reduces risk, delivers value faster, and makes debugging easier because changes are smaller. The second consideration is the importance of documentation. A process that is not documented does not exist for anyone else on the team. Document your configuration, your deployment process, your rollback procedure, and your incident response plan. Use runbooks that anyone can follow, not just the person who set up the system. For more on documentation, see our article on how to build a deployment process your future team can inherit.

The third consideration is testing. Untested changes are the leading cause of production incidents. Before deploying any change, test it locally, test it in staging, and run your automated test suite. If you do not have automated tests, start by writing tests for your most critical paths (signup, login, payment). For more on testing, see our article on the testing void. The fourth consideration is monitoring. Without monitoring, you cannot detect issues until customers complain. Set up health checks, structured logging, metrics tracking, and alerts for error rate and response time. For more on monitoring, see our article on monitoring your SaaS without hiring a DevOps engineer.

The fifth consideration is security. Security is not optional when you are handling customer data and payment information. Ensure all secrets are in environment variables (never hardcoded), enforce HTTPS, set security headers, use rate limiting on auth endpoints, and hash passwords with bcrypt or argon2. For more on security, see our article on a practical security checklist for early-stage SaaS. The sixth consideration is backups and recovery. Your database should be backed up daily, backups should be stored off-site, and backup restore should be tested regularly. An untested backup is not a backup. For more on backups, see our article on how to rehearse a database restore before you need one.

The seventh consideration is cost management. Cloud costs can creep up over time, and without monitoring, they can exceed revenue. Track your monthly hosting cost, set a budget, and use fixed pricing (like Deployxa at $9/month for 15 apps) to avoid surprise bills. For more on cost management, see our article on how to estimate deployment costs for a small SaaS. The eighth consideration is team communication. When things go wrong, communication is as important as the fix. Set up a status page, communicate transparently during incidents, and publish post-mortems after. For more on communication, see our article on the SaaS founder's guide to status pages.

These considerations apply regardless of your specific technology stack, team size, or business model. By addressing each one systematically, you reduce the risk of outages, data loss, security breaches, and cost overruns, which protects your revenue and your customers' trust.

Conclusion: Reliable Webhooks, Reliable Payments

Stripe webhooks are critical for SaaS payment processing, but they are easy to get wrong. By implementing signature verification, idempotency, fast responses, and proper error handling, you can ensure your webhooks are reliable, secure, and tested. Do not launch your SaaS without tested webhook handling — it is the backbone of your payment flow.

Ready to set up Stripe webhooks? Follow the four requirements above, test with Stripe CLI, and deploy with confidence. For more, see the webhook reliability gap and the practical security checklist for early-stage SaaS. Explore our free developer tools to speed up your workflow.

Ready to deploy with Deployxa?

Deploy your apps globally with automatic SSL and AI diagnostics.

Start Free Now