Browse Source

displaying lnurlpay success_actions.

atmext
fiatjaf 4 years ago
parent
commit
cf0bd7ece8
  1. 26
      lnbits/core/static/js/wallet.js
  2. 4
      lnbits/core/views/api.py
  3. 50
      lnbits/static/js/base.js
  4. 60
      lnbits/static/js/components.js
  5. 2
      lnbits/wallets/lnpay.py
  6. 2
      lnbits/wallets/spark.py

26
lnbits/core/static/js/wallet.js

@ -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.use(VueQrcodeReader)
@ -248,7 +248,7 @@ new Vue({
var checker = this.parse.paymentChecker
setTimeout(() => {
clearInterval(checker)
}, 1000)
}, 10000)
},
createInvoice: function () {
this.receive.status = 'loading'
@ -469,7 +469,7 @@ new Vue({
switch (response.data.success_action.tag) {
case 'url':
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,
html: true,
type: 'info',
@ -486,6 +486,26 @@ new Vue({
})
break
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
}
}

4
lnbits/core/views/api.py

@ -211,14 +211,14 @@ async def api_payment(payment_hash):
if not payment:
return jsonify({"message": "Payment does not exist."}), HTTPStatus.NOT_FOUND
elif not payment.pending:
return jsonify({"paid": True}), HTTPStatus.OK
return jsonify({"paid": True, "preimage": payment.preimage}), HTTPStatus.OK
try:
payment.check_pending()
except Exception:
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"])

50
lnbits/static/js/base.js

@ -1,10 +1,8 @@
/* globals moment, Vue, EventHub, axios, Quasar, _ */
/* globals crypto, moment, Vue, axios, Quasar, _ */
var LOCALE = 'en'
var EventHub = new Vue()
var LNbits = {
window.LOCALE = 'en'
window.EventHub = new Vue()
window.LNbits = {
api: {
request: function (method, url, apiKey, data) {
return axios({
@ -106,7 +104,7 @@ var LNbits = {
)
obj.msat = obj.balance
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('')
return obj
},
@ -134,7 +132,7 @@ var LNbits = {
obj.msat = obj.amount
obj.sat = obj.msat / 1000
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.isOut = obj.amount < 0
obj.isPaid = obj.pending === 0
@ -157,13 +155,13 @@ var LNbits = {
})
},
formatCurrency: function (value, currency) {
return new Intl.NumberFormat(LOCALE, {
return new Intl.NumberFormat(window.LOCALE, {
style: 'currency',
currency: currency
}).format(value)
},
formatSat: function (value) {
return new Intl.NumberFormat(LOCALE).format(value)
return new Intl.NumberFormat(window.LOCALE).format(value)
},
notifyApiError: function (error) {
var types = {
@ -246,7 +244,7 @@ var LNbits = {
}
}
var windowMixin = {
window.windowMixin = {
data: function () {
return {
g: {
@ -276,17 +274,17 @@ var windowMixin = {
created: function () {
this.$q.dark.set(this.$q.localStorage.getItem('lnbits.darkMode'))
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) {
this.g.wallet = Object.freeze(LNbits.map.wallet(window.wallet))
this.g.wallet = Object.freeze(window.LNbits.map.wallet(window.wallet))
}
if (window.extensions) {
var user = this.g.user
this.g.extensions = Object.freeze(
window.extensions
.map(function (data) {
return LNbits.map.extension(data)
return window.LNbits.map.extension(data)
})
.map(function (obj) {
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)
})
}

60
lnbits/static/js/components.js

@ -1,4 +1,4 @@
/* global Vue, moment, LNbits, EventHub */
/* global Vue, moment, LNbits, EventHub, decryptLnurlPayAES */
Vue.component('lnbits-fsat', {
props: {
@ -199,10 +199,64 @@ Vue.component('lnbits-payment-details', {
<div class="col-3"><b>Payment hash</b>:</div>
<div class="col-9 text-wrap mono">{{ payment.payment_hash }}</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-9 text-wrap mono">{{ payment.preimage }}</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>
`,
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
}
)
}
})

2
lnbits/wallets/lnpay.py

@ -34,7 +34,7 @@ class LNPayWallet(Wallet):
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(
self,

2
lnbits/wallets/spark.py

@ -29,6 +29,8 @@ class SparkWallet(Wallet):
params = args
elif kwargs:
params = kwargs
else:
params = {}
r = httpx.post(self.url + "/rpc", headers={"X-Access": self.token}, json={"method": key, "params": params})
try:

Loading…
Cancel
Save