ln-paywall

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2018 License: MPL-2.0

README

ln-paywall

GoDoc Build Status Go Report Card GitHub Releases

Go middleware for monetizing your API on a per-request basis with Bitcoin and Lightning ⚡️

Middlewares for:

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.

Contents

Purpose

Until the rise of cryptocurrencies, if you wanted to monetize your API (set up a paywall), you had to:

  1. Use a centralized service (like PayPal)
    • Can shut you down any time
    • High fees
    • Your API users need an account
    • Can be hacked
  2. Keep track of your API users (keep accounts and their API keys in some database)
    • Privacy concerns
    • Data breaches / leaks
  3. Charge for a bunch of requests, like 10.000 at a time, because real per-request payments weren't possible

With cryptocurrencies in general some of those problems were solved, but with long confirmation times and 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 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.

How it works

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:

  1. The first request gets rejected with the 402 Payment Required HTTP status, a Content-Type: application/vnd.lightning.bolt11 header and a Lightning (BOLT-11-conforming) invoice in the body
  2. The second request must contain a X-Preimage header with the preimage of the paid Lightning invoice. 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.

Prerequisites

There are currently two prerequisites:

  1. A running lnd node which listens to gRPC connections
    • If you don't run it locally, it needs to listen to connections from external machines (so for example on 0.0.0.0 instead of localhost) and has the TLS certificate configured to include the external IP address of the node.
  2. A supported storage mechanism. It's used to cache preimages that have been used as a payment for an API call, so that a user can't do multiple requests with the same preimage of a settled Lightning payment. The pay package currently provides factory functions for the following storages:
    • Redis
      • Run for example with Docker: docker run -d redis
        • Note: In production you should use a configuration with password!
    • A simple Go map
      • Disadvantage: Doesn't persist data, so when you restart your server, users can re-use old preimages
    • Roll your own!
      • Just implement the simple ln.StorageClient interface (only two methods!)

Usage

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".

net/http HandlerFunc

To show how to chain the middleware, this example includes a logging middleware as well.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/philippgille/ln-paywall/pay"
)

func pongHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "pong")
}

func withLogging(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Printf("Logged connection from %s", r.RemoteAddr)
		next.ServeHTTP(w, r)
	}
}

func main() {
	// Configure middleware
	invoiceOptions := pay.InvoiceOptions{
		Amount: 1,
		Memo:   "Ping API call",
	}
	lndOptions := pay.LNDoptions{
		Address:      "123.123.123.123:10009",
		CertFile:     "tls.cert",
		MacaroonFile: "invoice.macaroon",
	}
	redisClient := pay.NewRedisClient(pay.DefaultRedisOptions()) // Connects to localhost:6379
	// Create function that we can use in the middleware chain
	withPayment := pay.NewHandlerFuncMiddleware(invoiceOptions, lndOptions, redisClient)

	// Use a chain of middlewares for the "/ping" endpoint
	http.HandleFunc("/ping", withLogging(withPayment(pongHandler)))
	log.Fatal(http.ListenAndServe(":8080", nil))
}
Gin
package main

import (
	"github.com/gin-gonic/gin"

	"github.com/philippgille/ln-paywall/pay"
)

func main() {
	r := gin.Default()

	// Configure and use middleware
	invoiceOptions := pay.InvoiceOptions{
		Amount: 1,
		Memo:   "Ping API call",
	}
	lndOptions := pay.LNDoptions{
		Address:      "123.123.123.123:10009",
		CertFile:     "tls.cert",
		MacaroonFile: "invoice.macaroon",
	}
	redisClient := pay.NewRedisClient(pay.DefaultRedisOptions) // Connects to localhost:6379
	r.Use(pay.NewGinMiddleware(invoiceOptions, lndOptions, redisClient))

	r.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL