mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-17 07:34:24 +01:00
support custodians in prism
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Product>Prism</Product>
|
<Product>Prism</Product>
|
||||||
<Description>Automated value splits for Bitcoin.</Description>
|
<Description>Automated value splits for Bitcoin.</Description>
|
||||||
<Version>1.2.4</Version>
|
<Version>1.2.5</Version>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- Plugin development properties -->
|
<!-- Plugin development properties -->
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
{
|
{
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">Destination</label>
|
<label class="form-label">Destination</label>
|
||||||
<input type="text" @bind="WorkingCopy.Destination" class="form-control"/>
|
<input type="text" @bind="WorkingCopy.Destination" list="destinations" class="form-control"/>
|
||||||
@if (Invalid)
|
@if (Invalid)
|
||||||
{
|
{
|
||||||
<span class="text-danger w-100">Invalid</span>
|
<span class="text-danger w-100">Invalid</span>
|
||||||
|
|||||||
@@ -4,12 +4,16 @@
|
|||||||
@using BTCPayServer.HostedServices
|
@using BTCPayServer.HostedServices
|
||||||
@using BTCPayServer.Payments
|
@using BTCPayServer.Payments
|
||||||
@using BTCPayServer.PayoutProcessors
|
@using BTCPayServer.PayoutProcessors
|
||||||
|
@using BTCPayServer.Services.Custodian.Client
|
||||||
@using Microsoft.AspNetCore.Http
|
@using Microsoft.AspNetCore.Http
|
||||||
@using Microsoft.AspNetCore.Routing
|
@using Microsoft.AspNetCore.Routing
|
||||||
@using Microsoft.Extensions.Logging
|
@using Microsoft.Extensions.Logging
|
||||||
@using NBitcoin
|
@using NBitcoin
|
||||||
@using LightningAddressData = BTCPayServer.Data.LightningAddressData
|
@using LightningAddressData = BTCPayServer.Data.LightningAddressData
|
||||||
@using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest
|
@using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest
|
||||||
|
@using System.Collections
|
||||||
|
@using BTCPayServer.Abstractions.Custodians
|
||||||
|
@using BTCPayServer.Abstractions.Extensions
|
||||||
@inject IPluginHookService PluginHookService
|
@inject IPluginHookService PluginHookService
|
||||||
@inject LightningAddressService LightningAddressService
|
@inject LightningAddressService LightningAddressService
|
||||||
@inject PayoutProcessorService PayoutProcessorService
|
@inject PayoutProcessorService PayoutProcessorService
|
||||||
@@ -18,6 +22,8 @@
|
|||||||
@inject LinkGenerator LinkGenerator
|
@inject LinkGenerator LinkGenerator
|
||||||
@inject PullPaymentHostedService PullPaymentHostedService
|
@inject PullPaymentHostedService PullPaymentHostedService
|
||||||
@inject IHttpContextAccessor HttpContextAccessor
|
@inject IHttpContextAccessor HttpContextAccessor
|
||||||
|
@inject CustodianAccountRepository CustodianAccountRepository
|
||||||
|
@inject IEnumerable<ICustodian> Custodians
|
||||||
@inject ILogger<PrismEdit> Logger
|
@inject ILogger<PrismEdit> Logger
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
|
|
||||||
@@ -41,6 +47,10 @@ else
|
|||||||
{
|
{
|
||||||
<option value="@destination">@destination</option>
|
<option value="@destination">@destination</option>
|
||||||
}
|
}
|
||||||
|
@foreach (var destination in CustodianDestinations)
|
||||||
|
{
|
||||||
|
<option value="@destination.Key">@destination.Value</option>
|
||||||
|
}
|
||||||
</datalist>
|
</datalist>
|
||||||
|
|
||||||
|
|
||||||
@@ -205,6 +215,7 @@ else
|
|||||||
public PaymentMethodId pmichain { get; set; } = new("BTC", PaymentTypes.BTCLike);
|
public PaymentMethodId pmichain { get; set; } = new("BTC", PaymentTypes.BTCLike);
|
||||||
public bool NoPayoutProcessors { get; set; }
|
public bool NoPayoutProcessors { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, string> CustodianDestinations { get; set; }
|
||||||
private string PrismEditButtonsFilter { get; set; }
|
private string PrismEditButtonsFilter { get; set; }
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
@@ -236,6 +247,35 @@ else
|
|||||||
await Task.WhenAll(tasks);
|
await Task.WhenAll(tasks);
|
||||||
Settings = await fetchSettings;
|
Settings = await fetchSettings;
|
||||||
Users = await fetchLnAddresses;
|
Users = await fetchLnAddresses;
|
||||||
|
CustodianDestinations = await FetchCustodians();
|
||||||
|
|
||||||
|
async Task<Dictionary<string, string>> FetchCustodians()
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, string>();
|
||||||
|
var custodianConfigs = await CustodianAccountRepository.FindByStoreId(StoreId);
|
||||||
|
foreach (var custodianConfig in custodianConfigs)
|
||||||
|
{
|
||||||
|
var custodian = Custodians.GetCustodianByCode(custodianConfig.CustodianCode);
|
||||||
|
if(custodian is not ICanDeposit canDeposit)
|
||||||
|
continue;
|
||||||
|
foreach (var depositablePaymentMethod in canDeposit.GetDepositablePaymentMethods())
|
||||||
|
{
|
||||||
|
var pmi = PaymentMethodId.TryParse(depositablePaymentMethod);
|
||||||
|
if(pmi is null || pmi.CryptoCode != "BTC")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var custodianDestination = new CustodianDestinationValidator.CustodianDestination()
|
||||||
|
{
|
||||||
|
CustodianId = custodianConfig.Id,
|
||||||
|
PaymentMethod = pmi.ToString()
|
||||||
|
};
|
||||||
|
result.TryAdd(custodianDestination.ToString(), $"{custodianConfig.Name} {pmi.ToPrettyString()} (Custodian)");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
EditContext = new EditContext(Settings);
|
EditContext = new EditContext(Settings);
|
||||||
MessageStore = new ValidationMessageStore(EditContext);
|
MessageStore = new ValidationMessageStore(EditContext);
|
||||||
EditContext.OnValidationRequested += Validate;
|
EditContext.OnValidationRequested += Validate;
|
||||||
@@ -252,6 +292,7 @@ else
|
|||||||
await base.OnAfterRenderAsync(firstRender);
|
await base.OnAfterRenderAsync(firstRender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void FieldChanged(object sender, FieldChangedEventArgs e)
|
private void FieldChanged(object sender, FieldChangedEventArgs e)
|
||||||
{
|
{
|
||||||
StatusMessageModel = null;
|
StatusMessageModel = null;
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
|
using BTCPayServer.Abstractions.Custodians;
|
||||||
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Payments;
|
||||||
|
using BTCPayServer.Services.Custodian.Client;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.Prism;
|
||||||
|
|
||||||
|
public class CustodianDestinationValidator : IPluginHookFilter
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
public string Hook => "prism-destination-validate";
|
||||||
|
|
||||||
|
public CustodianDestinationValidator(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<object> Execute(object args)
|
||||||
|
{
|
||||||
|
var result = new PrismDestinationValidationResult();
|
||||||
|
if (args is not string args1 || !args1.StartsWith("custodian:")) return args;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var custodianDestination =
|
||||||
|
JObject.Parse(args1.Substring("custodian:".Length)).ToObject<CustodianDestination>();
|
||||||
|
var custodianPaymentMethod = custodianDestination.PaymentMethod is null
|
||||||
|
? new PaymentMethodId("BTC", PaymentTypes.LightningLike)
|
||||||
|
: PaymentMethodId.Parse(custodianDestination.PaymentMethod);
|
||||||
|
|
||||||
|
result.PaymentMethod = custodianPaymentMethod;
|
||||||
|
await using var ctx = _serviceProvider.GetService<ApplicationDbContextFactory>().CreateContext();
|
||||||
|
var custodianAccountData = ctx.CustodianAccount.SingleOrDefault(data => data.Id == custodianDestination.CustodianId);
|
||||||
|
if (custodianAccountData is null)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var custdodian = _serviceProvider.GetServices<ICustodian>().GetCustodianByCode(custodianAccountData.CustodianCode);
|
||||||
|
if (custdodian is null)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (custdodian is ICanDeposit canDeposit &&
|
||||||
|
canDeposit.GetDepositablePaymentMethods() is { } paymentMethods &&
|
||||||
|
paymentMethods.Any(s => PaymentMethodId.TryParse(s) == custodianPaymentMethod))
|
||||||
|
{
|
||||||
|
result.Success = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Success = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
result.Success = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class CustodianDestination
|
||||||
|
{
|
||||||
|
public string CustodianId { get; set; }
|
||||||
|
public string PaymentMethod { get; set; }
|
||||||
|
|
||||||
|
override public string ToString()
|
||||||
|
{
|
||||||
|
return $"custodian:{JObject.FromObject(this)}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
104
Plugins/BTCPayServer.Plugins.Prism/CustodianPrismClaimCreate.cs
Normal file
104
Plugins/BTCPayServer.Plugins.Prism/CustodianPrismClaimCreate.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
|
using BTCPayServer.Abstractions.Custodians;
|
||||||
|
using BTCPayServer.Abstractions.Extensions;
|
||||||
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.HostedServices;
|
||||||
|
using BTCPayServer.Payments;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Plugins.Prism;
|
||||||
|
|
||||||
|
public class CustodianPrismClaimCreate : IPluginHookFilter
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
public string Hook => "prism-claim-create";
|
||||||
|
|
||||||
|
public CustodianPrismClaimCreate(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<object> Execute(object args)
|
||||||
|
{
|
||||||
|
if (args is not ClaimRequest claimRequest)
|
||||||
|
{
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (claimRequest.Destination?.ToString() is not { } args1 || !args1.StartsWith("custodian:")) return args;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
var custodianDestination = JObject.Parse(args1.Substring("custodian:".Length))
|
||||||
|
.ToObject<CustodianDestinationValidator.CustodianDestination>();
|
||||||
|
var custodianPaymentMethod = custodianDestination.PaymentMethod is null
|
||||||
|
? new PaymentMethodId("BTC", PaymentTypes.LightningLike)
|
||||||
|
: PaymentMethodId.Parse(custodianDestination.PaymentMethod);
|
||||||
|
|
||||||
|
await using var ctx = _serviceProvider.GetRequiredService<ApplicationDbContextFactory>().CreateContext();
|
||||||
|
var custodianAccountData = await ctx.CustodianAccount.SingleOrDefaultAsync(data => data.Id == custodianDestination.CustodianId);
|
||||||
|
if (custodianAccountData is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var custdodian = _serviceProvider.GetServices<ICustodian>().GetCustodianByCode(custodianAccountData.CustodianCode);
|
||||||
|
if (custdodian is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (custdodian is not ICanDeposit canDeposit ||
|
||||||
|
canDeposit.GetDepositablePaymentMethods() is { } paymentMethods &&
|
||||||
|
paymentMethods.Any(s => PaymentMethodId.TryParse(s) == custodianPaymentMethod))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handler = _serviceProvider.GetServices<IPayoutHandler>().FindPayoutHandler(custodianPaymentMethod);
|
||||||
|
if (handler is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = custodianAccountData.GetBlob();
|
||||||
|
config["depositAddressConfig"] = JToken.FromObject(new
|
||||||
|
{
|
||||||
|
amount = claimRequest.Value
|
||||||
|
});
|
||||||
|
var depositAddressAsync =
|
||||||
|
await canDeposit.GetDepositAddressAsync(custodianPaymentMethod.ToString(),config, CancellationToken.None);
|
||||||
|
if (depositAddressAsync.Address is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var claimDestination = await handler.ParseClaimDestination(custodianPaymentMethod,
|
||||||
|
depositAddressAsync.Address, CancellationToken.None);
|
||||||
|
if (claimDestination.destination is null)
|
||||||
|
{
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
claimRequest.Destination = claimDestination.destination;
|
||||||
|
claimRequest.PaymentMethodId = custodianPaymentMethod;
|
||||||
|
|
||||||
|
return claimRequest;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BTCPayServer.Abstractions.Contracts;
|
using BTCPayServer.Abstractions.Contracts;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
|
|||||||
@@ -22,10 +22,12 @@ public class PrismPlugin : BaseBTCPayServerPlugin
|
|||||||
"store-integrations-nav"));
|
"store-integrations-nav"));
|
||||||
applicationBuilder.AddSingleton<SatBreaker>();
|
applicationBuilder.AddSingleton<SatBreaker>();
|
||||||
applicationBuilder.AddHostedService(provider => provider.GetRequiredService<SatBreaker>());
|
applicationBuilder.AddHostedService(provider => provider.GetRequiredService<SatBreaker>());
|
||||||
|
applicationBuilder.AddSingleton<IPluginHookFilter, CustodianDestinationValidator>();
|
||||||
applicationBuilder.AddSingleton<IPluginHookFilter, LNURLPrismDestinationValidator>();
|
applicationBuilder.AddSingleton<IPluginHookFilter, LNURLPrismDestinationValidator>();
|
||||||
applicationBuilder.AddSingleton<IPluginHookFilter, OnChainPrismDestinationValidator>();
|
applicationBuilder.AddSingleton<IPluginHookFilter, OnChainPrismDestinationValidator>();
|
||||||
applicationBuilder.AddSingleton<IPluginHookFilter, LNURLPrismClaimCreate>();
|
applicationBuilder.AddSingleton<IPluginHookFilter, LNURLPrismClaimCreate>();
|
||||||
applicationBuilder.AddSingleton<IPluginHookFilter, OnChainPrismClaimCreate>();
|
applicationBuilder.AddSingleton<IPluginHookFilter, OnChainPrismClaimCreate>();
|
||||||
|
applicationBuilder.AddSingleton<IPluginHookFilter, CustodianPrismClaimCreate>();
|
||||||
base.Execute(applicationBuilder);
|
base.Execute(applicationBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user