mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Add a way to customize lightning invoice description
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<Version>1.0.1.76</Version>
|
<Version>1.0.1.77</Version>
|
||||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ namespace BTCPayServer.Controllers
|
|||||||
.Where(c => c.Network != null)
|
.Where(c => c.Network != null)
|
||||||
.Select(o =>
|
.Select(o =>
|
||||||
(SupportedPaymentMethod: o.SupportedPaymentMethod,
|
(SupportedPaymentMethod: o.SupportedPaymentMethod,
|
||||||
PaymentMethod: CreatePaymentMethodAsync(o.Handler, o.SupportedPaymentMethod, o.Network, entity, storeBlob)))
|
PaymentMethod: CreatePaymentMethodAsync(o.Handler, o.SupportedPaymentMethod, o.Network, entity, store)))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
List<string> paymentMethodErrors = new List<string>();
|
List<string> paymentMethodErrors = new List<string>();
|
||||||
@@ -183,15 +183,16 @@ namespace BTCPayServer.Controllers
|
|||||||
return new DataWrapper<InvoiceResponse>(resp) { Facade = "pos/invoice" };
|
return new DataWrapper<InvoiceResponse>(resp) { Facade = "pos/invoice" };
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<PaymentMethod> CreatePaymentMethodAsync(IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network, InvoiceEntity entity, StoreBlob storeBlob)
|
private async Task<PaymentMethod> CreatePaymentMethodAsync(IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network, InvoiceEntity entity, StoreData store)
|
||||||
{
|
{
|
||||||
|
var storeBlob = store.GetStoreBlob();
|
||||||
var rate = await storeBlob.ApplyRateRules(network, _RateProviders.GetRateProvider(network, false)).GetRateAsync(entity.ProductInformation.Currency);
|
var rate = await storeBlob.ApplyRateRules(network, _RateProviders.GetRateProvider(network, false)).GetRateAsync(entity.ProductInformation.Currency);
|
||||||
PaymentMethod paymentMethod = new PaymentMethod();
|
PaymentMethod paymentMethod = new PaymentMethod();
|
||||||
paymentMethod.ParentEntity = entity;
|
paymentMethod.ParentEntity = entity;
|
||||||
paymentMethod.Network = network;
|
paymentMethod.Network = network;
|
||||||
paymentMethod.SetId(supportedPaymentMethod.PaymentId);
|
paymentMethod.SetId(supportedPaymentMethod.PaymentId);
|
||||||
paymentMethod.Rate = rate;
|
paymentMethod.Rate = rate;
|
||||||
var paymentDetails = await handler.CreatePaymentMethodDetails(supportedPaymentMethod, paymentMethod, network);
|
var paymentDetails = await handler.CreatePaymentMethodDetails(supportedPaymentMethod, paymentMethod, store, network);
|
||||||
if (storeBlob.NetworkFeeDisabled)
|
if (storeBlob.NetworkFeeDisabled)
|
||||||
paymentDetails.SetNoTxFee();
|
paymentDetails.SetNoTxFee();
|
||||||
paymentMethod.SetPaymentMethodDetails(paymentDetails);
|
paymentMethod.SetPaymentMethodDetails(paymentDetails);
|
||||||
|
|||||||
@@ -282,6 +282,7 @@ namespace BTCPayServer.Controllers
|
|||||||
vm.MonitoringExpiration = storeBlob.MonitoringExpiration;
|
vm.MonitoringExpiration = storeBlob.MonitoringExpiration;
|
||||||
vm.InvoiceExpiration = storeBlob.InvoiceExpiration;
|
vm.InvoiceExpiration = storeBlob.InvoiceExpiration;
|
||||||
vm.RateMultiplier = (double)storeBlob.GetRateMultiplier();
|
vm.RateMultiplier = (double)storeBlob.GetRateMultiplier();
|
||||||
|
vm.LightningDescriptionTemplate = storeBlob.LightningDescriptionTemplate;
|
||||||
vm.PreferredExchange = storeBlob.PreferredExchange.IsCoinAverage() ? "coinaverage" : storeBlob.PreferredExchange;
|
vm.PreferredExchange = storeBlob.PreferredExchange.IsCoinAverage() ? "coinaverage" : storeBlob.PreferredExchange;
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
@@ -356,6 +357,7 @@ namespace BTCPayServer.Controllers
|
|||||||
blob.NetworkFeeDisabled = !model.NetworkFee;
|
blob.NetworkFeeDisabled = !model.NetworkFee;
|
||||||
blob.MonitoringExpiration = model.MonitoringExpiration;
|
blob.MonitoringExpiration = model.MonitoringExpiration;
|
||||||
blob.InvoiceExpiration = model.InvoiceExpiration;
|
blob.InvoiceExpiration = model.InvoiceExpiration;
|
||||||
|
blob.LightningDescriptionTemplate = model.LightningDescriptionTemplate ?? string.Empty;
|
||||||
|
|
||||||
bool newExchange = blob.PreferredExchange != model.PreferredExchange;
|
bool newExchange = blob.PreferredExchange != model.PreferredExchange;
|
||||||
blob.PreferredExchange = model.PreferredExchange;
|
blob.PreferredExchange = model.PreferredExchange;
|
||||||
|
|||||||
@@ -270,6 +270,20 @@ namespace BTCPayServer.Data
|
|||||||
[JsonConverter(typeof(UriJsonConverter))]
|
[JsonConverter(typeof(UriJsonConverter))]
|
||||||
public Uri CustomCSS { get; set; }
|
public Uri CustomCSS { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
string _LightningDescriptionTemplate;
|
||||||
|
public string LightningDescriptionTemplate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _LightningDescriptionTemplate ?? "Paid to {StoreName} (Order ID: {OrderId})";
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_LightningDescriptionTemplate = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IRateProvider ApplyRateRules(BTCPayNetwork network, IRateProvider rateProvider)
|
public IRateProvider ApplyRateRules(BTCPayNetwork network, IRateProvider rateProvider)
|
||||||
{
|
{
|
||||||
if (!PreferredExchange.IsCoinAverage())
|
if (!PreferredExchange.IsCoinAverage())
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Display(Name = "Description template of the lightning invoice")]
|
||||||
|
public string LightningDescriptionTemplate { get; set; }
|
||||||
|
|
||||||
public class LightningNode
|
public class LightningNode
|
||||||
{
|
{
|
||||||
public string CryptoCode { get; set; }
|
public string CryptoCode { get; set; }
|
||||||
@@ -100,6 +103,5 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
} = new List<LightningNode>();
|
} = new List<LightningNode>();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||||||
_WalletProvider = walletProvider;
|
_WalletProvider = walletProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(DerivationStrategy supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network)
|
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(DerivationStrategy supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetwork network)
|
||||||
{
|
{
|
||||||
if (!_ExplorerProvider.IsAvailable(network))
|
if (!_ExplorerProvider.IsAvailable(network))
|
||||||
throw new PaymentMethodUnavailableException($"Full node not available");
|
throw new PaymentMethodUnavailableException($"Full node not available");
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
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.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
|
|
||||||
namespace BTCPayServer.Payments
|
namespace BTCPayServer.Payments
|
||||||
@@ -16,25 +17,26 @@ namespace BTCPayServer.Payments
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="supportedPaymentMethod"></param>
|
/// <param name="supportedPaymentMethod"></param>
|
||||||
/// <param name="paymentMethod"></param>
|
/// <param name="paymentMethod"></param>
|
||||||
|
/// <param name="store"></param>
|
||||||
/// <param name="network"></param>
|
/// <param name="network"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<IPaymentMethodDetails> CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network);
|
Task<IPaymentMethodDetails> CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetwork network);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IPaymentMethodHandler<T> : IPaymentMethodHandler where T : ISupportedPaymentMethod
|
public interface IPaymentMethodHandler<T> : IPaymentMethodHandler where T : ISupportedPaymentMethod
|
||||||
{
|
{
|
||||||
Task<IPaymentMethodDetails> CreatePaymentMethodDetails(T supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network);
|
Task<IPaymentMethodDetails> CreatePaymentMethodDetails(T supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetwork network);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class PaymentMethodHandlerBase<T> : IPaymentMethodHandler<T> where T : ISupportedPaymentMethod
|
public abstract class PaymentMethodHandlerBase<T> : IPaymentMethodHandler<T> where T : ISupportedPaymentMethod
|
||||||
{
|
{
|
||||||
public abstract Task<IPaymentMethodDetails> CreatePaymentMethodDetails(T supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network);
|
public abstract Task<IPaymentMethodDetails> CreatePaymentMethodDetails(T supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetwork network);
|
||||||
|
|
||||||
Task<IPaymentMethodDetails> IPaymentMethodHandler.CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network)
|
Task<IPaymentMethodDetails> IPaymentMethodHandler.CreatePaymentMethodDetails(ISupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetwork network)
|
||||||
{
|
{
|
||||||
if (supportedPaymentMethod is T method)
|
if (supportedPaymentMethod is T method)
|
||||||
{
|
{
|
||||||
return CreatePaymentMethodDetails(method, paymentMethod, network);
|
return CreatePaymentMethodDetails(method, paymentMethod, store, network);
|
||||||
}
|
}
|
||||||
throw new NotSupportedException("Invalid supportedPaymentMethod");
|
throw new NotSupportedException("Invalid supportedPaymentMethod");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,8 +48,10 @@ namespace BTCPayServer.Payments.Lightning.Charge
|
|||||||
{
|
{
|
||||||
var message = CreateMessage(HttpMethod.Post, "invoice");
|
var message = CreateMessage(HttpMethod.Post, "invoice");
|
||||||
Dictionary<string, string> parameters = new Dictionary<string, string>();
|
Dictionary<string, string> parameters = new Dictionary<string, string>();
|
||||||
parameters.Add("msatoshi", request.Amont.MilliSatoshi.ToString(CultureInfo.InvariantCulture));
|
parameters.Add("msatoshi", request.Amount.MilliSatoshi.ToString(CultureInfo.InvariantCulture));
|
||||||
parameters.Add("expiry", ((int)request.Expiry.TotalSeconds).ToString(CultureInfo.InvariantCulture));
|
parameters.Add("expiry", ((int)request.Expiry.TotalSeconds).ToString(CultureInfo.InvariantCulture));
|
||||||
|
if(request.Description != null)
|
||||||
|
parameters.Add("description", request.Description);
|
||||||
message.Content = new FormUrlEncodedContent(parameters);
|
message.Content = new FormUrlEncodedContent(parameters);
|
||||||
var result = await _Client.SendAsync(message, cancellation);
|
var result = await _Client.SendAsync(message, cancellation);
|
||||||
result.EnsureSuccessStatusCode();
|
result.EnsureSuccessStatusCode();
|
||||||
@@ -154,7 +156,7 @@ namespace BTCPayServer.Payments.Lightning.Charge
|
|||||||
|
|
||||||
async Task<LightningInvoice> ILightningInvoiceClient.CreateInvoice(LightMoney amount, string description, TimeSpan expiry, CancellationToken cancellation)
|
async Task<LightningInvoice> ILightningInvoiceClient.CreateInvoice(LightMoney amount, string description, TimeSpan expiry, CancellationToken cancellation)
|
||||||
{
|
{
|
||||||
var invoice = await CreateInvoiceAsync(new CreateInvoiceRequest() { Amont = amount, Expiry = expiry, Description = description ?? "" });
|
var invoice = await CreateInvoiceAsync(new CreateInvoiceRequest() { Amount = amount, Expiry = expiry, Description = description ?? "" });
|
||||||
return new LightningInvoice() { Id = invoice.Id, Amount = amount, BOLT11 = invoice.PayReq, Status = "unpaid" };
|
return new LightningInvoice() { Id = invoice.Id, Amount = amount, BOLT11 = invoice.PayReq, Status = "unpaid" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace BTCPayServer.Payments.Lightning.Charge
|
|||||||
{
|
{
|
||||||
public class CreateInvoiceRequest
|
public class CreateInvoiceRequest
|
||||||
{
|
{
|
||||||
public LightMoney Amont { get; set; }
|
public LightMoney Amount { get; set; }
|
||||||
public TimeSpan Expiry { get; set; }
|
public TimeSpan Expiry { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Net;
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.HostedServices;
|
using BTCPayServer.HostedServices;
|
||||||
using BTCPayServer.Payments.Lightning.Charge;
|
using BTCPayServer.Payments.Lightning.Charge;
|
||||||
using BTCPayServer.Payments.Lightning.CLightning;
|
using BTCPayServer.Payments.Lightning.CLightning;
|
||||||
@@ -23,8 +24,9 @@ namespace BTCPayServer.Payments.Lightning
|
|||||||
_LightningClientFactory = lightningClientFactory;
|
_LightningClientFactory = lightningClientFactory;
|
||||||
_Dashboard = dashboard;
|
_Dashboard = dashboard;
|
||||||
}
|
}
|
||||||
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(LightningSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, BTCPayNetwork network)
|
public override async Task<IPaymentMethodDetails> CreatePaymentMethodDetails(LightningSupportedPaymentMethod supportedPaymentMethod, PaymentMethod paymentMethod, StoreData store, BTCPayNetwork network)
|
||||||
{
|
{
|
||||||
|
var storeBlob = store.GetStoreBlob();
|
||||||
var test = Test(supportedPaymentMethod, network);
|
var test = Test(supportedPaymentMethod, network);
|
||||||
var invoice = paymentMethod.ParentEntity;
|
var invoice = paymentMethod.ParentEntity;
|
||||||
var due = Extensions.RoundUp(invoice.ProductInformation.Price / paymentMethod.Rate, 8);
|
var due = Extensions.RoundUp(invoice.ProductInformation.Price / paymentMethod.Rate, 8);
|
||||||
@@ -36,7 +38,11 @@ namespace BTCPayServer.Payments.Lightning
|
|||||||
LightningInvoice lightningInvoice = null;
|
LightningInvoice lightningInvoice = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
lightningInvoice = await client.CreateInvoice(new LightMoney(due, LightMoneyUnit.BTC), invoice.ProductInformation.ItemDesc, expiry);
|
string description = storeBlob.LightningDescriptionTemplate;
|
||||||
|
description = description.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("{ItemDescription}", invoice.ProductInformation.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
|
||||||
|
.Replace("{OrderId}", invoice.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
|
||||||
|
lightningInvoice = await client.CreateInvoice(new LightMoney(due, LightMoneyUnit.BTC), description, expiry);
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch(Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -129,6 +129,14 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="LightningDescriptionTemplate"></label>
|
||||||
|
<input asp-for="LightningDescriptionTemplate" class="form-control" />
|
||||||
|
<span asp-validation-for="LightningDescriptionTemplate" class="text-danger"></span>
|
||||||
|
<p class="form-text text-muted">
|
||||||
|
Placeholder available are: {StoreName}, {ItemDescription} and {OrderId}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<button name="command" type="submit" class="btn btn-success" value="Save">Save</button>
|
<button name="command" type="submit" class="btn btn-success" value="Save">Save</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user