From 69eaaef963dade9b87b472482a3857942e3d5e84 Mon Sep 17 00:00:00 2001 From: Andrew Camilleri Date: Wed, 23 Dec 2020 06:00:38 +0100 Subject: [PATCH] GreenField: Store OnChain Payment Method (#2157) * GreenField: Store set payment methods * add swagger docs * fix swagger --- ...TCPayServerClient.OnChainPaymentMethods.cs | 74 +++ .../Models/OnChainPaymentMethodData.cs | 38 ++ .../OnChainPaymentMethodPreviewResultData.cs | 23 + BTCPayServer.Tests/GreenfieldAPITests.cs | 53 +++ .../StoreOnChainPaymentMethodsController.cs | 235 +++++++++ BTCPayServer/Payments/PaymentTypes.Bitcoin.cs | 4 +- ...plate.stores-payment-methods.on-chain.json | 444 ++++++++++++++++++ 7 files changed, 869 insertions(+), 2 deletions(-) create mode 100644 BTCPayServer.Client/BTCPayServerClient.OnChainPaymentMethods.cs create mode 100644 BTCPayServer.Client/Models/OnChainPaymentMethodData.cs create mode 100644 BTCPayServer.Client/Models/OnChainPaymentMethodPreviewResultData.cs create mode 100644 BTCPayServer/Controllers/GreenField/StoreOnChainPaymentMethodsController.cs create mode 100644 BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-payment-methods.on-chain.json diff --git a/BTCPayServer.Client/BTCPayServerClient.OnChainPaymentMethods.cs b/BTCPayServer.Client/BTCPayServerClient.OnChainPaymentMethods.cs new file mode 100644 index 000000000..f92be78cc --- /dev/null +++ b/BTCPayServer.Client/BTCPayServerClient.OnChainPaymentMethods.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using BTCPayServer.Client.Models; + +namespace BTCPayServer.Client +{ + public partial class BTCPayServerClient + { + public virtual async Task> GetStoreOnChainPaymentMethods(string storeId, + CancellationToken token = default) + { + var response = + await _httpClient.SendAsync( + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain"), token); + return await HandleResponse>(response); + } + + public virtual async Task GetStoreOnChainPaymentMethod(string storeId, + string cryptoCode, CancellationToken token = default) + { + var response = + await _httpClient.SendAsync( + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}"), token); + return await HandleResponse(response); + } + + public virtual async Task RemoveStoreOnChainPaymentMethod(string storeId, + string cryptoCode, CancellationToken token = default) + { + var response = + await _httpClient.SendAsync( + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}", + method: HttpMethod.Delete), token); + await HandleResponse(response); + } + + public virtual async Task UpdateStoreOnChainPaymentMethod(string storeId, + string cryptoCode, OnChainPaymentMethodData paymentMethod, + CancellationToken token = default) + { + var response = await _httpClient.SendAsync( + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}", + bodyPayload: paymentMethod, method: HttpMethod.Put), token); + return await HandleResponse(response); + } + + public virtual async Task + PreviewProposedStoreOnChainPaymentMethodAddresses( + string storeId, string cryptoCode, OnChainPaymentMethodData paymentMethod, int offset = 0, + int amount = 10, + CancellationToken token = default) + { + var response = await _httpClient.SendAsync( + CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/Onchain/{cryptoCode}/preview", + bodyPayload: paymentMethod, + queryPayload: new Dictionary() {{"offset", offset}, {"amount", amount}}, + method: HttpMethod.Post), token); + return await HandleResponse(response); + } + + public virtual async Task PreviewStoreOnChainPaymentMethodAddresses( + string storeId, string cryptoCode, int offset = 0, int amount = 10, + CancellationToken token = default) + { + var response = await _httpClient.SendAsync( + 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); + } + } +} diff --git a/BTCPayServer.Client/Models/OnChainPaymentMethodData.cs b/BTCPayServer.Client/Models/OnChainPaymentMethodData.cs new file mode 100644 index 000000000..9d682cec4 --- /dev/null +++ b/BTCPayServer.Client/Models/OnChainPaymentMethodData.cs @@ -0,0 +1,38 @@ +namespace BTCPayServer.Client.Models +{ + public class OnChainPaymentMethodData + { + /// + /// Whether the payment method is enabled + /// + public bool Enabled { get; set; } + + /// + /// Whether the payment method is the default + /// + public bool Default { get; set; } + + /// + /// Crypto code of the payment method + /// + public string CryptoCode { get; set; } + + /// + /// The derivation scheme + /// + public string DerivationScheme { get; set; } + + public OnChainPaymentMethodData() + { + } + + public OnChainPaymentMethodData(string cryptoCode, string derivationScheme, bool enabled, + bool defaultMethod) + { + Enabled = enabled; + Default = defaultMethod; + CryptoCode = cryptoCode; + DerivationScheme = derivationScheme; + } + } +} diff --git a/BTCPayServer.Client/Models/OnChainPaymentMethodPreviewResultData.cs b/BTCPayServer.Client/Models/OnChainPaymentMethodPreviewResultData.cs new file mode 100644 index 000000000..313f36c5d --- /dev/null +++ b/BTCPayServer.Client/Models/OnChainPaymentMethodPreviewResultData.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace BTCPayServer.Client.Models +{ + public class OnChainPaymentMethodPreviewResultData + { + /// + /// a list of addresses generated by the derivation scheme + /// + public IList Addresses { get; set; } = + new List(); + + public class OnChainPaymentMethodPreviewResultAddressItem + { + /// + /// The key path relative to the account key path. + /// + public string KeyPath { get; set; } + //The address generated at the key path + public string Address { get; set; } + } + } +} diff --git a/BTCPayServer.Tests/GreenfieldAPITests.cs b/BTCPayServer.Tests/GreenfieldAPITests.cs index 7b030056d..c06cd0497 100644 --- a/BTCPayServer.Tests/GreenfieldAPITests.cs +++ b/BTCPayServer.Tests/GreenfieldAPITests.cs @@ -1209,8 +1209,61 @@ namespace BTCPayServer.Tests Assert.Empty(await viewOnlyClient.GetNotifications(true)); Assert.Empty(await viewOnlyClient.GetNotifications(false)); } + + [Fact(Timeout = TestTimeout)] + [Trait("Integration", "Integration")] + public async Task OnChainPaymentMethodAPITests() + { + using var tester = ServerTester.Create(); + await tester.StartAsync(); + var user = tester.NewAccount(); + await user.GrantAccessAsync(true); + var client = await user.CreateClient(Policies.CanModifyStoreSettings); + var viewOnlyClient = await user.CreateClient(Policies.CanViewStoreSettings); + + var store = await client.CreateStore(new CreateStoreRequest() {Name = "test store"}); + + Assert.Empty(await client.GetStoreOnChainPaymentMethods(store.Id)); + await AssertHttpError(403, async () => + { + await viewOnlyClient.UpdateStoreOnChainPaymentMethod(store.Id, "BTC", new OnChainPaymentMethodData() { }); + }); + var xpriv = new Mnemonic("all all all all all all all all all all all all").DeriveExtKey() + .Derive(KeyPath.Parse("m/84'/0'/0'")); + var xpub = xpriv.Neuter().ToString(Network.RegTest); + var firstAddress = xpriv.Derive(KeyPath.Parse("0/0")).Neuter().GetPublicKey().GetAddress(ScriptPubKeyType.Segwit, Network.RegTest).ToString(); + await AssertHttpError(404, async () => + { + await client.PreviewStoreOnChainPaymentMethodAddresses(store.Id, "BTC"); + }); + + Assert.Equal(firstAddress, (await viewOnlyClient.PreviewProposedStoreOnChainPaymentMethodAddresses(store.Id, "BTC", + new OnChainPaymentMethodData() {Default = true, Enabled = true, DerivationScheme = xpub})).Addresses.First().Address); + + var method = await client.UpdateStoreOnChainPaymentMethod(store.Id, "BTC", + new OnChainPaymentMethodData() {Default = true, Enabled = true, DerivationScheme = xpub}); + + Assert.Equal(xpub,method.DerivationScheme); + + method = await client.GetStoreOnChainPaymentMethod(store.Id, "BTC"); + + Assert.Equal(xpub,method.DerivationScheme); + + + Assert.Equal(firstAddress, (await viewOnlyClient.PreviewStoreOnChainPaymentMethodAddresses(store.Id, "BTC")).Addresses.First().Address); + await AssertHttpError(403, async () => + { + await viewOnlyClient.RemoveStoreOnChainPaymentMethod(store.Id, "BTC"); + }); + await client.RemoveStoreOnChainPaymentMethod(store.Id, "BTC"); + await AssertHttpError(404, async () => + { + await client.GetStoreOnChainPaymentMethod(store.Id, "BTC"); + }); + } + [Fact(Timeout = TestTimeout)] [Trait("Fast", "Fast")] diff --git a/BTCPayServer/Controllers/GreenField/StoreOnChainPaymentMethodsController.cs b/BTCPayServer/Controllers/GreenField/StoreOnChainPaymentMethodsController.cs new file mode 100644 index 000000000..96588d473 --- /dev/null +++ b/BTCPayServer/Controllers/GreenField/StoreOnChainPaymentMethodsController.cs @@ -0,0 +1,235 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using BTCPayServer.Abstractions.Constants; +using BTCPayServer.Client; +using BTCPayServer.Client.Models; +using BTCPayServer.Data; +using BTCPayServer.Payments; +using BTCPayServer.Services.Stores; +using BTCPayServer.Services.Wallets; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using NBXplorer.DerivationStrategy; +using StoreData = BTCPayServer.Data.StoreData; + +namespace BTCPayServer.Controllers.GreenField +{ + [ApiController] + [Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + public class StoreOnChainPaymentMethodsController : ControllerBase + { + private StoreData Store => HttpContext.GetStoreData(); + private readonly StoreRepository _storeRepository; + private readonly BTCPayNetworkProvider _btcPayNetworkProvider; + private readonly BTCPayWalletProvider _walletProvider; + + public StoreOnChainPaymentMethodsController( + StoreRepository storeRepository, + BTCPayNetworkProvider btcPayNetworkProvider, + BTCPayWalletProvider walletProvider) + { + _storeRepository = storeRepository; + _btcPayNetworkProvider = btcPayNetworkProvider; + _walletProvider = walletProvider; + } + + [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [HttpGet("~/api/v1/stores/{storeId}/payment-methods/" + BitcoinPaymentType.GreenFieldApiId)] + public ActionResult> GetOnChainPaymentMethods( + [FromQuery] bool enabledOnly = false) + { + var blob = Store.GetStoreBlob(); + var excludedPaymentMethods = blob.GetExcludedPaymentMethods(); + var defaultPaymentId = Store.GetDefaultPaymentId(_btcPayNetworkProvider); + return Ok(Store.GetSupportedPaymentMethods(_btcPayNetworkProvider) + .Where((method) => method.PaymentId.PaymentType == PaymentTypes.BTCLike) + .OfType() + .Select(strategy => + new OnChainPaymentMethodData(strategy.PaymentId.CryptoCode, + strategy.AccountDerivation.ToString(), !excludedPaymentMethods.Match(strategy.PaymentId), + defaultPaymentId == strategy.PaymentId)) + .Where((result) => !enabledOnly || result.Enabled) + .ToList() + ); + } + + [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [HttpGet("~/api/v1/stores/{storeId}/payment-methods/" + BitcoinPaymentType.GreenFieldApiId + "/{cryptoCode}")] + public ActionResult GetOnChainPaymentMethod(string cryptoCode) + { + if (!GetCryptoCodeWallet(cryptoCode, out BTCPayNetwork _, out BTCPayWallet _)) + { + return NotFound(); + } + + var method = GetExistingBtcLikePaymentMethod(cryptoCode); + if (method is null) + { + return NotFound(); + } + return Ok(method); + } + + [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [HttpGet("~/api/v1/stores/{storeId}/payment-methods/" + BitcoinPaymentType.GreenFieldApiId + + "/{cryptoCode}/preview")] + public ActionResult GetOnChainPaymentMethodPreview( + string cryptoCode, + int offset = 0, int amount = 10) + { + if (!GetCryptoCodeWallet(cryptoCode, out var network, out BTCPayWallet _)) + { + return NotFound(); + } + + var paymentMethod = GetExistingBtcLikePaymentMethod(cryptoCode); + if (string.IsNullOrEmpty(paymentMethod?.DerivationScheme)) + { + return NotFound(); + } + + var strategy = DerivationSchemeSettings.Parse(paymentMethod.DerivationScheme, network); + var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit); + + var line = strategy.AccountDerivation.GetLineFor(deposit); + var result = new OnChainPaymentMethodPreviewResultData(); + for (var i = offset; i < amount; i++) + { + var address = line.Derive((uint)i); + result.Addresses.Add( + new OnChainPaymentMethodPreviewResultData.OnChainPaymentMethodPreviewResultAddressItem() + { + KeyPath = deposit.GetKeyPath((uint)i).ToString(), + Address = address.ScriptPubKey.GetDestinationAddress(strategy.Network.NBitcoinNetwork) + .ToString() + }); + } + + return Ok(result); + } + + + [Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [HttpPost("~/api/v1/stores/{storeId}/payment-methods/" + BitcoinPaymentType.GreenFieldApiId + + "/{cryptoCode}/preview")] + public IActionResult GetProposedOnChainPaymentMethodPreview(string cryptoCode, + [FromBody] OnChainPaymentMethodData paymentMethodData, + int offset = 0, int amount = 10) + { + if (!GetCryptoCodeWallet(cryptoCode, out var network, out BTCPayWallet _)) + { + return NotFound(); + } + + try + { + var strategy = DerivationSchemeSettings.Parse(paymentMethodData.DerivationScheme, network); + var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit); + var line = strategy.AccountDerivation.GetLineFor(deposit); + var result = new OnChainPaymentMethodPreviewResultData(); + for (var i = offset; i < amount; i++) + { + var derivation = line.Derive((uint)i); + result.Addresses.Add( + new + OnChainPaymentMethodPreviewResultData. + OnChainPaymentMethodPreviewResultAddressItem() + { + KeyPath = deposit.GetKeyPath((uint)i).ToString(), + Address = strategy.Network.NBXplorerNetwork.CreateAddress(strategy.AccountDerivation, + line.KeyPathTemplate.GetKeyPath((uint)i), + derivation.ScriptPubKey).ToString() + }); + } + + return Ok(result); + } + + catch + { + ModelState.AddModelError(nameof(OnChainPaymentMethodData.DerivationScheme), + "Invalid Derivation Scheme"); + return this.CreateValidationError(ModelState); + } + } + + [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [HttpDelete("~/api/v1/stores/{storeId}/payment-methods/" + BitcoinPaymentType.GreenFieldApiId + + "/{cryptoCode}")] + public async Task> RemoveOnChainPaymentMethod( + string cryptoCode, + int offset = 0, int amount = 10) + { + if (!GetCryptoCodeWallet(cryptoCode, out BTCPayNetwork _, out BTCPayWallet _)) + { + return NotFound(); + } + + var id = new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike); + var store = Store; + store.SetSupportedPaymentMethod(id, null); + await _storeRepository.UpdateStore(store); + return Ok(); + } + + [Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] + [HttpPut("~/api/v1/stores/{storeId}/payment-methods/" + BitcoinPaymentType.GreenFieldApiId + "/{cryptoCode}")] + public async Task UpdateOnChainPaymentMethod(string cryptoCode, + [FromBody] OnChainPaymentMethodData paymentMethodData) + { + var id = new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike); + + if (!GetCryptoCodeWallet(cryptoCode, out var network, out var wallet)) + { + return NotFound(); + } + + try + { + var store = Store; + var storeBlob = store.GetStoreBlob(); + var strategy = DerivationSchemeSettings.Parse(paymentMethodData.DerivationScheme, network); + if (strategy != null) + await wallet.TrackAsync(strategy.AccountDerivation); + store.SetSupportedPaymentMethod(id, strategy); + storeBlob.SetExcluded(id, !paymentMethodData.Enabled); + store.SetStoreBlob(storeBlob); + await _storeRepository.UpdateStore(store); + return Ok(GetExistingBtcLikePaymentMethod(cryptoCode, store)); + } + catch + { + ModelState.AddModelError(nameof(OnChainPaymentMethodData.DerivationScheme), + "Invalid Derivation Scheme"); + return this.CreateValidationError(ModelState); + } + } + + private bool GetCryptoCodeWallet(string cryptoCode, out BTCPayNetwork network, out BTCPayWallet wallet) + { + network = _btcPayNetworkProvider.GetNetwork(cryptoCode); + wallet = network != null ? _walletProvider.GetWallet(network) : null; + return wallet != null; + } + + private OnChainPaymentMethodData GetExistingBtcLikePaymentMethod(string cryptoCode, StoreData store = null) + { + store ??= Store; + var storeBlob = store.GetStoreBlob(); + var defaultPaymentMethod = store.GetDefaultPaymentId(_btcPayNetworkProvider); + var id = new PaymentMethodId(cryptoCode, PaymentTypes.BTCLike); + var paymentMethod = store + .GetSupportedPaymentMethods(_btcPayNetworkProvider) + .OfType() + .FirstOrDefault(method => method.PaymentId == id); + + var excluded = storeBlob.IsExcluded(id); + return paymentMethod == null + ? null + : new OnChainPaymentMethodData(paymentMethod.PaymentId.CryptoCode, + paymentMethod.AccountDerivation.ToString(), !excluded, + defaultPaymentMethod == paymentMethod.PaymentId); + } + } +} diff --git a/BTCPayServer/Payments/PaymentTypes.Bitcoin.cs b/BTCPayServer/Payments/PaymentTypes.Bitcoin.cs index 4a54d4e80..8e7ee6f92 100644 --- a/BTCPayServer/Payments/PaymentTypes.Bitcoin.cs +++ b/BTCPayServer/Payments/PaymentTypes.Bitcoin.cs @@ -18,8 +18,8 @@ namespace BTCPayServer.Payments public override string ToPrettyString() => "On-Chain"; public override string GetId() => "BTCLike"; public override string GetBadge() => "🔗"; - public override string ToStringNormalized() => "OnChain"; - + public override string ToStringNormalized() => GreenFieldApiId; + public const string GreenFieldApiId = "OnChain"; public override CryptoPaymentData DeserializePaymentData(BTCPayNetworkBase network, string str) { return ((BTCPayNetwork)network)?.ToObject(str); diff --git a/BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-payment-methods.on-chain.json b/BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-payment-methods.on-chain.json new file mode 100644 index 000000000..1b5086f86 --- /dev/null +++ b/BTCPayServer/wwwroot/swagger/v1/swagger.template.stores-payment-methods.on-chain.json @@ -0,0 +1,444 @@ +{ + "paths": { + "/api/v1/stores/{storeId}/payment-methods/OnChain": { + "get": { + "tags": [ + "Store Payment Methods (On Chain)" + ], + "summary": "Get store on-chain payment methods", + "description": "View information about the stores' configured on-chain payment methods", + "operationId": "StoreOnChainPaymentMethods_GetOnChainPaymentMethods", + "responses": { + "200": { + "description": "list of payment methods", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnChainPaymentMethodDataList" + } + } + } + } + }, + "security": [ + { + "API Key": [ + "btcpay.store.canviewstoresettings" + ], + "Basic": [] + } + ] + } + }, + "/api/v1/stores/{storeId}/payment-methods/OnChain/{cryptoCode}": { + "get": { + "tags": [ + "Store Payment Methods (On Chain)" + ], + "summary": "Get store on-chain payment method", + "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 payment method to fetch", + "schema": { + "type": "string" + } + } + ], + "description": "View information about the specified payment method", + "operationId": "StoreOnChainPaymentMethods_GetOnChainPaymentMethod", + "responses": { + "200": { + "description": "specified payment method", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnChainPaymentMethodData" + } + } + } + }, + "403": { + "description": "If you are authenticated but forbidden to view the specified store" + }, + "404": { + "description": "The key is not found for this store/payment method" + } + }, + "security": [ + { + "API Key": [ + "btcpay.store.canviewstoresettings" + ], + "Basic": [] + } + ] + }, + "put": { + "tags": [ + "Store Payment Methods (On Chain)" + ], + "summary": "Update store on-chain payment method", + "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 payment method to update", + "schema": { + "type": "string" + } + } + ], + "description": "Update the specified store's payment method", + "requestBody": { + "x-name": "request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnChainPaymentMethodData" + } + } + }, + "required": true, + "x-position": 1 + }, + "operationId": "StoreOnChainPaymentMethods_UpdateOnChainPaymentMethod", + "responses": { + "200": { + "description": "updated specified payment method", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnChainPaymentMethodData" + } + } + } + }, + "400": { + "description": "A list of errors that occurred when updating the store payment method", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidationProblemDetails" + } + } + } + }, + "403": { + "description": "If you are authenticated but forbidden to update the specified store" + }, + "404": { + "description": "The key is not found for this store" + } + }, + "security": [ + { + "API Key": [ + "btcpay.store.canmodifystoresettings" + ], + "Basic": [] + } + ] + }, + "delete": { + "tags": [ + "Store Payment Methods (On Chain)" + ], + "summary": "Remove store on-chain payment method", + "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 payment method to update", + "schema": { + "type": "string" + } + } + ], + "description": "Removes the specified store payment method.", + "responses": { + "200": { + "description": "The payment method has been removed" + }, + "400": { + "description": "A list of errors that occurred when removing the payment method", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidationProblemDetails" + } + } + } + }, + "403": { + "description": "If you are authenticated but forbidden to remove the specified payment method" + }, + "404": { + "description": "The key is not found for this store/payment-method" + } + }, + "security": [ + { + "API Key": [ + "btcpay.store.canmodifystoresettings" + ], + "Basic": [] + } + ] + } + }, + "/api/v1/stores/{storeId}/payment-methods/OnChain/{cryptoCode}/preview": { + "get": { + "tags": [ + "Store Payment Methods (On Chain)" + ], + "summary": "Preview store on-chain payment method addresses", + "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 payment method to fetch", + "schema": { + "type": "string" + } + }, + { + "name": "offset", + "in": "query", + "required": false, + "description": "From which index to fetch the addresses", + "schema": { + "type": "number" + } + }, + { + "name": "amount", + "in": "query", + "required": false, + "description": "Number of addresses to preview", + "schema": { + "type": "number" + } + } + ], + "description": "View addresses of the current payment method of the store", + "operationId": "StoreOnChainPaymentMethods_GetOnChainPaymentMethodPreview", + "responses": { + "200": { + "description": "specified payment method addresses", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnChainPaymentMethodPreviewResultData" + } + } + } + }, + "403": { + "description": "If you are authenticated but forbidden to view the specified store" + }, + "404": { + "description": "The key is not found for this store/payment method" + } + }, + "security": [ + { + "API Key": [ + "btcpay.store.canviewstoresettings" + ], + "Basic": [] + } + ] + }, + "post": { + "tags": [ + "Store Payment Methods (On Chain)" + ], + "summary": "Preview proposed store on-chain payment method addresses", + "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 payment method to fetch", + "schema": { + "type": "string" + } + }, + { + "name": "offset", + "in": "query", + "required": false, + "description": "From which index to fetch the addresses", + "schema": { + "type": "number" + } + }, + { + "name": "amount", + "in": "query", + "required": false, + "description": "Number of addresses to preview", + "schema": { + "type": "number" + } + } + ], + "description": "View addresses of a proposed payment method of the store", + "operationId": "StoreOnChainPaymentMethods_GetOnChainPaymentMethodPreview", + "requestBody": { + "x-name": "request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnChainPaymentMethodData" + } + } + }, + "required": true, + "x-position": 1 + }, + "responses": { + "200": { + "description": "specified payment method addresses", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnChainPaymentMethodPreviewResultData" + } + } + } + }, + "403": { + "description": "If you are authenticated but forbidden to view the specified store" + }, + "404": { + "description": "The key is not found for this store" + } + }, + "security": [ + { + "API Key": [ + "btcpay.store.canviewstoresettings" + ], + "Basic": [] + } + ] + } + } + }, + "components": { + "schemas": { + "OnChainPaymentMethodDataList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OnChainPaymentMethodData" + } + }, + "OnChainPaymentMethodData": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Whether the payment method is enabled" + }, + "default": { + "type": "boolean", + "description": "Whether the payment method is the default" + }, + "cryptoCode": { + "type": "string", + "description": "Crypto code of the payment method" + }, + "derivationScheme": { + "type": "string", + "description": "The derivation scheme" + } + } + }, + "OnChainPaymentMethodPreviewResultData": { + "type": "object", + "additionalProperties": false, + "properties": { + "addresses": { + "type": "array", + "description": "a list of addresses generated by the derivation scheme", + "items": { + "$ref": "#/components/schemas/OnChainPaymentMethodPreviewResultAddressItem" + } + } + } + }, + "OnChainPaymentMethodPreviewResultAddressItem": { + "type": "object", + "additionalProperties": false, + "properties": { + "keyPath": { + "type": "string", + "description": "The key path relative to the account key path." + }, + "address": { + "type": "string", + "description": "The address generated at the key path" + } + } + } + } + }, + "tags": [ + { + "name": "Store Payment Methods (On Chain)" + } + ] +}