Skip to content

Complete example of testing Stripe webhooks locally using Volley. No ngrok, no tunneling, no exposing your server. Includes Express.js server, webhook handlers, and best practices.

Notifications You must be signed in to change notification settings

volleyhq/volley-stripe-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Volley + Stripe Webhook Example

Complete example of testing Stripe webhooks locally using Volley - No ngrok, no tunneling, no exposing your server.

Volley Stripe Node.js

This repository demonstrates how to test Stripe webhooks locally using Volley, a webhook-as-a-service platform. Unlike ngrok, Volley provides permanent webhook URLs and doesn't require tunneling.

🎯 What You'll Learn

  • How to set up Stripe webhooks with Volley
  • How to test webhooks locally without exposing your server
  • How to handle Stripe webhook events in Node.js/Express
  • Best practices for webhook security and verification

πŸš€ Quick Start

Prerequisites

Installation

  1. Clone this repository:

    git clone https://github.com/volleyhq/volley-stripe-example.git
    cd volley-stripe-example
  2. Install dependencies:

    npm install
  3. Set up environment variables:

    cp .env.example .env
    # Edit .env with your Stripe secret key
  4. Create a Volley source:

    • Go to Volley Dashboard
    • Create a new organization and project
    • Create a new source (e.g., "stripe-webhooks")
    • Copy your ingestion ID (e.g., abc123xyz)
  5. Configure Stripe webhook:

    • Go to Stripe Dashboard
    • Click "Add endpoint"
    • Enter your Volley webhook URL: https://api.volleyhooks.com/hook/YOUR_INGESTION_ID
    • Select events to listen to (e.g., payment_intent.succeeded, customer.created)
    • Click "Add endpoint"
  6. Start your local server:

    npm start
  7. Forward webhooks to localhost:

    volley listen --source YOUR_INGESTION_ID --forward-to http://localhost:3000/webhooks/stripe
  8. Test it:

    • Trigger a test event in Stripe Dashboard
    • Watch it appear in your local server logs!

πŸ“ Project Structure

volley-stripe-example/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ server.js          # Express server setup
β”‚   β”œβ”€β”€ webhookHandler.js  # Stripe webhook handler
β”‚   └── handlers/          # Event-specific handlers
β”‚       β”œβ”€β”€ paymentIntent.js
β”‚       β”œβ”€β”€ customer.js
β”‚       └── invoice.js
β”œβ”€β”€ .env.example           # Environment variables template
β”œβ”€β”€ package.json
└── README.md

πŸ’» Code Example

Basic Webhook Handler

// src/webhookHandler.js
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const router = express.Router();

// Stripe webhook endpoint
router.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    // Verify webhook signature
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    console.error('Webhook signature verification failed:', err.message);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      await handlePaymentIntentSucceeded(event.data.object);
      break;
    case 'customer.created':
      await handleCustomerCreated(event.data.object);
      break;
    case 'invoice.payment_succeeded':
      await handleInvoicePaymentSucceeded(event.data.object);
      break;
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }

  // Return a response to acknowledge receipt
  res.json({ received: true });
});

module.exports = router;

Event Handlers

// src/handlers/paymentIntent.js
async function handlePaymentIntentSucceeded(paymentIntent) {
  console.log('Payment succeeded:', paymentIntent.id);
  // Your business logic here
  // e.g., update database, send confirmation email, etc.
}

// src/handlers/customer.js
async function handleCustomerCreated(customer) {
  console.log('Customer created:', customer.id);
  // Your business logic here
}

πŸ” Security Best Practices

1. Verify Webhook Signatures

Always verify Stripe webhook signatures to ensure requests are from Stripe:

const event = stripe.webhooks.constructEvent(
  req.body,
  sig,
  process.env.STRIPE_WEBHOOK_SECRET
);

2. Use Environment Variables

Never commit secrets to version control:

# .env
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

3. Idempotency

Handle duplicate webhooks gracefully:

// Use Stripe event IDs for idempotency
const eventId = event.id;
if (await isEventProcessed(eventId)) {
  return; // Already processed
}
await markEventAsProcessed(eventId);

πŸ§ͺ Testing

Manual Testing

  1. Use Stripe CLI to send test webhooks:

    stripe trigger payment_intent.succeeded
  2. Or use Stripe Dashboard:

    • Go to Webhooks β†’ Your endpoint β†’ Send test webhook

Automated Testing

// test/webhook.test.js
const request = require('supertest');
const app = require('../src/server');

describe('Stripe Webhooks', () => {
  it('should handle payment_intent.succeeded', async () => {
    const event = {
      id: 'evt_test',
      type: 'payment_intent.succeeded',
      data: { object: { id: 'pi_test' } }
    };
    
    const response = await request(app)
      .post('/webhooks/stripe')
      .send(JSON.stringify(event))
      .set('stripe-signature', 'test-signature');
    
    expect(response.status).toBe(200);
  });
});

πŸ†š Why Volley Instead of ngrok?

Feature Volley ngrok
Webhook URLs βœ… Permanent, never change ❌ Change on restart
Tunneling ❌ Not required βœ… Required
Local Server Privacy βœ… Completely private ⚠️ Exposed through tunnel
Built-in Retry βœ… Automatic ❌ No
Monitoring βœ… Full dashboard ❌ Limited
Production Ready βœ… Same URL for dev/prod ❌ Dev tool only

Learn more: Volley vs ngrok

πŸ“š Additional Resources

🀝 Contributing

Found a bug or have a suggestion? Please open an issue or submit a pull request!

πŸ“„ License

MIT License - See LICENSE file for details


Built with ❀️ using Volley and Stripe

About

Complete example of testing Stripe webhooks locally using Volley. No ngrok, no tunneling, no exposing your server. Includes Express.js server, webhook handlers, and best practices.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published