Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
95c50dcc0d | |||
71a192d0ba | |||
83dd80ae86 | |||
ccfa85b5e0 | |||
e59821caa1 | |||
2215e5e069 | |||
48b2e682bf | |||
5750da574a | |||
b40095f603 | |||
ec11585223 |
@ -0,0 +1,63 @@
|
||||
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<IEnumerable<LightningNetworkPaymentMethodData>>
|
||||
GetStoreLightningNetworkPaymentMethods(string storeId,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork"), token);
|
||||
return await HandleResponse<IEnumerable<LightningNetworkPaymentMethodData>>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningNetworkPaymentMethodData> GetStoreLightningNetworkPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}"), token);
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task RemoveStoreLightningNetworkPaymentMethod(string storeId,
|
||||
string cryptoCode, CancellationToken token = default)
|
||||
{
|
||||
var response =
|
||||
await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}",
|
||||
method: HttpMethod.Delete), token);
|
||||
await HandleResponse(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningNetworkPaymentMethodData> UpdateStoreLightningNetworkPaymentMethod(
|
||||
string storeId,
|
||||
string cryptoCode, LightningNetworkPaymentMethodData paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}",
|
||||
bodyPayload: paymentMethod, method: HttpMethod.Put), token);
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<LightningNetworkPaymentMethodData>
|
||||
UpdateStoreLightningNetworkPaymentMethodToInternalNode(string storeId,
|
||||
string cryptoCode, LightningNetworkPaymentMethodData paymentMethod,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(
|
||||
CreateHttpRequest($"api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}/internal",
|
||||
method: HttpMethod.Put), token);
|
||||
return await HandleResponse<LightningNetworkPaymentMethodData>(response);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LightningNetworkPaymentMethodData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the payment method is enabled
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Crypto code of the payment method
|
||||
/// </summary>
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public string ConnectionString { get; set; }
|
||||
|
||||
public LightningNetworkPaymentMethodData()
|
||||
{
|
||||
}
|
||||
|
||||
public LightningNetworkPaymentMethodData(string cryptoCode, string connectionString, bool enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
CryptoCode = cryptoCode;
|
||||
ConnectionString = connectionString;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
using System.Reflection;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Plugins.Test.Migrations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Design;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BTCPayServer.Plugins.Test
|
||||
{
|
||||
|
@ -1266,10 +1266,70 @@ namespace BTCPayServer.Tests
|
||||
await client.GetStoreOnChainPaymentMethod(store.Id, "BTC");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[Fact(Timeout = 60 * 2 * 1000)]
|
||||
[Trait("Lightning", "Lightning")]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task LightningNetworkPaymentMethodAPITests()
|
||||
{
|
||||
using var tester = ServerTester.Create();
|
||||
tester.ActivateLightning();
|
||||
await tester.StartAsync();
|
||||
await tester.EnsureChannelsSetup();
|
||||
var user = tester.NewAccount();
|
||||
await user.GrantAccessAsync(true);
|
||||
var client = await user.CreateClient(Policies.CanModifyStoreSettings);
|
||||
var viewOnlyClient = await user.CreateClient(Policies.CanViewStoreSettings);
|
||||
tester.PayTester.GetService<BTCPayServerEnvironment>().DevelopmentOverride = false;
|
||||
var store = await client.GetStore(user.StoreId);
|
||||
|
||||
Assert.Empty(await client.GetStoreLightningNetworkPaymentMethods(store.Id));
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnlyClient.UpdateStoreLightningNetworkPaymentMethod(store.Id, "BTC", new LightningNetworkPaymentMethodData() { });
|
||||
});
|
||||
await AssertHttpError(404, async () =>
|
||||
{
|
||||
await client.GetStoreLightningNetworkPaymentMethod(store.Id, "BTC");
|
||||
});
|
||||
await user.RegisterLightningNodeAsync("BTC", LightningConnectionType.CLightning, false);
|
||||
|
||||
var method = await client.GetStoreLightningNetworkPaymentMethod(store.Id, "BTC");
|
||||
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");
|
||||
});
|
||||
|
||||
var settings = (await tester.PayTester.GetService<SettingsRepository>().GetSettingAsync<PoliciesSettings>())?? new PoliciesSettings();
|
||||
settings.AllowLightningInternalNodeForAll = false;
|
||||
await tester.PayTester.GetService<SettingsRepository>().UpdateSetting(settings);
|
||||
var nonAdminUser = tester.NewAccount();
|
||||
await nonAdminUser.GrantAccessAsync(false);
|
||||
var nonAdminUserClient= await nonAdminUser.CreateClient(Policies.CanModifyStoreSettings);
|
||||
|
||||
await AssertHttpError(404, async () =>
|
||||
{
|
||||
await nonAdminUserClient.GetStoreLightningNetworkPaymentMethod(nonAdminUser.StoreId, "BTC");
|
||||
});
|
||||
await Assert.ThrowsAsync<GreenFieldValidationException>(async () =>
|
||||
{
|
||||
await nonAdminUserClient.UpdateStoreLightningNetworkPaymentMethod(nonAdminUser.StoreId, "BTC", method);
|
||||
});
|
||||
|
||||
settings = await tester.PayTester.GetService<SettingsRepository>().GetSettingAsync<PoliciesSettings>();
|
||||
settings.AllowLightningInternalNodeForAll = true;
|
||||
await tester.PayTester.GetService<SettingsRepository>().UpdateSetting(settings);
|
||||
|
||||
await nonAdminUserClient.UpdateStoreLightningNetworkPaymentMethod(nonAdminUser.StoreId, "BTC", method);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
[Trait("Fast", "Fast")]
|
||||
public void NumericJsonConverterTests()
|
||||
|
@ -63,4 +63,8 @@ The `./docker-lightning-channel-teardown.sh` script closes any existing lightnin
|
||||
2. Run `docker-compose pull` (this will ensure you have the lastest images)
|
||||
3. Run again with `docker-compose up dev`
|
||||
|
||||
How to run the Altcoin environment?
|
||||
|
||||
`docker-compose -f docker-compose.altcoins.yml up dev`
|
||||
|
||||
If you still have issues, try to restart docker.
|
||||
|
@ -257,7 +257,7 @@ namespace BTCPayServer.Tests
|
||||
RegisterLightningNodeAsync(cryptoCode, connectionType, isMerchant).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public async Task RegisterLightningNodeAsync(string cryptoCode, LightningConnectionType connectionType, bool isMerchant = true)
|
||||
public async Task RegisterLightningNodeAsync(string cryptoCode, LightningConnectionType connectionType, bool isMerchant = true, string storeId = null)
|
||||
{
|
||||
var storeController = this.GetController<StoresController>();
|
||||
|
||||
@ -288,8 +288,8 @@ namespace BTCPayServer.Tests
|
||||
else
|
||||
throw new NotSupportedException(connectionType.ToString());
|
||||
|
||||
await storeController.AddLightningNode(StoreId,
|
||||
new LightningNodeViewModel() { ConnectionString = connectionString, SkipPortTest = true }, "save", "BTC");
|
||||
await storeController.AddLightningNode(storeId ?? StoreId,
|
||||
new LightningNodeViewModel() {ConnectionString = connectionString, SkipPortTest = true}, "save", "BTC");
|
||||
if (storeController.ModelState.ErrorCount != 0)
|
||||
Assert.False(true, storeController.ModelState.FirstOrDefault().Value.Errors[0].ErrorMessage);
|
||||
}
|
||||
|
@ -0,0 +1,246 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Controllers.GreenField
|
||||
{
|
||||
[ApiController]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
public class StoreLightningNetworkPaymentMethodsController : ControllerBase
|
||||
{
|
||||
private StoreData Store => HttpContext.GetStoreData();
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
|
||||
private readonly IOptions<LightningNetworkOptions> _lightningNetworkOptions;
|
||||
private readonly CssThemeManager _cssThemeManager;
|
||||
private readonly BTCPayServerEnvironment _btcPayServerEnvironment;
|
||||
|
||||
public StoreLightningNetworkPaymentMethodsController(
|
||||
StoreRepository storeRepository,
|
||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
IOptions<LightningNetworkOptions> lightningNetworkOptions,
|
||||
CssThemeManager cssThemeManager,
|
||||
BTCPayServerEnvironment btcPayServerEnvironment)
|
||||
{
|
||||
_storeRepository = storeRepository;
|
||||
_btcPayNetworkProvider = btcPayNetworkProvider;
|
||||
_lightningNetworkOptions = lightningNetworkOptions;
|
||||
_cssThemeManager = cssThemeManager;
|
||||
_btcPayServerEnvironment = btcPayServerEnvironment;
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork")]
|
||||
public ActionResult<IEnumerable<LightningNetworkPaymentMethodData>> GetLightningPaymentMethods(
|
||||
[FromQuery] bool enabledOnly = false)
|
||||
{
|
||||
var blob = Store.GetStoreBlob();
|
||||
var excludedPaymentMethods = blob.GetExcludedPaymentMethods();
|
||||
return Ok(Store.GetSupportedPaymentMethods(_btcPayNetworkProvider)
|
||||
.Where((method) => method.PaymentId.PaymentType == PaymentTypes.LightningLike)
|
||||
.OfType<LightningSupportedPaymentMethod>()
|
||||
.Select(paymentMethod =>
|
||||
new LightningNetworkPaymentMethodData(paymentMethod.PaymentId.CryptoCode,
|
||||
paymentMethod.GetLightningUrl().ToString(), !excludedPaymentMethods.Match(paymentMethod.PaymentId)))
|
||||
.Where((result) => !enabledOnly || result.Enabled)
|
||||
.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}")]
|
||||
public ActionResult<LightningNetworkPaymentMethodData> GetLightningNetworkPaymentMethod(string cryptoCode)
|
||||
{
|
||||
if (!GetNetwork(cryptoCode, out BTCPayNetwork _))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var method = GetExistingLightningLikePaymentMethod(cryptoCode);
|
||||
if (method is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(method);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpDelete("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}")]
|
||||
public async Task<IActionResult> RemoveLightningNetworkPaymentMethod(
|
||||
string cryptoCode,
|
||||
int offset = 0, int amount = 10)
|
||||
{
|
||||
if (!GetNetwork(cryptoCode, out BTCPayNetwork _))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var id = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
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/LightningNetwork/{cryptoCode}")]
|
||||
public async Task<IActionResult> UpdateLightningNetworkPaymentMethod(string cryptoCode,
|
||||
[FromBody] LightningNetworkPaymentMethodData paymentMethodData)
|
||||
{
|
||||
var id = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
|
||||
if (!GetNetwork(cryptoCode, out var network))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var internalLightning = GetInternalLightningNode(network.CryptoCode);
|
||||
|
||||
if (string.IsNullOrEmpty(paymentMethodData?.ConnectionString))
|
||||
{
|
||||
ModelState.AddModelError(nameof(LightningNetworkPaymentMethodData.ConnectionString),
|
||||
"Missing connectionString");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return this.CreateValidationError(ModelState);
|
||||
|
||||
|
||||
PaymentMethodId paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike);
|
||||
Payments.Lightning.LightningSupportedPaymentMethod paymentMethod = null;
|
||||
if (!string.IsNullOrEmpty(paymentMethodData.ConnectionString))
|
||||
{
|
||||
if (!LightningConnectionString.TryParse(paymentMethodData.ConnectionString, false,
|
||||
out var connectionString, out var error))
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), $"Invalid URL ({error})");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
if (connectionString.ConnectionType == LightningConnectionType.LndGRPC)
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString),
|
||||
$"BTCPay does not support gRPC connections");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
bool isInternalNode = connectionString.IsInternalNode(internalLightning);
|
||||
|
||||
if (connectionString.BaseUri.Scheme == "http")
|
||||
{
|
||||
if (!isInternalNode && !connectionString.AllowInsecure)
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), "The url must be HTTPS");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
}
|
||||
|
||||
if (connectionString.MacaroonFilePath != null)
|
||||
{
|
||||
if (!CanUseInternalLightning())
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString),
|
||||
"You are not authorized to use macaroonfilepath");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
if (!System.IO.File.Exists(connectionString.MacaroonFilePath))
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString),
|
||||
"The macaroonfilepath file does not exist");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
if (!System.IO.Path.IsPathRooted(connectionString.MacaroonFilePath))
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString),
|
||||
"The macaroonfilepath should be fully rooted");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
}
|
||||
|
||||
if (isInternalNode && !CanUseInternalLightning())
|
||||
{
|
||||
ModelState.AddModelError(nameof(paymentMethodData.ConnectionString), "Unauthorized url");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
paymentMethod = new Payments.Lightning.LightningSupportedPaymentMethod()
|
||||
{
|
||||
CryptoCode = paymentMethodId.CryptoCode
|
||||
};
|
||||
paymentMethod.SetLightningUrl(connectionString);
|
||||
}
|
||||
|
||||
var store = Store;
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
store.SetSupportedPaymentMethod(id, paymentMethod);
|
||||
storeBlob.SetExcluded(id, !paymentMethodData.Enabled);
|
||||
store.SetStoreBlob(storeBlob);
|
||||
await _storeRepository.UpdateStore(store);
|
||||
return Ok(GetExistingLightningLikePaymentMethod(cryptoCode, store));
|
||||
}
|
||||
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpPut("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}/internal")]
|
||||
public Task<IActionResult> UpdateLightningNetworkPaymentMethodToInternalNode(string cryptoCode)
|
||||
{
|
||||
return UpdateLightningNetworkPaymentMethod(cryptoCode,
|
||||
new LightningNetworkPaymentMethodData(cryptoCode, GetInternalLightningNode(cryptoCode).ToString(), true));
|
||||
}
|
||||
|
||||
private LightningNetworkPaymentMethodData GetExistingLightningLikePaymentMethod(string cryptoCode, StoreData store = null)
|
||||
{
|
||||
store ??= Store;
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var id = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
|
||||
var paymentMethod = store
|
||||
.GetSupportedPaymentMethods(_btcPayNetworkProvider)
|
||||
.OfType<LightningSupportedPaymentMethod>()
|
||||
.FirstOrDefault(method => method.PaymentId == id);
|
||||
|
||||
var excluded = storeBlob.IsExcluded(id);
|
||||
return paymentMethod == null
|
||||
? null
|
||||
: new LightningNetworkPaymentMethodData(paymentMethod.PaymentId.CryptoCode,
|
||||
paymentMethod.GetLightningUrl().ToString(), !excluded);
|
||||
}
|
||||
|
||||
private bool GetNetwork(string cryptoCode, out BTCPayNetwork network)
|
||||
{
|
||||
network = _btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
network = network?.SupportLightning is true ? network : null;
|
||||
return network != null;
|
||||
}
|
||||
|
||||
private LightningConnectionString GetInternalLightningNode(string cryptoCode)
|
||||
{
|
||||
if (_lightningNetworkOptions.Value.InternalLightningByCryptoCode.TryGetValue(cryptoCode, out var connectionString))
|
||||
{
|
||||
return CanUseInternalLightning() ? connectionString : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool CanUseInternalLightning()
|
||||
{
|
||||
return (_btcPayServerEnvironment.IsDeveloping || User.IsInRole(Roles.ServerAdmin) || _cssThemeManager.AllowLightningInternalNodeForAll);
|
||||
}
|
||||
}
|
||||
}
|
@ -165,12 +165,12 @@ namespace BTCPayServer.Controllers.GreenField
|
||||
});
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
|
||||
[HttpDelete("~/api/v1/stores/{storeId}/payment-methods/onchain/{cryptoCode}")]
|
||||
public async Task<ActionResult<OnChainPaymentMethodPreviewResultData>> RemoveOnChainPaymentMethod(
|
||||
public async Task<IActionResult> RemoveOnChainPaymentMethod(
|
||||
string cryptoCode,
|
||||
int offset = 0, int amount = 10)
|
||||
{
|
||||
|
@ -75,9 +75,14 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
vm.CryptoCode = network.CryptoCode;
|
||||
|
||||
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
if (derivationSchemeSettings == null)
|
||||
return NotFound();
|
||||
|
||||
vm.NBXSeedAvailable = await CanUseHotWallet() && !string.IsNullOrEmpty(await ExplorerClientProvider.GetExplorerClient(network)
|
||||
.GetMetadataAsync<string>(GetDerivationSchemeSettings(walletId).AccountDerivation,
|
||||
WellknownMetadataKeys.Mnemonic));
|
||||
.GetMetadataAsync<string>(derivationSchemeSettings.AccountDerivation, WellknownMetadataKeys.Mnemonic));
|
||||
|
||||
if (await vm.GetPSBT(network.NBitcoinNetwork) is PSBT psbt)
|
||||
{
|
||||
vm.Decoded = psbt.ToString();
|
||||
@ -98,9 +103,13 @@ namespace BTCPayServer.Controllers
|
||||
return await WalletPSBT(walletId, vm);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
vm.CryptoCode = network.CryptoCode;
|
||||
|
||||
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
if (derivationSchemeSettings == null)
|
||||
return NotFound();
|
||||
|
||||
vm.NBXSeedAvailable = await CanUseHotWallet() && !string.IsNullOrEmpty(await ExplorerClientProvider.GetExplorerClient(network)
|
||||
.GetMetadataAsync<string>(GetDerivationSchemeSettings(walletId).AccountDerivation,
|
||||
WellknownMetadataKeys.Mnemonic));
|
||||
.GetMetadataAsync<string>(derivationSchemeSettings.AccountDerivation, WellknownMetadataKeys.Mnemonic));
|
||||
var psbt = await vm.GetPSBT(network.NBitcoinNetwork);
|
||||
if (psbt == null)
|
||||
{
|
||||
@ -127,7 +136,6 @@ namespace BTCPayServer.Controllers
|
||||
return View(vm);
|
||||
|
||||
case "update":
|
||||
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
psbt = await ExplorerClientProvider.UpdatePSBT(derivationSchemeSettings, psbt);
|
||||
if (psbt == null)
|
||||
{
|
||||
|
@ -27,7 +27,10 @@ namespace BTCPayServer.Controllers
|
||||
public IActionResult NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId)
|
||||
{
|
||||
return View(new NewPullPaymentModel()
|
||||
if (GetDerivationSchemeSettings(walletId) == null)
|
||||
return NotFound();
|
||||
|
||||
return View(new NewPullPaymentModel
|
||||
{
|
||||
Name = "",
|
||||
Currency = "BTC",
|
||||
@ -41,6 +44,9 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<IActionResult> NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, NewPullPaymentModel model)
|
||||
{
|
||||
if (GetDerivationSchemeSettings(walletId) == null)
|
||||
return NotFound();
|
||||
|
||||
model.Name ??= string.Empty;
|
||||
model.Currency = model.Currency.ToUpperInvariant().Trim();
|
||||
if (_currencyTable.GetCurrencyData(model.Currency, false) is null)
|
||||
@ -99,7 +105,10 @@ namespace BTCPayServer.Controllers
|
||||
.Where(p => p.State == PayoutState.Completed || p.State == PayoutState.InProgress)
|
||||
})
|
||||
.ToListAsync();
|
||||
var vm = new PullPaymentsModel();
|
||||
|
||||
var vm = new PullPaymentsModel
|
||||
{ HasDerivationSchemeSettings = GetDerivationSchemeSettings(walletId) != null };
|
||||
|
||||
foreach (var o in pps)
|
||||
{
|
||||
var pp = o.PullPayment;
|
||||
@ -175,8 +184,9 @@ namespace BTCPayServer.Controllers
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, PayoutsModel vm, CancellationToken cancellationToken)
|
||||
{
|
||||
if (vm is null)
|
||||
if (vm is null || GetDerivationSchemeSettings(walletId) == null)
|
||||
return NotFound();
|
||||
|
||||
var storeId = walletId.StoreId;
|
||||
var paymentMethodId = new PaymentMethodId(walletId.CryptoCode, PaymentTypes.BTCLike);
|
||||
var payoutIds = vm.WaitingForApproval.Where(p => p.Selected).Select(p => p.PayoutId).ToArray();
|
||||
|
@ -149,7 +149,7 @@ namespace BTCPayServer.Hosting
|
||||
: Path.Combine(datadirs.Value.DataDir, sqliteFileName));
|
||||
|
||||
options.DatabaseType = DatabaseType.Sqlite;
|
||||
options.ConnectionString = sqliteFileName;
|
||||
options.ConnectionString = connStr;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -28,6 +28,8 @@ namespace BTCPayServer.Models.WalletViewModels
|
||||
}
|
||||
|
||||
public List<PullPaymentModel> PullPayments { get; set; } = new List<PullPaymentModel>();
|
||||
|
||||
public bool HasDerivationSchemeSettings { get; set; }
|
||||
}
|
||||
|
||||
public class NewPullPaymentModel
|
||||
|
@ -413,7 +413,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await GetInvoiceRaw(invoiceId);
|
||||
var invoiceData = await GetInvoiceRaw(invoiceId, context);
|
||||
if (invoiceData == null || (storeId != null &&
|
||||
!invoiceData.StoreDataId.Equals(storeId,
|
||||
StringComparison.InvariantCultureIgnoreCase)))
|
||||
@ -429,7 +429,7 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
var invoiceData = await GetInvoiceRaw(invoiceId);
|
||||
var invoiceData = await GetInvoiceRaw(invoiceId, context);
|
||||
if (invoiceData == null)
|
||||
{
|
||||
return false;
|
||||
@ -472,8 +472,11 @@ namespace BTCPayServer.Services.Invoices
|
||||
|
||||
public async Task<InvoiceEntity> GetInvoice(string id, bool inludeAddressData = false)
|
||||
{
|
||||
var res = await GetInvoiceRaw(id, inludeAddressData);
|
||||
return res == null ? null : ToEntity(res);
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
var res = await GetInvoiceRaw(id, context, inludeAddressData);
|
||||
return res == null ? null : ToEntity(res);
|
||||
}
|
||||
}
|
||||
public async Task<InvoiceEntity[]> GetInvoices(string[] invoiceIds)
|
||||
{
|
||||
@ -490,24 +493,21 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<InvoiceData> GetInvoiceRaw(string id, bool inludeAddressData = false)
|
||||
private async Task<InvoiceData> GetInvoiceRaw(string id, ApplicationDbContext dbContext, bool inludeAddressData = false)
|
||||
{
|
||||
using (var context = _ContextFactory.CreateContext())
|
||||
{
|
||||
IQueryable<Data.InvoiceData> query =
|
||||
context
|
||||
IQueryable<Data.InvoiceData> query =
|
||||
dbContext
|
||||
.Invoices
|
||||
.Include(o => o.Payments);
|
||||
if (inludeAddressData)
|
||||
query = query.Include(o => o.HistoricalAddressInvoices).Include(o => o.AddressInvoices);
|
||||
query = query.Where(i => i.Id == id);
|
||||
if (inludeAddressData)
|
||||
query = query.Include(o => o.HistoricalAddressInvoices).Include(o => o.AddressInvoices);
|
||||
query = query.Where(i => i.Id == id);
|
||||
|
||||
var invoice = (await query.ToListAsync()).FirstOrDefault();
|
||||
if (invoice == null)
|
||||
return null;
|
||||
var invoice = (await query.ToListAsync()).FirstOrDefault();
|
||||
if (invoice == null)
|
||||
return null;
|
||||
|
||||
return invoice;
|
||||
}
|
||||
return invoice;
|
||||
}
|
||||
|
||||
private InvoiceEntity ToEntity(Data.InvoiceData invoice)
|
||||
|
@ -18,14 +18,17 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row button-row">
|
||||
<div class="col-lg-12">
|
||||
<a asp-action="NewPullPayment"
|
||||
asp-route-walletId="@this.Context.GetRouteValue("walletId")"
|
||||
class="btn btn-primary" role="button" id="NewPullPayment"><span class="fa fa-plus"></span> Create a new pull payment</a>
|
||||
<a href="https://docs.btcpayserver.org/PullPayments/" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
|
||||
@if (Model.HasDerivationSchemeSettings)
|
||||
{
|
||||
<div class="row button-row">
|
||||
<div class="col-lg-12">
|
||||
<a asp-action="NewPullPayment"
|
||||
asp-route-walletId="@Context.GetRouteValue("walletId")"
|
||||
class="btn btn-primary" role="button" id="NewPullPayment"><span class="fa fa-plus"></span> Create a new pull payment</a>
|
||||
<a href="https://docs.btcpayserver.org/PullPayments/" target="_blank"><span class="fa fa-question-circle-o" title="More information..."></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
@foreach (var pp in Model.PullPayments)
|
||||
|
@ -0,0 +1,334 @@
|
||||
{
|
||||
"paths": {
|
||||
"/api/v1/stores/{storeId}/payment-methods/LightningNetwork": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Store Payment Methods (Lightning Network)"
|
||||
],
|
||||
"summary": "Get store Lightning Network payment methods",
|
||||
"description": "View information about the stores' configured Lightning Network payment methods",
|
||||
"operationId": "StoreLightningNetworkPaymentMethods_GetLightningNetworkPaymentMethods",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "list of payment methods",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/LightningNetworkPaymentMethodDataList"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"API Key": [
|
||||
"btcpay.store.canviewstoresettings"
|
||||
],
|
||||
"Basic": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Store Payment Methods (Lightning Network)"
|
||||
],
|
||||
"summary": "Get store Lightning Network 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": "StoreLightningNetworkPaymentMethods_GetLightningNetworkPaymentMethod",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "specified payment method",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/LightningNetworkPaymentMethodData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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 (Lightning Network)"
|
||||
],
|
||||
"summary": "Update store Lightning Network 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/LightningNetworkPaymentMethodData"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-position": 1
|
||||
},
|
||||
"operationId": "StoreLightningNetworkPaymentMethods_UpdateLightningNetworkPaymentMethod",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "updated specified payment method",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/LightningNetworkPaymentMethodData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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 (Lightning Network)"
|
||||
],
|
||||
"summary": "Remove store Lightning Network 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/LightningNetwork/{cryptoCode}/internal": {
|
||||
"put": {
|
||||
"tags": [
|
||||
"Store Payment Methods (Lightning Network)"
|
||||
],
|
||||
"summary": "Update store Lightning Network payment method to internal node",
|
||||
"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/LightningNetworkPaymentMethodData"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true,
|
||||
"x-position": 1
|
||||
},
|
||||
"operationId": "StoreLightningNetworkPaymentMethods_UpdateLightningNetworkPaymentMethodToInternalNode",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "updated specified payment method",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/LightningNetworkPaymentMethodData"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"LightningNetworkPaymentMethodDataList": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/LightningNetworkPaymentMethodData"
|
||||
}
|
||||
},
|
||||
"LightningNetworkPaymentMethodData": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the payment method is enabled"
|
||||
},
|
||||
"cryptoCode": {
|
||||
"type": "string",
|
||||
"description": "Crypto code of the payment method"
|
||||
},
|
||||
"connectionString": {
|
||||
"type": "string",
|
||||
"description": "The lightning connection string",
|
||||
"example": "type=clightning;server=..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
{
|
||||
"name": "Store Payment Methods (Lightning Network)"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>1.0.6.5</Version>
|
||||
<Version>1.0.6.6</Version>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
14
Changelog.md
14
Changelog.md
@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.6.6
|
||||
|
||||
### Bug fixes:
|
||||
|
||||
* Load correct connection string when using SQLite @Kukks
|
||||
* Greenfeld API: Invoice Metadata update was not updating @saliehendricks
|
||||
* Prevent access to wallet pags if wallet not set @dennisreimann
|
||||
|
||||
### New features
|
||||
|
||||
* Greenfield API: Can configure lightning payment methods @Kukks
|
||||
|
||||
## 1.0.6.5:
|
||||
|
||||
### Improvements:
|
||||
@ -18,7 +30,7 @@
|
||||
* Do not include Tor Location header when querying the modal checkout (see #2180) @Kukks
|
||||
* Webhooks should not be randomly deleted anymore. @NicolasDorier
|
||||
* Fix header not showing properly after login to BTCPay Server (see #2155) @dennisreimann
|
||||
|
||||
* Bug: Searching invoices was timing out if there was too much invoices @rockstardev @Kukks
|
||||
|
||||
### Miscellaneous:
|
||||
|
||||
|
Reference in New Issue
Block a user