Let me see if I can capture everything I did to make this happen, for standard stripe accounts and connected, but this is of course using Node.
What is described below has 2 main goals, 1 support a second standard account (we were transitioning from one to another) and for both of those, support connected accounts.
In the app/webhooks
folder there is by default stripe
, but you can add additional empty folders and these will each serve as a webhook endpoint.
For me, it is these four (stripe being the parent with all the actual workflows):
Next in app/lib/webhooks
there are js files, one for each of the named folders above:
The contents of the js files is modified to change the secretKey and endpointSecret, along with the endpoint folder created above
Here is the default stripe.js:
const webhook = require('../core/webhook');
const config = require('../setup/config');
const fs = require('fs-extra')
if (fs.existsSync('app/webhooks/stripe')) {
const stripe = require('stripe')(config.stripe.secretKey);
const endpointSecret = config.stripe.endpointSecret;
exports.handler = webhook.createHandler('stripe', (req, res, next) => {
const sig = req.headers['stripe-signature'];
try {
stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);
} catch (err) {
res.status(400).send(`Webhook Error: ${err.message}`);
return false;
}
// return the action name to execute
return req.body.type;
});
}
And here is a modified version using environment variables for the secrets:
const webhook = require('../core/webhook');
const config = require('../setup/config');
const fs = require('fs-extra')
if (fs.existsSync('app/webhooks/stripe_connect_ui')) {
const stripe = require('stripe')(process.env.STRIPE_SECRET_UI);
const endpointSecret = process.env.STRIPE_WEBHOOK_CONNECT_SECRET_UI;
exports.handler = webhook.createHandler('stripe', (req, res, next) => {
const sig = req.headers['stripe-signature'];
try {
stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);
} catch (err) {
res.status(400).send(`Webhook Error: ${err.message}`);
return false;
}
// return the action name to execute
return req.body.type;
});
}
So then we get to the actual webhook workflows. There will only be a single stripe
folder so the logic is done all within a single webhook for any you need to support.
If the code will be the same for everybody, then you just need something to determine which tenant, be it the endpoint or something in the Stripe POST values.
For example a connected account will always have a $_POST.account
value, which you can use to lookup in your db and process accordingly. I just use a conditional statement to run different code, based on the event being a standard vs connected account.
You then create at Stripe the webhook endpoints you need for both the standard and connected accounts.
You can see 2 of them here (2 are on another Stripe account):
This goes outside the standard Wappler support, so heavy testing using the Test settings of Stripe is important.
While I'm here, don't forget that webhooks are not guaranteed to come in any particular order, nor are they guaranteed to only be sent once, so use the event id's to make sure you are processing properly. And if you have a long running logic, don't do it in the webhook, log the event in your db and use a queue to process...webhooks should report back a 200 response quickly.