From 2229da1b071a5ba1e27e13bd0180ffba7968b1b2 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Wed, 11 Jun 2025 16:11:13 +0100 Subject: [PATCH 1/3] feat: deeplinks support --- models.py | 7 +++++- static/js/index.js | 3 +++ templates/boltcards/index.html | 12 +++++++++ views_lnurl.py | 45 +++++++++++++++++++++++++++++++++- 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/models.py b/models.py index ca744cb..59d800e 100644 --- a/models.py +++ b/models.py @@ -5,7 +5,7 @@ from lnurl import Lnurl from lnurl import encode as lnurl_encode from lnurl.types import LnurlPayMetadata -from pydantic import BaseModel +from pydantic import BaseModel, Field ZERO_KEY = "00000000000000000000000000000000" @@ -71,3 +71,8 @@ class Refund(BaseModel): hit_id: str refund_amount: int time: datetime + + +class UIDPost(BaseModel): + UID: str = Field(None, description="The UID of the card.") + LNURLW: str = Field(None, description="The LNURLW of the card.") diff --git a/static/js/index.js b/static/js/index.js index 2208189..e40324b 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -229,6 +229,9 @@ window.app = Vue.createApp({ this.qrCodeDialog.data = { id: card.id, link: window.location.origin + '/boltcards/api/v1/auth?a=' + card.otp, + encodedURI: encodeURIComponent( + window.location.origin + '/boltcards/api/v1/auth?a=' + card.otp + ), name: card.card_name, uid: card.uid, external_id: card.external_id, diff --git a/templates/boltcards/index.html b/templates/boltcards/index.html index 838eefb..e1123f7 100644 --- a/templates/boltcards/index.html +++ b/templates/boltcards/index.html @@ -463,6 +463,7 @@
Click to copy, then paste to NFC Card Creator > Backup the keys, or wipe the card first! + + Use Boltcard Programmer App +
Close
diff --git a/views_lnurl.py b/views_lnurl.py index f4a39b1..2bf52bd 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -5,23 +5,26 @@ import bolt11 from fastapi import APIRouter, HTTPException, Query, Request -from lnbits.core.services import create_invoice, pay_invoice from lnurl import encode as lnurl_encode from lnurl.types import LnurlPayMetadata from loguru import logger from starlette.responses import HTMLResponse +from lnbits.core.services import create_invoice, pay_invoice + from .crud import ( create_hit, get_card, get_card_by_external_id, get_card_by_otp, + get_card_by_uid, get_hit, get_hits_today, spend_hit, update_card_counter, update_card_otp, ) +from .models import UIDPost from .nxp424 import decrypt_sun, get_sun_mac boltcards_lnurl_router = APIRouter() @@ -179,6 +182,46 @@ async def api_auth(a, request: Request): return response +# /boltcards/api/v1/auth?a=00000000000000000000000000000000 +@boltcards_lnurl_router.post("/api/v1/auth") +async def api_auth_post(a: str, request: Request, data: UIDPost, wipe: bool = False): + card = None + if wipe: + card = await get_card_by_otp(a) + else: + if not data.UID: + raise HTTPException( + detail="Missing UID.", status_code=HTTPStatus.BAD_REQUEST + ) + + card = await get_card_by_uid(data.UID) + if not card: + raise HTTPException( + detail="Card does not exist.", status_code=HTTPStatus.NOT_FOUND + ) + new_otp = secrets.token_hex(16) + await update_card_otp(new_otp, card.id) + lnurlw_base = ( + f"{urlparse(str(request.url)).netloc}/boltcards/api/v1/scan/{card.external_id}" + ) + response = { + "CARD_NAME": card.card_name, + "ID": str(1), + "K0": card.k0, + "K1": card.k1, + "K2": card.k2, + "K3": card.k1, + "K4": card.k2, + "LNURLW_BASE": "LNURLW://" + lnurlw_base, + "LNURLW": "LNURLW://" + lnurlw_base, + "PROTOCOL_NAME": "NEW_BOLT_CARD_RESPONSE", + "PROTOCOL_VERSION": str(1), + } + if wipe: + response["action"] = "wipe" + return response + + ###############LNURLPAY REFUNDS################# From d8d8bddb48782379b2872a3528284e10b10cc894 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Fri, 13 Jun 2025 11:28:24 +0100 Subject: [PATCH 2/3] fix: move href logic into compute --- static/js/index.js | 9 +++++++++ templates/boltcards/index.html | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/static/js/index.js b/static/js/index.js index e40324b..b553347 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -148,6 +148,15 @@ window.app = Vue.createApp({ } } }, + computed: { + deeplinkUrl() { + const baseUrl = `boltcard://${this.qrCodeDialog.wipe ? 'reset' : 'program'}` + const url = + this.qrCodeDialog.data.link + + (this.qrCodeDialog.wipe ? '&wipe=true' : '') + return `${baseUrl}?url=${encodeURIComponent(url)}` + } + }, methods: { readNfcTag() { const ndef = new NDEFReader() diff --git a/templates/boltcards/index.html b/templates/boltcards/index.html index e1123f7..cd63021 100644 --- a/templates/boltcards/index.html +++ b/templates/boltcards/index.html @@ -479,7 +479,7 @@
unelevated outline color="grey" - :href="`boltcard://${qrCodeDialog.wipe ? 'reset' : 'program'}?url=${encodeURIComponent(qrCodeDialog.data.link + (qrCodeDialog.wipe ? '&wipe=true' : ''))}`" + :href="deeplinkUrl" target="_blank" label="Use Boltcard App" > From c70083733895e0ea0508aecd4c15ba3aee3415a9 Mon Sep 17 00:00:00 2001 From: Tiago Vasconcelos Date: Fri, 13 Jun 2025 11:28:41 +0100 Subject: [PATCH 3/3] chore: fix linting --- views_lnurl.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/views_lnurl.py b/views_lnurl.py index 2bf52bd..01da89e 100644 --- a/views_lnurl.py +++ b/views_lnurl.py @@ -5,13 +5,12 @@ import bolt11 from fastapi import APIRouter, HTTPException, Query, Request +from lnbits.core.services import create_invoice, pay_invoice from lnurl import encode as lnurl_encode from lnurl.types import LnurlPayMetadata from loguru import logger from starlette.responses import HTMLResponse -from lnbits.core.services import create_invoice, pay_invoice - from .crud import ( create_hit, get_card,