- Coin: Zano → Lethean, ticker: ZAN/ZANO → LTHN - Ports: 11211 → 36941 (mainnet RPC), 46941 (testnet RPC) - Wallet: 11212 → 36944/46944 - Address prefix: iTHN - URLs: zano.org → lethean.io - Explorer links: explorer.lthn.io Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
180 lines
8.3 KiB
C#
180 lines
8.3 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.Client;
|
|
using BTCPayServer.Data;
|
|
using BTCPayServer.Payments;
|
|
using BTCPayServer.Plugins.Lethean.Configuration;
|
|
using BTCPayServer.Plugins.Lethean.Payments;
|
|
using BTCPayServer.Plugins.Lethean.Services;
|
|
using BTCPayServer.Services.Invoices;
|
|
using BTCPayServer.Services.Stores;
|
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Localization;
|
|
|
|
namespace BTCPayServer.Plugins.Lethean.Controllers
|
|
{
|
|
[Route("stores/{storeId}/lethean")]
|
|
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
|
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
|
[Authorize(Policy = Policies.CanModifyServerSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
|
public class UILetheanStoreController : Controller
|
|
{
|
|
private readonly LetheanConfiguration _letheanConfiguration;
|
|
private readonly StoreRepository _storeRepository;
|
|
private readonly LetheanRpcProvider _letheanRpcProvider;
|
|
private readonly PaymentMethodHandlerDictionary _handlers;
|
|
private IStringLocalizer StringLocalizer { get; }
|
|
|
|
public UILetheanStoreController(LetheanConfiguration letheanConfiguration,
|
|
StoreRepository storeRepository, LetheanRpcProvider letheanRpcProvider,
|
|
PaymentMethodHandlerDictionary handlers,
|
|
IStringLocalizer stringLocalizer)
|
|
{
|
|
_letheanConfiguration = letheanConfiguration;
|
|
_storeRepository = storeRepository;
|
|
_letheanRpcProvider = letheanRpcProvider;
|
|
_handlers = handlers;
|
|
StringLocalizer = stringLocalizer;
|
|
}
|
|
|
|
public StoreData StoreData => HttpContext.GetStoreData();
|
|
|
|
private LetheanPaymentMethodViewModel GetLetheanPaymentMethodViewModel(
|
|
StoreData storeData, string cryptoCode,
|
|
IPaymentFilter excludeFilters)
|
|
{
|
|
var lethean = storeData.GetPaymentMethodConfigs(_handlers)
|
|
.Where(s => s.Value is LetheanPaymentPromptDetails)
|
|
.Select(s => (PaymentMethodId: s.Key, Details: (LetheanPaymentPromptDetails)s.Value));
|
|
var pmi = PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode);
|
|
var settings = lethean.Where(method => method.PaymentMethodId == pmi).Select(m => m.Details).SingleOrDefault();
|
|
_letheanRpcProvider.Summaries.TryGetValue(cryptoCode, out var summary);
|
|
|
|
var settlementThresholdChoice = LetheanSettlementThresholdChoice.StoreSpeedPolicy;
|
|
if (settings != null && settings.InvoiceSettledConfirmationThreshold is { } confirmations)
|
|
{
|
|
settlementThresholdChoice = confirmations switch
|
|
{
|
|
0 => LetheanSettlementThresholdChoice.ZeroConfirmation,
|
|
1 => LetheanSettlementThresholdChoice.AtLeastOne,
|
|
10 => LetheanSettlementThresholdChoice.AtLeastTen,
|
|
_ => LetheanSettlementThresholdChoice.Custom
|
|
};
|
|
}
|
|
|
|
return new LetheanPaymentMethodViewModel()
|
|
{
|
|
Enabled =
|
|
settings != null &&
|
|
!excludeFilters.Match(PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode)),
|
|
Summary = summary,
|
|
CryptoCode = cryptoCode,
|
|
SettlementConfirmationThresholdChoice = settlementThresholdChoice,
|
|
CustomSettlementConfirmationThreshold =
|
|
settings != null &&
|
|
settlementThresholdChoice is LetheanSettlementThresholdChoice.Custom
|
|
? settings.InvoiceSettledConfirmationThreshold
|
|
: null
|
|
};
|
|
}
|
|
|
|
[HttpGet("{cryptoCode}")]
|
|
public IActionResult GetStoreLetheanPaymentMethod(string cryptoCode)
|
|
{
|
|
cryptoCode = cryptoCode.ToUpperInvariant();
|
|
if (!_letheanConfiguration.LetheanConfigurationItems.ContainsKey(cryptoCode))
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var vm = GetLetheanPaymentMethodViewModel(StoreData, cryptoCode,
|
|
StoreData.GetStoreBlob().GetExcludedPaymentMethods());
|
|
return View("/Views/Lethean/GetStoreLetheanPaymentMethod.cshtml", vm);
|
|
}
|
|
|
|
[HttpPost("{cryptoCode}")]
|
|
[DisableRequestSizeLimit]
|
|
public async Task<IActionResult> GetStoreLetheanPaymentMethod(LetheanPaymentMethodViewModel viewModel, string cryptoCode)
|
|
{
|
|
cryptoCode = cryptoCode.ToUpperInvariant();
|
|
if (!_letheanConfiguration.LetheanConfigurationItems.TryGetValue(cryptoCode,
|
|
out _))
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
if (!ModelState.IsValid)
|
|
{
|
|
var vm = GetLetheanPaymentMethodViewModel(StoreData, cryptoCode,
|
|
StoreData.GetStoreBlob().GetExcludedPaymentMethods());
|
|
vm.Enabled = viewModel.Enabled;
|
|
vm.SettlementConfirmationThresholdChoice = viewModel.SettlementConfirmationThresholdChoice;
|
|
vm.CustomSettlementConfirmationThreshold = viewModel.CustomSettlementConfirmationThreshold;
|
|
return View("/Views/Lethean/GetStoreLetheanPaymentMethod.cshtml", vm);
|
|
}
|
|
|
|
var storeData = StoreData;
|
|
var blob = storeData.GetStoreBlob();
|
|
storeData.SetPaymentMethodConfig(_handlers[PaymentTypes.CHAIN.GetPaymentMethodId(cryptoCode)], new LetheanPaymentPromptDetails()
|
|
{
|
|
InvoiceSettledConfirmationThreshold = viewModel.SettlementConfirmationThresholdChoice switch
|
|
{
|
|
LetheanSettlementThresholdChoice.ZeroConfirmation => 0,
|
|
LetheanSettlementThresholdChoice.AtLeastOne => 1,
|
|
LetheanSettlementThresholdChoice.AtLeastTen => 10,
|
|
LetheanSettlementThresholdChoice.Custom when viewModel.CustomSettlementConfirmationThreshold is { } custom => custom,
|
|
_ => null
|
|
}
|
|
});
|
|
|
|
blob.SetExcluded(PaymentTypes.CHAIN.GetPaymentMethodId(viewModel.CryptoCode), !viewModel.Enabled);
|
|
storeData.SetStoreBlob(blob);
|
|
await _storeRepository.UpdateStore(storeData);
|
|
return RedirectToAction("GetStoreLetheanPaymentMethod", new { cryptoCode });
|
|
}
|
|
|
|
public class LetheanPaymentMethodViewModel : IValidatableObject
|
|
{
|
|
public LetheanRpcProvider.LetheanSummary Summary { get; set; }
|
|
public string CryptoCode { get; set; }
|
|
public bool Enabled { get; set; }
|
|
|
|
[Display(Name = "Consider the invoice settled when the payment transaction ...")]
|
|
public LetheanSettlementThresholdChoice SettlementConfirmationThresholdChoice { get; set; }
|
|
[Display(Name = "Required Confirmations"), Range(0, 100)]
|
|
public long? CustomSettlementConfirmationThreshold { get; set; }
|
|
|
|
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
|
{
|
|
if (SettlementConfirmationThresholdChoice is LetheanSettlementThresholdChoice.Custom
|
|
&& CustomSettlementConfirmationThreshold is null)
|
|
{
|
|
yield return new ValidationResult(
|
|
"You must specify the number of required confirmations when using a custom threshold.",
|
|
new[] { nameof(CustomSettlementConfirmationThreshold) });
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum LetheanSettlementThresholdChoice
|
|
{
|
|
[Display(Name = "Store Speed Policy", Description = "Use the store's speed policy")]
|
|
StoreSpeedPolicy,
|
|
[Display(Name = "Zero Confirmation", Description = "Is unconfirmed")]
|
|
ZeroConfirmation,
|
|
[Display(Name = "At Least One", Description = "Has at least 1 confirmation")]
|
|
AtLeastOne,
|
|
[Display(Name = "At Least Ten", Description = "Has at least 10 confirmations")]
|
|
AtLeastTen,
|
|
[Display(Name = "Custom", Description = "Custom")]
|
|
Custom
|
|
}
|
|
}
|
|
}
|