btcpayserver/BTCPayServer/PayoutProcessors/OnChain/UIOnChainAutomatedPayoutProcessorsController.cs
2024-04-04 16:31:04 +09:00

163 lines
7.0 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Payments;
using BTCPayServer.Services.Invoices;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace BTCPayServer.PayoutProcessors.OnChain;
public class UIOnChainAutomatedPayoutProcessorsController : Controller
{
private readonly EventAggregator _eventAggregator;
private readonly PaymentMethodHandlerDictionary _handlers;
private readonly OnChainAutomatedPayoutSenderFactory _onChainAutomatedPayoutSenderFactory;
private readonly PayoutProcessorService _payoutProcessorService;
public UIOnChainAutomatedPayoutProcessorsController(
EventAggregator eventAggregator,
PaymentMethodHandlerDictionary handlers,
OnChainAutomatedPayoutSenderFactory onChainAutomatedPayoutSenderFactory,
PayoutProcessorService payoutProcessorService)
{
_eventAggregator = eventAggregator;
_handlers = handlers;
_onChainAutomatedPayoutSenderFactory = onChainAutomatedPayoutSenderFactory;
_payoutProcessorService = payoutProcessorService;
}
PaymentMethodId GetPaymentMethodId(string cryptoCode) => PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode);
[HttpGet("~/stores/{storeId}/payout-processors/onchain-automated/{cryptocode}")]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Configure(string storeId, string cryptoCode)
{
var id = GetPaymentMethodId(cryptoCode);
if (!_onChainAutomatedPayoutSenderFactory.GetSupportedPaymentMethods().Any(i => id == i))
{
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Error,
Message = $"This processor cannot handle {cryptoCode}."
});
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors");
}
var wallet = HttpContext.GetStoreData().GetDerivationSchemeSettings(_handlers, cryptoCode);
if (wallet?.IsHotWallet is not true)
{
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Error,
Message = $"Either your {cryptoCode} wallet is not configured, or it is not a hot wallet. This processor cannot function until a hot wallet is configured in your store."
});
}
var activeProcessor =
(await _payoutProcessorService.GetProcessors(
new PayoutProcessorService.PayoutProcessorQuery()
{
Stores = new[] { storeId },
Processors = new[] { _onChainAutomatedPayoutSenderFactory.Processor },
PaymentMethods = new[]
{
PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode)
}
}))
.FirstOrDefault();
return View(new OnChainTransferViewModel(activeProcessor is null ? new OnChainAutomatedPayoutBlob() : OnChainAutomatedPayoutProcessor.GetBlob(activeProcessor)));
}
[HttpPost("~/stores/{storeId}/payout-processors/onchain-automated/{cryptocode}")]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Configure(string storeId, string cryptoCode, OnChainTransferViewModel automatedTransferBlob)
{
if (!ModelState.IsValid)
return View(automatedTransferBlob);
var id = GetPaymentMethodId(cryptoCode);
if (!_onChainAutomatedPayoutSenderFactory.GetSupportedPaymentMethods().Any(i => id == i))
{
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Error,
Message = $"This processor cannot handle {cryptoCode}."
});
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors");
}
var activeProcessor =
(await _payoutProcessorService.GetProcessors(
new PayoutProcessorService.PayoutProcessorQuery()
{
Stores = new[] { storeId },
Processors = new[] { OnChainAutomatedPayoutSenderFactory.ProcessorName },
PaymentMethods = new[]
{
PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode)
}
}))
.FirstOrDefault();
activeProcessor ??= new PayoutProcessorData();
activeProcessor.HasTypedBlob<OnChainAutomatedPayoutBlob>().SetBlob(automatedTransferBlob.ToBlob());
activeProcessor.StoreId = storeId;
activeProcessor.PaymentMethod = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode).ToString();
activeProcessor.Processor = _onChainAutomatedPayoutSenderFactory.Processor;
var tcs = new TaskCompletionSource();
_eventAggregator.Publish(new PayoutProcessorUpdated()
{
Data = activeProcessor,
Id = activeProcessor.Id,
Processed = tcs
});
TempData.SetStatusMessageModel(new StatusMessageModel
{
Severity = StatusMessageModel.StatusSeverity.Success,
Message = "Processor updated."
});
await tcs.Task;
return RedirectToAction("ConfigureStorePayoutProcessors", "UiPayoutProcessors", new { storeId });
}
public class OnChainTransferViewModel
{
public OnChainTransferViewModel()
{
}
public OnChainTransferViewModel(OnChainAutomatedPayoutBlob blob)
{
ProcessNewPayoutsInstantly = blob.ProcessNewPayoutsInstantly;
IntervalMinutes = blob.Interval.TotalMinutes;
FeeTargetBlock = blob.FeeTargetBlock;
Threshold = blob.Threshold;
}
public bool ProcessNewPayoutsInstantly { get; set; }
[Range(1, 1000)]
public int FeeTargetBlock { get; set; }
public decimal Threshold { get; set; }
[Range(AutomatedPayoutConstants.MinIntervalMinutes, AutomatedPayoutConstants.MaxIntervalMinutes)]
public double IntervalMinutes { get; set; }
public OnChainAutomatedPayoutBlob ToBlob()
{
return new OnChainAutomatedPayoutBlob
{
ProcessNewPayoutsInstantly = ProcessNewPayoutsInstantly,
FeeTargetBlock = FeeTargetBlock,
Interval = TimeSpan.FromMinutes(IntervalMinutes),
Threshold = Threshold
};
}
}
}