Skip to content

Commit

Permalink
Add Greenfield API endpoint for pull payment LNURL items (#4472)
Browse files Browse the repository at this point in the history
* Add Greenfield API endpoint for pull payment LNURL items

close #4365

* Rename GetLNURLs to GetPullPaymentLNURL

* update "ln-url-not-supported" to "lnurl-not-supported"

* remove hardcoding of "BTC"

* update "PullPayments_LNURL" to "PullPayments_GetPullPaymentLNURL"

* update description of 400 status code response

Co-authored-by: Nicolas Dorier <nicolas.dorier@gmail.com>
  • Loading branch information
bolatovumar and NicolasDorier committed Jan 26, 2023
1 parent de4ac2c commit 438dcc4
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 3 deletions.
10 changes: 10 additions & 0 deletions BTCPayServer.Client/BTCPayServerClient.PullPayments.cs
Expand Up @@ -97,5 +97,15 @@ public virtual async Task<PayoutData> ApprovePayout(string storeId, string payou
method: HttpMethod.Post, bodyPayload: request), cancellationToken);
await HandleResponse(response);
}

public virtual async Task<PullPaymentLNURL> GetPullPaymentLNURL(string pullPaymentId,
CancellationToken cancellationToken = default)
{
var response = await _httpClient.SendAsync(
CreateHttpRequest(
$"/api/v1/pull-payments/{pullPaymentId}/lnurl",
method: HttpMethod.Get), cancellationToken);
return await HandleResponse<PullPaymentLNURL>(response);
}
}
}
8 changes: 8 additions & 0 deletions BTCPayServer.Client/Models/PullPaymentLNURL.cs
@@ -0,0 +1,8 @@
namespace BTCPayServer.Client.Models
{
public class PullPaymentLNURL
{
public string LNURLBech32 { get; set; }
public string LNURLUri { get; set; }
}
}
21 changes: 18 additions & 3 deletions BTCPayServer.Tests/GreenfieldAPITests.cs
Expand Up @@ -672,10 +672,12 @@ await adminClient.CreateUser(new CreateApplicationUserRequest()
public async Task CanUsePullPaymentViaAPI()
{
using var tester = CreateServerTester();
tester.ActivateLightning();
await tester.StartAsync();
await tester.EnsureChannelsSetup();
var acc = tester.NewAccount();
acc.Register();
await acc.CreateStoreAsync();
await acc.GrantAccessAsync(true);
acc.RegisterLightningNode("BTC", LightningConnectionType.CLightning, false);
var storeId = (await acc.RegisterDerivationSchemeAsync("BTC", importKeysToNBX: true)).StoreId;
var client = await acc.CreateClient();
var result = await client.CreatePullPayment(storeId, new CreatePullPaymentRequest()
Expand Down Expand Up @@ -856,6 +858,8 @@ await this.AssertValidationError(new[] { "ExpiresAt" }, async () => await client
PaymentMethods = new[] { "BTC" }
});

await this.AssertAPIError("lnurl-not-supported", async () => await unauthenticated.GetPullPaymentLNURL(pp.Id));

destination = (await tester.ExplorerNode.GetNewAddressAsync()).ToString();
TestLogs.LogInformation("Try to pay it in BTC");
payout = await unauthenticated.CreatePayout(pp.Id, new CreatePayoutRequest()
Expand Down Expand Up @@ -906,7 +910,18 @@ await this.AssertAPIError("invalid-state", async () => await client.ApprovePayou
payout = (await client.GetPayouts(payout.PullPaymentId)).First(data => data.Id == payout.Id);
Assert.Equal(PayoutState.Completed, payout.State);
await AssertAPIError("invalid-state", async () => await client.MarkPayoutPaid(storeId, payout.Id));


// Test LNURL values
var test4 = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest()
{
Name = "Test 3",
Amount = 12.303228134m,
Currency = "BTC",
PaymentMethods = new[] { "BTC", "BTC-LightningNetwork", "BTC_LightningLike" }
});
var lnrURLs = await unauthenticated.GetPullPaymentLNURL(test4.Id);
Assert.IsType<string>(lnrURLs.LNURLBech32);
Assert.IsType<string>(lnrURLs.LNURLUri);

//permission test around auto approved pps and payouts
var nonApproved = await acc.CreateClient(Policies.CanCreateNonApprovedPullPayments);
Expand Down
Expand Up @@ -34,6 +34,7 @@ public class GreenfieldPullPaymentController : ControllerBase
private readonly CurrencyNameTable _currencyNameTable;
private readonly BTCPayNetworkJsonSerializerSettings _serializerSettings;
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
private readonly BTCPayNetworkProvider _networkProvider;
private readonly IAuthorizationService _authorizationService;

public GreenfieldPullPaymentController(PullPaymentHostedService pullPaymentService,
Expand All @@ -42,6 +43,7 @@ public class GreenfieldPullPaymentController : ControllerBase
CurrencyNameTable currencyNameTable,
Services.BTCPayNetworkJsonSerializerSettings serializerSettings,
IEnumerable<IPayoutHandler> payoutHandlers,
BTCPayNetworkProvider btcPayNetworkProvider,
IAuthorizationService authorizationService)
{
_pullPaymentService = pullPaymentService;
Expand All @@ -50,6 +52,7 @@ public class GreenfieldPullPaymentController : ControllerBase
_currencyNameTable = currencyNameTable;
_serializerSettings = serializerSettings;
_payoutHandlers = payoutHandlers;
_networkProvider = btcPayNetworkProvider;
_authorizationService = authorizationService;
}

Expand Down Expand Up @@ -243,6 +246,33 @@ public async Task<IActionResult> GetPayout(string pullPaymentId, string payoutId
return base.Ok(ToModel(payout));
}

[HttpGet("~/api/v1/pull-payments/{pullPaymentId}/lnurl")]
[AllowAnonymous]
public async Task<IActionResult> GetPullPaymentLNURL(string pullPaymentId)
{
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, false);
if (pp is null)
return PullPaymentNotFound();

var blob = pp.GetBlob();
var pms = blob.SupportedPaymentMethods.FirstOrDefault(id => id.PaymentType == LightningPaymentType.Instance && _networkProvider.DefaultNetwork.CryptoCode == id.CryptoCode);
if (pms is not null && blob.Currency.Equals(pms.CryptoCode, StringComparison.InvariantCultureIgnoreCase))
{
var lnurlEndpoint = new Uri(Url.Action("GetLNURLForPullPayment", "UILNURL", new
{
cryptoCode = _networkProvider.DefaultNetwork.CryptoCode,
pullPaymentId = pullPaymentId
}, Request.Scheme, Request.Host.ToString()));

return base.Ok(new PullPaymentLNURL() {
LNURLBech32 = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", true).ToString(),
LNURLUri = LNURL.LNURL.EncodeUri(lnurlEndpoint, "withdrawRequest", false).ToString()
});
}

return this.CreateAPIError("lnurl-not-supported", "LNURL not supported for this pull payment");
}

private Client.Models.PayoutData ToModel(Data.PayoutData p)
{
var blob = p.GetBlob(_serializerSettings);
Expand Down
Expand Up @@ -1242,6 +1242,11 @@ public override async Task<PayoutData> GetPullPaymentPayout(string pullPaymentId
return GetFromActionResult<PayoutData>(await GetController<GreenfieldPullPaymentController>().GetPayout(pullPaymentId, payoutId));
}

public override async Task<PullPaymentLNURL> GetPullPaymentLNURL(string pullPaymentId, CancellationToken cancellationToken = default)
{
return GetFromActionResult<PullPaymentLNURL>(await GetController<GreenfieldPullPaymentController>().GetPullPaymentLNURL(pullPaymentId));
}

public override async Task<PayoutData> GetStorePayout(string storeId, string payoutId,
CancellationToken cancellationToken = default)
{
Expand Down
Expand Up @@ -402,6 +402,46 @@
"security": []
}
},
"/api/v1/pull-payments/{pullPaymentId}/lnurl": {
"parameters": [
{
"name": "pullPaymentId",
"in": "path",
"required": true,
"description": "The ID of the pull payment",
"schema": {
"type": "string"
}
}
],
"get": {
"summary": "Get Pull Payment LNURL details",
"operationId": "PullPayments_GetPullPaymentLNURL",
"description": "Get Pull Payment LNURL details",
"responses": {
"200": {
"description": "Pull payment LNURL details",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LNURLData"
}
}
}
},
"404": {
"description": "Pull payment not found"
},
"400": {
"description": "Pull payment found but does not support LNURL"
}
},
"tags": [
"Pull payments (Public)"
],
"security": []
}
},
"/api/v1/stores/{storeId}/payouts": {
"parameters": [
{
Expand Down Expand Up @@ -1028,6 +1068,21 @@
"description": "The link to a page to claim payouts to this pull payment"
}
}
},
"LNURLData": {
"type": "object",
"properties": {
"lnurlBech32": {
"type": "string",
"description": "Bech32 representation of LNRURL",
"example": "lightning:lnurl1dp68gup69uhnzv3h9cczuvpwxyarzdp3xsez7sj5gvh42j2vfe24ynp0wa5hg6rywfshwtmswqhngvntdd6x6uzvx4jrvu2kvvur23n8v46rwjpexcc45563fn53w7"
},
"lnurlUri": {
"type": "string",
"description": "Bech32 representation of LNURL",
"example": "lnurlw://example.com/BTC/UILNURL/withdraw/pp/42kktmpL5d6qVc85Fget7H961ZSQ"
}
}
}
}
},
Expand Down

0 comments on commit 438dcc4

Please sign in to comment.