async def api_payment(payment_hash): payment = g.wallet.get_payment(payment_hash) if not payment: return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND elif not payment.pending: return jsonify({"paid": True}), HTTPStatus.OK try: if payment.is_uncheckable: pass elif payment.is_out: is_paid = not WALLET.get_payment_status( payment.checking_id).pending elif payment.is_in: is_paid = not WALLET.get_invoice_status( payment.checking_id).pending except Exception: return jsonify({"paid": False}), HTTPStatus.OK if is_paid: payment.set_pending(False) return jsonify({"paid": True}), HTTPStatus.OK return jsonify({"paid": False}), HTTPStatus.OK
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.") 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, ) return redirect(url_for("core.wallet", usr=user.id, wal=wallet.id))
def check_pending(self) -> None: if self.is_uncheckable: return if self.is_out: pending = WALLET.get_payment_status(self.checking_id) else: pending = WALLET.get_invoice_status(self.checking_id) self.set_pending(pending.pending)
def api_payments(): if "check_pending" in request.args: g.wallet.delete_expired_payments() for payment in g.wallet.get_payments(include_all_pending=True): if payment.is_out: payment.set_pending(WALLET.get_payment_status(payment.checking_id).pending) else: payment.set_pending(WALLET.get_invoice_status(payment.checking_id).pending) return jsonify(g.wallet.get_payments()), Status.OK
async def api_payments(): if "check_pending" in request.args: delete_expired_invoices() for payment in g.wallet.get_payments(complete=False, pending=True, exclude_uncheckable=True): if payment.is_out: payment.set_pending( WALLET.get_payment_status(payment.checking_id).pending) else: payment.set_pending( WALLET.get_invoice_status(payment.checking_id).pending) return jsonify(g.wallet.get_payments(pending=True)), HTTPStatus.OK
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 lndhub_gettxs(): for payment in g.wallet.get_payments(complete=False, pending=True, outgoing=True, incoming=False, exclude_uncheckable=True): payment.set_pending( WALLET.get_payment_status(payment.checking_id).pending) limit = int(request.args.get("limit", 200)) return jsonify([{ "payment_preimage": payment.preimage, "payment_hash": payment.payment_hash, "fee_msat": payment.fee * 1000, "type": "paid_invoice", "fee": payment.fee, "value": int(payment.amount / 1000), "timestamp": payment.time, "memo": payment.memo if not payment.pending else "Payment in transition", } for payment in reversed( g.wallet.get_payments( pending=True, complete=True, outgoing=True, incoming=False) [:limit])])
def lndhub_getuserinvoices(): delete_expired_invoices() for invoice in g.wallet.get_payments(complete=False, pending=True, outgoing=False, incoming=True, exclude_uncheckable=True): invoice.set_pending( WALLET.get_invoice_status(invoice.checking_id).pending) limit = int(request.args.get("limit", 200)) return jsonify([{ "r_hash": to_buffer(invoice.payment_hash), "payment_request": invoice.bolt11, "add_index": "500", "description": invoice.memo, "payment_hash": invoice.payment_hash, "ispaid": not invoice.pending, "amt": int(invoice.amount / 1000), "expire_time": int(time.time() + 1800), "timestamp": invoice.time, "type": "user_invoice", } for invoice in reversed( g.wallet.get_payments( pending=True, complete=True, incoming=True, outgoing=False) [:limit])])
async def check_invoice_status(wallet_id: str, payment_hash: str) -> PaymentStatus: payment = await get_wallet_payment(wallet_id, payment_hash) if not payment: return PaymentStatus(None) return WALLET.get_invoice_status(payment.checking_id)
def create_invoice(*, wallet_id: str, amount: int, memo: str) -> Tuple[str, str]: try: ok, checking_id, payment_request, error_message = WALLET.create_invoice(amount=amount, memo=memo) except Exception as e: ok, error_message = False, str(e) if not ok: raise Exception(error_message or "Unexpected backend error.") amount_msat = amount * 1000 create_payment(wallet_id=wallet_id, checking_id=checking_id, amount=amount_msat, memo=memo) return checking_id, payment_request
def pay_invoice(*, wallet_id: str, bolt11: str, max_sat: Optional[int] = None) -> str: temp_id = f"temp_{urlsafe_short_hash()}" try: invoice = bolt11_decode(bolt11) 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.") fee_reserve = max(1000, int(invoice.amount_msat * 0.01)) create_payment( wallet_id=wallet_id, checking_id=temp_id, amount=-invoice.amount_msat, fee=-fee_reserve, memo=temp_id, ) wallet = get_wallet(wallet_id) assert wallet, "invalid wallet id" if wallet.balance_msat < 0: raise PermissionError("Insufficient balance.") ok, checking_id, fee_msat, error_message = WALLET.pay_invoice(bolt11) if ok: create_payment( wallet_id=wallet_id, checking_id=checking_id, amount=-invoice.amount_msat, fee=fee_msat, memo=invoice.description, ) except Exception as e: ok, error_message = False, str(e) delete_payment(temp_id) if not ok: raise Exception(error_message or "Unexpected backend error.") return checking_id
def api_ticket_send_ticket(checking_id): theticket = get_ticket(checking_id) try: is_paid = not WALLET.get_invoice_status(checking_id).pending except Exception: return jsonify({"message": "Not paid."}), HTTPStatus.NOT_FOUND if is_paid: wallet = get_wallet(theticket.wallet) payment = wallet.get_payment(checking_id) payment.set_pending(False) ticket = update_ticket(paid=True, checking_id=checking_id) return jsonify({"paid": True, "ticket_id": ticket.id}), HTTPStatus.OK return jsonify({"paid": False}), HTTPStatus.OK
def api_amilkit(amilk_id): milk = get_amilk(amilk_id) memo = milk.id try: withdraw_res = handle_lnurl(milk.lnurl, response_class=LnurlWithdrawResponse) except LnurlException: abort(HTTPStatus.INTERNAL_SERVER_ERROR, "Could not process withdraw LNURL.") print(withdraw_res.max_sats) try: checking_id, payment_request = create_invoice( wallet_id=milk.wallet, amount=withdraw_res.max_sats, memo=memo) #print(payment_request) except Exception as e: error_message = False, str(e) 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 withdraw LNURL.") for i in range(10): invoice_status = WALLET.get_invoice_status(checking_id) sleep(i) if not invoice_status.paid: continue else: return jsonify({"paid": False}), HTTPStatus.OK break return jsonify({"paid": True}), HTTPStatus.OK
def api_paywal_check_invoice(paywall_id): paywall = get_paywall(paywall_id) if not paywall: return jsonify({"message": "Paywall does not exist."}), HTTPStatus.NOT_FOUND try: is_paid = not WALLET.get_invoice_status(g.data["checking_id"]).pending except Exception: return jsonify({"paid": False}), HTTPStatus.OK if is_paid: wallet = get_wallet(paywall.wallet) payment = wallet.get_payment(g.data["checking_id"]) payment.set_pending(False) return jsonify({"paid": True, "url": paywall.url, "remembers": paywall.remembers}), HTTPStatus.OK return jsonify({"paid": False}), HTTPStatus.OK
def api_tpos_check_invoice(tpos_id, checking_id): tpos = get_tpos(tpos_id) if not tpos: return jsonify({"message": "TPoS does not exist."}), Status.NOT_FOUND try: is_paid = not WALLET.get_invoice_status(checking_id).pending except Exception: return jsonify({"paid": False}), Status.OK if is_paid: wallet = get_wallet(tpos.wallet) payment = wallet.get_payment(checking_id) payment.set_pending(False) return jsonify({"paid": True}), Status.OK return jsonify({"paid": False}), Status.OK
def api_ticket_send_ticket(checking_id): form = get_form(g.data['form']) if not form: return jsonify({"message": "LNTicket does not exist."}), HTTPStatus.NOT_FOUND try: is_paid = not WALLET.get_invoice_status(checking_id).pending except Exception: return jsonify({"message": "Not paid."}), HTTPStatus.NOT_FOUND if is_paid: wallet = get_wallet(form.wallet) payment = wallet.get_payment(checking_id) payment.set_pending(False) create_ticket(wallet=form.wallet, **g.data) return jsonify({"paid": True}), HTTPStatus.OK return jsonify({"paid": False}), HTTPStatus.OK
def get_diagonalleys_orders(wallet_ids: Union[str, List[str]]) -> List[Orders]: if isinstance(wallet_ids, str): wallet_ids = [wallet_ids] with open_ext_db("diagonalley") as db: q = ",".join(["?"] * len(wallet_ids)) rows = db.fetchall(f"SELECT * FROM orders WHERE wallet IN ({q})", (*wallet_ids, )) for r in rows: PAID = WALLET.get_invoice_status(r["invoiceid"]).paid if PAID: with open_ext_db("diagonalley") as db: db.execute("UPDATE orders SET paid = ? WHERE id = ?", ( True, r["id"], )) rows = db.fetchall( f"SELECT * FROM orders WHERE wallet IN ({q})", (*wallet_ids, )) return [Orders(**row) for row in rows]
async def invoice_listener(nursery): async for checking_id in WALLET.paid_invoices_stream(): nursery.start_soon(invoice_callback_dispatcher, checking_id)
async def invoice_listener(): async for checking_id in WALLET.paid_invoices_stream(): await run_on_pseudo_request(invoice_callback_dispatcher, checking_id)
async def invoice_listener(): async for checking_id in WALLET.paid_invoices_stream(): print("> got a payment notification", checking_id) current_app.nursery.start_soon(invoice_callback_dispatcher, checking_id)
async def invoice_listener(): async with trio.open_nursery() as nursery: async for checking_id in WALLET.paid_invoices_stream(): nursery.start_soon(invoice_callback_dispatcher, checking_id)
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