mirror of https://github.com/lukechilds/lnbits.git
10 changed files with 224 additions and 38 deletions
@ -1,10 +1,15 @@ |
|||
from hashlib import sha256 |
|||
from typing import NamedTuple |
|||
|
|||
|
|||
class Paywall(NamedTuple): |
|||
id: str |
|||
wallet: str |
|||
secret: str |
|||
url: str |
|||
memo: str |
|||
amount: int |
|||
time: int |
|||
|
|||
def key_for(self, fingerprint: str) -> str: |
|||
return sha256(f"{self.secret}{fingerprint}".encode("utf-8")).hexdigest() |
|||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,114 @@ |
|||
{% extends "public.html" %} |
|||
|
|||
|
|||
{% block page %} |
|||
<div class="row q-col-gutter-md justify-center"> |
|||
<div class="col-12 col-sm-6 col-md-5 col-lg-4"> |
|||
<q-card class="q-pa-lg"> |
|||
<q-card-section class="q-pa-none"> |
|||
<q-responsive v-if="pr" :ratio="1" class="q-mx-xl q-mb-md"> |
|||
<qrcode v-if="pr" :value="pr" :options="{width: 800}" class="rounded-borders"></qrcode> |
|||
</q-responsive> |
|||
<div v-if="redirectUrl"> |
|||
<p>You can access the URL behind this paywall:<br> |
|||
<strong>{% raw %}{{ redirectUrl }}{% endraw %}</strong></p> |
|||
<div class="row q-mt-lg"> |
|||
<q-btn outline color="grey" type="a" :href="redirectUrl">Open URL</q-btn> |
|||
</div> |
|||
</div> |
|||
</q-card-section> |
|||
</q-card> |
|||
</div> |
|||
<div class="col-12 col-sm-6 col-md-5 col-lg-4 q-gutter-y-md"> |
|||
<q-card> |
|||
<q-card-section> |
|||
<h6 class="text-subtitle1 q-mb-sm q-mt-none">LNbits paywall</h6> |
|||
</q-card-section> |
|||
</q-card> |
|||
</div> |
|||
</div> |
|||
{% endblock %} |
|||
|
|||
{% block scripts %} |
|||
<script src="{{ url_for('paywall.static', filename='vendor/fingerprintjs2@2.1.0/fingerprint2.min.js') }}"></script> |
|||
<script src="{{ url_for('static', filename='vendor/vue-qrcode@1.0.2/vue-qrcode.min.js') }}"></script> |
|||
<script> |
|||
Vue.component(VueQrcode.name, VueQrcode); |
|||
|
|||
new Vue({ |
|||
el: '#vue', |
|||
mixins: [windowMixin], |
|||
data: function () { |
|||
return { |
|||
pr: null, |
|||
fingerprint: { |
|||
hash: null, |
|||
isValid: false |
|||
}, |
|||
redirectUrl: null |
|||
}; |
|||
}, |
|||
methods: { |
|||
getInvoice: function () { |
|||
var self = this; |
|||
|
|||
axios.get( |
|||
'/paywall/api/v1/paywalls/{{ paywall.id }}/invoice' |
|||
).then(function (response) { |
|||
self.pr = response.data.payment_request; |
|||
|
|||
dismissMsg = self.$q.notify({ |
|||
timeout: 0, |
|||
message: 'Waiting for payment...' |
|||
}); |
|||
|
|||
paymentChecker = setInterval(function () { |
|||
axios.post( |
|||
'/paywall/api/v1/paywalls/{{ paywall.id }}/check_invoice', |
|||
{checking_id: response.data.checking_id, fingerprint: self.fingerprint.hash} |
|||
).then(function (res) { |
|||
if (res.data.paid) { |
|||
clearInterval(paymentChecker); |
|||
dismissMsg(); |
|||
self.redirectUrl = res.data.url; |
|||
self.$q.localStorage.set('lnbits.paywall.{{ paywall.id }}', res.data.key); |
|||
|
|||
self.$q.notify({ |
|||
type: 'positive', |
|||
message: 'Payment received!', |
|||
icon: null |
|||
}); |
|||
} |
|||
}); |
|||
}, 2000); |
|||
}); |
|||
} |
|||
}, |
|||
created: function () { |
|||
var self = this; |
|||
|
|||
Fingerprint2.get(function (components) { |
|||
self.fingerprint.hash = Fingerprint2.x64hash128(JSON.stringify(components)); |
|||
|
|||
var key = self.$q.localStorage.getItem('lnbits.paywall.{{ paywall.id }}'); |
|||
|
|||
if (key) { |
|||
axios.post( |
|||
'/paywall/api/v1/paywalls/{{ paywall.id }}/check_access', |
|||
{key: key, fingerprint: self.fingerprint.hash} |
|||
).then(function (response) { |
|||
if (response.data.valid) { |
|||
self.fingerprint.isValid = true; |
|||
self.redirectUrl = response.data.url; |
|||
} else { |
|||
self.getInvoice(); |
|||
} |
|||
}); |
|||
} else { |
|||
self.getInvoice(); |
|||
}; |
|||
}); |
|||
} |
|||
}); |
|||
</script> |
|||
{% endblock %} |
@ -1 +0,0 @@ |
|||
{{ paywall.url }} |
Loading…
Reference in new issue