configuration for nostr

This commit is contained in:
callebtc 2023-04-24 17:12:35 +02:00
commit 64da75d605
7 changed files with 170 additions and 95 deletions

View file

@ -58,10 +58,11 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
comment_chars, comment_chars,
currency, currency,
fiat_base_multiplier, fiat_base_multiplier,
username username,
zaps
) )
VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", """,
( (
link_id, link_id,
@ -78,6 +79,7 @@ async def create_pay_link(data: CreatePayLinkData, wallet_id: str) -> PayLink:
data.currency, data.currency,
data.fiat_base_multiplier, data.fiat_base_multiplier,
data.username, data.username,
data.zaps,
), ),
) )
assert result assert result

View file

@ -147,6 +147,7 @@ async def api_lnurl_response(request: Request, link_id, lnaddress=False):
if link.comment_chars > 0: if link.comment_chars > 0:
params["commentAllowed"] = link.comment_chars params["commentAllowed"] = link.comment_chars
params["allowsNostr"] = True if link.zaps:
params["nostrPubkey"] = nostr_publickey.hex() params["allowsNostr"] = True
params["nostrPubkey"] = nostr_publickey.hex()
return params return params

View file

@ -150,6 +150,13 @@ async def m006_redux(db):
async def m007_add_lnaddress_username(db): async def m007_add_lnaddress_username(db):
""" """
Add headers and body to webhooks Add Lightning address to pay links
""" """
await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN username TEXT;") await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN username TEXT;")
async def m008_add_zap_enabled_column(db):
"""
Add Nostr zaps to pay links
"""
await db.execute("ALTER TABLE lnurlp.pay_links ADD COLUMN zaps BOOLEAN;")

View file

@ -24,6 +24,7 @@ class CreatePayLinkData(BaseModel):
success_url: str = Query(None) success_url: str = Query(None)
fiat_base_multiplier: int = Query(100, ge=1) fiat_base_multiplier: int = Query(100, ge=1)
username: str = Query(None) username: str = Query(None)
zaps: bool = Query(False)
class PayLink(BaseModel): class PayLink(BaseModel):
@ -34,6 +35,7 @@ class PayLink(BaseModel):
served_meta: int served_meta: int
served_pr: int served_pr: int
username: Optional[str] username: Optional[str]
zaps: Optional[bool]
domain: Optional[str] domain: Optional[str]
webhook_url: Optional[str] webhook_url: Optional[str]
webhook_headers: Optional[str] webhook_headers: Optional[str]

View file

@ -40,7 +40,9 @@ new Vue({
formDialog: { formDialog: {
show: false, show: false,
fixedAmount: true, fixedAmount: true,
data: {} data: {
zaps:false
}
}, },
qrCodeDialog: { qrCodeDialog: {
show: false, show: false,
@ -140,7 +142,8 @@ new Vue({
'success_url', 'success_url',
'comment_chars', 'comment_chars',
'currency', 'currency',
'username' 'username',
'zaps'
), ),
(value, key) => (value, key) =>
(key === 'webhook_url' || (key === 'webhook_url' ||

View file

@ -72,9 +72,9 @@ async def on_invoice_paid(payment: Payment):
) )
# NIP-57 # NIP-57
# load the zap request
nostr = payment.extra.get("nostr") nostr = payment.extra.get("nostr")
if nostr: if pay_link and pay_link.zaps and nostr:
event_json = json.loads(nostr) event_json = json.loads(nostr)
def get_tag(event_json, tag): def get_tag(event_json, tag):
@ -114,7 +114,7 @@ async def on_invoice_paid(payment: Payment):
wsts: List[Thread] = [] wsts: List[Thread] = []
# # send zap via nostrclient # # send zap via nostrclient
# ws, wst = send_zap(f"ws://localhost:{settings.port}/nostrclient/api/v1/relay") # ws, wst = send_zap(f"wss://localhost:{settings.port}/nostrclient/api/v1/relay")
# wss += [ws] # wss += [ws]
# wsts += [wst] # wsts += [wst]
@ -138,7 +138,6 @@ async def on_invoice_paid(payment: Payment):
async def mark_webhook_sent( async def mark_webhook_sent(
payment_hash: str, status: int, is_success: bool, reason_phrase="", text="" payment_hash: str, status: int, is_success: bool, reason_phrase="", text=""
) -> None: ) -> None:
await update_payment_extra( await update_payment_extra(
payment_hash, payment_hash,
{ {

View file

@ -26,7 +26,7 @@
> >
{% raw %} {% raw %}
<template v-slot:header="props"> <template v-slot:header="props">
<q-tr class="text-left" :props="props"> <q-tr class="text-left" :props="props">
<q-th auto-width></q-th> <q-th auto-width></q-th>
<q-th auto-width>Description</q-th> <q-th auto-width>Description</q-th>
<q-th auto-width>Amount</q-th> <q-th auto-width>Amount</q-th>
@ -48,7 +48,8 @@
type="a" type="a"
:href="props.row.pay_url" :href="props.row.pay_url"
target="_blank" target="_blank"
><q-tooltip>Sharable Page</q-tooltip></q-btn> ><q-tooltip>Sharable Page</q-tooltip></q-btn
>
<q-btn <q-btn
unelevated unelevated
dense dense
@ -56,7 +57,8 @@
icon="visibility" icon="visibility"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'" :color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
@click="openQrCodeDialog(props.row.id)" @click="openQrCodeDialog(props.row.id)"
><q-tooltip>View Link</q-tooltip></q-btn> ><q-tooltip>View Link</q-tooltip></q-btn
>
</q-td> </q-td>
<q-td auto-width>{{ props.row.description }}</q-td> <q-td auto-width>{{ props.row.description }}</q-td>
<q-td auto-width> <q-td auto-width>
@ -66,10 +68,14 @@
<span v-else>{{ props.row.min }} - {{ props.row.max }}</span> <span v-else>{{ props.row.min }} - {{ props.row.max }}</span>
</q-td> </q-td>
<q-td>{{ props.row.currency || 'sat' }}</q-td> <q-td>{{ props.row.currency || 'sat' }}</q-td>
<q-td auto-width :class="(props.row.username) ? 'text-normal' : 'text-grey'">{{ props.row.username || 'None' }}</q-td> <q-td
auto-width
:class="(props.row.username) ? 'text-normal' : 'text-grey'"
>{{ props.row.username || 'None' }}</q-td
>
<q-td> <q-td>
<q-icon v-if="props.row.webhook_url" size="14px" name="http"> <q-icon v-if="props.row.webhook_url" size="14px" name="http">
<q-tooltip>Webhook to {{ props.row.webhook_url}}</q-tooltip> <q-tooltip>Webhook to {{ props.row.webhook_url }}</q-tooltip>
</q-icon> </q-icon>
<q-icon <q-icon
v-if="props.row.success_text || props.row.success_url" v-if="props.row.success_text || props.row.success_url"
@ -102,7 +108,7 @@
icon="edit" icon="edit"
color="light-blue" color="light-blue"
> >
<q-tooltip>Edit</q-tooltip> <q-tooltip>Edit</q-tooltip>
</q-btn> </q-btn>
<q-btn <q-btn
flat flat
@ -111,7 +117,8 @@
@click="deletePayLink(props.row.id)" @click="deletePayLink(props.row.id)"
icon="cancel" icon="cancel"
color="pink" color="pink"
><q-tooltip>Delete</q-tooltip></q-btn> ><q-tooltip>Delete</q-tooltip></q-btn
>
</q-td> </q-td>
</q-tr> </q-tr>
</template> </template>
@ -125,7 +132,7 @@
<q-card> <q-card>
<q-card-section> <q-card-section>
<h6 class="text-subtitle1 q-my-none"> <h6 class="text-subtitle1 q-my-none">
{{SITE_TITLE}} LNURL-pay extension {{ SITE_TITLE }} LNURL-pay extension
</h6> </h6>
</q-card-section> </q-card-section>
<q-card-section class="q-pa-none"> <q-card-section class="q-pa-none">
@ -152,29 +159,30 @@
> >
</q-select> </q-select>
<q-input <q-input
filled filled
dense dense
v-model.trim="formDialog.data.description" v-model.trim="formDialog.data.description"
type="text" type="text"
label="Item description *" label="Item description *"
> >
</q-input> </q-input>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<q-input <q-input
filled filled
dense dense
v-model.trim="formDialog.data.username" v-model.trim="formDialog.data.username"
type="text" type="text"
label="Lightning Address" label="Lightning Address"
> />
</div> </div>
<div class="col" style="margin-top: 10px"> <div class="col" style="margin-top: 10px">
<span class="label"> &nbsp; @ {% raw %} {{domain}} {% endraw %} </span> <span class="label">
&nbsp; @ {% raw %} {{ domain }} {% endraw %}
</span>
</div> </div>
</q-input>
</div> </div>
<div class="row q-col-gutter-sm"> <div class="row q-col-gutter-sm q-mx-sm">
<q-input <q-input
filled filled
dense dense
@ -209,63 +217,111 @@
v-model="formDialog.data.currency" v-model="formDialog.data.currency"
:display-value="formDialog.data.currency || 'satoshis'" :display-value="formDialog.data.currency || 'satoshis'"
label="Currency" label="Currency"
:hint="'Amounts will be converted at use-time to satoshis. ' + (formDialog.data.currency && fiatRates[formDialog.data.currency] ? `Currently 1 ${formDialog.data.currency} = ${fiatRates[formDialog.data.currency]} sat` : '')" :hint="'Converted to satoshis at each payment. ' + (formDialog.data.currency && fiatRates[formDialog.data.currency] ? `Currently 1 ${formDialog.data.currency} = ${fiatRates[formDialog.data.currency]} sat` : '')"
@input="updateFiatRate" @input="updateFiatRate"
/> />
</div> </div>
</div> </div>
<q-input <q-expansion-item
filled group="advanced"
dense icon="settings"
v-model.number="formDialog.data.comment_chars" label="Advanced options"
type="number"
label="Comment maximum characters"
hint="Tell wallets to prompt users for a comment that will be sent along with the payment. LNURLp will store the comment and send it in the webhook."
> >
</q-input> <q-card>
<q-input <q-card-section>
filled <h5 class="text-caption q-mt-sm q-mb-none">LNURL</h5>
dense <div class="row">
v-model="formDialog.data.webhook_url" <div class="col-12">
type="text" <q-input
label="Webhook URL (optional)" filled
hint="A URL to be called whenever this link receives a payment." dense
></q-input> v-model.number="formDialog.data.comment_chars"
<q-input type="number"
filled label="Comment maximum characters"
dense hint="Allow the payer to attach a comment."
v-if="formDialog.data.webhook_url" >
v-model="formDialog.data.webhook_headers" </q-input>
type="text" </div>
label="Webhook headers (optional)" </div>
hint="Custom data as JSON string, send headers along with the webhook." <div class="row">
></q-input> <div class="col-12">
<q-input <q-input
filled filled
dense dense
v-if="formDialog.data.webhook_url" v-model="formDialog.data.webhook_url"
v-model="formDialog.data.webhook_body" type="text"
type="text" label="Webhook URL (optional)"
label="Webhook custom data (optional)" hint="A URL to be called whenever this link receives a payment."
hint="Custom data as JSON string, will get posted along with webhook 'body' field." ></q-input>
></q-input> </div>
<q-input </div>
filled <div class="row">
dense <div class="col-12">
v-model="formDialog.data.success_text" <q-input
type="text" filled
label="Success message (optional)" dense
hint="Will be shown to the user in his wallet after a successful payment." v-if="formDialog.data.webhook_url"
></q-input> v-model="formDialog.data.webhook_headers"
<q-input type="text"
filled label="Webhook headers (optional)"
dense hint="Custom data as JSON string, send headers along with the webhook."
v-model="formDialog.data.success_url" ></q-input>
type="text" </div>
label="Success URL (optional)" </div>
hint="Will be shown as a clickable link to the user in his wallet after a successful payment, appended by the payment_hash as a query string." <div class="row">
> <div class="col-12">
</q-input> <q-input
filled
dense
v-if="formDialog.data.webhook_url"
v-model="formDialog.data.webhook_body"
type="text"
label="Webhook custom data (optional)"
hint="Custom data as JSON string, will get posted along with webhook 'body' field."
></q-input>
</div>
</div>
<div class="row">
<div class="col-12">
<q-input
filled
dense
v-model="formDialog.data.success_text"
type="text"
label="Success message (optional)"
hint="Will be shown to the user in his wallet after a successful payment."
></q-input>
</div>
</div>
<div class="row">
<div class="col-12">
<q-input
filled
dense
v-model="formDialog.data.success_url"
type="text"
label="Success URL (optional)"
hint="Link will be shown to the sender after a successful payment."
>
</q-input>
</div>
</div>
</q-card-section>
<q-card-section>
<h5 class="text-caption q-mt-sm q-mb-none">Nostr</h5>
<div class="row">
<div class="col-12">
<q-checkbox
:toggle-indeterminate="false"
dense
v-model="formDialog.data.zaps"
label="Enable nostr zaps"
/>
</div>
</div>
</q-card-section>
</q-card>
</q-expansion-item>
<div class="row q-mt-lg"> <div class="row q-mt-lg">
<q-btn <q-btn
v-if="formDialog.data.id" v-if="formDialog.data.id"
@ -311,17 +367,22 @@
<strong>ID:</strong> {{ qrCodeDialog.data.id }}<br /> <strong>ID:</strong> {{ qrCodeDialog.data.id }}<br />
<strong>Amount:</strong> {{ qrCodeDialog.data.amount }}<br /> <strong>Amount:</strong> {{ qrCodeDialog.data.amount }}<br />
<span v-if="qrCodeDialog.data.currency" <span v-if="qrCodeDialog.data.currency"
><strong>{{ qrCodeDialog.data.currency }} price:</strong> {{ ><strong>{{ qrCodeDialog.data.currency }} price:</strong>
fiatRates[qrCodeDialog.data.currency] ? {{
fiatRates[qrCodeDialog.data.currency] + ' sat' : 'Loading...' }}<br fiatRates[qrCodeDialog.data.currency]
? fiatRates[qrCodeDialog.data.currency] + ' sat'
: 'Loading...'
}}<br
/></span> /></span>
<strong>Accepts comments:</strong> {{ qrCodeDialog.data.comments }}<br /> <strong>Accepts comments:</strong> {{ qrCodeDialog.data.comments
}}<br />
<strong>Dispatches webhook to:</strong> {{ qrCodeDialog.data.webhook <strong>Dispatches webhook to:</strong> {{ qrCodeDialog.data.webhook
}}<br /> }}<br />
<strong>On success:</strong> {{ qrCodeDialog.data.success }}<br /> <strong>On success:</strong> {{ qrCodeDialog.data.success }}<br />
<span v-if="qrCodeDialog.data.username"> <span v-if="qrCodeDialog.data.username">
<strong>Lightning Address: </strong> {{ qrCodeDialog.data.username}}@{{domain}} <strong>Lightning Address: </strong>
<br/> {{ qrCodeDialog.data.username }}@{{ domain }}
<br />
</span> </span>
</p> </p>
{% endraw %} {% endraw %}