mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 09:54:30 +01:00
Merge pull request #2060 from btcpayserver/feat/unified-qr
Unified QR code, BIP21 with lightning fallback
This commit is contained in:
commit
6c7433b2f1
@ -30,14 +30,21 @@ namespace BTCPayServer.Client.Models
|
||||
public double PaymentTolerance { get; set; } = 0;
|
||||
public bool AnyoneCanCreateInvoice { get; set; }
|
||||
|
||||
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool ShowRecommendedFee { get; set; } = true;
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int RecommendedFeeBlockTarget { get; set; } = 1;
|
||||
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DefaultLang { get; set; } = "en";
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
|
||||
public string CustomLogo { get; set; }
|
||||
|
||||
@ -45,16 +52,13 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public string HtmlTitle { get; set; }
|
||||
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public NetworkFeeMode NetworkFeeMode { get; set; } = NetworkFeeMode.Never;
|
||||
|
||||
public bool PayJoinEnabled { get; set; }
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
|
||||
|
||||
[JsonExtensionData]
|
||||
|
@ -770,30 +770,31 @@ namespace BTCPayServer.Tests
|
||||
BitcoinAddress.Create(invoice.BitcoinAddress, Network.RegTest), Money.Coins(0.00005m));
|
||||
});
|
||||
await tester.ExplorerNode.GenerateAsync(1);
|
||||
await Task.Delay(100); // wait a bit for payment to process before fetching new invoice
|
||||
var newInvoice = await user.BitPay.GetInvoiceAsync(invoice.Id);
|
||||
var newBolt11 = newInvoice.CryptoInfo.First(o => o.PaymentUrls.BOLT11 != null).PaymentUrls.BOLT11;
|
||||
var oldBolt11= invoice.CryptoInfo.First(o => o.PaymentUrls.BOLT11 != null).PaymentUrls.BOLT11;
|
||||
Assert.NotEqual(newBolt11,oldBolt11);
|
||||
var newBolt11 = newInvoice.CryptoInfo.First(o => o.PaymentUrls.BOLT11 != null).PaymentUrls.BOLT11;
|
||||
var oldBolt11 = invoice.CryptoInfo.First(o => o.PaymentUrls.BOLT11 != null).PaymentUrls.BOLT11;
|
||||
Assert.NotEqual(newBolt11, oldBolt11);
|
||||
Assert.Equal(newInvoice.BtcDue.GetValue(), BOLT11PaymentRequest.Parse(newBolt11, Network.RegTest).MinimumAmount.ToDecimal(LightMoneyUnit.BTC));
|
||||
|
||||
Logs.Tester.LogInformation($"Paying invoice {newInvoice.Id} remaining due amount {newInvoice.BtcDue.GetValue()} via lightning" );
|
||||
|
||||
Logs.Tester.LogInformation($"Paying invoice {newInvoice.Id} remaining due amount {newInvoice.BtcDue.GetValue()} via lightning");
|
||||
var evt = await tester.WaitForEvent<InvoiceDataChangedEvent>(async () =>
|
||||
{
|
||||
await tester.SendLightningPaymentAsync(newInvoice);
|
||||
}, evt => evt.InvoiceId == invoice.Id);
|
||||
|
||||
var fetchedInvoice = await tester.PayTester.InvoiceRepository.GetInvoice(evt.InvoiceId);
|
||||
Assert.Contains(fetchedInvoice.Status, new []{InvoiceStatus.Complete, InvoiceStatus.Confirmed});
|
||||
Assert.Contains(fetchedInvoice.Status, new[] { InvoiceStatus.Complete, InvoiceStatus.Confirmed });
|
||||
Assert.Equal(InvoiceExceptionStatus.None, fetchedInvoice.ExceptionStatus);
|
||||
|
||||
Logs.Tester.LogInformation($"Paying invoice {invoice.Id} original full amount bolt11 invoice " );
|
||||
|
||||
Logs.Tester.LogInformation($"Paying invoice {invoice.Id} original full amount bolt11 invoice ");
|
||||
evt = await tester.WaitForEvent<InvoiceDataChangedEvent>(async () =>
|
||||
{
|
||||
await tester.SendLightningPaymentAsync(invoice);
|
||||
}, evt => evt.InvoiceId == invoice.Id);
|
||||
Assert.Equal(evt.InvoiceId, invoice.Id);
|
||||
fetchedInvoice = await tester.PayTester.InvoiceRepository.GetInvoice(evt.InvoiceId);
|
||||
Assert.Equal( 3,fetchedInvoice.Payments.Count);
|
||||
Assert.Equal(3, fetchedInvoice.Payments.Count);
|
||||
}
|
||||
|
||||
[Fact(Timeout = 60 * 2 * 1000)]
|
||||
@ -1508,7 +1509,7 @@ namespace BTCPayServer.Tests
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// [Fact(Timeout = TestTimeout)]
|
||||
[Fact()]
|
||||
[Trait("Integration", "Integration")]
|
||||
@ -1527,9 +1528,9 @@ namespace BTCPayServer.Tests
|
||||
BitcoinAddress.Create(invoice.BitcoinAddress, Network.RegTest),
|
||||
Money.Coins(0.01m));
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var payments = Assert.IsType<InvoiceDetailsModel>(
|
||||
Assert.IsType<ViewResult>(await user.GetController<InvoiceController>().Invoice(invoice.Id)).Model)
|
||||
.Payments;
|
||||
@ -1990,7 +1991,60 @@ namespace BTCPayServer.Tests
|
||||
};
|
||||
var criteriaCompat = store.GetPaymentMethodCriteria(tester.NetworkProvider, blob);
|
||||
Assert.Single(criteriaCompat);
|
||||
Assert.NotNull(criteriaCompat.FirstOrDefault(methodCriteria => methodCriteria.Value.ToString() == "2 USD" && methodCriteria.Above && methodCriteria.PaymentMethod == new PaymentMethodId("BTC", BitcoinPaymentType.Instance) ));
|
||||
Assert.NotNull(criteriaCompat.FirstOrDefault(methodCriteria => methodCriteria.Value.ToString() == "2 USD" && methodCriteria.Above && methodCriteria.PaymentMethod == new PaymentMethodId("BTC", BitcoinPaymentType.Instance)));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanSetUnifiedQrCode()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
tester.ActivateLightning();
|
||||
await tester.StartAsync();
|
||||
await tester.EnsureChannelsSetup();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("BTC", ScriptPubKeyType.Segwit);
|
||||
user.RegisterLightningNode("BTC", LightningConnectionType.CLightning);
|
||||
|
||||
var invoice = user.BitPay.CreateInvoice(
|
||||
new Invoice()
|
||||
{
|
||||
Price = 5.5m,
|
||||
Currency = "USD",
|
||||
PosData = "posData",
|
||||
OrderId = "orderId",
|
||||
ItemDesc = "Some description",
|
||||
FullNotifications = true
|
||||
}, Facade.Merchant);
|
||||
|
||||
// validate that invoice data model doesn't have lightning string initially
|
||||
var res = await user.GetController<InvoiceController>().Checkout(invoice.Id);
|
||||
var paymentMethodFirst = Assert.IsType<PaymentModel>(
|
||||
Assert.IsType<ViewResult>(res).Model
|
||||
);
|
||||
Assert.DoesNotContain("&lightning=", paymentMethodFirst.InvoiceBitcoinUrlQR);
|
||||
|
||||
// enable unified QR code in settings
|
||||
var vm = Assert.IsType<CheckoutExperienceViewModel>(Assert
|
||||
.IsType<ViewResult>(user.GetController<StoresController>().CheckoutExperience()).Model
|
||||
);
|
||||
vm.OnChainWithLnInvoiceFallback = true;
|
||||
Assert.IsType<RedirectToActionResult>(
|
||||
user.GetController<StoresController>().CheckoutExperience(vm).Result
|
||||
);
|
||||
|
||||
// validate that QR code now has both onchain and offchain payment urls
|
||||
res = await user.GetController<InvoiceController>().Checkout(invoice.Id);
|
||||
var paymentMethodSecond = Assert.IsType<PaymentModel>(
|
||||
Assert.IsType<ViewResult>(res).Model
|
||||
);
|
||||
Assert.Contains("&lightning=", paymentMethodSecond.InvoiceBitcoinUrlQR);
|
||||
Assert.StartsWith("BITCOIN:", paymentMethodSecond.InvoiceBitcoinUrlQR);
|
||||
var split = paymentMethodSecond.InvoiceBitcoinUrlQR.Split('?')[0];
|
||||
Assert.True($"BITCOIN:{paymentMethodSecond.BtcAddress.ToUpperInvariant()}" == split);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2030,7 +2084,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
Assert.Single(invoice.CryptoInfo);
|
||||
Assert.Equal(PaymentTypes.LightningLike.ToString(), invoice.CryptoInfo[0].PaymentType);
|
||||
|
||||
|
||||
//test backward compat
|
||||
var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
var blob = store.GetStoreBlob();
|
||||
@ -2044,8 +2098,8 @@ namespace BTCPayServer.Tests
|
||||
};
|
||||
var criteriaCompat = store.GetPaymentMethodCriteria(tester.NetworkProvider, blob);
|
||||
Assert.Single(criteriaCompat);
|
||||
Assert.NotNull(criteriaCompat.FirstOrDefault(methodCriteria => methodCriteria.Value.ToString() == "2 USD" && !methodCriteria.Above && methodCriteria.PaymentMethod == new PaymentMethodId("BTC", LightningPaymentType.Instance) ));
|
||||
|
||||
Assert.NotNull(criteriaCompat.FirstOrDefault(methodCriteria => methodCriteria.Value.ToString() == "2 USD" && !methodCriteria.Above && methodCriteria.PaymentMethod == new PaymentMethodId("BTC", LightningPaymentType.Instance)));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,21 +120,22 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
//we do not include OnChainMinValue and LightningMaxValue because moving the CurrencyValueJsonConverter to the Client csproj is hard and requires a refactor (#1571 & #1572)
|
||||
NetworkFeeMode = storeBlob.NetworkFeeMode,
|
||||
RequiresRefundEmail = storeBlob.RequiresRefundEmail,
|
||||
LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi,
|
||||
LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints,
|
||||
OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback,
|
||||
RedirectAutomatically = storeBlob.RedirectAutomatically,
|
||||
ShowRecommendedFee = storeBlob.ShowRecommendedFee,
|
||||
RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget,
|
||||
DefaultLang = storeBlob.DefaultLang,
|
||||
MonitoringExpiration = storeBlob.MonitoringExpiration,
|
||||
InvoiceExpiration = storeBlob.InvoiceExpiration,
|
||||
LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi,
|
||||
CustomLogo = storeBlob.CustomLogo,
|
||||
CustomCSS = storeBlob.CustomCSS,
|
||||
HtmlTitle = storeBlob.HtmlTitle,
|
||||
AnyoneCanCreateInvoice = storeBlob.AnyoneCanInvoice,
|
||||
LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate,
|
||||
PaymentTolerance = storeBlob.PaymentTolerance,
|
||||
RedirectAutomatically = storeBlob.RedirectAutomatically,
|
||||
PayJoinEnabled = storeBlob.PayJoinEnabled,
|
||||
LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints
|
||||
PayJoinEnabled = storeBlob.PayJoinEnabled
|
||||
};
|
||||
}
|
||||
|
||||
@ -155,21 +156,22 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
//we do not include OnChainMinValue and LightningMaxValue because moving the CurrencyValueJsonConverter to the Client csproj is hard and requires a refactor (#1571 & #1572)
|
||||
blob.NetworkFeeMode = restModel.NetworkFeeMode;
|
||||
blob.RequiresRefundEmail = restModel.RequiresRefundEmail;
|
||||
blob.LightningAmountInSatoshi = restModel.LightningAmountInSatoshi;
|
||||
blob.LightningPrivateRouteHints = restModel.LightningPrivateRouteHints;
|
||||
blob.OnChainWithLnInvoiceFallback = restModel.OnChainWithLnInvoiceFallback;
|
||||
blob.RedirectAutomatically = restModel.RedirectAutomatically;
|
||||
blob.ShowRecommendedFee = restModel.ShowRecommendedFee;
|
||||
blob.RecommendedFeeBlockTarget = restModel.RecommendedFeeBlockTarget;
|
||||
blob.DefaultLang = restModel.DefaultLang;
|
||||
blob.MonitoringExpiration = restModel.MonitoringExpiration;
|
||||
blob.InvoiceExpiration = restModel.InvoiceExpiration;
|
||||
blob.LightningAmountInSatoshi = restModel.LightningAmountInSatoshi;
|
||||
blob.CustomLogo = restModel.CustomLogo;
|
||||
blob.CustomCSS = restModel.CustomCSS;
|
||||
blob.HtmlTitle = restModel.HtmlTitle;
|
||||
blob.AnyoneCanInvoice = restModel.AnyoneCanCreateInvoice;
|
||||
blob.LightningDescriptionTemplate = restModel.LightningDescriptionTemplate;
|
||||
blob.PaymentTolerance = restModel.PaymentTolerance;
|
||||
blob.RedirectAutomatically = restModel.RedirectAutomatically;
|
||||
blob.PayJoinEnabled = restModel.PayJoinEnabled;
|
||||
blob.LightningPrivateRouteHints = restModel.LightningPrivateRouteHints;
|
||||
model.SetStoreBlob(blob);
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ namespace BTCPayServer.Controllers
|
||||
if (paymentMethodCriteria.Value != null)
|
||||
{
|
||||
currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, paymentMethodCriteria.Value.Currency));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,7 +305,9 @@ namespace BTCPayServer.Controllers
|
||||
}).ToArray());
|
||||
}
|
||||
|
||||
private async Task<PaymentMethod> CreatePaymentMethodAsync(Dictionary<CurrencyPair, Task<RateResult>> fetchingByCurrencyPair, IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetworkBase network, InvoiceEntity entity, StoreData store, InvoiceLogs logs)
|
||||
private async Task<PaymentMethod> CreatePaymentMethodAsync(Dictionary<CurrencyPair, Task<RateResult>> fetchingByCurrencyPair,
|
||||
IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetworkBase network, InvoiceEntity entity,
|
||||
StoreData store, InvoiceLogs logs)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -317,12 +319,14 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
return null;
|
||||
}
|
||||
PaymentMethod paymentMethod = new PaymentMethod();
|
||||
paymentMethod.ParentEntity = entity;
|
||||
paymentMethod.Network = network;
|
||||
var paymentMethod = new PaymentMethod
|
||||
{
|
||||
ParentEntity = entity,
|
||||
Network = network,
|
||||
Rate = rate.BidAsk.Bid,
|
||||
PreferOnion = Uri.TryCreate(entity.ServerUrl, UriKind.Absolute, out var u) && u.DnsSafeHost.EndsWith(".onion", StringComparison.OrdinalIgnoreCase)
|
||||
};
|
||||
paymentMethod.SetId(supportedPaymentMethod.PaymentId);
|
||||
paymentMethod.Rate = rate.BidAsk.Bid;
|
||||
paymentMethod.PreferOnion = Uri.TryCreate(entity.ServerUrl, UriKind.Absolute, out var u) && u.DnsSafeHost.EndsWith(".onion", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
using (logs.Measure($"{logPrefix} Payment method details creation"))
|
||||
{
|
||||
@ -339,7 +343,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var amount = paymentMethod.Calculate().Due.GetValue(network as BTCPayNetwork);
|
||||
var limitValueCrypto = criteria.Value.Value / currentRateToCrypto.BidAsk.Bid;
|
||||
|
||||
|
||||
if (amount < limitValueCrypto && criteria.Above)
|
||||
{
|
||||
logs.Write($"{logPrefix} invoice amount below accepted value for payment method", InvoiceEventData.EventSeverity.Error);
|
||||
@ -369,7 +373,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Unexpected exception ({ex.ToString()})", InvoiceEventData.EventSeverity.Error);
|
||||
logs.Write($"{supportedPaymentMethod.PaymentId.CryptoCode}: Unexpected exception ({ex})", InvoiceEventData.EventSeverity.Error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -375,16 +375,19 @@ namespace BTCPayServer.Controllers
|
||||
Type = criteria.Above ? PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan : PaymentMethodCriteriaViewModel.CriteriaType.LessThan,
|
||||
Value = criteria.Value?.ToString() ?? ""
|
||||
}).ToList();
|
||||
|
||||
vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail;
|
||||
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
||||
vm.LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints;
|
||||
vm.OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback;
|
||||
vm.RedirectAutomatically = storeBlob.RedirectAutomatically;
|
||||
vm.ShowRecommendedFee = storeBlob.ShowRecommendedFee;
|
||||
vm.RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget;
|
||||
|
||||
vm.CustomCSS = storeBlob.CustomCSS;
|
||||
vm.CustomLogo = storeBlob.CustomLogo;
|
||||
vm.HtmlTitle = storeBlob.HtmlTitle;
|
||||
vm.SetLanguages(_LangService, storeBlob.DefaultLang);
|
||||
vm.RequiresRefundEmail = storeBlob.RequiresRefundEmail;
|
||||
vm.ShowRecommendedFee = storeBlob.ShowRecommendedFee;
|
||||
vm.RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget;
|
||||
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
||||
vm.LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints;
|
||||
vm.RedirectAutomatically = storeBlob.RedirectAutomatically;
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
@ -450,16 +453,20 @@ namespace BTCPayServer.Controllers
|
||||
blob.LightningMaxValue = null;
|
||||
blob.OnChainMinValue = null;
|
||||
#pragma warning restore 612
|
||||
|
||||
blob.RequiresRefundEmail = model.RequiresRefundEmail;
|
||||
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
|
||||
blob.LightningPrivateRouteHints = model.LightningPrivateRouteHints;
|
||||
blob.OnChainWithLnInvoiceFallback = model.OnChainWithLnInvoiceFallback;
|
||||
blob.RedirectAutomatically = model.RedirectAutomatically;
|
||||
blob.ShowRecommendedFee = model.ShowRecommendedFee;
|
||||
blob.RecommendedFeeBlockTarget = model.RecommendedFeeBlockTarget;
|
||||
|
||||
blob.CustomLogo = model.CustomLogo;
|
||||
blob.CustomCSS = model.CustomCSS;
|
||||
blob.HtmlTitle = string.IsNullOrWhiteSpace(model.HtmlTitle) ? null : model.HtmlTitle;
|
||||
blob.DefaultLang = model.DefaultLang;
|
||||
blob.RequiresRefundEmail = model.RequiresRefundEmail;
|
||||
blob.ShowRecommendedFee = model.ShowRecommendedFee;
|
||||
blob.RecommendedFeeBlockTarget = model.RecommendedFeeBlockTarget;
|
||||
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
|
||||
blob.LightningPrivateRouteHints = model.LightningPrivateRouteHints;
|
||||
blob.RedirectAutomatically = model.RedirectAutomatically;
|
||||
|
||||
if (CurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
needUpdate = true;
|
||||
|
@ -31,22 +31,17 @@ namespace BTCPayServer.Data
|
||||
|
||||
[Obsolete("Use NetworkFeeMode instead")]
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public bool? NetworkFeeDisabled
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public bool? NetworkFeeDisabled { get; set; }
|
||||
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public NetworkFeeMode NetworkFeeMode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public NetworkFeeMode NetworkFeeMode { get; set; }
|
||||
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
public bool ShowRecommendedFee { get; set; }
|
||||
|
||||
public int RecommendedFeeBlockTarget { get; set; }
|
||||
|
||||
CurrencyPair[] _DefaultCurrencyPairs;
|
||||
@ -81,11 +76,7 @@ namespace BTCPayServer.Data
|
||||
[DefaultValue(typeof(TimeSpan), "00:15:00")]
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
[JsonConverter(typeof(TimeSpanJsonConverter.Minutes))]
|
||||
public TimeSpan InvoiceExpiration
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public TimeSpan InvoiceExpiration { get; set; }
|
||||
|
||||
public decimal Spread { get; set; } = 0.0m;
|
||||
|
||||
@ -106,8 +97,6 @@ namespace BTCPayServer.Data
|
||||
[Obsolete]
|
||||
[JsonConverter(typeof(CurrencyValueJsonConverter))]
|
||||
public CurrencyValue LightningMaxValue { get; set; }
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
|
||||
public string CustomCSS { get; set; }
|
||||
public string CustomLogo { get; set; }
|
||||
@ -185,10 +174,10 @@ namespace BTCPayServer.Data
|
||||
public Dictionary<string, string> WalletKeyPathRoots { get; set; }
|
||||
|
||||
public EmailSettings EmailSettings { get; set; }
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
public bool PayJoinEnabled { get; set; }
|
||||
|
||||
public StoreHints Hints { get; set; }
|
||||
|
||||
public class StoreHints
|
||||
{
|
||||
public bool Wallet { get; set; }
|
||||
@ -229,7 +218,7 @@ namespace BTCPayServer.Data
|
||||
public CurrencyValue Value { get; set; }
|
||||
public bool Above { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class RateRule_Obsolete
|
||||
{
|
||||
public RateRule_Obsolete()
|
||||
|
@ -31,6 +31,31 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
|
||||
[Display(Name = "Default payment method on checkout")]
|
||||
public string DefaultPaymentMethod { get; set; }
|
||||
|
||||
|
||||
[Display(Name = "Requires a refund email")]
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
|
||||
[Display(Name = "Display lightning payment amounts in Satoshis")]
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
|
||||
[Display(Name = "Add hop hints for private channels to the lightning invoice")]
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
|
||||
[Display(Name = "Include lightning invoice fallback to on-chain BIP21 payment url")]
|
||||
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||
|
||||
[Display(Name = "Redirect invoice to redirect url automatically after paid")]
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
|
||||
[Display(Name = "Show recommended fee")]
|
||||
public bool ShowRecommendedFee { get; set; }
|
||||
|
||||
[Display(Name = "Recommended fee confirmation target blocks")]
|
||||
[Range(1, double.PositiveInfinity)]
|
||||
public int RecommendedFeeBlockTarget { get; set; }
|
||||
|
||||
|
||||
[Display(Name = "Default language on checkout")]
|
||||
public string DefaultLang { get; set; }
|
||||
|
||||
@ -42,25 +67,6 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
[Display(Name = "Custom HTML title to display on Checkout page")]
|
||||
public string HtmlTitle { get; set; }
|
||||
|
||||
[Display(Name = "Requires a refund email")]
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
|
||||
[Display(Name = "Show recommended fee")]
|
||||
public bool ShowRecommendedFee { get; set; }
|
||||
|
||||
[Display(Name = "Recommended fee confirmation target blocks")]
|
||||
[Range(1, double.PositiveInfinity)]
|
||||
public int RecommendedFeeBlockTarget { get; set; }
|
||||
|
||||
[Display(Name = "Display lightning payment amounts in Satoshis")]
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
|
||||
[Display(Name = "Add hop hints for private channels to the lightning invoice")]
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
|
||||
[Display(Name = "Redirect invoice to redirect url automatically after paid")]
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
|
||||
public List<PaymentMethodCriteriaViewModel> PaymentMethodCriteria { get; set; }
|
||||
}
|
||||
|
||||
|
@ -46,8 +46,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
public Task<KeyPathInformation> ReserveAddress;
|
||||
}
|
||||
|
||||
public override void PreparePaymentModel(PaymentModel model, InvoiceResponse invoiceResponse,
|
||||
StoreBlob storeBlob)
|
||||
public override void PreparePaymentModel(PaymentModel model, InvoiceResponse invoiceResponse, StoreBlob storeBlob)
|
||||
{
|
||||
var paymentMethodId = new PaymentMethodId(model.CryptoCode, PaymentTypes.BTCLike);
|
||||
|
||||
@ -55,9 +54,33 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode);
|
||||
model.IsLightning = false;
|
||||
model.PaymentMethodName = GetPaymentMethodName(network);
|
||||
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls.BIP21;
|
||||
model.InvoiceBitcoinUrlQR = cryptoInfo.PaymentUrls.BIP21;
|
||||
|
||||
|
||||
var lightningFallback = "";
|
||||
if (storeBlob.OnChainWithLnInvoiceFallback)
|
||||
{
|
||||
var lightningInfo = invoiceResponse.CryptoInfo.FirstOrDefault(a =>
|
||||
a.GetpaymentMethodId() == new PaymentMethodId(model.CryptoCode, PaymentTypes.LightningLike));
|
||||
if (!String.IsNullOrEmpty(lightningInfo?.PaymentUrls?.BOLT11))
|
||||
lightningFallback = "&" + lightningInfo.PaymentUrls.BOLT11.Replace("lightning:", "lightning=", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls.BIP21 + lightningFallback;
|
||||
// We're trying to make as many characters uppercase to make QR smaller
|
||||
// Ref: https://github.com/btcpayserver/btcpayserver/pull/2060#issuecomment-723828348
|
||||
model.InvoiceBitcoinUrlQR = cryptoInfo.PaymentUrls.BIP21
|
||||
.Replace("bitcoin:", "BITCOIN:", StringComparison.OrdinalIgnoreCase)
|
||||
+ lightningFallback.ToUpperInvariant().Replace("LIGHTNING=", "lightning=", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (bech32Prefixes.Any(a => model.BtcAddress.StartsWith(a, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
model.InvoiceBitcoinUrlQR = model.InvoiceBitcoinUrlQR.Replace(
|
||||
$"BITCOIN:{model.BtcAddress}", $"BITCOIN:{model.BtcAddress.ToUpperInvariant()}",
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
);
|
||||
}
|
||||
}
|
||||
private static string[] bech32Prefixes = new[] { "bc1", "tb1", "bcrt1" };
|
||||
|
||||
public override string GetCryptoImage(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
|
@ -12,15 +12,13 @@ namespace BTCPayServer.Payments
|
||||
public class BitcoinPaymentType : PaymentType
|
||||
{
|
||||
public static BitcoinPaymentType Instance { get; } = new BitcoinPaymentType();
|
||||
private BitcoinPaymentType()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private BitcoinPaymentType() { }
|
||||
|
||||
public override string ToPrettyString() => "On-Chain";
|
||||
public override string GetId() => "BTCLike";
|
||||
public override string GetBadge() => "🔗";
|
||||
public override string ToStringNormalized() => "OnChain";
|
||||
public override string GetBadge() => "🔗";
|
||||
|
||||
public override CryptoPaymentData DeserializePaymentData(BTCPayNetworkBase network, string str)
|
||||
{
|
||||
@ -72,8 +70,8 @@ namespace BTCPayServer.Payments
|
||||
public override string GetPaymentLink(BTCPayNetworkBase network, IPaymentMethodDetails paymentMethodDetails,
|
||||
Money cryptoInfoDue, string serverUri)
|
||||
{
|
||||
var bip21 = ((BTCPayNetwork)network).GenerateBIP21(paymentMethodDetails.GetPaymentDestination(), cryptoInfoDue);
|
||||
|
||||
var bip21 = ((BTCPayNetwork)network).GenerateBIP21(paymentMethodDetails.GetPaymentDestination(), cryptoInfoDue);
|
||||
|
||||
if ((paymentMethodDetails as BitcoinLikeOnChainPaymentMethod)?.PayjoinEnabled is true)
|
||||
{
|
||||
bip21 += $"&{PayjoinClient.BIP21EndpointKey}={serverUri.WithTrailingSlash()}{network.CryptoCode}/{PayjoinClient.BIP21EndpointKey}";
|
||||
|
@ -11,18 +11,13 @@ namespace BTCPayServer.Payments
|
||||
{
|
||||
public static LightningPaymentType Instance { get; } = new LightningPaymentType();
|
||||
|
||||
private LightningPaymentType()
|
||||
{
|
||||
}
|
||||
private LightningPaymentType() { }
|
||||
|
||||
public override string ToPrettyString() => "Off-Chain";
|
||||
public override string GetId() => "LightningLike";
|
||||
public override string GetBadge() => "⚡";
|
||||
public override string GetBadge() => "⚡";
|
||||
public override string ToStringNormalized() => "LightningNetwork";
|
||||
|
||||
public override string ToStringNormalized()
|
||||
{
|
||||
return "LightningNetwork";
|
||||
}
|
||||
public override CryptoPaymentData DeserializePaymentData(BTCPayNetworkBase network, string str)
|
||||
{
|
||||
return ((BTCPayNetwork)network)?.ToObject<LightningLikePaymentData>(str);
|
||||
@ -35,7 +30,7 @@ namespace BTCPayServer.Payments
|
||||
|
||||
public override IPaymentMethodDetails DeserializePaymentMethodDetails(BTCPayNetworkBase network, string str)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<Payments.Lightning.LightningLikePaymentMethodDetails>(str);
|
||||
return JsonConvert.DeserializeObject<LightningLikePaymentMethodDetails>(str);
|
||||
}
|
||||
|
||||
public override string SerializePaymentMethodDetails(BTCPayNetworkBase network, IPaymentMethodDetails details)
|
||||
@ -57,8 +52,10 @@ namespace BTCPayServer.Payments
|
||||
public override string GetPaymentLink(BTCPayNetworkBase network, IPaymentMethodDetails paymentMethodDetails,
|
||||
Money cryptoInfoDue, string serverUri)
|
||||
{
|
||||
return
|
||||
$"lightning:{paymentMethodDetails.GetPaymentDestination().ToUpperInvariant().Replace("LIGHTNING:", "", StringComparison.InvariantCultureIgnoreCase)}";
|
||||
var lnInvoiceTrimmedOfScheme = paymentMethodDetails.GetPaymentDestination().ToLowerInvariant()
|
||||
.Replace("lightning:", "", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
return $"lightning:{lnInvoiceTrimmedOfScheme}";
|
||||
}
|
||||
|
||||
public override string InvoiceViewPaymentPartialName { get; } = "ViewLightningLikePaymentData";
|
||||
|
@ -36,73 +36,34 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
public string OrderId { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerName")]
|
||||
public string BuyerName
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerName { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerEmail")]
|
||||
public string BuyerEmail
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerEmail { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerCountry")]
|
||||
public string BuyerCountry
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerCountry { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerZip")]
|
||||
public string BuyerZip
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerZip { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerState")]
|
||||
public string BuyerState
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerState { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerCity")]
|
||||
public string BuyerCity
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerCity { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerAddress2")]
|
||||
public string BuyerAddress2
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerAddress2 { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerAddress1")]
|
||||
public string BuyerAddress1
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerAddress1 { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "buyerPhone")]
|
||||
public string BuyerPhone
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerPhone { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "itemDesc")]
|
||||
public string ItemDesc
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string ItemDesc { get; set; }
|
||||
[JsonProperty(PropertyName = "itemCode")]
|
||||
public string ItemCode
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string ItemCode { get; set; }
|
||||
[JsonProperty(PropertyName = "physical")]
|
||||
public bool? Physical
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public bool? Physical { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "taxIncluded", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public decimal? TaxIncluded
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public decimal? TaxIncluded { get; set; }
|
||||
public string PosData { get; set; }
|
||||
[JsonExtensionData]
|
||||
public IDictionary<string, JToken> AdditionalData { get; set; }
|
||||
@ -122,133 +83,64 @@ namespace BTCPayServer.Services.Invoices
|
||||
class BuyerInformation
|
||||
{
|
||||
[JsonProperty(PropertyName = "buyerName")]
|
||||
public string BuyerName
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerName { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerEmail")]
|
||||
public string BuyerEmail
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerEmail { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerCountry")]
|
||||
public string BuyerCountry
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerCountry { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerZip")]
|
||||
public string BuyerZip
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerZip { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerState")]
|
||||
public string BuyerState
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerState { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerCity")]
|
||||
public string BuyerCity
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerCity { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerAddress2")]
|
||||
public string BuyerAddress2
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerAddress2 { get; set; }
|
||||
[JsonProperty(PropertyName = "buyerAddress1")]
|
||||
public string BuyerAddress1
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerAddress1 { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "buyerPhone")]
|
||||
public string BuyerPhone
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string BuyerPhone { get; set; }
|
||||
}
|
||||
|
||||
class ProductInformation
|
||||
{
|
||||
[JsonProperty(PropertyName = "itemDesc")]
|
||||
public string ItemDesc
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string ItemDesc { get; set; }
|
||||
[JsonProperty(PropertyName = "itemCode")]
|
||||
public string ItemCode
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string ItemCode { get; set; }
|
||||
[JsonProperty(PropertyName = "physical")]
|
||||
public bool Physical
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public bool Physical { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "price")]
|
||||
public decimal Price
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public decimal Price { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "taxIncluded", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public decimal TaxIncluded
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public decimal TaxIncluded { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "currency")]
|
||||
public string Currency
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string Currency { get; set; }
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public BTCPayNetworkProvider Networks { get; set; }
|
||||
public const int InternalTagSupport_Version = 1;
|
||||
public const int GreenfieldInvoices_Version = 2;
|
||||
public const int Lastest_Version = 2;
|
||||
public int Version { get; set; }
|
||||
public string Id
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string StoreId
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public string Id { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
|
||||
public SpeedPolicy SpeedPolicy
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public SpeedPolicy SpeedPolicy { get; set; }
|
||||
[Obsolete("Use GetPaymentMethod(network) instead")]
|
||||
public decimal Rate
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public DateTimeOffset InvoiceTime
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public DateTimeOffset ExpirationTime
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public decimal Rate { get; set; }
|
||||
public DateTimeOffset InvoiceTime { get; set; }
|
||||
public DateTimeOffset ExpirationTime { get; set; }
|
||||
|
||||
[Obsolete("Use GetPaymentMethod(network).GetPaymentMethodDetails().GetDestinationAddress() instead")]
|
||||
public string DepositAddress
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public InvoiceMetadata Metadata
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string DepositAddress { get; set; }
|
||||
|
||||
public InvoiceMetadata Metadata { get; set; }
|
||||
|
||||
public decimal Price { get; set; }
|
||||
public string Currency { get; set; }
|
||||
@ -267,18 +159,10 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
|
||||
[Obsolete("Use GetDerivationStrategies instead")]
|
||||
public string DerivationStrategy
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string DerivationStrategy { get; set; }
|
||||
|
||||
[Obsolete("Use GetPaymentMethodFactories() instead")]
|
||||
public string DerivationStrategies
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string DerivationStrategies { get; set; }
|
||||
public IEnumerable<T> GetSupportedPaymentMethod<T>(PaymentMethodId paymentMethodId) where T : ISupportedPaymentMethod
|
||||
{
|
||||
return
|
||||
@ -335,28 +219,18 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public InvoiceStatus Status
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public InvoiceStatus Status { get; set; }
|
||||
[JsonProperty(PropertyName = "status")]
|
||||
[Obsolete("Use Status instead")]
|
||||
public string StatusString => InvoiceState.ToString(Status);
|
||||
[JsonIgnore]
|
||||
public InvoiceExceptionStatus ExceptionStatus
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public InvoiceExceptionStatus ExceptionStatus { get; set; }
|
||||
[JsonProperty(PropertyName = "exceptionStatus")]
|
||||
[Obsolete("Use ExceptionStatus instead")]
|
||||
public string ExceptionStatusString => InvoiceState.ToString(ExceptionStatus);
|
||||
|
||||
[Obsolete("Use GetPayments instead")]
|
||||
public List<PaymentEntity> Payments
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public List<PaymentEntity> Payments { get; set; }
|
||||
|
||||
#pragma warning disable CS0618
|
||||
public List<PaymentEntity> GetPayments()
|
||||
@ -372,22 +246,10 @@ namespace BTCPayServer.Services.Invoices
|
||||
return GetPayments(network.CryptoCode);
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
public bool Refundable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string RefundMail
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public bool Refundable { get; set; }
|
||||
public string RefundMail { get; set; }
|
||||
[JsonProperty("redirectURL")]
|
||||
public string RedirectURLTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string RedirectURLTemplate { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Uri RedirectURL => FillPlaceholdersUri(RedirectURLTemplate);
|
||||
@ -401,65 +263,29 @@ namespace BTCPayServer.Services.Invoices
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool RedirectAutomatically
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
|
||||
[Obsolete("Use GetPaymentMethod(network).GetTxFee() instead")]
|
||||
public Money TxFee
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public bool FullNotifications
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string NotificationEmail
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public Money TxFee { get; set; }
|
||||
public bool FullNotifications { get; set; }
|
||||
public string NotificationEmail { get; set; }
|
||||
|
||||
[JsonProperty("notificationURL")]
|
||||
public string NotificationURLTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string NotificationURLTemplate { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Uri NotificationURL => FillPlaceholdersUri(NotificationURLTemplate);
|
||||
public string ServerUrl
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string ServerUrl { get; set; }
|
||||
|
||||
[Obsolete("Use Set/GetPaymentMethod() instead")]
|
||||
[JsonProperty(PropertyName = "cryptoData")]
|
||||
public JObject PaymentMethod { get; set; }
|
||||
|
||||
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
|
||||
public DateTimeOffset MonitoringExpiration
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public HistoricalAddressInvoiceData[] HistoricalAddresses
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public DateTimeOffset MonitoringExpiration { get; set; }
|
||||
public HistoricalAddressInvoiceData[] HistoricalAddresses { get; set; }
|
||||
|
||||
public HashSet<string> AvailableAddressHashes
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public HashSet<string> AvailableAddressHashes { get; set; }
|
||||
public bool ExtendedNotifications { get; set; }
|
||||
public List<InvoiceEventData> Events { get; internal set; }
|
||||
public double PaymentTolerance { get; set; }
|
||||
@ -559,7 +385,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
var minerInfo = new MinerFeeInfo();
|
||||
minerInfo.TotalFee = accounting.NetworkFee.Satoshi;
|
||||
minerInfo.SatoshiPerBytes = ((BitcoinLikeOnChainPaymentMethod) details).FeeRate
|
||||
minerInfo.SatoshiPerBytes = ((BitcoinLikeOnChainPaymentMethod)details).FeeRate
|
||||
.GetFee(1).Satoshi;
|
||||
dto.MinerFees.TryAdd(cryptoInfo.CryptoCode, minerInfo);
|
||||
cryptoInfo.PaymentUrls = new InvoicePaymentUrls()
|
||||
@ -1181,7 +1007,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
paymentData.Network = Network;
|
||||
if (paymentData is BitcoinLikePaymentData bitcoin)
|
||||
{
|
||||
@ -1229,9 +1055,10 @@ namespace BTCPayServer.Services.Invoices
|
||||
PaymentType paymentType;
|
||||
if (string.IsNullOrEmpty(CryptoPaymentDataType))
|
||||
{
|
||||
paymentType = BitcoinPaymentType.Instance;;
|
||||
paymentType = BitcoinPaymentType.Instance;
|
||||
;
|
||||
}
|
||||
else if(!PaymentTypes.TryParse(CryptoPaymentDataType, out paymentType))
|
||||
else if (!PaymentTypes.TryParse(CryptoPaymentDataType, out paymentType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
@using BTCPayServer.Payments
|
||||
@using BTCPayServer.Payments
|
||||
@model CheckoutExperienceViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
@ -36,9 +36,9 @@
|
||||
@for (var index = 0; index < Model.PaymentMethodCriteria.Count; index++)
|
||||
{
|
||||
var criteria = Model.PaymentMethodCriteria[index];
|
||||
<tr >
|
||||
<tr>
|
||||
<td class="border-right-0 border-left-0 pt-3">
|
||||
<input type="hidden" asp-for="PaymentMethodCriteria[index].PaymentMethod"/>
|
||||
<input type="hidden" asp-for="PaymentMethodCriteria[index].PaymentMethod" />
|
||||
@PaymentMethodId.Parse(criteria.PaymentMethod).ToPrettyString()
|
||||
</td>
|
||||
<td class="border-right-0 border-left-0 ">
|
||||
@ -49,43 +49,46 @@
|
||||
|
||||
</td>
|
||||
<td class="border-right-0 border-left-0">
|
||||
<input placeholder="6.15 USD" asp-for="PaymentMethodCriteria[index].Value" class="form-control my-0" style="max-width: 20ch;"/>
|
||||
<input placeholder="6.15 USD" asp-for="PaymentMethodCriteria[index].Value" class="form-control my-0" style="max-width: 20ch;" />
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
|
||||
<div class="form-group mb-4">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="RequiresRefundEmail" type="checkbox" class="form-check-input"/>
|
||||
<input asp-for="RequiresRefundEmail" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="RequiresRefundEmail" class="form-check-label"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="LightningAmountInSatoshi" type="checkbox" class="form-check-input"/>
|
||||
<input asp-for="LightningAmountInSatoshi" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="LightningAmountInSatoshi" class="form-check-label"></label>
|
||||
<span asp-validation-for="LightningAmountInSatoshi" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="LightningPrivateRouteHints" type="checkbox" class="form-check-input"/>
|
||||
<input asp-for="LightningPrivateRouteHints" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="LightningPrivateRouteHints" class="form-check-label"></label>
|
||||
<span asp-validation-for="LightningPrivateRouteHints" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="RedirectAutomatically" type="checkbox" class="form-check-input"/>
|
||||
<input asp-for="OnChainWithLnInvoiceFallback" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="OnChainWithLnInvoiceFallback" class="form-check-label"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="RedirectAutomatically" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="RedirectAutomatically" class="form-check-label"></label>
|
||||
<span asp-validation-for="RedirectAutomatically" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="ShowRecommendedFee" type="checkbox" class="form-check-input"/>
|
||||
<input asp-for="ShowRecommendedFee" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="ShowRecommendedFee" class="form-check-label"></label>
|
||||
<p class="form-text text-muted">Fee will be shown for BTC and LTC onchain payments only.</p>
|
||||
</div>
|
||||
@ -103,19 +106,19 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="HtmlTitle"></label>
|
||||
<input asp-for="HtmlTitle" class="form-control"/>
|
||||
<input asp-for="HtmlTitle" class="form-control" />
|
||||
<span asp-validation-for="HtmlTitle" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomLogo"></label>
|
||||
<a href="https://docs.btcpayserver.org/Theme/#checkout-page-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
|
||||
<input asp-for="CustomLogo" class="form-control"/>
|
||||
<input asp-for="CustomLogo" class="form-control" />
|
||||
<span asp-validation-for="CustomLogo" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSS"></label>
|
||||
<a href="https://docs.btcpayserver.org/Theme/#checkout-page-themes" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
|
||||
<input asp-for="CustomCSS" class="form-control"/>
|
||||
<input asp-for="CustomCSS" class="form-control" />
|
||||
<span asp-validation-for="CustomCSS" class="text-danger"></span>
|
||||
<p class="form-text text-muted">
|
||||
Bundled Themes:
|
||||
|
@ -320,6 +320,31 @@
|
||||
"default": false,
|
||||
"description": "If true, then no authentication is needed to create invoices on this store."
|
||||
},
|
||||
"requiresRefundEmail": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If true, the checkout page will ask to enter an email address before accessing payment information."
|
||||
},
|
||||
"lightningAmountInSatoshi": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If true, lightning payment methods show amount in satoshi in the checkout page."
|
||||
},
|
||||
"lightningPrivateRouteHints": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Should private route hints be included in the lightning payment of the checkout page."
|
||||
},
|
||||
"onChainWithLnInvoiceFallback": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Include lightning invoice fallback to on-chain BIP21 payment url."
|
||||
},
|
||||
"redirectAutomatically": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "After successfull payment, should the checkout page redirect the user automatically to the redirect URL of the invoice?"
|
||||
},
|
||||
"showRecommendedFee": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
@ -335,11 +360,6 @@
|
||||
"default": "en",
|
||||
"description": "The default language to use in the checkout page. (The different translations available are listed [here](https://github.com/btcpayserver/btcpayserver/tree/master/BTCPayServer/wwwroot/locales)"
|
||||
},
|
||||
"lightningAmountInSatoshi": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If true, lightning payment methods show amount in satoshi in the checkout page."
|
||||
},
|
||||
"customLogo": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
@ -355,16 +375,6 @@
|
||||
"nullable": true,
|
||||
"description": "The HTML title of the checkout page (when you over the tab in your browser)"
|
||||
},
|
||||
"redirectAutomatically": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "After successfull payment, should the checkout page redirect the user automatically to the redirect URL of the invoice?"
|
||||
},
|
||||
"requiresRefundEmail": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If true, the checkout page will ask to enter an email address before accessing payment information."
|
||||
},
|
||||
"networkFeeMode": {
|
||||
"$ref": "#/components/schemas/NetworkFeeMode"
|
||||
},
|
||||
@ -372,11 +382,6 @@
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If true, payjoin will be proposed in the checkout page if possible. ([More information](https://docs.btcpayserver.org/Payjoin/))"
|
||||
},
|
||||
"lightningPrivateRouteHints": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Should private route hints be included in the lightning payment of the checkout page."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user