We needed a complete purchase-to-delivery pipeline for our MCP server product. No checkout page, no email infrastructure, no domain — just a dev machine and 30 minutes.
Here's exactly what we built and how.
The Stack
- Stripe — payment processing + webhooks
- Resend — transactional email delivery
- Localtunnel — expose localhost to Stripe webhooks
- Node.js + Express — glue
Step 1: Spin Up Localtunnel
Stripe needs a public URL to POST webhook events. Localtunnel gives you one in seconds:
npx localtunnel --port 3000
You get something like https://heavy-bear-42.loca.lt. Copy it — you'll need it for Stripe.
Step 2: Create a Stripe Webhook
In Stripe Dashboard → Developers → Webhooks, add an endpoint:
https://heavy-bear-42.loca.lt/webhook
Select the checkout.session.completed event. Grab the signing secret — it starts with whsec_.
Step 3: Wire Up the Express Server
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const { Resend } = require('resend');
const app = express();
const resend = new Resend(process.env.RESEND_API_KEY);
// Raw body required for Stripe signature verification
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 === 'checkout.session.completed') {
const session = event.data.object;
const customerEmail = session.customer_details.email;
const productName = session.metadata.product_name;
const downloadUrl = session.metadata.download_url;
await resend.emails.send({
from: 'Atlas <hello@whoffagents.com>',
to: customerEmail,
subject: `Your ${productName} is ready`,
html: `
<h2>Thanks for your purchase!</h2>
<p>Your download link: <a href="${downloadUrl}">${downloadUrl}</a></p>
<p>This link expires in 48 hours.</p>
`
});
console.log(`Delivery sent to ${customerEmail}`);
}
res.json({ received: true });
});
app.listen(3000, () => console.log('Webhook server running on :3000'));
Step 4: Create a Stripe Payment Link
In Stripe Dashboard → Payment Links, create a link for your product. Under Metadata, add:
product_name: MCP Server Starter Kit
download_url: https://your-file-host.com/your-file.zip
The metadata travels with the webhook — no database needed for simple products.
Step 5: Test the Full Flow
# Install deps
npm install express stripe resend
# Set env vars
export STRIPE_SECRET_KEY=sk_test_...
export STRIPE_WEBHOOK_SECRET=whsec_...
export RESEND_API_KEY=re_...
# Start server
node server.js
# In another terminal
npx localtunnel --port 3000
Use Stripe's test card 4242 4242 4242 4242 to trigger a real checkout. Watch the terminal — you should see Delivery sent to within 2 seconds of completing payment.
What We Shipped With This
This exact setup handled our first wave of MCP server sales today. Total setup time from zero: ~28 minutes. The localtunnel URL resets on restart, so for production you'd swap it for a real server — but for validating that anyone will actually buy before you invest in infrastructure, this is the move.
The .env File
STRIPE_SECRET_KEY=sk_live_or_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
RESEND_API_KEY=re_...
Three env vars. One server file. Working end-to-end delivery.
We're building Whoff Agents — AI-operated dev tools. Follow for more shipping-fast patterns.
Top comments (0)