Browse Source

Add files via upload

Login
Arc 5 years ago
committed by GitHub
parent
commit
13f01dfbe6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      lnbits/extensions/lnticket/README.md
  2. 8
      lnbits/extensions/lnticket/__init__.py
  3. 6
      lnbits/extensions/lnticket/config.json.example
  4. 107
      lnbits/extensions/lnticket/crud.py
  5. 36
      lnbits/extensions/lnticket/migrations.py
  6. 23
      lnbits/extensions/lnticket/models.py
  7. 27
      lnbits/extensions/lnticket/templates/lnticket/_api_docs.html
  8. 211
      lnbits/extensions/lnticket/templates/lnticket/display.html
  9. 441
      lnbits/extensions/lnticket/templates/lnticket/index.html
  10. 22
      lnbits/extensions/lnticket/views.py
  11. 143
      lnbits/extensions/lnticket/views_api.py

11
lnbits/extensions/lnticket/README.md

@ -0,0 +1,11 @@
<h1>Example Extension</h1>
<h2>*tagline*</h2>
This is an example extension to help you organise and build you own.
Try to include an image
<img src="https://i.imgur.com/9i4xcQB.png">
<h2>If your extension has API endpoints, include useful ones here</h2>
<code>curl -H "Content-type: application/json" -X POST https://YOUR-LNBITS/YOUR-EXTENSION/api/v1/EXAMPLE -d '{"amount":"100","memo":"example"}' -H "X-Api-Key: YOUR_WALLET-ADMIN/INVOICE-KEY"</code>

8
lnbits/extensions/lnticket/__init__.py

@ -0,0 +1,8 @@
from flask import Blueprint
lnticket_ext: Blueprint = Blueprint("lnticket", __name__, static_folder="static", template_folder="templates")
from .views_api import * # noqa
from .views import * # noqa

6
lnbits/extensions/lnticket/config.json.example

@ -0,0 +1,6 @@
{
"name": "LNTicket",
"short_description": "Pay-per-word LN ticket system",
"icon": "contact_support",
"contributors": ["benarc"]
}

107
lnbits/extensions/lnticket/crud.py

@ -0,0 +1,107 @@
from typing import List, Optional, Union
from lnbits.db import open_ext_db
from lnbits.helpers import urlsafe_short_hash
from .models import Tickets, Forms
#######TICKETS########
def create_ticket(wallet: str, form: str, name: str, email: str, ltext: str, sats: int) -> Tickets:
formdata = get_form(form)
amount = formdata.amountmade + sats
with open_ext_db("lnticket") as db:
ticket_id = urlsafe_short_hash()
db.execute(
"""
INSERT INTO tickets (id, form, email, ltext, name, wallet, sats)
VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(ticket_id, form, email, ltext, name, wallet, sats),
)
db.execute(
"""
UPDATE forms
SET amountmade = ?
WHERE id = ?
""",
(amount, form),
)
return get_ticket(ticket_id)
def get_ticket(ticket_id: str) -> Optional[Tickets]:
with open_ext_db("lnticket") as db:
row = db.fetchone("SELECT * FROM tickets WHERE id = ?", (ticket_id,))
return Tickets(**row) if row else None
def get_tickets(wallet_ids: Union[str, List[str]]) -> List[Tickets]:
if isinstance(wallet_ids, str):
wallet_ids = [wallet_ids]
with open_ext_db("lnticket") as db:
q = ",".join(["?"] * len(wallet_ids))
rows = db.fetchall(f"SELECT * FROM tickets WHERE wallet IN ({q})", (*wallet_ids,))
return [Tickets(**row) for row in rows]
def delete_ticket(ticket_id: str) -> None:
with open_ext_db("lnticket") as db:
db.execute("DELETE FROM tickets WHERE id = ?", (ticket_id,))
########FORMS#########
def create_form(*, wallet: str, name: str, description: str, costpword: int) -> Forms:
with open_ext_db("lnticket") as db:
form_id = urlsafe_short_hash()
db.execute(
"""
INSERT INTO forms (id, wallet, name, description, costpword, amountmade)
VALUES (?, ?, ?, ?, ?, ?)
""",
(form_id, wallet, name, description, costpword, 0 ),
)
return get_form(form_id)
def update_form(form_id: str, **kwargs) -> Forms:
q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
with open_ext_db("lnticket") as db:
db.execute(f"UPDATE forms SET {q} WHERE id = ?", (*kwargs.values(), form_id))
row = db.fetchone("SELECT * FROM forms WHERE id = ?", (form_id,))
return Forms(**row) if row else None
def get_form(form_id: str) -> Optional[Forms]:
with open_ext_db("lnticket") as db:
row = db.fetchone("SELECT * FROM forms WHERE id = ?", (form_id,))
return Forms(**row) if row else None
def get_forms(wallet_ids: Union[str, List[str]]) -> List[Forms]:
if isinstance(wallet_ids, str):
wallet_ids = [wallet_ids]
with open_ext_db("lnticket") as db:
q = ",".join(["?"] * len(wallet_ids))
rows = db.fetchall(f"SELECT * FROM forms WHERE wallet IN ({q})", (*wallet_ids,))
return [Forms(**row) for row in rows]
def delete_form(form_id: str) -> None:
with open_ext_db("lnticket") as db:
db.execute("DELETE FROM forms WHERE id = ?", (form_id,))

36
lnbits/extensions/lnticket/migrations.py

@ -0,0 +1,36 @@
from lnbits.db import open_ext_db
def m001_initial(db):
db.execute(
"""
CREATE TABLE IF NOT EXISTS forms (
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT NOT NULL,
costpword INTEGER NOT NULL,
amountmade INTEGER NOT NULL,
time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now'))
);
"""
)
db.execute(
"""
CREATE TABLE IF NOT EXISTS tickets (
id TEXT PRIMARY KEY,
form TEXT NOT NULL,
email TEXT NOT NULL,
ltext TEXT NOT NULL,
name TEXT NOT NULL,
wallet TEXT NOT NULL,
sats INTEGER NOT NULL,
time TIMESTAMP NOT NULL DEFAULT (strftime('%s', 'now'))
);
"""
)
def migrate():
with open_ext_db("lnticket") as db:
m001_initial(db)

23
lnbits/extensions/lnticket/models.py

@ -0,0 +1,23 @@
from typing import NamedTuple
class Forms(NamedTuple):
id: str
wallet: str
name: str
description: str
costpword: int
amountmade: int
time: int
class Tickets(NamedTuple):
id: str
form: str
email: str
ltext: str
name: str
wallet: str
sats: int
time: int

27
lnbits/extensions/lnticket/templates/lnticket/_api_docs.html

@ -0,0 +1,27 @@
<q-expansion-item
group="extras"
icon="swap_vertical_circle"
label="Info"
:content-inset-level="0.5"
>
<q-card>
<q-card-section>
<h5 class="text-subtitle1 q-my-none">LNTickets: Get paid sats for questions</h5>
<p>LNTickets allow you to charge people per word for contacting you. Applications incude, paid support ticketting, PAYG language services, such as translation, spam protection (people have to pay to contact you).<br/>
<small> Created by, <a href="https://github.com/benarc">Ben Arc</a></small></p>
</q-card>
</q-card-section>
</q-card-section>
</q-expansion-item>
<q-expansion-item
group="extras"
icon="swap_vertical_circle"
label="API info"
:content-inset-level="0.5"
>
</q-expansion-item>

211
lnbits/extensions/lnticket/templates/lnticket/display.html

@ -0,0 +1,211 @@
{% extends "public.html" %} {% block page %}
<div class="row q-col-gutter-md justify-center">
<div class="col-12 col-md-7 col-lg-6 q-gutter-y-md">
<q-card class="q-pa-lg">
<q-card-section class="q-pa-none">
<h3 class="q-my-none">{{ form_name }}</h3>
<br />
<h5 class="q-my-none">{{ form_desc }}</h5>
<br />
<q-form @submit="Invoice()" class="q-gutter-md">
<q-input
filled
dense
v-model.trim="formDialog.data.name"
type="name"
label="Your name "
></q-input>
<q-input
filled
dense
v-model.trim="formDialog.data.email"
type="email"
label="Your email (optional, if you want a reply)"
></q-input>
<q-input
filled
dense
v-model.number="formDialog.data.text"
type="textarea"
label="{{ form_costpword }} sats per word"
></q-input>
<p>{% raw %}{{amountWords}}{% endraw %}</p>
<div class="row q-mt-lg">
<q-btn
unelevated
color="deep-purple"
:disable="formDialog.data.name == '' || formDialog.data.text == ''"
type="submit"
>Submit</q-btn
>
<q-btn @click="resetForm" flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>
</div>
</q-form>
</q-card-section>
</q-card>
</div>
<q-dialog v-model="receive.show" position="top" @hide="closeReceiveDialog">
<q-card
v-if="!receive.paymentReq"
class="q-pa-lg q-pt-xl lnbits__dialog-card"
>
</q-card>
<q-card v-else class="q-pa-lg q-pt-xl lnbits__dialog-card">
<div class="text-center q-mb-lg">
<a :href="'lightning:' + receive.paymentReq">
<q-responsive :ratio="1" class="q-mx-xl">
<qrcode
:value="paymentReq"
:options="{width: 340}"
class="rounded-borders"
></qrcode>
</q-responsive>
</a>
</div>
<div class="row q-mt-lg">
<q-btn outline color="grey" @click="copyText(receive.paymentReq)"
>Copy invoice</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Close</q-btn>
</div>
</q-card>
</q-dialog>
</div>
{% endblock %} {% block scripts %}
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script>
<script>
console.log('{{ form_costpword }}')
Vue.component(VueQrcode.name, VueQrcode)
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
return {
paymentReq: null,
redirectUrl: null,
formDialog: {
show: false,
data: {
name: '',
email: '',
text: ''
}
},
receive: {
show: false,
status: 'pending',
paymentReq: null
}
}
},
computed: {
amountWords() {
var regex = /\s+/gi
var char = this.formDialog.data.text
.trim()
.replace(regex, ' ')
.split(' ').length
this.formDialog.data.sats = char * parseInt('{{ form_costpword }}')
if (this.formDialog.data.sats == parseInt('{{ form_costpword }}')) {
return '0 Sats to pay'
} else {
return this.formDialog.data.sats + ' Sats to pay'
}
}
},
methods: {
resetForm: function (e) {
e.preventDefault()
this.formDialog.data.name = ''
this.formDialog.data.email = ''
this.formDialog.data.text = ''
this.formDialog.data.sats = 0
},
closeReceiveDialog: function () {
var checker = this.receive.paymentChecker
dismissMsg()
clearInterval(paymentChecker)
setTimeout(function () {}, 10000)
},
Invoice: function () {
var self = this
axios
.get(
'/lnticket/api/v1/tickets/' +
'{{ form_id }}' +
'/' +
self.formDialog.data.sats
)
.then(function (response) {
self.paymentReq = response.data.payment_request
self.paymentCheck = response.data.checking_id
dismissMsg = self.$q.notify({
timeout: 0,
message: 'Waiting for payment...'
})
self.receive = {
show: true,
status: 'pending',
paymentReq: self.paymentReq
}
if (self.formDialog.data.email == '') {
daemail = 'null'
} else {
daemail = self.formDialog.data.email
}
paymentChecker = setInterval(function () {
axios
.post('/lnticket/api/v1/tickets/' + self.paymentCheck, {
form: '{{ form_id }}',
name: self.formDialog.data.name,
email: daemail,
ltext: self.formDialog.data.text,
sats: self.formDialog.data.sats
})
.then(function (res) {
if (res.data.paid) {
clearInterval(paymentChecker)
dismissMsg()
self.formDialog.data.name = ''
self.formDialog.data.email = ''
self.formDialog.data.text = ''
self.formDialog.data.sats = 0
self.$q.notify({
type: 'positive',
message: 'Sent, thank you!',
icon: null
})
self.receive = {
show: false,
status: 'complete',
paymentReq: null
}
}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
}, 2000)
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
}
}
})
</script>
{% endblock %}

441
lnbits/extensions/lnticket/templates/lnticket/index.html

@ -0,0 +1,441 @@
{% extends "base.html" %} {% from "macros.jinja" import window_vars with context
%} {% block page %}
<div class="row q-col-gutter-md">
<div class="col-12 col-md-8 col-lg-7 q-gutter-y-md">
<q-card>
<q-card-section>
<q-btn unelevated color="deep-purple" @click="formDialog.show = true"
>New Form</q-btn
>
</q-card-section>
</q-card>
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">Forms</h5>
</div>
<div class="col-auto">
<q-btn flat color="grey" @click="exportformsCSV"
>Export to CSV</q-btn
>
</div>
</div>
<q-table
dense
flat
:data="forms"
row-key="id"
:columns="formsTable.columns"
:pagination.sync="formsTable.pagination"
>
{% raw %}
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }}
</q-th>
<q-th auto-width></q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width>
<q-btn
unelevated
dense
size="xs"
icon="link"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
type="a"
:href="props.row.displayUrl"
target="_blank"
></q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn
flat
dense
size="xs"
@click="updateformDialog(props.row.id)"
icon="edit"
color="light-blue"
></q-btn>
</q-td>
<q-td auto-width>
<q-btn
flat
dense
size="xs"
@click="deleteForm(props.row.id)"
icon="cancel"
color="pink"
></q-btn>
</q-td>
</q-tr>
</template>
{% endraw %}
</q-table>
</q-card-section>
</q-card>
<q-card>
<q-card-section>
<div class="row items-center no-wrap q-mb-md">
<div class="col">
<h5 class="text-subtitle1 q-my-none">Tickets</h5>
</div>
<div class="col-auto">
<q-btn flat color="grey" @click="exportticketsCSV"
>Export to CSV</q-btn
>
</div>
</div>
<q-table
dense
flat
:data="tickets"
row-key="id"
:columns="ticketsTable.columns"
:pagination.sync="ticketsTable.pagination"
>
{% raw %}
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }}
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width>
<q-btn
unelevated
dense
size="xs"
icon="email"
:color="($q.dark.isActive) ? 'grey-7' : 'grey-5'"
type="a"
:href="'mailto:' + props.row.email"
></q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn
flat
dense
size="xs"
@click="deleteTicket(props.row.id)"
icon="cancel"
color="pink"
></q-btn>
</q-td>
</q-tr>
</template>
{% endraw %}
</q-table>
</q-card-section>
</q-card>
</div>
<div class="col-12 col-md-4 col-lg-5 q-gutter-y-md">
<q-card>
<q-card-section>
<h6 class="text-subtitle1 q-my-none">LNbits LNTicket extension</h6>
</q-card-section>
<q-card-section class="q-pa-none">
<q-separator></q-separator>
<q-list>
{% include "lnticket/_api_docs.html" %}
</q-list>
</q-card-section>
</q-card>
</div>
<q-dialog v-model="formDialog.show" position="top">
<q-card class="q-pa-lg q-pt-xl lnbits__dialog-card">
<q-form @submit="sendFormData" class="q-gutter-md">
<q-select
filled
dense
emit-value
v-model="formDialog.data.wallet"
:options="g.user.walletOptions"
label="Wallet *"
>
</q-select>
<q-input
filled
dense
v-model.trim="formDialog.data.name"
type="name"
label="Form name "
></q-input>
<q-input
filled
dense
v-model.trim="formDialog.data.description"
type="textarea"
label="Description "
></q-input>
<q-input
filled
dense
v-model.number="formDialog.data.costpword"
type="number"
label="Amount per word"
></q-input>
<div class="row q-mt-lg">
<q-btn
v-if="formDialog.data.id"
unelevated
color="deep-purple"
type="submit"
>Update Form</q-btn
>
<q-btn
v-else
unelevated
color="deep-purple"
:disable="formDialog.data.costpword == null || formDialog.data.costpword < 0 || formDialog.data.name == null"
type="submit"
>Create Form</q-btn
>
<q-btn v-close-popup flat color="grey" class="q-ml-auto"
>Cancel</q-btn
>
</div>
</q-form>
</q-card>
</q-dialog>
</div>
{% endblock %} {% block scripts %} {{ window_vars(user) }}
<script>
var mapLNTicket = function (obj) {
obj.date = Quasar.utils.date.formatDate(
new Date(obj.time * 1000),
'YYYY-MM-DD HH:mm'
)
obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.amount)
obj.displayUrl = ['/lnticket/', obj.id].join('')
return obj
}
new Vue({
el: '#vue',
mixins: [windowMixin],
data: function () {
return {
forms: [],
tickets: [],
formsTable: {
columns: [
{name: 'id', align: 'left', label: 'ID', field: 'id'},
{name: 'name', align: 'left', label: 'Name', field: 'name'},
{name: 'wallet', align: 'left', label: 'Wallet', field: 'wallet'},
{
name: 'description',
align: 'left',
label: 'Description',
field: 'description'
},
{
name: 'costpword',
align: 'left',
label: 'Cost Per Word',
field: 'costpword'
},
{
name: 'amountmade',
align: 'left',
label: 'Amount Made',
field: 'amountmade'
}
],
pagination: {
rowsPerPage: 10
}
},
ticketsTable: {
columns: [
{name: 'id', align: 'left', label: 'ID', field: 'id'},
{name: 'name', align: 'left', label: 'Name', field: 'name'},
{name: 'email', align: 'left', label: 'Email', field: 'email'},
{name: 'ltext', align: 'left', label: 'Ticket', field: 'ltext'},
{name: 'sats', align: 'left', label: 'Paid', field: 'sats'}
],
pagination: {
rowsPerPage: 10
}
},
formDialog: {
show: false,
data: {}
}
}
},
methods: {
getTickets: function () {
var self = this
LNbits.api
.request(
'GET',
'/lnticket/api/v1/tickets?all_wallets',
this.g.user.wallets[0].inkey
)
.then(function (response) {
self.tickets = response.data.map(function (obj) {
return mapLNTicket(obj)
})
})
},
deleteTicket: function (ticketId) {
var self = this
var tickets = _.findWhere(this.tickets, {id: ticketId})
LNbits.utils
.confirmDialog('Are you sure you want to delete this ticket')
.onOk(function () {
LNbits.api
.request(
'DELETE',
'/lnticket/api/v1/tickets/' + ticketId,
_.findWhere(self.g.user.wallets, {id: tickets.wallet}).inkey
)
.then(function (response) {
self.tickets = _.reject(self.tickets, function (obj) {
return obj.id == ticketId
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
})
},
exportticketsCSV: function () {
LNbits.utils.exportCSV(this.ticketsTable.columns, this.tickets)
},
getForms: function () {
var self = this
LNbits.api
.request(
'GET',
'/lnticket/api/v1/forms?all_wallets',
this.g.user.wallets[0].inkey
)
.then(function (response) {
self.forms = response.data.map(function (obj) {
return mapLNTicket(obj)
})
})
},
sendFormData: function () {
var wallet = _.findWhere(this.g.user.wallets, {
id: this.formDialog.data.wallet
})
var data = this.formDialog.data
if (data.id) {
this.updateForm(wallet, data)
} else {
this.createForm(wallet, data)
}
},
createForm: function (wallet, data) {
var self = this
LNbits.api
.request('POST', '/lnticket/api/v1/forms', wallet.inkey, data)
.then(function (response) {
self.forms.push(mapLNTicket(response.data))
self.formDialog.show = false
self.formDialog.data = {}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
updateformDialog: function (formId) {
var link = _.findWhere(this.forms, {id: formId})
console.log(link.id)
this.formDialog.data.id = link.id
this.formDialog.data.wallet = link.wallet
this.formDialog.data.name = link.name
this.formDialog.data.description = link.description
this.formDialog.data.costpword = link.costpword
this.formDialog.show = true
},
updateForm: function (wallet, data) {
var self = this
console.log(data)
LNbits.api
.request(
'PUT',
'/lnticket/api/v1/forms/' + data.id,
wallet.inkey,
data
)
.then(function (response) {
self.forms = _.reject(self.forms, function (obj) {
return obj.id == data.id
})
self.forms.push(mapLNTicket(response.data))
self.formDialog.show = false
self.formDialog.data = {}
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
},
deleteForm: function (formsId) {
var self = this
var forms = _.findWhere(this.forms, {id: formsId})
LNbits.utils
.confirmDialog('Are you sure you want to delete this form link?')
.onOk(function () {
LNbits.api
.request(
'DELETE',
'/lnticket/api/v1/forms/' + formsId,
_.findWhere(self.g.user.wallets, {id: forms.wallet}).inkey
)
.then(function (response) {
self.forms = _.reject(self.forms, function (obj) {
return obj.id == formsId
})
})
.catch(function (error) {
LNbits.utils.notifyApiError(error)
})
})
},
exportformsCSV: function () {
LNbits.utils.exportCSV(this.formsTable.columns, this.forms)
}
},
created: function () {
if (this.g.user.wallets.length) {
this.getTickets()
this.getForms()
}
}
})
</script>
{% endblock %}

22
lnbits/extensions/lnticket/views.py

@ -0,0 +1,22 @@
from flask import g, abort, render_template
from lnbits.decorators import check_user_exists, validate_uuids
from http import HTTPStatus
from lnbits.extensions.lnticket import lnticket_ext
from .crud import get_form
@lnticket_ext.route("/")
@validate_uuids(["usr"], required=True)
@check_user_exists()
def index():
return render_template("lnticket/index.html", user=g.user)
@lnticket_ext.route("/<form_id>")
def display(form_id):
form = get_form(form_id) or abort(HTTPStatus.NOT_FOUND, "LNTicket does not exist.")
print(form.id)
return render_template("lnticket/display.html", form_id=form.id, form_name=form.name, form_desc=form.description, form_costpword=form.costpword)

143
lnbits/extensions/lnticket/views_api.py

@ -0,0 +1,143 @@
from flask import g, jsonify, request
from http import HTTPStatus
from lnbits.core.crud import get_user, get_wallet
from lnbits.core.services import create_invoice
from lnbits.decorators import api_check_wallet_key, api_validate_post_request
from lnbits.settings import WALLET
from lnbits.extensions.lnticket import lnticket_ext
from .crud import create_ticket, get_ticket, get_tickets, delete_ticket, create_form, update_form, get_form, get_forms, delete_form
#########FORMS##########
@lnticket_ext.route("/api/v1/forms", methods=["GET"])
@api_check_wallet_key("invoice")
def api_forms():
wallet_ids = [g.wallet.id]
if "all_wallets" in request.args:
wallet_ids = get_user(g.wallet.user).wallet_ids
return jsonify([form._asdict() for form in get_forms(wallet_ids)]), HTTPStatus.OK
@lnticket_ext.route("/api/v1/forms", methods=["POST"])
@lnticket_ext.route("/api/v1/forms/<form_id>", methods=["PUT"])
@api_check_wallet_key("invoice")
@api_validate_post_request(
schema={
"wallet": {"type": "string", "empty": False, "required": True},
"name": {"type": "string", "empty": False, "required": True},
"description": {"type": "string", "min": 0, "required": True},
"costpword": {"type": "integer", "min": 0, "required": True}
}
)
def api_form_create(form_id=None):
if form_id:
form = get_form(form_id)
print(g.data)
if not form:
return jsonify({"message": "Form does not exist."}), HTTPStatus.NOT_FOUND
if form.wallet != g.wallet.id:
return jsonify({"message": "Not your form."}), HTTPStatus.FORBIDDEN
form = update_form(form_id, **g.data)
else:
form = create_form(**g.data)
return jsonify(form._asdict()), HTTPStatus.CREATED
@lnticket_ext.route("/api/v1/forms/<form_id>", methods=["DELETE"])
@api_check_wallet_key("invoice")
def api_form_delete(form_id):
form = get_form(form_id)
if not form:
return jsonify({"message": "Form does not exist."}), HTTPStatus.NOT_FOUND
if form.wallet != g.wallet.id:
return jsonify({"message": "Not your form."}), HTTPStatus.FORBIDDEN
delete_form(form_id)
return "", HTTPStatus.NO_CONTENT
#########tickets##########
@lnticket_ext.route("/api/v1/tickets", methods=["GET"])
@api_check_wallet_key("invoice")
def api_tickets():
wallet_ids = [g.wallet.id]
if "all_wallets" in request.args:
wallet_ids = get_user(g.wallet.user).wallet_ids
return jsonify([form._asdict() for form in get_tickets(wallet_ids)]), HTTPStatus.OK
@lnticket_ext.route("/api/v1/tickets/<form_id>/<sats>", methods=["GET"])
def api_ticket_create(form_id, sats):
form = get_form(form_id)
try:
checking_id, payment_request = create_invoice(
wallet_id=form.wallet, amount=sats, memo=f"#lnticket {form_id}"
)
except Exception as e:
return jsonify({"message": str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR
return jsonify({"checking_id": checking_id, "payment_request": payment_request}), HTTPStatus.OK
@lnticket_ext.route("/api/v1/tickets/<checking_id>", methods=["POST"])
@api_validate_post_request(
schema={
"form": {"type": "string", "empty": False, "required": True},
"name": {"type": "string", "empty": False, "required": True},
"email": {"type": "string", "empty": False, "required": True},
"ltext": {"type": "string", "empty": False, "required": True},
"sats": {"type": "integer", "min": 0, "required": True}
})
def api_ticket_send_ticket(checking_id):
form = get_form(g.data['form'])
if not form:
return jsonify({"message": "LNTicket does not exist."}), HTTPStatus.NOT_FOUND
try:
is_paid = not WALLET.get_invoice_status(checking_id).pending
except Exception:
return jsonify({"message": "Not paid."}), HTTPStatus.NOT_FOUND
if is_paid:
wallet = get_wallet(form.wallet)
payment = wallet.get_payment(checking_id)
payment.set_pending(False)
create_ticket(wallet=form.wallet, **g.data)
return jsonify({"paid": True}), HTTPStatus.OK
return jsonify({"paid": False}), HTTPStatus.OK
@lnticket_ext.route("/api/v1/tickets/<ticket_id>", methods=["DELETE"])
@api_check_wallet_key("invoice")
def api_ticket_delete(ticket_id):
ticket = get_ticket(ticket_id)
if not ticket:
return jsonify({"message": "Paywall does not exist."}), HTTPStatus.NOT_FOUND
if ticket.wallet != g.wallet.id:
return jsonify({"message": "Not your ticket."}), HTTPStatus.FORBIDDEN
delete_ticket(ticket_id)
return "", HTTPStatus.NO_CONTENT
Loading…
Cancel
Save