mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Greenfield: Allow marking payout status and payment proofs (#4244)
This allows external services to integrate with the payouts system to process payouts. This is also a step to allow plugins to provide payout processors. * It provides the payment proof through the greenfield payoust api. * It allows you to set the state of a payout outside of the usual flow: * When state is awaiting payment, allow setting to In progess or completed * When state is in progress, allow setting back to awaiting payment
This commit is contained in:
@@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest;
|
||||
|
||||
namespace BTCPayServer.Controllers.Greenfield
|
||||
{
|
||||
@@ -243,6 +244,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
model.Destination = blob.Destination;
|
||||
model.PaymentMethod = p.PaymentMethodId;
|
||||
model.CryptoCode = p.GetPaymentMethodId().CryptoCode;
|
||||
model.PaymentProof = p.GetProofBlobJson();
|
||||
return model;
|
||||
}
|
||||
|
||||
@@ -417,19 +419,15 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
|
||||
|
||||
return base.Ok(payouts
|
||||
.Select(ToModel).ToList());
|
||||
.Select(ToModel).ToArray());
|
||||
}
|
||||
|
||||
[HttpDelete("~/api/v1/stores/{storeId}/payouts/{payoutId}")]
|
||||
[Authorize(Policy = Policies.CanManagePullPayments, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public async Task<IActionResult> CancelPayout(string storeId, string payoutId)
|
||||
{
|
||||
using var ctx = _dbContextFactory.CreateContext();
|
||||
var payout = await ctx.Payouts.GetPayout(payoutId, storeId);
|
||||
if (payout is null)
|
||||
return PayoutNotFound();
|
||||
await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(new[] { payoutId }));
|
||||
return Ok();
|
||||
var res= await _pullPaymentService.Cancel(new PullPaymentHostedService.CancelRequest(new[] { payoutId }, new []{storeId}));
|
||||
return MapResult(res.First().Value);
|
||||
}
|
||||
|
||||
[HttpPost("~/api/v1/stores/{storeId}/payouts/{payoutId}")]
|
||||
@@ -490,29 +488,67 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||
[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 PayoutPaidRequest()
|
||||
var result = await _pullPaymentService.MarkPaid(new MarkPayoutRequest()
|
||||
{
|
||||
//TODO: Allow API to specify the manual proof object
|
||||
Proof = null,
|
||||
PayoutId = payoutId
|
||||
Proof = request.PaymentProof,
|
||||
PayoutId = payoutId,
|
||||
State = request.State
|
||||
});
|
||||
var errorMessage = PayoutPaidRequest.GetErrorMessage(result);
|
||||
switch (result)
|
||||
{
|
||||
case PayoutPaidRequest.PayoutPaidResult.Ok:
|
||||
return Ok();
|
||||
case PayoutPaidRequest.PayoutPaidResult.InvalidState:
|
||||
return this.CreateAPIError("invalid-state", errorMessage);
|
||||
case PayoutPaidRequest.PayoutPaidResult.NotFound:
|
||||
return PayoutNotFound();
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
return MapResult(result);
|
||||
}
|
||||
|
||||
[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");
|
||||
|
||||
Reference in New Issue
Block a user