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