def create_invoice( *, wallet_id: str, amount: int, memo: str, description_hash: Optional[bytes] = None, extra: Optional[Dict] = None, ) -> Tuple[str, str]: invoice_memo = None if description_hash else memo storeable_memo = memo ok, checking_id, payment_request, error_message = WALLET.create_invoice( amount=amount, memo=invoice_memo, description_hash=description_hash) if not ok: raise Exception(error_message or "Unexpected backend error.") invoice = bolt11.decode(payment_request) amount_msat = amount * 1000 create_payment( wallet_id=wallet_id, checking_id=checking_id, payment_request=payment_request, payment_hash=invoice.payment_hash, amount=amount_msat, memo=storeable_memo, extra=extra, ) g.db.commit() return invoice.payment_hash, payment_request
def validate_action(self, query: Dict[str, str]) -> None: tag = self.tag params = json.loads(self.params) # Perform tag-specific checks. if tag == "withdrawRequest": for field in ["pr"]: if not field in query: raise LnurlValidationError( f'Missing required parameter: "{field}"') # Check the bolt11 invoice(s) provided. pr = query["pr"] if "," in pr: raise LnurlValidationError( "Multiple payment requests not supported") try: invoice = bolt11.decode(pr) except ValueError: raise LnurlValidationError( 'Invalid parameter ("pr"): Lightning payment request expected' ) if invoice.amount_msat < params["minWithdrawable"]: raise LnurlValidationError( 'Amount in invoice must be greater than or equal to "minWithdrawable"' ) if invoice.amount_msat > params["maxWithdrawable"]: raise LnurlValidationError( 'Amount in invoice must be less than or equal to "maxWithdrawable"' ) else: raise LnurlValidationError(f'Unknown subprotocol: "{tag}"')
async def api_payments_decode(): try: if g.data["data"][:5] == "LNURL": url = lnurl.decode(g.data["data"]) return ( jsonify({"domain": url}), HTTPStatus.OK, ) else: invoice = bolt11.decode(g.data["data"]) return ( jsonify( { "payment_hash": invoice.payment_hash, "amount_msat": invoice.amount_msat, "description": invoice.description, "description_hash": invoice.description_hash, "payee": invoice.payee, "date": invoice.date, "expiry": invoice.expiry, "secret": invoice.secret, "route_hints": invoice.route_hints, "min_final_cltv_expiry": invoice.min_final_cltv_expiry, } ), HTTPStatus.OK, ) except: return jsonify({"message": "Failed to decode"}), HTTPStatus.BAD_REQUEST
def lndhub_payinvoice(): try: pay_invoice( wallet_id=g.wallet.id, payment_request=g.data["invoice"], extra={"tag": "lndhub"}, ) except Exception as e: return jsonify({ "error": True, "code": 10, "message": "Payment failed: " + str(e), }) invoice: bolt11.Invoice = bolt11.decode(g.data["invoice"]) return jsonify({ "payment_error": "", "payment_preimage": "0" * 64, "route": {}, "payment_hash": invoice.payment_hash, "decoded": decoded_as_lndhub(invoice), "fee_msat": 0, "type": "paid_invoice", "fee": 0, "value": invoice.amount_msat / 1000, "timestamp": int(time.time()), "memo": invoice.description, })
def delete_expired_invoices() -> None: rows = g.db.fetchall( """ SELECT bolt11 FROM apipayments WHERE pending = 1 AND amount > 0 AND time < strftime('%s', 'now') - 86400 """ ) for (payment_request,) in rows: try: invoice = bolt11.decode(payment_request) except: continue expiration_date = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) if expiration_date > datetime.datetime.utcnow(): continue g.db.execute( """ DELETE FROM apipayments WHERE pending = 1 AND hash = ? """, (invoice.payment_hash,), )
async def delete_expired_invoices(conn: Optional[Connection] = None, ) -> None: # first we delete all invoices older than one month await (conn or db).execute(""" DELETE FROM apipayments WHERE pending = 1 AND amount > 0 AND time < strftime('%s', 'now') - 2592000 """) # then we delete all expired invoices, checking one by one rows = await (conn or db).fetchall(""" SELECT bolt11 FROM apipayments WHERE pending = 1 AND bolt11 IS NOT NULL AND amount > 0 AND time < strftime('%s', 'now') - 86400 """) for (payment_request, ) in rows: try: invoice = bolt11.decode(payment_request) except: continue expiration_date = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) if expiration_date > datetime.datetime.utcnow(): continue await (conn or db).execute( """ DELETE FROM apipayments WHERE pending = 1 AND hash = ? """, (invoice.payment_hash, ), )
async def api_public_payment_longpolling(payment_hash): payment = await get_standalone_payment(payment_hash) if not payment: return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND elif not payment.pending: return jsonify({"status": "paid"}), HTTPStatus.OK try: invoice = bolt11.decode(payment.bolt11) expiration = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) if expiration < datetime.datetime.now(): return jsonify({"status": "expired"}), HTTPStatus.OK except: return jsonify({"message": "Invalid bolt11 invoice."}), HTTPStatus.BAD_REQUEST send_payment, receive_payment = trio.open_memory_channel(0) print("adding standalone invoice listener", payment_hash, send_payment) api_invoice_listeners.append(send_payment) async for payment in receive_payment: if payment.payment_hash == payment_hash: return jsonify({"status": "paid"}), HTTPStatus.OK
async def lndhub_addinvoice(): try: _, pr = await create_invoice( wallet_id=g.wallet.id, amount=int(g.data["amt"]), memo=g.data["memo"], extra={"tag": "lndhub"}, ) except Exception as e: return jsonify( { "error": True, "code": 7, "message": "Failed to create invoice: " + str(e), } ) invoice = bolt11.decode(pr) return jsonify( { "pay_req": pr, "payment_request": pr, "add_index": "500", "r_hash": to_buffer(invoice.payment_hash), "hash": invoice.payment_hash, } )
def GetInvoiceFromDbRecord(self, invoice, index): payment_request = invoice[6] pending = invoice[1] # IF IT IS PAID (NOT PENDING) if (pending == 0): settled = True state = ln.Invoice.InvoiceState.SETTLED amt_paid_sat = int(invoice[2] / 1000) settle_date = invoice[5] else: settled = False state = ln.Invoice.InvoiceState.OPEN amt_paid_sat = 0 settle_date = None decoded = bolt11.decode(payment_request) payment_hash = decoded.payment_hash rhash = bytes.fromhex(payment_hash) return ln.Invoice(value=int(invoice[2] / 1000), memo=invoice[4], creation_date=invoice[5], payment_request=invoice[6], r_hash=rhash, add_index=index, expiry=decoded.expiry, state=state, settled=settled, amt_paid_sat=amt_paid_sat, settle_date=settle_date)
def create_invoice(wallet_id: int, amount: int, memo: str, description_hash: Optional[bytes] = None, extra: Optional[Dict] = None, webhook: Optional[str] = None) -> Tuple[str, str]: invoice_memo = None if description_hash else memo storeable_memo = memo ok, checking_id, payment_request, error_message = WALLET.create_invoice( amount=amount, memo=invoice_memo, description_hash=description_hash) if not ok: raise Exception(error_message or "Unexpected backend error.") invoice = bolt11.decode(payment_request) amount_msat = amount * 1000 print(amount_msat) Payment.objects.create(wallet_id=wallet_id, checking_id=checking_id, payment_request=payment_request, payment_hash=invoice.payment_hash, amount=amount_msat, memo=storeable_memo, extra=json.dumps(extra) if extra and extra != {} and type(extra) is dict else None, webhook=webhook) return invoice.payment_hash, payment_request
async def api_payments_create_invoice(): if "description_hash" in g.data: description_hash = unhexlify(g.data["description_hash"]) memo = "" else: description_hash = b"" memo = g.data["memo"] try: payment_hash, payment_request = create_invoice( wallet_id=g.wallet.id, amount=g.data["amount"], memo=memo, description_hash=description_hash) except Exception as e: g.db.rollback() return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR invoice = bolt11.decode(payment_request) return ( jsonify({ "payment_hash": invoice.payment_hash, "payment_request": payment_request, # maintain backwards compatibility with API clients: "checking_id": invoice.payment_hash, }), HTTPStatus.CREATED, )
async def create_invoice( *, wallet_id: str, amount: int, # in satoshis memo: str, description_hash: Optional[bytes] = None, extra: Optional[Dict] = None, webhook: Optional[str] = None, conn: Optional[Connection] = None, ) -> Tuple[str, str]: invoice_memo = None if description_hash else memo storeable_memo = memo ok, checking_id, payment_request, error_message = await WALLET.create_invoice( amount=amount, memo=invoice_memo, description_hash=description_hash) if not ok: raise InvoiceFailure(error_message or "Unexpected backend error.") invoice = bolt11.decode(payment_request) amount_msat = amount * 1000 await create_payment( wallet_id=wallet_id, checking_id=checking_id, payment_request=payment_request, payment_hash=invoice.payment_hash, amount=amount_msat, memo=storeable_memo, extra=extra, webhook=webhook, conn=conn, ) return invoice.payment_hash, payment_request
async def api_payments_create_invoice(): if "description_hash" in g.data: description_hash = unhexlify(g.data["description_hash"]) memo = "" else: description_hash = b"" memo = g.data["memo"] try: payment_hash, payment_request = await create_invoice( wallet_id=g.wallet.id, amount=g.data["amount"], memo=memo, description_hash=description_hash, extra=g.data.get("extra"), webhook=g.data.get("webhook"), ) except Exception as exc: await db.rollback() raise exc await db.commit() invoice = bolt11.decode(payment_request) lnurl_response: Union[None, bool, str] = None if g.data.get("lnurl_callback"): async with httpx.AsyncClient() as client: try: r = await client.get( g.data["lnurl_callback"], params={"pr": payment_request}, timeout=10, ) if r.is_error: lnurl_response = r.text else: resp = json.loads(r.text) if resp["status"] != "OK": lnurl_response = resp["reason"] else: lnurl_response = True except (httpx.ConnectError, httpx.RequestError): lnurl_response = False return ( jsonify( { "payment_hash": invoice.payment_hash, "payment_request": payment_request, # maintain backwards compatibility with API clients: "checking_id": invoice.payment_hash, "lnurl_response": lnurl_response, } ), HTTPStatus.CREATED, )
def save_link_invoice(link_id: int, payment_request: str) -> None: inv = bolt11.decode(payment_request) with open_ext_db("lnurlp") as db: db.execute( """ INSERT INTO invoices (pay_link, payment_hash, expiry) VALUES (?, ?, ?) """, (link_id, inv.payment_hash, inv.expiry), )
async def lnurlwallet(): memo = "LNbits LNURL funding" try: withdraw_res = handle_lnurl(request.args.get("lightning")) if not withdraw_res.ok: abort(HTTPStatus.BAD_REQUEST, f"Could not process LNURL-withdraw: {withdraw_res.error_msg}") if not isinstance(withdraw_res, LnurlWithdrawResponse): abort(HTTPStatus.BAD_REQUEST, "Not a valid LNURL-withdraw.") except LnurlException: abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process LNURL-withdraw.") try: ok, checking_id, payment_request, error_message = WALLET.create_invoice(withdraw_res.max_sats, memo) except Exception as e: ok, error_message = False, str(e) if not ok: abort(HTTPStatus.INTERNAL_SERVER_ERROR, error_message) r = requests.get( withdraw_res.callback.base, params={**withdraw_res.callback.query_params, **{"k1": withdraw_res.k1, "pr": payment_request}}, ) if not r.ok: abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process LNURL-withdraw.") inv = bolt11.decode(payment_request) for i in range(10): invoice_status = WALLET.get_invoice_status(checking_id) sleep(i) if not invoice_status.paid: continue break user = get_user(create_account().id) wallet = create_wallet(user_id=user.id) create_payment( wallet_id=wallet.id, checking_id=checking_id, amount=withdraw_res.max_sats * 1000, memo=memo, pending=invoice_status.pending, payment_request=payment_request, payment_hash=inv.payment_hash, ) return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
def DecodePayReq(self, request, context): decoded = bolt11.decode(request.pay_req) try: pr = ln.PayReq(timestamp=int(time.time()), destination=decoded.payee, payment_hash=decoded.payment_hash, num_satoshis=int(decoded.amount_msat / 1000), expiry=decoded.expiry, description=decoded.description, description_hash=decoded.description_hash, payment_addr=bytes.fromhex(decoded.payee), features={}) except Exception as inst: print("error") print(inst) return pr
async def api_public_payment_longpolling(payment_hash): payment = await get_standalone_payment(payment_hash) if not payment: return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND elif not payment.pending: return jsonify({"status": "paid"}), HTTPStatus.OK try: invoice = bolt11.decode(payment.bolt11) expiration = datetime.datetime.fromtimestamp(invoice.date + invoice.expiry) if expiration < datetime.datetime.now(): return jsonify({"status": "expired"}), HTTPStatus.OK except: return jsonify({"message": "Invalid bolt11 invoice."}), HTTPStatus.BAD_REQUEST send_payment, receive_payment = trio.open_memory_channel(0) print("adding standalone invoice listener", payment_hash, send_payment) api_invoice_listeners.append(send_payment) response = None async def payment_info_receiver(cancel_scope): async for payment in receive_payment: if payment.payment_hash == payment_hash: nonlocal response response = (jsonify({"status": "paid"}), HTTPStatus.OK) cancel_scope.cancel() async def timeouter(cancel_scope): await trio.sleep(45) cancel_scope.cancel() async with trio.open_nursery() as nursery: nursery.start_soon(payment_info_receiver, nursery.cancel_scope) nursery.start_soon(timeouter, nursery.cancel_scope) if response: return response else: return jsonify({"message": "timeout"}), HTTPStatus.REQUEST_TIMEOUT
async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse: invoice = lnbits_bolt11.decode(bolt11) fee_limit_percent = fee_limit_msat / invoice.amount_msat * 100 payload = { "bolt11": bolt11, "maxfeepercent": "{:.11}".format(fee_limit_percent), "exemptfee": 0 # so fee_limit_percent is applied even on payments with fee under 5000 millisatoshi (which is default value of exemptfee) } try: r = self.ln.call("pay", payload) except RpcError as exc: return PaymentResponse(False, None, 0, None, str(exc)) fee_msat = r["msatoshi_sent"] - r["msatoshi"] preimage = r["payment_preimage"] return PaymentResponse(True, r["payment_hash"], fee_msat, preimage, None)
def AddInvoice(self, request, context): print("s" + request.memo + "s") memo = request.memo if not memo: memo = "new request" params = {'out': False, 'amount': request.value, 'memo': memo} invoices = self.GetInvoices(context) add_index = len(invoices) + 1 print("AddInvoice") test_url = "https://ptsv2.com/t/qu294-1600515698/post" resp = self.DoPostWithApiKey(self.add_invoice_url, params, context) #resp = self.DoPostWithApiKey(test_url, params, context) payment_request = resp["payment_request"] decoded = bolt11.decode(payment_request) payment_hash = decoded.payment_hash rhash = bytes.fromhex(payment_hash) return ln.AddInvoiceResponse( # add_index=resp["checking_id"], no tengo index en la response de lnbits # add_index=int(time.time()), add_index=add_index, r_hash=rhash, payment_request=payment_request)
def pay_invoice(wallet_id: int, payment_request: str, max_sat: Optional[int] = None, extra: Optional[Dict] = None, description: str = "") -> str: temp_id: str = f"temp_{shortuuid.uuid()}" internal_id: str = f"internal_{shortuuid.uuid()}" invoice: Invoice = bolt11.decode(payment_request) if invoice.amount_msat == 0: raise ValueError("Amountless invoices not supported.") if max_sat and invoice.amount_msat > max_sat * 1000: raise ValueError("Amount in invoice is too high.") # put all parameters that don't change here PaymentKwargs = TypedDict( "PaymentKwargs", { "wallet_id": str, "payment_request": str, "payment_hash": str, "amount": int, "memo": str, "extra": Optional[Dict], }, ) payment_kwargs: PaymentKwargs = dict(wallet_id=wallet_id, payment_request=payment_request, payment_hash=invoice.payment_hash, amount=-invoice.amount_msat, memo=description or invoice.description or "", extra=extra) try: payment = Payment.objects.get(payment_hash=invoice.payment_hash, pending=True, amount__gt=0) except Payment.DoesNotExist: payment = None if payment: Payment.objects.create(checking_id=internal_id, fee=0, pending=False, **payment_kwargs) else: fee_reserve = max(1000, int(invoice.amount_msat * 0.01)) Payment.objects.create(checking_id=temp_id, fee=-fee_reserve, **payment_kwargs) try: wallet = Wallet.objects.get(id=wallet_id) except Wallet.DoesNotExist: raise ValueError("Wallet {} not found".format(wallet_id)) if wallet.balance < 0: raise PermissionError("Insufficient balance") if payment: Payment.objects.get(checking_id=payment.checking_id, pending=False) else: ln_payment: PaymentResponse = WALLET.pay_invoice(payment_request) if ln_payment.ok and ln_payment.checking_id: Payment.objects.create(checking_id=ln_payment.checking_id, fee=ln_payment.fee_msat, preimage=ln_payment.preimage, pending=False, **payment_kwargs) Payment.objects.filter(checking_id=temp_id).delete() else: Payment.objects.filter(checking_id=temp_id).delete() raise Exception(payment.error_message or "Failed to pay_invoice on backend") return invoice.payment_hash
async def api_payments_create_invoice(): if "description_hash" in g.data: description_hash = unhexlify(g.data["description_hash"]) memo = "" else: description_hash = b"" memo = g.data["memo"] async with db.connect() as conn: try: payment_hash, payment_request = await create_invoice( wallet_id=g.wallet.id, amount=g.data["amount"], memo=memo, description_hash=description_hash, extra=g.data.get("extra"), webhook=g.data.get("webhook"), conn=conn, ) except InvoiceFailure as e: return jsonify({"message": str(e)}), 520 except Exception as exc: raise exc invoice = bolt11.decode(payment_request) lnurl_response: Union[None, bool, str] = None if g.data.get("lnurl_callback"): if "lnurl_balance_check" in g.data: save_balance_check(g.wallet.id, g.data["lnurl_balance_check"]) async with httpx.AsyncClient() as client: try: r = await client.get( g.data["lnurl_callback"], params={ "pr": payment_request, "balanceNotify": url_for( "core.lnurl_balance_notify", service=urlparse(g.data["lnurl_callback"]).netloc, wal=g.wallet.id, _external=True, ), }, timeout=10, ) if r.is_error: lnurl_response = r.text else: resp = json.loads(r.text) if resp["status"] != "OK": lnurl_response = resp["reason"] else: lnurl_response = True except (httpx.ConnectError, httpx.RequestError): lnurl_response = False return ( jsonify({ "payment_hash": invoice.payment_hash, "payment_request": payment_request, # maintain backwards compatibility with API clients: "checking_id": invoice.payment_hash, "lnurl_response": lnurl_response, }), HTTPStatus.CREATED, )
async def api_payments_pay_lnurl(): domain = urlparse(g.data["callback"]).netloc async with httpx.AsyncClient() as client: try: r = await client.get( g.data["callback"], params={ "amount": g.data["amount"], "comment": g.data["comment"] }, timeout=40, ) if r.is_error: return jsonify({"message": "failed to connect"}), HTTPStatus.BAD_REQUEST except (httpx.ConnectError, httpx.RequestError): return jsonify({"message": "failed to connect"}), HTTPStatus.BAD_REQUEST params = json.loads(r.text) if params.get("status") == "ERROR": return ( jsonify( {"message": f"{domain} said: '{params.get('reason', '')}'"}), HTTPStatus.BAD_REQUEST, ) invoice = bolt11.decode(params["pr"]) if invoice.amount_msat != g.data["amount"]: return ( jsonify({ "message": f"{domain} returned an invalid invoice. Expected {g.data['amount']} msat, got {invoice.amount_msat}." }), HTTPStatus.BAD_REQUEST, ) if invoice.description_hash != g.data["description_hash"]: return ( jsonify({ "message": f"{domain} returned an invalid invoice. Expected description_hash == {g.data['description_hash']}, got {invoice.description_hash}." }), HTTPStatus.BAD_REQUEST, ) extra = {} if params.get("successAction"): extra["success_action"] = params["successAction"] if g.data["comment"]: extra["comment"] = g.data["comment"] payment_hash = await pay_invoice( wallet_id=g.wallet.id, payment_request=params["pr"], description=g.data.get("description", ""), extra=extra, ) return ( jsonify({ "success_action": params.get("successAction"), "payment_hash": payment_hash, # maintain backwards compatibility with API clients: "checking_id": payment_hash, }), HTTPStatus.CREATED, )
def lndhub_decodeinvoice(): invoice = request.args.get("invoice") inv = bolt11.decode(invoice) return jsonify(decoded_as_lndhub(inv))
def pay_invoice(*, wallet_id: str, payment_request: str, max_sat: Optional[int] = None, extra: Optional[Dict] = None) -> str: temp_id = f"temp_{urlsafe_short_hash()}" internal_id = f"internal_{urlsafe_short_hash()}" invoice = bolt11.decode(payment_request) if invoice.amount_msat == 0: raise ValueError("Amountless invoices not supported.") if max_sat and invoice.amount_msat > max_sat * 1000: raise ValueError("Amount in invoice is too high.") # put all parameters that don't change here PaymentKwargs = TypedDict( "PaymentKwargs", { "wallet_id": str, "payment_request": str, "payment_hash": str, "amount": int, "memo": str, "extra": Optional[Dict], }, ) payment_kwargs: PaymentKwargs = dict( wallet_id=wallet_id, payment_request=payment_request, payment_hash=invoice.payment_hash, amount=-invoice.amount_msat, memo=invoice.description or "", extra=extra, ) # check_internal() returns the checking_id of the invoice we're waiting for internal = check_internal(invoice.payment_hash) if internal: # create a new payment from this wallet create_payment(checking_id=internal_id, fee=0, pending=False, **payment_kwargs) else: # create a temporary payment here so we can check if # the balance is enough in the next step fee_reserve = max(1000, int(invoice.amount_msat * 0.01)) create_payment(checking_id=temp_id, fee=-fee_reserve, **payment_kwargs) # do the balance check wallet = get_wallet(wallet_id) assert wallet if wallet.balance_msat < 0: g.db.rollback() raise PermissionError("Insufficient balance.") else: g.db.commit() if internal: # mark the invoice from the other side as not pending anymore # so the other side only has access to his new money when we are sure # the payer has enough to deduct from update_payment_status(checking_id=internal, pending=False) else: # actually pay the external invoice ok, checking_id, fee_msat, error_message = WALLET.pay_invoice( payment_request) if ok: create_payment(checking_id=checking_id, fee=fee_msat, **payment_kwargs) delete_payment(temp_id) else: raise Exception(error_message or "Failed to pay_invoice on backend.") g.db.commit() return invoice.payment_hash
async def pay_invoice( *, wallet_id: str, payment_request: str, max_sat: Optional[int] = None, extra: Optional[Dict] = None, description: str = "", conn: Optional[Connection] = None, ) -> str: invoice = bolt11.decode(payment_request) fee_reserve_msat = fee_reserve(invoice.amount_msat) async with (db.reuse_conn(conn) if conn else db.connect()) as conn: temp_id = f"temp_{urlsafe_short_hash()}" internal_id = f"internal_{urlsafe_short_hash()}" if invoice.amount_msat == 0: raise ValueError("Amountless invoices not supported.") if max_sat and invoice.amount_msat > max_sat * 1000: raise ValueError("Amount in invoice is too high.") # put all parameters that don't change here PaymentKwargs = TypedDict( "PaymentKwargs", { "wallet_id": str, "payment_request": str, "payment_hash": str, "amount": int, "memo": str, "extra": Optional[Dict], }, ) payment_kwargs: PaymentKwargs = dict( wallet_id=wallet_id, payment_request=payment_request, payment_hash=invoice.payment_hash, amount=-invoice.amount_msat, memo=description or invoice.description or "", extra=extra, ) # check_internal() returns the checking_id of the invoice we're waiting for internal_checking_id = await check_internal(invoice.payment_hash, conn=conn) if internal_checking_id: # create a new payment from this wallet await create_payment( checking_id=internal_id, fee=0, pending=False, conn=conn, **payment_kwargs, ) else: # create a temporary payment here so we can check if # the balance is enough in the next step await create_payment( checking_id=temp_id, fee=-fee_reserve_msat, conn=conn, **payment_kwargs, ) # do the balance check wallet = await get_wallet(wallet_id, conn=conn) assert wallet if wallet.balance_msat < 0: raise PermissionError("Insufficient balance.") if internal_checking_id: # mark the invoice from the other side as not pending anymore # so the other side only has access to his new money when we are sure # the payer has enough to deduct from async with db.connect() as conn: await update_payment_status( checking_id=internal_checking_id, pending=False, conn=conn, ) # notify receiver asynchronously from lnbits.tasks import internal_invoice_paid await internal_invoice_paid.send(internal_checking_id) else: # actually pay the external invoice payment: PaymentResponse = await WALLET.pay_invoice( payment_request, fee_reserve_msat) if payment.checking_id: async with db.connect() as conn: await create_payment( checking_id=payment.checking_id, fee=payment.fee_msat, preimage=payment.preimage, pending=payment.ok == None, conn=conn, **payment_kwargs, ) await delete_payment(temp_id, conn=conn) else: async with db.connect() as conn: await delete_payment(temp_id, conn=conn) raise PaymentFailure( payment.error_message or "Payment failed, but backend didn't give us an error message.") return invoice.payment_hash