How to Integrate Stripe CLI with Next.js inside Docker Containers
I recently added Stripe integration into my dockerized Next.js application.
In order to test webhooks locally, I wanted to incorporate Stripe CLI into my developent workflow.
I could have used the Stripe Dashboard (Developers
> Webhooks
) to manage these webhooks. However, in order to do this, I would need to expose my local server to the public internet over some secure tunnel. While there are services dedicated to exactly this (e.g. ngrok
, localtunnel
), it requires extra steps to configure the endpoint and update the dashboard each time I start developing.
The natural solution was to use Stripe’s officially supported Stripe CLI.
I’ve been running my Next.js and PostgreSQL instances in containers spun up with docker-compose
, so I opted to try out Stripe CLI’s official Docker image.
Suppose this is what I’m working with.
version: "3"
services:
next-app:
image: custom-next-image
container_name: next-app
networks:
- next-postgres-network
ports:
- 3000:3000
postgres-db:
image: postgres
container_name: postgres-db
networks:
- next-postgres-network
ports:
- 5432:5432
volumes:
- pg-data:/var/lib/postgresql/data
networks:
next-postgres-network:
driver: bridge
volumes:
pg-data:
driver: local
We have two services, next-app
and postgres-db
, that live under the next-postgres-network
network.
For the sake of brevity, I won’t go over how to set up Next.js with Docker for development. I’ll assume we have an image built and ready to run.
next-app
runs some custom Next.js application built prior to runningdocker-compose up
.postgres-db
is just pulling thepostgres
image from Docker Hub.
Set up Stripe CLI in docker-compose
We’ll want to add another service for Stripe CLI.
stripe-cli:
image: stripe/stripe-cli
container_name: stripe-cli
networks:
- next-prisma-postgres-network
However, to force Stripe CLI to start listening for webhook events, we need to execute a command on container startup. docker-compose
allows us to use the command
field in each service.
stripe-cli:
image: stripe/stripe-cli
container_name: stripe-cli
networks:
- next-prisma-postgres-network
command: <some_command>
Configure Stripe CLI
According to the docs, we can use stripe listen
to listen for webhook events.
Inside the Docker container, we can just use listen
.
There are three things we need to do before we set the command:
- Add our
STRIPE_SECRET_KEY
to our.env
. This is accessible from the Stripe Dashboard. - Add our
STRIPE_DEVICE_NAME
to our.env
. This will be how you identify the specific device that events are being sent to (e.g.personal-laptop
,work-laptop
, etc.). This is viewable atDevelopers
>Webhooks
in the Stripe Dashboard. - Identify the API route that will handle all the webhook events. In this example, we’ll assume all events will be
POST
requests to/api/stripe/webhook
.
Assuming we’ve done all the above, we can then replace <some_command>
in our Stripe CLI service to be the command below.
listen --api-key ${STRIPE_SECRET_KEY} \
--device-name ${STRIPE_DEVICE_NAME} \
--forward-to next-app:3000/api/stripe/webhook
The Stripe CLI documentation contains more information on specific flags we can use to configure our setup.
Configure Next.js Webhook Secret
Once we’re satisfied, we can start up our containers. We’ll notice that the Stripe CLI container outputs a webhook secret key upon start up.
Ready! Your webhook signing secret is whsec_xxx
We’ll want to take this secret key and set it as an environment variable in our Next.js applicaation.
# .env
...
STRIPE_WEBHOOK_SECRET=whsec_xxx
This is the secret we use when parsing and constructing the event passed into /api/stripe/webhook
.
...
const buf = await buffer(req);
const sig = req.headers["stripe-signature"];
const event = stripe.webhooks.constructEvent(
buf.toString(),
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
...