mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 22:44:29 +01:00
BTCPay Abstractions: Move PaymentMethod specific logic to their handlers (#850)
This commit is contained in:
committed by
Nicolas Dorier
parent
d3e3c31b0c
commit
81dae7d350
@@ -275,6 +275,8 @@ namespace BTCPayServer.Tests
|
||||
return _Host.Services.GetRequiredService<T>();
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider => _Host.Services;
|
||||
|
||||
public T GetController<T>(string userId = null, string storeId = null, Claim[] additionalClaims = null) where T : Controller
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
|
||||
@@ -60,6 +60,7 @@ using BTCPayServer.Models.AccountViewModels;
|
||||
using BTCPayServer.Services.U2F.Models;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
@@ -98,44 +99,58 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanCalculateCryptoDue2()
|
||||
{
|
||||
var dummy = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest).ToString();
|
||||
#pragma warning disable CS0618
|
||||
InvoiceEntity invoiceEntity = new InvoiceEntity();
|
||||
var dummy = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest).ToString();
|
||||
var networkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
|
||||
{
|
||||
new BitcoinLikePaymentHandler(null, networkProvider, null, null),
|
||||
new LightningLikePaymentHandler(null, null, networkProvider, null),
|
||||
});
|
||||
InvoiceEntity invoiceEntity = new InvoiceEntity() { PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary};
|
||||
invoiceEntity.Payments = new System.Collections.Generic.List<PaymentEntity>();
|
||||
invoiceEntity.ProductInformation = new ProductInformation() { Price = 100 };
|
||||
invoiceEntity.ProductInformation = new ProductInformation() {Price = 100};
|
||||
PaymentMethodDictionary paymentMethods = new PaymentMethodDictionary();
|
||||
paymentMethods.Add(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "BTC",
|
||||
Rate = 10513.44m,
|
||||
}.SetPaymentMethodDetails(new BTCPayServer.Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
NextNetworkFee = Money.Coins(0.00000100m),
|
||||
DepositAddress = dummy
|
||||
}));
|
||||
paymentMethods.Add(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "LTC",
|
||||
Rate = 216.79m
|
||||
}.SetPaymentMethodDetails(new BTCPayServer.Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
NextNetworkFee = Money.Coins(0.00010000m),
|
||||
DepositAddress = dummy
|
||||
}));
|
||||
paymentMethods.Add(new PaymentMethod() {CryptoCode = "BTC", Rate = 10513.44m,}.SetPaymentMethodDetails(
|
||||
new BTCPayServer.Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
NextNetworkFee = Money.Coins(0.00000100m), DepositAddress = dummy
|
||||
}));
|
||||
paymentMethods.Add(new PaymentMethod() {CryptoCode = "LTC", Rate = 216.79m}.SetPaymentMethodDetails(
|
||||
new BTCPayServer.Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
NextNetworkFee = Money.Coins(0.00010000m), DepositAddress = dummy
|
||||
}));
|
||||
invoiceEntity.SetPaymentMethods(paymentMethods);
|
||||
|
||||
var btc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
var accounting = btc.Calculate();
|
||||
|
||||
invoiceEntity.Payments.Add(new PaymentEntity() { Accounted = true, CryptoCode = "BTC", NetworkFee = 0.00000100m }.SetCryptoPaymentData(new BitcoinLikePaymentData()
|
||||
{
|
||||
Output = new TxOut() { Value = Money.Coins(0.00151263m) }
|
||||
}));
|
||||
invoiceEntity.Payments.Add(
|
||||
new PaymentEntity()
|
||||
{
|
||||
Accounted = true,
|
||||
CryptoCode = "BTC",
|
||||
NetworkFee = 0.00000100m,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
}
|
||||
.SetCryptoPaymentData(new BitcoinLikePaymentData()
|
||||
{
|
||||
Output = new TxOut() {Value = Money.Coins(0.00151263m)}
|
||||
}));
|
||||
accounting = btc.Calculate();
|
||||
invoiceEntity.Payments.Add(new PaymentEntity() { Accounted = true, CryptoCode = "BTC", NetworkFee = 0.00000100m }.SetCryptoPaymentData(new BitcoinLikePaymentData()
|
||||
{
|
||||
Output = new TxOut() { Value = accounting.Due }
|
||||
}));
|
||||
invoiceEntity.Payments.Add(
|
||||
new PaymentEntity()
|
||||
{
|
||||
Accounted = true,
|
||||
CryptoCode = "BTC",
|
||||
NetworkFee = 0.00000100m,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
}
|
||||
.SetCryptoPaymentData(new BitcoinLikePaymentData()
|
||||
{
|
||||
Output = new TxOut() {Value = accounting.Due}
|
||||
}));
|
||||
accounting = btc.Calculate();
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
Assert.Equal(Money.Zero, accounting.DueUncapped);
|
||||
@@ -195,57 +210,81 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanCalculateCryptoDue()
|
||||
{
|
||||
var entity = new InvoiceEntity();
|
||||
var networkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
|
||||
{
|
||||
new BitcoinLikePaymentHandler(null, networkProvider, null, null),
|
||||
new LightningLikePaymentHandler(null, null, networkProvider, null),
|
||||
});
|
||||
var entity = new InvoiceEntity() {PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary};
|
||||
#pragma warning disable CS0618
|
||||
entity.Payments = new System.Collections.Generic.List<PaymentEntity>();
|
||||
entity.SetPaymentMethod(new PaymentMethod() { CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m) });
|
||||
entity.ProductInformation = new ProductInformation() { Price = 5000 };
|
||||
entity.SetPaymentMethod(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m)
|
||||
});
|
||||
entity.ProductInformation = new ProductInformation() {Price = 5000};
|
||||
|
||||
var paymentMethod = entity.GetPaymentMethods().TryGet("BTC", PaymentTypes.BTCLike);
|
||||
var accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { Output = new TxOut(Money.Coins(0.5m), new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
Output = new TxOut(Money.Coins(0.5m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
});
|
||||
|
||||
accounting = paymentMethod.Calculate();
|
||||
//Since we need to spend one more txout, it should be 1.1 - 0,5 + 0.1
|
||||
Assert.Equal(Money.Coins(0.7m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.2m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { Output = new TxOut(Money.Coins(0.2m), new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
Output = new TxOut(Money.Coins(0.2m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
});
|
||||
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(0.6m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.3m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { Output = new TxOut(Money.Coins(0.6m), new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
Output = new TxOut(Money.Coins(0.6m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
});
|
||||
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.3m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { Output = new TxOut(Money.Coins(0.2m), new Key()), Accounted = true });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
Output = new TxOut(Money.Coins(0.2m), new Key()),
|
||||
Accounted = true,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
});
|
||||
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.3m), accounting.TotalDue);
|
||||
|
||||
entity = new InvoiceEntity();
|
||||
entity.ProductInformation = new ProductInformation() { Price = 5000 };
|
||||
entity = new InvoiceEntity() {PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary};
|
||||
entity.ProductInformation = new ProductInformation() {Price = 5000};
|
||||
PaymentMethodDictionary paymentMethods = new PaymentMethodDictionary();
|
||||
paymentMethods.Add(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "BTC",
|
||||
Rate = 1000,
|
||||
NextNetworkFee = Money.Coins(0.1m)
|
||||
});
|
||||
paymentMethods.Add(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "LTC",
|
||||
Rate = 500,
|
||||
NextNetworkFee = Money.Coins(0.01m)
|
||||
});
|
||||
paymentMethods.Add(
|
||||
new PaymentMethod() {CryptoCode = "BTC", Rate = 1000, NextNetworkFee = Money.Coins(0.1m)});
|
||||
paymentMethods.Add(
|
||||
new PaymentMethod() {CryptoCode = "LTC", Rate = 500, NextNetworkFee = Money.Coins(0.01m)});
|
||||
entity.SetPaymentMethods(paymentMethods);
|
||||
entity.Payments = new List<PaymentEntity>();
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
@@ -254,9 +293,17 @@ namespace BTCPayServer.Tests
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
|
||||
accounting = paymentMethod.Calculate();
|
||||
|
||||
Assert.Equal(Money.Coins(10.01m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
CryptoCode = "BTC",
|
||||
Output = new TxOut(Money.Coins(1.0m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
});
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
accounting = paymentMethod.Calculate();
|
||||
@@ -273,8 +320,14 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(Money.Coins(2.0m), accounting.Paid);
|
||||
Assert.Equal(Money.Coins(10.01m + 0.1m * 2), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "LTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true, NetworkFee = 0.01m });
|
||||
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
CryptoCode = "LTC",
|
||||
Output = new TxOut(Money.Coins(1.0m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.01m,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
});
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
accounting = paymentMethod.Calculate();
|
||||
@@ -293,7 +346,14 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(2, accounting.TxRequired);
|
||||
|
||||
var remaining = Money.Coins(4.2m - 0.5m + 0.01m / 2);
|
||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(remaining, new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
CryptoCode = "BTC",
|
||||
Output = new TxOut(remaining, new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m,
|
||||
PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary
|
||||
});
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
accounting = paymentMethod.Calculate();
|
||||
@@ -310,7 +370,8 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||
Assert.Equal(Money.Coins(3.0m) + remaining * 2, accounting.Paid);
|
||||
// Paying 2 BTC fee, LTC fee removed because fully paid
|
||||
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 + 0.1m * 2 /* + 0.01m no need to pay this fee anymore */), accounting.TotalDue);
|
||||
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 + 0.1m * 2 /* + 0.01m no need to pay this fee anymore */),
|
||||
accounting.TotalDue);
|
||||
Assert.Equal(1, accounting.TxRequired);
|
||||
Assert.Equal(accounting.Paid, accounting.TotalDue);
|
||||
#pragma warning restore CS0618
|
||||
@@ -353,14 +414,23 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanAcceptInvoiceWithTolerance()
|
||||
{
|
||||
var entity = new InvoiceEntity();
|
||||
var networkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
|
||||
{
|
||||
new BitcoinLikePaymentHandler(null, networkProvider, null, null),
|
||||
new LightningLikePaymentHandler(null, null, networkProvider, null),
|
||||
});
|
||||
var entity = new InvoiceEntity() {PaymentMethodHandlerDictionary = paymentMethodHandlerDictionary};
|
||||
#pragma warning disable CS0618
|
||||
entity.Payments = new List<PaymentEntity>();
|
||||
entity.SetPaymentMethod(new PaymentMethod() { CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m) });
|
||||
entity.ProductInformation = new ProductInformation() { Price = 5000 };
|
||||
entity.SetPaymentMethod(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m)
|
||||
});
|
||||
entity.ProductInformation = new ProductInformation() {Price = 5000};
|
||||
entity.PaymentTolerance = 0;
|
||||
|
||||
|
||||
@@ -370,13 +440,13 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.TotalDue);
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.MinimumTotalDue);
|
||||
|
||||
entity.PaymentTolerance = 10;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(0.99m), accounting.MinimumTotalDue);
|
||||
entity.PaymentTolerance = 10;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(0.99m), accounting.MinimumTotalDue);
|
||||
|
||||
entity.PaymentTolerance = 100;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Satoshis(1), accounting.MinimumTotalDue);
|
||||
entity.PaymentTolerance = 100;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Satoshis(1), accounting.MinimumTotalDue);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,7 @@ using BTCPayServer.Models;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBitpayClient;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@@ -19,15 +17,12 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
private InvoiceController _InvoiceController;
|
||||
private InvoiceRepository _InvoiceRepository;
|
||||
private BTCPayNetworkProvider _NetworkProvider;
|
||||
|
||||
public InvoiceControllerAPI(InvoiceController invoiceController,
|
||||
InvoiceRepository invoceRepository,
|
||||
BTCPayNetworkProvider networkProvider)
|
||||
InvoiceRepository invoceRepository)
|
||||
{
|
||||
this._InvoiceController = invoiceController;
|
||||
this._InvoiceRepository = invoceRepository;
|
||||
this._NetworkProvider = networkProvider;
|
||||
_InvoiceController = invoiceController;
|
||||
_InvoiceRepository = invoceRepository;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@@ -51,8 +46,7 @@ namespace BTCPayServer.Controllers
|
||||
})).FirstOrDefault();
|
||||
if (invoice == null)
|
||||
throw new BitpayHttpException(404, "Object not found");
|
||||
var resp = invoice.EntityToDTO();
|
||||
return new DataWrapper<InvoiceResponse>(resp);
|
||||
return new DataWrapper<InvoiceResponse>(invoice.EntityToDTO());
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("invoices")]
|
||||
@@ -81,7 +75,7 @@ namespace BTCPayServer.Controllers
|
||||
StoreId = new[] { this.HttpContext.GetStoreData().Id }
|
||||
};
|
||||
|
||||
var entities = (await _InvoiceRepository.GetInvoices(query))
|
||||
var entities = (await _InvoiceRepository.GetInvoices(query))
|
||||
.Select((o) => o.EntityToDTO()).ToArray();
|
||||
|
||||
return DataWrapper.Create(entities);
|
||||
|
||||
@@ -19,10 +19,8 @@ using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Invoices.Export;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using NBXplorer;
|
||||
@@ -77,12 +75,15 @@ namespace BTCPayServer.Controllers
|
||||
StatusMessage = StatusMessage
|
||||
};
|
||||
|
||||
model.Addresses = invoice.HistoricalAddresses.Select(h => new InvoiceDetailsModel.AddressModel
|
||||
{
|
||||
Destination = h.GetAddress(),
|
||||
PaymentMethod = ToString(h.GetPaymentMethodId()),
|
||||
Current = !h.UnAssigned.HasValue
|
||||
}).ToArray();
|
||||
model.Addresses = invoice.HistoricalAddresses.Select(h =>
|
||||
new InvoiceDetailsModel.AddressModel
|
||||
{
|
||||
Destination = h.GetAddress(),
|
||||
PaymentMethod =
|
||||
invoice.PaymentMethodHandlerDictionary[h.GetPaymentMethodId()]
|
||||
.ToPrettyString(h.GetPaymentMethodId()),
|
||||
Current = !h.UnAssigned.HasValue
|
||||
}).ToArray();
|
||||
|
||||
var details = InvoicePopulatePayments(invoice);
|
||||
model.CryptoPayments = details.CryptoPayments;
|
||||
@@ -101,11 +102,12 @@ namespace BTCPayServer.Controllers
|
||||
var accounting = data.Calculate();
|
||||
var paymentMethodId = data.GetId();
|
||||
var cryptoPayment = new InvoiceDetailsModel.CryptoPayment();
|
||||
cryptoPayment.PaymentMethod = ToString(paymentMethodId);
|
||||
cryptoPayment.PaymentMethod = invoice.PaymentMethodHandlerDictionary[paymentMethodId]
|
||||
.ToPrettyString(data.GetId());
|
||||
cryptoPayment.Due = _CurrencyNameTable.DisplayFormatCurrency(accounting.Due.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode);
|
||||
cryptoPayment.Paid = _CurrencyNameTable.DisplayFormatCurrency(accounting.CryptoPaid.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode);
|
||||
cryptoPayment.Overpaid = _CurrencyNameTable.DisplayFormatCurrency(accounting.OverpaidHelper.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode);
|
||||
|
||||
//TODO: abstract
|
||||
var onchainMethod = data.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod;
|
||||
if (onchainMethod != null)
|
||||
{
|
||||
@@ -160,21 +162,6 @@ namespace BTCPayServer.Controllers
|
||||
return model;
|
||||
}
|
||||
|
||||
private string ToString(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var type = paymentMethodId.PaymentType.ToString();
|
||||
switch (paymentMethodId.PaymentType)
|
||||
{
|
||||
case PaymentTypes.BTCLike:
|
||||
type = "On-Chain";
|
||||
break;
|
||||
case PaymentTypes.LightningLike:
|
||||
type = "Off-Chain";
|
||||
break;
|
||||
}
|
||||
return $"{paymentMethodId.CryptoCode} ({type})";
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("i/{invoiceId}")]
|
||||
[Route("i/{invoiceId}/{paymentMethodId}")]
|
||||
@@ -229,7 +216,7 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
|
||||
//TODO: abstract
|
||||
private async Task<PaymentModel> GetInvoiceModel(string invoiceId, PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
|
||||
@@ -245,6 +232,7 @@ namespace BTCPayServer.Controllers
|
||||
BTCPayNetworkBase network = _NetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
if (network == null && isDefaultPaymentId)
|
||||
{
|
||||
//TODO: need to look into a better way for this as it does not scale
|
||||
network = _NetworkProvider.GetAll().OfType<BTCPayNetwork>().FirstOrDefault();
|
||||
paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
|
||||
}
|
||||
@@ -291,10 +279,6 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
CryptoCode = network.CryptoCode,
|
||||
RootPath = this.Request.PathBase.Value.WithTrailingSlash(),
|
||||
PaymentMethodId = paymentMethodId.ToString(),
|
||||
PaymentMethodName = GetDisplayName(paymentMethodId, network),
|
||||
CryptoImage = GetImage(paymentMethodId, network),
|
||||
IsLightning = paymentMethodId.PaymentType == PaymentTypes.LightningLike,
|
||||
OrderId = invoice.OrderId,
|
||||
InvoiceId = invoice.Id,
|
||||
DefaultLang = storeBlob.DefaultLang ?? "en",
|
||||
@@ -316,13 +300,7 @@ namespace BTCPayServer.Controllers
|
||||
MerchantRefLink = invoice.RedirectURL ?? "/",
|
||||
RedirectAutomatically = invoice.RedirectAutomatically,
|
||||
StoreName = store.StoreName,
|
||||
InvoiceBitcoinUrl = paymentMethodId.PaymentType == PaymentTypes.BTCLike ? cryptoInfo.PaymentUrls.BIP21 :
|
||||
paymentMethodId.PaymentType == PaymentTypes.LightningLike ? cryptoInfo.PaymentUrls.BOLT11 :
|
||||
throw new NotSupportedException(),
|
||||
PeerInfo = (paymentMethodDetails as LightningLikePaymentMethodDetails)?.NodeInfo,
|
||||
InvoiceBitcoinUrlQR = paymentMethodId.PaymentType == PaymentTypes.BTCLike ? cryptoInfo.PaymentUrls.BIP21 :
|
||||
paymentMethodId.PaymentType == PaymentTypes.LightningLike ? cryptoInfo.PaymentUrls.BOLT11.ToUpperInvariant() :
|
||||
throw new NotSupportedException(),
|
||||
TxCount = accounting.TxRequired,
|
||||
BtcPaid = accounting.Paid.ToString(),
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
@@ -340,37 +318,38 @@ namespace BTCPayServer.Controllers
|
||||
StoreId = store.Id,
|
||||
AvailableCryptos = invoice.GetPaymentMethods()
|
||||
.Where(i => i.Network != null)
|
||||
.Select(kv => new PaymentModel.AvailableCrypto()
|
||||
.Select(kv =>
|
||||
{
|
||||
PaymentMethodId = kv.GetId().ToString(),
|
||||
CryptoCode = kv.GetId().CryptoCode,
|
||||
PaymentMethodName = GetDisplayName(kv.GetId(), kv.Network),
|
||||
IsLightning = kv.GetId().PaymentType == PaymentTypes.LightningLike,
|
||||
CryptoImage = GetImage(kv.GetId(), kv.Network),
|
||||
Link = Url.Action(nameof(Checkout), new { invoiceId = invoiceId, paymentMethodId = kv.GetId().ToString() })
|
||||
var availableCryptoPaymentMethodId = kv.GetId();
|
||||
var availableCryptoHandler =
|
||||
invoice.PaymentMethodHandlerDictionary[availableCryptoPaymentMethodId];
|
||||
return new PaymentModel.AvailableCrypto()
|
||||
{
|
||||
PaymentMethodId = kv.GetId().ToString(),
|
||||
CryptoCode = kv.GetId().CryptoCode,
|
||||
PaymentMethodName = availableCryptoHandler.GetPaymentMethodName(availableCryptoPaymentMethodId),
|
||||
IsLightning =
|
||||
kv.GetId().PaymentType == PaymentTypes.LightningLike,
|
||||
CryptoImage = Request.GetRelativePathOrAbsolute(availableCryptoHandler.GetCryptoImage(availableCryptoPaymentMethodId)),
|
||||
Link = Url.Action(nameof(Checkout),
|
||||
new
|
||||
{
|
||||
invoiceId = invoiceId,
|
||||
paymentMethodId = kv.GetId().ToString()
|
||||
})
|
||||
};
|
||||
}).Where(c => c.CryptoImage != "/")
|
||||
.OrderByDescending(a => a.CryptoCode == "BTC").ThenBy(a => a.PaymentMethodName).ThenBy(a => a.IsLightning ? 1 : 0)
|
||||
.ToList()
|
||||
};
|
||||
|
||||
invoice.PaymentMethodHandlerDictionary[paymentMethod.GetId()].PreparePaymentModel(model, dto);
|
||||
model.PaymentMethodId = paymentMethodId.ToString();
|
||||
var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);
|
||||
model.TimeLeft = expiration.PrettyPrint();
|
||||
return model;
|
||||
}
|
||||
|
||||
private string GetDisplayName(PaymentMethodId paymentMethodId, BTCPayNetworkBase network)
|
||||
{
|
||||
return paymentMethodId.PaymentType == PaymentTypes.BTCLike ?
|
||||
network.DisplayName : network.DisplayName + " (Lightning)";
|
||||
}
|
||||
|
||||
private string GetImage(PaymentMethodId paymentMethodId, BTCPayNetworkBase network)
|
||||
{
|
||||
//the direct casting ((BTCPayNetwork)network) ) for ln image is only temp..this method is offloaded to payment handlers in other pull requests
|
||||
return paymentMethodId.PaymentType == PaymentTypes.BTCLike ?
|
||||
this.Request.GetRelativePathOrAbsolute(network.CryptoImagePath) : this.Request.GetRelativePathOrAbsolute(((BTCPayNetwork)network).LightningImagePath);
|
||||
}
|
||||
|
||||
private string OrderAmountFromInvoice(string cryptoCode, ProductInformation productInformation)
|
||||
{
|
||||
// if invoice source currency is the same as currently display currency, no need for "order amount from invoice"
|
||||
@@ -548,20 +527,13 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private SelectList GetPaymentMethodsSelectList()
|
||||
{
|
||||
return new SelectList(
|
||||
_NetworkProvider.GetAll()
|
||||
.SelectMany(network => new[]
|
||||
{
|
||||
new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike),
|
||||
new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike)
|
||||
}).Select(id =>
|
||||
{
|
||||
var handler = _paymentMethodHandlers.GetCorrectHandler(id);
|
||||
return new SelectListItem(handler.ToPrettyString(id), id.ToString());
|
||||
}),
|
||||
return new SelectList(_paymentMethodHandlerDictionary.Distinct().SelectMany(handler =>
|
||||
handler.GetSupportedPaymentMethods()
|
||||
.Select(id => new SelectListItem(handler.ToPrettyString(id), id.ToString()))),
|
||||
nameof(SelectListItem.Value),
|
||||
nameof(SelectListItem.Text));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("invoices/create")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
|
||||
@@ -36,8 +36,7 @@ namespace BTCPayServer.Controllers
|
||||
private CurrencyNameTable _CurrencyNameTable;
|
||||
EventAggregator _EventAggregator;
|
||||
BTCPayNetworkProvider _NetworkProvider;
|
||||
private readonly IEnumerable<IPaymentMethodHandler> _paymentMethodHandlers;
|
||||
private readonly BTCPayWalletProvider _WalletProvider;
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
IServiceProvider _ServiceProvider;
|
||||
public InvoiceController(
|
||||
IServiceProvider serviceProvider,
|
||||
@@ -47,10 +46,9 @@ namespace BTCPayServer.Controllers
|
||||
RateFetcher rateProvider,
|
||||
StoreRepository storeRepository,
|
||||
EventAggregator eventAggregator,
|
||||
BTCPayWalletProvider walletProvider,
|
||||
ContentSecurityPolicies csp,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
IEnumerable<IPaymentMethodHandler> paymentMethodHandlers)
|
||||
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
|
||||
{
|
||||
_ServiceProvider = serviceProvider;
|
||||
_CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
|
||||
@@ -60,8 +58,7 @@ namespace BTCPayServer.Controllers
|
||||
_UserManager = userManager;
|
||||
_EventAggregator = eventAggregator;
|
||||
_NetworkProvider = networkProvider;
|
||||
_paymentMethodHandlers = paymentMethodHandlers;
|
||||
_WalletProvider = walletProvider;
|
||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||
_CSP = csp;
|
||||
}
|
||||
|
||||
@@ -72,12 +69,7 @@ namespace BTCPayServer.Controllers
|
||||
throw new UnauthorizedAccessException();
|
||||
InvoiceLogs logs = new InvoiceLogs();
|
||||
logs.Write("Creation of invoice starting");
|
||||
var entity = new InvoiceEntity
|
||||
{
|
||||
Version = InvoiceEntity.Lastest_Version,
|
||||
InvoiceTime = DateTimeOffset.UtcNow,
|
||||
Networks = _NetworkProvider
|
||||
};
|
||||
var entity = _InvoiceRepository.CreateNewInvoice();
|
||||
|
||||
var getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id);
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
@@ -168,7 +160,7 @@ namespace BTCPayServer.Controllers
|
||||
var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.Where(s => !excludeFilter.Match(s.PaymentId))
|
||||
.Select(c =>
|
||||
(Handler: (IPaymentMethodHandler)_ServiceProvider.GetService(typeof(IPaymentMethodHandler<>).MakeGenericType(c.GetType())),
|
||||
(Handler: _paymentMethodHandlerDictionary[c.PaymentId],
|
||||
SupportedPaymentMethod: c,
|
||||
Network: _NetworkProvider.GetNetwork<BTCPayNetworkBase>(c.PaymentId.CryptoCode)))
|
||||
.Where(c => c.Network != null)
|
||||
@@ -272,38 +264,15 @@ namespace BTCPayServer.Controllers
|
||||
paymentMethod.SetPaymentMethodDetails(paymentDetails);
|
||||
}
|
||||
|
||||
Func<Money, Money, bool> compare = null;
|
||||
CurrencyValue limitValue = null;
|
||||
string errorMessage = null;
|
||||
if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.LightningLike &&
|
||||
storeBlob.LightningMaxValue != null)
|
||||
var errorMessage = await
|
||||
handler
|
||||
.IsPaymentMethodAllowedBasedOnInvoiceAmount(storeBlob, fetchingByCurrencyPair,
|
||||
paymentMethod.Calculate().Due, supportedPaymentMethod.PaymentId);
|
||||
if (errorMessage != null)
|
||||
{
|
||||
compare = (a, b) => a > b;
|
||||
limitValue = storeBlob.LightningMaxValue;
|
||||
errorMessage = "The amount of the invoice is too high to be paid with lightning";
|
||||
logs.Write($"{logPrefix} {errorMessage}");
|
||||
return null;
|
||||
}
|
||||
else if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.BTCLike &&
|
||||
storeBlob.OnChainMinValue != null)
|
||||
{
|
||||
compare = (a, b) => a < b;
|
||||
limitValue = storeBlob.OnChainMinValue;
|
||||
errorMessage = "The amount of the invoice is too low to be paid on chain";
|
||||
}
|
||||
|
||||
if (compare != null)
|
||||
{
|
||||
var limitValueRate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, limitValue.Currency)];
|
||||
if (limitValueRate.BidAsk != null)
|
||||
{
|
||||
var limitValueCrypto = Money.Coins(limitValue.Value / limitValueRate.BidAsk.Bid);
|
||||
if (compare(paymentMethod.Calculate().Due, limitValueCrypto))
|
||||
{
|
||||
logs.Write($"{logPrefix} {errorMessage}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////
|
||||
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
@@ -17,6 +17,7 @@ using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
@@ -56,7 +57,8 @@ namespace BTCPayServer.Controllers
|
||||
LanguageService langService,
|
||||
ChangellyClientProvider changellyClientProvider,
|
||||
IOptions<MvcJsonOptions> mvcJsonOptions,
|
||||
IHostingEnvironment env, IHttpClientFactory httpClientFactory)
|
||||
IHostingEnvironment env, IHttpClientFactory httpClientFactory,
|
||||
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
|
||||
{
|
||||
_RateFactory = rateFactory;
|
||||
_Repo = repo;
|
||||
@@ -69,6 +71,7 @@ namespace BTCPayServer.Controllers
|
||||
_WalletProvider = walletProvider;
|
||||
_Env = env;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||
_NetworkProvider = networkProvider;
|
||||
_ExplorerProvider = explorerProvider;
|
||||
_FeeRateProvider = feeRateProvider;
|
||||
@@ -91,6 +94,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly ChangellyClientProvider _changellyClientProvider;
|
||||
IHostingEnvironment _Env;
|
||||
private IHttpClientFactory _httpClientFactory;
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
|
||||
[TempData]
|
||||
public string StatusMessage
|
||||
@@ -362,7 +366,7 @@ namespace BTCPayServer.Controllers
|
||||
void SetCryptoCurrencies(CheckoutExperienceViewModel vm, Data.StoreData storeData)
|
||||
{
|
||||
var choices = storeData.GetEnabledPaymentIds(_NetworkProvider)
|
||||
.Select(o => new CheckoutExperienceViewModel.Format() { Name = GetDisplayName(o), Value = o.ToString(), PaymentId = o }).ToArray();
|
||||
.Select(o => new CheckoutExperienceViewModel.Format() { Name = _paymentMethodHandlerDictionary[o].ToPrettyString(o), Value = o.ToString(), PaymentId = o }).ToArray();
|
||||
|
||||
var defaultPaymentId = storeData.GetDefaultPaymentId(_NetworkProvider);
|
||||
var chosen = choices.FirstOrDefault(c => c.PaymentId == defaultPaymentId);
|
||||
@@ -370,13 +374,6 @@ namespace BTCPayServer.Controllers
|
||||
vm.DefaultPaymentMethod = chosen?.Value;
|
||||
}
|
||||
|
||||
private string GetDisplayName(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var display = _NetworkProvider.GetNetwork<BTCPayNetworkBase>(paymentMethodId.CryptoCode)?.DisplayName ?? paymentMethodId.CryptoCode;
|
||||
return paymentMethodId.PaymentType == PaymentTypes.BTCLike ?
|
||||
display : $"{display} (Lightning)";
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{storeId}/checkout")]
|
||||
public async Task<IActionResult> CheckoutExperience(CheckoutExperienceViewModel model)
|
||||
@@ -472,36 +469,38 @@ namespace BTCPayServer.Controllers
|
||||
.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.ToDictionary(c => c.Network.CryptoCode);
|
||||
foreach (var network in _NetworkProvider.GetAll())
|
||||
{
|
||||
var strategy = derivationByCryptoCode.TryGet(network.CryptoCode);
|
||||
vm.DerivationSchemes.Add(new StoreViewModel.DerivationScheme()
|
||||
{
|
||||
Crypto = network.CryptoCode,
|
||||
Value = strategy?.ToPrettyString() ?? string.Empty,
|
||||
WalletId = new WalletId(store.Id, network.CryptoCode),
|
||||
Enabled = !excludeFilters.Match(new Payments.PaymentMethodId(network.CryptoCode, Payments.PaymentTypes.BTCLike))
|
||||
});
|
||||
}
|
||||
|
||||
var lightningByCryptoCode = store
|
||||
.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<Payments.Lightning.LightningSupportedPaymentMethod>()
|
||||
.ToDictionary(c => c.CryptoCode);
|
||||
.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<LightningSupportedPaymentMethod>()
|
||||
.ToDictionary(c => c.CryptoCode);
|
||||
|
||||
foreach (var network in _NetworkProvider.GetAll())
|
||||
foreach (var paymentMethodId in _paymentMethodHandlerDictionary.Distinct().SelectMany(handler => handler.GetSupportedPaymentMethods()))
|
||||
{
|
||||
var lightning = lightningByCryptoCode.TryGet(network.CryptoCode);
|
||||
var paymentId = new Payments.PaymentMethodId(network.CryptoCode, Payments.PaymentTypes.LightningLike);
|
||||
vm.LightningNodes.Add(new StoreViewModel.LightningNode()
|
||||
switch (paymentMethodId.PaymentType)
|
||||
{
|
||||
CryptoCode = network.CryptoCode,
|
||||
Address = lightning?.GetLightningUrl()?.BaseUri.AbsoluteUri ?? string.Empty,
|
||||
Enabled = !excludeFilters.Match(paymentId)
|
||||
});
|
||||
case PaymentTypes.BTCLike:
|
||||
var strategy = derivationByCryptoCode.TryGet(paymentMethodId.CryptoCode);
|
||||
vm.DerivationSchemes.Add(new StoreViewModel.DerivationScheme()
|
||||
{
|
||||
Crypto = paymentMethodId.CryptoCode,
|
||||
Value = strategy?.ToPrettyString() ?? string.Empty,
|
||||
WalletId = new WalletId(store.Id, paymentMethodId.CryptoCode),
|
||||
Enabled = !excludeFilters.Match(paymentMethodId)
|
||||
});
|
||||
break;
|
||||
case PaymentTypes.LightningLike:
|
||||
var lightning = lightningByCryptoCode.TryGet(paymentMethodId.CryptoCode);
|
||||
vm.LightningNodes.Add(new StoreViewModel.LightningNode()
|
||||
{
|
||||
CryptoCode = paymentMethodId.CryptoCode,
|
||||
Address = lightning?.GetLightningUrl()?.BaseUri.AbsoluteUri ?? string.Empty,
|
||||
Enabled = !excludeFilters.Match(paymentMethodId)
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var changellyEnabled = storeBlob.ChangellySettings != null && storeBlob.ChangellySettings.Enabled;
|
||||
vm.ThirdPartyPaymentMethods.Add(new StoreViewModel.ThirdPartyPaymentMethod()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -39,11 +39,6 @@ namespace BTCPayServer
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static IPaymentMethodHandler GetCorrectHandler(
|
||||
this IEnumerable<IPaymentMethodHandler> paymentMethodHandlers, PaymentMethodId paymentMethodId)
|
||||
{
|
||||
return paymentMethodHandlers.First(handler => handler.CanHandle(paymentMethodId));
|
||||
}
|
||||
public static string PrettyPrint(this TimeSpan expiration)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
@@ -42,7 +42,6 @@ namespace BTCPayServer.HostedServices
|
||||
IBackgroundJobClient _JobClient;
|
||||
EventAggregator _EventAggregator;
|
||||
InvoiceRepository _InvoiceRepository;
|
||||
BTCPayNetworkProvider _NetworkProvider;
|
||||
private readonly EmailSenderFactory _EmailSenderFactory;
|
||||
|
||||
public InvoiceNotificationManager(
|
||||
@@ -51,14 +50,12 @@ namespace BTCPayServer.HostedServices
|
||||
EventAggregator eventAggregator,
|
||||
InvoiceRepository invoiceRepository,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
ILogger<InvoiceNotificationManager> logger,
|
||||
EmailSenderFactory emailSenderFactory)
|
||||
{
|
||||
_Client = httpClientFactory.CreateClient();
|
||||
_JobClient = jobClient;
|
||||
_EventAggregator = eventAggregator;
|
||||
_InvoiceRepository = invoiceRepository;
|
||||
_NetworkProvider = networkProvider;
|
||||
_EmailSenderFactory = emailSenderFactory;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using NBXplorer;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using NBXplorer.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -11,12 +9,8 @@ using BTCPayServer.Logging;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.Collections.Concurrent;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Events;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services;
|
||||
|
||||
namespace BTCPayServer.HostedServices
|
||||
{
|
||||
|
||||
@@ -46,7 +46,7 @@ using BTCPayServer.Services.Apps;
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
using BTCPayServer.Services.U2F;
|
||||
using BundlerMinifier.TagHelpers;
|
||||
|
||||
using System.Collections.Generic;
|
||||
namespace BTCPayServer.Hosting
|
||||
{
|
||||
public static class BTCPayServerServices
|
||||
@@ -71,10 +71,12 @@ namespace BTCPayServer.Hosting
|
||||
{
|
||||
var opts = o.GetRequiredService<BTCPayServerOptions>();
|
||||
var dbContext = o.GetRequiredService<ApplicationDbContextFactory>();
|
||||
var paymentMethodHandlerDictionary = o.GetService<PaymentMethodHandlerDictionary>();
|
||||
var dbpath = Path.Combine(opts.DataDir, "InvoiceDB");
|
||||
if (!Directory.Exists(dbpath))
|
||||
Directory.CreateDirectory(dbpath);
|
||||
return new InvoiceRepository(dbContext, dbpath, o.GetRequiredService<BTCPayNetworkProvider>());
|
||||
return new InvoiceRepository(dbContext, dbpath, o.GetRequiredService<BTCPayNetworkProvider>(),
|
||||
paymentMethodHandlerDictionary);
|
||||
});
|
||||
services.AddSingleton<BTCPayServerEnvironment>();
|
||||
services.TryAddSingleton<TokenRepository>();
|
||||
@@ -179,14 +181,16 @@ namespace BTCPayServer.Hosting
|
||||
|
||||
services.AddSingleton<BitcoinLikePaymentHandler>();
|
||||
services.AddSingleton<IPaymentMethodHandler>(provider => provider.GetService<BitcoinLikePaymentHandler>());
|
||||
services.AddSingleton<IPaymentMethodHandler<DerivationSchemeSettings>>(provider => provider.GetService<BitcoinLikePaymentHandler>());
|
||||
services.AddSingleton<IPaymentMethodHandler<DerivationSchemeSettings, BTCPayNetwork>>(provider => provider.GetService<BitcoinLikePaymentHandler>());
|
||||
services.AddSingleton<IHostedService, NBXplorerListener>();
|
||||
|
||||
services.AddSingleton<LightningLikePaymentHandler>();
|
||||
services.AddSingleton<IPaymentMethodHandler>(provider => provider.GetService<LightningLikePaymentHandler>());
|
||||
services.AddSingleton<IPaymentMethodHandler<LightningSupportedPaymentMethod>>(provider => provider.GetService<LightningLikePaymentHandler>());
|
||||
services.AddSingleton<IPaymentMethodHandler<LightningSupportedPaymentMethod, BTCPayNetwork>>(provider => provider.GetService<LightningLikePaymentHandler>());
|
||||
services.AddSingleton<IHostedService, LightningListener>();
|
||||
|
||||
services.AddSingleton<PaymentMethodHandlerDictionary>();
|
||||
|
||||
services.AddSingleton<ChangellyClientProvider>();
|
||||
|
||||
services.AddSingleton<IHostedService, NBXplorerWaiters>();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Models.PaymentRequestViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
|
||||
@@ -1,29 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.InvoicingModels;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Payments.Bitcoin
|
||||
{
|
||||
public class BitcoinLikePaymentHandler : PaymentMethodHandlerBase<DerivationSchemeSettings>
|
||||
public class BitcoinLikePaymentHandler : PaymentMethodHandlerBase<DerivationSchemeSettings, BTCPayNetwork>
|
||||
{
|
||||
ExplorerClientProvider _ExplorerProvider;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private IFeeProviderFactory _FeeRateProviderFactory;
|
||||
private Services.Wallets.BTCPayWalletProvider _WalletProvider;
|
||||
|
||||
public BitcoinLikePaymentHandler(ExplorerClientProvider provider,
|
||||
IFeeProviderFactory feeRateProviderFactory,
|
||||
Services.Wallets.BTCPayWalletProvider walletProvider)
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
IFeeProviderFactory feeRateProviderFactory,
|
||||
Services.Wallets.BTCPayWalletProvider walletProvider)
|
||||
{
|
||||
if (provider == null)
|
||||
throw new ArgumentNullException(nameof(provider));
|
||||
_ExplorerProvider = provider;
|
||||
this._FeeRateProviderFactory = feeRateProviderFactory;
|
||||
_networkProvider = networkProvider;
|
||||
_FeeRateProviderFactory = feeRateProviderFactory;
|
||||
_WalletProvider = walletProvider;
|
||||
}
|
||||
|
||||
@@ -33,12 +38,106 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
public Task<BitcoinAddress> ReserveAddress;
|
||||
}
|
||||
|
||||
public override object PreparePayment(DerivationSchemeSettings supportedPaymentMethod, StoreData store, BTCPayNetworkBase network)
|
||||
public override void PreparePaymentModel(PaymentModel model, InvoiceResponse invoiceResponse)
|
||||
{
|
||||
var paymentMethodId = new PaymentMethodId(model.CryptoCode, PaymentTypes.BTCLike);
|
||||
|
||||
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode);
|
||||
model.IsLightning = false;
|
||||
model.PaymentMethodName = GetPaymentMethodName(network);
|
||||
model.CryptoImage = GetCryptoImage(network);
|
||||
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls.BIP21;
|
||||
model.InvoiceBitcoinUrlQR = cryptoInfo.PaymentUrls.BIP21;
|
||||
}
|
||||
|
||||
public override string GetCryptoImage(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
return GetCryptoImage(network);
|
||||
}
|
||||
|
||||
private string GetCryptoImage(BTCPayNetworkBase network)
|
||||
{
|
||||
return network.CryptoImagePath;
|
||||
}
|
||||
|
||||
public override string GetPaymentMethodName(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
return GetPaymentMethodName(network);
|
||||
}
|
||||
|
||||
public override async Task<string> IsPaymentMethodAllowedBasedOnInvoiceAmount(StoreBlob storeBlob,
|
||||
Dictionary<CurrencyPair, Task<RateResult>> rate, Money amount, PaymentMethodId paymentMethodId)
|
||||
{
|
||||
if (storeBlob.OnChainMinValue == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var limitValueRate =
|
||||
await rate[new CurrencyPair(paymentMethodId.CryptoCode, storeBlob.OnChainMinValue.Currency)];
|
||||
|
||||
if (limitValueRate.BidAsk != null)
|
||||
{
|
||||
var limitValueCrypto = Money.Coins(storeBlob.OnChainMinValue.Value / limitValueRate.BidAsk.Bid);
|
||||
|
||||
if (amount > limitValueCrypto)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return "The amount of the invoice is too low to be paid on chain";
|
||||
}
|
||||
|
||||
public override IEnumerable<PaymentMethodId> GetSupportedPaymentMethods()
|
||||
{
|
||||
return _networkProvider.GetAll()
|
||||
.Select(network => new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike));
|
||||
}
|
||||
|
||||
public override CryptoPaymentData GetCryptoPaymentData(PaymentEntity paymentEntity)
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
|
||||
BitcoinLikePaymentData paymentData;
|
||||
if (string.IsNullOrEmpty(paymentEntity.CryptoPaymentDataType))
|
||||
{
|
||||
// For invoices created when CryptoPaymentDataType was not existing, we just consider that it is a RBFed payment for safety
|
||||
paymentData = new BitcoinLikePaymentData();
|
||||
paymentData.Outpoint = paymentEntity.Outpoint;
|
||||
paymentData.Output = paymentEntity.Output;
|
||||
paymentData.RBF = true;
|
||||
paymentData.ConfirmationCount = 0;
|
||||
paymentData.Legacy = true;
|
||||
return paymentData;
|
||||
}
|
||||
|
||||
paymentData =
|
||||
JsonConvert.DeserializeObject<BitcoinLikePaymentData>(paymentEntity.CryptoPaymentData);
|
||||
// legacy
|
||||
paymentData.Output = paymentEntity.Output;
|
||||
paymentData.Outpoint = paymentEntity.Outpoint;
|
||||
#pragma warning restore CS0618
|
||||
return paymentData;
|
||||
}
|
||||
|
||||
private string GetPaymentMethodName(BTCPayNetworkBase network)
|
||||
{
|
||||
return network.DisplayName;
|
||||
}
|
||||
|
||||
|
||||
public override object PreparePayment(DerivationSchemeSettings supportedPaymentMethod, StoreData store,
|
||||
BTCPayNetworkBase network)
|
||||
{
|
||||
return new Prepare()
|
||||
{
|
||||
GetFeeRate = _FeeRateProviderFactory.CreateFeeProvider(network).GetFeeRateAsync(),
|
||||
ReserveAddress = _WalletProvider.GetWallet(network).ReserveAddressAsync(supportedPaymentMethod.AccountDerivation)
|
||||
ReserveAddress = _WalletProvider.GetWallet(network)
|
||||
.ReserveAddressAsync(supportedPaymentMethod.AccountDerivation)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,12 +146,13 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
|
||||
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(
|
||||
DerivationSchemeSettings supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store,
|
||||
BTCPayNetworkBase network, object preparePaymentObject)
|
||||
BTCPayNetwork network, object preparePaymentObject)
|
||||
{
|
||||
if (!_ExplorerProvider.IsAvailable(network))
|
||||
throw new PaymentMethodUnavailableException($"Full node not available");
|
||||
var prepare = (Prepare)preparePaymentObject;
|
||||
Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod onchainMethod = new Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod();
|
||||
Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod onchainMethod =
|
||||
new Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod();
|
||||
onchainMethod.NetworkFeeMode = store.GetStoreBlob().NetworkFeeMode;
|
||||
onchainMethod.FeeRate = await prepare.GetFeeRate;
|
||||
switch (onchainMethod.NetworkFeeMode)
|
||||
@@ -68,5 +168,35 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
onchainMethod.DepositAddress = (await prepare.ReserveAddress).ToString();
|
||||
return onchainMethod;
|
||||
}
|
||||
|
||||
public override void PrepareInvoiceDto(InvoiceResponse invoiceResponse, InvoiceEntity invoiceEntity,
|
||||
InvoiceCryptoInfo invoiceCryptoInfo,
|
||||
PaymentMethodAccounting accounting, PaymentMethod info)
|
||||
{
|
||||
var scheme = info.Network.UriScheme;
|
||||
|
||||
var minerInfo = new MinerFeeInfo();
|
||||
minerInfo.TotalFee = accounting.NetworkFee.Satoshi;
|
||||
minerInfo.SatoshiPerBytes = ((BitcoinLikeOnChainPaymentMethod)info.GetPaymentMethodDetails()).FeeRate
|
||||
.GetFee(1).Satoshi;
|
||||
invoiceResponse.MinerFees.TryAdd(invoiceCryptoInfo.CryptoCode, minerInfo);
|
||||
invoiceCryptoInfo.PaymentUrls = new NBitpayClient.InvoicePaymentUrls()
|
||||
{
|
||||
BIP21 = $"{scheme}:{invoiceCryptoInfo.Address}?amount={invoiceCryptoInfo.Due}",
|
||||
};
|
||||
|
||||
#pragma warning disable 618
|
||||
if (info.CryptoCode == "BTC")
|
||||
{
|
||||
invoiceResponse.BTCPrice = invoiceCryptoInfo.Price;
|
||||
invoiceResponse.Rate = invoiceCryptoInfo.Rate;
|
||||
invoiceResponse.ExRates = invoiceCryptoInfo.ExRates;
|
||||
invoiceResponse.BitcoinAddress = invoiceCryptoInfo.Address;
|
||||
invoiceResponse.BTCPaid = invoiceCryptoInfo.Paid;
|
||||
invoiceResponse.BTCDue = invoiceCryptoInfo.Due;
|
||||
invoiceResponse.PaymentUrls = invoiceCryptoInfo.PaymentUrls;
|
||||
}
|
||||
#pragma warning restore 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
public NBXplorerListener(ExplorerClientProvider explorerClients,
|
||||
BTCPayWalletProvider wallets,
|
||||
InvoiceRepository invoiceRepository,
|
||||
EventAggregator aggregator, Microsoft.Extensions.Hosting.IApplicationLifetime lifetime)
|
||||
EventAggregator aggregator,
|
||||
Microsoft.Extensions.Hosting.IApplicationLifetime lifetime)
|
||||
{
|
||||
PollInterval = TimeSpan.FromMinutes(1.0);
|
||||
_Wallets = wallets;
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.InvoicingModels;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using InvoiceResponse = BTCPayServer.Models.InvoiceResponse;
|
||||
|
||||
namespace BTCPayServer.Payments
|
||||
{
|
||||
@@ -21,7 +26,8 @@ namespace BTCPayServer.Payments
|
||||
/// <param name="network"></param>
|
||||
/// <param name="preparePaymentObject"></param>
|
||||
/// <returns></returns>
|
||||
Task<IPaymentMethodDetails> CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetworkBase network, object preparePaymentObject);
|
||||
Task<IPaymentMethodDetails> CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod,
|
||||
PaymentMethod paymentMethod, StoreData store, BTCPayNetworkBase network, object preparePaymentObject);
|
||||
|
||||
/// <summary>
|
||||
/// This method called before the rate have been fetched
|
||||
@@ -32,57 +38,92 @@ namespace BTCPayServer.Payments
|
||||
/// <returns></returns>
|
||||
object PreparePayment(ISupportedPaymentMethod supportedPaymentMethod, StoreData store, BTCPayNetworkBase network);
|
||||
|
||||
bool CanHandle(PaymentMethodId paymentMethodId);
|
||||
void PrepareInvoiceDto(InvoiceResponse invoiceResponse, InvoiceEntity invoiceEntity,
|
||||
InvoiceCryptoInfo invoiceCryptoInfo,
|
||||
PaymentMethodAccounting accounting, PaymentMethod info);
|
||||
|
||||
|
||||
string ToPrettyString(PaymentMethodId paymentMethodId);
|
||||
|
||||
void PreparePaymentModel(PaymentModel model, InvoiceResponse invoiceResponse);
|
||||
string GetCryptoImage(PaymentMethodId paymentMethodId);
|
||||
string GetPaymentMethodName(PaymentMethodId paymentMethodId);
|
||||
|
||||
Task<string> IsPaymentMethodAllowedBasedOnInvoiceAmount(StoreBlob storeBlob,
|
||||
Dictionary<CurrencyPair, Task<RateResult>> rate,
|
||||
Money amount, PaymentMethodId paymentMethodId);
|
||||
|
||||
IEnumerable<PaymentMethodId> GetSupportedPaymentMethods();
|
||||
|
||||
CryptoPaymentData GetCryptoPaymentData(PaymentEntity paymentEntity);
|
||||
}
|
||||
|
||||
public interface IPaymentMethodHandler<T> : IPaymentMethodHandler where T : ISupportedPaymentMethod
|
||||
public interface IPaymentMethodHandler<TSupportedPaymentMethod, TBTCPayNetwork> : IPaymentMethodHandler
|
||||
where TSupportedPaymentMethod : ISupportedPaymentMethod
|
||||
where TBTCPayNetwork : BTCPayNetworkBase
|
||||
{
|
||||
Task<IPaymentMethodDetails> CreatePaymentMethodDetails(T supportedPaymentMethod, PaymentMethod paymentMethod,
|
||||
StoreData store, BTCPayNetworkBase network, object preparePaymentObject);
|
||||
Task<IPaymentMethodDetails> CreatePaymentMethodDetails(TSupportedPaymentMethod supportedPaymentMethod,
|
||||
PaymentMethod paymentMethod, StoreData store, TBTCPayNetwork network, object preparePaymentObject);
|
||||
}
|
||||
|
||||
public abstract class PaymentMethodHandlerBase<T> : IPaymentMethodHandler<T> where T : ISupportedPaymentMethod
|
||||
public abstract class
|
||||
PaymentMethodHandlerBase<TSupportedPaymentMethod, TBTCPayNetwork> : IPaymentMethodHandler<
|
||||
TSupportedPaymentMethod, TBTCPayNetwork>
|
||||
where TSupportedPaymentMethod : ISupportedPaymentMethod
|
||||
where TBTCPayNetwork : BTCPayNetworkBase
|
||||
{
|
||||
public abstract string PrettyDescription { get; }
|
||||
public abstract PaymentTypes PaymentType { get; }
|
||||
public abstract Task<IPaymentMethodDetails> CreatePaymentMethodDetails(T supportedPaymentMethod,
|
||||
PaymentMethod paymentMethod, StoreData store, BTCPayNetworkBase network, object preparePaymentObject);
|
||||
public virtual object PreparePayment(T supportedPaymentMethod, StoreData store, BTCPayNetworkBase network)
|
||||
|
||||
public abstract Task<IPaymentMethodDetails> CreatePaymentMethodDetails(
|
||||
TSupportedPaymentMethod supportedPaymentMethod,
|
||||
PaymentMethod paymentMethod, StoreData store, TBTCPayNetwork network, object preparePaymentObject);
|
||||
|
||||
public abstract void PrepareInvoiceDto(InvoiceResponse invoiceResponse, InvoiceEntity invoiceEntity,
|
||||
InvoiceCryptoInfo invoiceCryptoInfo, PaymentMethodAccounting accounting, PaymentMethod info);
|
||||
|
||||
public abstract void PreparePaymentModel(PaymentModel model, InvoiceResponse invoiceResponse);
|
||||
public abstract string GetCryptoImage(PaymentMethodId paymentMethodId);
|
||||
public abstract string GetPaymentMethodName(PaymentMethodId paymentMethodId);
|
||||
|
||||
public abstract Task<string> IsPaymentMethodAllowedBasedOnInvoiceAmount(StoreBlob storeBlob,
|
||||
Dictionary<CurrencyPair, Task<RateResult>> rate, Money amount, PaymentMethodId paymentMethodId);
|
||||
|
||||
public abstract IEnumerable<PaymentMethodId> GetSupportedPaymentMethods();
|
||||
public abstract CryptoPaymentData GetCryptoPaymentData(PaymentEntity paymentEntity);
|
||||
|
||||
|
||||
public virtual object PreparePayment(TSupportedPaymentMethod supportedPaymentMethod, StoreData store,
|
||||
BTCPayNetworkBase network)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Task<IPaymentMethodDetails> CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod,
|
||||
StoreData store, BTCPayNetworkBase network, object preparePaymentObject)
|
||||
{
|
||||
if (supportedPaymentMethod is TSupportedPaymentMethod method && network is TBTCPayNetwork correctNetwork)
|
||||
{
|
||||
return CreatePaymentMethodDetails(method, paymentMethod, store, correctNetwork, preparePaymentObject);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Invalid supportedPaymentMethod");
|
||||
}
|
||||
|
||||
object IPaymentMethodHandler.PreparePayment(ISupportedPaymentMethod supportedPaymentMethod, StoreData store,
|
||||
BTCPayNetworkBase network)
|
||||
{
|
||||
if (supportedPaymentMethod is T method)
|
||||
if (supportedPaymentMethod is TSupportedPaymentMethod method)
|
||||
{
|
||||
return PreparePayment(method, store, network);
|
||||
}
|
||||
throw new NotSupportedException("Invalid supportedPaymentMethod");
|
||||
}
|
||||
|
||||
public bool CanHandle(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
return paymentMethodId.PaymentType.Equals(PaymentType);
|
||||
throw new NotSupportedException("Invalid supportedPaymentMethod");
|
||||
}
|
||||
|
||||
public string ToPrettyString(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
return $"{paymentMethodId.CryptoCode} ({PrettyDescription})";
|
||||
}
|
||||
|
||||
Task<IPaymentMethodDetails> IPaymentMethodHandler.CreatePaymentMethodDetails(
|
||||
ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store,
|
||||
BTCPayNetworkBase network, object preparePaymentObject)
|
||||
{
|
||||
if (supportedPaymentMethod is T method)
|
||||
{
|
||||
return CreatePaymentMethodDetails(method, paymentMethod, store, network, preparePaymentObject);
|
||||
}
|
||||
throw new NotSupportedException("Invalid supportedPaymentMethod");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ using BTCPayServer.Services.Invoices;
|
||||
namespace BTCPayServer.Payments
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represent a mode of payment supported by a store.
|
||||
/// It is stored at the store level and cloned to the invoice during invoice creation.
|
||||
/// A class for configuration of a type of payment method stored on a store level.
|
||||
/// It is cloned to invoices of the store during invoice creation.
|
||||
/// This object will be serialized in database in json
|
||||
/// </summary>
|
||||
public interface ISupportedPaymentMethod
|
||||
|
||||
@@ -8,27 +8,36 @@ using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.InvoicingModels;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Payments.Lightning
|
||||
{
|
||||
public class LightningLikePaymentHandler : PaymentMethodHandlerBase<LightningSupportedPaymentMethod>
|
||||
public class LightningLikePaymentHandler : PaymentMethodHandlerBase<LightningSupportedPaymentMethod, BTCPayNetwork>
|
||||
{
|
||||
public static int LIGHTNING_TIMEOUT = 5000;
|
||||
|
||||
NBXplorerDashboard _Dashboard;
|
||||
private readonly LightningClientFactoryService _lightningClientFactory;
|
||||
private readonly BTCPayNetworkProvider _networkProvider;
|
||||
private readonly SocketFactory _socketFactory;
|
||||
|
||||
public LightningLikePaymentHandler(
|
||||
NBXplorerDashboard dashboard,
|
||||
LightningClientFactoryService lightningClientFactory,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
SocketFactory socketFactory)
|
||||
{
|
||||
_Dashboard = dashboard;
|
||||
_lightningClientFactory = lightningClientFactory;
|
||||
_networkProvider = networkProvider;
|
||||
_socketFactory = socketFactory;
|
||||
}
|
||||
|
||||
@@ -36,7 +45,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||
public override PaymentTypes PaymentType => PaymentTypes.LightningLike;
|
||||
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(
|
||||
LightningSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store,
|
||||
BTCPayNetworkBase network, object preparePaymentObject)
|
||||
BTCPayNetwork network, object preparePaymentObject)
|
||||
{
|
||||
//direct casting to (BTCPayNetwork) is fixed in other pull requests with better generic interfacing for handlers
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
@@ -131,5 +140,83 @@ namespace BTCPayServer.Payments.Lightning
|
||||
throw new PaymentMethodUnavailableException($"Error while connecting to the lightning node via {nodeInfo.Host}:{nodeInfo.Port} ({ex.Message})");
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<PaymentMethodId> GetSupportedPaymentMethods()
|
||||
{
|
||||
return _networkProvider.GetAll()
|
||||
.Select(network => new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike));
|
||||
}
|
||||
|
||||
|
||||
public override async Task<string> IsPaymentMethodAllowedBasedOnInvoiceAmount(StoreBlob storeBlob,
|
||||
Dictionary<CurrencyPair, Task<RateResult>> rate, Money amount, PaymentMethodId paymentMethodId)
|
||||
{
|
||||
if (storeBlob.OnChainMinValue == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var limitValueRate = await rate[new CurrencyPair(paymentMethodId.CryptoCode, storeBlob.OnChainMinValue.Currency)];
|
||||
|
||||
if (limitValueRate.BidAsk != null)
|
||||
{
|
||||
var limitValueCrypto = Money.Coins(storeBlob.OnChainMinValue.Value / limitValueRate.BidAsk.Bid);
|
||||
|
||||
if (amount < limitValueCrypto)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return "The amount of the invoice is too high to be paid with lightning";
|
||||
}
|
||||
public override CryptoPaymentData GetCryptoPaymentData(PaymentEntity paymentEntity)
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
return JsonConvert.DeserializeObject<LightningLikePaymentData>(paymentEntity.CryptoPaymentData);
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
public override void PrepareInvoiceDto(InvoiceResponse invoiceResponse, InvoiceEntity invoiceEntity,
|
||||
InvoiceCryptoInfo invoiceCryptoInfo, PaymentMethodAccounting accounting, PaymentMethod info)
|
||||
{
|
||||
invoiceCryptoInfo.PaymentUrls = new InvoicePaymentUrls()
|
||||
{
|
||||
BOLT11 = $"lightning:{invoiceCryptoInfo.Address}"
|
||||
};
|
||||
}
|
||||
|
||||
public override void PreparePaymentModel(PaymentModel model, InvoiceResponse invoiceResponse)
|
||||
{
|
||||
var paymentMethodId = new PaymentMethodId(model.CryptoCode, PaymentTypes.LightningLike);
|
||||
|
||||
var cryptoInfo = invoiceResponse.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(model.CryptoCode);
|
||||
model.IsLightning = true;
|
||||
model.PaymentMethodName = GetPaymentMethodName(network);
|
||||
model.CryptoImage = GetCryptoImage(network);
|
||||
model.InvoiceBitcoinUrl = cryptoInfo.PaymentUrls.BOLT11;
|
||||
model.InvoiceBitcoinUrlQR = cryptoInfo.PaymentUrls.BOLT11.ToUpperInvariant();
|
||||
}
|
||||
|
||||
public override string GetCryptoImage(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
return GetCryptoImage(network);
|
||||
}
|
||||
|
||||
private string GetCryptoImage(BTCPayNetworkBase network)
|
||||
{
|
||||
return ((BTCPayNetwork)network).LightningImagePath;
|
||||
}
|
||||
public override string GetPaymentMethodName(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var network = _networkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
return GetPaymentMethodName(network);
|
||||
}
|
||||
|
||||
private string GetPaymentMethodName(BTCPayNetworkBase network)
|
||||
{
|
||||
return $"{network.DisplayName} (Lightning)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace BTCPayServer.Payments
|
||||
{
|
||||
public static ISupportedPaymentMethod Deserialize(PaymentMethodId paymentMethodId, JToken value, BTCPayNetworkBase network)
|
||||
{
|
||||
//Todo: Abstract
|
||||
if (paymentMethodId.PaymentType == PaymentTypes.BTCLike)
|
||||
{
|
||||
var bitcoinSpecificBtcPayNetwork = (BTCPayNetwork)network;
|
||||
@@ -37,6 +38,7 @@ namespace BTCPayServer.Payments
|
||||
|
||||
public static IPaymentMethodDetails DeserializePaymentMethodDetails(PaymentMethodId paymentMethodId, JObject jobj)
|
||||
{
|
||||
//Todo: Abstract
|
||||
if(paymentMethodId.PaymentType == PaymentTypes.BTCLike)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod>(jobj.ToString());
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace BTCPayServer.Payments
|
||||
if (cryptoCode == null)
|
||||
throw new ArgumentNullException(nameof(cryptoCode));
|
||||
PaymentType = paymentType;
|
||||
CryptoCode = cryptoCode;
|
||||
CryptoCode = cryptoCode.ToUpperInvariant();
|
||||
}
|
||||
|
||||
[Obsolete("Should only be used for legacy stuff")]
|
||||
@@ -62,6 +62,7 @@ namespace BTCPayServer.Payments
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
//BTCLike case is special because it is in legacy mode.
|
||||
return PaymentType == PaymentTypes.BTCLike ? CryptoCode : $"{CryptoCode}_{PaymentType}";
|
||||
}
|
||||
|
||||
@@ -74,7 +75,8 @@ namespace BTCPayServer.Payments
|
||||
PaymentTypes type = PaymentTypes.BTCLike;
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
switch (parts[1].ToLowerInvariant())
|
||||
var typePart = parts[1].ToLowerInvariant();
|
||||
switch (typePart)
|
||||
{
|
||||
case "btclike":
|
||||
case "onchain":
|
||||
@@ -85,7 +87,12 @@ namespace BTCPayServer.Payments
|
||||
type = PaymentTypes.LightningLike;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
if (!Enum.TryParse(typePart, true, out type ))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
paymentMethodId = new PaymentMethodId(parts[0], type);
|
||||
|
||||
@@ -68,6 +68,8 @@ namespace BTCPayServer.Services
|
||||
}
|
||||
}
|
||||
|
||||
public HttpContext Context => httpContext.HttpContext;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder txt = new StringBuilder();
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
@@ -77,6 +78,7 @@ namespace BTCPayServer.Services.Invoices.Export
|
||||
PaymentId = pdata.GetPaymentId(),
|
||||
CryptoCode = cryptoCode,
|
||||
ConversionRate = pmethod.Rate,
|
||||
//TODO: Abstract, we should use Payment Type or Pretty Description from handler
|
||||
PaymentType = payment.GetPaymentMethodId().PaymentType == Payments.PaymentTypes.BTCLike ? "OnChain" : "OffChain",
|
||||
Destination = payment.GetCryptoPaymentData().GetDestination(Networks.GetNetwork<BTCPayNetworkBase>(cryptoCode)),
|
||||
Paid = pdata.GetValue().ToString(CultureInfo.InvariantCulture),
|
||||
|
||||
@@ -4,6 +4,7 @@ using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NBitcoin.DataEncoders;
|
||||
@@ -113,6 +114,9 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
public class InvoiceEntity
|
||||
{
|
||||
|
||||
[JsonIgnore]
|
||||
public PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary { get; set; }
|
||||
[JsonIgnore]
|
||||
public BTCPayNetworkProvider Networks { get; set; }
|
||||
public const int InternalTagSupport_Version = 1;
|
||||
@@ -421,7 +425,6 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
cryptoInfo.ExRates = exrates;
|
||||
var paymentId = info.GetId();
|
||||
var scheme = info.Network.UriScheme;
|
||||
cryptoInfo.Url = ServerUrl.WithTrailingSlash() + $"i/{paymentId}/{Id}";
|
||||
|
||||
cryptoInfo.Payments = GetPayments(info.Network).Select(entity =>
|
||||
@@ -440,39 +443,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
if (paymentId.PaymentType == PaymentTypes.BTCLike)
|
||||
{
|
||||
var minerInfo = new MinerFeeInfo();
|
||||
minerInfo.TotalFee = accounting.NetworkFee.Satoshi;
|
||||
minerInfo.SatoshiPerBytes = ((BitcoinLikeOnChainPaymentMethod)info.GetPaymentMethodDetails()).FeeRate.GetFee(1).Satoshi;
|
||||
dto.MinerFees.TryAdd(paymentId.CryptoCode, minerInfo);
|
||||
var cryptoSuffix = cryptoInfo.CryptoCode == "BTC" ? "" : "/" + cryptoInfo.CryptoCode;
|
||||
cryptoInfo.PaymentUrls = new NBitpayClient.InvoicePaymentUrls()
|
||||
{
|
||||
BIP21 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}",
|
||||
};
|
||||
}
|
||||
PaymentMethodHandlerDictionary[paymentId].PrepareInvoiceDto(dto, this, cryptoInfo, accounting, info);
|
||||
|
||||
if (paymentId.PaymentType == PaymentTypes.LightningLike)
|
||||
{
|
||||
cryptoInfo.PaymentUrls = new NBitpayClient.InvoicePaymentUrls()
|
||||
{
|
||||
BOLT11 = $"lightning:{cryptoInfo.Address}"
|
||||
};
|
||||
}
|
||||
#pragma warning disable CS0618
|
||||
if (info.CryptoCode == "BTC" && paymentId.PaymentType == PaymentTypes.BTCLike)
|
||||
{
|
||||
dto.BTCPrice = cryptoInfo.Price;
|
||||
dto.Rate = cryptoInfo.Rate;
|
||||
dto.ExRates = cryptoInfo.ExRates;
|
||||
dto.BitcoinAddress = cryptoInfo.Address;
|
||||
dto.BTCPaid = cryptoInfo.Paid;
|
||||
dto.BTCDue = cryptoInfo.Due;
|
||||
dto.PaymentUrls = cryptoInfo.PaymentUrls;
|
||||
}
|
||||
|
||||
#pragma warning restore CS0618
|
||||
dto.CryptoInfo.Add(cryptoInfo);
|
||||
dto.PaymentCodes.Add(paymentId.ToString(), cryptoInfo.PaymentUrls);
|
||||
dto.PaymentSubtotals.Add(paymentId.ToString(), subtotalPrice.Satoshi);
|
||||
@@ -842,8 +814,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
var totalDueNoNetworkCost = Money.Coins(Extensions.RoundUp(totalDue, precision));
|
||||
bool paidEnough = paid >= Extensions.RoundUp(totalDue, precision);
|
||||
int txRequired = 0;
|
||||
var payments =
|
||||
ParentEntity.GetPayments()
|
||||
|
||||
_ = ParentEntity.GetPayments()
|
||||
.Where(p => p.Accounted && paymentPredicate(p))
|
||||
.OrderBy(p => p.ReceivedTime)
|
||||
.Select(_ =>
|
||||
@@ -854,15 +826,16 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
totalDue += txFee;
|
||||
}
|
||||
|
||||
paidEnough |= Extensions.RoundUp(paid, precision) >= Extensions.RoundUp(totalDue, precision);
|
||||
if (GetId() == _.GetPaymentMethodId())
|
||||
{
|
||||
cryptoPaid += _.GetCryptoPaymentData().GetValue();
|
||||
txRequired++;
|
||||
}
|
||||
|
||||
return _;
|
||||
})
|
||||
.ToArray();
|
||||
}).ToArray();
|
||||
|
||||
var accounting = new PaymentMethodAccounting();
|
||||
accounting.TxCount = txRequired;
|
||||
@@ -895,6 +868,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public class PaymentEntity
|
||||
{
|
||||
[JsonIgnore]
|
||||
public PaymentMethodHandlerDictionary PaymentMethodHandlerDictionary { get; set; }
|
||||
public int Version { get; set; }
|
||||
public DateTimeOffset ReceivedTime
|
||||
{
|
||||
@@ -934,33 +909,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public CryptoPaymentData GetCryptoPaymentData()
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
if (string.IsNullOrEmpty(CryptoPaymentDataType))
|
||||
{
|
||||
// For invoices created when CryptoPaymentDataType was not existing, we just consider that it is a RBFed payment for safety
|
||||
var paymentData = new Payments.Bitcoin.BitcoinLikePaymentData();
|
||||
paymentData.Outpoint = Outpoint;
|
||||
paymentData.Output = Output;
|
||||
paymentData.RBF = true;
|
||||
paymentData.ConfirmationCount = 0;
|
||||
paymentData.Legacy = true;
|
||||
return paymentData;
|
||||
}
|
||||
if (GetPaymentMethodId().PaymentType == PaymentTypes.BTCLike)
|
||||
{
|
||||
var paymentData = JsonConvert.DeserializeObject<Payments.Bitcoin.BitcoinLikePaymentData>(CryptoPaymentData);
|
||||
// legacy
|
||||
paymentData.Output = Output;
|
||||
paymentData.Outpoint = Outpoint;
|
||||
return paymentData;
|
||||
}
|
||||
if (GetPaymentMethodId().PaymentType == PaymentTypes.LightningLike)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<Payments.Lightning.LightningLikePaymentData>(CryptoPaymentData);
|
||||
}
|
||||
|
||||
throw new NotSupportedException(nameof(CryptoPaymentDataType) + " does not support " + CryptoPaymentDataType);
|
||||
#pragma warning restore CS0618
|
||||
var paymentMethodId = GetPaymentMethodId();
|
||||
return PaymentMethodHandlerDictionary[paymentMethodId].GetCryptoPaymentData(this);
|
||||
}
|
||||
|
||||
public PaymentEntity SetCryptoPaymentData(CryptoPaymentData cryptoPaymentData)
|
||||
@@ -980,6 +930,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
internal decimal GetValue(PaymentMethodDictionary paymentMethods, PaymentMethodId paymentMethodId, decimal? value = null)
|
||||
{
|
||||
|
||||
value = value ?? this.GetCryptoPaymentData().GetValue();
|
||||
var to = paymentMethodId;
|
||||
var from = this.GetPaymentMethodId();
|
||||
@@ -1007,7 +958,9 @@ namespace BTCPayServer.Services.Invoices
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A record of a payment
|
||||
/// </summary>
|
||||
public interface CryptoPaymentData
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using DBriize;
|
||||
using DBriize;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -38,8 +38,11 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
private ApplicationDbContextFactory _ContextFactory;
|
||||
private readonly BTCPayNetworkProvider _Networks;
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
private CustomThreadPool _IndexerThread;
|
||||
public InvoiceRepository(ApplicationDbContextFactory contextFactory, string dbreezePath, BTCPayNetworkProvider networks)
|
||||
|
||||
public InvoiceRepository(ApplicationDbContextFactory contextFactory, string dbreezePath,
|
||||
BTCPayNetworkProvider networks, PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
|
||||
{
|
||||
int retryCount = 0;
|
||||
retry:
|
||||
@@ -51,6 +54,18 @@ retry:
|
||||
_IndexerThread = new CustomThreadPool(1, "Invoice Indexer");
|
||||
_ContextFactory = contextFactory;
|
||||
_Networks = networks;
|
||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||
}
|
||||
|
||||
public InvoiceEntity CreateNewInvoice()
|
||||
{
|
||||
return new InvoiceEntity()
|
||||
{
|
||||
PaymentMethodHandlerDictionary = _paymentMethodHandlerDictionary,
|
||||
Networks = _Networks,
|
||||
Version = InvoiceEntity.Lastest_Version,
|
||||
InvoiceTime = DateTimeOffset.UtcNow,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<bool> RemovePendingInvoice(string invoiceId)
|
||||
@@ -138,6 +153,7 @@ retry:
|
||||
{
|
||||
List<string> textSearch = new List<string>();
|
||||
invoice = ToObject(ToBytes(invoice));
|
||||
invoice.PaymentMethodHandlerDictionary = _paymentMethodHandlerDictionary;
|
||||
invoice.Networks = _Networks;
|
||||
invoice.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(16));
|
||||
#pragma warning disable CS0618
|
||||
@@ -430,7 +446,7 @@ retry:
|
||||
{
|
||||
var paymentEntity = ToObject<PaymentEntity>(p.Blob, null);
|
||||
paymentEntity.Accounted = p.Accounted;
|
||||
|
||||
paymentEntity.PaymentMethodHandlerDictionary = _paymentMethodHandlerDictionary;
|
||||
// PaymentEntity on version 0 does not have their own fee, because it was assumed that the payment method have fixed fee.
|
||||
// We want to hide this legacy detail in InvoiceRepository, so we fetch the fee from the PaymentMethod and assign it to the PaymentEntity.
|
||||
if (paymentEntity.Version == 0)
|
||||
@@ -635,6 +651,7 @@ retry:
|
||||
if (invoice == null)
|
||||
return null;
|
||||
InvoiceEntity invoiceEntity = ToObject(invoice.Blob);
|
||||
invoiceEntity.PaymentMethodHandlerDictionary = _paymentMethodHandlerDictionary;
|
||||
PaymentMethod paymentMethod = invoiceEntity.GetPaymentMethod(new PaymentMethodId(network.CryptoCode, paymentData.GetPaymentType()), null);
|
||||
IPaymentMethodDetails paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
PaymentEntity entity = new PaymentEntity
|
||||
@@ -645,7 +662,8 @@ retry:
|
||||
#pragma warning restore CS0618
|
||||
ReceivedTime = date.UtcDateTime,
|
||||
Accounted = accounted,
|
||||
NetworkFee = paymentMethodDetails.GetNextNetworkFee()
|
||||
NetworkFee = paymentMethodDetails.GetNextNetworkFee(),
|
||||
PaymentMethodHandlerDictionary = _paymentMethodHandlerDictionary
|
||||
};
|
||||
entity.SetCryptoPaymentData(paymentData);
|
||||
|
||||
@@ -702,6 +720,7 @@ retry:
|
||||
private InvoiceEntity ToObject(byte[] value)
|
||||
{
|
||||
var entity = NBitcoin.JsonConverters.Serializer.ToObject<InvoiceEntity>(ZipUtils.Unzip(value), null);
|
||||
entity.PaymentMethodHandlerDictionary = _paymentMethodHandlerDictionary;
|
||||
entity.Networks = _Networks;
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Payments;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using BTCPayServer.Payments;
|
||||
|
||||
namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
public class PaymentMethodHandlerDictionary : IEnumerable<IPaymentMethodHandler>
|
||||
{
|
||||
private readonly Dictionary<PaymentMethodId, IPaymentMethodHandler> _mappedHandlers =
|
||||
new Dictionary<PaymentMethodId, IPaymentMethodHandler>();
|
||||
|
||||
public PaymentMethodHandlerDictionary(IEnumerable<IPaymentMethodHandler> paymentMethodHandlers)
|
||||
{
|
||||
foreach (var paymentMethodHandler in paymentMethodHandlers)
|
||||
{
|
||||
foreach (var supportedPaymentMethod in paymentMethodHandler.GetSupportedPaymentMethods())
|
||||
{
|
||||
_mappedHandlers.Add(supportedPaymentMethod, paymentMethodHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IPaymentMethodHandler this[PaymentMethodId index] => _mappedHandlers[index];
|
||||
public IEnumerator<IPaymentMethodHandler> GetEnumerator()
|
||||
{
|
||||
return _mappedHandlers.Values.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user