displaying lnurlpay success_actions.
This commit is contained in:
parent
8d135489ab
commit
cf0bd7ece8
6 changed files with 121 additions and 23 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
/* globals windowMixin, decode, Vue, VueQrcodeReader, VueQrcode, Quasar, LNbits, _, EventHub, Chart */
|
/* globals windowMixin, decode, Vue, VueQrcodeReader, VueQrcode, Quasar, LNbits, _, EventHub, Chart, decryptLnurlPayAES */
|
||||||
|
|
||||||
Vue.component(VueQrcode.name, VueQrcode)
|
Vue.component(VueQrcode.name, VueQrcode)
|
||||||
Vue.use(VueQrcodeReader)
|
Vue.use(VueQrcodeReader)
|
||||||
|
|
@ -248,7 +248,7 @@ new Vue({
|
||||||
var checker = this.parse.paymentChecker
|
var checker = this.parse.paymentChecker
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
clearInterval(checker)
|
clearInterval(checker)
|
||||||
}, 1000)
|
}, 10000)
|
||||||
},
|
},
|
||||||
createInvoice: function () {
|
createInvoice: function () {
|
||||||
this.receive.status = 'loading'
|
this.receive.status = 'loading'
|
||||||
|
|
@ -469,7 +469,7 @@ new Vue({
|
||||||
switch (response.data.success_action.tag) {
|
switch (response.data.success_action.tag) {
|
||||||
case 'url':
|
case 'url':
|
||||||
this.$q.notify({
|
this.$q.notify({
|
||||||
message: `<a target="_blank" style="color:inherit" href="${response.data.success_action.url}">${response.data.success_action.url}</a>`,
|
message: `<a target="_blank" style="color: inherit" href="${response.data.success_action.url}">${response.data.success_action.url}</a>`,
|
||||||
caption: response.data.success_action.description,
|
caption: response.data.success_action.description,
|
||||||
html: true,
|
html: true,
|
||||||
type: 'info',
|
type: 'info',
|
||||||
|
|
@ -486,6 +486,26 @@ new Vue({
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
case 'aes':
|
case 'aes':
|
||||||
|
LNbits.api
|
||||||
|
.getPayment(this.g.wallet, response.data.payment_hash)
|
||||||
|
.then(
|
||||||
|
({data: payment}) =>
|
||||||
|
console.log(payment) ||
|
||||||
|
decryptLnurlPayAES(
|
||||||
|
response.data.success_action,
|
||||||
|
payment.preimage
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(value => {
|
||||||
|
this.$q.notify({
|
||||||
|
message: value,
|
||||||
|
caption: response.data.success_action.description,
|
||||||
|
html: true,
|
||||||
|
type: 'info',
|
||||||
|
timeout: 0,
|
||||||
|
closeBtn: true
|
||||||
|
})
|
||||||
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -211,14 +211,14 @@ async def api_payment(payment_hash):
|
||||||
if not payment:
|
if not payment:
|
||||||
return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND
|
return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND
|
||||||
elif not payment.pending:
|
elif not payment.pending:
|
||||||
return jsonify({"paid": True}), HTTPStatus.OK
|
return jsonify({"paid": True, "preimage": payment.preimage}), HTTPStatus.OK
|
||||||
|
|
||||||
try:
|
try:
|
||||||
payment.check_pending()
|
payment.check_pending()
|
||||||
except Exception:
|
except Exception:
|
||||||
return jsonify({"paid": False}), HTTPStatus.OK
|
return jsonify({"paid": False}), HTTPStatus.OK
|
||||||
|
|
||||||
return jsonify({"paid": not payment.pending}), HTTPStatus.OK
|
return jsonify({"paid": not payment.pending, "preimage": payment.preimage}), HTTPStatus.OK
|
||||||
|
|
||||||
|
|
||||||
@core_app.route("/api/v1/payments/sse", methods=["GET"])
|
@core_app.route("/api/v1/payments/sse", methods=["GET"])
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
/* globals moment, Vue, EventHub, axios, Quasar, _ */
|
/* globals crypto, moment, Vue, axios, Quasar, _ */
|
||||||
|
|
||||||
var LOCALE = 'en'
|
window.LOCALE = 'en'
|
||||||
|
window.EventHub = new Vue()
|
||||||
var EventHub = new Vue()
|
window.LNbits = {
|
||||||
|
|
||||||
var LNbits = {
|
|
||||||
api: {
|
api: {
|
||||||
request: function (method, url, apiKey, data) {
|
request: function (method, url, apiKey, data) {
|
||||||
return axios({
|
return axios({
|
||||||
|
|
@ -106,7 +104,7 @@ var LNbits = {
|
||||||
)
|
)
|
||||||
obj.msat = obj.balance
|
obj.msat = obj.balance
|
||||||
obj.sat = Math.round(obj.balance / 1000)
|
obj.sat = Math.round(obj.balance / 1000)
|
||||||
obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.sat)
|
obj.fsat = new Intl.NumberFormat(window.LOCALE).format(obj.sat)
|
||||||
obj.url = ['/wallet?usr=', obj.user, '&wal=', obj.id].join('')
|
obj.url = ['/wallet?usr=', obj.user, '&wal=', obj.id].join('')
|
||||||
return obj
|
return obj
|
||||||
},
|
},
|
||||||
|
|
@ -134,7 +132,7 @@ var LNbits = {
|
||||||
obj.msat = obj.amount
|
obj.msat = obj.amount
|
||||||
obj.sat = obj.msat / 1000
|
obj.sat = obj.msat / 1000
|
||||||
obj.tag = obj.extra.tag
|
obj.tag = obj.extra.tag
|
||||||
obj.fsat = new Intl.NumberFormat(LOCALE).format(obj.sat)
|
obj.fsat = new Intl.NumberFormat(window.LOCALE).format(obj.sat)
|
||||||
obj.isIn = obj.amount > 0
|
obj.isIn = obj.amount > 0
|
||||||
obj.isOut = obj.amount < 0
|
obj.isOut = obj.amount < 0
|
||||||
obj.isPaid = obj.pending === 0
|
obj.isPaid = obj.pending === 0
|
||||||
|
|
@ -157,13 +155,13 @@ var LNbits = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
formatCurrency: function (value, currency) {
|
formatCurrency: function (value, currency) {
|
||||||
return new Intl.NumberFormat(LOCALE, {
|
return new Intl.NumberFormat(window.LOCALE, {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: currency
|
currency: currency
|
||||||
}).format(value)
|
}).format(value)
|
||||||
},
|
},
|
||||||
formatSat: function (value) {
|
formatSat: function (value) {
|
||||||
return new Intl.NumberFormat(LOCALE).format(value)
|
return new Intl.NumberFormat(window.LOCALE).format(value)
|
||||||
},
|
},
|
||||||
notifyApiError: function (error) {
|
notifyApiError: function (error) {
|
||||||
var types = {
|
var types = {
|
||||||
|
|
@ -246,7 +244,7 @@ var LNbits = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var windowMixin = {
|
window.windowMixin = {
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
g: {
|
g: {
|
||||||
|
|
@ -276,17 +274,17 @@ var windowMixin = {
|
||||||
created: function () {
|
created: function () {
|
||||||
this.$q.dark.set(this.$q.localStorage.getItem('lnbits.darkMode'))
|
this.$q.dark.set(this.$q.localStorage.getItem('lnbits.darkMode'))
|
||||||
if (window.user) {
|
if (window.user) {
|
||||||
this.g.user = Object.freeze(LNbits.map.user(window.user))
|
this.g.user = Object.freeze(window.LNbits.map.user(window.user))
|
||||||
}
|
}
|
||||||
if (window.wallet) {
|
if (window.wallet) {
|
||||||
this.g.wallet = Object.freeze(LNbits.map.wallet(window.wallet))
|
this.g.wallet = Object.freeze(window.LNbits.map.wallet(window.wallet))
|
||||||
}
|
}
|
||||||
if (window.extensions) {
|
if (window.extensions) {
|
||||||
var user = this.g.user
|
var user = this.g.user
|
||||||
this.g.extensions = Object.freeze(
|
this.g.extensions = Object.freeze(
|
||||||
window.extensions
|
window.extensions
|
||||||
.map(function (data) {
|
.map(function (data) {
|
||||||
return LNbits.map.extension(data)
|
return window.LNbits.map.extension(data)
|
||||||
})
|
})
|
||||||
.map(function (obj) {
|
.map(function (obj) {
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|
@ -303,3 +301,27 @@ var windowMixin = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.decryptLnurlPayAES = function (success_action, preimage) {
|
||||||
|
let keyb = new Uint8Array(
|
||||||
|
preimage.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16))
|
||||||
|
)
|
||||||
|
|
||||||
|
return crypto.subtle
|
||||||
|
.importKey('raw', keyb, {name: 'AES-CBC', length: 256}, false, ['decrypt'])
|
||||||
|
.then(key => {
|
||||||
|
let ivb = Uint8Array.from(window.atob(success_action.iv), c =>
|
||||||
|
c.charCodeAt(0)
|
||||||
|
)
|
||||||
|
let ciphertextb = Uint8Array.from(
|
||||||
|
window.atob(success_action.ciphertext),
|
||||||
|
c => c.charCodeAt(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
return crypto.subtle.decrypt({name: 'AES-CBC', iv: ivb}, key, ciphertextb)
|
||||||
|
})
|
||||||
|
.then(valueb => {
|
||||||
|
let decoder = new TextDecoder('utf-8')
|
||||||
|
return decoder.decode(valueb)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
/* global Vue, moment, LNbits, EventHub */
|
/* global Vue, moment, LNbits, EventHub, decryptLnurlPayAES */
|
||||||
|
|
||||||
Vue.component('lnbits-fsat', {
|
Vue.component('lnbits-fsat', {
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -199,10 +199,64 @@ Vue.component('lnbits-payment-details', {
|
||||||
<div class="col-3"><b>Payment hash</b>:</div>
|
<div class="col-3"><b>Payment hash</b>:</div>
|
||||||
<div class="col-9 text-wrap mono">{{ payment.payment_hash }}</div>
|
<div class="col-9 text-wrap mono">{{ payment.payment_hash }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" v-if="payment.preimage">
|
<div class="row" v-if="hasPreimage">
|
||||||
<div class="col-3"><b>Payment proof</b>:</div>
|
<div class="col-3"><b>Payment proof</b>:</div>
|
||||||
<div class="col-9 text-wrap mono">{{ payment.preimage }}</div>
|
<div class="col-9 text-wrap mono">{{ payment.preimage }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row" v-if="hasSuccessAction">
|
||||||
|
<div class="col-3"><b>Success action</b>:</div>
|
||||||
|
<div class="col-9">
|
||||||
|
<lnbits-lnurlpay-success-action
|
||||||
|
:payment="payment"
|
||||||
|
:success_action="payment.extra.success_action"
|
||||||
|
></lnbits-lnurlpay-success-action>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`
|
`,
|
||||||
|
computed: {
|
||||||
|
hasPreimage() {
|
||||||
|
return (
|
||||||
|
this.payment.preimage &&
|
||||||
|
this.payment.preimage !==
|
||||||
|
'0000000000000000000000000000000000000000000000000000000000000000'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
hasSuccessAction() {
|
||||||
|
return (
|
||||||
|
this.hasPreimage &&
|
||||||
|
this.payment.extra &&
|
||||||
|
this.payment.extra.success_action
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Vue.component('lnbits-lnurlpay-success-action', {
|
||||||
|
props: ['payment', 'success_action'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
decryptedValue: this.success_action.ciphertext
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<p class="q-mb-sm">{{ success_action.message || success_action.description }}</p>
|
||||||
|
<code v-if="decryptedValue" class="text-h6 q-mt-sm q-mb-none">
|
||||||
|
{{ decryptedValue }}
|
||||||
|
</code>
|
||||||
|
<p v-else-if="success_action.url" class="text-h6 q-mt-sm q-mb-none">
|
||||||
|
<a target="_blank" style="color: inherit;" :href="success_action.url">{{ success_action.url }}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
mounted: function () {
|
||||||
|
if (this.success_action.tag !== 'aes') return null
|
||||||
|
|
||||||
|
decryptLnurlPayAES(this.success_action, this.payment.preimage).then(
|
||||||
|
value => {
|
||||||
|
this.decryptedValue = value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class LNPayWallet(Wallet):
|
||||||
f"Wallet {data['user_label']} (data['id']) not active, but {data['statusType']['name']}", 0
|
f"Wallet {data['user_label']} (data['id']) not active, but {data['statusType']['name']}", 0
|
||||||
)
|
)
|
||||||
|
|
||||||
return StatusResponse(None, data["balance"] / 1000)
|
return StatusResponse(None, data["balance"] * 1000)
|
||||||
|
|
||||||
def create_invoice(
|
def create_invoice(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ class SparkWallet(Wallet):
|
||||||
params = args
|
params = args
|
||||||
elif kwargs:
|
elif kwargs:
|
||||||
params = kwargs
|
params = kwargs
|
||||||
|
else:
|
||||||
|
params = {}
|
||||||
|
|
||||||
r = httpx.post(self.url + "/rpc", headers={"X-Access": self.token}, json={"method": key, "params": params})
|
r = httpx.post(self.url + "/rpc", headers={"X-Access": self.token}, json={"method": key, "params": params})
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue