---
title: "How to receive Stripe webhooks on localhost | WebhookRelay"
meta:
  "og:description": "Often when building an application that integrates with 3rd party services we need a way to receive webhooks"
  "og:title": "How to receive Stripe webhooks on localhost"
  description: "Often when building an application that integrates with 3rd party services we need a way to receive webhooks"
---

![Stripes](https://webhookrelay.com/blog/receiving-stripe-webhooks-localhost/images/stripes.svg)

# **How to receive Stripe webhooks on localhost**

Often when building an application that integrates with 3rd party services we need a way to receive webhooks

![stripe webhooks](https://webhookrelay.com/blog/receiving-stripe-webhooks-localhost/images/blog/stripe-webhooks-localhost/stripe-webhooks-flow.svg)

In recent years [Stripe](https://stripe.com/) has become a major payments provider. It is loved by managers and developers for a reason. Stripe has easy to use APIs, SDKs in multiple languages and outstanding documentation. Like other payment platforms, Stripe utilizes webhooks to inform about customer, subscription, card and many other changes in the state.

While this strategy works great in production, during development it can be tricky to receive these webhooks, especially when webhooks are critical for building subscription (or recurring payment) based systems where your backend system needs to track subscription status.

In this article, we will:

- Build a simple application to handle several Stripe webhooks that indicate subscription change.
- We will use `relay forward` command to receive webhooks on localhost.
- We will use Stripe's webhooks testings dashboard to simulate subscription change events.

## [Prerequisites](#prerequisites)

This post/guide assumes that you have:

- Stripe account. You can register at [https://dashboard.stripe.com/register](https://dashboard.stripe.com/register).
- Webhook Relay account. You can register at [https://my.webhookrelay.com/register](https://my.webhookrelay.com/register).
- Relay CLI, installation instructions can be found [here](https://docs.webhookrelay.com/installation-options/installation-options/install-cli).
- Golang environment, installation instructions can be found here: [https://golang.org/doc/install](https://golang.org/doc/install).

## [Building application](#building-application)

There are many libraries available for Stripe. You can find a maintained list on Stripe's documentation page here: [https://stripe.com/docs/libraries](https://stripe.com/docs/libraries).

Our sample app is written in Go. Application source is pretty straightforward. There is only one handler to receive webhooks (documentation on how to use webhooks is here: [https://stripe.com/docs/webhooks](https://stripe.com/docs/webhooks)), validate signature and print to the terminal customer ID and current subscription status:

```
package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"

    stripe "github.com/stripe/stripe-go"
    "github.com/stripe/stripe-go/webhook"
)

// port - default port to start application on
const port = ":8090"

// webhook event types
const (
    stripeEventTypeSubscriptionUpdated string = "customer.subscription.updated"
    // canceled subscription
    stripeEventTypeSubscriptionDeleted string = "customer.subscription.deleted"
    // card deletion event
    stripeEventTypeSourceDeleted string = "customer.source.deleted"
)

func validateSignature(payload []byte, header, secret string) (stripe.Event, error) {
    return webhook.ConstructEvent(payload, header, secret)
}

func main() {
    secret := os.Getenv("SIGNING_SECRET")
    if secret == "" {
        fmt.Println("SIGNING_SECRET env variable is required")
        os.Exit(1)
    }

    // preparing HTTP server
    srv := &http.Server{Addr: port, Handler: http.DefaultServeMux}

    // incoming stripe webhook handler
    http.HandleFunc("/stripe", func(resp http.ResponseWriter, req *http.Request) {
        body, err := ioutil.ReadAll(req.Body)
        if err != nil {
            resp.WriteHeader(http.StatusBadRequest)
            return
        }

        // validating signature
        event, err := validateSignature(body, req.Header.Get("Stripe-Signature"), secret)
        if err != nil {
            resp.WriteHeader(http.StatusBadRequest)
            fmt.Printf("Failed to validate signature: %s", err)
            return
        }

        switch event.Type {
        case stripeEventTypeSubscriptionUpdated, stripeEventTypeSubscriptionDeleted:
            // subscription status change
            customerID, ok := event.Data.Obj["customer"].(string)
            if !ok {
                fmt.Println("customer key missing from event.Data.Obj")
                return
            }

            subStatus, ok := event.Data.Obj["status"].(string)
            if !ok {
                fmt.Println("status key missing from event.Data.Obj")
                return
            }

            fmt.Printf("customer %s subscription updated, current status: %s \n", customerID, subStatus)
        case stripeEventTypeSourceDeleted:
            customerID, ok := event.Data.Obj["customer"].(string)
            if !ok {
                fmt.Println("customer key missing from event.Data.Obj")
                return
            }
            fmt.Printf("card deleted for customer %s \n", customerID)
        }
    })

    fmt.Printf("Receiving Stripe webhooks on http://localhost%s/stripe \n", port)
    // starting server
    err := srv.ListenAndServe()

    if err != http.ErrServerClosed {
        log.Fatalf("listen: %s\n", err)
    }
}
```

Source code can be found here: [https://github.com/webhookrelay/stripe-webhook-demo](https://github.com/webhookrelay/stripe-webhook-demo).

To install and run it, in your Go working environment you can just do:

```
go get github.com/webhookrelay/stripe-webhook-demo
cd $GOPATH/src/github.com/webhookrelay/stripe-webhook-demo/
go install
```

Our application will expect Stripe webhooks on [http://localhost:8090/stripe](http://localhost:8090/stripe).

## [Receiving webhooks on localhost](#receiving-webhooks-on-localhost)

To start receiving webhooks on localhost, we will use **relay** CLI:

```
relay forward --bucket stripe http://localhost:8090/stripe
```

flag _--bucket stripe_ is optional but helps a lot when we restart relay CLI as it reuses the same public endpoint.

Output of the command should display your _my.webhookrelay.com/v1/webhooks/{id here}_ public endpoint:

```
relay forward --bucket stripe http://localhost:8090/stripe
Forwarding:
https://my.webhookrelay.com/v1/webhooks/d52caf28-d7ce-1e90-b9e3-36294f1dca74 -> http://localhost:8090/stripe
```

## [Testing webhooks via Stripe](#testing-webhooks-via-stripe)

![getting webhooks on localhost](https://webhookrelay.com/blog/receiving-stripe-webhooks-localhost/images/blog/stripe-webhooks-localhost/relay-forward-stripe.gif)

Let's go to our Stripe dashboard, API webhooks section ([https://dashboard.stripe.com/account/webhooks](https://dashboard.stripe.com/account/webhooks)) and:

1. Add an endpoint with your unique Webhook Relay URL.
2. Get a signing secret, set it for our stripe-webhook-demo application, and launch it:```
$ export SIGNING_SECRET=whsec_********************************
$ stripe-webhook-demo
Receiving Stripe webhooks on http://localhost:8090/stripe
```
3. Click on "Send test webhook", select `customer.subscription.updated` and send it.
4. View the stripe-webhook-demo output. It should display a customer ID and subscription status:```
$ stripe-webhook-demo
Receiving Stripe webhooks on http://localhost:8090/stripe
customer cus_00000000000000 subscription updated, current status: active
```

## [Wrapping up](#wrapping-up)

In this post we created a sample application that can receive webhooks from Stripe. We used `relay forward` command to receive webhooks on localhost and checked out Stripe's webhook testing dashboard to speed up development.