#!/usr/bin/env python3 """ A small donation service so that users can request ln invoices This plugin spins up a small flask server that provides a form to users who wish to donate some money to the owner of the lightning node. The server can run on an arbitrary port and returns an invoice. Also a list of previously paid invoices (only those that used this service) will be displayed. Displaying paid invoices could be made optionally in a future version. Author: Rene Pickhardt (https://ln.rene-pickhardt.de) you can see a demo of the plugin (and leave a tip) directly at: https://ln.rene-pickhardt.de/donation LICENSE: MIT / APACHE """ import base64 import json import multiprocessing import qrcode import sys from flask import Flask, render_template from flask_bootstrap import Bootstrap from flask_wtf import FlaskForm from io import BytesIO from lightning import LightningRpc, Plugin from os.path import join from random import random from time import time from wtforms import StringField, SubmitField, IntegerField from wtforms.validators import Required, NumberRange plugin = Plugin() class DonationForm(FlaskForm): """Form for donations """ amount = IntegerField("Enter how many Satoshis you want to donate!", validators=[Required(), NumberRange(min=1, max=16666666)]) description = StringField("Leave a comment (displayed publically)") submit = SubmitField('Donate') def make_base64_qr_code(bolt11): qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_H, box_size=4, border=4, ) qr.add_data(bolt11) qr.make(fit=True) img = qr.make_image() buffered = BytesIO() img.save(buffered, format="PNG") img_str = base64.b64encode(buffered.getvalue()).decode("utf-8") return img_str def ajax(label): global plugin msg = plugin.rpc.listinvoices(label)["invoices"][0] if msg["status"] == "paid": return "Your donation has been received and is well appricated." return "waiting" def donation_form(): global plugin form = DonationForm() b11 = None qr = None label = None if form.validate_on_submit(): amount = form.amount.data description = form.description.data label = "ln-plugin-donation-{}".format(random()) invoice = plugin.rpc.invoice( int(amount)*1000, label, description) b11 = invoice["bolt11"] qr = make_base64_qr_code(b11) invoices = plugin.rpc.listinvoices()["invoices"] donations = [] for invoice in invoices: if invoice["label"].startswith("ln-plugin-donation-"): # FIXME: change to paid after debugging if invoice["status"] == "paid": bolt11 = plugin.rpc.decodepay(invoice["bolt11"]) satoshis = int(bolt11["msatoshi"])//1000 description = bolt11["description"] ts = bolt11["created_at"] donations.append((ts, satoshis, description)) if b11 is not None: return render_template("donation.html", donations=sorted(donations, reverse=True), form=form, bolt11=b11, qr=qr, label=label) else: return render_template("donation.html", donations=sorted(donations, reverse=True), form=form) def worker(port): app = Flask(__name__) # FIXME: use hexlified hsm secret or something else app.config['SECRET_KEY'] = 'you-will-never-guess-this' app.add_url_rule('/donation', 'donation', donation_form, methods=["GET", "POST"]) app.add_url_rule('/is_invoice_paid/