btcpayserver/BTCPayServer/Controllers/GreenField/GreenfieldPullPaymentController.cs

562 lines
26 KiB
C#
Raw Normal View History

#nullable enable
2020-06-28 21:44:35 -05:00
using System;
using System.Collections.Generic;
2020-06-24 10:34:09 +09:00
using System.Linq;
2020-06-24 13:44:26 +09:00
using System.Threading;
2020-06-24 10:34:09 +09:00
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
2020-06-24 10:34:09 +09:00
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.Payments;
using BTCPayServer.Services;
using BTCPayServer.Services.Rates;
using Microsoft.AspNetCore.Authorization;
2020-06-30 08:26:19 +02:00
using Microsoft.AspNetCore.Cors;
2020-06-24 10:34:09 +09:00
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest;
2020-06-24 10:34:09 +09:00
2022-01-14 13:05:23 +09:00
namespace BTCPayServer.Controllers.Greenfield
2020-06-24 10:34:09 +09:00
{
[ApiController]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
2020-06-30 08:26:19 +02:00
[EnableCors(CorsPolicies.All)]
2020-06-24 10:34:09 +09:00
public class GreenfieldPullPaymentController : ControllerBase
{
private readonly PullPaymentHostedService _pullPaymentService;
private readonly LinkGenerator _linkGenerator;
private readonly ApplicationDbContextFactory _dbContextFactory;
private readonly CurrencyNameTable _currencyNameTable;
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
2020-06-24 10:34:09 +09:00
public GreenfieldPullPaymentController(PullPaymentHostedService pullPaymentService,
LinkGenerator linkGenerator,
ApplicationDbContextFactory dbContextFactory,
CurrencyNameTable currencyNameTable,
Services.BTCPayNetworkJsonSerializerSettings serializerSettings,
IEnumerable<IPayoutHandler> payoutHandlers)
2020-06-24 10:34:09 +09:00
{
_pullPaymentService = pullPaymentService;
_linkGenerator = linkGenerator;
_dbContextFactory = dbContextFactory;
_currencyNameTable = currencyNameTable;
_serializerSettings = serializerSettings;
_payoutHandlers = payoutHandlers;
2020-06-24 10:34:09 +09:00
}
[HttpGet("~/api/v1/stores/{storeId}/pull-payments")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> GetPullPayments(string storeId, bool includeArchived = false)
{
using var ctx = _dbContextFactory.CreateContext();
var pps = await ctx.PullPayments
.Where(p => p.StoreId == storeId && (includeArchived || !p.Archived))
.OrderByDescending(p => p.StartDate)
.ToListAsync();
return Ok(pps.Select(CreatePullPaymentData).ToArray());
}
[HttpPost("~/api/v1/stores/{storeId}/pull-payments")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> CreatePullPayment(string storeId, CreatePullPaymentRequest request)
{
if (request is null)
{
ModelState.AddModelError(string.Empty, "Missing body");
return this.CreateValidationError(ModelState);
}
if (request.Amount <= 0.0m)
{
ModelState.AddModelError(nameof(request.Amount), "The amount should more than 0.");
}
if (request.Name is String name && name.Length > 50)
{
ModelState.AddModelError(nameof(request.Name), "The name should be maximum 50 characters.");
}
if (request.Currency is String currency)
{
2020-06-24 13:44:26 +09:00
request.Currency = currency.ToUpperInvariant().Trim();
if (_currencyNameTable.GetCurrencyData(request.Currency, false) is null)
2020-06-24 10:34:09 +09:00
{
2020-06-24 13:44:26 +09:00
ModelState.AddModelError(nameof(request.Currency), "Invalid currency");
2020-06-24 10:34:09 +09:00
}
}
else
{
ModelState.AddModelError(nameof(request.Currency), "This field is required");
}
if (request.ExpiresAt is DateTimeOffset expires && request.StartsAt is DateTimeOffset start && expires < start)
{
ModelState.AddModelError(nameof(request.ExpiresAt), $"expiresAt should be higher than startAt");
}
if (request.Period <= TimeSpan.Zero)
{
ModelState.AddModelError(nameof(request.Period), $"The period should be positive");
}
if (request.BOLT11Expiration <= TimeSpan.Zero)
{
ModelState.AddModelError(nameof(request.BOLT11Expiration), $"The BOLT11 expiration should be positive");
}
PaymentMethodId?[]? paymentMethods = null;
if (request.PaymentMethods is { } paymentMethodsStr)
2020-06-24 10:34:09 +09:00
{
paymentMethods = paymentMethodsStr.Select(s =>
2020-06-24 17:51:00 +09:00
{
PaymentMethodId.TryParse(s, out var pmi);
return pmi;
}).ToArray();
2021-12-31 16:59:02 +09:00
var supported = (await _payoutHandlers.GetSupportedPaymentMethods(HttpContext.GetStoreData())).ToArray();
for (int i = 0; i < paymentMethods.Length; i++)
{
if (!supported.Contains(paymentMethods[i]))
{
request.AddModelError(paymentRequest => paymentRequest.PaymentMethods[i], "Invalid or unsupported payment method", this);
}
}
2020-06-24 10:34:09 +09:00
}
else
{
ModelState.AddModelError(nameof(request.PaymentMethods), "This field is required");
}
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
var ppId = await _pullPaymentService.CreatePullPayment(new HostedServices.CreatePullPayment()
{
StartsAt = request.StartsAt,
ExpiresAt = request.ExpiresAt,
Period = request.Period,
BOLT11Expiration = request.BOLT11Expiration,
2020-06-24 10:34:09 +09:00
Name = request.Name,
Description = request.Description,
2020-06-24 10:34:09 +09:00
Amount = request.Amount,
2020-06-24 13:44:26 +09:00
Currency = request.Currency,
2020-06-24 10:34:09 +09:00
StoreId = storeId,
PaymentMethodIds = paymentMethods,
AutoApproveClaims = request.AutoApproveClaims
2020-06-24 10:34:09 +09:00
});
var pp = await _pullPaymentService.GetPullPayment(ppId, false);
2020-06-24 10:34:09 +09:00
return this.Ok(CreatePullPaymentData(pp));
}
private Client.Models.PullPaymentData CreatePullPaymentData(Data.PullPaymentData pp)
{
var ppBlob = pp.GetBlob();
return new BTCPayServer.Client.Models.PullPaymentData()
{
Id = pp.Id,
StartsAt = pp.StartDate,
ExpiresAt = pp.EndDate,
Amount = ppBlob.Limit,
Name = ppBlob.Name,
Description = ppBlob.Description,
2020-06-24 10:34:09 +09:00
Currency = ppBlob.Currency,
Period = ppBlob.Period,
Archived = pp.Archived,
AutoApproveClaims = ppBlob.AutoApproveClaims,
BOLT11Expiration = ppBlob.BOLT11Expiration,
2020-06-24 10:34:09 +09:00
ViewLink = _linkGenerator.GetUriByAction(
2022-01-07 12:32:00 +09:00
nameof(UIPullPaymentController.ViewPullPayment),
"UIPullPayment",
2020-06-24 10:34:09 +09:00
new { pullPaymentId = pp.Id },
Request.Scheme,
Request.Host,
Request.PathBase)
};
}
[HttpGet("~/api/v1/pull-payments/{pullPaymentId}")]
[AllowAnonymous]
public async Task<IActionResult> GetPullPayment(string pullPaymentId)
{
if (pullPaymentId is null)
return PullPaymentNotFound();
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, false);
2020-06-24 10:34:09 +09:00
if (pp is null)
return PullPaymentNotFound();
2020-06-24 10:34:09 +09:00
return Ok(CreatePullPaymentData(pp));
}
private PayoutState[]? GetStateFilter(bool includeCancelled) =>
includeCancelled
? null
: new[]
{
PayoutState.Completed, PayoutState.AwaitingApproval, PayoutState.AwaitingPayment,
PayoutState.InProgress
};
2020-06-24 10:34:09 +09:00
[HttpGet("~/api/v1/pull-payments/{pullPaymentId}/payouts")]
[AllowAnonymous]
public async Task<IActionResult> GetPayouts(string pullPaymentId, bool includeCancelled = false)
{
if (pullPaymentId is null)
return PullPaymentNotFound();
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, true);
2020-06-24 10:34:09 +09:00
if (pp is null)
return PullPaymentNotFound();
var payouts =await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{
PullPayments = new[] {pullPaymentId},
States = GetStateFilter(includeCancelled)
});
2020-06-24 10:34:09 +09:00
return base.Ok(payouts
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
.Select(ToModel).ToList());
2020-06-24 10:34:09 +09:00
}
[HttpGet("~/api/v1/pull-payments/{pullPaymentId}/payouts/{payoutId}")]
[AllowAnonymous]
public async Task<IActionResult> GetPayout(string pullPaymentId, string payoutId)
{
if (payoutId is null)
return PayoutNotFound();
await using var ctx = _dbContextFactory.CreateContext();
var payout = (await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{
PullPayments = new[] {pullPaymentId}, PayoutIds = new[] {payoutId}
})).FirstOrDefault();
2021-12-31 16:59:02 +09:00
if (payout is null)
return PayoutNotFound();
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
return base.Ok(ToModel(payout));
}
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
private Client.Models.PayoutData ToModel(Data.PayoutData p)
2020-06-24 10:34:09 +09:00
{
var blob = p.GetBlob(_serializerSettings);
var model = new Client.Models.PayoutData()
{
Id = p.Id,
PullPaymentId = p.PullPaymentDataId,
Date = p.Date,
Amount = blob.Amount,
PaymentMethodAmount = blob.CryptoAmount,
2020-06-24 13:44:26 +09:00
Revision = blob.Revision,
State = p.State
2020-06-24 10:34:09 +09:00
};
model.Destination = blob.Destination;
2020-06-24 10:34:09 +09:00
model.PaymentMethod = p.PaymentMethodId;
model.CryptoCode = p.GetPaymentMethodId().CryptoCode;
model.PaymentProof = p.GetProofBlobJson();
2020-06-24 10:34:09 +09:00
return model;
}
[HttpPost("~/api/v1/pull-payments/{pullPaymentId}/payouts")]
[AllowAnonymous]
public async Task<IActionResult> CreatePayout(string pullPaymentId, CreatePayoutRequest request, CancellationToken cancellationToken)
2020-06-24 10:34:09 +09:00
{
if (!PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId))
{
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");
return this.CreateValidationError(ModelState);
}
2021-12-31 16:59:02 +09:00
var payoutHandler = _payoutHandlers.FindPayoutHandler(paymentMethodId);
if (payoutHandler is null)
2020-06-24 10:34:09 +09:00
{
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");
return this.CreateValidationError(ModelState);
}
await using var ctx = _dbContextFactory.CreateContext();
2020-06-24 10:34:09 +09:00
var pp = await ctx.PullPayments.FindAsync(pullPaymentId);
if (pp is null)
return PullPaymentNotFound();
2020-06-24 10:34:09 +09:00
var ppBlob = pp.GetBlob();
var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, request!.Destination, ppBlob, cancellationToken);
if (destination.destination is null)
2020-06-24 10:34:09 +09:00
{
2021-12-31 16:59:02 +09:00
ModelState.AddModelError(nameof(request.Destination), destination.error ?? "The destination is invalid for the payment specified");
2020-06-24 10:34:09 +09:00
return this.CreateValidationError(ModelState);
}
if (request.Amount is null && destination.destination.Amount != null)
{
request.Amount = destination.destination.Amount;
}
else if (request.Amount != null && destination.destination.Amount != null && request.Amount != destination.destination.Amount)
{
ModelState.AddModelError(nameof(request.Amount), $"Amount is implied in destination ({destination.destination.Amount}) that does not match the payout amount provided {request.Amount})");
return this.CreateValidationError(ModelState);
}
if (request.Amount is { } v && (v < ppBlob.MinimumClaim || v == 0.0m))
2020-06-24 10:34:09 +09:00
{
ModelState.AddModelError(nameof(request.Amount), $"Amount too small (should be at least {ppBlob.MinimumClaim})");
return this.CreateValidationError(ModelState);
}
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
var result = await _pullPaymentService.Claim(new ClaimRequest()
2020-06-24 10:34:09 +09:00
{
Destination = destination.destination,
2020-06-24 10:34:09 +09:00
PullPaymentId = pullPaymentId,
Value = request.Amount,
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
PaymentMethodId = paymentMethodId,
2020-06-24 10:34:09 +09:00
});
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
return HandleClaimResult(result);
}
[HttpPost("~/api/v1/stores/{storeId}/payouts")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> CreatePayoutThroughStore(string storeId, CreatePayoutThroughStoreRequest request)
{
if (request is null || !PaymentMethodId.TryParse(request?.PaymentMethod, out var paymentMethodId))
{
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");
return this.CreateValidationError(ModelState);
}
var payoutHandler = _payoutHandlers.FindPayoutHandler(paymentMethodId);
if (payoutHandler is null)
{
ModelState.AddModelError(nameof(request.PaymentMethod), "Invalid payment method");
return this.CreateValidationError(ModelState);
}
await using var ctx = _dbContextFactory.CreateContext();
PullPaymentBlob? ppBlob = null;
if (request?.PullPaymentId is not null)
{
var pp = await ctx.PullPayments.FirstOrDefaultAsync(data =>
data.Id == request.PullPaymentId && data.StoreId == storeId);
if (pp is null)
return PullPaymentNotFound();
ppBlob = pp.GetBlob();
}
var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, request!.Destination, ppBlob, default);
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
if (destination.destination is null)
{
ModelState.AddModelError(nameof(request.Destination), destination.error ?? "The destination is invalid for the payment specified");
return this.CreateValidationError(ModelState);
}
if (request.Amount is null && destination.destination.Amount != null)
{
request.Amount = destination.destination.Amount;
}
else if (request.Amount != null && destination.destination.Amount != null && request.Amount != destination.destination.Amount)
{
ModelState.AddModelError(nameof(request.Amount), $"Amount is implied in destination ({destination.destination.Amount}) that does not match the payout amount provided {request.Amount})");
return this.CreateValidationError(ModelState);
}
if (request.Amount is { } v && (v < ppBlob?.MinimumClaim || v == 0.0m))
{
var minimumClaim = ppBlob?.MinimumClaim is decimal val ? val : 0.0m;
ModelState.AddModelError(nameof(request.Amount), $"Amount too small (should be at least {minimumClaim})");
return this.CreateValidationError(ModelState);
}
var result = await _pullPaymentService.Claim(new ClaimRequest()
{
Destination = destination.destination,
PullPaymentId = request.PullPaymentId,
PreApprove = request.Approved,
Value = request.Amount,
PaymentMethodId = paymentMethodId,
StoreId = storeId
});
return HandleClaimResult(result);
}
private IActionResult HandleClaimResult(ClaimRequest.ClaimResponse result)
{
2020-06-24 10:34:09 +09:00
switch (result.Result)
{
case ClaimRequest.ClaimResult.Ok:
break;
case ClaimRequest.ClaimResult.Duplicate:
return this.CreateAPIError("duplicate-destination", ClaimRequest.GetErrorMessage(result.Result));
case ClaimRequest.ClaimResult.Expired:
return this.CreateAPIError("expired", ClaimRequest.GetErrorMessage(result.Result));
case ClaimRequest.ClaimResult.NotStarted:
return this.CreateAPIError("not-started", ClaimRequest.GetErrorMessage(result.Result));
case ClaimRequest.ClaimResult.Archived:
return this.CreateAPIError("archived", ClaimRequest.GetErrorMessage(result.Result));
case ClaimRequest.ClaimResult.Overdraft:
return this.CreateAPIError("overdraft", ClaimRequest.GetErrorMessage(result.Result));
case ClaimRequest.ClaimResult.AmountTooLow:
return this.CreateAPIError("amount-too-low", ClaimRequest.GetErrorMessage(result.Result));
case ClaimRequest.ClaimResult.PaymentMethodNotSupported:
return this.CreateAPIError("payment-method-not-supported", ClaimRequest.GetErrorMessage(result.Result));
default:
throw new NotSupportedException("Unsupported ClaimResult");
}
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
return Ok(ToModel(result.PayoutData));
2020-06-24 10:34:09 +09:00
}
[HttpDelete("~/api/v1/stores/{storeId}/pull-payments/{pullPaymentId}")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> ArchivePullPayment(string storeId, string pullPaymentId)
{
using var ctx = _dbContextFactory.CreateContext();
var pp = await ctx.PullPayments.FindAsync(pullPaymentId);
if (pp is null || pp.StoreId != storeId)
return PullPaymentNotFound();
2020-06-24 10:34:09 +09:00
await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(pullPaymentId));
return Ok();
}
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
[HttpGet("~/api/v1/stores/{storeId}/payouts")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> GetStorePayouts(string storeId, bool includeCancelled = false)
{
var payouts = await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{
Stores = new[] {storeId},
States = GetStateFilter(includeCancelled)
});
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
return base.Ok(payouts
.Select(ToModel).ToArray());
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
}
2020-06-24 10:34:09 +09:00
[HttpDelete("~/api/v1/stores/{storeId}/payouts/{payoutId}")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> CancelPayout(string storeId, string payoutId)
{
var res= await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(new[] { payoutId }, new []{storeId}));
return MapResult(res.First().Value);
2020-06-24 10:34:09 +09:00
}
2020-06-24 13:44:26 +09:00
[HttpPost("~/api/v1/stores/{storeId}/payouts/{payoutId}")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> ApprovePayout(string storeId, string payoutId, ApprovePayoutRequest approvePayoutRequest, CancellationToken cancellationToken = default)
{
using var ctx = _dbContextFactory.CreateContext();
ctx.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var revision = approvePayoutRequest?.Revision;
if (revision is null)
{
ModelState.AddModelError(nameof(approvePayoutRequest.Revision), "The `revision` property is required");
}
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
var payout = await ctx.Payouts.GetPayout(payoutId, storeId, true, true);
if (payout is null)
return PayoutNotFound();
RateResult? rateResult = null;
2020-06-24 13:44:26 +09:00
try
{
rateResult = await _pullPaymentService.GetRate(payout, approvePayoutRequest?.RateRule, cancellationToken);
if (rateResult.BidAsk == null)
{
return this.CreateAPIError("rate-unavailable", $"Rate unavailable: {rateResult.EvaluatedRule}");
}
}
catch (FormatException)
{
ModelState.AddModelError(nameof(approvePayoutRequest.RateRule), "Invalid RateRule");
return this.CreateValidationError(ModelState);
}
var result = (await _pullPaymentService.Approve(new PullPaymentHostedService.PayoutApproval()
2020-06-24 13:44:26 +09:00
{
PayoutId = payoutId,
Revision = revision!.Value,
2020-06-24 13:44:26 +09:00
Rate = rateResult.BidAsk.Ask
})).Result;
2020-06-24 13:44:26 +09:00
var errorMessage = PullPaymentHostedService.PayoutApproval.GetErrorMessage(result);
switch (result)
{
case PullPaymentHostedService.PayoutApproval.Result.Ok:
Transfer Processors (#3476) * Automated Transfer processors This PR introduces a few things: * Payouts can now be directly nested under a store instead of through a pull payment. * The Wallet Send screen now has an option to "schedule" instead of simply creating a transaction. When you click on schedule, all transaction destinations are converted into approved payouts. Any options relating to fees or coin selection are discarded. * There is a new concept introduced, called "Transfer Processors". Transfer Processors are services for stores that process payouts that are awaiting payment. Each processor specifies which payment methods it can handle. BTCPay Server will have some forms of transfer processors baked in but it has been designed to allow the Plugin System to provide additional processors. * The initial transfer processors provided are "automated processors", for on chain and lightning payment methods. They can be configured to process payouts every X amount of minutes. For on-chain, this means payments are batched into one transaction, resulting in more efficient and cheaper fees for processing. * * fix build * extract * remove magic string stuff * fix error message when scheduling * Paginate migration * add payout count to payment method tab * remove unused var * add protip * optimzie payout migration dramatically * Remove useless double condition * Fix bunch of warnings * Remove warning * Remove warnigns * Rename to Payout processors * fix typo Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
2022-04-24 05:19:34 +02:00
return Ok(ToModel(await ctx.Payouts.GetPayout(payoutId, storeId, true)));
2020-06-24 13:44:26 +09:00
case PullPaymentHostedService.PayoutApproval.Result.InvalidState:
return this.CreateAPIError("invalid-state", errorMessage);
case PullPaymentHostedService.PayoutApproval.Result.TooLowAmount:
return this.CreateAPIError("amount-too-low", errorMessage);
case PullPaymentHostedService.PayoutApproval.Result.OldRevision:
return this.CreateAPIError("old-revision", errorMessage);
case PullPaymentHostedService.PayoutApproval.Result.NotFound:
return PayoutNotFound();
2020-06-24 13:44:26 +09:00
default:
throw new NotSupportedException();
}
}
[HttpPost("~/api/v1/stores/{storeId}/payouts/{payoutId}/mark-paid")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> MarkPayoutPaid(string storeId, string payoutId, CancellationToken cancellationToken = default)
{
return await MarkPayout(storeId, payoutId, new Client.Models.MarkPayoutRequest()
{
State = PayoutState.Completed,
PaymentProof = null
});
}
[HttpPost("~/api/v1/stores/{storeId}/payouts/{payoutId}/mark")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> MarkPayout(string storeId, string payoutId, Client.Models.MarkPayoutRequest request)
{
request ??= new();
if (request.State == PayoutState.Cancelled)
{
return await CancelPayout(storeId, payoutId);
}
if (request.PaymentProof is not null &&
!BitcoinLikePayoutHandler.TryParseProofType(request.PaymentProof, out string _))
{
ModelState.AddModelError(nameof(request.PaymentProof), "Payment proof must have a 'proofType' property");
}
if (!ModelState.IsValid)
return this.CreateValidationError(ModelState);
var result = await _pullPaymentService.MarkPaid(new MarkPayoutRequest()
{
Proof = request.PaymentProof,
PayoutId = payoutId,
State = request.State
});
return MapResult(result);
}
2021-12-31 16:59:02 +09:00
[HttpGet("~/api/v1/stores/{storeId}/payouts/{payoutId}")]
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
public async Task<IActionResult> GetStorePayout(string storeId, string payoutId)
{
await using var ctx = _dbContextFactory.CreateContext();
var payout = (await _pullPaymentService.GetPayouts(new PullPaymentHostedService.PayoutQuery()
{
Stores = new[] {storeId}, PayoutIds = new[] {payoutId}
})).FirstOrDefault();
if (payout is null)
return PayoutNotFound();
return base.Ok(ToModel(payout));
}
private IActionResult MapResult(MarkPayoutRequest.PayoutPaidResult result)
{
var errorMessage = MarkPayoutRequest.GetErrorMessage(result);
return result switch
{
MarkPayoutRequest.PayoutPaidResult.Ok => Ok(),
MarkPayoutRequest.PayoutPaidResult.InvalidState => this.CreateAPIError("invalid-state", errorMessage),
MarkPayoutRequest.PayoutPaidResult.NotFound => PayoutNotFound(),
_ => throw new NotSupportedException()
};
}
private IActionResult PayoutNotFound()
{
return this.CreateAPIError(404, "payout-not-found", "The payout was not found");
}
private IActionResult PullPaymentNotFound()
{
return this.CreateAPIError(404, "pullpayment-not-found", "The pull payment was not found");
}
2020-06-24 10:34:09 +09:00
}
}