mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 05:54:26 +01:00
Allow LN Address to customize invoice metadata, and various bug fixes on LNUrl (#4855)
* Allow LN Address to customize invoice metadata solves https://github.com/OpenSats/website/issues/8 * Refactor GetLNUrl * Fix lightningAddresssettings.Max being ignored * Fix: The payRequest generated by the callback wasn't the same as the original --------- Co-authored-by: nicolas.dorier <nicolas.dorier@gmail.com>
This commit is contained in:
@@ -35,6 +35,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using LightningAddressData = BTCPayServer.Data.LightningAddressData;
|
||||
using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest;
|
||||
|
||||
@@ -244,17 +245,6 @@ namespace BTCPayServer
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var pmi = new PaymentMethodId(cryptoCode, PaymentTypes.LNURLPay);
|
||||
var lnpmi = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
var methods = store.GetSupportedPaymentMethods(_btcPayNetworkProvider);
|
||||
var lnUrlMethod =
|
||||
methods.FirstOrDefault(method => method.PaymentId == pmi) as LNURLPaySupportedPaymentMethod;
|
||||
var lnMethod = methods.FirstOrDefault(method => method.PaymentId == lnpmi);
|
||||
if (lnUrlMethod is null || lnMethod is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
ViewPointOfSaleViewModel.Item[] items;
|
||||
string currencyCode;
|
||||
PointOfSaleSettings posS = null;
|
||||
@@ -278,6 +268,9 @@ namespace BTCPayServer
|
||||
ViewPointOfSaleViewModel.Item item = null;
|
||||
if (!string.IsNullOrEmpty(itemCode))
|
||||
{
|
||||
var pmi = GetLNUrlPaymentMethodId(cryptoCode, store, out _);
|
||||
if (pmi is null)
|
||||
return NotFound("LNUrl or LN is disabled");
|
||||
var escapedItemId = Extensions.UnescapeBackSlashUriString(itemCode);
|
||||
item = items.FirstOrDefault(item1 =>
|
||||
item1.Id.Equals(itemCode, StringComparison.InvariantCultureIgnoreCase) ||
|
||||
@@ -295,9 +288,39 @@ namespace BTCPayServer
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return await GetLNURL(cryptoCode, app.StoreDataId, currencyCode, null, null,
|
||||
() => (null, app, item, new List<string> { AppService.GetAppInternalTag(appId) }, item?.Price.Value, true));
|
||||
|
||||
var createInvoice = new CreateInvoiceRequest()
|
||||
{
|
||||
Amount = item?.Price.Value,
|
||||
Currency = currencyCode,
|
||||
Checkout = new InvoiceDataBase.CheckoutOptions()
|
||||
{
|
||||
RedirectURL = app.AppType switch
|
||||
{
|
||||
PointOfSaleAppType.AppType => app.GetSettings<PointOfSaleSettings>().RedirectUrl ??
|
||||
HttpContext.Request.GetAbsoluteUri($"/apps/{app.Id}/pos"),
|
||||
_ => null
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var invoiceMetadata = new InvoiceMetadata();
|
||||
invoiceMetadata.OrderId =AppService.GetAppOrderId(app);
|
||||
if (item != null)
|
||||
{
|
||||
invoiceMetadata.ItemCode = item.Id;
|
||||
invoiceMetadata.ItemDesc = item.Description;
|
||||
}
|
||||
createInvoice.Metadata = invoiceMetadata.ToJObject();
|
||||
|
||||
|
||||
return await GetLNURLRequest(
|
||||
cryptoCode,
|
||||
store,
|
||||
store.GetStoreBlob(),
|
||||
createInvoice,
|
||||
additionalTags: new List<string> { AppService.GetAppInternalTag(appId) },
|
||||
allowOverpay: false);
|
||||
}
|
||||
|
||||
public class EditLightningAddressVM
|
||||
@@ -327,6 +350,9 @@ namespace BTCPayServer
|
||||
[Display(Name = "Max sats")]
|
||||
[Range(1, double.PositiveInfinity)]
|
||||
public decimal? Max { get; set; }
|
||||
|
||||
[Display(Name = "Invoice metadata")]
|
||||
public string InvoiceMetadata { get; set; }
|
||||
}
|
||||
|
||||
public ConcurrentDictionary<string, LightningAddressItem> Items { get; } = new ();
|
||||
@@ -344,111 +370,103 @@ namespace BTCPayServer
|
||||
public async Task<IActionResult> ResolveLightningAddress(string username)
|
||||
{
|
||||
var lightningAddressSettings = await _lightningAddressService.ResolveByAddress(username);
|
||||
if (lightningAddressSettings is null)
|
||||
{
|
||||
if (lightningAddressSettings is null || username is null)
|
||||
return NotFound("Unknown username");
|
||||
|
||||
var store = await _storeRepository.FindStore(lightningAddressSettings.StoreDataId);
|
||||
if (store is null)
|
||||
return NotFound("Unknown username");
|
||||
}
|
||||
|
||||
var blob = lightningAddressSettings.GetBlob();
|
||||
return await GetLNURL("BTC", lightningAddressSettings.StoreDataId, blob.CurrencyCode, blob.Min, blob.Max,
|
||||
() => (username, null, null, null, null, true));
|
||||
|
||||
return await GetLNURLRequest(
|
||||
"BTC",
|
||||
store,
|
||||
store.GetStoreBlob(),
|
||||
new CreateInvoiceRequest()
|
||||
{
|
||||
Currency = blob?.CurrencyCode,
|
||||
Metadata = blob?.InvoiceMetadata
|
||||
},
|
||||
new LNURLPayRequest()
|
||||
{
|
||||
MinSendable = blob?.Min is decimal min ? new LightMoney(min, LightMoneyUnit.Satoshi) : null,
|
||||
MaxSendable = blob?.Max is decimal max ? new LightMoney(max, LightMoneyUnit.Satoshi) : null,
|
||||
},
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "text/identifier", $"{username}@{Request.Host}" }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("pay")]
|
||||
public async Task<IActionResult> GetLNURL(string cryptoCode, string storeId, string currencyCode = null,
|
||||
decimal? min = null, decimal? max = null,
|
||||
Func<(string username, AppData app, ViewPointOfSaleViewModel.Item item, List<string> additionalTags, decimal? invoiceAmount, bool? anyoneCanInvoice)>
|
||||
internalDetails = null)
|
||||
[EnableCors(CorsPolicies.All)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
public async Task<IActionResult> GetLNUrlForStore(
|
||||
string cryptoCode,
|
||||
string storeId,
|
||||
string currencyCode = null)
|
||||
{
|
||||
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
if (network is null || !network.SupportLightning)
|
||||
{
|
||||
return NotFound("This network does not support Lightning");
|
||||
}
|
||||
|
||||
var store = await _storeRepository.FindStore(storeId);
|
||||
var store = this.HttpContext.GetStoreData();
|
||||
if (store is null)
|
||||
{
|
||||
return NotFound("Store not found");
|
||||
}
|
||||
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
currencyCode ??= storeBlob.DefaultCurrency ?? cryptoCode;
|
||||
var pmi = new PaymentMethodId(cryptoCode, PaymentTypes.LNURLPay);
|
||||
var lnpmi = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
var methods = store.GetSupportedPaymentMethods(_btcPayNetworkProvider);
|
||||
var lnUrlMethod =
|
||||
methods.FirstOrDefault(method => method.PaymentId == pmi) as LNURLPaySupportedPaymentMethod;
|
||||
var lnMethod = methods.FirstOrDefault(method => method.PaymentId == lnpmi);
|
||||
if (lnUrlMethod is null || lnMethod is null)
|
||||
{
|
||||
return NotFound("LNURL or Lightning payment method not found");
|
||||
}
|
||||
return NotFound();
|
||||
|
||||
var blob = store.GetStoreBlob();
|
||||
if (blob.GetExcludedPaymentMethods().Match(pmi) || blob.GetExcludedPaymentMethods().Match(lnpmi))
|
||||
{
|
||||
return NotFound("LNURL or Lightning payment method disabled");
|
||||
}
|
||||
|
||||
(string username, AppData app, ViewPointOfSaleViewModel.Item item, List<string> additionalTags, decimal? invoiceAmount, bool? anyoneCanInvoice) =
|
||||
(internalDetails ?? (() => (null, null, null, null, null, null)))();
|
||||
|
||||
if ((anyoneCanInvoice ?? blob.AnyoneCanInvoice) is false)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var lnAddress = username is null ? null : $"{username}@{Request.Host}";
|
||||
List<string[]> lnurlMetadata = new();
|
||||
|
||||
var redirectUrl = app?.AppType switch
|
||||
{
|
||||
PointOfSaleAppType.AppType => app.GetSettings<PointOfSaleSettings>().RedirectUrl ??
|
||||
HttpContext.Request.GetAbsoluteUri($"/apps/{app.Id}/pos"),
|
||||
_ => null
|
||||
};
|
||||
var invoiceRequest = new CreateInvoiceRequest
|
||||
{
|
||||
Amount = invoiceAmount,
|
||||
Checkout = new InvoiceDataBase.CheckoutOptions
|
||||
if (!blob.AnyoneCanInvoice)
|
||||
return NotFound("'Anyone can invoice' is turned off");
|
||||
return await GetLNURLRequest(
|
||||
cryptoCode,
|
||||
store,
|
||||
blob,
|
||||
new CreateInvoiceRequest
|
||||
{
|
||||
PaymentMethods = new[] { pmi.ToStringNormalized() },
|
||||
Expiration = blob.InvoiceExpiration < TimeSpan.FromMinutes(2)
|
||||
? blob.InvoiceExpiration
|
||||
: TimeSpan.FromMinutes(2),
|
||||
RedirectURL = redirectUrl
|
||||
},
|
||||
Currency = currencyCode,
|
||||
Type = invoiceAmount is null ? InvoiceType.TopUp : InvoiceType.Standard,
|
||||
};
|
||||
Currency = currencyCode
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<IActionResult> GetLNURLRequest(
|
||||
string cryptoCode,
|
||||
Data.StoreData store,
|
||||
Data.StoreBlob blob,
|
||||
CreateInvoiceRequest createInvoice,
|
||||
LNURLPayRequest lnurlRequest = null,
|
||||
Dictionary<string, string> lnUrlMetadata = null,
|
||||
List<string> additionalTags = null,
|
||||
bool allowOverpay = true)
|
||||
{
|
||||
if (GetLNUrlPaymentMethodId(cryptoCode, store, out _) is null)
|
||||
return NotFound("LNUrl or LN is disabled");
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
invoiceRequest.Metadata =
|
||||
new InvoiceMetadata
|
||||
{
|
||||
ItemCode = item.Id,
|
||||
ItemDesc = item.Description,
|
||||
OrderId = AppService.GetAppOrderId(app)
|
||||
}.ToJObject();
|
||||
}
|
||||
InvoiceEntity i;
|
||||
try
|
||||
{
|
||||
i = await _invoiceController.CreateInvoiceCoreRaw(invoiceRequest, store, Request.GetAbsoluteRoot(), additionalTags);
|
||||
i = await _invoiceController.CreateInvoiceCoreRaw(createInvoice, store, Request.GetAbsoluteRoot(), additionalTags);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return this.CreateAPIError(null, e.Message);
|
||||
}
|
||||
if (i.Type != InvoiceType.TopUp)
|
||||
{
|
||||
min = i.GetPaymentMethod(pmi).Calculate().Due.ToDecimal(MoneyUnit.Satoshi);
|
||||
max = item?.Price?.Type == ViewPointOfSaleViewModel.Item.ItemPrice.ItemPriceType.Minimum ? null : min;
|
||||
}
|
||||
lnurlRequest = await CreateLNUrlRequestFromInvoice(cryptoCode, i, store, blob, lnurlRequest, lnUrlMetadata, allowOverpay);
|
||||
return lnurlRequest is null ? NotFound() : Ok(lnurlRequest);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(username))
|
||||
private async Task<LNURLPayRequest> CreateLNUrlRequestFromInvoice(
|
||||
string cryptoCode,
|
||||
InvoiceEntity i,
|
||||
Data.StoreData store,
|
||||
StoreBlob blob,
|
||||
LNURLPayRequest lnurlRequest = null,
|
||||
Dictionary<string, string> lnUrlMetadata = null,
|
||||
bool allowOverpay = true)
|
||||
{
|
||||
var pmi = GetLNUrlPaymentMethodId(cryptoCode, store, out var lnUrlMethod);
|
||||
if (pmi is null)
|
||||
return null;
|
||||
lnurlRequest ??= new LNURLPayRequest();
|
||||
lnUrlMetadata ??= new Dictionary<string, string>();
|
||||
|
||||
if (lnUrlMetadata?.TryGetValue("text/identifier", out var lnAddress) is true && lnAddress is string)
|
||||
{
|
||||
var pm = i.GetPaymentMethod(pmi);
|
||||
var paymentMethodDetails = (LNURLPayPaymentMethodDetails)pm.GetPaymentMethodDetails();
|
||||
@@ -457,36 +475,68 @@ namespace BTCPayServer
|
||||
await _invoiceRepository.UpdateInvoicePaymentMethod(i.Id, pm);
|
||||
}
|
||||
|
||||
var invoiceDescription = blob.LightningDescriptionTemplate
|
||||
.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{ItemDescription}", i.Metadata.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{OrderId}", i.Metadata.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
lnurlMetadata.Add(new[] { "text/plain", invoiceDescription });
|
||||
if (!string.IsNullOrEmpty(username))
|
||||
if (!lnUrlMetadata.ContainsKey("text/plain"))
|
||||
{
|
||||
lnurlMetadata.Add(new[] { "text/identifier", lnAddress });
|
||||
var invoiceDescription = blob.LightningDescriptionTemplate
|
||||
.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{ItemDescription}", i.Metadata.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{OrderId}", i.Metadata.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
lnUrlMetadata.Add("text/plain", invoiceDescription);
|
||||
}
|
||||
|
||||
if (await _pluginHookService.ApplyFilter("modify-lnurlp-request", new LNURLPayRequest
|
||||
{
|
||||
Tag = "payRequest",
|
||||
MinSendable = new LightMoney(min ?? 1m, LightMoneyUnit.Satoshi),
|
||||
MaxSendable =
|
||||
max is null
|
||||
? LightMoney.FromUnit(6.12m, LightMoneyUnit.BTC)
|
||||
: new LightMoney(max.Value, LightMoneyUnit.Satoshi),
|
||||
CommentAllowed = lnUrlMethod.LUD12Enabled ? 2000 : 0,
|
||||
Metadata = JsonConvert.SerializeObject(lnurlMetadata),
|
||||
Callback = new Uri(_linkGenerator.GetUriByAction(
|
||||
lnurlRequest.Tag = "payRequest";
|
||||
lnurlRequest.CommentAllowed = lnUrlMethod.LUD12Enabled ? 2000 : 0;
|
||||
lnurlRequest.Callback = new Uri(_linkGenerator.GetUriByAction(
|
||||
action: nameof(GetLNURLForInvoice),
|
||||
controller: "UILNURL",
|
||||
values: new {cryptoCode, invoiceId = i.Id}, Request.Scheme, Request.Host, Request.PathBase))
|
||||
}) is not LNURLPayRequest lnurlp)
|
||||
values: new { pmi.CryptoCode, invoiceId = i.Id }, Request.Scheme, Request.Host, Request.PathBase));
|
||||
lnurlRequest.Metadata = JsonConvert.SerializeObject(lnUrlMetadata.Select(kv => new[] { kv.Key, kv.Value }));
|
||||
if (i.Type != InvoiceType.TopUp)
|
||||
{
|
||||
return NotFound();
|
||||
lnurlRequest.MinSendable = new LightMoney(i.GetPaymentMethod(pmi).Calculate().Due.ToDecimal(MoneyUnit.Satoshi), LightMoneyUnit.Satoshi);
|
||||
if (!allowOverpay)
|
||||
lnurlRequest.MaxSendable = lnurlRequest.MinSendable;
|
||||
}
|
||||
return Ok(lnurlp);
|
||||
|
||||
// We don't think BTCPay handle well 0 sats payments, just in case make it minimum one sat.
|
||||
if (lnurlRequest.MinSendable is null || lnurlRequest.MinSendable < LightMoney.Satoshis(1.0m))
|
||||
lnurlRequest.MinSendable = LightMoney.Satoshis(1.0m);
|
||||
|
||||
if (lnurlRequest.MaxSendable is null)
|
||||
lnurlRequest.MaxSendable = LightMoney.FromUnit(6.12m, LightMoneyUnit.BTC);
|
||||
|
||||
lnurlRequest = await _pluginHookService.ApplyFilter("modify-lnurlp-request", lnurlRequest) as LNURLPayRequest;
|
||||
|
||||
i.Metadata ??= new InvoiceMetadata();
|
||||
var metadata = i.Metadata.ToJObject();
|
||||
if (metadata.Property("payRequest") is null)
|
||||
{
|
||||
metadata.Add("payRequest", JToken.FromObject(lnurlRequest));
|
||||
await _invoiceRepository.UpdateInvoiceMetadata(i.Id, i.StoreId, metadata);
|
||||
}
|
||||
|
||||
return lnurlRequest;
|
||||
}
|
||||
|
||||
PaymentMethodId GetLNUrlPaymentMethodId(string cryptoCode, Data.StoreData store, out LNURLPaySupportedPaymentMethod lnUrlSettings)
|
||||
{
|
||||
lnUrlSettings = null;
|
||||
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
if (network is null || !network.SupportLightning)
|
||||
return null;
|
||||
var pmi = new PaymentMethodId(cryptoCode, PaymentTypes.LNURLPay);
|
||||
var lnpmi = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
var methods = store.GetSupportedPaymentMethods(_btcPayNetworkProvider);
|
||||
var lnUrlMethod =
|
||||
methods.FirstOrDefault(method => method.PaymentId == pmi) as LNURLPaySupportedPaymentMethod;
|
||||
var lnMethod = methods.FirstOrDefault(method => method.PaymentId == lnpmi);
|
||||
if (lnUrlMethod is null || lnMethod is null)
|
||||
return null;
|
||||
var blob = store.GetStoreBlob();
|
||||
if (blob.GetExcludedPaymentMethods().Match(pmi) || blob.GetExcludedPaymentMethods().Match(lnpmi))
|
||||
return null;
|
||||
lnUrlSettings = lnUrlMethod;
|
||||
return pmi;
|
||||
}
|
||||
|
||||
[HttpGet("pay/i/{invoiceId}")]
|
||||
@@ -501,61 +551,46 @@ namespace BTCPayServer
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (comment is not null)
|
||||
comment = comment.Truncate(2000);
|
||||
|
||||
var pmi = new PaymentMethodId(cryptoCode, PaymentTypes.LNURLPay);
|
||||
var i = await _invoiceRepository.GetInvoice(invoiceId, true);
|
||||
if (i is null)
|
||||
return NotFound();
|
||||
|
||||
var store = await _storeRepository.FindStore(i.StoreId);
|
||||
if (store is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (i.Status == InvoiceStatusLegacy.New)
|
||||
{
|
||||
var isTopup = i.IsUnsetTopUp();
|
||||
var lnurlSupportedPaymentMethod =
|
||||
i.GetSupportedPaymentMethod<LNURLPaySupportedPaymentMethod>(pmi).FirstOrDefault();
|
||||
if (lnurlSupportedPaymentMethod is null)
|
||||
{
|
||||
var pmi = GetLNUrlPaymentMethodId(cryptoCode, store, out var lnurlSupportedPaymentMethod);
|
||||
if (pmi is null)
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var lightningPaymentMethod = i.GetPaymentMethod(pmi);
|
||||
var accounting = lightningPaymentMethod.Calculate();
|
||||
var paymentMethodDetails =
|
||||
lightningPaymentMethod.GetPaymentMethodDetails() as LNURLPayPaymentMethodDetails;
|
||||
if (paymentMethodDetails.LightningSupportedPaymentMethod is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var amt = amount.HasValue ? new LightMoney(amount.Value) : null;
|
||||
var min = new LightMoney(isTopup ? 1m : accounting.Due.ToUnit(MoneyUnit.Satoshi), LightMoneyUnit.Satoshi);
|
||||
var max = isTopup ? LightMoney.FromUnit(6.12m, LightMoneyUnit.BTC) : min;
|
||||
|
||||
List<string[]> lnurlMetadata = new();
|
||||
|
||||
LNURLPayRequest lnurlPayRequest;
|
||||
var blob = store.GetStoreBlob();
|
||||
var invoiceDescription = blob.LightningDescriptionTemplate
|
||||
.Replace("{StoreName}", store.StoreName ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{ItemDescription}", i.Metadata.ItemDesc ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{OrderId}", i.Metadata.OrderId ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
lnurlMetadata.Add(new[] { "text/plain", invoiceDescription });
|
||||
if (!string.IsNullOrEmpty(paymentMethodDetails.ConsumedLightningAddress))
|
||||
if (i.Metadata.AdditionalData.TryGetValue("payRequest", out var t) && t is JObject jo)
|
||||
{
|
||||
lnurlMetadata.Add(new[] { "text/identifier", paymentMethodDetails.ConsumedLightningAddress });
|
||||
lnurlPayRequest = jo.ToObject<LNURLPayRequest>();
|
||||
}
|
||||
else
|
||||
{
|
||||
lnurlPayRequest = await CreateLNUrlRequestFromInvoice(cryptoCode, i, store, blob, allowOverpay: false);
|
||||
if (lnurlPayRequest is null)
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var metadata = JsonConvert.SerializeObject(lnurlMetadata);
|
||||
if (amt != null && (amt < min || amount > max))
|
||||
{
|
||||
if (amount is null)
|
||||
return Ok(lnurlPayRequest);
|
||||
|
||||
var amt = new LightMoney(amount.Value);
|
||||
if (amt < lnurlPayRequest.MinSendable || amount > lnurlPayRequest.MaxSendable)
|
||||
return BadRequest(new LNUrlStatusResponse { Status = "ERROR", Reason = "Amount is out of bounds." });
|
||||
}
|
||||
|
||||
|
||||
LNURLPayRequest.LNURLPayRequestCallbackResponse.ILNURLPayRequestSuccessAction successAction = null;
|
||||
if ((i.ReceiptOptions?.Enabled ?? blob.ReceiptOptions.Enabled) is true)
|
||||
{
|
||||
@@ -565,7 +600,7 @@ namespace BTCPayServer
|
||||
Tag = "url",
|
||||
Description = "Thank you for your purchase. Here is your receipt",
|
||||
Url = _linkGenerator.GetUriByAction(
|
||||
nameof(UIInvoiceController.InvoiceReceipt),
|
||||
nameof(UIInvoiceController.InvoiceReceipt),
|
||||
"UIInvoice",
|
||||
new { invoiceId },
|
||||
Request.Scheme,
|
||||
@@ -574,22 +609,15 @@ namespace BTCPayServer
|
||||
};
|
||||
}
|
||||
|
||||
if (amt is null)
|
||||
bool updatePaymentMethod = false;
|
||||
if (lnurlSupportedPaymentMethod.LUD12Enabled)
|
||||
{
|
||||
if (await _pluginHookService.ApplyFilter("modify-lnurlp-request", new LNURLPayRequest
|
||||
{
|
||||
Tag = "payRequest",
|
||||
MinSendable = min,
|
||||
MaxSendable = max,
|
||||
CommentAllowed = lnurlSupportedPaymentMethod.LUD12Enabled ? 2000 : 0,
|
||||
Metadata = metadata,
|
||||
Callback = new Uri(Request.GetCurrentUrl())
|
||||
}) is not LNURLPayRequest lnurlp)
|
||||
comment = comment?.Truncate(2000);
|
||||
if (paymentMethodDetails.ProvidedComment != comment)
|
||||
{
|
||||
return NotFound();
|
||||
paymentMethodDetails.ProvidedComment = comment;
|
||||
updatePaymentMethod = true;
|
||||
}
|
||||
|
||||
return Ok(lnurlp);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(paymentMethodDetails.BOLT11) || paymentMethodDetails.GeneratedBoltAmount != amt)
|
||||
@@ -613,11 +641,11 @@ namespace BTCPayServer
|
||||
try
|
||||
{
|
||||
var expiry = i.ExpirationTime.ToUniversalTime() - DateTimeOffset.UtcNow;
|
||||
var metadata = JsonConvert.SerializeObject(lnurlPayRequest.Metadata);
|
||||
var description = (await _pluginHookService.ApplyFilter("modify-lnurlp-description", metadata)) as string;
|
||||
if (description is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var param = new CreateInvoiceParams(amt, description, expiry)
|
||||
{
|
||||
PrivateRouteHints = blob.LightningPrivateRouteHints,
|
||||
@@ -649,42 +677,25 @@ namespace BTCPayServer
|
||||
paymentMethodDetails.Preimage = string.IsNullOrEmpty(invoice.Preimage) ? null : uint256.Parse(invoice.Preimage);
|
||||
paymentMethodDetails.InvoiceId = invoice.Id;
|
||||
paymentMethodDetails.GeneratedBoltAmount = amt;
|
||||
if (lnurlSupportedPaymentMethod.LUD12Enabled)
|
||||
{
|
||||
paymentMethodDetails.ProvidedComment = comment;
|
||||
}
|
||||
|
||||
lightningPaymentMethod.SetPaymentMethodDetails(paymentMethodDetails);
|
||||
await _invoiceRepository.UpdateInvoicePaymentMethod(invoiceId, lightningPaymentMethod);
|
||||
updatePaymentMethod = true;
|
||||
|
||||
_eventAggregator.Publish(new InvoiceNewPaymentDetailsEvent(invoiceId,
|
||||
paymentMethodDetails, pmi));
|
||||
return Ok(new LNURLPayRequest.LNURLPayRequestCallbackResponse
|
||||
{
|
||||
Disposable = true,
|
||||
Routes = Array.Empty<string>(),
|
||||
Pr = paymentMethodDetails.BOLT11,
|
||||
SuccessAction = successAction
|
||||
});
|
||||
}
|
||||
|
||||
if (paymentMethodDetails.GeneratedBoltAmount == amt)
|
||||
if (updatePaymentMethod)
|
||||
{
|
||||
if (lnurlSupportedPaymentMethod.LUD12Enabled && paymentMethodDetails.ProvidedComment != comment)
|
||||
{
|
||||
paymentMethodDetails.ProvidedComment = comment;
|
||||
lightningPaymentMethod.SetPaymentMethodDetails(paymentMethodDetails);
|
||||
await _invoiceRepository.UpdateInvoicePaymentMethod(invoiceId, lightningPaymentMethod);
|
||||
}
|
||||
|
||||
return Ok(new LNURLPayRequest.LNURLPayRequestCallbackResponse
|
||||
{
|
||||
Disposable = true,
|
||||
Routes = Array.Empty<string>(),
|
||||
Pr = paymentMethodDetails.BOLT11,
|
||||
SuccessAction = successAction
|
||||
});
|
||||
lightningPaymentMethod.SetPaymentMethodDetails(paymentMethodDetails);
|
||||
await _invoiceRepository.UpdateInvoicePaymentMethod(invoiceId, lightningPaymentMethod);
|
||||
}
|
||||
|
||||
return Ok(new LNURLPayRequest.LNURLPayRequestCallbackResponse
|
||||
{
|
||||
Disposable = true,
|
||||
Routes = Array.Empty<string>(),
|
||||
Pr = paymentMethodDetails.BOLT11,
|
||||
SuccessAction = successAction
|
||||
});
|
||||
}
|
||||
|
||||
return BadRequest(new LNUrlStatusResponse
|
||||
@@ -725,6 +736,7 @@ namespace BTCPayServer
|
||||
CurrencyCode = blob.CurrencyCode,
|
||||
StoreId = storeId,
|
||||
Username = s.Username,
|
||||
InvoiceMetadata = blob.InvoiceMetadata?.ToString(Formatting.Indented)
|
||||
};
|
||||
}
|
||||
).ToList()
|
||||
@@ -746,6 +758,18 @@ namespace BTCPayServer
|
||||
vm.AddModelError(addressVm => addressVm.Add.CurrencyCode, "Currency is invalid", this);
|
||||
}
|
||||
|
||||
JObject metadata = null;
|
||||
if (!string.IsNullOrEmpty(vm.Add.InvoiceMetadata) )
|
||||
{
|
||||
try
|
||||
{
|
||||
metadata = JObject.Parse(vm.Add.InvoiceMetadata);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
vm.AddModelError(addressVm => addressVm.Add.InvoiceMetadata, "Metadata must be a valid json object", this);
|
||||
}
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(vm);
|
||||
@@ -760,7 +784,8 @@ namespace BTCPayServer
|
||||
{
|
||||
Max = vm.Add.Max,
|
||||
Min = vm.Add.Min,
|
||||
CurrencyCode = vm.Add.CurrencyCode
|
||||
CurrencyCode = vm.Add.CurrencyCode,
|
||||
InvoiceMetadata = metadata
|
||||
})))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
|
||||
Reference in New Issue
Block a user