Checkout v2: Option to display amount in Sats in BIP21 case (#4730)

This commit is contained in:
d11n
2023-03-09 21:36:11 +01:00
committed by GitHub
parent f57eab3008
commit abe29f21f0
16 changed files with 71 additions and 51 deletions

View File

@@ -1305,7 +1305,7 @@
"name":"Satoshis", "name":"Satoshis",
"code":"SATS", "code":"SATS",
"divisibility":0, "divisibility":0,
"symbol":"Sats", "symbol":"sats",
"crypto":true "crypto":true
}, },
{ {

View File

@@ -182,7 +182,7 @@ namespace BTCPayServer.Tests
var invoiceId = s.CreateInvoice(10, "USD", "a@g.com"); var invoiceId = s.CreateInvoice(10, "USD", "a@g.com");
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
Assert.Contains("Sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text); Assert.Contains("sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text);
} }
[Fact(Timeout = TestTimeout)] [Fact(Timeout = TestTimeout)]

View File

@@ -102,7 +102,7 @@ namespace BTCPayServer.Tests
s.Driver.ElementDoesNotExist(By.Id("Address_BTC")); s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
s.Driver.FindElement(By.Id("PayByLNURL")); 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);
s.GoToHome(); s.GoToHome();
s.GoToLightningSettings(); s.GoToLightningSettings();
@@ -111,7 +111,7 @@ namespace BTCPayServer.Tests
Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text); Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text);
s.GoToInvoiceCheckout(invoiceId); s.GoToInvoiceCheckout(invoiceId);
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2")); s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
Assert.Contains("Sats", s.Driver.FindElement(By.Id("AmountDue")).Text); Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
// Expire // Expire
var expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); var expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds"));
@@ -193,6 +193,7 @@ namespace BTCPayServer.Tests
s.GoToHome(); s.GoToHome();
s.GoToStore(StoreNavPages.CheckoutAppearance); s.GoToStore(StoreNavPages.CheckoutAppearance);
s.Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), true); s.Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), true);
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), false);
s.Driver.FindElement(By.Id("Save")).Click(); s.Driver.FindElement(By.Id("Save")).Click();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
@@ -200,6 +201,7 @@ 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")));
Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).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.Id("PayInWallet")).GetAttribute("href"); payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
@@ -214,6 +216,16 @@ namespace BTCPayServer.Tests
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")); s.Driver.FindElement(By.Id("PayByLNURL"));
// Switch to amount displayed in sats
s.GoToHome();
s.GoToStore(StoreNavPages.CheckoutAppearance);
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true);
s.Driver.FindElement(By.Id("Save")).Click();
Assert.Contains("Store successfully updated", s.FindAlertMessage().Text);
s.GoToInvoiceCheckout(invoiceId);
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text);
// BIP21 with LN as default payment method // BIP21 with LN as default payment method
s.GoToHome(); s.GoToHome();

View File

@@ -326,7 +326,7 @@ namespace BTCPayServer.Tests
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest); var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[] var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
{ {
new BitcoinLikePaymentHandler(null, networkProvider, null, null, null), new BitcoinLikePaymentHandler(null, networkProvider, null, null, null, null),
new LightningLikePaymentHandler(null, null, networkProvider, null, null, null), new LightningLikePaymentHandler(null, null, networkProvider, null, null, null),
}); });
var entity = new InvoiceEntity(); var entity = new InvoiceEntity();
@@ -512,7 +512,7 @@ namespace BTCPayServer.Tests
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest); var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[] var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
{ {
new BitcoinLikePaymentHandler(null, networkProvider, null, null, null), new BitcoinLikePaymentHandler(null, networkProvider, null, null, null, null),
new LightningLikePaymentHandler(null, null, networkProvider, null, null, null), new LightningLikePaymentHandler(null, null, networkProvider, null, null, null),
}); });
var entity = new InvoiceEntity(); var entity = new InvoiceEntity();
@@ -1466,14 +1466,14 @@ namespace BTCPayServer.Tests
Assert.Equal(1m / 0.000061m, rule2.BidAsk.Bid); Assert.Equal(1m / 0.000061m, rule2.BidAsk.Bid);
// testing rounding // testing rounding
rule2 = rules.GetRuleFor(CurrencyPair.Parse("Sats_EUR")); rule2 = rules.GetRuleFor(CurrencyPair.Parse("SATS_EUR"));
rule2.ExchangeRates.SetRate("coinbase", CurrencyPair.Parse("BTC_EUR"), new BidAsk(1.23m, 2.34m)); rule2.ExchangeRates.SetRate("coinbase", CurrencyPair.Parse("BTC_EUR"), new BidAsk(1.23m, 2.34m));
Assert.True(rule2.Reevaluate()); Assert.True(rule2.Reevaluate());
Assert.Equal("0.00000001 * (1.23, 2.34)", rule2.ToString(true)); Assert.Equal("0.00000001 * (1.23, 2.34)", rule2.ToString(true));
Assert.Equal(0.0000000234m, rule2.BidAsk.Ask); Assert.Equal(0.0000000234m, rule2.BidAsk.Ask);
Assert.Equal(0.0000000123m, rule2.BidAsk.Bid); Assert.Equal(0.0000000123m, rule2.BidAsk.Bid);
rule2 = rules.GetRuleFor(CurrencyPair.Parse("EUR_Sats")); rule2 = rules.GetRuleFor(CurrencyPair.Parse("EUR_SATS"));
rule2.ExchangeRates.SetRate("coinbase", CurrencyPair.Parse("BTC_EUR"), new BidAsk(1.23m, 2.34m)); rule2.ExchangeRates.SetRate("coinbase", CurrencyPair.Parse("BTC_EUR"), new BidAsk(1.23m, 2.34m));
Assert.True(rule2.Reevaluate()); Assert.True(rule2.Reevaluate());
Assert.Equal("1 / (0.00000001 * (1.23, 2.34))", rule2.ToString(true)); Assert.Equal("1 / (0.00000001 * (1.23, 2.34))", rule2.ToString(true));
@@ -1715,7 +1715,7 @@ namespace BTCPayServer.Tests
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest); var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[] var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
{ {
new BitcoinLikePaymentHandler(null, networkProvider, null, null, null), new BitcoinLikePaymentHandler(null, networkProvider, null, null, null, null),
new LightningLikePaymentHandler(null, null, networkProvider, null, null, null), new LightningLikePaymentHandler(null, null, networkProvider, null, null, null),
}); });
var networkBTC = networkProvider.GetNetwork("BTC"); var networkBTC = networkProvider.GetNetwork("BTC");

View File

@@ -221,7 +221,7 @@ namespace BTCPayServer.Tests
var receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network); var receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network);
string errorCode = receiverAddressType == senderAddressType ? null : "unavailable|any UTXO available"; string errorCode = receiverAddressType == senderAddressType ? null : "unavailable|any UTXO available";
var invoice = receiverUser.BitPay.CreateInvoice(new Invoice() { Price = 50000, Currency = "sats", FullNotifications = true }); var invoice = receiverUser.BitPay.CreateInvoice(new Invoice() { Price = 50000, Currency = "SATS", FullNotifications = true });
if (unsupportedFormats.Contains(receiverAddressType)) if (unsupportedFormats.Contains(receiverAddressType))
{ {
Assert.Null(TestAccount.GetPayjoinBitcoinUrl(invoice, cashCow.Network)); Assert.Null(TestAccount.GetPayjoinBitcoinUrl(invoice, cashCow.Network));

View File

@@ -27,7 +27,6 @@ using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets; using BTCPayServer.Services.Wallets;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Rendering;
@@ -388,6 +387,7 @@ namespace BTCPayServer.Controllers
vm.UseNewCheckout = storeBlob.CheckoutType == Client.Models.CheckoutType.V2; vm.UseNewCheckout = storeBlob.CheckoutType == Client.Models.CheckoutType.V2;
vm.OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback; vm.OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback;
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail; vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail;
vm.LazyPaymentMethods = storeBlob.LazyPaymentMethods; vm.LazyPaymentMethods = storeBlob.LazyPaymentMethods;
vm.RedirectAutomatically = storeBlob.RedirectAutomatically; vm.RedirectAutomatically = storeBlob.RedirectAutomatically;
@@ -507,6 +507,7 @@ namespace BTCPayServer.Controllers
blob.CheckoutType = model.UseNewCheckout ? Client.Models.CheckoutType.V2 : Client.Models.CheckoutType.V1; blob.CheckoutType = model.UseNewCheckout ? Client.Models.CheckoutType.V2 : Client.Models.CheckoutType.V1;
blob.OnChainWithLnInvoiceFallback = model.OnChainWithLnInvoiceFallback; blob.OnChainWithLnInvoiceFallback = model.OnChainWithLnInvoiceFallback;
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
blob.RequiresRefundEmail = model.RequiresRefundEmail; blob.RequiresRefundEmail = model.RequiresRefundEmail;
blob.LazyPaymentMethods = model.LazyPaymentMethods; blob.LazyPaymentMethods = model.LazyPaymentMethods;
blob.RedirectAutomatically = model.RedirectAutomatically; blob.RedirectAutomatically = model.RedirectAutomatically;

View File

@@ -26,6 +26,9 @@ namespace BTCPayServer.Models.StoreViewModels
[Display(Name = "Unify on-chain and lightning payment URL/QR code")] [Display(Name = "Unify on-chain and lightning payment URL/QR code")]
public bool OnChainWithLnInvoiceFallback { get; set; } public bool OnChainWithLnInvoiceFallback { get; set; }
[Display(Name = "Display Lightning payment amounts in Satoshis")]
public bool LightningAmountInSatoshi { get; set; }
[Display(Name = "Default payment method on checkout")] [Display(Name = "Default payment method on checkout")]
public string DefaultPaymentMethod { get; set; } public string DefaultPaymentMethod { get; set; }

View File

@@ -10,6 +10,7 @@ using BTCPayServer.Models;
using BTCPayServer.Models.InvoicingModels; using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Services; using BTCPayServer.Services;
using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using NBitcoin; using NBitcoin;
using NBitcoin.DataEncoders; using NBitcoin.DataEncoders;
using NBXplorer.Models; using NBXplorer.Models;
@@ -23,12 +24,14 @@ namespace BTCPayServer.Payments.Bitcoin
private readonly BTCPayNetworkProvider _networkProvider; private readonly BTCPayNetworkProvider _networkProvider;
private readonly IFeeProviderFactory _FeeRateProviderFactory; private readonly IFeeProviderFactory _FeeRateProviderFactory;
private readonly NBXplorerDashboard _dashboard; private readonly NBXplorerDashboard _dashboard;
private readonly CurrencyNameTable _currencyNameTable;
private readonly Services.Wallets.BTCPayWalletProvider _WalletProvider; private readonly Services.Wallets.BTCPayWalletProvider _WalletProvider;
private readonly Dictionary<string, string> _bech32Prefix; private readonly Dictionary<string, string> _bech32Prefix;
public BitcoinLikePaymentHandler(ExplorerClientProvider provider, public BitcoinLikePaymentHandler(ExplorerClientProvider provider,
BTCPayNetworkProvider networkProvider, BTCPayNetworkProvider networkProvider,
IFeeProviderFactory feeRateProviderFactory, IFeeProviderFactory feeRateProviderFactory,
CurrencyNameTable currencyNameTable,
NBXplorerDashboard dashboard, NBXplorerDashboard dashboard,
Services.Wallets.BTCPayWalletProvider walletProvider) Services.Wallets.BTCPayWalletProvider walletProvider)
{ {
@@ -37,6 +40,7 @@ namespace BTCPayServer.Payments.Bitcoin
_FeeRateProviderFactory = feeRateProviderFactory; _FeeRateProviderFactory = feeRateProviderFactory;
_dashboard = dashboard; _dashboard = dashboard;
_WalletProvider = walletProvider; _WalletProvider = walletProvider;
_currencyNameTable = currencyNameTable;
_bech32Prefix = networkProvider.GetAll().OfType<BTCPayNetwork>() _bech32Prefix = networkProvider.GetAll().OfType<BTCPayNetwork>()
.Where(network => network.NBitcoinNetwork?.Consensus?.SupportSegwit is true).ToDictionary(network => network.CryptoCode, .Where(network => network.NBitcoinNetwork?.Consensus?.SupportSegwit is true).ToDictionary(network => network.CryptoCode,
@@ -63,8 +67,10 @@ namespace BTCPayServer.Payments.Bitcoin
model.FeeRate = paymentMethodDetails.GetFeeRate(); model.FeeRate = paymentMethodDetails.GetFeeRate();
model.PaymentMethodName = GetPaymentMethodName(network); model.PaymentMethodName = GetPaymentMethodName(network);
var bip21Case = network.SupportLightning && storeBlob.OnChainWithLnInvoiceFallback;
var amountInSats = bip21Case && storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC";
string lightningFallback = null; string lightningFallback = null;
if (model.Activated && network.SupportLightning && storeBlob.OnChainWithLnInvoiceFallback) if (model.Activated && bip21Case)
{ {
var lightningInfo = invoiceResponse.CryptoInfo.FirstOrDefault(a => var lightningInfo = invoiceResponse.CryptoInfo.FirstOrDefault(a =>
a.GetpaymentMethodId() == new PaymentMethodId(model.CryptoCode, PaymentTypes.LightningLike)); a.GetpaymentMethodId() == new PaymentMethodId(model.CryptoCode, PaymentTypes.LightningLike));
@@ -135,6 +141,11 @@ namespace BTCPayServer.Payments.Bitcoin
{ {
model.InvoiceBitcoinUrl = model.InvoiceBitcoinUrlQR = string.Empty; model.InvoiceBitcoinUrl = model.InvoiceBitcoinUrlQR = string.Empty;
} }
if (model.Activated && amountInSats)
{
base.PreparePaymentModelForAmountInSats(model, paymentMethod, _currencyNameTable);
}
} }
public override string GetCryptoImage(PaymentMethodId paymentMethodId) public override string GetCryptoImage(PaymentMethodId paymentMethodId)

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Logging; using BTCPayServer.Logging;
@@ -98,6 +99,20 @@ namespace BTCPayServer.Payments
{ {
return null; return null;
} }
public virtual void PreparePaymentModelForAmountInSats(PaymentModel model, IPaymentMethod paymentMethod, CurrencyNameTable currencyNameTable)
{
var satoshiCulture = new CultureInfo(CultureInfo.InvariantCulture.Name)
{
NumberFormat = { NumberGroupSeparator = " " }
};
model.CryptoCode = "sats";
model.BtcDue = Money.Parse(model.BtcDue).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.BtcPaid = Money.Parse(model.BtcPaid).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.OrderAmount = Money.Parse(model.OrderAmount).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.NetworkFee = new Money(model.NetworkFee, MoneyUnit.BTC).ToUnit(MoneyUnit.Satoshi);
model.Rate = currencyNameTable.DisplayFormatCurrency(paymentMethod.Rate / 100_000_000, model.InvoiceCurrency);
}
public Task<IPaymentMethodDetails> CreatePaymentMethodDetails(InvoiceLogs logs, public Task<IPaymentMethodDetails> CreatePaymentMethodDetails(InvoiceLogs logs,
ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod,

View File

@@ -1,23 +1,17 @@
#nullable enable #nullable enable
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using BTCPayServer.Client.Models; using BTCPayServer.Client.Models;
using BTCPayServer.Configuration; using BTCPayServer.Configuration;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.Lightning; using BTCPayServer.Lightning;
using BTCPayServer.Logging; using BTCPayServer.Logging;
using BTCPayServer.Models; using BTCPayServer.Models;
using BTCPayServer.Models.InvoicingModels; using BTCPayServer.Models.InvoicingModels;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices; using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates; using BTCPayServer.Services.Rates;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using NBitcoin;
namespace BTCPayServer.Payments.Lightning namespace BTCPayServer.Payments.Lightning
{ {
@@ -112,23 +106,16 @@ namespace BTCPayServer.Payments.Lightning
var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode); var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode);
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId); var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
var lnurl = cryptoInfo.PaymentUrls?.AdditionalData["LNURLP"].ToObject<string>(); var lnurl = cryptoInfo.PaymentUrls?.AdditionalData["LNURLP"].ToObject<string>();
model.PaymentMethodName = GetPaymentMethodName(network); model.PaymentMethodName = GetPaymentMethodName(network);
model.BtcAddress = lnurl?.Replace(UriScheme, ""); model.BtcAddress = lnurl?.Replace(UriScheme, "");
model.InvoiceBitcoinUrl = lnurl; model.InvoiceBitcoinUrl = lnurl;
model.InvoiceBitcoinUrlQR = lnurl?.ToUpperInvariant().Replace(UriScheme.ToUpperInvariant(), UriScheme); model.InvoiceBitcoinUrlQR = lnurl?.ToUpperInvariant().Replace(UriScheme.ToUpperInvariant(), UriScheme);
model.PeerInfo = ((LNURLPayPaymentMethodDetails)paymentMethod.GetPaymentMethodDetails()).NodeInfo; model.PeerInfo = ((LNURLPayPaymentMethodDetails)paymentMethod.GetPaymentMethodDetails()).NodeInfo;
if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC") if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC")
{ {
var satoshiCulture = new CultureInfo(CultureInfo.InvariantCulture.Name); base.PreparePaymentModelForAmountInSats(model, paymentMethod, _currencyNameTable);
satoshiCulture.NumberFormat.NumberGroupSeparator = " ";
model.CryptoCode = "Sats";
model.BtcDue = Money.Parse(model.BtcDue).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.BtcPaid = Money.Parse(model.BtcPaid).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.OrderAmount = Money.Parse(model.OrderAmount).ToUnit(MoneyUnit.Satoshi)
.ToString("N0", satoshiCulture);
model.NetworkFee = new Money(model.NetworkFee, MoneyUnit.BTC).ToUnit(MoneyUnit.Satoshi);
model.Rate =
_currencyNameTable.DisplayFormatCurrency(paymentMethod.Rate / 100_000_000, model.InvoiceCurrency);
} }
} }

View File

@@ -1,7 +1,6 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -212,24 +211,17 @@ namespace BTCPayServer.Payments.Lightning
StoreBlob storeBlob, IPaymentMethod paymentMethod) StoreBlob storeBlob, IPaymentMethod paymentMethod)
{ {
var paymentMethodId = paymentMethod.GetId(); var paymentMethodId = paymentMethod.GetId();
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId); var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode); var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode);
model.PaymentMethodName = GetPaymentMethodName(network); model.PaymentMethodName = GetPaymentMethodName(network);
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls?.BOLT11; model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls?.BOLT11;
model.InvoiceBitcoinUrlQR = $"lightning:{cryptoInfo.PaymentUrls?.BOLT11?.ToUpperInvariant()?.Substring("LIGHTNING:".Length)}"; model.InvoiceBitcoinUrlQR = $"lightning:{cryptoInfo.PaymentUrls?.BOLT11?.ToUpperInvariant()?.Substring("LIGHTNING:".Length)}";
model.PeerInfo = ((LightningLikePaymentMethodDetails)paymentMethod.GetPaymentMethodDetails()).NodeInfo; model.PeerInfo = ((LightningLikePaymentMethodDetails)paymentMethod.GetPaymentMethodDetails()).NodeInfo;
if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC") if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC")
{ {
var satoshiCulture = new CultureInfo(CultureInfo.InvariantCulture.Name); base.PreparePaymentModelForAmountInSats(model, paymentMethod, _currencyNameTable);
satoshiCulture.NumberFormat.NumberGroupSeparator = " ";
model.CryptoCode = "Sats";
model.BtcDue = Money.Parse(model.BtcDue).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.BtcPaid = Money.Parse(model.BtcPaid).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.OrderAmount = Money.Parse(model.OrderAmount).ToUnit(MoneyUnit.Satoshi).ToString("N0", satoshiCulture);
model.NetworkFee = new Money(model.NetworkFee, MoneyUnit.BTC).ToUnit(MoneyUnit.Satoshi);
model.Rate = _currencyNameTable.DisplayFormatCurrency(paymentMethod.Rate / 100_000_000, model.InvoiceCurrency);
} }
} }
public override string GetCryptoImage(PaymentMethodId paymentMethodId) public override string GetCryptoImage(PaymentMethodId paymentMethodId)

View File

@@ -50,7 +50,7 @@
<div class="paywithRowRight cursorPointer" v-on:click="openPaymentMethodDialog"> <div class="paywithRowRight cursorPointer" v-on:click="openPaymentMethodDialog">
<span class="payment__currencies " v-show="!changingCurrencies"> <span class="payment__currencies " v-show="!changingCurrencies">
<img v-bind:src="srvModel.cryptoImage" /> <img v-bind:src="srvModel.cryptoImage" />
<span>{{srvModel.paymentMethodName}} ({{srvModel.cryptoCodeSrv}})</span> <span>{{srvModel.paymentMethodName}} ({{srvModel.cryptoCode}})</span>
<span v-show="srvModel.isLightning">&#9889;</span> <span v-show="srvModel.isLightning">&#9889;</span>
<span class="clickable_indicator fa fa-angle-right"></span> <span class="clickable_indicator fa fa-angle-right"></span>
</span> </span>
@@ -75,7 +75,7 @@
{ {
<div class="payment__currencies_noborder"> <div class="payment__currencies_noborder">
<img v-bind:src="srvModel.cryptoImage" /> <img v-bind:src="srvModel.cryptoImage" />
<span>{{srvModel.paymentMethodName}} ({{srvModel.cryptoCodeSrv}})</span> <span>{{srvModel.paymentMethodName}} ({{srvModel.cryptoCode}})</span>
<span v-show="srvModel.isLightning">&#9889;</span> <span v-show="srvModel.isLightning">&#9889;</span>
</div> </div>
} }
@@ -102,8 +102,8 @@
<span>{{ srvModel.btcDue }} {{ srvModel.cryptoCode }}</span> <span>{{ srvModel.btcDue }} {{ srvModel.cryptoCode }}</span>
</div> </div>
<div class="single-item-order__right__ex-rate" v-if="srvModel.orderAmountFiat && srvModel.cryptoCode"> <div class="single-item-order__right__ex-rate" v-if="srvModel.orderAmountFiat && srvModel.cryptoCode">
<span v-if="srvModel.cryptoCodeSrv === 'Sats'">1 Sat = {{ srvModel.rate }}</span> <span v-if="srvModel.cryptoCode === 'sats'">1 sat = {{ srvModel.rate }}</span>
<span v-else>1 {{ srvModel.cryptoCodeSrv }} = {{ srvModel.rate }}</span> <span v-else>1 {{ srvModel.cryptoCode }} = {{ srvModel.rate }}</span>
</div> </div>
</div> </div>
<span class="fa fa-angle-double-down" v-if="!srvModel.isUnsetTopUp"></span> <span class="fa fa-angle-double-down" v-if="!srvModel.isUnsetTopUp"></span>

View File

@@ -358,8 +358,6 @@
if (jsonData.paymentMethodId === this.srvModel.paymentMethodId) { if (jsonData.paymentMethodId === this.srvModel.paymentMethodId) {
this.changingCurrencies = false; this.changingCurrencies = false;
} }
// displaying satoshis for lightning payments
jsonData.cryptoCodeSrv = jsonData.cryptoCode;
// expand line items to show details on amount due for multi-transaction payment // expand line items to show details on amount due for multi-transaction payment
if (this.srvModel.txCount === 1 && jsonData.txCount > 1) { if (this.srvModel.txCount === 1 && jsonData.txCount > 1) {
this.onlyExpandLineItems(); this.onlyExpandLineItems();

View File

@@ -193,8 +193,8 @@
<div v-if="srvModel.rate && srvModel.cryptoCode"> <div v-if="srvModel.rate && srvModel.cryptoCode">
<dt v-t="'exchange_rate'"></dt> <dt v-t="'exchange_rate'"></dt>
<dd :data-clipboard="srvModel.rate" :data-clipboard-confirm="$t('copy_confirm')"> <dd :data-clipboard="srvModel.rate" :data-clipboard-confirm="$t('copy_confirm')">
<template v-if="srvModel.cryptoCodeSrv === 'Sats'">1 Sat = {{ srvModel.rate }}</template> <template v-if="srvModel.cryptoCode === 'sats'">1 sat = {{ srvModel.rate }}</template>
<template v-else>1 {{ srvModel.cryptoCodeSrv }} = {{ srvModel.rate }}</template> <template v-else>1 {{ srvModel.cryptoCode }} = {{ srvModel.rate }}</template>
</dd> </dd>
</div> </div>
<div v-if="srvModel.networkFee"> <div v-if="srvModel.networkFee">

View File

@@ -94,6 +94,10 @@
<vc:icon symbol="info" /> <vc:icon symbol="info" />
</a> </a>
</div> </div>
<div class="form-check">
<input asp-for="LightningAmountInSatoshi" type="checkbox" class="form-check-input" />
<label asp-for="LightningAmountInSatoshi" class="form-check-label"></label>
</div>
<div class="checkout-settings collapse @(Model.UseNewCheckout ? "" : "show")" id="OldCheckoutSettings"> <div class="checkout-settings collapse @(Model.UseNewCheckout ? "" : "show")" id="OldCheckoutSettings">
<div class="form-check"> <div class="form-check">
<input asp-for="RequiresRefundEmail" type="checkbox" class="form-check-input" /> <input asp-for="RequiresRefundEmail" type="checkbox" class="form-check-input" />

View File

@@ -260,10 +260,7 @@ function initApp() {
const { status } = data; const { status } = data;
window.parent.postMessage({ invoiceId, status }, '*'); window.parent.postMessage({ invoiceId, status }, '*');
} }
// displaying satoshis for lightning payments
data.cryptoCodeSrv = data.cryptoCode;
const newEnd = new Date(); const newEnd = new Date();
newEnd.setSeconds(newEnd.getSeconds() + data.expirationSeconds); newEnd.setSeconds(newEnd.getSeconds() + data.expirationSeconds);
this.endDate = newEnd; this.endDate = newEnd;