Compare commits

...

4 Commits

Author SHA1 Message Date
b0cab55b06 Merge branch 'master' into lnutl-pay-buttobn 2023-06-28 11:56:46 +02:00
d959795d52 Cleanups 2023-06-27 14:02:46 +02:00
0884f5acf6 UI updates 2023-06-27 14:02:46 +02:00
4c27019d96 Support LNURL in pay button 2023-06-27 14:02:46 +02:00
4 changed files with 103 additions and 21 deletions
BTCPayServer
Controllers
Views/Shared
wwwroot/paybutton

@ -443,28 +443,37 @@ namespace BTCPayServer
}
[HttpGet("pay")]
[HttpGet("{storeId}/pay")]
[EnableCors(CorsPolicies.All)]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> GetLNUrlForStore(
string cryptoCode,
string storeId,
string currencyCode = null)
string currency = null,
string orderId = null,
decimal? amount = null)
{
var store = this.HttpContext.GetStoreData();
var store = await _storeRepository.FindStore(storeId);
if (store is null)
return NotFound();
var blob = store.GetStoreBlob();
var blob = store.GetStoreBlob();
if (!blob.AnyoneCanInvoice)
return NotFound("'Anyone can invoice' is turned off");
var metadata = new InvoiceMetadata();
if (!string.IsNullOrEmpty(orderId))
{
metadata.OrderId = orderId;
}
return await GetLNURLRequest(
cryptoCode,
store,
blob,
new CreateInvoiceRequest
{
Currency = currencyCode
Amount = amount,
Metadata = metadata.ToJObject(),
Currency = currency
});
}

@ -1,5 +1,6 @@
@using BTCPayServer.Views.Stores
@inject BTCPayServer.Security.ContentSecurityPolicies Csp
@inject Security.ContentSecurityPolicies Csp
@inject BTCPayNetworkProvider NetworkProvider
@model BTCPayServer.Plugins.PayButton.Models.PayButtonViewModel
@{
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().Id);
@ -14,6 +15,7 @@
<script src="~/vendor/highlightjs/highlight.min.js" asp-append-version="true"></script>
<script src="~/vendor/vuejs/vue.min.js" asp-append-version="true"></script>
<script src="~/vendor/vuejs-vee-validate/vee-validate.js" asp-append-version="true"></script>
<script src="~/vendor/vue-qrcode/vue-qrcode.min.js" asp-append-version="true"></script>
<script src="~/paybutton/paybutton.js" asp-append-version="true"></script>
<template id="template-modal" csp-allow>
if (!window.btcpay) {
@ -116,13 +118,34 @@
</template>
<script>
window.lnurlEndpoint = @Safe.Json(Url.Action("GetLNUrlForStore", "UILNURL", new
{
storeId = Model.StoreId,
cryptoCode = NetworkProvider.DefaultNetwork.CryptoCode
}, "lnurlp", Context.Request.Host.ToString()));
const srvModel = @Safe.Json(Model);
const payButtonCtrl = new Vue({
el: '#payButtonCtrl',
components: {
qrcode: VueQrcode
},
data: {
srvModel: srvModel,
originalButtonImageUrl: srvModel.payButtonImageUrl,
buttonInlineTextMode: false
buttonInlineTextMode: false,
previewLink: "",
lnurlLink: "",
alternativeMode: 'link',
qrOptions: {
width: 256,
height: 256,
margin: 1,
color: {
dark: '#000',
light: '#f5f5f7'
}
}
},
computed: {
imageUrlRequired() {
@ -131,7 +154,7 @@
},
methods: {
inputChanges(event, buttonSize) {
inputChanges(event, buttonSize);
inputChanges(payButtonCtrl, event, buttonSize, );
}
},
watch: {
@ -145,8 +168,9 @@
}
this.inputChanges();
}
}
},
});
inputChanges(payButtonCtrl);
</script>
}
@ -311,10 +335,6 @@
<div class="col-xl-4 mt-4 mt-xl-0">
<h5 class="mb-3">Preview</h5>
<div id="preview"></div>
<div v-show="!srvModel.appIdEndpoint">
<h5 class="mt-4 mb-3">Link</h5>
<span>Alternatively, you can share <a id="preview-link" href="#">this link</a> or encode it in a QR code.</span>
</div>
</div>
</div>
@ -388,7 +408,6 @@
</div>
<h4 class="mt-5 mb-3">Generated Code</h4>
<div class="row" v-show="!errors.any()">
<div class="col-xxl-8">
<pre><code id="mainCode" class="html"></code></pre>
@ -402,6 +421,49 @@
Please fix errors shown in order for code generation to successfully execute.
</div>
</div>
<div v-if="!srvModel.appIdEndpoint && (previewLink || lnurlLink)">
<h4 class="mt-4 mb-3">Alternatives</h4>
<p>You can also share the link/LNURL or encode it in a QR code.</p>
<div class="align-items-center" style="width:256px">
<ul class="nav my-3 btcpay-pills align-items-center gap-2">
<li class="nav-item" v-if="previewLink">
<a class="btcpay-pill" :class="{ active: alternativeMode === 'link' }" data-bs-toggle="tab" data-bs-target="#Alternative-Link" role="tab" href="#Alternative-Link">
Link
</a>
</li>
<li class="nav-item" v-if="previewLink">
<a class="btcpay-pill" :class="{ active: alternativeMode === 'lnurl' }" data-bs-toggle="tab" data-bs-target="#Alternative-LNURL" role="tab" href="#Alternative-LNURL">
LNURL
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane" :class="{ active: alternativeMode === 'link' }" id="Alternative-Link" role="tabpanel">
<a class="qr-container d-inline-block" :class="{ active: true }" :href="previewLink">
<qrcode :value="previewLink" :options="qrOptions" tag="img"></qrcode>
</a>
<div class="input-group mt-3">
<div class="form-floating">
<vc:truncate-center text="previewLink" is-vue="true" padding="15" elastic="true" classes="form-control-plaintext" />
<label>Link URL</label>
</div>
</div>
</div>
<div class="tab-pane" :class="{ active: alternativeMode === 'lnurl' }" id="Alternative-LNURL" role="tabpanel">
<a class="qr-container d-inline-block" :href="lnurlLink">
<qrcode :value="lnurlLink" :options="qrOptions" tag="img"></qrcode>
</a>
<div class="input-group mt-3">
<div class="form-floating">
<vc:truncate-center text="lnurlLink" is-vue="true" padding="15" elastic="true" classes="form-control-plaintext" />
<label>LNURL</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script id="template-paybutton-styles" type="text/template">

@ -11,7 +11,7 @@
<div class="modal-body text-center">
<div class="text-center my-2" :style="{height: `${qrOptions.height}px`}">
<component v-if="currentFragment" :is="currentMode.href ? 'a': 'div'" class="qr-container d-inline-block" :href="currentMode.href">
<qrcode :value="currentFragment" :options="qrOptions"></qrcode>
<qrcode :value="currentFragment" :options="qrOptions"></qrcode>
</component>
</div>
<ul class="nav btcpay-pills justify-content-center mt-4 mb-3" v-if="modes && Object.keys(modes).length > 1">

@ -1,6 +1,3 @@
$(function () {
inputChanges();
});
function esc(input) {
return ('' + input) /* Forces the conversion to string. */
@ -61,7 +58,7 @@ function getScripts(srvModel) {
return scripts
}
function inputChanges(event, buttonSize) {
function inputChanges(vueApp, event, buttonSize) {
if (buttonSize !== null && buttonSize !== undefined) {
srvModel.buttonSize = buttonSize;
}
@ -187,8 +184,22 @@ function inputChanges(event, buttonSize) {
}
});
url = url.href;
$("#preview-link").attr('href', url);
vueApp.previewLink = url;
if (window.lnurlEndpoint){
let lnurlResult = lnurlEndpoint + "?";
if (srvModel.currency){
lnurlResult += `&currency=${srvModel.currency}`;
}
if (srvModel.price){
lnurlResult += `&amount=${srvModel.price}`;
}
if (srvModel.orderId){
lnurlResult += `&orderId=${srvModel.orderId}`;
}
lnurlResult= lnurlResult.replace("?&", "?");
vueApp.lnurlLink = lnurlResult;
}
$('pre code').each(function (i, block) {
hljs.highlightBlock(block);