mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 06:21:44 +01:00
Checkout: Allow NFC/LNURL-W whenever LNURL is available (#4671)
* Checkout: Allow NFC/LNURL-W whenever LNURL is available With what we have in master right now, we display NFC only for top-up invoices. With these changes, we display NFC in all cases, where LNURL is available. Note that this hides LNURL from the list of selectable payment methods, it's only available to use the NFC — and explicitely selectable only for the edge case of top-up invoice + non-unified QR (as before). Rationale: Now that we got NFC tightly integrated, it doesn't make sense to support the NFC experience only for top-up invoices. With this we bring back LNURL for regular invoices as well, but don't make it selectable and use it only for the NFC functionality. * Fix LNURL condition * Improve and test NFC/LNURL display condition Restores what was fixed in #4660. * Fix and test Lightning-only case * Add cache busting for locales
This commit is contained in:
parent
d542a61f5a
commit
d73d0f178f
8 changed files with 86 additions and 39 deletions
|
@ -23,7 +23,7 @@
|
||||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
||||||
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
||||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
||||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="108.0.5359.7100" />
|
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="110.0.5481.7700" />
|
||||||
<PackageReference Include="xunit" Version="2.4.2" />
|
<PackageReference Include="xunit" Version="2.4.2" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
|
|
@ -62,7 +62,7 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Contains("LNURL", s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Text);
|
Assert.Contains("LNURL", s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Text);
|
||||||
var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
var payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
|
var payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
var copyAddress = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
var copyAddress = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
||||||
Assert.Equal($"bitcoin:{address}", payUrl);
|
Assert.Equal($"bitcoin:{address}", payUrl);
|
||||||
Assert.StartsWith("bcrt", s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value"));
|
Assert.StartsWith("bcrt", s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value"));
|
||||||
|
@ -70,15 +70,17 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Equal(address, copyAddress);
|
Assert.Equal(address, copyAddress);
|
||||||
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
||||||
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
|
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
|
||||||
|
s.Driver.ElementDoesNotExist(By.Id("PayByLNURL"));
|
||||||
|
|
||||||
// Switch to LNURL
|
// Switch to LNURL
|
||||||
s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click();
|
s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click();
|
||||||
TestUtils.Eventually(() =>
|
TestUtils.Eventually(() =>
|
||||||
{
|
{
|
||||||
payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
Assert.StartsWith("lightning:lnurl", payUrl);
|
Assert.StartsWith("lightning:lnurl", payUrl);
|
||||||
Assert.StartsWith("lnurl", s.Driver.WaitForElement(By.Id("Lightning_BTC")).GetAttribute("value"));
|
Assert.StartsWith("lnurl", s.Driver.WaitForElement(By.Id("Lightning_BTC")).GetAttribute("value"));
|
||||||
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
||||||
|
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Default payment method
|
// Default payment method
|
||||||
|
@ -91,12 +93,13 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Contains("Bitcoin", s.Driver.WaitForElement(By.CssSelector(".payment-method")).Text);
|
Assert.Contains("Bitcoin", s.Driver.WaitForElement(By.CssSelector(".payment-method")).Text);
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
copyAddress = s.Driver.FindElement(By.Id("Lightning_BTC_LightningLike")).GetAttribute("value");
|
copyAddress = s.Driver.FindElement(By.Id("Lightning_BTC_LightningLike")).GetAttribute("value");
|
||||||
Assert.Equal($"lightning:{address}", payUrl);
|
Assert.Equal($"lightning:{address}", payUrl);
|
||||||
Assert.Equal(address, copyAddress);
|
Assert.Equal(address, copyAddress);
|
||||||
Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue);
|
Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue);
|
||||||
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
||||||
|
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||||
|
|
||||||
// Lightning amount in Sats
|
// Lightning amount in Sats
|
||||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
||||||
|
@ -198,7 +201,7 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
var copyAddressOnchain = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
var copyAddressOnchain = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
||||||
var copyAddressLightning = s.Driver.FindElement(By.Id("Lightning_BTC")).GetAttribute("value");
|
var copyAddressLightning = s.Driver.FindElement(By.Id("Lightning_BTC")).GetAttribute("value");
|
||||||
Assert.StartsWith($"bitcoin:{address}?amount=", payUrl);
|
Assert.StartsWith($"bitcoin:{address}?amount=", payUrl);
|
||||||
|
@ -209,6 +212,7 @@ namespace BTCPayServer.Tests
|
||||||
Assert.StartsWith("lnbcrt", copyAddressLightning);
|
Assert.StartsWith("lnbcrt", copyAddressLightning);
|
||||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?amount=", qrValue);
|
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?amount=", qrValue);
|
||||||
Assert.Contains("&lightning=LNBCRT", qrValue);
|
Assert.Contains("&lightning=LNBCRT", qrValue);
|
||||||
|
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||||
|
|
||||||
// BIP21 with LN as default payment method
|
// BIP21 with LN as default payment method
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
|
@ -216,9 +220,10 @@ namespace BTCPayServer.Tests
|
||||||
s.GoToInvoiceCheckout(invoiceId);
|
s.GoToInvoiceCheckout(invoiceId);
|
||||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||||
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
||||||
payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
Assert.StartsWith("bitcoin:", payUrl);
|
Assert.StartsWith("bitcoin:", payUrl);
|
||||||
Assert.Contains("&lightning=lnbcrt", payUrl);
|
Assert.Contains("&lightning=lnbcrt", payUrl);
|
||||||
|
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||||
|
|
||||||
// Ensure LNURL is enabled
|
// Ensure LNURL is enabled
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
|
@ -233,7 +238,7 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
||||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||||
payUrl = s.Driver.FindElement(By.CssSelector(".btn-primary")).GetAttribute("href");
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
copyAddressOnchain = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
copyAddressOnchain = s.Driver.FindElement(By.Id("Address_BTC")).GetAttribute("value");
|
||||||
copyAddressLightning = s.Driver.FindElement(By.Id("Lightning_BTC")).GetAttribute("value");
|
copyAddressLightning = s.Driver.FindElement(By.Id("Lightning_BTC")).GetAttribute("value");
|
||||||
Assert.StartsWith($"bitcoin:{address}", payUrl);
|
Assert.StartsWith($"bitcoin:{address}", payUrl);
|
||||||
|
@ -243,6 +248,7 @@ namespace BTCPayServer.Tests
|
||||||
Assert.Equal(address, copyAddressOnchain);
|
Assert.Equal(address, copyAddressOnchain);
|
||||||
Assert.StartsWith("lnurl", copyAddressLightning);
|
Assert.StartsWith("lnurl", copyAddressLightning);
|
||||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
||||||
|
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||||
|
|
||||||
// Expiry message should not show amount for topup invoice
|
// Expiry message should not show amount for topup invoice
|
||||||
expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
|
expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
|
||||||
|
@ -282,6 +288,26 @@ namespace BTCPayServer.Tests
|
||||||
Assert.True(paymentInfo.Displayed);
|
Assert.True(paymentInfo.Displayed);
|
||||||
Assert.Contains("This invoice will expire in", paymentInfo.Text);
|
Assert.Contains("This invoice will expire in", paymentInfo.Text);
|
||||||
Assert.Contains("09:5", paymentInfo.Text);
|
Assert.Contains("09:5", paymentInfo.Text);
|
||||||
|
|
||||||
|
// Disable LNURL again
|
||||||
|
s.GoToHome();
|
||||||
|
s.GoToLightningSettings();
|
||||||
|
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), false);
|
||||||
|
s.Driver.FindElement(By.Id("save")).Click();
|
||||||
|
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
|
||||||
|
|
||||||
|
// Test:
|
||||||
|
// - NFC/LNURL-W available with just Lightning
|
||||||
|
// - BIP21 works correctly even though Lightning is default payment method
|
||||||
|
s.GoToHome();
|
||||||
|
invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
|
||||||
|
s.GoToInvoiceCheckout(invoiceId);
|
||||||
|
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||||
|
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
||||||
|
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||||
|
Assert.StartsWith("bitcoin:", payUrl);
|
||||||
|
Assert.Contains("&lightning=lnbcrt", payUrl);
|
||||||
|
s.Driver.FindElement(By.Id("PayByLNURL"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact(Timeout = TestTimeout)]
|
[Fact(Timeout = TestTimeout)]
|
||||||
|
|
|
@ -652,18 +652,20 @@ namespace BTCPayServer.Controllers
|
||||||
var lnurlId = PaymentMethodId.Parse("BTC_LNURLPAY");
|
var lnurlId = PaymentMethodId.Parse("BTC_LNURLPAY");
|
||||||
if (paymentMethodId is null)
|
if (paymentMethodId is null)
|
||||||
{
|
{
|
||||||
var enabledPaymentIds = store.GetEnabledPaymentIds(_NetworkProvider)
|
var enabledPaymentIds = store.GetEnabledPaymentIds(_NetworkProvider).ToArray();
|
||||||
.Where(pmId => storeBlob.CheckoutType == CheckoutType.V1 ||
|
|
||||||
// Exclude LNURL for Checkout v2 + non-top up invoices
|
|
||||||
pmId != lnurlId || invoice.IsUnsetTopUp())
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
// Exclude Lightning if OnChainWithLnInvoiceFallback is active and we have both payment methods
|
// Exclude Lightning if OnChainWithLnInvoiceFallback is active and we have both payment methods
|
||||||
if (storeBlob is { CheckoutType: CheckoutType.V2, OnChainWithLnInvoiceFallback: true } &&
|
if (storeBlob is { CheckoutType: CheckoutType.V2, OnChainWithLnInvoiceFallback: true })
|
||||||
enabledPaymentIds.Contains(btcId) && enabledPaymentIds.Contains(lnId))
|
{
|
||||||
|
if (enabledPaymentIds.Contains(btcId) && enabledPaymentIds.Contains(lnId))
|
||||||
{
|
{
|
||||||
enabledPaymentIds = enabledPaymentIds.Where(pmId => pmId != lnId).ToArray();
|
enabledPaymentIds = enabledPaymentIds.Where(pmId => pmId != lnId).ToArray();
|
||||||
}
|
}
|
||||||
|
if (enabledPaymentIds.Contains(btcId) && enabledPaymentIds.Contains(lnurlId))
|
||||||
|
{
|
||||||
|
enabledPaymentIds = enabledPaymentIds.Where(pmId => pmId != lnurlId).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PaymentMethodId? invoicePaymentId = invoice.GetDefaultPaymentMethod();
|
PaymentMethodId? invoicePaymentId = invoice.GetDefaultPaymentMethod();
|
||||||
PaymentMethodId? storePaymentId = store.GetDefaultPaymentId();
|
PaymentMethodId? storePaymentId = store.GetDefaultPaymentId();
|
||||||
|
@ -688,7 +690,7 @@ namespace BTCPayServer.Controllers
|
||||||
if (paymentMethodId is null)
|
if (paymentMethodId is null)
|
||||||
{
|
{
|
||||||
paymentMethodId = enabledPaymentIds.FirstOrDefault(e => e.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode && e.PaymentType == PaymentTypes.BTCLike) ??
|
paymentMethodId = enabledPaymentIds.FirstOrDefault(e => e.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode && e.PaymentType == PaymentTypes.BTCLike) ??
|
||||||
enabledPaymentIds.FirstOrDefault(e => e.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode && e.PaymentType == PaymentTypes.LightningLike) ??
|
enabledPaymentIds.FirstOrDefault(e => e.CryptoCode == _NetworkProvider.DefaultNetwork.CryptoCode && e.PaymentType != PaymentTypes.LNURLPay) ??
|
||||||
enabledPaymentIds.FirstOrDefault();
|
enabledPaymentIds.FirstOrDefault();
|
||||||
}
|
}
|
||||||
isDefaultPaymentId = true;
|
isDefaultPaymentId = true;
|
||||||
|
@ -703,7 +705,12 @@ namespace BTCPayServer.Controllers
|
||||||
return null;
|
return null;
|
||||||
var paymentMethodTemp = invoice
|
var paymentMethodTemp = invoice
|
||||||
.GetPaymentMethods()
|
.GetPaymentMethods()
|
||||||
.FirstOrDefault(c => paymentMethodId.CryptoCode == c.GetId().CryptoCode);
|
.FirstOrDefault(pm =>
|
||||||
|
{
|
||||||
|
var pmId = pm.GetId();
|
||||||
|
return paymentMethodId.CryptoCode == pmId.CryptoCode &&
|
||||||
|
((invoice.IsUnsetTopUp() && !storeBlob.OnChainWithLnInvoiceFallback) || pmId != lnurlId);
|
||||||
|
});
|
||||||
if (paymentMethodTemp == null)
|
if (paymentMethodTemp == null)
|
||||||
paymentMethodTemp = invoice.GetPaymentMethods().FirstOrDefault();
|
paymentMethodTemp = invoice.GetPaymentMethods().FirstOrDefault();
|
||||||
if (paymentMethodTemp is null)
|
if (paymentMethodTemp is null)
|
||||||
|
@ -768,6 +775,7 @@ namespace BTCPayServer.Controllers
|
||||||
BrandColor = storeBlob.BrandColor,
|
BrandColor = storeBlob.BrandColor,
|
||||||
CheckoutType = invoice.CheckoutType ?? storeBlob.CheckoutType,
|
CheckoutType = invoice.CheckoutType ?? storeBlob.CheckoutType,
|
||||||
HtmlTitle = storeBlob.HtmlTitle ?? "BTCPay Invoice",
|
HtmlTitle = storeBlob.HtmlTitle ?? "BTCPay Invoice",
|
||||||
|
OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback,
|
||||||
CryptoImage = Request.GetRelativePathOrAbsolute(paymentMethodHandler.GetCryptoImage(paymentMethodId)),
|
CryptoImage = Request.GetRelativePathOrAbsolute(paymentMethodHandler.GetCryptoImage(paymentMethodId)),
|
||||||
BtcAddress = paymentMethodDetails.GetPaymentDestination(),
|
BtcAddress = paymentMethodDetails.GetPaymentDestination(),
|
||||||
BtcDue = accounting.Due.ShowMoney(divisibility),
|
BtcDue = accounting.Due.ShowMoney(divisibility),
|
||||||
|
@ -803,9 +811,6 @@ namespace BTCPayServer.Controllers
|
||||||
IsMultiCurrency = invoice.GetPayments(false).Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
|
IsMultiCurrency = invoice.GetPayments(false).Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
|
||||||
StoreId = store.Id,
|
StoreId = store.Id,
|
||||||
AvailableCryptos = invoice.GetPaymentMethods()
|
AvailableCryptos = invoice.GetPaymentMethods()
|
||||||
.Where(i => i.Network != null && storeBlob.CheckoutType == CheckoutType.V1 ||
|
|
||||||
// Exclude LNURL for Checkout v2 + non-top up invoices
|
|
||||||
i.GetId() != lnurlId || invoice.IsUnsetTopUp())
|
|
||||||
.Select(kv =>
|
.Select(kv =>
|
||||||
{
|
{
|
||||||
var availableCryptoPaymentMethodId = kv.GetId();
|
var availableCryptoPaymentMethodId = kv.GetId();
|
||||||
|
@ -835,20 +840,16 @@ namespace BTCPayServer.Controllers
|
||||||
{
|
{
|
||||||
var onchainPM = model.AvailableCryptos.Find(c => c.PaymentMethodId == btcId.ToString());
|
var onchainPM = model.AvailableCryptos.Find(c => c.PaymentMethodId == btcId.ToString());
|
||||||
var lightningPM = model.AvailableCryptos.Find(c => c.PaymentMethodId == lnId.ToString());
|
var lightningPM = model.AvailableCryptos.Find(c => c.PaymentMethodId == lnId.ToString());
|
||||||
var lnurlPM = model.AvailableCryptos.Find(c => c.PaymentMethodId == lnurlId.ToString());
|
|
||||||
if (onchainPM != null && lightningPM != null)
|
if (onchainPM != null && lightningPM != null)
|
||||||
{
|
{
|
||||||
model.AvailableCryptos.Remove(lightningPM);
|
model.AvailableCryptos.Remove(lightningPM);
|
||||||
}
|
}
|
||||||
if (onchainPM != null && lnurlPM != null)
|
|
||||||
{
|
|
||||||
model.AvailableCryptos.Remove(lnurlPM);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentMethodHandler.PreparePaymentModel(model, dto, storeBlob, paymentMethod);
|
paymentMethodHandler.PreparePaymentModel(model, dto, storeBlob, paymentMethod);
|
||||||
model.UISettings = paymentMethodHandler.GetCheckoutUISettings();
|
model.UISettings = paymentMethodHandler.GetCheckoutUISettings();
|
||||||
model.PaymentMethodId = paymentMethodId.ToString();
|
model.PaymentMethodId = paymentMethodId.ToString();
|
||||||
|
model.PaymentType = paymentMethodId.PaymentType.ToString();
|
||||||
var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);
|
var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);
|
||||||
model.TimeLeft = expiration.PrettyPrint();
|
model.TimeLeft = expiration.PrettyPrint();
|
||||||
return model;
|
return model;
|
||||||
|
|
|
@ -32,6 +32,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||||
public List<AvailableCrypto> AvailableCryptos { get; set; } = new();
|
public List<AvailableCrypto> AvailableCryptos { get; set; } = new();
|
||||||
public bool IsModal { get; set; }
|
public bool IsModal { get; set; }
|
||||||
public bool IsUnsetTopUp { get; set; }
|
public bool IsUnsetTopUp { get; set; }
|
||||||
|
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||||
public string CryptoCode { get; set; }
|
public string CryptoCode { get; set; }
|
||||||
public string InvoiceId { get; set; }
|
public string InvoiceId { get; set; }
|
||||||
public string BtcAddress { get; set; }
|
public string BtcAddress { get; set; }
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-link" data-clipboard-target="#Lightning_@Model.PaymentMethodId" :data-clipboard-confirm="$t('copy_confirm')" v-t="'copy'"></button>
|
<button type="button" class="btn btn-link" data-clipboard-target="#Lightning_@Model.PaymentMethodId" :data-clipboard-confirm="$t('copy_confirm')" v-t="'copy'"></button>
|
||||||
</div>
|
</div>
|
||||||
<a v-if="model.invoiceBitcoinUrl" class="btn btn-primary rounded-pill w-100 mt-4" target="_top"
|
<a v-if="model.invoiceBitcoinUrl" class="btn btn-primary rounded-pill w-100 mt-4" target="_top" id="PayInWallet"
|
||||||
:href="model.invoiceBitcoinUrl" :title="$t(hasPayjoin ? 'BIP21 payment link with PayJoin support' : 'BIP21 payment link')" v-t="'pay_in_wallet'"></a>
|
:href="model.invoiceBitcoinUrl" :title="$t(hasPayjoin ? 'BIP21 payment link with PayJoin support' : 'BIP21 payment link')" v-t="'pay_in_wallet'"></a>
|
||||||
@await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-v2-bitcoin-post-content", model = Model})
|
@await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-v2-bitcoin-post-content", model = Model})
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-link" data-clipboard-target="#Lightning_@Model.PaymentMethodId" :data-clipboard-confirm="$t('copy_confirm')" v-t="'copy'"></button>
|
<button type="button" class="btn btn-link" data-clipboard-target="#Lightning_@Model.PaymentMethodId" :data-clipboard-confirm="$t('copy_confirm')" v-t="'copy'"></button>
|
||||||
</div>
|
</div>
|
||||||
<a v-if="model.invoiceBitcoinUrl" class="btn btn-primary rounded-pill w-100 mt-4" target="_top"
|
<a v-if="model.invoiceBitcoinUrl" class="btn btn-primary rounded-pill w-100 mt-4" target="_top" id="PayInWallet"
|
||||||
:href="model.invoiceBitcoinUrl" v-t="'pay_in_wallet'"></a>
|
:href="model.invoiceBitcoinUrl" v-t="'pay_in_wallet'"></a>
|
||||||
@await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-v2-lightning-post-content", model = Model})
|
@await Component.InvokeAsync("UiExtensionPoint", new {location = "checkout-v2-lightning-post-content", model = Model})
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
<template id="lnurl-withdraw-template">
|
<template id="lnurl-withdraw-template">
|
||||||
<template v-if="display">
|
<template v-if="display">
|
||||||
<button v-if="isV2" class="btn btn-secondary rounded-pill w-100 mt-4" type="button"
|
<button v-if="isV2" class="btn btn-secondary rounded-pill w-100 mt-4" type="button"
|
||||||
v-on:click="startScan"
|
:disabled="scanning || submitting" v-on:click="startScan" :id="btnId"
|
||||||
v-bind:disabled="scanning || submitting"
|
:class="{ 'loading': scanning || submitting, 'text-secondary': !supported }">{{btnText}}</button>
|
||||||
v-bind:class="{ 'loading': scanning || submitting, 'text-secondary': !supported }">{{btnText}}</button>
|
|
||||||
<bp-loading-button v-else>
|
<bp-loading-button v-else>
|
||||||
<button v-on:click="startScan" class="action-button" style="margin: 0 45px;width:calc(100% - 90px) !important" v-bind:disabled="scanning || submitting"
|
<button class="action-button" style="margin: 0 45px;width:calc(100% - 90px) !important"
|
||||||
v-bind:class="{ 'loading': scanning || submitting, 'action-button': supported, 'btn btn-text w-100': !supported }">
|
:disabled="scanning || submitting" v-on:click="startScan" :id="btnId"
|
||||||
|
:class="{ 'loading': scanning || submitting, 'action-button': supported, 'btn btn-text w-100': !supported }">
|
||||||
<span class="button-text">{{btnText}}</span>
|
<span class="button-text">{{btnText}}</span>
|
||||||
<div class="loader-wrapper">
|
<div class="loader-wrapper">
|
||||||
@await Html.PartialAsync("~/Views/UIInvoice/Checkout-Spinner.cshtml")
|
@await Html.PartialAsync("~/Views/UIInvoice/Checkout-Spinner.cshtml")
|
||||||
|
@ -26,9 +26,25 @@ Vue.component("lnurl-withdraw-checkout", {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
display: function () {
|
display: function () {
|
||||||
return (
|
const {
|
||||||
this.model.paymentMethodId === 'BTC_LNURLPAY' || (
|
onChainWithLnInvoiceFallback: isUnified,
|
||||||
this.model.paymentMethodId === 'BTC' && this.model.invoiceBitcoinUrl.match(/lightning=lnurl/i)));
|
paymentMethodId: activePaymentMethodId,
|
||||||
|
availableCryptos: availablePaymentMethods,
|
||||||
|
invoiceBitcoinUrl: paymentUrl
|
||||||
|
} = this.model
|
||||||
|
const lnurlwAvailable =
|
||||||
|
// Either we have LN or LNURL available directly
|
||||||
|
!!availablePaymentMethods.find(pm => ['BTC_LNURLPAY', 'BTC_LightningLike'].includes(pm.paymentMethodId)) ||
|
||||||
|
// Or the BIP21 payment URL flags Lightning support
|
||||||
|
!!paymentUrl.match(/lightning=ln/i)
|
||||||
|
return activePaymentMethodId === 'BTC_LNURLPAY' || (
|
||||||
|
// Unified QR/BIP21 case
|
||||||
|
(activePaymentMethodId === 'BTC' && isUnified && lnurlwAvailable) ||
|
||||||
|
// Lightning with LNURL available
|
||||||
|
(activePaymentMethodId === 'BTC_LightningLike' && lnurlwAvailable))
|
||||||
|
},
|
||||||
|
btnId: function () {
|
||||||
|
return this.supported ? 'PayByNFC' : 'PayByLNURL'
|
||||||
},
|
},
|
||||||
btnText: function () {
|
btnText: function () {
|
||||||
if (this.supported) {
|
if (this.supported) {
|
||||||
|
|
|
@ -11,7 +11,10 @@
|
||||||
ViewData["Title"] = Model.HtmlTitle;
|
ViewData["Title"] = Model.HtmlTitle;
|
||||||
|
|
||||||
var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method");
|
var hasPaymentPlugins = UiExtensions.Any(extension => extension.Location == "checkout-payment-method");
|
||||||
var paymentMethodCount = Model.AvailableCryptos.Count;
|
// Show LNURL as selectable payment method only for top up invoices + non-BIP21 case
|
||||||
|
var displayedPaymentMethods = Model.IsUnsetTopUp && !Model.OnChainWithLnInvoiceFallback
|
||||||
|
? Model.AvailableCryptos
|
||||||
|
: Model.AvailableCryptos.Where(c => c.PaymentMethodId != "BTC_LNURLPAY").ToList();
|
||||||
}
|
}
|
||||||
@functions {
|
@functions {
|
||||||
private string PaymentMethodName(PaymentModel.AvailableCrypto pm)
|
private string PaymentMethodName(PaymentModel.AvailableCrypto pm)
|
||||||
|
@ -73,12 +76,12 @@
|
||||||
<div id="PaymentDetails" class="payment-details" v-collapsible="displayPaymentDetails">
|
<div id="PaymentDetails" class="payment-details" v-collapsible="displayPaymentDetails">
|
||||||
<payment-details :srv-model="srvModel" :is-active="isActive" class="pb-4"></payment-details>
|
<payment-details :srv-model="srvModel" :is-active="isActive" class="pb-4"></payment-details>
|
||||||
</div>
|
</div>
|
||||||
@if (paymentMethodCount > 1 || hasPaymentPlugins)
|
@if (displayedPaymentMethods.Count > 1 || hasPaymentPlugins)
|
||||||
{
|
{
|
||||||
<div class="mt-3 mb-2">
|
<div class="mt-3 mb-2">
|
||||||
<h6 class="text-center mb-3" v-t="'pay_with'"></h6>
|
<h6 class="text-center mb-3" v-t="'pay_with'"></h6>
|
||||||
<div class="btcpay-pills d-flex flex-wrap align-items-center justify-content-center gap-2 pb-2">
|
<div class="btcpay-pills d-flex flex-wrap align-items-center justify-content-center gap-2 pb-2">
|
||||||
@foreach (var crypto in Model.AvailableCryptos)
|
@foreach (var crypto in displayedPaymentMethods)
|
||||||
{
|
{
|
||||||
<a asp-action="Checkout" asp-route-invoiceId="@Model.InvoiceId" asp-route-paymentMethodId="@crypto.PaymentMethodId"
|
<a asp-action="Checkout" asp-route-invoiceId="@Model.InvoiceId" asp-route-paymentMethodId="@crypto.PaymentMethodId"
|
||||||
class="btcpay-pill m-0 payment-method"
|
class="btcpay-pill m-0 payment-method"
|
||||||
|
@ -210,7 +213,7 @@
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
<script>
|
<script>
|
||||||
const i18nUrl = @Safe.Json($"{Model.RootPath}locales/checkout/{{{{lng}}}}.json");
|
const i18nUrl = @Safe.Json($"{Model.RootPath}locales/checkout/{{{{lng}}}}.json?v={Env.Version}");
|
||||||
const statusUrl = @Safe.Json(Url.Action("GetStatus", new { invoiceId = Model.InvoiceId }));
|
const statusUrl = @Safe.Json(Url.Action("GetStatus", new { invoiceId = Model.InvoiceId }));
|
||||||
const statusWsUrl = @Safe.Json(Url.Action("GetStatusWebSocket", new { invoiceId = Model.InvoiceId }));
|
const statusWsUrl = @Safe.Json(Url.Action("GetStatusWebSocket", new { invoiceId = Model.InvoiceId }));
|
||||||
const availableLanguages = ['en']; // @Safe.Json(LangService.GetLanguages().Select(language => language.Code));
|
const availableLanguages = ['en']; // @Safe.Json(LangService.GetLanguages().Select(language => language.Code));
|
||||||
|
|
Loading…
Add table
Reference in a new issue