#!/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 multiprocessing import qrcode from flask import Flask, render_template from flask_bootstrap import Bootstrap from flask_wtf import FlaskForm from io import BytesIO from pyln.client import Plugin from random import random 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/