diff --git a/BTCPayServer.Client/BTCPayServerClient.OnChainPaymentMethods.cs b/BTCPayServer.Client/BTCPayServerClient.OnChainPaymentMethods.cs index b1b6ebb6b..28118c23c 100644 --- a/BTCPayServer.Client/BTCPayServerClient.OnChainPaymentMethods.cs +++ b/BTCPayServer.Client/BTCPayServerClient.OnChainPaymentMethods.cs @@ -20,7 +20,7 @@ namespace BTCPayServer.Client var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain", + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain", query), token); return await HandleResponse>(response); } @@ -30,7 +30,7 @@ namespace BTCPayServer.Client { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}"), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}"), token); return await HandleResponse(response); } @@ -39,7 +39,7 @@ namespace BTCPayServer.Client { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}", + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}", method: HttpMethod.Delete), token); await HandleResponse(response); } @@ -49,7 +49,7 @@ namespace BTCPayServer.Client CancellationToken token = default) { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}", + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}", bodyPayload: paymentMethod, method: HttpMethod.Put), token); return await HandleResponse(response); } @@ -61,7 +61,7 @@ namespace BTCPayServer.Client CancellationToken token = default) { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/preview", + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview", bodyPayload: paymentMethod, queryPayload: new Dictionary() { { "offset", offset }, { "amount", amount } }, method: HttpMethod.Post), token); @@ -73,7 +73,7 @@ namespace BTCPayServer.Client CancellationToken token = default) { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/preview", + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/preview", queryPayload: new Dictionary() { { "offset", offset }, { "amount", amount } }, method: HttpMethod.Get), token); return await HandleResponse(response); @@ -84,7 +84,7 @@ namespace BTCPayServer.Client CancellationToken token = default) { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/generate", + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/generate", bodyPayload: request, method: HttpMethod.Post), token); return await HandleResponse(response); diff --git a/BTCPayServer.Client/BTCPayServerClient.OnChainWallet.cs b/BTCPayServer.Client/BTCPayServerClient.OnChainWallet.cs index ee024f1cf..64b7f430a 100644 --- a/BTCPayServer.Client/BTCPayServerClient.OnChainWallet.cs +++ b/BTCPayServer.Client/BTCPayServerClient.OnChainWallet.cs @@ -16,7 +16,7 @@ namespace BTCPayServer.Client { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet"), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet"), token); return await HandleResponse(response); } public virtual async Task GetOnChainFeeRate(string storeId, string cryptoCode, int? blockTarget = null, @@ -29,7 +29,7 @@ namespace BTCPayServer.Client } var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet/feeRate", queryParams), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/feeRate", queryParams), token); return await HandleResponse(response); } @@ -38,7 +38,7 @@ namespace BTCPayServer.Client { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet/address", new Dictionary() + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/address", new Dictionary() { {"forceGenerate", forceGenerate} }), token); @@ -50,7 +50,7 @@ namespace BTCPayServer.Client { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet/address", method: HttpMethod.Delete), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/address", method: HttpMethod.Delete), token); await HandleResponse(response); } @@ -65,7 +65,7 @@ namespace BTCPayServer.Client } var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet/transactions", query), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions", query), token); return await HandleResponse>(response); } @@ -75,7 +75,18 @@ namespace BTCPayServer.Client { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet/transactions/{transactionId}"), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions/{transactionId}"), token); + return await HandleResponse(response); + } + + public virtual async Task PatchOnChainWalletTransaction( + string storeId, string cryptoCode, string transactionId, + PatchOnChainTransactionRequest request, + CancellationToken token = default) + { + var response = + await _httpClient.SendAsync( + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions/{transactionId}", queryPayload: null, bodyPayload: request, HttpMethod.Patch), token); return await HandleResponse(response); } @@ -85,7 +96,7 @@ namespace BTCPayServer.Client { var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet/utxos"), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/utxos"), token); return await HandleResponse>(response); } @@ -100,7 +111,7 @@ namespace BTCPayServer.Client } var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet/transactions", null, request, HttpMethod.Post), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions", null, request, HttpMethod.Post), token); return await HandleResponse(response); } @@ -115,7 +126,7 @@ namespace BTCPayServer.Client } var response = await _httpClient.SendAsync( - CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/wallet/transactions", null, request, HttpMethod.Post), token); + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions", null, request, HttpMethod.Post), token); return Transaction.Parse(await HandleResponse(response), network); } } diff --git a/BTCPayServer.Client/Models/PatchOnChainTransactionRequest.cs b/BTCPayServer.Client/Models/PatchOnChainTransactionRequest.cs new file mode 100644 index 000000000..a8f6623f2 --- /dev/null +++ b/BTCPayServer.Client/Models/PatchOnChainTransactionRequest.cs @@ -0,0 +1,12 @@ +#nullable enable +using System.Collections.Generic; + +namespace BTCPayServer.Client.Models +{ + public class PatchOnChainTransactionRequest + { + + public string? Comment { get; set; } = null; + public List? Labels { get; set; } = null; + } +} diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index 81b62ec9e..115406319 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -1703,7 +1703,7 @@ namespace BTCPayServer.Tests Assert.Equal(firstAddress, (await viewOnlyClient.PreviewProposedStoreOnChainPaymentMethodAddresses(store.Id, "BTC", new UpdateOnChainPaymentMethodRequest() { Enabled = true, DerivationScheme = xpub })).Addresses.First().Address); - await AssertValidationError(new[] { "accountKeyPath" }, () => viewOnlyClient.SendHttpRequest(path: $"api/v1/stores/{store.Id}/payment-methods/Onchain/BTC/preview", method: HttpMethod.Post, + await AssertValidationError(new[] { "accountKeyPath" }, () => viewOnlyClient.SendHttpRequest(path: $"api/v1/stores/{store.Id}/payment-methods/onchain/BTC/preview", method: HttpMethod.Post, bodyPayload: JObject.Parse("{\"accountKeyPath\": \"0/1\"}"))); var method = await client.UpdateStoreOnChainPaymentMethod(store.Id, "BTC", @@ -2126,7 +2126,30 @@ namespace BTCPayServer.Tests { await viewOnlyClient.GetOnChainWalletTransaction(walletId.StoreId, walletId.CryptoCode, txdata.TransactionHash.ToString()); }); - await client.GetOnChainWalletTransaction(walletId.StoreId, walletId.CryptoCode, txdata.TransactionHash.ToString()); + var transaction = await client.GetOnChainWalletTransaction(walletId.StoreId, walletId.CryptoCode, txdata.TransactionHash.ToString()); + + Assert.Equal(transaction.TransactionHash, txdata.TransactionHash); + Assert.Equal(String.Empty, transaction.Comment); + Assert.Equal(new Dictionary(), transaction.Labels); + + // transaction patch tests + var patchedTransaction = await client.PatchOnChainWalletTransaction( + walletId.StoreId, walletId.CryptoCode, txdata.TransactionHash.ToString(), + new PatchOnChainTransactionRequest() { + Comment = "test comment", + Labels = new List + { + "test label" + } + }); + Assert.Equal("test comment", patchedTransaction.Comment); + Assert.Equal( + new Dictionary() + { + { "test label", new LabelData(){ Type = "raw", Text = "test label" } } + }.ToJson(), + patchedTransaction.Labels.ToJson() + ); await AssertHttpError(403, async () => { diff --git a/BTCPayServer/Controllers/GreenField/GreenfieldStoreOnChainWalletsController.cs b/BTCPayServer/Controllers/GreenField/GreenfieldStoreOnChainWalletsController.cs index 18f7dc9b2..3ef3ec37f 100644 --- a/BTCPayServer/Controllers/GreenField/GreenfieldStoreOnChainWalletsController.cs +++ b/BTCPayServer/Controllers/GreenField/GreenfieldStoreOnChainWalletsController.cs @@ -19,6 +19,7 @@ using BTCPayServer.Payments.PayJoin; using BTCPayServer.Payments.PayJoin.Sender; using BTCPayServer.Services; using BTCPayServer.Services.Wallets; +using BTCPayServer.Services.Labels; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; @@ -50,6 +51,7 @@ namespace BTCPayServer.Controllers.Greenfield private readonly EventAggregator _eventAggregator; private readonly WalletReceiveService _walletReceiveService; private readonly IFeeProviderFactory _feeProviderFactory; + private readonly LabelFactory _labelFactory; public GreenfieldStoreOnChainWalletsController( IAuthorizationService authorizationService, @@ -64,7 +66,9 @@ namespace BTCPayServer.Controllers.Greenfield DelayedTransactionBroadcaster delayedTransactionBroadcaster, EventAggregator eventAggregator, WalletReceiveService walletReceiveService, - IFeeProviderFactory feeProviderFactory) + IFeeProviderFactory feeProviderFactory, + LabelFactory labelFactory + ) { _authorizationService = authorizationService; _btcPayWalletProvider = btcPayWalletProvider; @@ -79,6 +83,7 @@ namespace BTCPayServer.Controllers.Greenfield _eventAggregator = eventAggregator; _walletReceiveService = walletReceiveService; _feeProviderFactory = feeProviderFactory; + _labelFactory = labelFactory; } [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] @@ -229,6 +234,65 @@ namespace BTCPayServer.Controllers.Greenfield return Ok(ToModel(walletTransactionsInfoAsync, tx, wallet)); } + [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [HttpPatch("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions/{transactionId}")] + public async Task PatchOnChainWalletTransaction( + string storeId, + string cryptoCode, + string transactionId, + [FromBody] PatchOnChainTransactionRequest request + ) + { + if (IsInvalidWalletRequest(cryptoCode, out var network, + out var derivationScheme, out var actionResult)) + return actionResult; + + var wallet = _btcPayWalletProvider.GetWallet(network); + var tx = await wallet.FetchTransaction(derivationScheme.AccountDerivation, uint256.Parse(transactionId)); + if (tx is null) + { + return this.CreateAPIError(404, "transaction-not-found", "The transaction was not found."); + } + + var walletId = new WalletId(storeId, cryptoCode); + var walletTransactionsInfoAsync = _walletRepository.GetWalletTransactionsInfo(walletId); + if (!(await walletTransactionsInfoAsync).TryGetValue(transactionId, out var walletTransactionInfo)) + { + walletTransactionInfo = new WalletTransactionInfo(); + } + + if (request.Comment != null) + { + walletTransactionInfo.Comment = request.Comment.Trim().Truncate(WalletTransactionDataExtensions.MaxCommentSize); + } + + if (request.Labels != null) + { + var walletBlobInfo = await _walletRepository.GetWalletInfo(walletId); + + foreach (string label in request.Labels) + { + var rawLabel = await _labelFactory.BuildLabel( + walletBlobInfo, + Request, + walletTransactionInfo, + walletId, + transactionId, + label + ); + walletTransactionInfo.Labels.TryAdd(rawLabel.Text, rawLabel); + } + } + + await _walletRepository.SetWalletTransactionInfo(walletId, transactionId, walletTransactionInfo); + var walletTransactionsInfo = + (await _walletRepository.GetWalletTransactionsInfo(walletId, new[] { transactionId })) + .Values + .FirstOrDefault(); + + return Ok(ToModel(walletTransactionsInfo, tx, wallet)); + } + [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [HttpGet("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/utxos")] public async Task GetOnChainWalletUTXOs(string storeId, string cryptoCode) diff --git a/BTCPayServer/Controllers/UIWalletsController.cs b/BTCPayServer/Controllers/UIWalletsController.cs index 36ca1fcad..64a5ded49 100644 --- a/BTCPayServer/Controllers/UIWalletsController.cs +++ b/BTCPayServer/Controllers/UIWalletsController.cs @@ -121,21 +121,7 @@ namespace BTCPayServer.Controllers _connectionFactory = connectionFactory; _walletHistogramService = walletHistogramService; } - - // Borrowed from https://github.com/ManageIQ/guides/blob/master/labels.md - readonly string[] LabelColorScheme = - { - "#fbca04", - "#0e8a16", - "#ff7619", - "#84b6eb", - "#5319e7", - "#cdcdcd", - "#cc317c", - }; - - const int MaxLabelSize = 20; - const int MaxCommentSize = 200; + [HttpPost] [Route("{walletId}")] public async Task ModifyTransaction( @@ -173,33 +159,19 @@ namespace BTCPayServer.Controllers var walletTransactionsInfo = await walletTransactionsInfoAsync; if (addlabel != null) { - addlabel = addlabel.Trim().TrimStart('{').ToLowerInvariant().Replace(',', ' ').Truncate(MaxLabelSize); - var labels = _labelFactory.GetWalletColoredLabels(walletBlobInfo, Request); if (!walletTransactionsInfo.TryGetValue(transactionId, out var walletTransactionInfo)) { walletTransactionInfo = new WalletTransactionInfo(); } - if (!labels.Any(l => l.Text.Equals(addlabel, StringComparison.OrdinalIgnoreCase))) - { - List allColors = new List(); - allColors.AddRange(LabelColorScheme); - allColors.AddRange(labels.Select(l => l.Color)); - var chosenColor = - allColors - .GroupBy(k => k) - .OrderBy(k => k.Count()) - .ThenBy(k => - { - var indexInColorScheme = Array.IndexOf(LabelColorScheme, k.Key); - - // Ensures that any label color which may not be in our label color scheme is given the least priority - return indexInColorScheme == -1 ? double.PositiveInfinity : indexInColorScheme; - }) - .First().Key; - walletBlobInfo.LabelColors.Add(addlabel, chosenColor); - await WalletRepository.SetWalletInfo(walletId, walletBlobInfo); - } - var rawLabel = new RawLabel(addlabel); + + var rawLabel = await _labelFactory.BuildLabel( + walletBlobInfo, + Request, + walletTransactionInfo, + walletId, + transactionId, + addlabel + ); if (walletTransactionInfo.Labels.TryAdd(rawLabel.Text, rawLabel)) { await WalletRepository.SetWalletTransactionInfo(walletId, transactionId, walletTransactionInfo); @@ -224,7 +196,7 @@ namespace BTCPayServer.Controllers } else if (addcomment != null) { - addcomment = addcomment.Trim().Truncate(MaxCommentSize); + addcomment = addcomment.Trim().Truncate(WalletTransactionDataExtensions.MaxCommentSize); if (!walletTransactionsInfo.TryGetValue(transactionId, out var walletTransactionInfo)) { walletTransactionInfo = new WalletTransactionInfo(); diff --git a/BTCPayServer/Data/WalletTransactionDataExtensions.cs b/BTCPayServer/Data/WalletTransactionDataExtensions.cs index 040fb351e..7deb96d5f 100644 --- a/BTCPayServer/Data/WalletTransactionDataExtensions.cs +++ b/BTCPayServer/Data/WalletTransactionDataExtensions.cs @@ -17,6 +17,8 @@ namespace BTCPayServer.Data } public static class WalletTransactionDataExtensions { + public static int MaxCommentSize = 200; + public static WalletTransactionInfo GetBlobInfo(this WalletTransactionData walletTransactionData) { WalletTransactionInfo blobInfo; diff --git a/BTCPayServer/Services/Labels/LabelFactory.cs b/BTCPayServer/Services/Labels/LabelFactory.cs index 23665ffdc..8a4a5cded 100644 --- a/BTCPayServer/Services/Labels/LabelFactory.cs +++ b/BTCPayServer/Services/Labels/LabelFactory.cs @@ -1,23 +1,30 @@ +#nullable enable using System; -using System.Collections.Generic; using System.Drawing; -using Amazon.Util.Internal.PlatformServices; -using BTCPayServer.Client.Models; -using BTCPayServer.Data; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; -using Newtonsoft.Json.Linq; +using BTCPayServer.Data; +using BTCPayServer.Client.Models; +using BTCPayServer.Abstractions.Extensions; namespace BTCPayServer.Services.Labels { public class LabelFactory { private readonly LinkGenerator _linkGenerator; + private readonly WalletRepository _walletRepository; - public LabelFactory(LinkGenerator linkGenerator) + public LabelFactory( + LinkGenerator linkGenerator, + WalletRepository walletRepository + ) { _linkGenerator = linkGenerator; + _walletRepository = walletRepository; } public IEnumerable ColorizeTransactionLabels(WalletBlobInfo walletBlobInfo, WalletTransactionInfo transactionInfo, @@ -39,7 +46,7 @@ namespace BTCPayServer.Services.Labels } const string DefaultColor = "#000"; - private ColoredLabel CreateLabel(LabelData uncoloredLabel, string color, HttpRequest request) + private ColoredLabel CreateLabel(LabelData uncoloredLabel, string? color, HttpRequest request) { ArgumentNullException.ThrowIfNull(uncoloredLabel); color ??= DefaultColor; @@ -92,7 +99,69 @@ namespace BTCPayServer.Services.Labels } return coloredLabel; } - + + // Borrowed from https://github.com/ManageIQ/guides/blob/master/labels.md + readonly string[] LabelColorScheme = + { + "#fbca04", + "#0e8a16", + "#ff7619", + "#84b6eb", + "#5319e7", + "#cdcdcd", + "#cc317c", + }; + + readonly int MaxLabelSize = 20; + + async public Task BuildLabel( + WalletBlobInfo walletBlobInfo, + HttpRequest request, + WalletTransactionInfo walletTransactionInfo, + WalletId walletId, + string transactionId, + string label + ) + { + label = label.Trim().TrimStart('{').ToLowerInvariant().Replace(',', ' ').Truncate(MaxLabelSize); + var labels = GetWalletColoredLabels(walletBlobInfo, request); + + if (!labels.Any(l => l.Text.Equals(label, StringComparison.OrdinalIgnoreCase))) + { + var chosenColor = ChooseBackgroundColor(walletBlobInfo, request); + walletBlobInfo.LabelColors.Add(label, chosenColor); + await _walletRepository.SetWalletInfo(walletId, walletBlobInfo); + } + + return new RawLabel(label); + } + + private string ChooseBackgroundColor( + WalletBlobInfo walletBlobInfo, + HttpRequest request + ) + { + var labels = GetWalletColoredLabels(walletBlobInfo, request); + + List allColors = new List(); + allColors.AddRange(LabelColorScheme); + allColors.AddRange(labels.Select(l => l.Color)); + var chosenColor = + allColors + .GroupBy(k => k) + .OrderBy(k => k.Count()) + .ThenBy(k => + { + var indexInColorScheme = Array.IndexOf(LabelColorScheme, k.Key); + + // Ensures that any label color which may not be in our label color scheme is given the least priority + return indexInColorScheme == -1 ? double.PositiveInfinity : indexInColorScheme; + }) + .First().Key; + + return chosenColor; + } + private string TextColor(string bgColor) { int nThreshold = 105; diff --git a/BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-wallet.on-chain.json b/BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-wallet.on-chain.json index d80ec6946..4ba0b02c8 100644 --- a/BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-wallet.on-chain.json +++ b/BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-wallet.on-chain.json @@ -1,6 +1,6 @@ { "paths": { - "/api/v1/stores/{storeId}/payment-methods/OnChain/{cryptoCode}/wallet": { + "/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet": { "get": { "tags": [ "Store Wallet (On Chain)" @@ -57,7 +57,7 @@ ] } }, - "/api/v1/stores/{storeId}/payment-methods/OnChain/{cryptoCode}/wallet/feeRate": { + "/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/feerate": { "get": { "tags": [ "Store Wallet (On Chain)" @@ -124,7 +124,7 @@ ] } }, - "/api/v1/stores/{storeId}/payment-methods/OnChain/{cryptoCode}/wallet/address": { + "/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/address": { "get": { "tags": [ "Store Wallet (On Chain)" @@ -239,7 +239,7 @@ ] } }, - "/api/v1/stores/{storeId}/payment-methods/OnChain/{cryptoCode}/wallet/transactions": { + "/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions": { "get": { "tags": [ "Store Wallet (On Chain)" @@ -405,7 +405,7 @@ ] } }, - "/api/v1/stores/{storeId}/payment-methods/OnChain/{cryptoCode}/wallet/transactions/{transactionId}": { + "/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/transactions/{transactionId}": { "get": { "tags": [ "Store Wallet (On Chain)" @@ -469,9 +469,83 @@ "Basic": [] } ] + }, + "patch": { + "tags": [ + "Store Wallet (On Chain)" + ], + "summary": "Patch store on-chain wallet transaction info", + "parameters": [ + { + "name": "storeId", + "in": "path", + "required": true, + "description": "The store to fetch", + "schema": { + "type": "string" + } + }, + { + "name": "cryptoCode", + "in": "path", + "required": true, + "description": "The crypto code of the wallet to fetch", + "schema": { + "type": "string" + }, + "example": "BTC" + }, + { + "name": "transactionId", + "in": "path", + "required": true, + "description": "The transaction id to fetch", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchOnChainTransactionRequest" + } + } + } + }, + "description": "Patch store on-chain wallet transaction info", + "operationId": "StoreOnChainWallets_PatchOnChainWalletTransaction", + "responses": { + "200": { + "description": "transaction", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnChainWalletTransactionData" + } + } + } + }, + "403": { + "description": "If you are authenticated but forbidden to view the specified store" + }, + "404": { + "description": "The key is not found for this store/wallet" + } + }, + "security": [ + { + "API_Key": [ + "btcpay.store.canmodifystoresettings" + ], + "Basic": [] + } + ] } }, - "/api/v1/stores/{storeId}/payment-methods/OnChain/{cryptoCode}/wallet/utxos": { + "/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}/wallet/utxos": { "get": { "tags": [ "Store Wallet (On Chain)" @@ -559,7 +633,7 @@ "type": "object", "additionalProperties": false, "properties": { - "feeRate": { + "feerate": { "type": "number", "format": "decimal", "description": "The fee rate (sats per byte) based on the wallet's configured recommended block confirmation target" @@ -758,7 +832,7 @@ "$ref": "#/components/schemas/CreateOnChainTransactionRequestDestination" } }, - "feeRate": { + "feerate": { "type": "number", "format": "decimal or long (sats/byte)", "description": "Transaction fee." @@ -795,6 +869,25 @@ } } } + }, + "PatchOnChainTransactionRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "comment": { + "nullable": true, + "type": "string", + "description": "Transaction comment" + }, + "labels": { + "nullable": true, + "description": "Transaction labels", + "type": "array", + "items": { + "type": "string" + } + } + } } } },