Converted some core stuff

This commit is contained in:
Ben Arc
2021-08-20 21:31:01 +01:00
parent bdbdc1601b
commit c4b37c6508
4 changed files with 99 additions and 157 deletions

View File

@@ -6,11 +6,11 @@ from ecdsa import SECP256k1, SigningKey # type: ignore
from lnurl import encode as lnurl_encode # type: ignore from lnurl import encode as lnurl_encode # type: ignore
from typing import List, NamedTuple, Optional, Dict from typing import List, NamedTuple, Optional, Dict
from sqlite3 import Row from sqlite3 import Row
from pydantic import BaseModel
from lnbits.settings import WALLET from lnbits.settings import WALLET
class User(NamedTuple): class User(BaseModel):
id: str id: str
email: str email: str
extensions: List[str] = [] extensions: List[str] = []
@@ -26,7 +26,7 @@ class User(NamedTuple):
return w[0] if w else None return w[0] if w else None
class Wallet(NamedTuple): class Wallet(BaseModel):
id: str id: str
name: str name: str
user: str user: str
@@ -73,7 +73,7 @@ class Wallet(NamedTuple):
return await get_wallet_payment(self.id, payment_hash) return await get_wallet_payment(self.id, payment_hash)
class Payment(NamedTuple): class Payment(BaseModel):
checking_id: str checking_id: str
pending: bool pending: bool
amount: int amount: int
@@ -161,7 +161,7 @@ class Payment(NamedTuple):
await delete_payment(self.checking_id) await delete_payment(self.checking_id)
class BalanceCheck(NamedTuple): class BalanceCheck(BaseModel):
wallet: str wallet: str
service: str service: str
url: str url: str

View File

@@ -67,45 +67,30 @@ async def api_payments():
HTTPStatus.OK, HTTPStatus.OK,
) )
class CreateInvoiceData(BaseModel):
amount: int = Query(None, ge=1)
memo: str = None
unit: Optional[str] = None
description_hash: str = None
lnurl_callback: Optional[str] = None
lnurl_balance_check: Optional[str] = None
extra: Optional[dict] = None
webhook: Optional[str] = None
@api_check_wallet_key("invoice") @api_check_wallet_key("invoice")
@api_validate_post_request(
schema={
"amount": {"type": "number", "min": 0.001, "required": True},
"memo": {
"type": "string",
"empty": False,
"required": True,
"excludes": "description_hash",
},
"unit": {"type": "string", "empty": False, "required": False},
"description_hash": {
"type": "string",
"empty": False,
"required": True,
"excludes": "memo",
},
"lnurl_callback": {"type": "string", "nullable": True, "required": False},
"lnurl_balance_check": {"type": "string", "required": False},
"extra": {"type": "dict", "nullable": True, "required": False},
"webhook": {"type": "string", "empty": False, "required": False},
}
)
# async def api_payments_create_invoice(amount: List[str] = Query([type: str = Query(None)])): # async def api_payments_create_invoice(amount: List[str] = Query([type: str = Query(None)])):
async def api_payments_create_invoice(data: CreateInvoiceData):
if "description_hash" in data:
async def api_payments_create_invoice(memo: Union[None, constr(min_length=1)], amount: int): description_hash = unhexlify(data.description_hash)
if "description_hash" in g.data:
description_hash = unhexlify(g.data["description_hash"])
memo = "" memo = ""
else: else:
description_hash = b"" description_hash = b""
memo = g.data["memo"] memo = data.memo
if g.data.get("unit") or "sat" == "sat": if data.unit or "sat" == "sat":
amount = g.data["amount"] amount = data.amount
else: else:
price_in_sats = await fiat_amount_as_satoshis(g.data["amount"], g.data["unit"]) price_in_sats = await fiat_amount_as_satoshis(data.amount, data.unit)
amount = price_in_sats amount = price_in_sats
async with db.connect() as conn: async with db.connect() as conn:
@@ -115,31 +100,31 @@ async def api_payments_create_invoice(memo: Union[None, constr(min_length=1)], a
amount=amount, amount=amount,
memo=memo, memo=memo,
description_hash=description_hash, description_hash=description_hash,
extra=g.data.get("extra"), extra=data.extra,
webhook=g.data.get("webhook"), webhook=data.webhook,
conn=conn, conn=conn,
) )
except InvoiceFailure as e: except InvoiceFailure as e:
return jsonable_encoder({"message": str(e)}), 520 return {"message": str(e)}, 520
except Exception as exc: except Exception as exc:
raise exc raise exc
invoice = bolt11.decode(payment_request) invoice = bolt11.decode(payment_request)
lnurl_response: Union[None, bool, str] = None lnurl_response: Union[None, bool, str] = None
if g.data.get("lnurl_callback"): if data.lnurl_callback:
if "lnurl_balance_check" in g.data: if "lnurl_balance_check" in g.data:
save_balance_check(g.wallet.id, g.data["lnurl_balance_check"]) save_balance_check(g.wallet.id, data.lnurl_balance_check)
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
try: try:
r = await client.get( r = await client.get(
g.data["lnurl_callback"], data.lnurl_callback,
params={ params={
"pr": payment_request, "pr": payment_request,
"balanceNotify": url_for( "balanceNotify": url_for(
"core.lnurl_balance_notify", "core.lnurl_balance_notify",
service=urlparse(g.data["lnurl_callback"]).netloc, service=urlparse(data.lnurl_callback).netloc,
wal=g.wallet.id, wal=g.wallet.id,
_external=True, _external=True,
), ),
@@ -158,15 +143,13 @@ async def api_payments_create_invoice(memo: Union[None, constr(min_length=1)], a
lnurl_response = False lnurl_response = False
return ( return (
jsonable_encoder(
{ {
"payment_hash": invoice.payment_hash, "payment_hash": invoice.payment_hash,
"payment_request": payment_request, "payment_request": payment_request,
# maintain backwards compatibility with API clients: # maintain backwards compatibility with API clients:
"checking_id": invoice.payment_hash, "checking_id": invoice.payment_hash,
"lnurl_response": lnurl_response, "lnurl_response": lnurl_response,
} },
),
HTTPStatus.CREATED, HTTPStatus.CREATED,
) )
@@ -181,97 +164,76 @@ async def api_payments_pay_invoice(
payment_request=bolt11, payment_request=bolt11,
) )
except ValueError as e: except ValueError as e:
return jsonable_encoder({"message": str(e)}), HTTPStatus.BAD_REQUEST return {"message": str(e)}, HTTPStatus.BAD_REQUEST
except PermissionError as e: except PermissionError as e:
return jsonable_encoder({"message": str(e)}), HTTPStatus.FORBIDDEN return {"message": str(e)}, HTTPStatus.FORBIDDEN
except PaymentFailure as e: except PaymentFailure as e:
return jsonable_encoder({"message": str(e)}), 520 return {"message": str(e)}, 520
except Exception as exc: except Exception as exc:
raise exc raise exc
return ( return (
jsonable_encoder(
{ {
"payment_hash": payment_hash, "payment_hash": payment_hash,
# maintain backwards compatibility with API clients: # maintain backwards compatibility with API clients:
"checking_id": payment_hash, "checking_id": payment_hash,
} },
),
HTTPStatus.CREATED, HTTPStatus.CREATED,
) )
@core_app.route("/api/v1/payments", methods=["POST"]) @core_app.post("/api/v1/payments")
@api_validate_post_request(schema={"out": {"type": "boolean", "required": True}}) async def api_payments_create(out: bool = True):
async def api_payments_create(): if out is True:
if g.data["out"] is True:
return await api_payments_pay_invoice() return await api_payments_pay_invoice()
return await api_payments_create_invoice() return await api_payments_create_invoice()
class CreateLNURLData(BaseModel):
description_hash: str
callback: str
amount: int
comment: Optional[str] = None
description: Optional[str] = None
@core_app.route("/api/v1/payments/lnurl", methods=["POST"]) @core_app.post("/api/v1/payments/lnurl")
@api_check_wallet_key("admin") @api_check_wallet_key("admin")
@api_validate_post_request( async def api_payments_pay_lnurl(data: CreateLNURLData):
schema={ domain = urlparse(data.callback).netloc
"description_hash": {"type": "string", "empty": False, "required": True},
"callback": {"type": "string", "empty": False, "required": True},
"amount": {"type": "number", "empty": False, "required": True},
"comment": {
"type": "string",
"nullable": True,
"empty": True,
"required": False,
},
"description": {
"type": "string",
"nullable": True,
"empty": True,
"required": False,
},
}
)
async def api_payments_pay_lnurl():
domain = urlparse(g.data["callback"]).netloc
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
try: try:
r = await client.get( r = await client.get(
g.data["callback"], data.callback,
params={"amount": g.data["amount"], "comment": g.data["comment"]}, params={"amount": data.amount, "comment": data.comment},
timeout=40, timeout=40,
) )
if r.is_error: if r.is_error:
raise httpx.ConnectError raise httpx.ConnectError
except (httpx.ConnectError, httpx.RequestError): except (httpx.ConnectError, httpx.RequestError):
return ( return (
jsonify({"message": f"Failed to connect to {domain}."}), {"message": f"Failed to connect to {domain}."},
HTTPStatus.BAD_REQUEST, HTTPStatus.BAD_REQUEST,
) )
params = json.loads(r.text) params = json.loads(r.text)
if params.get("status") == "ERROR": if params.get("status") == "ERROR":
return ( return ({"message": f"{domain} said: '{params.get('reason', '')}'"},
jsonify({"message": f"{domain} said: '{params.get('reason', '')}'"}),
HTTPStatus.BAD_REQUEST, HTTPStatus.BAD_REQUEST,
) )
invoice = bolt11.decode(params["pr"]) invoice = bolt11.decode(params["pr"])
if invoice.amount_msat != g.data["amount"]: if invoice.amount_msat != data.amount:
return ( return (
jsonify(
{ {
"message": f"{domain} returned an invalid invoice. Expected {g.data['amount']} msat, got {invoice.amount_msat}." "message": f"{domain} returned an invalid invoice. Expected {g.data['amount']} msat, got {invoice.amount_msat}."
} },
),
HTTPStatus.BAD_REQUEST, HTTPStatus.BAD_REQUEST,
) )
if invoice.description_hash != g.data["description_hash"]: if invoice.description_hash != g.data["description_hash"]:
return ( return (
jsonify(
{ {
"message": f"{domain} returned an invalid invoice. Expected description_hash == {g.data['description_hash']}, got {invoice.description_hash}." "message": f"{domain} returned an invalid invoice. Expected description_hash == {g.data['description_hash']}, got {invoice.description_hash}."
} },
),
HTTPStatus.BAD_REQUEST, HTTPStatus.BAD_REQUEST,
) )
@@ -279,51 +241,49 @@ async def api_payments_pay_lnurl():
if params.get("successAction"): if params.get("successAction"):
extra["success_action"] = params["successAction"] extra["success_action"] = params["successAction"]
if g.data["comment"]: if data.comment:
extra["comment"] = g.data["comment"] extra["comment"] = data.comment
payment_hash = await pay_invoice( payment_hash = await pay_invoice(
wallet_id=g.wallet.id, wallet_id=g.wallet.id,
payment_request=params["pr"], payment_request=params["pr"],
description=g.data.get("description", ""), description=data.description,
extra=extra, extra=extra,
) )
return ( return (
jsonify(
{ {
"success_action": params.get("successAction"), "success_action": params.get("successAction"),
"payment_hash": payment_hash, "payment_hash": payment_hash,
# maintain backwards compatibility with API clients: # maintain backwards compatibility with API clients:
"checking_id": payment_hash, "checking_id": payment_hash,
} },
),
HTTPStatus.CREATED, HTTPStatus.CREATED,
) )
@core_app.route("/api/v1/payments/<payment_hash>", methods=["GET"]) @core_app.get("/api/v1/payments/<payment_hash>")
@api_check_wallet_key("invoice") @api_check_wallet_key("invoice")
async def api_payment(payment_hash): async def api_payment(payment_hash):
payment = await g.wallet.get_payment(payment_hash) payment = await g.wallet.get_payment(payment_hash)
if not payment: if not payment:
return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND return {"message": "Payment does not exist."}, HTTPStatus.NOT_FOUND
elif not payment.pending: elif not payment.pending:
return jsonify({"paid": True, "preimage": payment.preimage}), HTTPStatus.OK return {"paid": True, "preimage": payment.preimage}, HTTPStatus.OK
try: try:
await payment.check_pending() await payment.check_pending()
except Exception: except Exception:
return jsonify({"paid": False}), HTTPStatus.OK return {"paid": False}, HTTPStatus.OK
return ( return (
jsonify({"paid": not payment.pending, "preimage": payment.preimage}), {"paid": not payment.pending, "preimage": payment.preimage},
HTTPStatus.OK, HTTPStatus.OK,
) )
@core_app.route("/api/v1/payments/sse", methods=["GET"]) @core_app.get("/api/v1/payments/sse")
@api_check_wallet_key("invoice", accept_querystring=True) @api_check_wallet_key("invoice", accept_querystring=True)
async def api_payments_sse(): async def api_payments_sse():
this_wallet_id = g.wallet.id this_wallet_id = g.wallet.id
@@ -376,7 +336,7 @@ async def api_payments_sse():
return response return response
@core_app.route("/api/v1/lnurlscan/<code>", methods=["GET"]) @core_app.get("/api/v1/lnurlscan/<code>")
@api_check_wallet_key("invoice") @api_check_wallet_key("invoice")
async def api_lnurlscan(code: str): async def api_lnurlscan(code: str):
try: try:
@@ -395,7 +355,7 @@ async def api_lnurlscan(code: str):
) )
# will proceed with these values # will proceed with these values
else: else:
return jsonify({"message": "invalid lnurl"}), HTTPStatus.BAD_REQUEST return {"message": "invalid lnurl"}, HTTPStatus.BAD_REQUEST
# params is what will be returned to the client # params is what will be returned to the client
params: Dict = {"domain": domain} params: Dict = {"domain": domain}
@@ -411,7 +371,7 @@ async def api_lnurlscan(code: str):
r = await client.get(url, timeout=5) r = await client.get(url, timeout=5)
if r.is_error: if r.is_error:
return ( return (
jsonify({"domain": domain, "message": "failed to get parameters"}), {"domain": domain, "message": "failed to get parameters"},
HTTPStatus.SERVICE_UNAVAILABLE, HTTPStatus.SERVICE_UNAVAILABLE,
) )
@@ -419,12 +379,10 @@ async def api_lnurlscan(code: str):
data = json.loads(r.text) data = json.loads(r.text)
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
return ( return (
jsonify(
{ {
"domain": domain, "domain": domain,
"message": f"got invalid response '{r.text[:200]}'", "message": f"got invalid response '{r.text[:200]}'",
} },
),
HTTPStatus.SERVICE_UNAVAILABLE, HTTPStatus.SERVICE_UNAVAILABLE,
) )
@@ -432,9 +390,7 @@ async def api_lnurlscan(code: str):
tag = data["tag"] tag = data["tag"]
if tag == "channelRequest": if tag == "channelRequest":
return ( return (
jsonify( {"domain": domain, "kind": "channel", "message": "unsupported"},
{"domain": domain, "kind": "channel", "message": "unsupported"}
),
HTTPStatus.BAD_REQUEST, HTTPStatus.BAD_REQUEST,
) )
@@ -481,32 +437,24 @@ async def api_lnurlscan(code: str):
params.update(commentAllowed=data.get("commentAllowed", 0)) params.update(commentAllowed=data.get("commentAllowed", 0))
except KeyError as exc: except KeyError as exc:
return ( return (
jsonify(
{ {
"domain": domain, "domain": domain,
"message": f"lnurl JSON response invalid: {exc}", "message": f"lnurl JSON response invalid: {exc}",
} },
),
HTTPStatus.SERVICE_UNAVAILABLE, HTTPStatus.SERVICE_UNAVAILABLE,
) )
return params
return jsonify(params)
@core_app.route("/api/v1/lnurlauth", methods=["POST"]) @core_app.post("/api/v1/lnurlauth", methods=["POST"])
@api_check_wallet_key("admin") @api_check_wallet_key("admin")
@api_validate_post_request( async def api_perform_lnurlauth(callback: str):
schema={ err = await perform_lnurlauth(callback)
"callback": {"type": "string", "required": True},
}
)
async def api_perform_lnurlauth():
err = await perform_lnurlauth(g.data["callback"])
if err: if err:
return jsonify({"reason": err.reason}), HTTPStatus.SERVICE_UNAVAILABLE return {"reason": err.reason}, HTTPStatus.SERVICE_UNAVAILABLE
return "", HTTPStatus.OK return "", HTTPStatus.OK
@core_app.route("/api/v1/currencies", methods=["GET"]) @core_app.route("/api/v1/currencies", methods=["GET"])
async def api_list_currencies_available(): async def api_list_currencies_available():
return jsonify(list(currencies.keys())) return list(currencies.keys())

View File

@@ -4,7 +4,6 @@ from quart import (
g, g,
current_app, current_app,
abort, abort,
jsonify,
request, request,
redirect, redirect,
render_template, render_template,
@@ -28,21 +27,21 @@ from ..crud import (
from ..services import redeem_lnurl_withdraw, pay_invoice from ..services import redeem_lnurl_withdraw, pay_invoice
@core_app.route("/favicon.ico") @core_app.get("/favicon.ico")
async def favicon(): async def favicon():
return await send_from_directory( return await send_from_directory(
path.join(core_app.root_path, "static"), "favicon.ico" path.join(core_app.root_path, "static"), "favicon.ico"
) )
@core_app.route("/") @core_app.get("/")
async def home(): async def home():
return await render_template( return await render_template(
"core/index.html", lnurl=request.args.get("lightning", None) "core/index.html", lnurl=request.args.get("lightning", None)
) )
@core_app.route("/extensions") @core_app.get("/extensions")
@validate_uuids(["usr"], required=True) @validate_uuids(["usr"], required=True)
@check_user_exists() @check_user_exists()
async def extensions(): async def extensions():
@@ -66,7 +65,7 @@ async def extensions():
return await render_template("core/extensions.html", user=await get_user(g.user.id)) return await render_template("core/extensions.html", user=await get_user(g.user.id))
@core_app.route("/wallet") @core_app.get("/wallet")
@validate_uuids(["usr", "wal"]) @validate_uuids(["usr", "wal"])
async def wallet(): async def wallet():
user_id = request.args.get("usr", type=str) user_id = request.args.get("usr", type=str)
@@ -108,19 +107,18 @@ async def wallet():
) )
@core_app.route("/withdraw") @core_app.get("/withdraw")
@validate_uuids(["usr", "wal"], required=True) @validate_uuids(["usr", "wal"], required=True)
async def lnurl_full_withdraw(): async def lnurl_full_withdraw():
user = await get_user(request.args.get("usr")) user = await get_user(request.args.get("usr"))
if not user: if not user:
return jsonify({"status": "ERROR", "reason": "User does not exist."}) return {"status": "ERROR", "reason": "User does not exist."}
wallet = user.get_wallet(request.args.get("wal")) wallet = user.get_wallet(request.args.get("wal"))
if not wallet: if not wallet:
return jsonify({"status": "ERROR", "reason": "Wallet does not exist."}) return{"status": "ERROR", "reason": "Wallet does not exist."}
return jsonify( return {
{
"tag": "withdrawRequest", "tag": "withdrawRequest",
"callback": url_for( "callback": url_for(
"core.lnurl_full_withdraw_callback", "core.lnurl_full_withdraw_callback",
@@ -136,19 +134,18 @@ async def lnurl_full_withdraw():
"core.lnurl_full_withdraw", usr=user.id, wal=wallet.id, _external=True "core.lnurl_full_withdraw", usr=user.id, wal=wallet.id, _external=True
), ),
} }
)
@core_app.route("/withdraw/cb") @core_app.get("/withdraw/cb")
@validate_uuids(["usr", "wal"], required=True) @validate_uuids(["usr", "wal"], required=True)
async def lnurl_full_withdraw_callback(): async def lnurl_full_withdraw_callback():
user = await get_user(request.args.get("usr")) user = await get_user(request.args.get("usr"))
if not user: if not user:
return jsonify({"status": "ERROR", "reason": "User does not exist."}) return {"status": "ERROR", "reason": "User does not exist."}
wallet = user.get_wallet(request.args.get("wal")) wallet = user.get_wallet(request.args.get("wal"))
if not wallet: if not wallet:
return jsonify({"status": "ERROR", "reason": "Wallet does not exist."}) return {"status": "ERROR", "reason": "Wallet does not exist."}
pr = request.args.get("pr") pr = request.args.get("pr")
@@ -164,10 +161,10 @@ async def lnurl_full_withdraw_callback():
if balance_notify: if balance_notify:
await save_balance_notify(wallet.id, balance_notify) await save_balance_notify(wallet.id, balance_notify)
return jsonify({"status": "OK"}) return {"status": "OK"}
@core_app.route("/deletewallet") @core_app.get("/deletewallet")
@validate_uuids(["usr", "wal"], required=True) @validate_uuids(["usr", "wal"], required=True)
@check_user_exists() @check_user_exists()
async def deletewallet(): async def deletewallet():
@@ -186,7 +183,7 @@ async def deletewallet():
return redirect(url_for("core.home")) return redirect(url_for("core.home"))
@core_app.route("/withdraw/notify/<service>") @core_app.get("/withdraw/notify/<service>")
@validate_uuids(["wal"], required=True) @validate_uuids(["wal"], required=True)
async def lnurl_balance_notify(service: str): async def lnurl_balance_notify(service: str):
bc = await get_balance_check(request.args.get("wal"), service) bc = await get_balance_check(request.args.get("wal"), service)
@@ -194,7 +191,7 @@ async def lnurl_balance_notify(service: str):
redeem_lnurl_withdraw(bc.wallet, bc.url) redeem_lnurl_withdraw(bc.wallet, bc.url)
@core_app.route("/lnurlwallet") @core_app.get("/lnurlwallet")
async def lnurlwallet(): async def lnurlwallet():
async with db.connect() as conn: async with db.connect() as conn:
account = await create_account(conn=conn) account = await create_account(conn=conn)
@@ -213,14 +210,13 @@ async def lnurlwallet():
return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id)) return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
@core_app.route("/manifest/<usr>.webmanifest") @core_app.get("/manifest/<usr>.webmanifest")
async def manifest(usr: str): async def manifest(usr: str):
user = await get_user(usr) user = await get_user(usr)
if not user: if not user:
return "", HTTPStatus.NOT_FOUND return "", HTTPStatus.NOT_FOUND
return jsonify( return {
{
"short_name": "LNbits", "short_name": "LNbits",
"name": "LNbits Wallet", "name": "LNbits Wallet",
"icons": [ "icons": [
@@ -244,6 +240,4 @@ async def manifest(usr: str):
"url": "/wallet?usr=" + usr + "&wal=" + wallet.id, "url": "/wallet?usr=" + usr + "&wal=" + wallet.id,
} }
for wallet in user.wallets for wallet in user.wallets
], ],}
}
)

View File

@@ -10,22 +10,22 @@ from ..crud import get_standalone_payment
from ..tasks import api_invoice_listeners from ..tasks import api_invoice_listeners
@core_app.route("/public/v1/payment/<payment_hash>", methods=["GET"]) @core_app.get("/public/v1/payment/<payment_hash>")
async def api_public_payment_longpolling(payment_hash): async def api_public_payment_longpolling(payment_hash):
payment = await get_standalone_payment(payment_hash) payment = await get_standalone_payment(payment_hash)
if not payment: if not payment:
return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND return {"message": "Payment does not exist."}, HTTPStatus.NOT_FOUND
elif not payment.pending: elif not payment.pending:
return jsonify({"status": "paid"}), HTTPStatus.OK return {"status": "paid"}, HTTPStatus.OK
try: try:
invoice = bolt11.decode(payment.bolt11) invoice = bolt11.decode(payment.bolt11)
expiration = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) expiration = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry)
if expiration < datetime.datetime.now(): if expiration < datetime.datetime.now():
return jsonify({"status": "expired"}), HTTPStatus.OK return {"status": "expired"}, HTTPStatus.OK
except: except:
return jsonify({"message": "Invalid bolt11 invoice."}), HTTPStatus.BAD_REQUEST return {"message": "Invalid bolt11 invoice."}, HTTPStatus.BAD_REQUEST
send_payment, receive_payment = trio.open_memory_channel(0) send_payment, receive_payment = trio.open_memory_channel(0)
@@ -38,7 +38,7 @@ async def api_public_payment_longpolling(payment_hash):
async for payment in receive_payment: async for payment in receive_payment:
if payment.payment_hash == payment_hash: if payment.payment_hash == payment_hash:
nonlocal response nonlocal response
response = (jsonify({"status": "paid"}), HTTPStatus.OK) response = ({"status": "paid"}, HTTPStatus.OK)
cancel_scope.cancel() cancel_scope.cancel()
async def timeouter(cancel_scope): async def timeouter(cancel_scope):
@@ -52,4 +52,4 @@ async def api_public_payment_longpolling(payment_hash):
if response: if response:
return response return response
else: else:
return jsonify({"message": "timeout"}), HTTPStatus.REQUEST_TIMEOUT return {"message": "timeout"}, HTTPStatus.REQUEST_TIMEOUT