Skip to content

Instantly share code, notes, and snippets.

@adrienemery
Last active October 26, 2019 07:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adrienemery/0506f487da101a0e81e77c6e860cb502 to your computer and use it in GitHub Desktop.
Save adrienemery/0506f487da101a0e81e77c6e860cb502 to your computer and use it in GitHub Desktop.
LND "PayPI" (charge for api using lightning network)
"""
This app is an example of how you could charge for access to an API using
the lightning network and LND. The example uses a JWT token that grants 1-hour access
(the default jwt token expirey) but many different payment schemes could be
imagined (bulk payments/pay per call etc).
But for now I just wanted to show how simple it could be to add a paywall to an api service.
To use this api:
1. Make a POST request to get a new token and invoice at `/token`
2. Pay the invoice that comes along with the token on the lightning network
3. Make a GET request to `/payme`
"""
import os
import binascii
from functools import wraps
import grpc
import rpc_pb2 as ln
import rpc_pb2_grpc as lnrpc
from flask import Flask, jsonify, request
from flask_jwt_simple import JWTManager, jwt_required, create_jwt, get_jwt_identity
os.environ["GRPC_SSL_CIPHER_SUITES"] = 'HIGH+ECDSA'
app = Flask(__name__)
# Setup the Flask-JWT-Simple extension
JWT_SECRET_KEY = os.getenv('JWT_SECRET_KEY')
app.config['JWT_SECRET_KEY'] = JWT_SECRET_KEY
jwt = JWTManager(app)
# Setup the grpc stub
with open(os.path.expanduser('~/Library/Application Support/Lnd/tls.cert'), 'rb') as f:
cert = f.read()
with open(os.path.expanduser('~/Library/Application Support/Lnd/admin.macaroon'), 'rb') as f:
macaroon_bytes = f.read()
macaroon_hex = binascii.hexlify(macaroon_bytes).decode()
creds = grpc.ssl_channel_credentials(cert)
channel = grpc.secure_channel('127.0.0.1:10009', creds)
stub = lnrpc.LightningStub(channel)
def invoice_paid(fn):
"""Decorator to ensure invoice has been paid
Raises:
HTTP 402 Error if payment not settled
"""
@wraps(fn)
def wrapper(*args, **kwargs):
r_hash_str = get_jwt_identity()
rpc_request = ln.PaymentHash(r_hash_str=r_hash_str)
invoice = stub.LookupInvoice(rpc_request, metadata=[('macaroon', macaroon_hex)])
if invoice.settled is False:
return jsonify({"msg": "Payment required"}), 402
return fn(*args, **kwargs)
return wrapper
@app.route('/token', methods=['POST'])
def obtain_token():
"""Create a JWT with the r_hash of a lightning invoice encoded as the indentity
"""
# geneate a lightning invoice
rpc_request = ln.Invoice(value=100, memo='api access')
response = stub.AddInvoice(rpc_request, metadata=[('macaroon', macaroon_hex)])
r_hash_str = binascii.hexlify(response.r_hash).decode()
# use the r_hash as the itentity
data = {
'jwt': create_jwt(identity=r_hash_str),
'invoice': response.payment_request
}
return jsonify(data), 200
@app.route('/payme', methods=['GET'])
@jwt_required
@invoice_paid
def protected():
return jsonify({'msg': "You Paid!"}), 200
if __name__ == '__main__':
app.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment