Summary
I am developing an Express.js application to handle Stripe webhooks. Despite configuring the webhook route as recommended, I keep encountering a 404 Not Found
error when Stripe attempts to post events to my webhook endpoint.
Environment
- Node.js Version: [v20.14.0]
- Express Version: [4.18.2]
- Stripe Version: [1.19.5]
Issue Description
When I run the Stripe CLI to forward events to my local server, I receive a 404 Not Found
error for each event. Below are the details of the error messages:
2024-06-09 16:30:24 --> invoice.paid [evt_1PPfGkRsJfE0QDMT8OOiGWVh]
2024-06-09 16:30:24 <-- [404] POST http://localhost:3080/webhook [evt_1PPfGkRsJfE0QDMT8OOiGWVh]
Server Code (index.js)
javascript
require('dotenv').config();
const path = require('path');
const express = require('express');
const cors = require('cors');
const mongoSanitize = require('express-mongo-sanitize');
const routes = require('./routes');
const webhookRoutes = require('./routes/webhookRoutes');
const { PORT, HOST } = process.env;
const port = Number(PORT) || 3080;
const host = HOST || 'localhost';
const app = express();
app.disable('x-powered-by');
app.use(cors());
app.use(express.json({ limit: '3mb' }));
app.use(mongoSanitize());
app.use(express.urlencoded({ extended: true, limit: '3mb' }));
// Webhook route must handle raw request bodies for signature verification
app.use('/webhook', express.raw({ type: 'application/json' }), webhookRoutes);
app.use('/api', routes); // Example for other routes
app.use((req, res) => {
res.status(404).sendFile(path.join(__dirname, 'public', '404.html'));
});
app.listen(port, host, () => {
console.log(`Server listening at http://${host}:${port}`);
});
Webhook Route (webhookRoutes.js)
javascript
Copy code
const express = require('express');
const router = express.Router();
const { handleWebhook } = require('../controllers/webhookController');
router.post('/', express.raw({ type: 'application/json' }), handleWebhook);
module.exports = router;
Webhook Controller (webhookController.js)
javascript
Copy code
const Stripe = require('stripe');
const { updateUserSubscriptionStatus } = require('../../models/userMethods');
const { logger } = require('~/config');
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_ENDPOINT_SECRET;
const handleWebhook = async (req, res) => {
logger.info('Received a webhook event');
let event = req.body;
// Verify webhook signature
if (endpointSecret) {
const signature = req.headers['stripe-signature'];
try {
event = stripe.webhooks.constructEvent(req.body, signature, endpointSecret);
logger.info('Webhook signature verified');
} catch (err) {
logger.error('⚠️ Webhook signature verification failed:', err.message);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
}
try {
switch (event.type) {
case 'invoice.paid': {
const invoice = event.data.object;
const customerId = invoice.customer;
logger.info(`Handling invoice.paid for customer ${customerId}`);
const updateResult = await updateUserSubscriptionStatus(customerId, 'active');
if (updateResult instanceof Error) {
logger.error('[updateUserSubscriptionStatus]', updateResult);
const { status, message } = updateResult;
return res.status(status).send({ message });
}
logger.info('User subscription status updated successfully');
res.status(200).send('Success');
break;
}
case 'payment_intent.succeeded': {
const paymentIntent = event.data.object;
logger.info(`PaymentIntent for ${paymentIntent.amount} was successful!`);
res.status(200).send('Success');
break;
}
case 'payment_method.attached': {
const paymentMethod = event.data.object;
logger.info('PaymentMethod successfully attached');
res.status(200).send('Success');
break;
}
default: {
logger.warn(`Unhandled event type ${event.type}`);
res.status(400).send(`Unhandled event type ${event.type}`);
}
}
} catch (err) {
logger.error('[handleWebhook]', err);
res.status(500).json({ message: err.message });
}
};
module.exports = {
handleWebhook,
};
Steps to Reproduce
Start the Server:
node index.js
Use Stripe CLI to Forward Events:
Start Stripe CLI to listen for events and forward them to your local server:
stripe listen –forward-to http://localhost:3080/webhook
Trigger a Test Event:
Use Stripe CLI to trigger a test event:
stripe trigger invoice.paid
Expected Behavior
The webhook endpoint should correctly receive and process Stripe events, and the server should log the events and respond with a 200 status code.
Actual Behavior
The server responds with a 404 Not Found error for the webhook endpoint.
Request for Help
I would appreciate any help in diagnosing why the webhook endpoint is not being recognized and how to fix the 404 errors. Thank you!
Ray Wu is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.