mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 23:54:26 +01:00
fix issues
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Product>LN Prism</Product>
|
<Product>LN Prism</Product>
|
||||||
<Description>Automated value splits for lightning.</Description>
|
<Description>Automated value splits for lightning.</Description>
|
||||||
<Version>1.0.3</Version>
|
<Version>1.0.4</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- Plugin development properties -->
|
<!-- Plugin development properties -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
namespace BTCPayServer.Plugins.Prism;
|
namespace BTCPayServer.Plugins.Prism;
|
||||||
|
|
||||||
public record PendingPayout(long BalanceAmount, long FeeCharged);
|
public record PendingPayout(long PayoutAmount, long FeeCharged);
|
||||||
@@ -33,6 +33,7 @@ namespace BTCPayServer.Plugins.Prism
|
|||||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||||
private readonly LightningClientFactoryService _lightningClientFactoryService;
|
private readonly LightningClientFactoryService _lightningClientFactoryService;
|
||||||
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
|
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
|
||||||
|
private readonly BTCPayNetworkJsonSerializerSettings _btcPayNetworkJsonSerializerSettings;
|
||||||
private Dictionary<string, PrismSettings> _prismSettings;
|
private Dictionary<string, PrismSettings> _prismSettings;
|
||||||
|
|
||||||
public SatBreaker(StoreRepository storeRepository,
|
public SatBreaker(StoreRepository storeRepository,
|
||||||
@@ -43,7 +44,8 @@ namespace BTCPayServer.Plugins.Prism
|
|||||||
LightningLikePayoutHandler lightningLikePayoutHandler,
|
LightningLikePayoutHandler lightningLikePayoutHandler,
|
||||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||||
LightningClientFactoryService lightningClientFactoryService,
|
LightningClientFactoryService lightningClientFactoryService,
|
||||||
IOptions<LightningNetworkOptions> lightningNetworkOptions) : base(eventAggregator, logger)
|
IOptions<LightningNetworkOptions> lightningNetworkOptions,
|
||||||
|
BTCPayNetworkJsonSerializerSettings btcPayNetworkJsonSerializerSettings) : base(eventAggregator, logger)
|
||||||
{
|
{
|
||||||
_storeRepository = storeRepository;
|
_storeRepository = storeRepository;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -53,6 +55,7 @@ namespace BTCPayServer.Plugins.Prism
|
|||||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||||
_lightningClientFactoryService = lightningClientFactoryService;
|
_lightningClientFactoryService = lightningClientFactoryService;
|
||||||
_lightningNetworkOptions = lightningNetworkOptions;
|
_lightningNetworkOptions = lightningNetworkOptions;
|
||||||
|
_btcPayNetworkJsonSerializerSettings = btcPayNetworkJsonSerializerSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||||
@@ -78,88 +81,101 @@ namespace BTCPayServer.Plugins.Prism
|
|||||||
{
|
{
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var payoutsToCheck = _prismSettings.ToDictionary(pair => pair.Key, pair => pair.Value.PendingPayouts);
|
try
|
||||||
var payoutIds = payoutsToCheck.SelectMany(pair => pair.Value.Keys).ToArray();
|
|
||||||
var payouts = (await _pullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
|
|
||||||
{
|
{
|
||||||
PayoutIds = payoutIds,
|
var payoutsToCheck =
|
||||||
States = new[] {PayoutState.Cancelled, PayoutState.Completed}
|
_prismSettings.ToDictionary(pair => pair.Key, pair => pair.Value.PendingPayouts);
|
||||||
}));
|
var payoutIds = payoutsToCheck
|
||||||
var lnClients = new Dictionary<string, ILightningClient>();
|
.SelectMany(pair => pair.Value?.Keys.ToArray() ?? Array.Empty<string>()).ToArray();
|
||||||
var res = new Dictionary<string, CreditDestination>();
|
var payouts = (await _pullPaymentHostedService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
|
||||||
|
|
||||||
foreach (var payout in payouts)
|
|
||||||
{
|
|
||||||
if (payoutsToCheck.TryGetValue(payout.StoreDataId, out var pendingPayouts) &&
|
|
||||||
pendingPayouts.TryGetValue(payout.Id, out var pendingPayout))
|
|
||||||
{
|
{
|
||||||
long toCredit = 0;
|
PayoutIds = payoutIds,
|
||||||
switch (payout.State)
|
States = new[] {PayoutState.Cancelled, PayoutState.Completed}
|
||||||
|
}));
|
||||||
|
var lnClients = new Dictionary<string, ILightningClient>();
|
||||||
|
var res = new Dictionary<string, CreditDestination>();
|
||||||
|
|
||||||
|
foreach (var payout in payouts)
|
||||||
|
{
|
||||||
|
if (payoutsToCheck.TryGetValue(payout.StoreDataId, out var pendingPayouts) &&
|
||||||
|
pendingPayouts.TryGetValue(payout.Id, out var pendingPayout))
|
||||||
{
|
{
|
||||||
case PayoutState.Completed:
|
long toCredit = 0;
|
||||||
|
switch (payout.State)
|
||||||
|
{
|
||||||
|
case PayoutState.Completed:
|
||||||
|
|
||||||
var proof = _lightningLikePayoutHandler.ParseProof(payout) as PayoutLightningBlob;
|
var proof = _lightningLikePayoutHandler.ParseProof(payout) as PayoutLightningBlob;
|
||||||
|
|
||||||
long? feePaid = null;
|
long? feePaid = null;
|
||||||
if (!string.IsNullOrEmpty(proof?.PaymentHash))
|
if (!string.IsNullOrEmpty(proof?.PaymentHash))
|
||||||
{
|
|
||||||
if (!lnClients.TryGetValue(payout.StoreDataId, out var lnClient))
|
|
||||||
{
|
{
|
||||||
var store = await _storeRepository.FindStore(payout.StoreDataId);
|
if (!lnClients.TryGetValue(payout.StoreDataId, out var lnClient))
|
||||||
|
{
|
||||||
|
var store = await _storeRepository.FindStore(payout.StoreDataId);
|
||||||
|
|
||||||
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC");
|
var network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>("BTC");
|
||||||
var id = new PaymentMethodId("BTC", PaymentTypes.LightningLike);
|
var id = new PaymentMethodId("BTC", PaymentTypes.LightningLike);
|
||||||
var existing = store.GetSupportedPaymentMethods(_btcPayNetworkProvider)
|
var existing = store.GetSupportedPaymentMethods(_btcPayNetworkProvider)
|
||||||
.OfType<LightningSupportedPaymentMethod>()
|
.OfType<LightningSupportedPaymentMethod>()
|
||||||
.FirstOrDefault(d => d.PaymentId == id);
|
.FirstOrDefault(d => d.PaymentId == id);
|
||||||
if (existing?.GetExternalLightningUrl() is { } connectionString)
|
if (existing?.GetExternalLightningUrl() is { } connectionString)
|
||||||
{
|
{
|
||||||
lnClient = _lightningClientFactoryService.Create(connectionString, network);
|
lnClient = _lightningClientFactoryService.Create(connectionString,
|
||||||
}
|
network);
|
||||||
else if (existing?.IsInternalNode is true &&
|
}
|
||||||
_lightningNetworkOptions.Value.InternalLightningByCryptoCode
|
else if (existing?.IsInternalNode is true &&
|
||||||
.TryGetValue(network.CryptoCode,
|
_lightningNetworkOptions.Value.InternalLightningByCryptoCode
|
||||||
out var internalLightningNode))
|
.TryGetValue(network.CryptoCode,
|
||||||
{
|
out var internalLightningNode))
|
||||||
lnClient = _lightningClientFactoryService.Create(internalLightningNode,
|
{
|
||||||
network);
|
lnClient = _lightningClientFactoryService.Create(internalLightningNode,
|
||||||
|
network);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
lnClients.Add(payout.StoreDataId, lnClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lnClient is not null)
|
||||||
lnClients.Add(payout.StoreDataId, lnClient);
|
{
|
||||||
|
var p = await lnClient.GetPayment(proof.PaymentHash, CancellationToken);
|
||||||
|
feePaid = (long) p.Fee.ToUnit(LightMoneyUnit.Satoshi);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lnClient is not null)
|
if (feePaid is not null)
|
||||||
{
|
{
|
||||||
var p = await lnClient.GetPayment(proof.PaymentHash, CancellationToken);
|
toCredit = pendingPayout.FeeCharged - feePaid.Value;
|
||||||
feePaid = (long) p.Fee.ToUnit(LightMoneyUnit.Satoshi);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (feePaid is not null)
|
break;
|
||||||
{
|
case PayoutState.Cancelled:
|
||||||
toCredit = pendingPayout.FeeCharged - feePaid.Value;
|
toCredit = pendingPayout.PayoutAmount + pendingPayout.FeeCharged;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
res.TryAdd(payout.StoreDataId,
|
||||||
case PayoutState.Cancelled:
|
new CreditDestination(payout.StoreDataId, new Dictionary<string, long>(),
|
||||||
toCredit = pendingPayout.BalanceAmount + pendingPayout.FeeCharged;
|
new List<string>()));
|
||||||
break;
|
var credDest = res[payout.StoreDataId];
|
||||||
|
credDest.PayoutsToRemove.Add(payout.Id);
|
||||||
|
|
||||||
|
credDest.Destcredits.Add(payout.GetBlob(_btcPayNetworkJsonSerializerSettings).Destination,
|
||||||
|
toCredit);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.TryAdd(payout.StoreDataId,
|
|
||||||
new CreditDestination(payout.StoreDataId, new Dictionary<string, long>(),
|
|
||||||
new List<string>()));
|
|
||||||
var credDest = res[payout.StoreDataId];
|
|
||||||
credDest.PayoutsToRemove.Add(payout.Id);
|
|
||||||
credDest.Destcredits.Add(payout.Destination, toCredit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource(cancellationToken);
|
||||||
|
PushEvent(new PayoutCheckResult(res.Values.ToArray(), tcs));
|
||||||
|
//we wait for ProcessEvent to handle this result so that we avoid race conditions.
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Error while checking payouts");
|
||||||
}
|
}
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource(cancellationToken);
|
|
||||||
PushEvent(new PayoutCheckResult(res.Values.ToArray(), tcs));
|
|
||||||
//we wait for ProcessEvent to handle this result so that we avoid race conditions.
|
|
||||||
await tcs.Task;
|
|
||||||
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
|
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,11 +191,12 @@ namespace BTCPayServer.Plugins.Prism
|
|||||||
return _prismSettings.TryGetValue(storeId, out var settings) ? settings : new PrismSettings();
|
return _prismSettings.TryGetValue(storeId, out var settings) ? settings : new PrismSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UpdatePrismSettingsForStore(string storeId, PrismSettings updatedSettings, bool skipLock = false)
|
public async Task<bool> UpdatePrismSettingsForStore(string storeId, PrismSettings updatedSettings,
|
||||||
|
bool skipLock = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(!skipLock)
|
if (!skipLock)
|
||||||
await _updateLock.WaitAsync();
|
await _updateLock.WaitAsync();
|
||||||
var currentSettings = await Get(storeId);
|
var currentSettings = await Get(storeId);
|
||||||
|
|
||||||
@@ -199,7 +216,7 @@ namespace BTCPayServer.Plugins.Prism
|
|||||||
|
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if(!skipLock)
|
if (!skipLock)
|
||||||
_updateLock.Release();
|
_updateLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,15 +330,18 @@ namespace BTCPayServer.Plugins.Prism
|
|||||||
|
|
||||||
private async Task CreatePayouts(string storeId, PrismSettings prismSettings)
|
private async Task CreatePayouts(string storeId, PrismSettings prismSettings)
|
||||||
{
|
{
|
||||||
var threshold = (long) Math.Max(1,
|
|
||||||
Math.Round(prismSettings.SatThreshold * 1.02, 0, MidpointRounding.AwayFromZero));
|
|
||||||
foreach (var (destination, amtMsats) in prismSettings.DestinationBalance)
|
foreach (var (destination, amtMsats) in prismSettings.DestinationBalance)
|
||||||
{
|
{
|
||||||
var amt = amtMsats / 1000;
|
var amt = amtMsats / 1000;
|
||||||
if (amt >= threshold)
|
if (amt >= prismSettings.SatThreshold)
|
||||||
{
|
{
|
||||||
var reserveFee = (long) Math.Max(1, Math.Round(amt * 0.02, 0, MidpointRounding.AwayFromZero));
|
var reserveFee = (long) Math.Max(1, Math.Round(amt * 0.02, 0, MidpointRounding.AwayFromZero));
|
||||||
var payoutAmount = amt - reserveFee;
|
var payoutAmount = amt - reserveFee;
|
||||||
|
if (payoutAmount <= 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var payout = await _pullPaymentHostedService.Claim(new ClaimRequest()
|
var payout = await _pullPaymentHostedService.Claim(new ClaimRequest()
|
||||||
{
|
{
|
||||||
Destination = new LNURLPayClaimDestinaton(destination),
|
Destination = new LNURLPayClaimDestinaton(destination),
|
||||||
@@ -332,8 +352,9 @@ namespace BTCPayServer.Plugins.Prism
|
|||||||
});
|
});
|
||||||
if (payout.Result == ClaimRequest.ClaimResult.Ok)
|
if (payout.Result == ClaimRequest.ClaimResult.Ok)
|
||||||
{
|
{
|
||||||
prismSettings.PendingPayouts??=new();
|
prismSettings.PendingPayouts ??= new();
|
||||||
prismSettings.PendingPayouts.Add(payout.PayoutData.Id, new PendingPayout(payoutAmount, reserveFee));
|
prismSettings.PendingPayouts.Add(payout.PayoutData.Id,
|
||||||
|
new PendingPayout(payoutAmount, reserveFee));
|
||||||
prismSettings.DestinationBalance.AddOrReplace(destination,
|
prismSettings.DestinationBalance.AddOrReplace(destination,
|
||||||
amtMsats - (payoutAmount + reserveFee) * 1000);
|
amtMsats - (payoutAmount + reserveFee) * 1000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>@payoutId</td>
|
<td>@payoutId</td>
|
||||||
<td>@pendingPayout.FeeCharged</td>
|
<td>@pendingPayout.FeeCharged</td>
|
||||||
<td>@pendingPayout.BalanceAmount</td>
|
<td>@pendingPayout.PayoutAmount</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user