mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Make Invoice Create Faster And Fix Gap Limit Issue (#1843)
* Make Invoice Create Faster And Fix Gap Limit Issue This make address reserve only when user "activate" paymet method in ui. optional setting in store checkout ui. * Fix swagger documentation around Lazy payment methods * fix changed code signature * Add missing GreenField API for activate feature * Fix checkout experience styling for activate feature * Fix issue with Checkout activate button * Make lightning also work with activation * Make sure PreparePaymentModel is still called on payment handlers even when unactivated * Make payment link return empty if not activated * Add activate payment method method to client and add test * remove debugger * add e2e test * Rearranging lazy payments position in UI to be near dependent Unified QR code * fix rebase conflicts * Make lazy payment method mode activate on UI load. Co-authored-by: Kukks <evilkukka@gmail.com> Co-authored-by: rockstardev <rockstardev@users.noreply.github.com> Co-authored-by: Andrew Camilleri <kukks@btcpayserver.org>
This commit is contained in:
@@ -85,5 +85,13 @@ namespace BTCPayServer.Client
|
||||
method: HttpMethod.Post), token);
|
||||
return await HandleResponse<InvoiceData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task ActivateInvoicePaymentMethod(string storeId, string invoiceId, string paymentMethod, CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods/{paymentMethod}/activate",
|
||||
method: HttpMethod.Post), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class InvoicePaymentMethodDataModel
|
||||
{
|
||||
public bool Activated { get; set; }
|
||||
public string Destination { get; set; }
|
||||
public string PaymentLink { get; set; }
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace BTCPayServer.Client.Models
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||
public bool LazyPaymentMethods { get; set; }
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
@@ -53,8 +54,6 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public string HtmlTitle { get; set; }
|
||||
|
||||
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public NetworkFeeMode NetworkFeeMode { get; set; } = NetworkFeeMode.Never;
|
||||
|
||||
@@ -1093,6 +1093,24 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
Assert.Equal("pt-PT", langs.FindBestMatch(match).Code);
|
||||
}
|
||||
|
||||
//payment method activation tests
|
||||
var store = await client.GetStore(user.StoreId);
|
||||
Assert.False(store.LazyPaymentMethods);
|
||||
store.LazyPaymentMethods = true;
|
||||
store = await client.UpdateStore(store.Id,
|
||||
JObject.FromObject(store).ToObject<UpdateStoreRequest>());
|
||||
Assert.True(store.LazyPaymentMethods);
|
||||
|
||||
invoice = await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest() {Amount = 1, Currency = "USD"});
|
||||
paymentMethods = await client.GetInvoicePaymentMethods(store.Id, invoice.Id);
|
||||
Assert.Single(paymentMethods);
|
||||
Assert.False(paymentMethods.First().Activated);
|
||||
await client.ActivateInvoicePaymentMethod(user.StoreId, invoice.Id,
|
||||
paymentMethods.First().PaymentMethod);
|
||||
paymentMethods = await client.GetInvoicePaymentMethods(store.Id, invoice.Id);
|
||||
Assert.Single(paymentMethods);
|
||||
Assert.True(paymentMethods.First().Activated);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using BTCPayServer.Views.Manage;
|
||||
using BTCPayServer.Views.Server;
|
||||
using BTCPayServer.Views.Stores;
|
||||
using BTCPayServer.Views.Wallets;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -25,14 +25,22 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
private readonly InvoiceController _invoiceController;
|
||||
private readonly InvoiceRepository _invoiceRepository;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
|
||||
public LanguageService LanguageService { get; }
|
||||
|
||||
public GreenFieldInvoiceController(InvoiceController invoiceController, InvoiceRepository invoiceRepository, LinkGenerator linkGenerator, LanguageService languageService)
|
||||
public GreenFieldInvoiceController(InvoiceController invoiceController, InvoiceRepository invoiceRepository,
|
||||
LinkGenerator linkGenerator, LanguageService languageService, BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
EventAggregator eventAggregator, PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
|
||||
{
|
||||
_invoiceController = invoiceController;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_linkGenerator = linkGenerator;
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_eventAggregator = eventAggregator;
|
||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||
LanguageService = languageService;
|
||||
}
|
||||
|
||||
@@ -268,6 +276,32 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
return Ok(ToPaymentMethodModels(invoice));
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewInvoices,
|
||||
AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPost("~/api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods/{paymentMethod}/activate")]
|
||||
public async Task<IActionResult> ActivateInvoicePaymentMethod(string storeId, string invoiceId, string paymentMethod)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var invoice = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (invoice?.StoreId != store.Id)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (PaymentMethodId.TryParse(paymentMethod, out var paymentMethodId))
|
||||
{
|
||||
await _invoiceRepository.ActivateInvoicePaymentMethod(_eventAggregator, _btcPayNetworkProvider,
|
||||
_paymentMethodHandlerDictionary, store, invoice, paymentMethodId);
|
||||
return Ok();
|
||||
}
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
private InvoicePaymentMethodDataModel[] ToPaymentMethodModels(InvoiceEntity entity)
|
||||
{
|
||||
@@ -281,6 +315,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
|
||||
return new InvoicePaymentMethodDataModel()
|
||||
{
|
||||
Activated = details.Activated,
|
||||
PaymentMethod = method.GetId().ToStringNormalized(),
|
||||
Destination = details.GetPaymentDestination(),
|
||||
Rate = method.Rate,
|
||||
|
||||
@@ -131,6 +131,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints,
|
||||
OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback,
|
||||
RedirectAutomatically = storeBlob.RedirectAutomatically,
|
||||
LazyPaymentMethods = storeBlob.LazyPaymentMethods,
|
||||
ShowRecommendedFee = storeBlob.ShowRecommendedFee,
|
||||
RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget,
|
||||
DefaultLang = storeBlob.DefaultLang,
|
||||
@@ -167,6 +168,7 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
blob.LightningAmountInSatoshi = restModel.LightningAmountInSatoshi;
|
||||
blob.LightningPrivateRouteHints = restModel.LightningPrivateRouteHints;
|
||||
blob.OnChainWithLnInvoiceFallback = restModel.OnChainWithLnInvoiceFallback;
|
||||
blob.LazyPaymentMethods = restModel.LazyPaymentMethods;
|
||||
blob.RedirectAutomatically = restModel.RedirectAutomatically;
|
||||
blob.ShowRecommendedFee = restModel.ShowRecommendedFee;
|
||||
blob.RecommendedFeeBlockTarget = restModel.RecommendedFeeBlockTarget;
|
||||
|
||||
@@ -14,6 +14,7 @@ using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.Filters;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.InvoicingModels;
|
||||
@@ -503,9 +504,9 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (!isDefaultPaymentId)
|
||||
return null;
|
||||
var paymentMethodTemp = invoice.GetPaymentMethods()
|
||||
.Where(c => paymentMethodId.CryptoCode == c.GetId().CryptoCode)
|
||||
.FirstOrDefault();
|
||||
var paymentMethodTemp = invoice
|
||||
.GetPaymentMethods()
|
||||
.FirstOrDefault(c => paymentMethodId.CryptoCode == c.GetId().CryptoCode);
|
||||
if (paymentMethodTemp == null)
|
||||
paymentMethodTemp = invoice.GetPaymentMethods().First();
|
||||
network = paymentMethodTemp.Network;
|
||||
@@ -514,6 +515,12 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
|
||||
var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
if (!paymentMethodDetails.Activated)
|
||||
{
|
||||
await _InvoiceRepository.ActivateInvoicePaymentMethod(_EventAggregator, _NetworkProvider,
|
||||
_paymentMethodHandlerDictionary, store, invoice, paymentMethod.GetId());
|
||||
return await GetInvoiceModel(invoiceId, paymentMethodId, lang);
|
||||
}
|
||||
var dto = invoice.EntityToDTO();
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var accounting = paymentMethod.Calculate();
|
||||
@@ -529,6 +536,7 @@ namespace BTCPayServer.Controllers
|
||||
var divisibility = _CurrencyNameTable.GetNumberFormatInfo(paymentMethod.GetId().CryptoCode, false)?.CurrencyDecimalDigits;
|
||||
var model = new PaymentModel()
|
||||
{
|
||||
Activated = paymentMethodDetails.Activated,
|
||||
CryptoCode = network.CryptoCode,
|
||||
RootPath = this.Request.PathBase.Value.WithTrailingSlash(),
|
||||
OrderId = invoice.Metadata.OrderId,
|
||||
|
||||
@@ -44,12 +44,10 @@ namespace BTCPayServer.Controllers
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
private readonly ApplicationDbContextFactory _dbContextFactory;
|
||||
private readonly PullPaymentHostedService _paymentHostedService;
|
||||
readonly IServiceProvider _ServiceProvider;
|
||||
|
||||
public WebhookNotificationManager WebhookNotificationManager { get; }
|
||||
|
||||
public InvoiceController(
|
||||
IServiceProvider serviceProvider,
|
||||
InvoiceRepository invoiceRepository,
|
||||
CurrencyNameTable currencyNameTable,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
@@ -63,7 +61,6 @@ namespace BTCPayServer.Controllers
|
||||
PullPaymentHostedService paymentHostedService,
|
||||
WebhookNotificationManager webhookNotificationManager)
|
||||
{
|
||||
_ServiceProvider = serviceProvider;
|
||||
_CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
|
||||
_StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
|
||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||
@@ -321,7 +318,16 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var logPrefix = $"{supportedPaymentMethod.PaymentId.ToPrettyString()}:";
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var preparePayment = handler.PreparePayment(supportedPaymentMethod, store, network);
|
||||
|
||||
object preparePayment;
|
||||
if (storeBlob.LazyPaymentMethods)
|
||||
{
|
||||
preparePayment = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
preparePayment = handler.PreparePayment(supportedPaymentMethod, store, network);
|
||||
}
|
||||
var rate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, entity.Currency)];
|
||||
if (rate.BidAsk == null)
|
||||
{
|
||||
|
||||
@@ -406,6 +406,7 @@ namespace BTCPayServer.Controllers
|
||||
vm.LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi;
|
||||
vm.LightningPrivateRouteHints = storeBlob.LightningPrivateRouteHints;
|
||||
vm.OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback;
|
||||
vm.LazyPaymentMethods = storeBlob.LazyPaymentMethods;
|
||||
vm.RedirectAutomatically = storeBlob.RedirectAutomatically;
|
||||
vm.ShowRecommendedFee = storeBlob.ShowRecommendedFee;
|
||||
vm.RecommendedFeeBlockTarget = storeBlob.RecommendedFeeBlockTarget;
|
||||
@@ -477,6 +478,7 @@ namespace BTCPayServer.Controllers
|
||||
}).ToList();
|
||||
|
||||
blob.RequiresRefundEmail = model.RequiresRefundEmail;
|
||||
blob.LazyPaymentMethods = model.LazyPaymentMethods;
|
||||
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
|
||||
blob.LightningPrivateRouteHints = model.LightningPrivateRouteHints;
|
||||
blob.OnChainWithLnInvoiceFallback = model.OnChainWithLnInvoiceFallback;
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace BTCPayServer.Data
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||
public bool LazyPaymentMethods { get; set; }
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
public bool ShowRecommendedFee { get; set; }
|
||||
public int RecommendedFeeBlockTarget { get; set; }
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace BTCPayServer.HostedServices
|
||||
|
||||
// We keep backward compatibility with bitpay by passing BTC info to the notification
|
||||
// we don't pass other info, as it is a bad idea to use IPN data for logic processing (can be faked)
|
||||
var btcCryptoInfo = dto.CryptoInfo.FirstOrDefault(c => c.GetpaymentMethodId() == new PaymentMethodId("BTC", Payments.PaymentTypes.BTCLike));
|
||||
var btcCryptoInfo = dto.CryptoInfo.FirstOrDefault(c => c.GetpaymentMethodId() == new PaymentMethodId("BTC", Payments.PaymentTypes.BTCLike) && !string.IsNullOrEmpty(c.Address));
|
||||
if (btcCryptoInfo != null)
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||
public string RootPath { get; set; }
|
||||
public decimal CoinSwitchAmountMarkupPercentage { get; set; }
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
public bool Activated { get; set; }
|
||||
public string InvoiceCurrency { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
@@ -45,6 +44,9 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
[Display(Name = "Include lightning invoice fallback to on-chain BIP21 payment url")]
|
||||
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||
|
||||
[Display(Name = "Only enable the payment method after user explicitly chooses it")]
|
||||
public bool LazyPaymentMethods { get; set; }
|
||||
|
||||
[Display(Name = "Redirect invoice to redirect url automatically after paid")]
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
|
||||
public decimal GetFeeRate()
|
||||
{
|
||||
return FeeRate.SatoshiPerByte;
|
||||
return FeeRate?.SatoshiPerByte ?? 0;
|
||||
}
|
||||
|
||||
public void SetPaymentDetails(IPaymentMethodDetails newPaymentMethodDetails)
|
||||
@@ -31,6 +31,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
DepositAddress = newPaymentMethodDetails.GetPaymentDestination();
|
||||
KeyPath = (newPaymentMethodDetails as BitcoinLikeOnChainPaymentMethod)?.KeyPath;
|
||||
}
|
||||
public bool Activated { get; set; } = true;
|
||||
public NetworkFeeMode NetworkFeeMode { get; set; }
|
||||
|
||||
FeeRate _NetworkFeeRate;
|
||||
|
||||
@@ -64,16 +64,24 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
model.PaymentMethodName = GetPaymentMethodName(network);
|
||||
|
||||
var lightningFallback = "";
|
||||
if (network.SupportLightning && storeBlob.OnChainWithLnInvoiceFallback)
|
||||
if (model.Activated && network.SupportLightning && storeBlob.OnChainWithLnInvoiceFallback)
|
||||
{
|
||||
var lightningInfo = invoiceResponse.CryptoInfo.FirstOrDefault(a =>
|
||||
a.GetpaymentMethodId() == new PaymentMethodId(model.CryptoCode, PaymentTypes.LightningLike));
|
||||
if (!String.IsNullOrEmpty(lightningInfo?.PaymentUrls?.BOLT11))
|
||||
if (!string.IsNullOrEmpty(lightningInfo?.PaymentUrls?.BOLT11))
|
||||
lightningFallback = "&" + lightningInfo.PaymentUrls.BOLT11.Replace("lightning:", "lightning=", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls.BIP21 + lightningFallback;
|
||||
model.InvoiceBitcoinUrlQR = model.InvoiceBitcoinUrl;
|
||||
if (model.Activated)
|
||||
{
|
||||
model.InvoiceBitcoinUrl = (cryptoInfo.PaymentUrls?.BIP21 ?? "") + lightningFallback;
|
||||
model.InvoiceBitcoinUrlQR = model.InvoiceBitcoinUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
model.InvoiceBitcoinUrl = "";
|
||||
model.InvoiceBitcoinUrlQR = "";
|
||||
}
|
||||
|
||||
// Most wallets still don't support BITCOIN: schema, so we're leaving this for better days
|
||||
// Ref: https://github.com/btcpayserver/btcpayserver/pull/2060#issuecomment-723828348
|
||||
@@ -145,12 +153,19 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
DerivationSchemeSettings supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store,
|
||||
BTCPayNetwork network, object preparePaymentObject)
|
||||
{
|
||||
if (preparePaymentObject is null)
|
||||
{
|
||||
return new BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
Activated = false
|
||||
};
|
||||
}
|
||||
if (!_ExplorerProvider.IsAvailable(network))
|
||||
throw new PaymentMethodUnavailableException($"Full node not available");
|
||||
var prepare = (Prepare)preparePaymentObject;
|
||||
var onchainMethod = new BitcoinLikeOnChainPaymentMethod();
|
||||
var blob = store.GetStoreBlob();
|
||||
|
||||
onchainMethod.Activated = true;
|
||||
// TODO: this needs to be refactored to move this logic into BitcoinLikeOnChainPaymentMethod
|
||||
// This is likely a constructor code
|
||||
onchainMethod.NetworkFeeMode = blob.NetworkFeeMode;
|
||||
|
||||
@@ -352,7 +352,6 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
if (strategy == null)
|
||||
continue;
|
||||
var cryptoId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
|
||||
var paymentMethod = invoice.GetPaymentMethod(cryptoId).GetPaymentMethodDetails() as BitcoinLikeOnChainPaymentMethod;
|
||||
|
||||
if (!invoice.Support(cryptoId))
|
||||
continue;
|
||||
@@ -403,6 +402,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
var paymentMethod = invoice.GetPaymentMethod(wallet.Network, PaymentTypes.BTCLike);
|
||||
if (paymentMethod != null &&
|
||||
paymentMethod.GetPaymentMethodDetails() is BitcoinLikeOnChainPaymentMethod btc &&
|
||||
btc.Activated &&
|
||||
btc.GetDepositAddress(wallet.Network.NBitcoinNetwork).ScriptPubKey == paymentData.ScriptPubKey &&
|
||||
paymentMethod.Calculate().Due > Money.Zero)
|
||||
{
|
||||
|
||||
@@ -16,5 +16,7 @@ namespace BTCPayServer.Payments
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
decimal GetNextNetworkFee();
|
||||
|
||||
bool Activated {get;set;}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,13 @@ namespace BTCPayServer.Payments.Lightning
|
||||
LightningSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store,
|
||||
BTCPayNetwork network, object preparePaymentObject)
|
||||
{
|
||||
if (preparePaymentObject is null)
|
||||
{
|
||||
return new LightningLikePaymentMethodDetails()
|
||||
{
|
||||
Activated = false
|
||||
};
|
||||
}
|
||||
//direct casting to (BTCPayNetwork) is fixed in other pull requests with better generic interfacing for handlers
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var test = GetNodeInfo(paymentMethod.PreferOnion, supportedPaymentMethod, network);
|
||||
@@ -99,6 +106,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||
var nodeInfo = await test;
|
||||
return new LightningLikePaymentMethodDetails
|
||||
{
|
||||
Activated = true,
|
||||
BOLT11 = lightningInvoice.BOLT11,
|
||||
InvoiceId = lightningInvoice.Id,
|
||||
NodeInfo = nodeInfo.ToString()
|
||||
@@ -191,8 +199,8 @@ namespace BTCPayServer.Payments.Lightning
|
||||
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode);
|
||||
model.PaymentMethodName = GetPaymentMethodName(network);
|
||||
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls.BOLT11;
|
||||
model.InvoiceBitcoinUrlQR = $"lightning:{cryptoInfo.PaymentUrls.BOLT11.ToUpperInvariant().Substring("LIGHTNING:".Length)}";
|
||||
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls?.BOLT11;
|
||||
model.InvoiceBitcoinUrlQR = $"lightning:{cryptoInfo.PaymentUrls?.BOLT11?.ToUpperInvariant()?.Substring("LIGHTNING:".Length)}";
|
||||
|
||||
model.PeerInfo = ((LightningLikePaymentMethodDetails) paymentMethod.GetPaymentMethodDetails()).NodeInfo;
|
||||
if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC")
|
||||
@@ -238,5 +246,12 @@ namespace BTCPayServer.Payments.Lightning
|
||||
{
|
||||
return $"{network.DisplayName} (Lightning)";
|
||||
}
|
||||
|
||||
public override object PreparePayment(LightningSupportedPaymentMethod supportedPaymentMethod, StoreData store,
|
||||
BTCPayNetworkBase network)
|
||||
{
|
||||
// pass a non null obj, so that if lazy payment feature is used, it has a marker to trigger activation
|
||||
return new { };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,5 +25,6 @@ namespace BTCPayServer.Payments.Lightning
|
||||
{
|
||||
return 0.0m;
|
||||
}
|
||||
public bool Activated { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||
.Where(c => c.GetId().PaymentType == PaymentTypes.LightningLike))
|
||||
{
|
||||
var lightningMethod = paymentMethod.GetPaymentMethodDetails() as LightningLikePaymentMethodDetails;
|
||||
if (lightningMethod == null)
|
||||
if (lightningMethod == null || !lightningMethod.Activated)
|
||||
continue;
|
||||
var lightningSupportedMethod = invoice.GetSupportedPaymentMethod<LightningSupportedPaymentMethod>()
|
||||
.FirstOrDefault(c => c.CryptoCode == paymentMethod.GetId().CryptoCode);
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace BTCPayServer.Payments.PayJoin
|
||||
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
|
||||
var paymentDetails =
|
||||
paymentMethod.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod;
|
||||
if (paymentDetails is null || !paymentDetails.PayjoinEnabled)
|
||||
if (paymentDetails is null || !paymentDetails.PayjoinEnabled || !paymentDetails.Activated)
|
||||
continue;
|
||||
if (invoice.GetAllBitcoinPaymentData().Any())
|
||||
{
|
||||
|
||||
@@ -70,6 +70,10 @@ namespace BTCPayServer.Payments
|
||||
public override string GetPaymentLink(BTCPayNetworkBase network, IPaymentMethodDetails paymentMethodDetails,
|
||||
Money cryptoInfoDue, string serverUri)
|
||||
{
|
||||
if (!paymentMethodDetails.Activated)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
var bip21 = ((BTCPayNetwork)network).GenerateBIP21(paymentMethodDetails.GetPaymentDestination(), cryptoInfoDue);
|
||||
|
||||
if ((paymentMethodDetails as BitcoinLikeOnChainPaymentMethod)?.PayjoinEnabled is true && serverUri != null)
|
||||
|
||||
@@ -52,6 +52,10 @@ namespace BTCPayServer.Payments
|
||||
public override string GetPaymentLink(BTCPayNetworkBase network, IPaymentMethodDetails paymentMethodDetails,
|
||||
Money cryptoInfoDue, string serverUri)
|
||||
{
|
||||
if (!paymentMethodDetails.Activated)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
var lnInvoiceTrimmedOfScheme = paymentMethodDetails.GetPaymentDestination().ToLowerInvariant()
|
||||
.Replace("lightning:", "", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Payments
|
||||
{
|
||||
DepositAddress = newPaymentDestination;
|
||||
}
|
||||
|
||||
public bool Activated { get; set; }
|
||||
public long Index { get; set; }
|
||||
public string XPub { get; set; }
|
||||
public string DepositAddress { get; set; }
|
||||
|
||||
@@ -35,6 +35,13 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Payments
|
||||
EthereumSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod,
|
||||
StoreData store, EthereumBTCPayNetwork network, object preparePaymentObject)
|
||||
{
|
||||
if (preparePaymentObject is null)
|
||||
{
|
||||
return new EthereumLikeOnChainPaymentMethodDetails()
|
||||
{
|
||||
Activated = false
|
||||
};
|
||||
}
|
||||
if (!_ethereumService.IsAvailable(network.CryptoCode, out var error))
|
||||
throw new PaymentMethodUnavailableException(error??$"Not configured yet");
|
||||
var invoice = paymentMethod.ParentEntity;
|
||||
@@ -47,7 +54,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Payments
|
||||
|
||||
return new EthereumLikeOnChainPaymentMethodDetails()
|
||||
{
|
||||
DepositAddress = address.Address, Index = address.Index, XPub = address.XPub
|
||||
DepositAddress = address.Address, Index = address.Index, XPub = address.XPub, Activated = true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -79,7 +86,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Payments
|
||||
model.PaymentMethodName = GetPaymentMethodName(network);
|
||||
model.CryptoImage = GetCryptoImage(network);
|
||||
model.InvoiceBitcoinUrl = "";
|
||||
model.InvoiceBitcoinUrlQR = cryptoInfo.Address;
|
||||
model.InvoiceBitcoinUrlQR = cryptoInfo.Address ?? "";
|
||||
}
|
||||
|
||||
public override string GetCryptoImage(PaymentMethodId paymentMethodId)
|
||||
|
||||
@@ -217,7 +217,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
|
||||
var invoices = await _invoiceRepository.GetInvoices(new InvoiceQuery() {InvoiceId = invoiceIds});
|
||||
invoices = invoices
|
||||
.Where(entity => PaymentMethods.Any(id => entity.GetPaymentMethod(id) != null))
|
||||
.Where(entity => PaymentMethods.Any(id => entity.GetPaymentMethod(id)?.GetPaymentMethodDetails()?.Activated is true))
|
||||
.ToArray();
|
||||
|
||||
await UpdatePaymentStates(invoices, cancellationToken);
|
||||
@@ -245,7 +245,7 @@ namespace BTCPayServer.Services.Altcoins.Ethereum.Services
|
||||
ExistingPayments: entity.GetPayments(network).Select(paymentEntity => (Payment: paymentEntity,
|
||||
PaymentData: (EthereumLikePaymentData)paymentEntity.GetCryptoPaymentData(),
|
||||
Invoice: entity))
|
||||
)).Where(tuple => tuple.PaymentMethodDetails != null).ToList();
|
||||
)).Where(tuple => tuple.PaymentMethodDetails?.GetPaymentMethodDetails()?.Activated is true).ToList();
|
||||
|
||||
var existingPaymentData = expandedInvoices.SelectMany(tuple =>
|
||||
tuple.ExistingPayments.Where(valueTuple => valueTuple.Payment.Accounted)).ToList();
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments
|
||||
{
|
||||
return 0.0m;
|
||||
}
|
||||
|
||||
public bool Activated { get; set; } = true;
|
||||
public long AccountIndex { get; set; }
|
||||
public long AddressIndex { get; set; }
|
||||
public string DepositAddress { get; set; }
|
||||
|
||||
@@ -34,6 +34,14 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments
|
||||
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(InvoiceLogs logs, MoneroSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod,
|
||||
StoreData store, MoneroLikeSpecificBtcPayNetwork network, object preparePaymentObject)
|
||||
{
|
||||
|
||||
if (preparePaymentObject is null)
|
||||
{
|
||||
return new MoneroLikeOnChainPaymentMethodDetails()
|
||||
{
|
||||
Activated = false
|
||||
};
|
||||
}
|
||||
|
||||
if (!_moneroRpcProvider.IsAvailable(network.CryptoCode))
|
||||
throw new PaymentMethodUnavailableException($"Node or wallet not available");
|
||||
@@ -49,7 +57,8 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments
|
||||
NextNetworkFee = MoneroMoney.Convert(feeRatePerByte * 100),
|
||||
AccountIndex = supportedPaymentMethod.AccountIndex,
|
||||
AddressIndex = address.AddressIndex,
|
||||
DepositAddress = address.Address
|
||||
DepositAddress = address.Address,
|
||||
Activated = true
|
||||
};
|
||||
|
||||
}
|
||||
@@ -77,15 +86,22 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments
|
||||
StoreBlob storeBlob, IPaymentMethod paymentMethod)
|
||||
{
|
||||
var paymentMethodId = paymentMethod.GetId();
|
||||
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
|
||||
var network = _networkProvider.GetNetwork<MoneroLikeSpecificBtcPayNetwork>(model.CryptoCode);
|
||||
model.PaymentMethodName = GetPaymentMethodName(network);
|
||||
model.CryptoImage = GetCryptoImage(network);
|
||||
model.InvoiceBitcoinUrl = MoneroPaymentType.Instance.GetPaymentLink(network, new MoneroLikeOnChainPaymentMethodDetails()
|
||||
if (model.Activated)
|
||||
{
|
||||
DepositAddress = cryptoInfo.Address
|
||||
}, cryptoInfo.Due, null);
|
||||
model.InvoiceBitcoinUrlQR = model.InvoiceBitcoinUrl;
|
||||
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
|
||||
model.InvoiceBitcoinUrl = MoneroPaymentType.Instance.GetPaymentLink(network,
|
||||
new MoneroLikeOnChainPaymentMethodDetails() {DepositAddress = cryptoInfo.Address}, cryptoInfo.Due,
|
||||
null);
|
||||
model.InvoiceBitcoinUrlQR = model.InvoiceBitcoinUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
model.InvoiceBitcoinUrl = "";
|
||||
model.InvoiceBitcoinUrlQR = "";
|
||||
}
|
||||
}
|
||||
public override string GetCryptoImage(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
|
||||
@@ -51,8 +51,9 @@ namespace BTCPayServer.Services.Altcoins.Monero.Payments
|
||||
|
||||
public override string GetPaymentLink(BTCPayNetworkBase network, IPaymentMethodDetails paymentMethodDetails, Money cryptoInfoDue, string serverUri)
|
||||
{
|
||||
return
|
||||
$"{(network as MoneroLikeSpecificBtcPayNetwork).UriScheme}:{paymentMethodDetails.GetPaymentDestination()}?tx_amount={cryptoInfoDue.ToDecimal(MoneyUnit.BTC)}";
|
||||
return paymentMethodDetails.Activated
|
||||
? $"{(network as MoneroLikeSpecificBtcPayNetwork).UriScheme}:{paymentMethodDetails.GetPaymentDestination()}?tx_amount={cryptoInfoDue.ToDecimal(MoneyUnit.BTC)}"
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
public override string InvoiceViewPaymentPartialName { get; } = "Monero/ViewMoneroLikePaymentData";
|
||||
|
||||
@@ -121,6 +121,7 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
||||
var paymentMethod = invoice.GetPaymentMethod(payment.Network, MoneroPaymentType.Instance);
|
||||
if (paymentMethod != null &&
|
||||
paymentMethod.GetPaymentMethodDetails() is MoneroLikeOnChainPaymentMethodDetails monero &&
|
||||
monero.Activated &&
|
||||
monero.GetPaymentDestination() == paymentData.GetDestination() &&
|
||||
paymentMethod.Calculate().Due > Money.Zero)
|
||||
{
|
||||
@@ -363,7 +364,8 @@ namespace BTCPayServer.Services.Altcoins.Monero.Services
|
||||
}
|
||||
|
||||
var invoices = await _invoiceRepository.GetInvoices(new InvoiceQuery() { InvoiceId = invoiceIds });
|
||||
invoices = invoices.Where(entity => entity.GetPaymentMethod(new PaymentMethodId(cryptoCode, MoneroPaymentType.Instance)) != null).ToArray();
|
||||
invoices = invoices.Where(entity => entity.GetPaymentMethod(new PaymentMethodId(cryptoCode, MoneroPaymentType.Instance))
|
||||
?.GetPaymentMethodDetails().Activated is true).ToArray();
|
||||
_logger.LogInformation($"Updating pending payments for {cryptoCode} in {string.Join(',', invoiceIds)}");
|
||||
await UpdatePaymentStates(cryptoCode, invoices);
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
foreach (var strat in strategies.Properties())
|
||||
{
|
||||
var paymentMethodId = PaymentMethodId.Parse(strat.Name);
|
||||
var network = Networks.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
var network = Networks.GetNetwork<BTCPayNetworkBase>(paymentMethodId.CryptoCode);
|
||||
if (network != null)
|
||||
{
|
||||
if (network == Networks.BTC && paymentMethodId.PaymentType == PaymentTypes.BTCLike)
|
||||
@@ -374,7 +374,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
}).ToList();
|
||||
|
||||
|
||||
if (paymentId.PaymentType == PaymentTypes.LightningLike)
|
||||
if (details?.Activated is true && paymentId.PaymentType == PaymentTypes.LightningLike)
|
||||
{
|
||||
cryptoInfo.PaymentUrls = new InvoicePaymentUrls()
|
||||
{
|
||||
@@ -382,7 +382,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
ServerUrl)
|
||||
};
|
||||
}
|
||||
else if (paymentId.PaymentType == PaymentTypes.BTCLike)
|
||||
else if (details?.Activated is true && paymentId.PaymentType == PaymentTypes.BTCLike)
|
||||
{
|
||||
var minerInfo = new MinerFeeInfo();
|
||||
minerInfo.TotalFee = accounting.NetworkFee.Satoshi;
|
||||
@@ -936,10 +936,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
private decimal GetTxFee()
|
||||
{
|
||||
var method = GetPaymentMethodDetails();
|
||||
if (method == null)
|
||||
return 0.0m;
|
||||
return method.GetNextNetworkFee();
|
||||
return GetPaymentMethodDetails()?.GetNextNetworkFee()?? 0m;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
52
BTCPayServer/Services/Invoices/InvoiceExtensions.cs
Normal file
52
BTCPayServer/Services/Invoices/InvoiceExtensions.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Payments;
|
||||
|
||||
namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
public static class InvoiceExtensions
|
||||
{
|
||||
|
||||
public static async Task ActivateInvoicePaymentMethod(this InvoiceRepository invoiceRepository,
|
||||
EventAggregator eventAggregator, BTCPayNetworkProvider btcPayNetworkProvider, PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
|
||||
StoreData store,InvoiceEntity invoice, PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var eligibleMethodToActivate = invoice.GetPaymentMethod(paymentMethodId);
|
||||
if (!eligibleMethodToActivate.GetPaymentMethodDetails().Activated)
|
||||
{
|
||||
var payHandler = paymentMethodHandlerDictionary[paymentMethodId];
|
||||
var supportPayMethod = invoice.GetSupportedPaymentMethod()
|
||||
.Single(method => method.PaymentId == paymentMethodId);
|
||||
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
|
||||
var network = btcPayNetworkProvider.GetNetwork(paymentMethodId.CryptoCode);
|
||||
var prepare = payHandler.PreparePayment(supportPayMethod, store, network);
|
||||
InvoiceLogs logs = new InvoiceLogs();
|
||||
try
|
||||
{
|
||||
logs.Write($"{paymentMethodId}: Activating", InvoiceEventData.EventSeverity.Info);
|
||||
var newDetails = await
|
||||
payHandler.CreatePaymentMethodDetails(logs, supportPayMethod, paymentMethod, store, network,
|
||||
prepare);
|
||||
eligibleMethodToActivate.SetPaymentMethodDetails(newDetails);
|
||||
await invoiceRepository.UpdateInvoicePaymentMethod(invoice.Id, eligibleMethodToActivate);
|
||||
}
|
||||
catch (PaymentMethodUnavailableException ex)
|
||||
{
|
||||
logs.Write($"{paymentMethodId}: Payment method unavailable ({ex.Message})", InvoiceEventData.EventSeverity.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logs.Write($"{paymentMethodId}: Unexpected exception ({ex})", InvoiceEventData.EventSeverity.Error);
|
||||
}
|
||||
|
||||
await invoiceRepository.AddInvoiceLogs(invoice.Id, logs);
|
||||
eventAggregator.Publish(new InvoiceNeedUpdateEvent(invoice.Id));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ using Microsoft.Extensions.Logging;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using Encoders = NBitcoin.DataEncoders.Encoders;
|
||||
using InvoiceData = BTCPayServer.Data.InvoiceData;
|
||||
|
||||
@@ -180,8 +181,12 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
if (paymentMethod.Network == null)
|
||||
throw new InvalidOperationException("CryptoCode unsupported");
|
||||
var paymentDestination = paymentMethod.GetPaymentMethodDetails().GetPaymentDestination();
|
||||
|
||||
var details = paymentMethod.GetPaymentMethodDetails();
|
||||
if (!details.Activated)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var paymentDestination = details.GetPaymentDestination();
|
||||
string address = GetDestination(paymentMethod);
|
||||
await context.AddressInvoices.AddAsync(new AddressInvoiceData()
|
||||
{
|
||||
@@ -244,7 +249,13 @@ namespace BTCPayServer.Services.Invoices
|
||||
if (paymentMethod.GetId().PaymentType == Payments.PaymentTypes.BTCLike)
|
||||
{
|
||||
var network = (BTCPayNetwork)paymentMethod.Network;
|
||||
return ((Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod)paymentMethod.GetPaymentMethodDetails()).GetDepositAddress(network.NBitcoinNetwork).ScriptPubKey.Hash.ToString();
|
||||
var details =
|
||||
(Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod)paymentMethod.GetPaymentMethodDetails();
|
||||
if (!details.Activated)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return details.GetDepositAddress(network.NBitcoinNetwork).ScriptPubKey.Hash.ToString();
|
||||
}
|
||||
///////////////
|
||||
return paymentMethod.GetPaymentMethodDetails().GetPaymentDestination();
|
||||
@@ -294,6 +305,39 @@ namespace BTCPayServer.Services.Invoices
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task UpdateInvoicePaymentMethod(string invoiceId, PaymentMethod paymentMethod)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
var invoice = await context.Invoices.FindAsync(invoiceId);
|
||||
if (invoice == null)
|
||||
return;
|
||||
var network = paymentMethod.Network;
|
||||
var invoiceEntity = invoice.GetBlob(_Networks);
|
||||
var newDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
var existing = invoiceEntity.GetPaymentMethod(paymentMethod.GetId());
|
||||
if (existing.GetPaymentMethodDetails().GetPaymentDestination() != newDetails.GetPaymentDestination() && newDetails.Activated)
|
||||
{
|
||||
await context.AddressInvoices.AddAsync(new AddressInvoiceData()
|
||||
{
|
||||
InvoiceDataId = invoiceId,
|
||||
CreatedTime = DateTimeOffset.UtcNow
|
||||
}
|
||||
.Set(GetDestination(paymentMethod), paymentMethod.GetId()));
|
||||
await context.HistoricalAddressInvoices.AddAsync(new HistoricalAddressInvoiceData()
|
||||
{
|
||||
InvoiceDataId = invoiceId,
|
||||
Assigned = DateTimeOffset.UtcNow
|
||||
}.SetAddress(paymentMethod.GetPaymentMethodDetails().GetPaymentDestination(), network.CryptoCode));
|
||||
}
|
||||
invoiceEntity.SetPaymentMethod(paymentMethod);
|
||||
invoice.Blob = ToBytes(invoiceEntity, network);
|
||||
AddToTextSearch(context, invoice, paymentMethod.GetPaymentMethodDetails().GetPaymentDestination());
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddPendingInvoiceIfNotPresent(string invoiceId)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</line-items>
|
||||
<component v-if="srvModel.uiSettings && srvModel.uiSettings.checkoutHeaderVueComponentName"
|
||||
<component v-if="srvModel.uiSettings && srvModel.uiSettings.checkoutHeaderVueComponentName && srvModel.activated"
|
||||
v-bind:srv-model="srvModel"
|
||||
v-bind:is="srvModel.uiSettings.checkoutHeaderVueComponentName">
|
||||
</component>
|
||||
@@ -184,7 +184,7 @@
|
||||
</form>
|
||||
</div>
|
||||
<div v-if="showPaymentUI">
|
||||
<component v-if="srvModel.uiSettings && srvModel.uiSettings.checkoutBodyVueComponentName"
|
||||
<component v-if="srvModel.uiSettings && srvModel.uiSettings.checkoutBodyVueComponentName && srvModel.activated"
|
||||
v-bind:srv-model="srvModel"
|
||||
v-bind:is="srvModel.uiSettings.checkoutBodyVueComponentName">
|
||||
</component>
|
||||
|
||||
@@ -80,6 +80,12 @@
|
||||
<label asp-for="OnChainWithLnInvoiceFallback" class="form-check-label"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="LazyPaymentMethods" type="checkbox" class="form-check-input" />
|
||||
<label asp-for="LazyPaymentMethods" 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" />
|
||||
|
||||
@@ -542,6 +542,71 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods/{paymentMethod}/activate": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Invoices"
|
||||
],
|
||||
"summary": "Activate Payment Method",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "storeId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The store to query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "invoiceId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The invoice to update",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "paymentMethod",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"description": "The payment method to activate",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Activate an invoice payment method (if lazy payments mode is enabled)",
|
||||
"operationId": "Invoices_ActivatePaymentMethod",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
},
|
||||
"400": {
|
||||
"description": "A list of errors that occurred when updating the invoice",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ValidationProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "If you are authenticated but forbidden to activate the invoice payment method"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
"btcpay.store.canviewinvoices"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
@@ -729,7 +794,7 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "A specific set of payment methods to use for this invoice (ie. BTC, BTC-LightningNetwork). By default, select all payment methods activated in the store."
|
||||
"description": "A specific set of payment methods to use for this invoice (ie. BTC, BTC-LightningNetwork). By default, select all payment methods enabled in the store."
|
||||
},
|
||||
"expirationMinutes": {
|
||||
"nullable": true,
|
||||
@@ -837,6 +902,10 @@
|
||||
"$ref": "#/components/schemas/Payment"
|
||||
},
|
||||
"description": "Payments made with this payment method."
|
||||
},
|
||||
"activated": {
|
||||
"type": "boolean",
|
||||
"description": "If the payment method is activated (when lazy payments option is enabled"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -381,6 +381,11 @@
|
||||
"default": false,
|
||||
"description": "If true, payjoin will be proposed in the checkout page if possible. ([More information](https://docs.btcpayserver.org/Payjoin/))"
|
||||
},
|
||||
"lazyPaymentMethods": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If true, payment methods are enabled individually upon user interaction in the invoice"
|
||||
},
|
||||
"defaultPaymentMethod": {
|
||||
"type": "string",
|
||||
"example": "BTC",
|
||||
|
||||
Reference in New Issue
Block a user