btcpayserver/BTCPayServer/PaymentRequest/PaymentRequestService.cs
Andrew Camilleri 022285806b
Form Builder (#4137)
* wip

* Cleanups

* UI updates

* Update UIFormsController.cs

* Make predefined forms usable statically

* Add support for pos app + forms

* pay request form rough support

* invoice form through receipt page

* Display form name in inherit from store setting

* Do not request additional forms on invoice from pay request

* fix up code

* move checkoutform id in checkout appearance outside of checkotu v2 toggle

* general fixes for form system

* fix pav bug

* UI updates

* Fix warnings in Form builder (#4331)

* Fix build warnings about string?

Enable nullable on UIFormsController.cs
Fixes CS8632 The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

* Clean up lack of space in injected services in Submit() of UIFormsController.cs

* Remove unused variables (CS0219) and assignment of nullable value to nullable type (CS8600)

* Cleanup double semicolons while we're at tit

* Fix: If reverse proxy wasn't well configured, and error message should have been displayed (#4322)

* fix monero issue

* Server Settings: Update Policies page (#4326)

Handles the multiple submit buttons on that page and closes #4319.

Contains some UI unifications with other pages and also shows the block explorers without needing to toggle the section via JS.

* Change confirmed to settled. (#4328)

* POS: Fix null pointer

Introduced in #4307, the referenced object needs to be `itemChoice` instead of `choice`.

* Add documentation link to plugins (#4329)

* Add documentation link to plugins

* Minor UI updates

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>

* Fix flaky test (#4330)

* Fix flaky test

* Update BTCPayServer/PayoutProcessors/BaseAutomatedPayoutProcessor.cs

Co-authored-by: d11n <mail@dennisreimann.de>

Co-authored-by: d11n <mail@dennisreimann.de>

* Remove invoice and store level form

* add form test

* fix migration for forms

* fix

* make pay request form submission redirect to invoice

* Refactor FormQuery to only be able to query single store and single form

* Put the Authorize at controller level on UIForms

* Fix warnings

* Fix ef request

* Fix query to forms, ensure no permission bypass

* Fix modify

* Remove storeId from step form

* Remove useless storeId parameter

* Hide custom form feature in UI

* Minor cleanups

* Remove custom form options from select for now

* More minor syntax cleanups

* Update test

* Add index - needs migration

* Refactoring: Use PostRedirect instead of TempData for data transfer

* Remove untested and unfinished code

* formResponse should be a JObject, not a string

* Fix case for Form type

Co-authored-by: Dennis Reimann <mail@dennisreimann.de>
Co-authored-by: JesterHodl <103882255+jesterhodl@users.noreply.github.com>
Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
Co-authored-by: Andreas Tasch <andy.tasch@gmail.com>
2022-11-25 10:42:55 +09:00

171 lines
7.7 KiB
C#

using System;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Models.PaymentRequestViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.PaymentRequests;
using BTCPayServer.Services.Rates;
using Microsoft.AspNetCore.SignalR;
using PaymentRequestData = BTCPayServer.Data.PaymentRequestData;
namespace BTCPayServer.PaymentRequest
{
public class PaymentRequestService
{
private readonly PaymentRequestRepository _PaymentRequestRepository;
private readonly BTCPayNetworkProvider _BtcPayNetworkProvider;
private readonly AppService _AppService;
private readonly CurrencyNameTable _currencies;
public PaymentRequestService(
PaymentRequestRepository paymentRequestRepository,
BTCPayNetworkProvider btcPayNetworkProvider,
AppService appService,
CurrencyNameTable currencies)
{
_PaymentRequestRepository = paymentRequestRepository;
_BtcPayNetworkProvider = btcPayNetworkProvider;
_AppService = appService;
_currencies = currencies;
}
public async Task UpdatePaymentRequestStateIfNeeded(string id)
{
var pr = await _PaymentRequestRepository.FindPaymentRequest(id, null);
await UpdatePaymentRequestStateIfNeeded(pr);
}
public async Task UpdatePaymentRequestStateIfNeeded(PaymentRequestData pr)
{
var blob = pr.GetBlob();
var currentStatus = pr.Status;
if (blob.ExpiryDate.HasValue)
{
if (blob.ExpiryDate.Value <= DateTimeOffset.UtcNow)
currentStatus = Client.Models.PaymentRequestData.PaymentRequestStatus.Expired;
}
else if (currentStatus != Client.Models.PaymentRequestData.PaymentRequestStatus.Completed)
{
currentStatus = Client.Models.PaymentRequestData.PaymentRequestStatus.Pending;
}
if (currentStatus != Client.Models.PaymentRequestData.PaymentRequestStatus.Expired)
{
var invoices = await _PaymentRequestRepository.GetInvoicesForPaymentRequest(pr.Id);
var contributions = _AppService.GetContributionsByPaymentMethodId(blob.Currency, invoices, true);
currentStatus = contributions.TotalCurrency >= blob.Amount
? Client.Models.PaymentRequestData.PaymentRequestStatus.Completed
: Client.Models.PaymentRequestData.PaymentRequestStatus.Pending;
}
if (currentStatus != pr.Status)
{
pr.Status = currentStatus;
await _PaymentRequestRepository.UpdatePaymentRequestStatus(pr.Id, currentStatus);
}
}
public async Task<ViewPaymentRequestViewModel> GetPaymentRequest(string id, string userId = null)
{
var pr = await _PaymentRequestRepository.FindPaymentRequest(id, null);
if (pr == null)
{
return null;
}
var blob = pr.GetBlob();
var invoices = await _PaymentRequestRepository.GetInvoicesForPaymentRequest(id);
var paymentStats = _AppService.GetContributionsByPaymentMethodId(blob.Currency, invoices, true);
var amountDue = blob.Amount - paymentStats.TotalCurrency;
var pendingInvoice = invoices.OrderByDescending(entity => entity.InvoiceTime)
.FirstOrDefault(entity => entity.Status == InvoiceStatusLegacy.New);
return new ViewPaymentRequestViewModel(pr)
{
Archived = pr.Archived,
AmountFormatted = _currencies.FormatCurrency(blob.Amount, blob.Currency),
AmountCollected = paymentStats.TotalCurrency,
AmountCollectedFormatted = _currencies.FormatCurrency(paymentStats.TotalCurrency, blob.Currency),
AmountDue = amountDue,
AmountDueFormatted = _currencies.FormatCurrency(amountDue, blob.Currency),
CurrencyData = _currencies.GetCurrencyData(blob.Currency, true),
LastUpdated = DateTime.UtcNow,
FormId = blob.FormId,
FormSubmitted = blob.FormResponse is not null,
AnyPendingInvoice = pendingInvoice != null,
PendingInvoiceHasPayments = pendingInvoice != null &&
pendingInvoice.ExceptionStatus != InvoiceExceptionStatus.None,
Invoices = new ViewPaymentRequestViewModel.InvoiceList(invoices.Select(entity =>
{
var state = entity.GetInvoiceState();
var payments = entity
.GetPayments(true)
.Select(paymentEntity =>
{
var paymentData = paymentEntity.GetCryptoPaymentData();
var paymentMethodId = paymentEntity.GetPaymentMethodId();
if (paymentData is null || paymentMethodId is null)
{
return null;
}
string txId = paymentData.GetPaymentId();
string link = GetTransactionLink(paymentMethodId, txId);
var paymentMethod = entity.GetPaymentMethod(paymentMethodId);
var amount = paymentData.GetValue();
var rate = paymentMethod.Rate;
var paid = (amount - paymentEntity.NetworkFee) * rate;
return new ViewPaymentRequestViewModel.PaymentRequestInvoicePayment
{
Amount = amount,
Paid = paid,
ReceivedDate = paymentEntity.ReceivedTime.DateTime,
PaidFormatted = _currencies.FormatCurrency(paid, blob.Currency),
RateFormatted = _currencies.FormatCurrency(rate, blob.Currency),
PaymentMethod = paymentMethodId.ToPrettyString(),
Link = link,
Id = txId,
Destination = paymentData.GetDestination()
};
})
.Where(payment => payment != null)
.ToList();
if (state.Status == InvoiceStatusLegacy.Invalid ||
state.Status == InvoiceStatusLegacy.Expired && !payments.Any())
return null;
return new ViewPaymentRequestViewModel.PaymentRequestInvoice
{
Id = entity.Id,
Amount = entity.Price,
AmountFormatted = _currencies.FormatCurrency(entity.Price, blob.Currency),
Currency = entity.Currency,
ExpiryDate = entity.ExpirationTime.DateTime,
State = state,
StateFormatted = state.ToString(),
Payments = payments
};
})
.Where(invoice => invoice != null))
};
}
private string GetTransactionLink(PaymentMethodId paymentMethodId, string txId)
{
var network = _BtcPayNetworkProvider.GetNetwork(paymentMethodId.CryptoCode);
if (network == null)
return null;
return paymentMethodId.PaymentType.GetTransactionLink(network, txId);
}
}
}