Tracking Stripe Payments with Slack and faasd
Tags: serverless, openfaas, raspberrypi, faasd
Published: 2020-04-05 14:22:44

---
title: Tracking Stripe Payments with Slack and faasd
published: true
date: 2020-04-04 14:49:54 UTC
tags: serverless,openfaas,raspberrypi,faasd
canonical_url: https://myedes.io/stripe-serverless-webhook-faasd/
---

 ![Tracking Stripe Payments with Slack and faasd](https://images.unsplash.com/photo-1484417894907-623942c8ee29?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ)

In my last blog post, I have written about how to turn a Raspberry Pi into a serverless platform with the lightweight [OpenFaas](https://www.openfaas.com/) implementation [faasd](https://github.com/openfaas/faasd). If you haven't checked it out already, you can find the link to the post below:
[Turn your Raspberry Pi into a serverless platform with faasd](https://dev.to/mehyedes/turn-your-raspberry-pi-into-a-serverless-platform-with-faasd-14nh)

> 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/](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](https://docs.inlets.dev/).

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/](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

![Tracking Stripe Payments with Slack and faasd](https://myedes.io/content/images/2020/03/stripe-slack-notifier-2.png)<figcaption>Pictured: conceptual architecture with faasd for functions, inlets for the tunnel and stripe to generate webhooks.</figcaption>

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:
[Get HTTPS for your local endpoints with inlets and Caddy](https://blog.alexellis.io/https-inlets-local-endpoints/)

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](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:

```bash
$ 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

![Tracking Stripe Payments with Slack and faasd](https://myedes.io/content/images/2020/03/stripe-webhook-1.png)

### Create a Slack App

To receive incoming webhooks we need to create an App in the Slack dashboard.

![Tracking Stripe Payments with Slack and faasd](https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-19-49-49.png)

## 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`

```bash
$ 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 the [mehyedes/stripe-slack-notifier](https://github.com/mehyedes/stripe-slack-notifier)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:

```python
import stripe
from babel import numbers
from slack_webhook import Slack

def fetch_secret(secret_name):
    secret_file = open(f"/var/openfaas/secrets/{secret_name}", 'r')
    return secret_file.read()

def handle(event, context):

    # Make sure to create the secrets below
    webhook_url = fetch_secret("slack-webhook-url")
    stripe.api_key = fetch_secret("stripe-secret-key")
    webhook_secret = fetch_secret("webhook-secret")
    
    payload = event.body
    received_sig = event.headers.get("Stripe-Signature", None)
    
    try:
        event = stripe.Webhook.construct_event(
            payload, received_sig, webhook_secret
        )
    except ValueError:
        print("Error while decoding event!")
        return {
            "body": "Bad payload",
            "statusCode": 400
        }
    except stripe.error.SignatureVerificationError:
        print("Invalid signature!")
        return {
            "body": "Bad signature", 
            "statusCode": 400
        }

    # Fail for all other event types  
    if event.type != "charge.succeeded":
        return {
            "body":"Unsupported event type",
            "statusCode": 422
        }
  
    amount = numbers.format_currency(
      event.data.object.amount / 100,
      event.data.object.currency.upper(), 
      locale='en'
    )

    try:
        slack = Slack(url=webhook_url)
        slack.post(text=f"You have a received a new payment of {amount} :moneybag: :tada:")
    except:
        print("An error occured when trying to send slack message.")
        return {
            "body": "Could not send slack message", 
            "statusCode": 500
        }
    return {
        "body": "Notification was sent successfully to Slack", 
        "statusCode": 200
    }
```
<figcaption>stripe-slack-notifier/handler.py</figcaption>

OpenFaaS [secrets](https://docs.openfaas.com/reference/secrets/#deploy-a-function-with-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:

```bash
$ 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](https://github.com/docker/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](https://github.com/mehyedes/stripe-slack-notifier/blob/master/.github/workflows/main.yml).

Manually, this can be achieved with the following steps:

1. Generate the Docker build context for the function

```bash
$ 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:

```bash
$ docker run --privileged --rm docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64
```

3.   Create a new builder for buildx

```bash
$ 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:

```bash
$ 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:

![Tracking Stripe Payments with Slack and faasd](https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-34-52.png)

To deploy the function to the Raspberry Pi, we can finally run the command below from our laptop:

```bash
$ 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`

```bash
$ 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:

![Tracking Stripe Payments with Slack and faasd](https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-53-24.png)

After a few moments, we can see a similar message in Slack:

![Tracking Stripe Payments with Slack and faasd](https://myedes.io/content/images/2020/03/Screenshot-from-2020-03-31-20-55-28.png)

> The function only supports `charge.succeeded` Stripe events for now, so any other type of event will receive `Unsupported 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](https://stripe.com/docs/webhooks) handler for Stripe API events using [faasd](https://github.com/openfaas/faasd) and [inlets](https://docs.inlets.dev/#/).

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](https://openfaas.slack.com)
- Extend the [mehyedes/stripe-slack-notifier](https://github.com/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](https://blog.alexellis.io/deploy-serverless-faasd-with-cloud-init/))
- Try out the [inlets-operator](https://github.com/inlets/inlets-operator) for getting public IP addresses for your local kubernetes cluster