mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-12 19:02:01 +01:00
Allow passing LNURLW to register boltcard
This commit is contained in:
parent
18fe420b74
commit
b9ef41b8c3
3 changed files with 71 additions and 2 deletions
|
@ -15,6 +15,8 @@ namespace BTCPayServer.Client.Models
|
||||||
}
|
}
|
||||||
public class RegisterBoltcardRequest
|
public class RegisterBoltcardRequest
|
||||||
{
|
{
|
||||||
|
[JsonProperty("LNURLW")]
|
||||||
|
public string LNURLW { get; set; }
|
||||||
[JsonConverter(typeof(HexJsonConverter))]
|
[JsonConverter(typeof(HexJsonConverter))]
|
||||||
[JsonProperty("UID")]
|
[JsonProperty("UID")]
|
||||||
public byte[] UID { get; set; }
|
public byte[] UID { get; set; }
|
||||||
|
|
|
@ -13,6 +13,7 @@ using BTCPayServer.Controllers;
|
||||||
using BTCPayServer.Events;
|
using BTCPayServer.Events;
|
||||||
using BTCPayServer.Lightning;
|
using BTCPayServer.Lightning;
|
||||||
using BTCPayServer.Models.InvoicingModels;
|
using BTCPayServer.Models.InvoicingModels;
|
||||||
|
using BTCPayServer.NTag424;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Payments.Lightning;
|
using BTCPayServer.Payments.Lightning;
|
||||||
using BTCPayServer.PayoutProcessors;
|
using BTCPayServer.PayoutProcessors;
|
||||||
|
@ -24,6 +25,7 @@ using BTCPayServer.Services.Stores;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
|
using NBitcoin.DataEncoders;
|
||||||
using NBitpayClient;
|
using NBitpayClient;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
@ -1115,6 +1117,35 @@ namespace BTCPayServer.Tests
|
||||||
OnExisting = OnExistingBehavior.KeepVersion
|
OnExisting = OnExistingBehavior.KeepVersion
|
||||||
});
|
});
|
||||||
Assert.Equal(card2.Version, card3.Version);
|
Assert.Equal(card2.Version, card3.Version);
|
||||||
|
var p = new byte[] { 0xc7 }.Concat(uid).Concat(new byte[8]).ToArray();
|
||||||
|
var card4 = await client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||||
|
{
|
||||||
|
OnExisting = OnExistingBehavior.KeepVersion,
|
||||||
|
LNURLW = card2.LNURLW + $"?p={Encoders.Hex.EncodeData(AESKey.Parse(card2.K1).Encrypt(p))}"
|
||||||
|
});
|
||||||
|
Assert.Equal(card2.Version, card4.Version);
|
||||||
|
Assert.Equal(card2.K4, card4.K4);
|
||||||
|
// Can't define both properties
|
||||||
|
await AssertValidationError(["LNURLW"], () => client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||||
|
{
|
||||||
|
OnExisting = OnExistingBehavior.KeepVersion,
|
||||||
|
UID = uid,
|
||||||
|
LNURLW = card2.LNURLW + $"?p={Encoders.Hex.EncodeData(AESKey.Parse(card2.K1).Encrypt(p))}"
|
||||||
|
}));
|
||||||
|
// p is malformed
|
||||||
|
await AssertValidationError(["LNURLW"], () => client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||||
|
{
|
||||||
|
OnExisting = OnExistingBehavior.KeepVersion,
|
||||||
|
UID = uid,
|
||||||
|
LNURLW = card2.LNURLW + $"?p=lol"
|
||||||
|
}));
|
||||||
|
// p is invalid
|
||||||
|
p[0] = 0;
|
||||||
|
await AssertValidationError(["LNURLW"], () => client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||||
|
{
|
||||||
|
OnExisting = OnExistingBehavior.KeepVersion,
|
||||||
|
LNURLW = card2.LNURLW + $"?p={Encoders.Hex.EncodeData(AESKey.Parse(card2.K1).Encrypt(p))}"
|
||||||
|
}));
|
||||||
// Test with SATS denomination values
|
// Test with SATS denomination values
|
||||||
var testSats = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest()
|
var testSats = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest()
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO.IsolatedStorage;
|
using System.IO.IsolatedStorage;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
@ -215,7 +216,29 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||||
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, false);
|
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, false);
|
||||||
if (pp is null)
|
if (pp is null)
|
||||||
return PullPaymentNotFound();
|
return PullPaymentNotFound();
|
||||||
_logs.PayServer.LogInformation(JsonConvert.SerializeObject(request, Formatting.Indented));
|
var issuerKey = await _settingsRepository.GetIssuerKey(_env);
|
||||||
|
// LNURLW is used by deeplinks
|
||||||
|
if (request?.LNURLW is not null)
|
||||||
|
{
|
||||||
|
if (request.UID is not null)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(nameof(request.LNURLW), "You should pass either LNURLW or UID but not both");
|
||||||
|
return this.CreateValidationError(ModelState);
|
||||||
|
}
|
||||||
|
var p = ExtractP(request.LNURLW);
|
||||||
|
if (p is null)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(nameof(request.LNURLW), "The LNURLW should contains a 'p=' parameter");
|
||||||
|
return this.CreateValidationError(ModelState);
|
||||||
|
}
|
||||||
|
if (issuerKey.TryDecrypt(p) is not BoltcardPICCData picc)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(nameof(request.LNURLW), "The LNURLW 'p=' parameter cannot be decrypted");
|
||||||
|
return this.CreateValidationError(ModelState);
|
||||||
|
}
|
||||||
|
request.UID = picc.Uid;
|
||||||
|
}
|
||||||
|
|
||||||
if (request?.UID is null || request.UID.Length != 7)
|
if (request?.UID is null || request.UID.Length != 7)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError(nameof(request.UID), "The UID is required and should be 7 bytes");
|
ModelState.AddModelError(nameof(request.UID), "The UID is required and should be 7 bytes");
|
||||||
|
@ -234,7 +257,6 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||||
_ => request.OnExisting
|
_ => request.OnExisting
|
||||||
};
|
};
|
||||||
|
|
||||||
var issuerKey = await _settingsRepository.GetIssuerKey(_env);
|
|
||||||
var version = await _dbContextFactory.LinkBoltcardToPullPayment(pullPaymentId, issuerKey, request.UID, request.OnExisting);
|
var version = await _dbContextFactory.LinkBoltcardToPullPayment(pullPaymentId, issuerKey, request.UID, request.OnExisting);
|
||||||
var keys = issuerKey.CreatePullPaymentCardKey(request.UID, version, pullPaymentId).DeriveBoltcardKeys(issuerKey);
|
var keys = issuerKey.CreatePullPaymentCardKey(request.UID, version, pullPaymentId).DeriveBoltcardKeys(issuerKey);
|
||||||
|
|
||||||
|
@ -254,6 +276,20 @@ namespace BTCPayServer.Controllers.Greenfield
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string? ExtractP(string? url)
|
||||||
|
{
|
||||||
|
if (url is null || !Uri.TryCreate(url, UriKind.Absolute, out var uri))
|
||||||
|
return null;
|
||||||
|
int num = uri.AbsoluteUri.IndexOf('?');
|
||||||
|
if (num == -1)
|
||||||
|
return null;
|
||||||
|
string input = uri.AbsoluteUri.Substring(num);
|
||||||
|
Match match = Regex.Match(input, "p=([a-f0-9A-F]{32})");
|
||||||
|
if (!match.Success)
|
||||||
|
return null;
|
||||||
|
return match.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("~/api/v1/pull-payments/{pullPaymentId}")]
|
[HttpGet("~/api/v1/pull-payments/{pullPaymentId}")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> GetPullPayment(string pullPaymentId)
|
public async Task<IActionResult> GetPullPayment(string pullPaymentId)
|
||||||
|
|
Loading…
Add table
Reference in a new issue