mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-08 19:32:10 +01:00
Introduce Payout metadata for api and plugins (#5182)
* Introduce Payout metadata for api and plugins * fix controller * fix metadata requirement * save an object * pr changes
This commit is contained in:
parent
dc986959fd
commit
36ea17a6b7
11 changed files with 120 additions and 9 deletions
|
@ -1,8 +1,11 @@
|
|||
#nullable enable
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public class CreatePayoutThroughStoreRequest : CreatePayoutRequest
|
||||
{
|
||||
public string? PullPaymentId { get; set; }
|
||||
public bool? Approved { get; set; }
|
||||
public JObject? Metadata { get; set; }
|
||||
}
|
||||
|
|
|
@ -31,5 +31,6 @@ namespace BTCPayServer.Client.Models
|
|||
public PayoutState State { get; set; }
|
||||
public int Revision { get; set; }
|
||||
public JObject PaymentProof { get; set; }
|
||||
public JObject Metadata { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1152,7 +1152,8 @@ namespace BTCPayServer.Tests
|
|||
Approved = false,
|
||||
PaymentMethod = "BTC",
|
||||
Amount = 0.0001m,
|
||||
Destination = address.ToString()
|
||||
Destination = address.ToString(),
|
||||
|
||||
});
|
||||
await AssertAPIError("invalid-state", async () =>
|
||||
{
|
||||
|
@ -3545,6 +3546,7 @@ namespace BTCPayServer.Tests
|
|||
PaymentMethod = "BTC_LightningNetwork",
|
||||
Destination = customerInvoice.BOLT11
|
||||
});
|
||||
Assert.Equal(payout.Metadata.ToString(), new JObject().ToString()); //empty
|
||||
Assert.Empty(await adminClient.GetStoreLightningAutomatedPayoutProcessors(admin.StoreId, "BTC_LightningNetwork"));
|
||||
await adminClient.UpdateStoreLightningAutomatedPayoutProcessors(admin.StoreId, "BTC_LightningNetwork",
|
||||
new LightningAutomatedPayoutSettings() { IntervalSeconds = TimeSpan.FromSeconds(600) });
|
||||
|
@ -3555,6 +3557,36 @@ namespace BTCPayServer.Tests
|
|||
(await adminClient.GetStorePayouts(admin.StoreId, false)).Single(data => data.Id == payout.Id);
|
||||
Assert.Equal(PayoutState.Completed, payoutC.State);
|
||||
});
|
||||
|
||||
payout = await adminClient.CreatePayout(admin.StoreId,
|
||||
new CreatePayoutThroughStoreRequest()
|
||||
{
|
||||
Approved = true,
|
||||
PaymentMethod = "BTC",
|
||||
Destination = (await tester.ExplorerNode.GetNewAddressAsync()).ToString(),
|
||||
Amount = 0.0001m,
|
||||
Metadata = JObject.FromObject(new
|
||||
{
|
||||
source ="apitest",
|
||||
sourceLink = "https://chocolate.com"
|
||||
})
|
||||
});
|
||||
Assert.Equal(payout.Metadata.ToString(), JObject.FromObject(new
|
||||
{
|
||||
source = "apitest",
|
||||
sourceLink = "https://chocolate.com"
|
||||
}).ToString());
|
||||
|
||||
payout =
|
||||
(await adminClient.GetStorePayouts(admin.StoreId, false)).Single(data => data.Id == payout.Id);
|
||||
|
||||
Assert.Equal(payout.Metadata.ToString(), JObject.FromObject(new
|
||||
{
|
||||
source = "apitest",
|
||||
sourceLink = "https://chocolate.com"
|
||||
}).ToString());
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Fact(Timeout = 60 * 2 * 1000)]
|
||||
|
|
|
@ -19,6 +19,7 @@ using Microsoft.AspNetCore.Cors;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest;
|
||||
|
||||
namespace BTCPayServer.Controllers.Greenfield
|
||||
|
@ -284,7 +285,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
Amount = blob.Amount,
|
||||
PaymentMethodAmount = blob.CryptoAmount,
|
||||
Revision = blob.Revision,
|
||||
State = p.State
|
||||
State = p.State,
|
||||
Metadata = blob.Metadata?? new JObject(),
|
||||
};
|
||||
model.Destination = blob.Destination;
|
||||
model.PaymentMethod = p.PaymentMethodId;
|
||||
|
@ -341,7 +343,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
Destination = destination.destination,
|
||||
PullPaymentId = pullPaymentId,
|
||||
Value = request.Amount,
|
||||
PaymentMethodId = paymentMethodId,
|
||||
PaymentMethodId = paymentMethodId
|
||||
});
|
||||
|
||||
return HandleClaimResult(result);
|
||||
|
@ -415,7 +417,8 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||
PreApprove = request.Approved,
|
||||
Value = request.Amount,
|
||||
PaymentMethodId = paymentMethodId,
|
||||
StoreId = storeId
|
||||
StoreId = storeId,
|
||||
Metadata = request.Metadata
|
||||
});
|
||||
return HandleClaimResult(result);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Authorization;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest;
|
||||
using PayoutData = BTCPayServer.Data.PayoutData;
|
||||
using PullPaymentData = BTCPayServer.Data.PullPaymentData;
|
||||
|
@ -529,10 +530,32 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
var ppBlob = item.PullPayment?.GetBlob();
|
||||
var payoutBlob = item.Payout.GetBlob(_jsonSerializerSettings);
|
||||
string payoutSource;
|
||||
if (payoutBlob.Metadata?.TryGetValue("source", StringComparison.InvariantCultureIgnoreCase,
|
||||
out var source) is true)
|
||||
{
|
||||
payoutSource = source.Value<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
payoutSource = ppBlob?.Name ?? item.PullPayment?.Id;
|
||||
}
|
||||
|
||||
string payoutSourceLink = null;
|
||||
if (payoutBlob.Metadata?.TryGetValue("sourceLink", StringComparison.InvariantCultureIgnoreCase,
|
||||
out var sourceLink) is true)
|
||||
{
|
||||
payoutSourceLink = sourceLink.Value<string>();
|
||||
}
|
||||
else if(item.PullPayment?.Id is not null)
|
||||
{
|
||||
payoutSourceLink = Url.Action("ViewPullPayment", "UIPullPayment", new { pullPaymentId = item.PullPayment?.Id });
|
||||
}
|
||||
var m = new PayoutsModel.PayoutModel
|
||||
{
|
||||
PullPaymentId = item.PullPayment?.Id,
|
||||
PullPaymentName = ppBlob?.Name ?? item.PullPayment?.Id,
|
||||
Source = payoutSource,
|
||||
SourceLink = payoutSourceLink,
|
||||
Date = item.Payout.Date,
|
||||
PayoutId = item.Payout.Id,
|
||||
Amount = _displayFormatter.Currency(payoutBlob.Amount, ppBlob?.Currency ?? PaymentMethodId.Parse(item.Payout.PaymentMethodId).CryptoCode),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
|
@ -12,5 +14,10 @@ namespace BTCPayServer.Data
|
|||
public int MinimumConfirmation { get; set; } = 1;
|
||||
public string Destination { get; set; }
|
||||
public int Revision { get; set; }
|
||||
|
||||
[JsonExtensionData]
|
||||
public Dictionary<string, JToken> AdditionalData { get; set; } = new();
|
||||
|
||||
public JObject Metadata { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,9 @@ namespace BTCPayServer.Data
|
|||
|
||||
public static PayoutBlob GetBlob(this PayoutData data, BTCPayNetworkJsonSerializerSettings serializers)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<PayoutBlob>(Encoding.UTF8.GetString(data.Blob), serializers.GetSerializer(data.GetPaymentMethodId().CryptoCode));
|
||||
var result = JsonConvert.DeserializeObject<PayoutBlob>(Encoding.UTF8.GetString(data.Blob), serializers.GetSerializer(data.GetPaymentMethodId().CryptoCode));
|
||||
result.Metadata ??= new JObject();
|
||||
return result;
|
||||
}
|
||||
public static void SetBlob(this PayoutData data, PayoutBlob blob, BTCPayNetworkJsonSerializerSettings serializers)
|
||||
{
|
||||
|
|
|
@ -597,7 +597,8 @@ namespace BTCPayServer.HostedServices
|
|||
var payoutBlob = new PayoutBlob()
|
||||
{
|
||||
Amount = claimed,
|
||||
Destination = req.ClaimRequest.Destination.ToString()
|
||||
Destination = req.ClaimRequest.Destination.ToString(),
|
||||
Metadata = req.ClaimRequest.Metadata?? new JObject(),
|
||||
};
|
||||
payout.SetBlob(payoutBlob, _jsonSerializerSettings);
|
||||
await ctx.Payouts.AddAsync(payout);
|
||||
|
@ -890,6 +891,7 @@ namespace BTCPayServer.HostedServices
|
|||
public IClaimDestination Destination { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
public bool? PreApprove { get; set; }
|
||||
public JObject Metadata { get; set; }
|
||||
}
|
||||
|
||||
public record PayoutEvent(PayoutEvent.PayoutEventType Type,PayoutData Payout)
|
||||
|
|
|
@ -26,7 +26,8 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||
public bool Selected { get; set; }
|
||||
public DateTimeOffset Date { get; set; }
|
||||
public string PullPaymentId { get; set; }
|
||||
public string PullPaymentName { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string SourceLink { get; set; }
|
||||
public string Destination { get; set; }
|
||||
public string Amount { get; set; }
|
||||
public string ProofLink { get; set; }
|
||||
|
|
|
@ -196,7 +196,14 @@
|
|||
<span>@pp.Date.ToBrowserDate()</span>
|
||||
</td>
|
||||
<td class="mw-100">
|
||||
<span>@pp.PullPaymentName</span>
|
||||
@if (pp.SourceLink is not null && pp.Source is not null)
|
||||
{
|
||||
<a href="@pp.SourceLink" rel="noreferrer noopener">@pp.Source</a>
|
||||
}
|
||||
else if (pp.Source is not null)
|
||||
{
|
||||
<span>@pp.Source</span>
|
||||
}
|
||||
</td>
|
||||
<td title="@pp.Destination">
|
||||
<span class="text-break">@pp.Destination</span>
|
||||
|
|
|
@ -904,6 +904,10 @@
|
|||
"approved": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to approve this payout automatically upon creation"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"description": "Additional metadata to store with the payout"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1012,6 +1016,32 @@
|
|||
},
|
||||
"paymentProof": {
|
||||
"$ref": "#/components/schemas/PayoutPaymentProof"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"description": "Additional information around the payout that can be supplied. The mentioned properties are all optional and you can introduce any json format you wish.",
|
||||
"example": {
|
||||
"source": "Payout created through the API"
|
||||
},
|
||||
"anyOf": [
|
||||
{
|
||||
"title": "General information",
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The source of the payout creation. Shown on the payout list page."
|
||||
},
|
||||
"sourceLink": {
|
||||
"type": "string",
|
||||
"format": "url",
|
||||
"nullable": true,
|
||||
"description": "A link to the source of the payout creation. Shown on the payout list page."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue