mirror of
https://github.com/lnbits/lnbits-legend.git
synced 2025-01-19 05:33:47 +01:00
732 lines
24 KiB
Python
732 lines
24 KiB
Python
import uuid
|
|
import os
|
|
import json
|
|
import requests
|
|
|
|
from flask import Flask, jsonify, render_template, request, redirect, url_for
|
|
from lnurl import Lnurl, LnurlWithdrawResponse, encode
|
|
from datetime import datetime
|
|
|
|
from . import bolt11
|
|
from .db import Database, ExtDatabase, FauDatabase
|
|
from .helpers import megajson
|
|
from .settings import LNBITS_PATH, WALLET, DEFAULT_USER_WALLET_NAME, FEE_RESERVE
|
|
|
|
|
|
app = Flask(__name__)
|
|
app.jinja_env.filters["megajson"] = megajson
|
|
|
|
|
|
@app.before_first_request
|
|
def init():
|
|
with Database() as db:
|
|
with open(os.path.join(LNBITS_PATH, "data", "schema.sql")) as schemafile:
|
|
for stmt in schemafile.read().split(";\n\n"):
|
|
db.execute(stmt, [])
|
|
|
|
@app.route("/")
|
|
def home():
|
|
return render_template("index.html")
|
|
|
|
|
|
@app.route("/deletewallet")
|
|
def deletewallet():
|
|
user_id = request.args.get("usr")
|
|
wallet_id = request.args.get("wal")
|
|
|
|
with Database() as db:
|
|
db.execute(
|
|
"""
|
|
UPDATE wallets AS w
|
|
SET
|
|
user = 'del:' || w.user,
|
|
adminkey = 'del:' || w.adminkey,
|
|
inkey = 'del:' || w.inkey
|
|
WHERE id = ? AND user = ?
|
|
""",
|
|
(wallet_id, user_id),
|
|
)
|
|
|
|
next_wallet = db.fetchone("SELECT id FROM wallets WHERE user = ?", (user_id,))
|
|
|
|
if next_wallet:
|
|
return redirect(url_for("wallet", usr=user_id, wal=next_wallet[0]))
|
|
|
|
return redirect(url_for("home"))
|
|
|
|
|
|
@app.route("/lnurl")
|
|
def lnurl():
|
|
lnurl = request.args.get("lightning")
|
|
return render_template("lnurl.html", lnurl=lnurl)
|
|
|
|
|
|
@app.route("/lnurlwallet")
|
|
def lnurlwallet():
|
|
lnurl = Lnurl(request.args.get("lightning"))
|
|
r = requests.get(lnurl.url)
|
|
if not r.ok:
|
|
return redirect(url_for("home"))
|
|
|
|
data = json.loads(r.text)
|
|
if data.get("status") == "ERROR":
|
|
return redirect(url_for("home"))
|
|
|
|
withdraw_res = LnurlWithdrawResponse(**data)
|
|
|
|
_, pay_hash, pay_req = WALLET.create_invoice(withdraw_res.max_sats, "LNbits lnurl funding")
|
|
|
|
r = requests.get(
|
|
withdraw_res.callback.base,
|
|
params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": pay_req}},
|
|
)
|
|
|
|
if not r.ok:
|
|
return redirect(url_for("home"))
|
|
data = json.loads(r.text)
|
|
|
|
for i in range(10):
|
|
r = WALLET.get_invoice_status(pay_hash).raw_response
|
|
if not r.ok:
|
|
continue
|
|
|
|
data = r.json()
|
|
break
|
|
|
|
with Database() as db:
|
|
wallet_id = uuid.uuid4().hex
|
|
user_id = uuid.uuid4().hex
|
|
wallet_name = DEFAULT_USER_WALLET_NAME
|
|
adminkey = uuid.uuid4().hex
|
|
inkey = uuid.uuid4().hex
|
|
|
|
db.execute("INSERT INTO accounts (id) VALUES (?)", (user_id,))
|
|
db.execute(
|
|
"INSERT INTO wallets (id, name, user, adminkey, inkey) VALUES (?, ?, ?, ?, ?)",
|
|
(wallet_id, wallet_name, user_id, adminkey, inkey),
|
|
)
|
|
db.execute(
|
|
"INSERT INTO apipayments (payhash, amount, wallet, pending, memo) VALUES (?, ?, ?, 0, ?)",
|
|
(pay_hash, withdraw_res.max_sats * 1000, wallet_id, "LNbits lnurl funding",),
|
|
)
|
|
|
|
return redirect(url_for("wallet", usr=user_id, wal=wallet_id))
|
|
|
|
|
|
@app.route("/wallet")
|
|
def wallet():
|
|
usr = request.args.get("usr")
|
|
wallet_id = request.args.get("wal")
|
|
wallet_name = request.args.get("nme")
|
|
|
|
if usr:
|
|
if not len(usr) > 20:
|
|
return redirect(url_for("home"))
|
|
if wallet_id:
|
|
if not len(wallet_id) > 20:
|
|
return redirect(url_for("home"))
|
|
|
|
# just usr: return a the first user wallet or create one if none found
|
|
# usr and wallet_id: return that wallet or create it if it doesn't exist
|
|
# usr, wallet_id and wallet_name: same as above, but use the specified name
|
|
# usr and wallet_name: generate a wallet_id and create
|
|
# wallet_id and wallet_name: create a user, then move an existing wallet or create
|
|
# just wallet_name: create a user, then generate a wallet_id and create
|
|
# nothing: create everything
|
|
|
|
with Database() as db:
|
|
# ensure this user exists
|
|
# -------------------------------
|
|
|
|
if not usr:
|
|
usr = uuid.uuid4().hex
|
|
return redirect(url_for("wallet", usr=usr, wal=wallet_id, nme=wallet_name))
|
|
|
|
db.execute(
|
|
"""
|
|
INSERT OR IGNORE INTO accounts (id) VALUES (?)
|
|
""",
|
|
(usr,),
|
|
)
|
|
|
|
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (usr,))
|
|
|
|
if not wallet_id:
|
|
if user_wallets and not wallet_name:
|
|
# fetch the first wallet from this user
|
|
# -------------------------------------
|
|
wallet_id = user_wallets[0]["id"]
|
|
else:
|
|
# create for this user
|
|
# --------------------
|
|
wallet_name = wallet_name or DEFAULT_USER_WALLET_NAME
|
|
wallet_id = uuid.uuid4().hex
|
|
db.execute(
|
|
"""
|
|
INSERT INTO wallets (id, name, user, adminkey, inkey)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
""",
|
|
(wallet_id, wallet_name, usr, uuid.uuid4().hex, uuid.uuid4().hex),
|
|
)
|
|
|
|
return redirect(url_for("wallet", usr=usr, wal=wallet_id, nme=wallet_name))
|
|
|
|
# if wallet_id is given, try to move it to this user or create
|
|
# ------------------------------------------------------------
|
|
db.execute(
|
|
"""
|
|
INSERT OR REPLACE INTO wallets (id, user, name, adminkey, inkey)
|
|
VALUES (?, ?,
|
|
coalesce((SELECT name FROM wallets WHERE id = ?), ?),
|
|
coalesce((SELECT adminkey FROM wallets WHERE id = ?), ?),
|
|
coalesce((SELECT inkey FROM wallets WHERE id = ?), ?)
|
|
)
|
|
""",
|
|
(
|
|
wallet_id,
|
|
usr,
|
|
wallet_id,
|
|
wallet_name or DEFAULT_USER_WALLET_NAME,
|
|
wallet_id,
|
|
uuid.uuid4().hex,
|
|
wallet_id,
|
|
uuid.uuid4().hex,
|
|
),
|
|
)
|
|
|
|
# finally, get the wallet with balance and transactions
|
|
# -----------------------------------------------------
|
|
|
|
wallet = db.fetchone(
|
|
"""
|
|
SELECT
|
|
coalesce((SELECT balance/1000 FROM balances WHERE wallet = wallets.id), 0) * ? AS balance,
|
|
*
|
|
FROM wallets
|
|
WHERE user = ? AND id = ?
|
|
""",
|
|
(1 - FEE_RESERVE, usr, wallet_id),
|
|
)
|
|
|
|
transactions = db.fetchall(
|
|
"""
|
|
SELECT *
|
|
FROM apipayments
|
|
WHERE wallet = ? AND pending = 0
|
|
ORDER BY time
|
|
""",
|
|
(wallet_id,),
|
|
)
|
|
|
|
return render_template(
|
|
"wallet.html", user_wallets=user_wallets, wallet=wallet, user=usr, transactions=transactions,
|
|
)
|
|
|
|
|
|
@app.route("/v1/invoices", methods=["GET", "POST"])
|
|
def api_invoices():
|
|
if request.headers["Content-Type"] != "application/json":
|
|
return jsonify({"ERROR": "MUST BE JSON"}), 400
|
|
|
|
postedjson = request.json
|
|
|
|
if "value" not in postedjson:
|
|
return jsonify({"ERROR": "NO VALUE"}), 400
|
|
|
|
if not postedjson["value"].isdigit():
|
|
return jsonify({"ERROR": "VALUE MUST BE A NUMBER"}), 400
|
|
|
|
if int(postedjson["value"]) < 0:
|
|
return jsonify({"ERROR": "AMOUNTLESS INVOICES NOT SUPPORTED"}), 400
|
|
|
|
if "memo" not in postedjson:
|
|
return jsonify({"ERROR": "NO MEMO"}), 400
|
|
|
|
with Database() as db:
|
|
wallet = db.fetchone(
|
|
"SELECT id FROM wallets WHERE inkey = ? OR adminkey = ?",
|
|
(request.headers["Grpc-Metadata-macaroon"], request.headers["Grpc-Metadata-macaroon"],),
|
|
)
|
|
|
|
if not wallet:
|
|
return jsonify({"ERROR": "NO KEY"}), 200
|
|
|
|
r, pay_hash, pay_req = WALLET.create_invoice(postedjson["value"], postedjson["memo"])
|
|
|
|
if not r.ok or "error" in r.json():
|
|
return jsonify({"ERROR": "UNEXPECTED BACKEND ERROR"}), 500
|
|
|
|
amount_msat = int(postedjson["value"]) * 1000
|
|
|
|
db.execute(
|
|
"INSERT INTO apipayments (payhash, amount, wallet, pending, memo) VALUES (?, ?, ?, 1, ?)",
|
|
(pay_hash, amount_msat, wallet["id"], postedjson["memo"],),
|
|
)
|
|
|
|
return jsonify({"pay_req": pay_req, "payment_hash": pay_hash}), 200
|
|
|
|
|
|
@app.route("/v1/channels/transactions", methods=["GET", "POST"])
|
|
def api_transactions():
|
|
|
|
if request.headers["Content-Type"] != "application/json":
|
|
return jsonify({"ERROR": "MUST BE JSON"}), 400
|
|
|
|
data = request.json
|
|
|
|
print(data)
|
|
if "payment_request" not in data:
|
|
return jsonify({"ERROR": "NO PAY REQ"}), 400
|
|
|
|
with Database() as db:
|
|
wallet = db.fetchone("SELECT id FROM wallets WHERE adminkey = ?", (request.headers["Grpc-Metadata-macaroon"],))
|
|
|
|
if not wallet:
|
|
return jsonify({"ERROR": "BAD AUTH"}), 401
|
|
|
|
# decode the invoice
|
|
invoice = bolt11.decode(data["payment_request"])
|
|
if invoice.amount_msat == 0:
|
|
return jsonify({"ERROR": "AMOUNTLESS INVOICES NOT SUPPORTED"}), 400
|
|
|
|
# insert the payment
|
|
db.execute(
|
|
"INSERT OR IGNORE INTO apipayments (payhash, amount, fee, wallet, pending, memo) VALUES (?, ?, ?, ?, 1, ?)",
|
|
(
|
|
invoice.payment_hash,
|
|
-int(invoice.amount_msat),
|
|
-int(invoice.amount_msat) * FEE_RESERVE,
|
|
wallet["id"],
|
|
invoice.description,
|
|
),
|
|
)
|
|
|
|
# check balance
|
|
balance = db.fetchone("SELECT balance/1000 FROM balances WHERE wallet = ?", (wallet["id"],))[0]
|
|
if balance < 0:
|
|
db.execute("DELETE FROM apipayments WHERE payhash = ? AND wallet = ?", (invoice.payment_hash, wallet["id"]))
|
|
return jsonify({"ERROR": "INSUFFICIENT BALANCE"}), 403
|
|
|
|
# check if the invoice is an internal one
|
|
if db.fetchone("SELECT count(*) FROM apipayments WHERE payhash = ?", (invoice.payment_hash,))[0] == 2:
|
|
# internal. mark both sides as fulfilled.
|
|
db.execute("UPDATE apipayments SET pending = 0, fee = 0 WHERE payhash = ?", (invoice.payment_hash,))
|
|
else:
|
|
# actually send the payment
|
|
r = WALLET.pay_invoice(data["payment_request"])
|
|
|
|
if not r.raw_response.ok or r.failed:
|
|
return jsonify({"ERROR": "UNEXPECTED PAYMENT ERROR"}), 500
|
|
|
|
# payment went through, not pending anymore, save actual fees
|
|
db.execute(
|
|
"UPDATE apipayments SET pending = 0, fee = ? WHERE payhash = ? AND wallet = ?",
|
|
(r.fee_msat, invoice.payment_hash, wallet["id"]),
|
|
)
|
|
|
|
return jsonify({"PAID": "TRUE", "payment_hash": invoice.payment_hash}), 200
|
|
|
|
|
|
@app.route("/v1/invoice/<payhash>", methods=["GET"])
|
|
def api_checkinvoice(payhash):
|
|
if request.headers["Content-Type"] != "application/json":
|
|
return jsonify({"ERROR": "MUST BE JSON"}), 400
|
|
|
|
with Database() as db:
|
|
payment = db.fetchone(
|
|
"""
|
|
SELECT pending
|
|
FROM apipayments
|
|
INNER JOIN wallets AS w ON apipayments.wallet = w.id
|
|
WHERE payhash = ?
|
|
AND (w.adminkey = ? OR w.inkey = ?)
|
|
""",
|
|
(payhash, request.headers["Grpc-Metadata-macaroon"], request.headers["Grpc-Metadata-macaroon"]),
|
|
)
|
|
|
|
if not payment:
|
|
return jsonify({"ERROR": "NO INVOICE"}), 404
|
|
|
|
if not payment["pending"]: # pending
|
|
return jsonify({"PAID": "TRUE"}), 200
|
|
|
|
if not WALLET.get_invoice_status(payhash).settled:
|
|
return jsonify({"PAID": "FALSE"}), 200
|
|
|
|
db.execute("UPDATE apipayments SET pending = 0 WHERE payhash = ?", (payhash,))
|
|
return jsonify({"PAID": "TRUE"}), 200
|
|
|
|
|
|
@app.route("/v1/payment/<payhash>", methods=["GET"])
|
|
def api_checkpayment(payhash):
|
|
if request.headers["Content-Type"] != "application/json":
|
|
return jsonify({"ERROR": "MUST BE JSON"}), 400
|
|
|
|
with Database() as db:
|
|
payment = db.fetchone(
|
|
"""
|
|
SELECT pending
|
|
FROM apipayments
|
|
INNER JOIN wallets AS w ON apipayments.wallet = w.id
|
|
WHERE payhash = ?
|
|
AND (w.adminkey = ? OR w.inkey = ?)
|
|
""",
|
|
(payhash, request.headers["Grpc-Metadata-macaroon"], request.headers["Grpc-Metadata-macaroon"]),
|
|
)
|
|
|
|
if not payment:
|
|
return jsonify({"ERROR": "NO INVOICE"}), 404
|
|
|
|
if not payment["pending"]: # pending
|
|
return jsonify({"PAID": "TRUE"}), 200
|
|
|
|
if not WALLET.get_payment_status(payhash).settled:
|
|
return jsonify({"PAID": "FALSE"}), 200
|
|
|
|
db.execute("UPDATE apipayments SET pending = 0 WHERE payhash = ?", (payhash,))
|
|
return jsonify({"PAID": "TRUE"}), 200
|
|
|
|
|
|
@app.route("/v1/checkpending", methods=["POST"])
|
|
def api_checkpending():
|
|
with Database() as db:
|
|
for pendingtx in db.fetchall(
|
|
"""
|
|
SELECT
|
|
payhash,
|
|
CASE
|
|
WHEN amount < 0 THEN 'send'
|
|
ELSE 'recv'
|
|
END AS kind
|
|
FROM apipayments
|
|
INNER JOIN wallets ON apipayments.wallet = wallets.id
|
|
WHERE time > strftime('%s', 'now') - 86400
|
|
AND pending = 1
|
|
AND (adminkey = ? OR inkey = ?)
|
|
""",
|
|
(request.headers["Grpc-Metadata-macaroon"], request.headers["Grpc-Metadata-macaroon"]),
|
|
):
|
|
payhash = pendingtx["payhash"]
|
|
kind = pendingtx["kind"]
|
|
|
|
if kind == "send":
|
|
payment_complete = WALLET.get_payment_status(payhash).settled
|
|
if payment_complete:
|
|
db.execute("UPDATE apipayments SET pending = 0 WHERE payhash = ?", (payhash,))
|
|
elif payment_complete is False:
|
|
db.execute("DELETE FROM apipayments WHERE payhash = ?", (payhash,))
|
|
|
|
elif kind == "recv" and WALLET.get_invoice_status(payhash).settled:
|
|
db.execute("UPDATE apipayments SET pending = 0 WHERE payhash = ?", (payhash,))
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
|
###########EXTENSIONS STUFF - ADD TO LNFAUCET FOLDER IF POSS
|
|
|
|
# Checks DB to see if the extensions are activated or not activated for the user
|
|
@app.route("/extensions")
|
|
def extensions():
|
|
usr = request.args.get("usr")
|
|
lnevents = request.args.get("lnevents")
|
|
lnjoust = request.args.get("lnjoust")
|
|
withdraw = request.args.get("withdraw")
|
|
if usr:
|
|
if not len(usr) > 20:
|
|
return redirect(url_for("home"))
|
|
|
|
with Database() as db:
|
|
|
|
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (usr,))
|
|
|
|
with ExtDatabase() as Extdd:
|
|
user_ext = Extdd.fetchall("SELECT * FROM overview WHERE user = ?", (usr,))
|
|
if not user_ext:
|
|
Extdd.execute(
|
|
"""
|
|
INSERT OR IGNORE INTO overview (user) VALUES (?)
|
|
""",
|
|
(usr,),
|
|
)
|
|
return redirect(url_for("extensions", usr=usr))
|
|
|
|
if lnevents:
|
|
if int(lnevents) != user_ext[0][1] and int(lnevents) < 2:
|
|
Extdd.execute("UPDATE overview SET lnevents = ? WHERE user = ?", (int(lnevents),usr,))
|
|
user_ext = Extdd.fetchall("SELECT * FROM overview WHERE user = ?", (usr,))
|
|
if lnjoust:
|
|
if int(lnjoust) != user_ext[0][2] and int(lnjoust) < 2:
|
|
Extdd.execute("UPDATE overview SET lnjoust = ? WHERE user = ?", (int(lnjoust),usr,))
|
|
user_ext = Extdd.fetchall("SELECT * FROM overview WHERE user = ?", (usr,))
|
|
if withdraw:
|
|
if int(withdraw) != user_ext[0][3] and int(withdraw) < 2:
|
|
Extdd.execute("UPDATE overview SET withdraw = ? WHERE user = ?", (int(withdraw),usr,))
|
|
user_ext = Extdd.fetchall("SELECT * FROM overview WHERE user = ?", (usr,))
|
|
|
|
return render_template(
|
|
"extensions.html", user_wallets=user_wallets, user=usr, user_ext=user_ext
|
|
)
|
|
|
|
|
|
# Main withdraw link page
|
|
@app.route("/withdraw")
|
|
def withdraw():
|
|
usr = request.args.get("usr")
|
|
fauc = request.args.get("fauc")
|
|
|
|
if usr:
|
|
if not len(usr) > 20:
|
|
return redirect(url_for("home"))
|
|
|
|
#Get all the data
|
|
with Database() as db:
|
|
user_wallets = db.fetchall("SELECT * FROM wallets WHERE user = ?", (usr,))
|
|
|
|
with ExtDatabase() as Extdd:
|
|
user_ext = Extdd.fetchall("SELECT * FROM overview WHERE user = ?", (usr,))
|
|
|
|
with FauDatabase() as Faudb:
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE usr = ?", (usr,))
|
|
|
|
#If del is selected by user from withdraw page, the withdraw link is to be deleted
|
|
faudel = request.args.get("del")
|
|
if faudel:
|
|
Faudb.execute("DELETE FROM withdraws WHERE uni = ?", (faudel,))
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE usr = ?", (usr,))
|
|
|
|
return render_template(
|
|
"withdraw.html", user_wallets=user_wallets, user=usr, user_ext=user_ext, user_fau=user_fau
|
|
)
|
|
|
|
|
|
#Returns encoded LNURL if web url and parameter gieven
|
|
@app.route("/v1/lnurlencode/<urlstr>/<parstr>", methods=["GET"])
|
|
def api_lnurlencode(urlstr, parstr):
|
|
|
|
if not urlstr:
|
|
return jsonify({"STATUS": "FALSE"}), 200
|
|
|
|
with FauDatabase() as Faudb:
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE uni = ?", (parstr,))
|
|
randar = user_fau[0][15].split(",")
|
|
randar = randar[:-1]
|
|
|
|
#If "Unique links" selected get correct rand, if not there is only one rand
|
|
if user_fau[0][12] > 0:
|
|
rand = randar[user_fau[0][10]-1]
|
|
else:
|
|
rand = randar[0]
|
|
|
|
lnurlstr = encode( "https://" + urlstr + "/v1/lnurlfetch/" + urlstr + "/" + parstr + "/" + rand)
|
|
|
|
return jsonify({"STATUS": "TRUE", "LNURL": lnurlstr}), 200
|
|
|
|
|
|
#Returns LNURL json
|
|
@app.route("/v1/lnurlfetch/<urlstr>/<parstr>/<rand>", methods=["GET"])
|
|
def api_lnurlfetch(parstr, urlstr, rand):
|
|
|
|
if not parstr:
|
|
return jsonify({"STATUS": "FALSE", "ERROR": "NO WALL ID"}), 200
|
|
|
|
if not urlstr:
|
|
|
|
return jsonify({"STATUS": "FALSE", "ERROR": "NO URL"}), 200
|
|
|
|
with FauDatabase() as Faudb:
|
|
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE uni = ?", (parstr,))
|
|
print(user_fau[0][0])
|
|
|
|
k1str = uuid.uuid4().hex
|
|
|
|
Faudb.execute("UPDATE withdraws SET withdrawals = ? WHERE uni = ?", (k1str ,parstr,))
|
|
|
|
res = LnurlWithdrawResponse(
|
|
callback='https://' + urlstr + '/v1/lnurlwithdraw/' + rand + '/',
|
|
k1=k1str,
|
|
min_withdrawable=user_fau[0][8]*1000,
|
|
max_withdrawable=user_fau[0][7]*1000,
|
|
default_description="LNURL withdraw",
|
|
)
|
|
print("res")
|
|
return res.json(), 200
|
|
|
|
|
|
|
|
#Pays invoice if passed k1 invoice and rand
|
|
@app.route("/v1/lnurlwithdraw/<rand>/", methods=["GET"])
|
|
def api_lnurlwithdraw(rand):
|
|
k1 = request.args.get("k1")
|
|
pr = request.args.get("pr")
|
|
print(rand)
|
|
|
|
|
|
if not k1:
|
|
return jsonify({"STATUS": "FALSE", "ERROR": "NO k1"}), 200
|
|
|
|
if not pr:
|
|
return jsonify({"STATUS": "FALSE", "ERROR": "NO PR"}), 200
|
|
|
|
with FauDatabase() as Faudb:
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE withdrawals = ?", (k1,))
|
|
|
|
if not user_fau:
|
|
return jsonify({"status":"ERROR", "reason":"NO AUTH"}), 400
|
|
|
|
if user_fau[0][10] <1:
|
|
return jsonify({"status":"ERROR", "reason":"withdraw SPENT"}), 400
|
|
|
|
# Check withdraw time
|
|
dt = datetime.now()
|
|
seconds = dt.timestamp()
|
|
secspast = seconds - user_fau[0][14]
|
|
print(secspast)
|
|
|
|
if secspast < user_fau[0][11]:
|
|
return jsonify({"status":"ERROR", "reason":"WAIT " + str(int(secspast)) + "s"}), 400
|
|
|
|
randar = user_fau[0][15].split(",")
|
|
if rand not in randar:
|
|
return jsonify({"status":"ERROR", "reason":"BAD AUTH"}), 400
|
|
if len(randar) > 1:
|
|
randar.remove(rand)
|
|
randstr = ','.join(randar)
|
|
|
|
# Update time
|
|
Faudb.execute("UPDATE withdraws SET tmestmp = ? AND rand = ? WHERE withdrawals = ?", (seconds , randstr, k1,))
|
|
|
|
header = {'Content-Type': 'application/json','Grpc-Metadata-macaroon':str(user_fau[0][4])}
|
|
|
|
data = {'payment_request': pr}
|
|
|
|
r = requests.post(url = "https://zapped.ngrok.io/v1/channels/transactions", headers=header, data=json.dumps(data))
|
|
|
|
r_json=r.json()
|
|
if "ERROR" in r_json:
|
|
return jsonify({"status":"ERROR", "reason":r_json["ERROR"]}), 400
|
|
|
|
with FauDatabase() as Faudb:
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE withdrawals = ?", (k1,))
|
|
|
|
upinc = user_fau[0][10] - 1
|
|
Faudb.execute("UPDATE withdraws SET inc = ? WHERE withdrawals = ?", (upinc ,k1,))
|
|
|
|
return jsonify({"status":"OK"}), 200
|
|
|
|
|
|
|
|
@app.route("/withdrawmaker", methods=["GET", "POST"])
|
|
def withdrawmaker():
|
|
data = request.json
|
|
amt = data["amt"]
|
|
tit = data["tit"]
|
|
wal = data["wal"]
|
|
minamt = data["minamt"]
|
|
maxamt = data["maxamt"]
|
|
tme = data["tme"]
|
|
uniq = data["uniq"]
|
|
usr = data["usr"]
|
|
wall = wal.split("-")
|
|
|
|
#If id that means its a link being edited, delet the record first
|
|
if "id" in data:
|
|
unid = data["id"].split("-")
|
|
uni = unid[1]
|
|
print(data["id"])
|
|
print(uni)
|
|
with FauDatabase() as Faudb:
|
|
Faudb.execute("DELETE FROM withdraws WHERE uni = ?", (unid[1],))
|
|
else:
|
|
uni = uuid.uuid4().hex
|
|
|
|
# Randomiser for random QR option
|
|
rand = ""
|
|
if uniq > 0:
|
|
for x in range(0,int(amt)):
|
|
rand += uuid.uuid4().hex[0:5] + ","
|
|
else:
|
|
rand = uuid.uuid4().hex[0:5] + ","
|
|
|
|
with Database() as dbb:
|
|
user_wallets = dbb.fetchall("SELECT * FROM wallets WHERE user = ? AND id = ?", (usr,wall[1],))
|
|
if not user_wallets:
|
|
return jsonify({"ERROR": "NO WALLET USER"}), 401
|
|
|
|
# Get time
|
|
dt = datetime.now()
|
|
seconds = dt.timestamp()
|
|
print(seconds)
|
|
|
|
#Add to DB
|
|
with FauDatabase() as db:
|
|
db.execute(
|
|
"INSERT OR IGNORE INTO withdraws (usr, wal, walnme, adm, uni, tit, maxamt, minamt, spent, inc, tme, uniq, withdrawals, tmestmp, rand) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
(
|
|
usr,
|
|
wall[1],
|
|
user_wallets[0][1],
|
|
user_wallets[0][3],
|
|
uni,
|
|
tit,
|
|
maxamt,
|
|
minamt,
|
|
0,
|
|
amt,
|
|
tme,
|
|
uniq,
|
|
0,
|
|
seconds,
|
|
rand,
|
|
),
|
|
)
|
|
|
|
#Get updated records
|
|
with ExtDatabase() as Extdd:
|
|
user_ext = Extdd.fetchall("SELECT * FROM overview WHERE user = ?", (usr,))
|
|
if not user_ext:
|
|
return jsonify({"ERROR": "NO WALLET USER"}), 401
|
|
|
|
with FauDatabase() as Faudb:
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE usr = ?", (usr,))
|
|
if not user_fau:
|
|
return jsonify({"ERROR": "NO WALLET USER"}), 401
|
|
|
|
|
|
return render_template(
|
|
"withdraw.html", user_wallets=user_wallets, user=usr, user_ext=user_ext, user_fau=user_fau
|
|
)
|
|
|
|
|
|
#Simple shareable link
|
|
@app.route("/displaywithdraw", methods=["GET", "POST"])
|
|
def displaywithdraw():
|
|
fauid = request.args.get("id")
|
|
|
|
with FauDatabase() as Faudb:
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE uni = ?", (fauid,))
|
|
|
|
return render_template(
|
|
"displaywithdraw.html", user_fau=user_fau,
|
|
)
|
|
|
|
#Simple printable page of links
|
|
@app.route("/printwithdraw/<urlstr>/", methods=["GET", "POST"])
|
|
def printwithdraw(urlstr):
|
|
fauid = request.args.get("id")
|
|
|
|
with FauDatabase() as Faudb:
|
|
user_fau = Faudb.fetchall("SELECT * FROM withdraws WHERE uni = ?", (fauid,))
|
|
randar = user_fau[0][15].split(",")
|
|
randar = randar[:-1]
|
|
lnurlar = []
|
|
print(len(randar))
|
|
for d in range(len(randar)):
|
|
lnurlar.append( encode("https://"+ urlstr +"/v1/lnurlfetch/" + urlstr + "/" + fauid + "/" + randar[d]))
|
|
|
|
return render_template(
|
|
"printwithdraws.html", lnurlar=lnurlar, user_fau=user_fau[0],
|
|
)
|