mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-12 19:02:01 +01:00
Pull Payment LNURLW have a paylink
This commit is contained in:
parent
692a13e0c8
commit
b1c171a5d9
10 changed files with 175 additions and 26 deletions
|
@ -43,7 +43,7 @@ public class LightningAddressDataBlob
|
||||||
public decimal? Max { get; set; }
|
public decimal? Max { get; set; }
|
||||||
|
|
||||||
public JObject InvoiceMetadata { get; set; }
|
public JObject InvoiceMetadata { get; set; }
|
||||||
|
public string PullPaymentId { get; set; }
|
||||||
[JsonExtensionData] public Dictionary<string, JToken> AdditionalData { get; set; }
|
[JsonExtensionData] public Dictionary<string, JToken> AdditionalData { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ using NBitcoin;
|
||||||
using NBitcoin.RPC;
|
using NBitcoin.RPC;
|
||||||
using OpenQA.Selenium;
|
using OpenQA.Selenium;
|
||||||
using OpenQA.Selenium.Chrome;
|
using OpenQA.Selenium.Chrome;
|
||||||
|
using OpenQA.Selenium.Support.Extensions;
|
||||||
using OpenQA.Selenium.Support.UI;
|
using OpenQA.Selenium.Support.UI;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ using BTCPayServer.Client;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
using BTCPayServer.Controllers;
|
using BTCPayServer.Controllers;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.HostedServices;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using BTCPayServer.Models.InvoicingModels;
|
using BTCPayServer.Models.InvoicingModels;
|
||||||
using BTCPayServer.NTag424;
|
using BTCPayServer.NTag424;
|
||||||
|
@ -2118,7 +2119,6 @@ namespace BTCPayServer.Tests
|
||||||
});
|
});
|
||||||
s.GoToHome();
|
s.GoToHome();
|
||||||
//offline/external payout test
|
//offline/external payout test
|
||||||
|
|
||||||
var newStore = s.CreateNewStore();
|
var newStore = s.CreateNewStore();
|
||||||
s.GenerateWallet("BTC", "", true, true);
|
s.GenerateWallet("BTC", "", true, true);
|
||||||
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
|
||||||
|
@ -2322,6 +2322,23 @@ namespace BTCPayServer.Tests
|
||||||
// p and c should work so long as no bolt11 has been submitted
|
// p and c should work so long as no bolt11 has been submitted
|
||||||
info = (LNURLWithdrawRequest)await LNURL.LNURL.FetchInformation(boltcardUrl, s.Server.PayTester.HttpClient);
|
info = (LNURLWithdrawRequest)await LNURL.LNURL.FetchInformation(boltcardUrl, s.Server.PayTester.HttpClient);
|
||||||
info = (LNURLWithdrawRequest)await LNURL.LNURL.FetchInformation(boltcardUrl, s.Server.PayTester.HttpClient);
|
info = (LNURLWithdrawRequest)await LNURL.LNURL.FetchInformation(boltcardUrl, s.Server.PayTester.HttpClient);
|
||||||
|
Assert.NotNull(info.PayLink);
|
||||||
|
Assert.StartsWith("lnurlp://", info.PayLink.AbsoluteUri);
|
||||||
|
// Ignore certs issue
|
||||||
|
info.PayLink = new Uri(info.PayLink.AbsoluteUri.Replace("lnurlp://", "http://"), UriKind.Absolute);
|
||||||
|
var payReq = (LNURLPayRequest)await LNURL.LNURL.FetchInformation(info.PayLink, s.Server.PayTester.HttpClient);
|
||||||
|
var callback = await payReq.SendRequest(LightMoney.Satoshis(100), Network.RegTest, s.Server.PayTester.HttpClient);
|
||||||
|
Assert.NotNull(callback.Pr);
|
||||||
|
var res = await s.Server.CustomerLightningD.Pay(callback.Pr);
|
||||||
|
Assert.Equal(PayResult.Ok, res.Result);
|
||||||
|
var ppService = s.Server.PayTester.GetService<PullPaymentHostedService>();
|
||||||
|
var serializer = s.Server.PayTester.GetService<BTCPayNetworkJsonSerializerSettings>();
|
||||||
|
await TestUtils.EventuallyAsync(async () =>
|
||||||
|
{
|
||||||
|
var pp = await ppService.GetPullPayment(ppid, true);
|
||||||
|
Assert.Contains(pp.Payouts.Select(p => p.GetBlob(serializer)), p => p.CryptoAmount == -LightMoney.Satoshis(100).ToUnit(LightMoneyUnit.BTC));
|
||||||
|
});
|
||||||
|
|
||||||
var fakeBoltcardUrl = new Uri(Regex.Replace(boltcardUrl.AbsoluteUri, "p=([A-F0-9]{32})", $"p={RandomBytes(16)}"));
|
var fakeBoltcardUrl = new Uri(Regex.Replace(boltcardUrl.AbsoluteUri, "p=([A-F0-9]{32})", $"p={RandomBytes(16)}"));
|
||||||
await Assert.ThrowsAsync<LNUrlException>(() => LNURL.LNURL.FetchInformation(fakeBoltcardUrl, s.Server.PayTester.HttpClient));
|
await Assert.ThrowsAsync<LNUrlException>(() => LNURL.LNURL.FetchInformation(fakeBoltcardUrl, s.Server.PayTester.HttpClient));
|
||||||
fakeBoltcardUrl = new Uri(Regex.Replace(boltcardUrl.AbsoluteUri, "c=([A-F0-9]{16})", $"c={RandomBytes(8)}"));
|
fakeBoltcardUrl = new Uri(Regex.Replace(boltcardUrl.AbsoluteUri, "c=([A-F0-9]{16})", $"c={RandomBytes(8)}"));
|
||||||
|
|
|
@ -14,11 +14,20 @@ using System.Threading;
|
||||||
using System;
|
using System;
|
||||||
using NBitcoin.DataEncoders;
|
using NBitcoin.DataEncoders;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using BTCPayServer.HostedServices;
|
||||||
|
using BTCPayServer.Services.Stores;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using BTCPayServer.Client.Models;
|
||||||
|
using BTCPayServer.Lightning;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
|
||||||
namespace BTCPayServer.Controllers;
|
namespace BTCPayServer.Controllers;
|
||||||
|
|
||||||
public class UIBoltcardController : Controller
|
public class UIBoltcardController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly PullPaymentHostedService _ppService;
|
||||||
|
private readonly StoreRepository _storeRepository;
|
||||||
|
|
||||||
public class BoltcardSettings
|
public class BoltcardSettings
|
||||||
{
|
{
|
||||||
[JsonConverter(typeof(NBitcoin.JsonConverters.HexJsonConverter))]
|
[JsonConverter(typeof(NBitcoin.JsonConverters.HexJsonConverter))]
|
||||||
|
@ -28,11 +37,15 @@ public class UIBoltcardController : Controller
|
||||||
UILNURLController lnUrlController,
|
UILNURLController lnUrlController,
|
||||||
SettingsRepository settingsRepository,
|
SettingsRepository settingsRepository,
|
||||||
ApplicationDbContextFactory contextFactory,
|
ApplicationDbContextFactory contextFactory,
|
||||||
|
PullPaymentHostedService ppService,
|
||||||
|
StoreRepository storeRepository,
|
||||||
BTCPayServerEnvironment env)
|
BTCPayServerEnvironment env)
|
||||||
{
|
{
|
||||||
LNURLController = lnUrlController;
|
LNURLController = lnUrlController;
|
||||||
SettingsRepository = settingsRepository;
|
SettingsRepository = settingsRepository;
|
||||||
ContextFactory = contextFactory;
|
ContextFactory = contextFactory;
|
||||||
|
_ppService = ppService;
|
||||||
|
_storeRepository = storeRepository;
|
||||||
Env = env;
|
Env = env;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +54,50 @@ public class UIBoltcardController : Controller
|
||||||
public ApplicationDbContextFactory ContextFactory { get; }
|
public ApplicationDbContextFactory ContextFactory { get; }
|
||||||
public BTCPayServerEnvironment Env { get; }
|
public BTCPayServerEnvironment Env { get; }
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpGet("~/boltcard/pay")]
|
||||||
|
public async Task<IActionResult> GetPayRequest([FromQuery] string? p, [FromQuery] long? amount = null)
|
||||||
|
{
|
||||||
|
var issuerKey = await SettingsRepository.GetIssuerKey(Env);
|
||||||
|
var piccData = issuerKey.TryDecrypt(p);
|
||||||
|
if (piccData is null)
|
||||||
|
return BadRequest(new LNUrlStatusResponse { Status = "ERROR", Reason = "Invalid PICCData" });
|
||||||
|
|
||||||
|
piccData = new BoltcardPICCData(piccData.Uid, int.MaxValue - 10); // do not check the counter
|
||||||
|
var registration = await ContextFactory.GetBoltcardRegistration(issuerKey, piccData, false);
|
||||||
|
var pp = await _ppService.GetPullPayment(registration!.PullPaymentId, false);
|
||||||
|
var store = await _storeRepository.FindStore(pp.StoreId);
|
||||||
|
var payRequest = new LNURLPayRequest
|
||||||
|
{
|
||||||
|
Tag = "payRequest",
|
||||||
|
MinSendable = LightMoney.Satoshis(1.0m),
|
||||||
|
MaxSendable = LightMoney.FromUnit(6.12m, LightMoneyUnit.BTC),
|
||||||
|
Callback = new Uri(GetPayLink(p, Request.Scheme), UriKind.Absolute),
|
||||||
|
CommentAllowed = 0
|
||||||
|
};
|
||||||
|
if (amount is null)
|
||||||
|
return Ok(payRequest);
|
||||||
|
|
||||||
|
var cryptoCode = "BTC";
|
||||||
|
LNURLController.ControllerContext.HttpContext = HttpContext;
|
||||||
|
var result = await LNURLController.GetLNURLRequest(
|
||||||
|
cryptoCode,
|
||||||
|
store,
|
||||||
|
store.GetStoreBlob(),
|
||||||
|
new CreateInvoiceRequest()
|
||||||
|
{
|
||||||
|
Currency = "BTC",
|
||||||
|
Amount = LightMoney.FromUnit(amount.Value, LightMoneyUnit.MilliSatoshi).ToUnit(LightMoneyUnit.BTC)
|
||||||
|
},
|
||||||
|
payRequest,
|
||||||
|
null,
|
||||||
|
[PullPaymentHostedService.GetInternalTag(pp.Id)]);
|
||||||
|
if (result is not OkObjectResult ok || ok.Value is not LNURLPayRequest payRequest2)
|
||||||
|
return result;
|
||||||
|
payRequest = payRequest2;
|
||||||
|
var invoiceId = payRequest.Callback.AbsoluteUri.Split('/').Last();
|
||||||
|
return await LNURLController.GetLNURLForInvoice(invoiceId, cryptoCode, amount.Value, null);
|
||||||
|
}
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpGet("~/boltcard")]
|
[HttpGet("~/boltcard")]
|
||||||
public async Task<IActionResult> GetWithdrawRequest([FromQuery] string? p, [FromQuery] string? c, [FromQuery] string? pr, [FromQuery] string? k1, CancellationToken cancellationToken)
|
public async Task<IActionResult> GetWithdrawRequest([FromQuery] string? p, [FromQuery] string? c, [FromQuery] string? pr, [FromQuery] string? k1, CancellationToken cancellationToken)
|
||||||
|
@ -65,6 +122,16 @@ public class UIBoltcardController : Controller
|
||||||
if (!cardKey.CheckSunMac(c, piccData))
|
if (!cardKey.CheckSunMac(c, piccData))
|
||||||
return BadRequest(new LNUrlStatusResponse { Status = "ERROR", Reason = "Replayed or expired query" });
|
return BadRequest(new LNUrlStatusResponse { Status = "ERROR", Reason = "Replayed or expired query" });
|
||||||
LNURLController.ControllerContext.HttpContext = HttpContext;
|
LNURLController.ControllerContext.HttpContext = HttpContext;
|
||||||
return await LNURLController.GetLNURLForPullPayment("BTC", registration.PullPaymentId, pr, $"{p}-{c}", cancellationToken);
|
var res = await LNURLController.GetLNURLForPullPayment("BTC", registration.PullPaymentId, pr, $"{p}-{c}", cancellationToken);
|
||||||
|
if (res is not OkObjectResult ok || ok.Value is not LNURLWithdrawRequest withdrawRequest)
|
||||||
|
return res;
|
||||||
|
var paylink = GetPayLink(p, "lnurlp");
|
||||||
|
withdrawRequest.PayLink = new Uri(paylink, UriKind.Absolute);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetPayLink(string? p, string scheme)
|
||||||
|
{
|
||||||
|
return Url.Action(nameof(GetPayRequest), "UIBoltcard", new { p }, scheme)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ using BTCPayServer.Plugins.PointOfSale.Models;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
|
using BTCPayServer.Services.PaymentRequests;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using LNURL;
|
using LNURL;
|
||||||
|
@ -436,6 +437,13 @@ namespace BTCPayServer
|
||||||
var store = await _storeRepository.FindStore(lightningAddressSettings.StoreDataId);
|
var store = await _storeRepository.FindStore(lightningAddressSettings.StoreDataId);
|
||||||
if (store is null)
|
if (store is null)
|
||||||
return NotFound("Unknown username");
|
return NotFound("Unknown username");
|
||||||
|
List<string> additionalTags = new List<string>();
|
||||||
|
if (blob?.PullPaymentId is not null)
|
||||||
|
{
|
||||||
|
var pp = await _pullPaymentHostedService.GetPullPayment(blob.PullPaymentId, false);
|
||||||
|
if (pp != null)
|
||||||
|
additionalTags.Add(PullPaymentHostedService.GetInternalTag(blob.PullPaymentId));
|
||||||
|
}
|
||||||
var result = await GetLNURLRequest(
|
var result = await GetLNURLRequest(
|
||||||
cryptoCode,
|
cryptoCode,
|
||||||
store,
|
store,
|
||||||
|
@ -453,7 +461,7 @@ namespace BTCPayServer
|
||||||
new Dictionary<string, string>
|
new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{ "text/identifier", $"{username}@{Request.Host}" }
|
{ "text/identifier", $"{username}@{Request.Host}" }
|
||||||
});
|
}, additionalTags);
|
||||||
if (result is not OkObjectResult ok || ok.Value is not LNURLPayRequest payRequest)
|
if (result is not OkObjectResult ok || ok.Value is not LNURLPayRequest payRequest)
|
||||||
return result;
|
return result;
|
||||||
var invoiceId = payRequest.Callback.AbsoluteUri.Split('/').Last();
|
var invoiceId = payRequest.Callback.AbsoluteUri.Split('/').Last();
|
||||||
|
@ -495,7 +503,7 @@ namespace BTCPayServer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IActionResult> GetLNURLRequest(
|
internal async Task<IActionResult> GetLNURLRequest(
|
||||||
string cryptoCode,
|
string cryptoCode,
|
||||||
Data.StoreData store,
|
Data.StoreData store,
|
||||||
Data.StoreBlob blob,
|
Data.StoreBlob blob,
|
||||||
|
|
|
@ -276,7 +276,8 @@ namespace BTCPayServer.Controllers
|
||||||
Destination = destination,
|
Destination = destination,
|
||||||
PullPaymentId = pullPaymentId,
|
PullPaymentId = pullPaymentId,
|
||||||
Value = vm.ClaimedAmount,
|
Value = vm.ClaimedAmount,
|
||||||
PaymentMethodId = paymentMethodId
|
PaymentMethodId = paymentMethodId,
|
||||||
|
StoreId = pp.StoreId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.Result != ClaimRequest.ClaimResult.Ok)
|
if (result.Result != ClaimRequest.ClaimResult.Ok)
|
||||||
|
|
|
@ -7,11 +7,13 @@ using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Extensions;
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Events;
|
||||||
using BTCPayServer.Logging;
|
using BTCPayServer.Logging;
|
||||||
using BTCPayServer.Models.WalletViewModels;
|
using BTCPayServer.Models.WalletViewModels;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Rating;
|
using BTCPayServer.Rating;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Notifications;
|
using BTCPayServer.Services.Notifications;
|
||||||
using BTCPayServer.Services.Notifications.Blobs;
|
using BTCPayServer.Services.Notifications.Blobs;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
|
@ -282,7 +284,7 @@ namespace BTCPayServer.HostedServices
|
||||||
|
|
||||||
return await query.FirstOrDefaultAsync(data => data.Id == pullPaymentId);
|
return await query.FirstOrDefaultAsync(data => data.Id == pullPaymentId);
|
||||||
}
|
}
|
||||||
|
record TopUpRequest(string PullPaymentId, InvoiceEntity InvoiceEntity);
|
||||||
class PayoutRequest
|
class PayoutRequest
|
||||||
{
|
{
|
||||||
public PayoutRequest(TaskCompletionSource<ClaimRequest.ClaimResponse> completionSource,
|
public PayoutRequest(TaskCompletionSource<ClaimRequest.ClaimResponse> completionSource,
|
||||||
|
@ -292,6 +294,8 @@ namespace BTCPayServer.HostedServices
|
||||||
ArgumentNullException.ThrowIfNull(completionSource);
|
ArgumentNullException.ThrowIfNull(completionSource);
|
||||||
Completion = completionSource;
|
Completion = completionSource;
|
||||||
ClaimRequest = request;
|
ClaimRequest = request;
|
||||||
|
if (request.StoreId is null)
|
||||||
|
throw new ArgumentNullException(nameof(request.StoreId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskCompletionSource<ClaimRequest.ClaimResponse> Completion { get; set; }
|
public TaskCompletionSource<ClaimRequest.ClaimResponse> Completion { get; set; }
|
||||||
|
@ -342,10 +346,20 @@ namespace BTCPayServer.HostedServices
|
||||||
{
|
{
|
||||||
payoutHandler.StartBackgroundCheck(Subscribe);
|
payoutHandler.StartBackgroundCheck(Subscribe);
|
||||||
}
|
}
|
||||||
|
_eventAggregator.Subscribe<Events.InvoiceEvent>(TopUpInvoice);
|
||||||
return new[] { Loop() };
|
return new[] { Loop() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TopUpInvoice(InvoiceEvent evt)
|
||||||
|
{
|
||||||
|
if (evt.EventCode == InvoiceEventCode.Completed)
|
||||||
|
{
|
||||||
|
foreach (var pullPaymentId in evt.Invoice.GetInternalTags("PULLPAY#"))
|
||||||
|
{
|
||||||
|
_Channel.Writer.TryWrite(new TopUpRequest(pullPaymentId, evt.Invoice));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
private void Subscribe(params Type[] events)
|
private void Subscribe(params Type[] events)
|
||||||
{
|
{
|
||||||
foreach (Type @event in events)
|
foreach (Type @event in events)
|
||||||
|
@ -358,6 +372,10 @@ namespace BTCPayServer.HostedServices
|
||||||
{
|
{
|
||||||
await foreach (var o in _Channel.Reader.ReadAllAsync())
|
await foreach (var o in _Channel.Reader.ReadAllAsync())
|
||||||
{
|
{
|
||||||
|
if (o is TopUpRequest topUp)
|
||||||
|
{
|
||||||
|
await HandleTopUp(topUp);
|
||||||
|
}
|
||||||
if (o is PayoutRequest req)
|
if (o is PayoutRequest req)
|
||||||
{
|
{
|
||||||
await HandleCreatePayout(req);
|
await HandleCreatePayout(req);
|
||||||
|
@ -392,6 +410,36 @@ namespace BTCPayServer.HostedServices
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task HandleTopUp(TopUpRequest topUp)
|
||||||
|
{
|
||||||
|
var pp = await this.GetPullPayment(topUp.PullPaymentId, false);
|
||||||
|
using var ctx = _dbContextFactory.CreateContext();
|
||||||
|
|
||||||
|
var payout = new Data.PayoutData()
|
||||||
|
{
|
||||||
|
Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20)),
|
||||||
|
Date = DateTimeOffset.UtcNow,
|
||||||
|
State = PayoutState.Completed,
|
||||||
|
PullPaymentDataId = pp.Id,
|
||||||
|
PaymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike).ToString(),
|
||||||
|
Destination = null,
|
||||||
|
StoreDataId = pp.StoreId
|
||||||
|
};
|
||||||
|
var rate = topUp.InvoiceEntity.Rates["BTC"];
|
||||||
|
var cryptoAmount = Math.Round(topUp.InvoiceEntity.PaidAmount.Net / rate, 11);
|
||||||
|
|
||||||
|
var payoutBlob = new PayoutBlob()
|
||||||
|
{
|
||||||
|
CryptoAmount = -cryptoAmount,
|
||||||
|
Amount = -topUp.InvoiceEntity.PaidAmount.Net,
|
||||||
|
Destination = null,
|
||||||
|
Metadata = new JObject(),
|
||||||
|
};
|
||||||
|
payout.SetBlob(payoutBlob, _jsonSerializerSettings);
|
||||||
|
await ctx.Payouts.AddAsync(payout);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
public bool SupportsLNURL(PullPaymentBlob blob)
|
public bool SupportsLNURL(PullPaymentBlob blob)
|
||||||
{
|
{
|
||||||
var pms = blob.SupportedPaymentMethods.FirstOrDefault(id =>
|
var pms = blob.SupportedPaymentMethods.FirstOrDefault(id =>
|
||||||
|
@ -845,6 +893,10 @@ namespace BTCPayServer.HostedServices
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetInternalTag(string id)
|
||||||
|
{
|
||||||
|
return $"PULLPAY#{id}";
|
||||||
|
}
|
||||||
|
|
||||||
class InternalPayoutPaidRequest
|
class InternalPayoutPaidRequest
|
||||||
{
|
{
|
||||||
|
|
|
@ -184,7 +184,7 @@ namespace BTCPayServer.Plugins.BoltcardTopUp.Controllers
|
||||||
{
|
{
|
||||||
Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20)),
|
Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20)),
|
||||||
Date = DateTimeOffset.UtcNow,
|
Date = DateTimeOffset.UtcNow,
|
||||||
State = PayoutState.AwaitingApproval,
|
State = PayoutState.Completed,
|
||||||
PullPaymentDataId = registration.PullPaymentId,
|
PullPaymentDataId = registration.PullPaymentId,
|
||||||
PaymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike).ToString(),
|
PaymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike).ToString(),
|
||||||
Destination = null,
|
Destination = null,
|
||||||
|
|
|
@ -7,12 +7,14 @@
|
||||||
@using BTCPayServer
|
@using BTCPayServer
|
||||||
|
|
||||||
@{
|
@{
|
||||||
var storeId = Context.GetStoreData().Id;
|
var storeId = Context.GetStoreData()?.Id;
|
||||||
}
|
}
|
||||||
|
@if (storeId != null)
|
||||||
|
{
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a asp-area="" asp-controller="UIBoltcardTopUp" asp-action="Keypad" asp-route-storeId="@storeId" class="nav-link">
|
<a asp-area="" asp-controller="UIBoltcardTopUp" asp-action="Keypad" asp-route-storeId="@storeId" class="nav-link">
|
||||||
<vc:icon symbol="pay-button" />
|
<vc:icon symbol="pay-button" />
|
||||||
<span>Boltcard Top-Up</span>
|
<span>Boltcard Top-Up</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
}
|
||||||
|
|
|
@ -487,6 +487,7 @@ namespace BTCPayServer.Services
|
||||||
|
|
||||||
public static WalletObjectData NewWalletObjectData(WalletObjectId id, JObject? data = null)
|
public static WalletObjectData NewWalletObjectData(WalletObjectId id, JObject? data = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
return new WalletObjectData()
|
return new WalletObjectData()
|
||||||
{
|
{
|
||||||
WalletId = id.WalletId.ToString(),
|
WalletId = id.WalletId.ToString(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue