refactor(tpos): migrate extension

This commit is contained in:
Eneko Illarramendi 2020-04-17 20:39:23 +02:00
parent 2d694c6b8a
commit a455379ee1
8 changed files with 325 additions and 330 deletions

View File

@ -30,6 +30,7 @@ Talisman(
"cdnjs.cloudflare.com",
"code.ionicframework.com",
"code.jquery.com",
"api.opennode.co",
"fonts.googleapis.com",
"fonts.gstatic.com",
"maxcdn.bootstrapcdn.com",

View File

@ -1,6 +1,6 @@
{
"name": "TPOS",
"short_description": "A shareable POS!",
"icon": "dialpad",
"contributors": ["talvasconcelos", "arcbtc"]
"name": "TPoS",
"short_description": "A shareable PoS terminal!",
"icon": "dialpad",
"contributors": ["talvasconcelos", "arcbtc"]
}

View File

@ -5,14 +5,16 @@ def m001_initial(db):
"""
Initial tposs table.
"""
db.execute("""
db.execute(
"""
CREATE TABLE IF NOT EXISTS tposs (
id TEXT PRIMARY KEY,
wallet TEXT NOT NULL,
name TEXT NOT NULL,
currency TEXT NOT NULL
);
""")
"""
)
def migrate():

View File

@ -8,7 +8,7 @@
<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="tposDialog.show = true">New TPoS</q-btn>
<q-btn unelevated color="deep-purple" @click="formDialog.show = true">New TPoS</q-btn>
</q-card-section>
</q-card>
@ -31,7 +31,6 @@
<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"
@ -39,17 +38,15 @@
>
{{ 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="vpn_lock" :color="($q.dark.isActive) ? 'grey-7' : 'grey-5'" type="a" :href="props.row.tpos" target="_blank"></q-btn>
</q-td>
<q-btn unelevated dense size="xs" icon="launch" :color="($q.dark.isActive) ? 'grey-7' : 'grey-5'" type="a" :href="props.row.tpos" target="_blank"></q-btn>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
@ -77,47 +74,43 @@
<q-separator></q-separator>
<q-list>
<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">Tiago's Point of Sale</h5>
<p>Is an instant and secure point of sale terminal. A merchant can use the PoS to accept payments to their LNbits wallet, without having to expose the wallet itself, which means it can even be shared with others. <br/>For example, a merchant could setup multiple TPoSs for each one of their colleagues, and use TPoS to track which colleague has taken sales. The terminal is optimized for mobile use, and is easily shared as a QR code, via the # share function on the TPoS. <br/>
<small> Created by, <a href="https://github.com/talvasconcelos">Tiago Vasconcelos</a></small></p>
group="extras"
icon="info"
label="Info">
<q-card>
<q-card-section>
<h5 class="text-subtitle1 q-mt-none q-mb-sm">Tiago's Point of Sale</h5>
<p>Is an instant and secure point of sale terminal. A merchant can use the PoS to accept payments to their LNbits wallet, without having to expose the wallet itself, which means it can even be shared with others.</p>
<p>For example, a merchant could setup multiple TPoSs for each one of their colleagues, and use TPoS to track which colleague has taken sales. The terminal is optimized for mobile use, and is easily shared as a QR code, via the "#" share function on the TPoS.</p>
<small>Created by <a href="https://github.com/talvasconcelos" target="_blank">Tiago Vasconcelos</a>.</small>
</q-card-section>
</q-card>
</q-card-section>
</q-expansion-item>
</q-expansion-item>
</q-list>
</q-card-section>
</q-card>
</div>
<q-dialog v-model="tposDialog.show" position="top">
<q-dialog v-model="formDialog.show" position="top" @hide="closeFormDialog">
<q-card class="q-pa-lg q-pt-xl" style="width: 500px">
<q-form class="q-gutter-md">
<q-input filled dense
v-model.trim="tposDialog.data.name"
label="Name"
placeholder="Tiago's PoS"></q-input>
<q-select filled dense emit-value v-model="tposDialog.data.wallet" :options="g.user.walletOptions" label="Wallet *">
</q-select>
<q-select filled dense emit-value v-model="tposDialog.data.currency" :options="currencyOptions" label="Currency *">
</q-select>
<q-btn unelevated
color="deep-purple"
:disable="tposDialog.data.currency == null || tposDialog.data.name == null"
@click="createTPoS">Create TPoS</q-btn>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
v-model.trim="formDialog.data.name"
label="Name"
placeholder="Tiago's PoS"></q-input>
<q-select filled dense
emit-value v-model="formDialog.data.wallet"
:options="g.user.walletOptions"
label="Wallet *"></q-select>
<q-select filled dense
emit-value v-model="formDialog.data.currency"
:options="currencyOptions"
label="Currency *"></q-select>
<q-btn unelevated
color="deep-purple"
:disable="formDialog.data.currency == null || formDialog.data.name == null"
@click="createTPoS">Create TPoS</q-btn>
<q-btn v-close-popup flat color="grey" class="q-ml-auto">Cancel</q-btn>
</q-form>
</q-card>
</q-dialog>
@ -141,9 +134,13 @@
return {
tposs: [],
currencyOptions: [
'USD', 'EUR', 'GBP', 'DZD', 'ARP', 'AUD', 'ATS', 'BSD', 'BBD', 'BEF', 'BMD', 'BRL', 'BGL', 'CAD', 'CLP', 'CNY', 'CYP', 'CSK', 'DKK', 'NLG', 'XCD', 'EGP', 'FJD', 'FIM', 'FRF', 'DEM', 'XAU', 'GRD', 'HKD', 'HUF', 'ISK', 'INR', 'IDR', 'IEP', 'ILS', 'ITL', 'JMD', 'JPY', 'JOD', 'KRW', 'LBP', 'LUF', 'MYR', 'MXP', 'NLG', 'NZD', 'NOK', 'PKR', 'XPD', 'PHP', 'XPT', 'PLZ', 'PTE', 'ROL', 'RUR', 'SAR', 'XAG', 'SGD', 'SKK', 'ZAR', 'KRW', 'ESP', 'XDR', 'SDD', 'SEK', 'CHF', 'TWD', 'THB', 'TTD', 'TRL', 'VEB', 'ZMK', 'EUR', 'XCD', 'XDR', 'XAG', 'XAU', 'XPD', 'XPT',
],
'USD', 'EUR', 'GBP', 'DZD', 'ARP', 'AUD', 'ATS', 'BSD', 'BBD', 'BEF', 'BMD', 'BRL', 'BGL', 'CAD', 'CLP',
'CNY', 'CYP', 'CSK', 'DKK', 'NLG', 'XCD', 'EGP', 'FJD', 'FIM', 'FRF', 'DEM', 'XAU', 'GRD', 'HKD', 'HUF',
'ISK', 'INR', 'IDR', 'IEP', 'ILS', 'ITL', 'JMD', 'JPY', 'JOD', 'KRW', 'LBP', 'LUF', 'MYR', 'MXP', 'NLG',
'NZD', 'NOK', 'PKR', 'XPD', 'PHP', 'XPT', 'PLZ', 'PTE', 'ROL', 'RUR', 'SAR', 'XAG', 'SGD', 'SKK', 'ZAR',
'KRW', 'ESP', 'XDR', 'SDD', 'SEK', 'CHF', 'TWD', 'THB', 'TTD', 'TRL', 'VEB', 'ZMK', 'EUR', 'XCD', 'XDR',
'XAG', 'XAU', 'XPD', 'XPT'
],
tpossTable: {
columns: [
{name: 'id', align: 'left', label: 'ID', field: 'id'},
@ -154,13 +151,16 @@
rowsPerPage: 10
}
},
tposDialog: {
formDialog: {
show: false,
data: {}
}
};
},
methods: {
closeFormDialog: function () {
this.formDialog.data = {};
},
getTPoSs: function () {
var self = this;
@ -170,29 +170,25 @@
this.g.user.wallets[0].inkey
).then(function (response) {
self.tposs = response.data.map(function (obj) {
console.log(obj)
return mapTPoS(obj);
});
});
},
createTPoS: function () {
var data = {
name: this.tposDialog.data.name,
currency: this.tposDialog.data.currency
name: this.formDialog.data.name,
currency: this.formDialog.data.currency
};
var self = this;
console.log(this.tposDialog.data.wallet);
LNbits.api.request(
'POST',
'/tpos/api/v1/tposs',
_.findWhere(this.g.user.wallets, {id: this.tposDialog.data.wallet}).inkey,
_.findWhere(this.g.user.wallets, {id: this.formDialog.data.wallet}).inkey,
data
).then(function (response) {
self.tposs.push(mapTPoS(response.data));
self.tposDialog.show = false;
self.tposDialog.data = {};
self.formDialog.show = false;
}).catch(function (error) {
LNbits.utils.notifyApiError(error);
});
@ -202,7 +198,7 @@
var tpos = _.findWhere(this.tposs, {id: tposId});
this.$q.dialog({
message: 'Are you sure you want to delete this TPoS link?',
message: 'Are you sure you want to delete this TPoS?',
ok: {
flat: true,
color: 'orange'
@ -235,5 +231,3 @@
});
</script>
{% endblock %}

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,5 @@
import requests
from flask import g, abort, render_template
from lnbits.core.crud import get_wallet
from lnbits.decorators import check_user_exists, validate_uuids
from lnbits.helpers import Status
@ -19,8 +16,6 @@ def index():
@tpos_ext.route("/<tpos_id>")
def tpos(tpos_id):
tpos = get_tpos(tpos_id) or abort(Status.NOT_FOUND, "tpos does not exist.")
r = requests.get("https://api.opennode.co/v1/rates")
r_json = r.json()
rr = get_wallet(tpos.wallet)
return render_template("tpos/tpos.html", tpos=tpos.id, inkey=rr.inkey, rate=r_json["data"]["BTC" + tpos.currency][tpos.currency], curr=tpos.currency)
tpos = get_tpos(tpos_id) or abort(Status.NOT_FOUND, "TPoS does not exist.")
return render_template("tpos/tpos.html", tpos=tpos)

View File

@ -1,6 +1,6 @@
from flask import g, jsonify, request
from lnbits.core.crud import get_user
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.helpers import Status
@ -30,9 +30,7 @@ def api_tposs():
}
)
def api_tpos_create():
print("poo")
tpos = create_tpos(wallet_id=g.wallet.id, name=g.data["name"], currency=g.data["currency"])
tpos = create_tpos(wallet_id=g.wallet.id, **g.data)
return jsonify(tpos._asdict()), Status.CREATED
@ -50,31 +48,44 @@ def api_tpos_delete(tpos_id):
delete_tpos(tpos_id)
return '', Status.NO_CONTENT
return "", Status.NO_CONTENT
@tpos_ext.route("/api/v1/tposs/invoice/<tpos_id>", methods=["POST"])
@tpos_ext.route("/api/v1/tposs/<tpos_id>/invoices/", methods=["POST"])
@api_validate_post_request(schema={"amount": {"type": "integer", "min": 1, "required": True}})
def api_tpos_create_invoice(tpos_id):
tpos = get_tpos(tpos_id)
if not tpos:
return jsonify({"message": "TPoS does not exist."}), Status.NOT_FOUND
try:
memo = f"TPoS {tpos_id}"
checking_id, payment_request = create_invoice(wallet_id=tpos.wallet, amount=g.data["amount"], memo=memo)
try:
checking_id, payment_request = create_invoice(
wallet_id=tpos.wallet, amount=g.data["amount"], memo=f"#tpos {tpos.name}"
)
except Exception as e:
return jsonify({"message": str(e)}), Status.INTERNAL_SERVER_ERROR
return jsonify({"checking_id": checking_id, "payment_request": payment_request}), Status.OK
return jsonify({"checking_id": checking_id, "payment_request": payment_request}), Status.CREATED
@tpos_ext.route("/api/v1/tposs/invoice/<checking_id>", methods=["GET"])
def api_tpos_check_invoice(checking_id):
print(checking_id)
PAID = WALLET.get_invoice_status(checking_id).paid
if PAID == True:
return jsonify({"PAID": True}), Status.OK
return jsonify({"PAID": False}), Status.OK
@tpos_ext.route("/api/v1/tposs/<tpos_id>/invoices/<checking_id>", methods=["GET"])
def api_tpos_check_invoice(tpos_id, checking_id):
tpos = get_tpos(tpos_id)
if not tpos:
return jsonify({"message": "TPoS does not exist."}), Status.NOT_FOUND
try:
is_paid = not WALLET.get_invoice_status(checking_id).pending
except Exception:
return jsonify({"paid": False}), Status.OK
if is_paid:
wallet = get_wallet(tpos.wallet)
payment = wallet.get_payment(checking_id)
payment.set_pending(False)
return jsonify({"paid": True}), Status.OK
return jsonify({"paid": False}), Status.OK

View File

@ -26,11 +26,15 @@
{% block drawer_toggle %}
<q-btn dense flat round icon="menu" @click="g.visibleDrawer = !g.visibleDrawer"></q-btn>
{% endblock %}
{% if SITE_TITLE != 'LNbits' %}
<q-toolbar-title>{{ SITE_TITLE }}</q-toolbar-title>
{% else %}
<q-toolbar-title><strong>LN</strong>bits</q-toolbar-title>
{% endif %}
<q-toolbar-title>
{% block toolbar_title %}
{% if SITE_TITLE != 'LNbits' %}
{{ SITE_TITLE }}
{% else %}
<strong>LN</strong>bits
{% endif %}
{% endblock %}
</q-toolbar-title>
{% block beta %}
<q-badge color="yellow" text-color="black">
<span><span v-show="$q.screen.gt.sm">USE WITH CAUTION - LNbits wallet is still in </span>BETA</span>
@ -49,23 +53,27 @@
</q-drawer>
{% endblock %}
<q-page-container>
<q-page class="q-px-md q-py-lg" :class="{'q-px-lg': $q.screen.gt.xs}">
{% block page %}{% endblock %}
</q-page>
</q-page-container>
{% block page_container %}
<q-page-container>
<q-page class="q-px-md q-py-lg" :class="{'q-px-lg': $q.screen.gt.xs}">
{% block page %}{% endblock %}
</q-page>
</q-page-container>
{% endblock %}
<q-footer class="bg-transparent q-px-lg q-py-md" :class="{'text-dark': !$q.dark.isActive}">
<q-toolbar>
<q-toolbar-title class="text-caption">
<strong>LN</strong>bits, free and open-source lightning wallet
</q-toolbar-title>
<q-space></q-space>
<q-btn flat dense :color="($q.dark.isActive) ? 'white' : 'deep-purple'" icon="code" type="a" href="https://github.com/arcbtc/lnbits" target="_blank" rel="noopener">
<q-tooltip>View project in GitHub</q-tooltip>
</q-btn>
</q-toolbar>
</q-footer>
{% block footer %}
<q-footer class="bg-transparent q-px-lg q-py-md" :class="{'text-dark': !$q.dark.isActive}">
<q-toolbar>
<q-toolbar-title class="text-caption">
<strong>LN</strong>bits, free and open-source lightning wallet
</q-toolbar-title>
<q-space></q-space>
<q-btn flat dense :color="($q.dark.isActive) ? 'white' : 'deep-purple'" icon="code" type="a" href="https://github.com/arcbtc/lnbits" target="_blank" rel="noopener">
<q-tooltip>View project in GitHub</q-tooltip>
</q-btn>
</q-toolbar>
</q-footer>
{% endblock %}
</q-layout>