Automated Invoice PDFs on Stripe Payment Success (5-Minute Setup)
Every SaaS needs to send invoices to customers. Most teams build rickety PDF generation pipelines: they use Puppeteer on a background worker, manage browser processes, deal with memory leaks, and wonder why their invoice job crashed at 2 AM.
Here's a simpler approach: generate invoice PDFs on Stripe charge.succeeded webhooks using PageBolt's API. No browser processes. No infrastructure overhead. Just Stripe → API call → PDF email.
The Setup: 5 Lines of Webhook Handler
First, install dependencies:
npm install express stripe dotenv node-fetch
Create your webhook handler:
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const fetch = require('node-fetch');
const app = express();
app.use(express.json());
// Stripe webhook endpoint
app.post('/webhook', express.raw({type: 'application/json'}), async (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}`);
}
if (event.type === 'charge.succeeded') {
const charge = event.data.object;
await generateAndEmailInvoice(charge);
}
res.json({received: true});
});
app.listen(3000, () => console.log('Webhook listening on port 3000'));
Generate the Invoice PDF
Here's the function that generates the PDF and emails it:
async function generateAndEmailInvoice(charge) {
const customerId = charge.customer;
const customer = await stripe.customers.retrieve(customerId);
// Generate HTML invoice
const invoiceHtml = `
<html>
<head>
<style>
body { font-family: Arial; margin: 40px; color: #333; }
.header { text-align: center; margin-bottom: 40px; }
.logo { font-size: 28px; font-weight: bold; }
.invoice-details { margin-bottom: 30px; }
.line { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid #eee; }
.total { font-weight: bold; font-size: 18px; padding-top: 20px; border-top: 2px solid #000; }
.footer { margin-top: 40px; font-size: 12px; color: #666; }
</style>
</head>
<body>
<div class="header">
<div class="logo">Your Company</div>
<h2>Invoice</h2>
</div>
<div class="invoice-details">
<p><strong>Bill To:</strong><br>
${customer.name}<br>
${customer.email}
</p>
<p><strong>Invoice Date:</strong> ${new Date(charge.created * 1000).toLocaleDateString()}</p>
<p><strong>Invoice ID:</strong> ${charge.id.substring(0, 8).toUpperCase()}</p>
</div>
<table style="width: 100%; border-collapse: collapse;">
<tr style="background: #f5f5f5;">
<th style="text-align: left; padding: 10px;">Description</th>
<th style="text-align: right; padding: 10px;">Amount</th>
</tr>
<tr>
<td style="padding: 10px;">Service Charge</td>
<td style="text-align: right; padding: 10px;">$${(charge.amount / 100).toFixed(2)}</td>
</tr>
</table>
<div class="total" style="text-align: right;">
Total: $${(charge.amount / 100).toFixed(2)}
</div>
<div class="footer">
<p>Thank you for your payment.</p>
<p>Questions? Contact billing@yourcompany.com</p>
</div>
</body>
</html>
`;
// Generate PDF with PageBolt
const pdfResponse = await fetch('https://api.pagebolt.com/v1/pdf', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PAGEBOLT_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
html: invoiceHtml,
format: 'A4',
margin: {top: 10, right: 10, bottom: 10, left: 10}
})
});
if (!pdfResponse.ok) {
console.error('PDF generation failed:', await pdfResponse.text());
return;
}
const pdfBuffer = await pdfResponse.buffer();
// Email the PDF (example using Sendgrid)
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
await sgMail.send({
to: customer.email,
from: 'billing@yourcompany.com',
subject: `Invoice ${charge.id.substring(0, 8).toUpperCase()}`,
text: 'Please find your invoice attached.',
attachments: [{
content: pdfBuffer.toString('base64'),
filename: `invoice-${charge.id.substring(0, 8)}.pdf`,
type: 'application/pdf',
disposition: 'attachment'
}]
});
console.log(`Invoice emailed to ${customer.email}`);
}
Environment Setup
Create a .env file:
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
PAGEBOLT_API_KEY=pf_live_xxx
SENDGRID_API_KEY=SG.xxx
Get your Stripe webhook secret:
- Go to Stripe Dashboard → Developers → Webhooks
- Create webhook endpoint pointing to
https://yourserver.com/webhook - Subscribe to
charge.succeededevent - Copy the signing secret
Deploy to Production
Using Vercel (serverless):
# Install vercel CLI
npm install -g vercel
# Deploy
vercel --prod
Update your Stripe webhook endpoint URL to your Vercel function URL.
Why This Beats Puppeteer
Puppeteer approach:
- Spin up browser process for each invoice (~500ms overhead)
- Manage memory and handle OOM errors
- Deploy to EC2 or Lambda with extra reserved capacity
- Cost: $100-300/month for modest volume
PageBolt approach:
- 3-line API call (~100ms)
- Zero infrastructure overhead
- Automatic scaling
- Cost: $29/month Starter plan (handles 5,000 PDFs)
For 1,000 invoices/month, PageBolt saves you $100+/month in infrastructure while being faster and more reliable.
Next Steps
- Sign up free at pagebolt.dev/pricing — 100 PDFs/month
- Test locally: Generate a sample invoice PDF with your HTML template
- Deploy webhook to production
- Watch invoices auto-generate on every Stripe payment
That's it. No browser management. No background worker headaches. Just invoices, automatically.
Start free today — pagebolt.dev/pricing. 100 PDFs/month, no credit card required.
Top comments (0)