Last active
October 26, 2019 07:34
-
-
Save adrienemery/0506f487da101a0e81e77c6e860cb502 to your computer and use it in GitHub Desktop.
LND "PayPI" (charge for api using lightning network)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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