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
);
...