Go middleware for monetizing your API on a per-request basis with Bitcoin and Lightning ⚡️
Go middleware for monetizing your API on a per-request basis with Bitcoin and Lightning ⚡️
Middlewares for:
HandlerFunc
Handler
A client package exists as well to make consuming LN-paywalled APIs extremely easy (you just use it like the standard Go http.Client
and the payment handling is done in the background).
An API gateway is on the roadmap as well, which you can use to monetize your API that's written in any language, not just in Go.
Until the rise of cryptocurrencies, if you wanted to monetize your API (set up a paywall), you had to:
With cryptocurrencies in general some of those problems were solved, but with long confirmation times and high per-transaction fees a real per-request billing was still not feasable.
But then came the Lightning Network, an implementation of routed payment channels, which enables real near-instant microtransactions with extremely low fees, which cryptocurrencies have long promised, but never delivered. It's a second layer on top of existing cryptocurrencies like Bitcoin that scales far beyond the limitations of the underlying blockchain.
ln-paywall
makes it easy to set up an API paywall for payments over the Lightning Network.
With ln-paywall
you can simply use one of the provided middlewares in your Go web service to have your web service do two things:
402 Payment Required
HTTP status, a Content-Type: application/vnd.lightning.bolt11
header and a Lightning (BOLT-11-conforming) invoice in the bodyX-Preimage
header with the preimage of the paid Lightning invoice (hex encoded). The middleware checks if 1) the invoice was paid and 2) not already used for a previous request. If both preconditions are met, it continues to the next middleware or final request handler.There are currently two prerequisites:
ln
package currently provides factory functions for the following LN implementations:
docker run -d -u `id -u` -v `pwd`/data:/data -p 9112:9112 -e API_TOKEN=secret shesek/lightning-charge
wall.LNClient
interface (only two methods!)wall
package currently provides factory functions for the following storages:
docker run -d -p 6379:6379 redis
bitnami/redis
which makes that easy)!wall.StorageClient
interface (only two methods!)Get the package with go get -u github.com/philippgille/ln-paywall/...
.
We strongly encourage you to use vendoring, because as long as ln-paywall
is version 0.x
, breaking changes may be introduced in new versions, including changes to the package name / import path. The project adheres to Semantic Versioning and all notable changes to this project are documented in RELEASES.md.
The best way to see how to use ln-paywall
is by example. In the below examples we create a web service that responds to requests to /ping
with "pong", using Gin as the web framework.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/philippgille/ln-paywall/ln"
"github.com/philippgille/ln-paywall/storage"
"github.com/philippgille/ln-paywall/wall"
)
func main() {
r := gin.Default()
// Configure middleware
invoiceOptions := wall.DefaultInvoiceOptions // Price: 1 Satoshi; Memo: "API call"
lndOptions := ln.DefaultLNDoptions // Address: "localhost:10009", CertFile: "tls.cert", MacaroonFile: "invoice.macaroon"
storageClient := storage.NewGoMap() // Local in-memory cache
lnClient, err := ln.NewLNDclient(lndOptions)
if err != nil {
panic(err)
}
// Use middleware
r.Use(wall.NewGinMiddleware(invoiceOptions, lnClient, storageClient))
r.GET("/ping", func(c *gin.Context) {
c.String(http.StatusOK, "pong")
})
r.Run() // Listen and serve on 0.0.0.0:8080
}
This is just the most basic example. See the list of examples below for examples with other web frameworks / routers / just the stdlib, as well as for a more complex and useful example.
Follow the links to the example code files.
Simple examples to show the use for the different web frameworks / routers / just the stdlib:
More complex and useful example:
package main
import (
"fmt"
"io/ioutil"
"github.com/philippgille/ln-paywall/ln"
"github.com/philippgille/ln-paywall/pay"
)
func main() {
// Set up client
lndOptions := ln.LNDoptions{ // Default address: "localhost:10009", CertFile: "tls.cert"
MacaroonFile: "admin.macaroon", // admin.macaroon is required for making payments
}
lnClient, err := ln.NewLNDclient(lndOptions)
if err != nil {
panic(err)
}
client := pay.NewClient(nil, lnClient) // Uses http.DefaultClient if no http.Client is passed
// Send request to an ln-paywalled API
res, err := client.Get("http://localhost:8080/ping")
if err != nil {
panic(err)
}
defer res.Body.Close()
// Print response body
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
fmt.Println(string(resBody))
}
You can also view this example here.