Merge pull request #2194 from btcpayserver/gf/ln-payment-emthod

GreenField API: Configure Store Lightning Payment Method
This commit is contained in:
Andrew Camilleri 2021-01-14 10:07:00 +01:00 committed by GitHub
commit 2215e5e069
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 744 additions and 7 deletions

View File

@ -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);
}
}
}

View File

@ -0,0 +1,31 @@
using NBitcoin;
using Newtonsoft.Json;
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;
}
}
}

View File

@ -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()

View File

@ -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);
}

View File

@ -0,0 +1,249 @@
using System;
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 BTCPayServer.Services.Wallets;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using NBitcoin;
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);
}
}
}

View File

@ -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)
{

View File

@ -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)"
}
]
}