Merge pull request #6570 from btcpayserver/fix-get-store-payment

Fix: Incorrect enabled value for v1/stores/{storeId}/payment-methods/{paymentMethod}
This commit is contained in:
Nicolas Dorier 2025-01-21 23:52:18 +09:00 committed by GitHub
commit 81cd51cc66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 78 additions and 19 deletions

View file

@ -3314,6 +3314,12 @@ namespace BTCPayServer.Tests
await viewOnlyClient.SendHttpRequest($"api/v1/stores/{store.Id}/payment-methods/onchain/BTC/preview", new JObject() { ["config"] = xpub.ToString() }, HttpMethod.Post);
var method = await client.UpdateStorePaymentMethod(store.Id, "BTC-CHAIN", new UpdatePaymentMethodRequest() { Enabled = true, Config = JValue.CreateString(xpub.ToString())});
var method2 = await client.UpdateStorePaymentMethod(store.Id, "BTC-CHAIN", new UpdatePaymentMethodRequest() { Enabled = true, Config = new JObject() { ["derivationScheme"] = xpub.ToString(), ["label"] = "test", ["accountKeyPath"] = "aaaaaaaa/84'/0'/0'" } });
Assert.Equal("aaaaaaaa", method2.Config["accountKeySettings"][0]["rootFingerprint"].ToString());
Assert.Equal("84'/0'/0'", method2.Config["accountKeySettings"][0]["accountKeyPath"].ToString());
var method3 = await client.UpdateStorePaymentMethod(store.Id, "BTC-CHAIN", new UpdatePaymentMethodRequest() { Enabled = true, Config = new JObject() { ["derivationScheme"] = xpub.ToString() } });
Assert.Equal(method.ToJson(), method3.ToJson());
Assert.Equal(firstAddress, (await viewOnlyClient.PreviewStoreOnChainPaymentMethodAddresses(store.Id, "BTC")).Addresses.First().Address);
await AssertHttpError(403, async () =>
@ -3378,7 +3384,6 @@ namespace BTCPayServer.Tests
Assert.Equal(24, generateResponse.Mnemonic.Words.Length);
Assert.Equal(Wordlist.Japanese, generateResponse.Mnemonic.WordList);
}
[Fact(Timeout = 60 * 2 * 1000)]

View file

@ -15,6 +15,7 @@ using StoreData = BTCPayServer.Data.StoreData;
using BTCPayServer.ModelBinders;
using BTCPayServer.Payments;
using BTCPayServer.Services.Stores;
using System;
namespace BTCPayServer.Controllers.Greenfield
{
@ -102,9 +103,9 @@ namespace BTCPayServer.Controllers.Greenfield
if (ctx.StripUnknownProperties)
config = JToken.FromObject(handler.ParsePaymentMethodConfig(config), handler.Serializer);
}
catch
catch (Exception ex)
{
ModelState.AddModelError(nameof(config), "Invalid configuration");
ModelState.AddModelError(nameof(config), $"Invalid configuration ({ex.Message})");
return this.CreateValidationError(ModelState);
}
Store.SetPaymentMethodConfig(paymentMethodId, config);
@ -151,7 +152,7 @@ namespace BTCPayServer.Controllers.Greenfield
method => new GenericPaymentMethodData()
{
PaymentMethodId = method.Key.ToString(),
Enabled = onlyEnabled.GetValueOrDefault(!excludedPaymentMethods.Match(method.Key)),
Enabled = !excludedPaymentMethods.Match(method.Key),
Config = includeConfig is true ? JToken.FromObject(method.Value, _handlers[method.Key].Serializer.ForAPI()) : null
}).ToArray());
}

View file

@ -408,6 +408,7 @@ public partial class UIStoresController
var client = _explorerProvider.GetExplorerClient(network);
var handler = _handlers.GetBitcoinHandler(cryptoCode);
var vm = new WalletSettingsViewModel
{
StoreId = storeId,
@ -417,18 +418,18 @@ public partial class UIStoresController
Network = network,
IsHotWallet = derivation.IsHotWallet,
Source = derivation.Source,
RootFingerprint = derivation.GetSigningAccountKeySettings().RootFingerprint.ToString(),
DerivationScheme = derivation.AccountDerivation.ToString(),
RootFingerprint = derivation.GetSigningAccountKeySettingsOrDefault()?.RootFingerprint.ToString(),
DerivationScheme = derivation.AccountDerivation?.ToString(),
DerivationSchemeInput = derivation.AccountOriginal,
KeyPath = derivation.GetSigningAccountKeySettings().AccountKeyPath?.ToString(),
KeyPath = derivation.GetSigningAccountKeySettingsOrDefault()?.AccountKeyPath?.ToString(),
UriScheme = network.NBitcoinNetwork.UriScheme,
Label = derivation.Label,
SelectedSigningKey = derivation.SigningKey.ToString(),
SelectedSigningKey = derivation.SigningKey?.ToString(),
NBXSeedAvailable = derivation.IsHotWallet &&
canUseHotWallet &&
!string.IsNullOrEmpty(await client.GetMetadataAsync<string>(derivation.AccountDerivation,
WellknownMetadataKeys.MasterHDKey)),
AccountKeys = derivation.AccountKeySettings
AccountKeys = (derivation.AccountKeySettings ?? [])
.Select(e => new WalletSettingsAccountKeyViewModel
{
AccountKey = e.AccountKey.ToString(),
@ -441,7 +442,7 @@ public partial class UIStoresController
CanUseHotWallet = canUseHotWallet,
CanUseRPCImport = rpcImport,
StoreName = store.StoreName,
CanSetupMultiSig = derivation.AccountKeySettings.Length > 1,
CanSetupMultiSig = (derivation.AccountKeySettings ?? []).Length > 1,
IsMultiSigOnServer = derivation.IsMultiSigOnServer,
DefaultIncludeNonWitnessUtxo = derivation.DefaultIncludeNonWitnessUtxo
};

View file

@ -80,7 +80,12 @@ namespace BTCPayServer
public AccountKeySettings GetSigningAccountKeySettings()
{
return AccountKeySettings.Single(a => a.AccountKey == SigningKey);
return (AccountKeySettings ?? []).Single(a => a.AccountKey == SigningKey);
}
public AccountKeySettings GetSigningAccountKeySettingsOrDefault()
{
return (AccountKeySettings ?? []).SingleOrDefault(a => a.AccountKey == SigningKey);
}
public AccountKeySettings[] AccountKeySettings { get; set; }

View file

@ -23,6 +23,7 @@ using NBXplorer.DerivationStrategy;
using NBXplorer.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static Org.BouncyCastle.Math.EC.ECCurve;
using StoreData = BTCPayServer.Data.StoreData;
namespace BTCPayServer.Payments.Bitcoin
@ -233,16 +234,50 @@ namespace BTCPayServer.Payments.Bitcoin
return null;
return GetAccountDerivation(value, network);
}
class AlternativeConfig
{
[JsonProperty]
public DerivationStrategyBase DerivationScheme { get; set; }
[JsonProperty]
public string Label { get; set; }
[JsonProperty]
public RootedKeyPath AccountKeyPath { get; set; }
public DerivationSchemeSettings ToDerivationSchemeSettings(BTCPayNetwork network)
{
if (DerivationScheme is null)
return null;
var scheme = new DerivationSchemeSettings(DerivationScheme, network);
scheme.AccountOriginal = DerivationScheme.ToString();
scheme.Label = Label;
if (AccountKeyPath is not null && scheme.AccountKeySettings is [{ } ak, ..])
{
ak.RootFingerprint = AccountKeyPath.MasterFingerprint;
ak.AccountKeyPath = AccountKeyPath.KeyPath;
}
return scheme;
}
}
public Task ValidatePaymentMethodConfig(PaymentMethodConfigValidationContext validationContext)
{
var parser = Network.GetDerivationSchemeParser();
DerivationSchemeSettings settings = new DerivationSchemeSettings();
if (parser.TryParseXpub(validationContext.Config.ToString(), ref settings))
if (validationContext.Config is JValue { Type: JTokenType.String, Value: string config }
&& parser.TryParseXpub(config, ref settings))
{
validationContext.Config = JToken.FromObject(settings, Serializer);
return Task.CompletedTask;
}
var res = validationContext.Config.ToObject<DerivationSchemeSettings>(Serializer);
DerivationSchemeSettings res = null;
try
{
res = validationContext.Config.ToObject<AlternativeConfig>(Serializer)?.ToDerivationSchemeSettings(Network);
if (res != null)
validationContext.Config = JToken.FromObject(res, Serializer);
}
catch { }
res ??= validationContext.Config.ToObject<DerivationSchemeSettings>(Serializer);
if (res is null)
{
validationContext.ModelState.AddModelError(nameof(validationContext.Config), "Invalid derivation scheme settings");
@ -252,6 +287,18 @@ namespace BTCPayServer.Payments.Bitcoin
{
validationContext.ModelState.AddModelError(nameof(res.AccountDerivation), "Invalid account derivation");
}
if (res.AccountKeySettings is null)
{
validationContext.ModelState.AddModelError(nameof(res.AccountKeySettings), "Invalid AccountKeySettings");
}
if (res.SigningKey is null)
{
validationContext.ModelState.AddModelError(nameof(res.SigningKey), "Invalid SigningKey");
}
if (res.GetSigningAccountKeySettingsOrDefault() is null)
{
validationContext.ModelState.AddModelError(nameof(res.AccountKeySettings), "AccountKeySettings doesn't include the SigningKey");
}
return Task.CompletedTask;
}

View file

@ -3,7 +3,7 @@
"/api/v1/stores/{storeId}/payment-methods": {
"get": {
"tags": [
"Store Payment Methods"
"Store (Payment Methods)"
],
"summary": "Get store payment methods",
"description": "View information about the stores' configured payment methods",
@ -79,7 +79,7 @@
"/api/v1/stores/{storeId}/payment-methods/{paymentMethodId}": {
"get": {
"tags": [
"Store Payment Methods"
"Store (Payment Methods)"
],
"summary": "Get store payment method",
"description": "View information about the stores' configured payment method",
@ -144,7 +144,7 @@
},
"put": {
"tags": [
"Store Payment Methods"
"Store (Payment Methods)"
],
"summary": "Update store's payment method",
"description": "Update information about the stores' configured payment method",
@ -209,7 +209,7 @@
},
"delete": {
"tags": [
"Store Payment Methods"
"Store (Payment Methods)"
],
"summary": "Delete store's payment method",
"description": "Delete information about the stores' configured payment method",
@ -369,7 +369,7 @@
},
"tags": [
{
"name": "Store Payment Methods",
"name": "Store (Payment Methods)",
"description": "Store Payment Methods operations"
}
]