feat: add fiat checkout and nostr + email notification (#50)
* feat: fiat and email/nostr notifications * make n bake
This commit is contained in:
parent
4afc78d44d
commit
680b035ec9
9 changed files with 379 additions and 61 deletions
|
|
@ -11,7 +11,9 @@ window.PageEventsDisplay = {
|
|||
data: {
|
||||
name: '',
|
||||
email: '',
|
||||
refund: ''
|
||||
refund: '',
|
||||
nostr_identifier: '',
|
||||
payment_method: 'lightning'
|
||||
}
|
||||
},
|
||||
ticketLink: {
|
||||
|
|
@ -23,7 +25,8 @@ window.PageEventsDisplay = {
|
|||
receive: {
|
||||
show: false,
|
||||
status: 'pending',
|
||||
paymentReq: null
|
||||
paymentReq: null,
|
||||
isFiat: false
|
||||
},
|
||||
paymentDismissMsg: null,
|
||||
paymentWebsocket: null
|
||||
|
|
@ -35,7 +38,17 @@ window.PageEventsDisplay = {
|
|||
},
|
||||
computed: {
|
||||
formatDescription() {
|
||||
return LNbits.utils.convertMarkdown(this.info)
|
||||
return LNbits.utils.convertMarkdown(this.event?.info || '')
|
||||
},
|
||||
allowFiatCheckout() {
|
||||
const currency = (this.event?.currency || '').toLowerCase()
|
||||
return this.event?.allow_fiat && !['sat', 'sats'].includes(currency)
|
||||
},
|
||||
allowEmailNotifications() {
|
||||
return Boolean(this.event?.extra?.email_notifications)
|
||||
},
|
||||
allowNostrNotifications() {
|
||||
return Boolean(this.event?.extra?.nostr_notifications)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -56,6 +69,8 @@ window.PageEventsDisplay = {
|
|||
this.formDialog.data.name = ''
|
||||
this.formDialog.data.email = ''
|
||||
this.formDialog.data.refund = ''
|
||||
this.formDialog.data.nostr_identifier = ''
|
||||
this.formDialog.data.payment_method = 'lightning'
|
||||
},
|
||||
|
||||
closeReceiveDialog() {
|
||||
|
|
@ -87,6 +102,9 @@ window.PageEventsDisplay = {
|
|||
this.paymentReq = null
|
||||
this.formDialog.data.name = ''
|
||||
this.formDialog.data.email = ''
|
||||
this.formDialog.data.refund = ''
|
||||
this.formDialog.data.nostr_identifier = ''
|
||||
this.formDialog.data.payment_method = 'lightning'
|
||||
Quasar.Notify.create({
|
||||
type: 'positive',
|
||||
message: 'Sent, thank you!',
|
||||
|
|
@ -95,7 +113,8 @@ window.PageEventsDisplay = {
|
|||
this.receive = {
|
||||
show: false,
|
||||
status: 'complete',
|
||||
paymentReq: null
|
||||
paymentReq: null,
|
||||
isFiat: false
|
||||
}
|
||||
this.ticketLink = {
|
||||
show: true,
|
||||
|
|
@ -103,9 +122,7 @@ window.PageEventsDisplay = {
|
|||
link: `/events/ticket/${paymentHash}`
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
window.location.href = `/events/ticket/${paymentHash}`
|
||||
}, 5000)
|
||||
window.open(`/events/ticket/${paymentHash}`, '_blank', 'noopener')
|
||||
},
|
||||
async createInvoice() {
|
||||
try {
|
||||
|
|
@ -117,10 +134,15 @@ window.PageEventsDisplay = {
|
|||
name: this.formDialog.data.name,
|
||||
email: this.formDialog.data.email,
|
||||
promo_code: this.formDialog.data.promo_code || null,
|
||||
refund_address: this.formDialog.data.refund || null
|
||||
refund_address: this.formDialog.data.refund || null,
|
||||
nostr_identifier: this.formDialog.data.nostr_identifier || null,
|
||||
payment_method: this.formDialog.data.payment_method
|
||||
}
|
||||
)
|
||||
this.paymentReq = data.payment_request
|
||||
const isFiat = Boolean(data.is_fiat)
|
||||
this.paymentReq = isFiat
|
||||
? data.fiat_payment_request || null
|
||||
: data.payment_request
|
||||
this.paymentHash = data.payment_hash
|
||||
|
||||
this.paymentDismissMsg = Quasar.Notify.create({
|
||||
|
|
@ -130,30 +152,34 @@ window.PageEventsDisplay = {
|
|||
this.receive = {
|
||||
show: true,
|
||||
status: 'pending',
|
||||
paymentReq: this.paymentReq
|
||||
paymentReq: this.paymentReq,
|
||||
isFiat
|
||||
}
|
||||
this.websocketListener(this.paymentHash)
|
||||
if (isFiat && this.paymentReq) {
|
||||
window.open(this.paymentReq, '_blank', 'noopener')
|
||||
}
|
||||
this.paymentWatcher(this.paymentHash)
|
||||
} catch (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
}
|
||||
},
|
||||
websocketListener(paymentHash) {
|
||||
paymentWatcher(paymentHash) {
|
||||
if (this.paymentWebsocket) {
|
||||
this.paymentWebsocket.close()
|
||||
}
|
||||
|
||||
const url = new URL(window.location)
|
||||
url.protocol = url.protocol === 'https:' ? 'wss' : 'ws'
|
||||
url.pathname = `/events/api/v1/tickets/ws/${paymentHash}`
|
||||
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
url.pathname = `/api/v1/ws/${paymentHash}`
|
||||
url.search = ''
|
||||
url.hash = ''
|
||||
|
||||
const ws = new WebSocket(url)
|
||||
const ws = new WebSocket(url.toString())
|
||||
this.paymentWebsocket = ws
|
||||
|
||||
ws.onmessage = event => {
|
||||
const data = JSON.parse(event.data)
|
||||
if (data.paid) {
|
||||
if (data.pending === false) {
|
||||
this.paymentSuccess(paymentHash)
|
||||
ws.close()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,41 +16,77 @@
|
|||
<q-card-section class="q-pa-none">
|
||||
<h5 class="q-mt-none">Buy Ticket</h5>
|
||||
<q-form @submit="createInvoice()" class="q-gutter-md">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.name"
|
||||
label="Your name "
|
||||
:rules="[val => nameValidation(val)]"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 q-pr-sm">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.email"
|
||||
type="email"
|
||||
:label="
|
||||
allowEmailNotifications
|
||||
? 'Your email (ticket delivery) '
|
||||
: 'Your email '
|
||||
"
|
||||
:rules="[
|
||||
val => !!val || '* Required',
|
||||
val => emailValidation(val)
|
||||
]"
|
||||
lazy-rules
|
||||
></q-input>
|
||||
</div>
|
||||
<div v-if="allowNostrNotifications" class="col-12 col-md-6">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.nostr_identifier"
|
||||
label="(optional) Nostr NIP-05 or npub"
|
||||
hint="If provided, we'll DM your ticket link after payment."
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.name"
|
||||
label="Your name "
|
||||
:rules="[val => nameValidation(val)]"
|
||||
></q-input>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.email"
|
||||
type="email"
|
||||
label="Your email "
|
||||
:rules="[
|
||||
val => !!val || '* Required',
|
||||
val => emailValidation(val)
|
||||
]"
|
||||
lazy-rules
|
||||
></q-input>
|
||||
<q-input
|
||||
v-if="this.extra?.conditional"
|
||||
v-if="event.extra?.conditional"
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.refund"
|
||||
label="Refund lnadress or LNURL "
|
||||
:rules="[val => !!val || '* Required']"
|
||||
lazy-rules
|
||||
:hint="`If minimum tickets (${this.extra?.min_tickets}) are not met, refund will be sent.`"
|
||||
></q-input>
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.promo_code"
|
||||
label="(optional) Promo Code "
|
||||
:hint="`If minimum tickets (${event.extra?.min_tickets}) are not met, refund will be sent.`"
|
||||
></q-input>
|
||||
<div class="row q-col-gutter-md q-pt-lg items-center">
|
||||
<div v-if="allowFiatCheckout" class="col-auto">
|
||||
<q-option-group
|
||||
v-model="formDialog.data.payment_method"
|
||||
inline
|
||||
:options="[
|
||||
{label: 'Lightning', value: 'lightning'},
|
||||
{
|
||||
label: `Fiat (${event.currency.toUpperCase()})`,
|
||||
value: 'fiat'
|
||||
}
|
||||
]"
|
||||
></q-option-group>
|
||||
</div>
|
||||
<div :class="allowFiatCheckout ? 'col-12 col-md-3' : 'col-12'">
|
||||
<q-input
|
||||
filled
|
||||
dense
|
||||
v-model.trim="formDialog.data.promo_code"
|
||||
label="(optional) Promo Code "
|
||||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
unelevated
|
||||
|
|
@ -82,8 +118,6 @@
|
|||
type="a"
|
||||
>Link to your ticket!</q-btn
|
||||
>
|
||||
<br /><br />
|
||||
<p>You'll be redirected in a few moments...</p>
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
|
|
@ -94,6 +128,37 @@
|
|||
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
||||
>
|
||||
</q-card>
|
||||
<q-card
|
||||
v-else-if="receive.isFiat"
|
||||
class="q-pa-lg q-pt-xl lnbits__dialog-card"
|
||||
>
|
||||
<div class="text-center q-mb-lg">
|
||||
<div class="text-h6 q-mb-sm">Continue to checkout</div>
|
||||
<div class="text-body2 text-grey-5 q-mb-lg">
|
||||
Your fiat checkout opened in a new tab. If it did not, use the
|
||||
button below.
|
||||
</div>
|
||||
<q-btn
|
||||
unelevated
|
||||
color="primary"
|
||||
type="a"
|
||||
:href="receive.paymentReq"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
Go to checkout
|
||||
</q-btn>
|
||||
</div>
|
||||
<div class="row q-mt-lg">
|
||||
<q-btn
|
||||
outline
|
||||
color="grey"
|
||||
@click="utils.copyText(receive.paymentReq)"
|
||||
>Copy payment link</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
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ window.PageEvents = {
|
|||
align: 'left',
|
||||
label: 'Price',
|
||||
field: row => {
|
||||
if (row.currency != 'sats') {
|
||||
if (this.isFiatCurrency(row.currency)) {
|
||||
return LNbits.utils.formatCurrency(
|
||||
row.price_per_ticket.toFixed(2),
|
||||
row.currency
|
||||
|
|
@ -98,6 +98,7 @@ window.PageEvents = {
|
|||
show: false,
|
||||
data: {
|
||||
currency: 'sats',
|
||||
allow_fiat: false,
|
||||
extra: {
|
||||
promo_codes: []
|
||||
}
|
||||
|
|
@ -106,6 +107,9 @@ window.PageEvents = {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
isFiatCurrency(currency) {
|
||||
return !['sat', 'sats'].includes((currency || '').toLowerCase())
|
||||
},
|
||||
getTickets() {
|
||||
LNbits.api
|
||||
.request(
|
||||
|
|
@ -158,10 +162,16 @@ window.PageEvents = {
|
|||
id: this.formDialog.data.wallet
|
||||
})
|
||||
const data = this.formDialog.data
|
||||
if (data.extra && !data.extra.promo_codes) {
|
||||
if (data.extra?.promo_codes) {
|
||||
data.extra.promo_codes = data.extra.promo_codes
|
||||
.filter(code => code.trim() !== '')
|
||||
.map(code => code.trim().toUpperCase())
|
||||
.filter(code => code.code?.trim() !== '')
|
||||
.map(code => ({
|
||||
...code,
|
||||
code: code.code.trim().toUpperCase()
|
||||
}))
|
||||
}
|
||||
if (!this.isFiatCurrency(data.currency)) {
|
||||
data.allow_fiat = false
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
|
|
@ -177,9 +187,12 @@ window.PageEvents = {
|
|||
} else {
|
||||
this.formDialog.data = {
|
||||
currency: 'sats',
|
||||
allow_fiat: false,
|
||||
extra: {
|
||||
conditional: false,
|
||||
min_tickets: 1,
|
||||
email_notifications: false,
|
||||
nostr_notifications: false,
|
||||
promo_codes: []
|
||||
}
|
||||
}
|
||||
|
|
@ -189,7 +202,11 @@ window.PageEvents = {
|
|||
resetEventDialog() {
|
||||
this.formDialog.show = false
|
||||
this.formDialog.data = {
|
||||
currency: 'sats',
|
||||
allow_fiat: false,
|
||||
extra: {
|
||||
email_notifications: false,
|
||||
nostr_notifications: false,
|
||||
promo_codes: []
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -371,6 +371,18 @@
|
|||
></q-input>
|
||||
</div>
|
||||
</div>
|
||||
<q-toggle
|
||||
v-model="formDialog.data.allow_fiat"
|
||||
:disable="
|
||||
formDialog.data.currency == null ||
|
||||
['sat', 'sats'].includes(
|
||||
(formDialog.data.currency || '').toLowerCase()
|
||||
)
|
||||
"
|
||||
label="Allow fiat checkout"
|
||||
left-label
|
||||
hint="Lets attendees pay through a configured fiat provider using the event currency."
|
||||
></q-toggle>
|
||||
<q-expansion-item
|
||||
group="advanced"
|
||||
icon="settings"
|
||||
|
|
@ -473,6 +485,21 @@
|
|||
>Add Promo Code</q-btn
|
||||
>
|
||||
</div>
|
||||
<q-separator class="q-my-md"></q-separator>
|
||||
<div class="text-subtitle1 q-mb-md">Ticket Delivery</div>
|
||||
<div class="text-caption">
|
||||
Send the paid ticket link automatically by email or Nostr DM.
|
||||
</div>
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.email_notifications"
|
||||
label="Email notifications"
|
||||
left-label
|
||||
></q-toggle>
|
||||
<q-toggle
|
||||
v-model="formDialog.data.extra.nostr_notifications"
|
||||
label="Nostr notifications"
|
||||
left-label
|
||||
></q-toggle>
|
||||
</q-expansion-item>
|
||||
|
||||
<div class="row q-mt-lg">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue