Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
154244cae1 | |||
37a0640b0c |
@ -12,7 +12,9 @@ using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Data.Payouts.LightningLike;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models.AppViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
@ -45,6 +47,8 @@ namespace BTCPayServer
|
||||
private readonly UIInvoiceController _invoiceController;
|
||||
private readonly LinkGenerator _linkGenerator;
|
||||
private readonly LightningAddressService _lightningAddressService;
|
||||
private readonly LightningLikePayoutHandler _lightningLikePayoutHandler;
|
||||
private readonly PullPaymentHostedService _pullPaymentHostedService;
|
||||
|
||||
public UILNURLController(InvoiceRepository invoiceRepository,
|
||||
EventAggregator eventAggregator,
|
||||
@ -54,7 +58,9 @@ namespace BTCPayServer
|
||||
AppService appService,
|
||||
UIInvoiceController invoiceController,
|
||||
LinkGenerator linkGenerator,
|
||||
LightningAddressService lightningAddressService)
|
||||
LightningAddressService lightningAddressService,
|
||||
LightningLikePayoutHandler lightningLikePayoutHandler,
|
||||
PullPaymentHostedService pullPaymentHostedService)
|
||||
{
|
||||
_invoiceRepository = invoiceRepository;
|
||||
_eventAggregator = eventAggregator;
|
||||
@ -65,9 +71,115 @@ namespace BTCPayServer
|
||||
_invoiceController = invoiceController;
|
||||
_linkGenerator = linkGenerator;
|
||||
_lightningAddressService = lightningAddressService;
|
||||
_lightningLikePayoutHandler = lightningLikePayoutHandler;
|
||||
_pullPaymentHostedService = pullPaymentHostedService;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("withdraw/pp/{pullPaymentId}")]
|
||||
public async Task<IActionResult> GetLNURLForPullPayment(string cryptoCode, string pullPaymentId, string pr)
|
||||
{
|
||||
|
||||
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
if (network is null || !network.SupportLightning)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var pmi = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
var pp = await _pullPaymentHostedService.GetPullPayment(pullPaymentId, false);
|
||||
if (!pp.IsRunning() || pp.IsSupported(pmi))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var blob = pp.GetBlob();
|
||||
if (!blob.AutoApproveClaims && !blob.Currency.Equals(cryptoCode, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var progress = _pullPaymentHostedService.CalculatePullPaymentProgress(pp, DateTimeOffset.UtcNow);
|
||||
|
||||
var remaining = progress.Limit - progress.Completed - progress.Awaiting;
|
||||
// pp.Payouts
|
||||
var request = new LNURLWithdrawRequest()
|
||||
{
|
||||
MaxWithdrawable = LightMoney.FromUnit(remaining, LightMoneyUnit.BTC),
|
||||
K1 = pullPaymentId,
|
||||
BalanceCheck = Request.GetAbsoluteRootUri(),
|
||||
CurrentBalance = LightMoney.FromUnit(remaining, LightMoneyUnit.BTC),
|
||||
MinWithdrawable =
|
||||
LightMoney.FromUnit(
|
||||
Math.Min(await _lightningLikePayoutHandler.GetMinimumPayoutAmount(pmi, null), remaining),
|
||||
LightMoneyUnit.BTC),
|
||||
Tag = "withdrawRequest",
|
||||
Callback = Request.GetAbsoluteRootUri(),
|
||||
};
|
||||
if (pr is not null)
|
||||
{
|
||||
if(BOLT11PaymentRequest.TryParse(pr, out var result, network.NBitcoinNetwork) )
|
||||
{
|
||||
if (result.MinimumAmount >= request.MinWithdrawable &&
|
||||
result.MinimumAmount <= request.MaxWithdrawable)
|
||||
{
|
||||
var store = await _storeRepository.FindStore(pp.StoreId);
|
||||
var pm = store.GetSupportedPaymentMethods(_btcPayNetworkProvider)
|
||||
.OfType<LightningSupportedPaymentMethod>()
|
||||
.FirstOrDefault(method => method.PaymentId == pmi);
|
||||
if (pm is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var claimResponse = await _pullPaymentHostedService.Claim(new ClaimRequest()
|
||||
{
|
||||
Destination = new BoltInvoiceClaimDestination(pr, result),
|
||||
PaymentMethodId = pmi,
|
||||
PreApprove = false,
|
||||
PullPaymentId = pullPaymentId,
|
||||
StoreId = pp.StoreId,
|
||||
Value = result.MinimumAmount.ToDecimal(LightMoneyUnit.BTC)
|
||||
});
|
||||
|
||||
if (claimResponse.Result == ClaimRequest.ClaimResult.Ok)
|
||||
{
|
||||
var client =
|
||||
_lightningLikePaymentHandler.CreateLightningClient(pm, network);
|
||||
var payResult = await client.Pay(pr);
|
||||
if (payResult.Result == PayResult.Ok)
|
||||
{
|
||||
await _pullPaymentHostedService.MarkPaid(new PayoutPaidRequest()
|
||||
{
|
||||
PayoutId = claimResponse.PayoutData.Id,
|
||||
Proof = new ManualPayoutProof {}
|
||||
});
|
||||
|
||||
return Ok(new LNUrlStatusResponse {Status = "OK"});
|
||||
}
|
||||
|
||||
await _pullPaymentHostedService.Cancel(
|
||||
new PullPaymentHostedService.CancelRequest(new string[]
|
||||
{
|
||||
claimResponse.PayoutData.Id
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
return BadRequest(new LNUrlStatusResponse {Status = "ERROR", Reason = "Pr could not be paid"});
|
||||
}
|
||||
}else
|
||||
{
|
||||
return BadRequest(new LNUrlStatusResponse {Status = "ERROR", Reason = "Pr was not a valid BOLT11"});
|
||||
}
|
||||
return Ok(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return Ok(request);
|
||||
}
|
||||
}
|
||||
[HttpGet("pay/app/{appId}/{itemCode}")]
|
||||
public async Task<IActionResult> GetLNURLForApp(string cryptoCode, string appId, string itemCode = null)
|
||||
{
|
||||
|
@ -21,6 +21,7 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PayoutData = BTCPayServer.Data.PayoutData;
|
||||
using PullPaymentData = BTCPayServer.Data.PullPaymentData;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
@ -216,52 +217,27 @@ namespace BTCPayServer.Controllers
|
||||
break;
|
||||
}
|
||||
|
||||
var pps = (await ppsQuery
|
||||
.Skip(vm.Skip)
|
||||
.Take(vm.Count)
|
||||
.ToListAsync()
|
||||
);
|
||||
foreach (var pp in pps)
|
||||
var pps = await ppsQuery
|
||||
.Skip(vm.Skip)
|
||||
.Take(vm.Count)
|
||||
.ToListAsync();
|
||||
vm.PullPayments.AddRange(pps.Select(pp =>
|
||||
{
|
||||
var totalCompleted = pp.Payouts.Where(p => (p.State == PayoutState.Completed ||
|
||||
p.State == PayoutState.InProgress) && p.IsInPeriod(pp, now))
|
||||
.Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum();
|
||||
var totalAwaiting = pp.Payouts.Where(p => (p.State == PayoutState.AwaitingPayment ||
|
||||
p.State == PayoutState.AwaitingApproval) &&
|
||||
p.IsInPeriod(pp, now)).Select(o =>
|
||||
o.GetBlob(_jsonSerializerSettings).Amount).Sum();
|
||||
;
|
||||
var ppBlob = pp.GetBlob();
|
||||
var ni = _currencyNameTable.GetCurrencyData(ppBlob.Currency, true);
|
||||
var nfi = _currencyNameTable.GetNumberFormatInfo(ppBlob.Currency, true);
|
||||
var period = pp.GetPeriod(now);
|
||||
vm.PullPayments.Add(new PullPaymentsModel.PullPaymentModel()
|
||||
var blob = pp.GetBlob();
|
||||
return new PullPaymentsModel.PullPaymentModel()
|
||||
{
|
||||
StartDate = pp.StartDate,
|
||||
EndDate = pp.EndDate,
|
||||
Id = pp.Id,
|
||||
Name = ppBlob.Name,
|
||||
Progress = new PullPaymentsModel.PullPaymentModel.ProgressModel()
|
||||
{
|
||||
CompletedPercent = (int)(totalCompleted / ppBlob.Limit * 100m),
|
||||
AwaitingPercent = (int)(totalAwaiting / ppBlob.Limit * 100m),
|
||||
Awaiting = totalAwaiting.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
|
||||
Completed = totalCompleted.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
|
||||
Limit = _currencyNameTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency),
|
||||
ResetIn = period?.End is DateTimeOffset nr ? ZeroIfNegative(nr - now).TimeString() : null,
|
||||
EndIn = pp.EndDate is DateTimeOffset end ? ZeroIfNegative(end - now).TimeString() : null,
|
||||
},
|
||||
Name = blob.Name,
|
||||
AutoApproveClaims = blob.AutoApproveClaims,
|
||||
Progress = _pullPaymentService.CalculatePullPaymentProgress(pp, now),
|
||||
Archived = pp.Archived
|
||||
});
|
||||
}
|
||||
};
|
||||
}));
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
public TimeSpan ZeroIfNegative(TimeSpan time)
|
||||
{
|
||||
if (time < TimeSpan.Zero)
|
||||
time = TimeSpan.Zero;
|
||||
return time;
|
||||
}
|
||||
|
||||
[HttpGet("stores/{storeId}/pull-payments/{pullPaymentId}/archive")]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
|
@ -17,7 +17,7 @@ namespace BTCPayServer.Data
|
||||
data.Blob = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(blob));
|
||||
}
|
||||
|
||||
public static bool IsSupported(this PullPaymentData data, BTCPayServer.Payments.PaymentMethodId paymentId)
|
||||
public static bool IsSupported(this PullPaymentData data, Payments.PaymentMethodId paymentId)
|
||||
{
|
||||
return data.GetBlob().SupportedPaymentMethods.Contains(paymentId);
|
||||
}
|
||||
|
@ -4,10 +4,13 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Models.WalletViewModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Notifications;
|
||||
using BTCPayServer.Services.Notifications.Blobs;
|
||||
@ -18,6 +21,7 @@ using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NBXplorer;
|
||||
using PayoutData = BTCPayServer.Data.PayoutData;
|
||||
using PullPaymentData = BTCPayServer.Data.PullPaymentData;
|
||||
|
||||
|
||||
namespace BTCPayServer.HostedServices
|
||||
@ -167,7 +171,8 @@ namespace BTCPayServer.HostedServices
|
||||
RateFetcher rateFetcher,
|
||||
IEnumerable<IPayoutHandler> payoutHandlers,
|
||||
ILogger<PullPaymentHostedService> logger,
|
||||
Logs logs) : base(logs)
|
||||
Logs logs,
|
||||
CurrencyNameTable currencyNameTable) : base(logs)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_jsonSerializerSettings = jsonSerializerSettings;
|
||||
@ -177,6 +182,7 @@ namespace BTCPayServer.HostedServices
|
||||
_rateFetcher = rateFetcher;
|
||||
_payoutHandlers = payoutHandlers;
|
||||
_logger = logger;
|
||||
_currencyNameTable = currencyNameTable;
|
||||
}
|
||||
|
||||
Channel<object> _Channel;
|
||||
@ -188,6 +194,7 @@ namespace BTCPayServer.HostedServices
|
||||
private readonly RateFetcher _rateFetcher;
|
||||
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
|
||||
private readonly ILogger<PullPaymentHostedService> _logger;
|
||||
private readonly CurrencyNameTable _currencyNameTable;
|
||||
private readonly CompositeDisposable _subscriptions = new CompositeDisposable();
|
||||
|
||||
internal override Task[] InitializeTasks()
|
||||
@ -610,6 +617,47 @@ namespace BTCPayServer.HostedServices
|
||||
}
|
||||
|
||||
|
||||
public PullPaymentsModel.PullPaymentModel.ProgressModel CalculatePullPaymentProgress(PullPaymentData pp, DateTimeOffset now)
|
||||
{
|
||||
var ppBlob = pp.GetBlob();
|
||||
|
||||
var ni = _currencyNameTable.GetCurrencyData(ppBlob.Currency, true);
|
||||
var nfi = _currencyNameTable.GetNumberFormatInfo(ppBlob.Currency, true);
|
||||
var totalCompleted = pp.Payouts.Where(p => (p.State == PayoutState.Completed ||
|
||||
p.State == PayoutState.InProgress) && p.IsInPeriod(pp, now))
|
||||
.Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum().RoundToSignificant(ni.Divisibility);
|
||||
var period = pp.GetPeriod(now);
|
||||
var totalAwaiting = pp.Payouts.Where(p => (p.State == PayoutState.AwaitingPayment ||
|
||||
p.State == PayoutState.AwaitingApproval) &&
|
||||
p.IsInPeriod(pp, now)).Select(o =>
|
||||
o.GetBlob(_jsonSerializerSettings).Amount).Sum().RoundToSignificant(ni.Divisibility);
|
||||
;
|
||||
var currencyData = _currencyNameTable.GetCurrencyData(ppBlob.Currency, true);
|
||||
return new PullPaymentsModel.PullPaymentModel.ProgressModel()
|
||||
{
|
||||
CompletedPercent = (int)(totalCompleted / ppBlob.Limit * 100m),
|
||||
AwaitingPercent = (int)(totalAwaiting / ppBlob.Limit * 100m),
|
||||
AwaitingFormatted = totalAwaiting.ToString("C", nfi),
|
||||
Awaiting = totalAwaiting,
|
||||
Completed = totalCompleted,
|
||||
CompletedFormatted = totalCompleted.ToString("C", nfi),
|
||||
Limit = ppBlob.Limit.RoundToSignificant(currencyData.Divisibility),
|
||||
LimitFormatted = _currencyNameTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency),
|
||||
ResetIn = period?.End is { } nr ? ZeroIfNegative(nr - now).TimeString() : null,
|
||||
EndIn = pp.EndDate is { } end ? ZeroIfNegative(end - now).TimeString() : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public TimeSpan ZeroIfNegative(TimeSpan time)
|
||||
{
|
||||
if (time < TimeSpan.Zero)
|
||||
time = TimeSpan.Zero;
|
||||
return time;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class InternalPayoutPaidRequest
|
||||
{
|
||||
public InternalPayoutPaidRequest(TaskCompletionSource<PayoutPaidRequest.PayoutPaidResult> completionSource,
|
||||
|
@ -23,6 +23,7 @@ namespace BTCPayServer.Models
|
||||
PaymentMethods = blob.SupportedPaymentMethods;
|
||||
SelectedPaymentMethod = PaymentMethods.First().ToString();
|
||||
Archived = data.Archived;
|
||||
AutoApprove = blob.AutoApproveClaims;
|
||||
Title = blob.View.Title;
|
||||
Description = blob.View.Description;
|
||||
Amount = blob.Limit;
|
||||
@ -94,6 +95,7 @@ namespace BTCPayServer.Models
|
||||
public string AmountCollectedFormatted { get; set; }
|
||||
public string AmountFormatted { get; set; }
|
||||
public bool Archived { get; set; }
|
||||
public bool AutoApprove { get; set; }
|
||||
|
||||
public class PayoutLine
|
||||
{
|
||||
|
@ -16,11 +16,14 @@ namespace BTCPayServer.Models.WalletViewModels
|
||||
{
|
||||
public int CompletedPercent { get; set; }
|
||||
public int AwaitingPercent { get; set; }
|
||||
public string Completed { get; set; }
|
||||
public string Awaiting { get; set; }
|
||||
public string Limit { get; set; }
|
||||
public string CompletedFormatted { get; set; }
|
||||
public string AwaitingFormatted { get; set; }
|
||||
public string LimitFormatted { get; set; }
|
||||
public string ResetIn { get; set; }
|
||||
public string EndIn { get; set; }
|
||||
public decimal Awaiting { get; set; }
|
||||
public decimal Completed { get; set; }
|
||||
public decimal Limit { get; set; }
|
||||
}
|
||||
public string Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
@ -1,10 +1,13 @@
|
||||
@using NUglify.Helpers
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using BTCPayServer.Payments
|
||||
@using Microsoft.AspNetCore.Server.IIS.Core
|
||||
@model BTCPayServer.Models.ViewPullPaymentModel
|
||||
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@inject BTCPayServer.Services.BTCPayServerEnvironment env
|
||||
@inject BTCPayNetworkProvider BtcPayNetworkProvider
|
||||
@inject ISettingsRepository _settingsRepository
|
||||
@{
|
||||
ViewData["Title"] = Model.Title;
|
||||
@ -80,6 +83,24 @@
|
||||
<button class="btn btn-lg btn-primary w-100 text-nowrap" type="submit">Claim Funds</button>
|
||||
</div>
|
||||
</div>
|
||||
@{
|
||||
var pms = Model.PaymentMethods.FirstOrDefault(id => id.PaymentType == LightningPaymentType.Instance && BtcPayNetworkProvider.DefaultNetwork.CryptoCode == id.CryptoCode);
|
||||
if (pms is not null && Model.AutoApprove)
|
||||
{
|
||||
var lnurlEndpoint = new Uri(Url.Action("GetLNURLForPullPayment", "UILNURL", new
|
||||
{
|
||||
cryptoCode = pms.CryptoCode,
|
||||
pullPaymentId = Model.Id
|
||||
}, Context.Request.Scheme, Context.Request.Host.ToString()));
|
||||
var lnUrl = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", true);
|
||||
<div class="row align-items-center">
|
||||
<div class="col-12 ">
|
||||
<a href="@lnUrl" rel="noreferrer noopener"><vc:qr-code data="@lnUrl.ToString().ToUpperInvariant()"/></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -71,11 +71,11 @@
|
||||
@foreach (var pp in Model.PullPayments)
|
||||
{
|
||||
<script id="tooptip_template_@pp.Id" type="text/template">
|
||||
<span>Awaiting: <span class="float-end">@pp.Progress.Awaiting</span></span>
|
||||
<span>Awaiting: <span class="float-end">@pp.Progress.AwaitingFormatted</span></span>
|
||||
<br />
|
||||
<span>Completed: <span class="float-end">@pp.Progress.Completed</span></span>
|
||||
<span>Completed: <span class="float-end">@pp.Progress.CompletedFormatted</span></span>
|
||||
<br />
|
||||
<span>Limit: <span class="float-end">@pp.Progress.Limit</span></span>
|
||||
<span>Limit: <span class="float-end">@pp.Progress.LimitFormatted</span></span>
|
||||
@if (pp.Progress.ResetIn != null)
|
||||
{
|
||||
<br />
|
||||
|
Reference in New Issue
Block a user