2020-06-29 04:44:35 +02:00
using System ;
2021-04-13 10:36:49 +02:00
using System.Collections.Generic ;
2020-06-24 03:34:09 +02:00
using System.Globalization ;
using System.Linq ;
using System.Threading.Tasks ;
2020-11-17 13:46:23 +01:00
using BTCPayServer.Abstractions.Extensions ;
using BTCPayServer.Abstractions.Models ;
2021-04-13 10:36:49 +02:00
using BTCPayServer.Client.Models ;
2020-06-24 03:34:09 +02:00
using BTCPayServer.Data ;
using BTCPayServer.HostedServices ;
using BTCPayServer.Models ;
using BTCPayServer.Payments ;
using BTCPayServer.Services ;
using BTCPayServer.Services.Rates ;
2020-06-24 06:44:26 +02:00
using Microsoft.AspNetCore.Authorization ;
2020-06-24 03:34:09 +02:00
using Microsoft.AspNetCore.Mvc ;
using Microsoft.EntityFrameworkCore ;
namespace BTCPayServer.Controllers
{
2020-06-24 06:44:26 +02:00
[AllowAnonymous]
2020-06-24 03:34:09 +02:00
public class PullPaymentController : Controller
{
private readonly ApplicationDbContextFactory _dbContextFactory ;
private readonly BTCPayNetworkProvider _networkProvider ;
private readonly CurrencyNameTable _currencyNameTable ;
private readonly PullPaymentHostedService _pullPaymentHostedService ;
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings ;
2021-04-13 10:36:49 +02:00
private readonly IEnumerable < IPayoutHandler > _payoutHandlers ;
2020-06-24 03:34:09 +02:00
public PullPaymentController ( ApplicationDbContextFactory dbContextFactory ,
BTCPayNetworkProvider networkProvider ,
CurrencyNameTable currencyNameTable ,
PullPaymentHostedService pullPaymentHostedService ,
2021-04-13 10:36:49 +02:00
BTCPayNetworkJsonSerializerSettings serializerSettings ,
IEnumerable < IPayoutHandler > payoutHandlers )
2020-06-24 03:34:09 +02:00
{
_dbContextFactory = dbContextFactory ;
_networkProvider = networkProvider ;
_currencyNameTable = currencyNameTable ;
_pullPaymentHostedService = pullPaymentHostedService ;
_serializerSettings = serializerSettings ;
2021-04-13 10:36:49 +02:00
_payoutHandlers = payoutHandlers ;
2020-06-24 03:34:09 +02:00
}
2021-06-10 11:43:45 +02:00
2020-06-24 03:34:09 +02:00
[Route("pull-payments/{pullPaymentId}")]
public async Task < IActionResult > ViewPullPayment ( string pullPaymentId )
{
using var ctx = _dbContextFactory . CreateContext ( ) ;
var pp = await ctx . PullPayments . FindAsync ( pullPaymentId ) ;
if ( pp is null )
return NotFound ( ) ;
var blob = pp . GetBlob ( ) ;
var payouts = ( await ctx . Payouts . GetPayoutInPeriod ( pp )
. OrderByDescending ( o = > o . Date )
. ToListAsync ( ) )
. Select ( o = > new
{
Entity = o ,
Blob = o . GetBlob ( _serializerSettings ) ,
2021-10-18 08:00:38 +02:00
ProofBlob = _payoutHandlers . FindPayoutHandler ( o . GetPaymentMethodId ( ) ) ? . ParseProof ( o )
2020-06-24 03:34:09 +02:00
} ) ;
var cd = _currencyNameTable . GetCurrencyData ( blob . Currency , false ) ;
var totalPaid = payouts . Where ( p = > p . Entity . State ! = PayoutState . Cancelled ) . Select ( p = > p . Blob . Amount ) . Sum ( ) ;
var amountDue = blob . Limit - totalPaid ;
ViewPullPaymentModel vm = new ViewPullPaymentModel ( pp , DateTimeOffset . UtcNow )
{
AmountFormatted = _currencyNameTable . FormatCurrency ( blob . Limit , blob . Currency ) ,
AmountCollected = totalPaid ,
AmountCollectedFormatted = _currencyNameTable . FormatCurrency ( totalPaid , blob . Currency ) ,
AmountDue = amountDue ,
ClaimedAmount = amountDue ,
AmountDueFormatted = _currencyNameTable . FormatCurrency ( amountDue , blob . Currency ) ,
CurrencyData = cd ,
2021-03-07 23:51:50 +01:00
StartDate = pp . StartDate ,
LastRefreshed = DateTime . Now ,
2020-06-24 03:34:09 +02:00
Payouts = payouts
2020-10-13 09:58:46 +02:00
. Select ( entity = > new ViewPullPaymentModel . PayoutLine
2020-06-28 10:55:27 +02:00
{
Id = entity . Entity . Id ,
Amount = entity . Blob . Amount ,
AmountFormatted = _currencyNameTable . FormatCurrency ( entity . Blob . Amount , blob . Currency ) ,
Currency = blob . Currency ,
2021-04-13 10:36:49 +02:00
Status = entity . Entity . State ,
Destination = entity . Blob . Destination ,
2021-10-18 05:37:59 +02:00
PaymentMethod = PaymentMethodId . Parse ( entity . Entity . PaymentMethodId ) ,
2021-04-13 10:36:49 +02:00
Link = entity . ProofBlob ? . Link ,
TransactionId = entity . ProofBlob ? . Id
2020-06-28 10:55:27 +02:00
} ) . ToList ( )
2020-06-24 03:34:09 +02:00
} ;
vm . IsPending & = vm . AmountDue > 0.0 m ;
return View ( nameof ( ViewPullPayment ) , vm ) ;
}
[Route("pull-payments/{pullPaymentId}/claim")]
[HttpPost]
public async Task < IActionResult > ClaimPullPayment ( string pullPaymentId , ViewPullPaymentModel vm )
{
using var ctx = _dbContextFactory . CreateContext ( ) ;
var pp = await ctx . PullPayments . FindAsync ( pullPaymentId ) ;
if ( pp is null )
{
ModelState . AddModelError ( nameof ( pullPaymentId ) , "This pull payment does not exists" ) ;
}
2021-04-13 10:36:49 +02:00
2020-06-24 03:34:09 +02:00
var ppBlob = pp . GetBlob ( ) ;
2021-04-13 10:36:49 +02:00
2021-10-18 05:37:59 +02:00
var paymentMethodId = ppBlob . SupportedPaymentMethods . FirstOrDefault ( id = > vm . SelectedPaymentMethod = = id . ToString ( ) ) ;
2021-10-18 08:00:38 +02:00
var payoutHandler = paymentMethodId is null ? null : _payoutHandlers . FindPayoutHandler ( paymentMethodId ) ;
2021-10-18 05:37:59 +02:00
if ( payoutHandler is null )
2020-06-24 03:34:09 +02:00
{
2021-10-18 05:37:59 +02:00
ModelState . AddModelError ( nameof ( vm . SelectedPaymentMethod ) , $"Invalid destination with selected payment method" ) ;
return await ViewPullPayment ( pullPaymentId ) ;
}
var destination = await payoutHandler ? . ParseClaimDestination ( paymentMethodId , vm . Destination , true ) ;
if ( destination . destination is null )
{
ModelState . AddModelError ( nameof ( vm . Destination ) , destination . error ? ? "Invalid destination with selected payment method" ) ;
return await ViewPullPayment ( pullPaymentId ) ;
}
if ( vm . ClaimedAmount = = 0 )
{
ModelState . AddModelError ( nameof ( vm . ClaimedAmount ) ,
$"Amount is required" ) ;
}
else if ( vm . ClaimedAmount ! = 0 & & destination . destination . Amount ! = null & & vm . ClaimedAmount ! = destination . destination . Amount )
{
ModelState . AddModelError ( nameof ( vm . ClaimedAmount ) ,
$"Amount is implied in destination ({destination.destination.Amount}) that does not match the payout amount provided {vm.ClaimedAmount})" ) ;
2020-06-24 03:34:09 +02:00
}
if ( ! ModelState . IsValid )
{
return await ViewPullPayment ( pullPaymentId ) ;
}
var result = await _pullPaymentHostedService . Claim ( new ClaimRequest ( )
{
2021-10-18 05:37:59 +02:00
Destination = destination . destination ,
2020-06-24 03:34:09 +02:00
PullPaymentId = pullPaymentId ,
Value = vm . ClaimedAmount ,
2021-10-18 05:37:59 +02:00
PaymentMethodId = paymentMethodId
2020-06-24 03:34:09 +02:00
} ) ;
if ( result . Result ! = ClaimRequest . ClaimResult . Ok )
{
if ( result . Result = = ClaimRequest . ClaimResult . AmountTooLow )
{
ModelState . AddModelError ( nameof ( vm . ClaimedAmount ) , ClaimRequest . GetErrorMessage ( result . Result ) ) ;
}
else
{
ModelState . AddModelError ( string . Empty , ClaimRequest . GetErrorMessage ( result . Result ) ) ;
}
return await ViewPullPayment ( pullPaymentId ) ;
}
else
{
TempData . SetStatusMessageModel ( new StatusMessageModel ( )
{
2020-08-08 12:47:28 +02:00
Message = $"Your claim request of {_currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency)} to {vm.Destination} has been submitted and is awaiting approval." ,
2020-06-24 03:34:09 +02:00
Severity = StatusMessageModel . StatusSeverity . Success
} ) ;
}
return RedirectToAction ( nameof ( ViewPullPayment ) , new { pullPaymentId = pullPaymentId } ) ;
}
}
}