From 7dcffb3461ec8f3c5a323797a330f2b2c716b679 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Tue, 6 Feb 2024 15:54:11 +0000 Subject: [PATCH] allow withdraw with boltcard --- templates/tpos/tpos.html | 50 ++++++++++++++++++++++++++++++-- views_api.py | 61 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/templates/tpos/tpos.html b/templates/tpos/tpos.html index 5f1b0d7..774786a 100644 --- a/templates/tpos/tpos.html +++ b/templates/tpos/tpos.html @@ -829,7 +829,9 @@
// if searchTerm entered, filter out items that don't match if (this.searchTerm) { items = items.filter(item => { - return item.title.toLowerCase().includes(this.searchTerm.toLowerCase()) + return item.title + .toLowerCase() + .includes(this.searchTerm.toLowerCase()) }) } // if categoryFilter entered, filter out items that don't match @@ -920,6 +922,7 @@
lnurl = res.data.lnurl dialog.data = {payment_request: lnurl} dialog.show = true + self.readNfcTag() dialog.dismissMsg = self.$q.notify({ timeout: 0, message: 'Withdraw...' @@ -949,6 +952,7 @@
self.atmToken = '' self.complete.show = true self.atmMode = false + this.connectionWithdraw.close() } } this.getRates() @@ -1108,7 +1112,9 @@
this.nfcTagReading = true this.$q.notify({ - message: 'Tap your NFC tag to pay this invoice with LNURLw.' + message: this.atmMode + ? 'Tap your NFC tag to withdraw with LNURLp.' + : 'Tap your NFC tag to pay this invoice with LNURLw.' }) return ndef.scan({signal: readerAbortController.signal}).then(() => { @@ -1136,7 +1142,15 @@
//User feedback, show loader icon self.nfcTagReading = false - self.payInvoice(lnurl, readerAbortController) + if (self.atmMode) { + LNbits.api + .request('GET', `${lnurl.replace('lnurlw://', 'https://')}`) + .then(res => { + self.makeWithdraw(res.data.payLink) + }) + } else { + self.payInvoice(lnurl, readerAbortController) + } this.$q.notify({ type: 'positive', @@ -1154,6 +1168,36 @@
}) } }, + makeWithdraw(payLink, readerAbortController) { + if (!payLink) { + this.$q.notify({ + type: 'negative', + message: 'LNURL not found in NFC tag.' + }) + return + } + LNbits.api + .request( + 'GET', + `/tpos/api/v1/atm/withdraw/${this.atmToken}/${this.sat}/pay?payLink=${payLink}` + ) + .then(res => { + if (!res.data.success) { + this.$q.notify({ + type: 'negative', + message: res.data.detail + }) + } else { + this.stack = [] + this.total = 0.0 + this.$q.notify({ + type: 'positive', + message: 'Topup successful!' + }) + } + readerAbortController.abort() + }) + }, payInvoice: function (lnurl, readerAbortController) { const self = this diff --git a/views_api.py b/views_api.py index e68f071..3f38e70 100644 --- a/views_api.py +++ b/views_api.py @@ -13,10 +13,8 @@ from lnbits.core.views.api import api_payment from lnbits.decorators import ( WalletTypeInfo, - check_admin, get_key_type, require_admin_key, - require_invoice_key, ) from lnbits.utils.exchange_rates import get_fiat_rate_satoshis @@ -245,6 +243,65 @@ async def api_tpos_atm_pin_check(tpos_id: str, atmpin: int): return token +@tpos_ext.get("/api/v1/atm/withdraw/{k1}/{amount}/pay", status_code=HTTPStatus.OK) +async def api_tpos_atm_pay( + request: Request, k1: str, amount: int, payLink: str = Query(...) +): + try: + # get the payment_request from the lnurl + payLink = payLink.replace("lnurlp://", "https://") + logger.debug(payLink) + async with httpx.AsyncClient() as client: + headers = {"user-agent": f"lnbits/tpos"} + r = await client.get(payLink, follow_redirects=True, headers=headers) + if r.is_error: + return {"success": False, "detail": "Error loading"} + resp = r.json() + + amount = amount * 1000 # convert to msats + + if resp["tag"] != "payRequest": + return {"success": False, "detail": "Wrong tag type"} + + if amount < resp["minSendable"]: + return {"success": False, "detail": "Amount too low"} + + if amount > resp["maxSendable"]: + return {"success": False, "detail": "Amount too high"} + + cb_res = await client.get( + resp["callback"], + follow_redirects=True, + headers=headers, + params={"amount": amount}, + ) + cb_resp = cb_res.json() + if cb_res.is_error: + return {"success": False, "detail": "Error loading callback"} + + # pay the invoice + lnurl_cb_url = str(request.url_for("tpos.tposlnurlcharge.callback")) + pay_invoice = await client.get( + lnurl_cb_url, + params={"pr": cb_resp["pr"], "k1": k1}, + ) + if pay_invoice.status_code != 200: + return {"success": False, "detail": "Error paying invoice"} + return {"success": True, "detail": "Payment successful"} + + except AssertionError as ex: + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, + detail=str(ex), + ) + except Exception as ex: + logger.warning(ex) + raise HTTPException( + status_code=HTTPStatus.INTERNAL_SERVER_ERROR, + detail="Cannot process atm withdraw", + ) + + @tpos_ext.get( "/api/v1/atm/withdraw/{withdraw_token}/{amount}", status_code=HTTPStatus.CREATED )