In my last blog post, I have written about how to turn a Raspberry Pi into a serverless platform with the lightweight OpenFaaS implementation faasd. If you haven't checked it out already, you can find the link to the post below:
Please note that a Raspberry Pi is not required here.
faasd can be installed without issues on any other machine or cloud instance as well.
This blog post from Alex Ellis describes how to build a faasd serverless appliance on DigitalOcan:
https://blog.alexellis.io/deploy-serverless-faasd-with-cloud-init/
I did that only as a small hobby project. But then, I wanted to use this setup for a real-world use case, and also use the opportunity to try another useful open-source project which is inlets.
Inlets is a cloud native tunnel that allows you to expose private endpoints to the Internet. In this case, it will be used to expose the OpenFaaS gateway running on my Raspberry Pi board to receive incoming Stripe Webhook events and dispatch them to the relevant function.
For more details about inlets, check the official docs and resources available at https://docs.inlets.dev/
So in this post, I will describe how I built a local OpenFaaS function that serves as a Stripe webhook. This function simply sends a Slack notification for every new charge.succeeded
event.
The architecture
Inlets is an open source HTTP tunnel which we can use to get a public IP address to receive webhooks. The inlets client will be running on the Raspberry Pi and connects to the inlets server using a websocket connection to listen for any incoming requests.
In our setup, every Stripe webhook event that is received by the inlets client will be forwarded to the OpenFaaS gateway on the Raspberry Pi, which will in turn trigger the stripe-slack-notifier
function to send a new Slack notification.
Preparing the Setup
Obtain an exit node with inlets
First, make sure you have created an inlets exit server following this nice guide by Alex Ellis:
This would allow you to have an exit node with a public domain that can be used for receiving webhook events. In the setup described earlier, the inlets client is running on the Raspberry Pi, so I have chosen the upstream to be http://127.0.0.1:8080, which is the OpenFaaS Gateway URL.
Using the token provided by the inlets server, the command below exposes the OpenFaaS Gateway to the Internet:
$ inlets client --remote wss://${REMOTE_EXIT_NODE} --upstream http://127.0.0.1:8080 --token ${INLETS_TOKEN}
Our function will then be available from the Internet using the https://${REMOTE_EXIT_NODE}/function/stripe-slack-notifier
Add a Stripe Webhook
We need to add a new Stripe webhook using the
Create a Slack App
To receive incoming webhooks we need to create an App in the Slack dashboard.
Create a function to receive webhooks from Stripe
The function stripe-slack-notifier
was built using the python3-http
template that is available in the OpenFaaS template store.
Creating a new function using faas-cli new
$ faas-cli template store list | grep python3-http
python3-http openfaas-incubator Python 3.6 with Flask and HTTP
$ faas-cli template store pull python3-http
# Make sure to set this to you Docker hub username
$ export DOCKER_REGISTRY_PREFIX=<Docker hub username>
$ faas-cli new stripe-slack-notifier --lang python3-http --prefix ${DOCKER_REGISTRY_PREFIX}
Folder: stripe-slack-notifier created.
___ _____ ____
/ _ \ _ __ ___ _ __ | ___|_ _ __ _/ ___|
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) | __/ | | | _| (_| | (_| |___) |
\___/| .__/ \___|_| |_|_| \__,_|\__,_|____/
|_|
Function created in folder: stripe-slack-notifier
Stack file written: stripe-slack-notifier.yml
$ tree .
.
├── stripe-slack-notifier
│ ├── handler.py
│ └── requirements.txt
└── stripe-slack-notifier.yml
The final source code for the function can be found in this Github repository:
The function is written in python. It will verify the received payload using the Stripe API key and the webhook singing secret, and then send a slack notification:
OpenFaaS secrets are used to pass the following data to the function:
- Stripe API key
- Webhook signing secret
- Slack Webhook URL
So they need to be created manually with faas-cli
before deploying the function:
$ faas-cli secret create slack-webhook-url \
--from-literal=${SLACK_WEBHOOK_URL} --gateway http://raspberrypi.loc:8080
$ faas-cli secret create stripe-secret-key \
--from-literal=${STRIPE_API_KEY} --gateway http://raspberrypi.loc:8080
$ faas-cli secret create webhook-secret \
--from-literal=${WEBHOOK_SIGNING_SECRET} --gateway http://raspberrypi.loc:8080
$ faas-cli secret list --gateway http://raspberrypi.loc:8080
NAME
slack-webhook-url
stripe-secret-key
webhook-secret
Building & deploying the function
Since the function will be running on Raspberry Pi, we need to build the Docker image for armv7
.
Fortunately, this has become easier with the multi platform builds feature by buildx available in docker v19.03.
But since we have already exposed our OpenFaaS gateway using inlets, we can use Github Actions to build and deploy the latest version of our function to the Raspberry Pi with every git push. The workflow file used to automate this can be found here.
Manually, this can be achieved with the following steps:
- Generate the Docker build context for the function
$ faas-cli build --shrinkwrap -f stripe-slack-notifier.yml
2. This step is not needed if using Docker for desktop as this would be done automatically for you. On Linux, we need to register other platforms like armv7
with the kernel using the docker/binfmt
image:
$ docker run --privileged --rm docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
3. Create a new builder for buildx
$ docker buildx create --name armhf --platform linux/arm/v7
armhf
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
armhf * docker-container
armhf0 unix:///var/run/docker.sock running linux/arm/v7, linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
default docker
default default running linux/amd64, linux/386
4. Finally build and push the image for both armv7
and amd64
platforms:
$ DOCKER_BUILD_KIT=1 DOCKER_CLI_EXPERIMENTAL=enabled docker buildx build --platform linux/arm/v7,linux/amd64 -t ${DOCKER_REGISTRY_PREFIX}/stripe-slack-notifier:latest --push .
A new Docker image is now available on the Docker registry for both amd64
and armv7
architectures:
To deploy the function to the Raspberry Pi, we can finally run the command below from our laptop:
$ faas-cli deploy -f stripe-slack-notifier.yml -g https://raspberrypi.loc:8080
Test the function
Check the status of the function with faas-cli describe
$ faas-cli describe stripe-slack-notifier --gateway http://raspberrypi.loc:8080
Name: stripe-slack-notifier
Status: Ready
Replicas: 1
Available replicas: 1
Invocations: 0
Image:
Function process:
URL: http://raspberrypi.loc:8080/function/stripe-slack-notifier
Async URL: http://raspberrypi.loc:8080/async-function/stripe-slack-notifier
We can test that it's now by triggering some test events from the Stripe dashboard:
After a few moments, we can see a similar message in Slack:
The function only supportscharge.succeeded
Stripe events for now, so any other type of event will receiveUnsupported event type
error from the function, and no Slack messages will be sent.
Conclusion
In this blog post, I have described how we can create a serverless webhook handler for Stripe API events using faasd and inlets.
As we have seen in this post, faasd can be a great alternative to running OpenFaaS without the need for managing a Kubernetes or Docker Swarm cluster. Combined with inlets, they can be used together for running and exposing functions for real-world use cases.
Things that you might want to do next:
- Join the OpenFaaS community on slack
- Extend the mehyedes/stripe-slack-notifier function to support other webhook event types
- Build a faasd serverless appliance on a cloud instance using cloud-init (see deploy-serverless-faasd-with-cloud-init)
- Try out the inlets-operator for getting public IP addresses for your local kubernetes cluster