mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
The Big Cleanup: Refactor BTCPay internals (#5809)
This commit is contained in:
@@ -11,6 +11,7 @@ using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Security.Greenfield;
|
||||
using BTCPayServer.Services;
|
||||
@@ -21,7 +22,9 @@ using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using CreateInvoiceRequest = BTCPayServer.Client.Models.CreateInvoiceRequest;
|
||||
using InvoiceData = BTCPayServer.Client.Models.InvoiceData;
|
||||
|
||||
@@ -42,6 +45,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
private readonly InvoiceActivator _invoiceActivator;
|
||||
private readonly ApplicationDbContextFactory _dbContextFactory;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly Dictionary<PaymentMethodId, IPaymentLinkExtension> _paymentLinkExtensions;
|
||||
private readonly PaymentMethodHandlerDictionary _handlers;
|
||||
|
||||
public LanguageService LanguageService { get; }
|
||||
|
||||
@@ -51,7 +56,9 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
InvoiceActivator invoiceActivator,
|
||||
PullPaymentHostedService pullPaymentService,
|
||||
ApplicationDbContextFactory dbContextFactory,
|
||||
IAuthorizationService authorizationService)
|
||||
IAuthorizationService authorizationService,
|
||||
Dictionary<PaymentMethodId, IPaymentLinkExtension> paymentLinkExtensions,
|
||||
PaymentMethodHandlerDictionary handlers)
|
||||
{
|
||||
_invoiceController = invoiceController;
|
||||
_invoiceRepository = invoiceRepository;
|
||||
@@ -63,6 +70,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
_pullPaymentService = pullPaymentService;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_authorizationService = authorizationService;
|
||||
_paymentLinkExtensions = paymentLinkExtensions;
|
||||
_handlers = handlers;
|
||||
LanguageService = languageService;
|
||||
}
|
||||
|
||||
@@ -347,7 +356,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
if (PaymentMethodId.TryParse(paymentMethod, out var paymentMethodId))
|
||||
{
|
||||
await _invoiceActivator.ActivateInvoicePaymentMethod(paymentMethodId, invoice, store);
|
||||
await _invoiceActivator.ActivateInvoicePaymentMethod(invoiceId, paymentMethodId);
|
||||
return Ok();
|
||||
}
|
||||
ModelState.AddModelError(nameof(paymentMethod), "Invalid payment method");
|
||||
@@ -384,33 +393,31 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
{
|
||||
return this.CreateAPIError("non-refundable", "Cannot refund this invoice");
|
||||
}
|
||||
PaymentMethod? invoicePaymentMethod = null;
|
||||
PaymentPrompt? paymentPrompt = null;
|
||||
PaymentMethodId? paymentMethodId = null;
|
||||
if (request.PaymentMethod is not null && PaymentMethodId.TryParse(request.PaymentMethod, out paymentMethodId))
|
||||
{
|
||||
invoicePaymentMethod = invoice.GetPaymentMethods().SingleOrDefault(method => method.GetId() == paymentMethodId);
|
||||
paymentPrompt = invoice.GetPaymentPrompt(paymentMethodId);
|
||||
}
|
||||
if (invoicePaymentMethod is null)
|
||||
if (paymentPrompt is null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(request.PaymentMethod), "Please select one of the payment methods which were available for the original invoice");
|
||||
}
|
||||
if (request.RefundVariant is null)
|
||||
ModelState.AddModelError(nameof(request.RefundVariant), "`refundVariant` is mandatory");
|
||||
if (!ModelState.IsValid || invoicePaymentMethod is null || paymentMethodId is null)
|
||||
if (!ModelState.IsValid || paymentPrompt is null || paymentMethodId is null)
|
||||
return this.CreateValidationError(ModelState);
|
||||
|
||||
var accounting = invoicePaymentMethod.Calculate();
|
||||
var accounting = paymentPrompt.Calculate();
|
||||
var cryptoPaid = accounting.Paid;
|
||||
var cdCurrency = _currencyNameTable.GetCurrencyData(invoice.Currency, true);
|
||||
var paidCurrency = Math.Round(cryptoPaid * invoicePaymentMethod.Rate, cdCurrency.Divisibility);
|
||||
var paidCurrency = Math.Round(cryptoPaid * paymentPrompt.Rate, cdCurrency.Divisibility);
|
||||
var rateResult = await _rateProvider.FetchRate(
|
||||
new CurrencyPair(paymentMethodId.CryptoCode, invoice.Currency),
|
||||
new CurrencyPair(paymentPrompt.Currency, invoice.Currency),
|
||||
store.GetStoreBlob().GetRateRules(_networkProvider),
|
||||
cancellationToken
|
||||
);
|
||||
var cryptoCode = invoicePaymentMethod.GetId().CryptoCode;
|
||||
var paymentMethodDivisibility = _currencyNameTable.GetCurrencyData(paymentMethodId.CryptoCode, false)?.Divisibility ?? 8;
|
||||
var paidAmount = cryptoPaid.RoundToSignificant(paymentMethodDivisibility);
|
||||
var paidAmount = cryptoPaid.RoundToSignificant(paymentPrompt.Divisibility);
|
||||
var createPullPayment = new CreatePullPayment
|
||||
{
|
||||
BOLT11Expiration = store.GetStoreBlob().RefundBOLT11Expiration,
|
||||
@@ -436,17 +443,17 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
var appliedDivisibility = paymentMethodDivisibility;
|
||||
var appliedDivisibility = paymentPrompt.Divisibility;
|
||||
switch (request.RefundVariant)
|
||||
{
|
||||
case RefundVariant.RateThen:
|
||||
createPullPayment.Currency = cryptoCode;
|
||||
createPullPayment.Currency = paymentPrompt.Currency;
|
||||
createPullPayment.Amount = paidAmount;
|
||||
createPullPayment.AutoApproveClaims = true;
|
||||
break;
|
||||
|
||||
case RefundVariant.CurrentRate:
|
||||
createPullPayment.Currency = cryptoCode;
|
||||
createPullPayment.Currency = paymentPrompt.Currency;
|
||||
createPullPayment.Amount = Math.Round(paidCurrency / rateResult.BidAsk.Bid, appliedDivisibility);
|
||||
createPullPayment.AutoApproveClaims = true;
|
||||
break;
|
||||
@@ -469,7 +476,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
}
|
||||
|
||||
var dueAmount = accounting.TotalDue;
|
||||
createPullPayment.Currency = cryptoCode;
|
||||
createPullPayment.Currency = paymentPrompt.Currency;
|
||||
createPullPayment.Amount = Math.Round(paidAmount - dueAmount, appliedDivisibility);
|
||||
createPullPayment.AutoApproveClaims = true;
|
||||
break;
|
||||
@@ -501,7 +508,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
createPullPayment.Currency = request.CustomCurrency;
|
||||
createPullPayment.Amount = request.CustomAmount.Value;
|
||||
createPullPayment.AutoApproveClaims = paymentMethodId.CryptoCode == request.CustomCurrency;
|
||||
createPullPayment.AutoApproveClaims = paymentPrompt.Currency == request.CustomCurrency;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -569,49 +576,52 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
private InvoicePaymentMethodDataModel[] ToPaymentMethodModels(InvoiceEntity entity, bool includeAccountedPaymentOnly)
|
||||
{
|
||||
return entity.GetPaymentMethods().Select(
|
||||
method =>
|
||||
return entity.GetPaymentPrompts().Select(
|
||||
prompt =>
|
||||
{
|
||||
var accounting = method.Calculate();
|
||||
var details = method.GetPaymentMethodDetails();
|
||||
var payments = method.ParentEntity.GetPayments(includeAccountedPaymentOnly).Where(paymentEntity =>
|
||||
paymentEntity.GetPaymentMethodId() == method.GetId());
|
||||
_handlers.TryGetValue(prompt.PaymentMethodId, out var handler);
|
||||
var accounting = prompt.Currency is not null ? prompt.Calculate() : null;
|
||||
var payments = prompt.ParentEntity.GetPayments(includeAccountedPaymentOnly).Where(paymentEntity =>
|
||||
paymentEntity.PaymentMethodId == prompt.PaymentMethodId);
|
||||
_paymentLinkExtensions.TryGetValue(prompt.PaymentMethodId, out var paymentLinkExtension);
|
||||
|
||||
var details = prompt.Details;
|
||||
if (handler is not null && prompt.Activated)
|
||||
details = JToken.FromObject(handler.ParsePaymentPromptDetails(details), handler.Serializer.ForAPI());
|
||||
return new InvoicePaymentMethodDataModel
|
||||
{
|
||||
Activated = details.Activated,
|
||||
PaymentMethod = method.GetId().ToStringNormalized(),
|
||||
CryptoCode = method.GetId().CryptoCode,
|
||||
Destination = details.GetPaymentDestination(),
|
||||
Rate = method.Rate,
|
||||
Due = accounting.DueUncapped,
|
||||
TotalPaid = accounting.Paid,
|
||||
PaymentMethodPaid = accounting.CryptoPaid,
|
||||
Amount = accounting.TotalDue,
|
||||
NetworkFee = accounting.NetworkFee,
|
||||
PaymentLink =
|
||||
method.GetId().PaymentType.GetPaymentLink(method.Network, entity, details, accounting.Due,
|
||||
Request.GetAbsoluteRoot()),
|
||||
Activated = prompt.Activated,
|
||||
PaymentMethodId = prompt.PaymentMethodId.ToString(),
|
||||
Currency = prompt.Currency,
|
||||
Destination = prompt.Destination,
|
||||
Rate = prompt.Currency is not null ? prompt.Rate : 0m,
|
||||
Due = accounting?.DueUncapped ?? 0m,
|
||||
TotalPaid = accounting?.Paid ?? 0m,
|
||||
PaymentMethodPaid = accounting?.PaymentMethodPaid ?? 0m,
|
||||
Amount = accounting?.TotalDue ?? 0m,
|
||||
PaymentMethodFee = accounting?.PaymentMethodFee ?? 0m,
|
||||
PaymentLink = (prompt.Activated ? paymentLinkExtension?.GetPaymentLink(prompt, Url) : null) ?? string.Empty,
|
||||
Payments = payments.Select(paymentEntity => ToPaymentModel(entity, paymentEntity)).ToList(),
|
||||
AdditionalData = details.GetAdditionalData()
|
||||
AdditionalData = prompt.Details
|
||||
};
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
public static InvoicePaymentMethodDataModel.Payment ToPaymentModel(InvoiceEntity entity, PaymentEntity paymentEntity)
|
||||
{
|
||||
var data = paymentEntity.GetCryptoPaymentData();
|
||||
return new InvoicePaymentMethodDataModel.Payment()
|
||||
{
|
||||
Destination = data.GetDestination(),
|
||||
Id = data.GetPaymentId(),
|
||||
Status = !paymentEntity.Accounted
|
||||
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Invalid
|
||||
: data.PaymentConfirmed(paymentEntity, entity.SpeedPolicy) || data.PaymentCompleted(paymentEntity)
|
||||
? InvoicePaymentMethodDataModel.Payment.PaymentStatus.Settled
|
||||
: InvoicePaymentMethodDataModel.Payment.PaymentStatus.Processing,
|
||||
Fee = paymentEntity.NetworkFee,
|
||||
Value = data.GetValue(),
|
||||
Destination = paymentEntity.Destination,
|
||||
Id = paymentEntity.Id,
|
||||
Status = paymentEntity.Status switch
|
||||
{
|
||||
PaymentStatus.Processing => InvoicePaymentMethodDataModel.Payment.PaymentStatus.Processing,
|
||||
PaymentStatus.Settled => InvoicePaymentMethodDataModel.Payment.PaymentStatus.Settled,
|
||||
PaymentStatus.Unaccounted => InvoicePaymentMethodDataModel.Payment.PaymentStatus.Invalid,
|
||||
_ => throw new NotSupportedException(paymentEntity.Status.ToString())
|
||||
},
|
||||
Fee = paymentEntity.PaymentMethodFee,
|
||||
Value = paymentEntity.Value,
|
||||
ReceivedDate = paymentEntity.ReceivedTime.DateTime
|
||||
};
|
||||
}
|
||||
@@ -656,8 +666,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
Monitoring = entity.MonitoringExpiration - entity.ExpirationTime,
|
||||
PaymentTolerance = entity.PaymentTolerance,
|
||||
PaymentMethods =
|
||||
entity.GetPaymentMethods().Select(method => method.GetId().ToStringNormalized()).ToArray(),
|
||||
DefaultPaymentMethod = entity.DefaultPaymentMethod,
|
||||
entity.GetPaymentPrompts().Select(method => method.PaymentMethodId.ToString()).ToArray(),
|
||||
DefaultPaymentMethod = entity.DefaultPaymentMethod?.ToString(),
|
||||
SpeedPolicy = entity.SpeedPolicy,
|
||||
DefaultLanguage = entity.DefaultLanguage,
|
||||
RedirectAutomatically = entity.RedirectAutomatically,
|
||||
|
||||
Reference in New Issue
Block a user