btcpay-plugin/Plugins/Lethean/Controllers/UILetheanStoreController.cs
Claude a3869db496
rebrand(lethean): update branding, ports, and config for Lethean blockchain
- 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>
2026-04-01 22:24:13 +01:00

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
}
}
}