Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class EventExtra(BaseModel):
notification_body: str = ""
onchain_enabled: bool = False
onchain_wallet_id: str | None = None
onchain_zeroconf: bool = False
onchain_fasttrack: bool = False


class CreateEvent(BaseModel):
Expand Down Expand Up @@ -113,6 +115,8 @@ class TicketExtra(BaseModel):
nostr_notification_sent: bool = False
refunded: bool = False
onchain: bool = False
onchain_address: str | None = None
satspay_charge_id: str | None = None


class CreateTicket(BaseModel):
Expand Down Expand Up @@ -170,9 +174,8 @@ class TicketPaymentRequest(BaseModel):
fiat_payment_request: str | None = None
fiat_provider: str | None = None
is_fiat: bool = False
onchain_address: str | None = None
onchain_mempool_endpoint: str | None = None
onchain_amount_sat: int | None = None
satspay_charge_url: str | None = None


class TicketFilters(FilterModel):
Expand Down
15 changes: 13 additions & 2 deletions services.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,27 @@ async def fetch_watchonly_wallet(api_key: str, wallet_id: str) -> dict[str, Any]
return resp.json()


async def fetch_onchain_address(api_key: str, wallet_id: str) -> dict[str, Any]:
async def get_satspay_charge(api_key: str, charge_id: str) -> dict[str, Any]:
async with httpx.AsyncClient() as client:
resp = await client.get(
url=f"http://{settings.host}:{settings.port}/watchonly/api/v1/address/{wallet_id}",
url=f"http://{settings.host}:{settings.port}/satspay/api/v1/charge/{charge_id}",
headers={"X-API-KEY": api_key},
)
resp.raise_for_status()
return resp.json()


async def create_satspay_charge(api_key: str, data: dict) -> dict[str, Any]:
async with httpx.AsyncClient() as client:
resp = await client.post(
url=f"http://{settings.host}:{settings.port}/satspay/api/v1/charge",
headers={"X-API-KEY": api_key},
json=data,
)
resp.raise_for_status()
return resp.json()


async def set_ticket_paid(ticket: Ticket) -> Ticket:
if ticket.paid:
return ticket
Expand Down
54 changes: 17 additions & 37 deletions static/js/display.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@ window.PageEventsDisplay = {
show: false,
status: 'pending',
paymentReq: null,
isFiat: false,
isOnchain: false,
onchainAddress: null,
onchainAmountSat: 0,
mempoolEndpoint: null
isFiat: false
},
paymentDismissMsg: null,
paymentWebsocket: null
Expand Down Expand Up @@ -99,16 +95,6 @@ window.PageEventsDisplay = {
options.push({label: 'Bitcoin', value: 'onchain'})
}
return options
},
onchainPaymentUri() {
if (!this.receive.onchainAddress) return ''
const btc = (this.receive.onchainAmountSat / 100000000).toFixed(8)
return `bitcoin:${this.receive.onchainAddress}?amount=${btc}`
},
mempoolAddressUrl() {
if (!this.receive.onchainAddress || !this.receive.mempoolEndpoint)
return null
return `${this.receive.mempoolEndpoint}/address/${this.receive.onchainAddress}`
}
},
methods: {
Expand Down Expand Up @@ -161,11 +147,7 @@ window.PageEventsDisplay = {
show: false,
status: 'pending',
paymentReq: null,
isFiat: false,
isOnchain: false,
onchainAddress: null,
onchainAmountSat: 0,
mempoolEndpoint: null
isFiat: false
}
},
nameValidation(val) {
Expand Down Expand Up @@ -203,11 +185,7 @@ window.PageEventsDisplay = {
show: false,
status: 'complete',
paymentReq: null,
isFiat: false,
isOnchain: false,
onchainAddress: null,
onchainAmountSat: 0,
mempoolEndpoint: null
isFiat: false
}
this.ticketLink = {
show: true,
Expand Down Expand Up @@ -235,13 +213,15 @@ window.PageEventsDisplay = {
: 'lightning'
}
)
const isOnchain = Boolean(data.onchain_address)
const isFiat = !isOnchain && Boolean(data.is_fiat)
if (data.satspay_charge_url) {
window.location.href = data.satspay_charge_url
return
}

const isFiat = Boolean(data.is_fiat)
this.paymentReq = isFiat
? data.fiat_payment_request || null
: isOnchain
? null
: data.payment_request
: data.payment_request
this.paymentHash = data.payment_hash

this.paymentDismissMsg = Quasar.Notify.create({
Expand All @@ -252,11 +232,7 @@ window.PageEventsDisplay = {
show: true,
status: 'pending',
paymentReq: this.paymentReq,
isFiat,
isOnchain,
onchainAddress: data.onchain_address || null,
onchainAmountSat: data.onchain_amount_sat || 0,
mempoolEndpoint: data.onchain_mempool_endpoint || null
isFiat
}
if (isFiat && this.paymentReq) {
window.open(this.paymentReq, '_blank', 'noopener')
Expand Down Expand Up @@ -291,8 +267,12 @@ window.PageEventsDisplay = {
console.error('WebSocket error:', error)
}
ws.onclose = () => {
if (this.paymentWebsocket === ws) {
this.paymentWebsocket = null
if (this.paymentWebsocket !== ws) return
this.paymentWebsocket = null
if (this.receive.show) {
setTimeout(() => {
if (this.receive.show) this.paymentWatcher(paymentHash)
}, 3000)
}
}
}
Expand Down
47 changes: 1 addition & 46 deletions static/js/display.vue
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@

<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
<q-card
v-if="!receive.paymentReq && !receive.isOnchain"
v-if="!receive.paymentReq"
class="q-pa-lg q-pt-xl lnbits__dialog-card"
>
</q-card>
Expand Down Expand Up @@ -173,51 +173,6 @@
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
<q-card
v-else-if="receive.isOnchain"
class="q-pa-lg q-pt-xl lnbits__dialog-card"
>
<div class="text-center q-mb-lg">
<div class="text-h6 q-mb-sm">Pay with Bitcoin</div>
<lnbits-qrcode
:href="onchainPaymentUri"
:value="onchainPaymentUri.toUpperCase()"
></lnbits-qrcode>
<div class="text-body1 q-mt-md">
<strong
>{{
(receive.onchainAmountSat / 100000000).toFixed(8)
}}
BTC</strong
>
</div>
<div
class="text-caption text-grey q-mt-xs"
style="word-break: break-all"
>
{{ receive.onchainAddress }}
</div>
</div>
<div class="row q-mt-lg q-col-gutter-sm">
<q-btn
outline
color="grey"
@click="utils.copyText(receive.onchainAddress)"
>Copy address</q-btn
>
<q-btn
v-if="mempoolAddressUrl"
outline
color="grey"
type="a"
:href="mempoolAddressUrl"
target="_blank"
rel="noopener"
>View on mempool</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
<q-card v-else class="q-pa-lg q-pt-xl lnbits__dialog-card">
<div class="text-center q-mb-lg">
<lnbits-qrcode
Expand Down
2 changes: 2 additions & 0 deletions static/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ window.PageEvents = {
nostr_notifications: false,
onchain_enabled: false,
onchain_wallet_id: null,
onchain_zeroconf: false,
onchain_fasttrack: false,
ticket_waves: [
{
id: 'primary',
Expand Down
19 changes: 16 additions & 3 deletions static/js/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -616,13 +616,14 @@

<q-expansion-item
group="advanced"
icon="currency_bitcoin"
icon="view_in_ar"
label="Onchain payments"
>
<div class="q-mt-lg">
<div class="text-caption q-mb-md">
Accept Bitcoin onchain payments. Requires the Watchonly
extension.
Accept Bitcoin onchain payments. Requires the
<strong>SatsPay</strong> and <strong>Watchonly</strong>
extensions to be installed and enabled.
</div>
<q-toggle
v-model="formDialog.data.extra.onchain_enabled"
Expand All @@ -647,6 +648,18 @@
map-options
hint="Bitcoin watchonly wallet for receiving onchain payments"
></q-select>
<div v-if="formDialog.data.extra.onchain_enabled" class="q-mt-md">
<q-toggle
v-model="formDialog.data.extra.onchain_zeroconf"
label="Zero-conf (accept unconfirmed transactions)"
left-label
></q-toggle>
<q-toggle
v-model="formDialog.data.extra.onchain_fasttrack"
label="Fasttrack (treat pending as paid)"
left-label
></q-toggle>
</div>
</div>
</q-expansion-item>

Expand Down
Loading
Loading