mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-19 05:33:31 +01:00
fix
This commit is contained in:
parent
89062fcb10
commit
4c0c2d2e94
@ -0,0 +1,150 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.NTag424;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBitcoin.DataEncoders;
|
||||
|
||||
namespace BTCPayServer.Plugins.BoltcardFactory.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("apps")]
|
||||
public class APIBoltcardFactoryController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<APIBoltcardFactoryController> _logger;
|
||||
private readonly AppService _appService;
|
||||
private readonly SettingsRepository _settingsRepository;
|
||||
private readonly BTCPayServerEnvironment _env;
|
||||
private readonly ApplicationDbContextFactory _dbContextFactory;
|
||||
private readonly PullPaymentHostedService _ppService;
|
||||
|
||||
public APIBoltcardFactoryController(
|
||||
ILogger<APIBoltcardFactoryController> logger,
|
||||
AppService appService,
|
||||
SettingsRepository settingsRepository,
|
||||
BTCPayServerEnvironment env,
|
||||
ApplicationDbContextFactory dbContextFactory,
|
||||
PullPaymentHostedService ppService)
|
||||
{
|
||||
_logger = logger;
|
||||
_appService = appService;
|
||||
_settingsRepository = settingsRepository;
|
||||
_env = env;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_ppService = ppService;
|
||||
}
|
||||
[HttpPost("{appId}/boltcards")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> RegisterBoltcard(string appId, RegisterBoltcardRequest? request, string? onExisting = null)
|
||||
{
|
||||
var app = await _appService.GetApp(appId, BoltcardFactoryPlugin.AppType);
|
||||
if (app is null)
|
||||
return NotFound();
|
||||
var issuerKey = await _settingsRepository.GetIssuerKey(_env);
|
||||
|
||||
// LNURLW is used by deeplinks
|
||||
if (request?.LNURLW is not null)
|
||||
{
|
||||
if (request.UID is not null)
|
||||
{
|
||||
_logger.LogInformation("You should pass either LNURLW or UID but not both");
|
||||
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)
|
||||
{
|
||||
_logger.LogInformation("The LNURLW should contains a 'p=' parameter");
|
||||
ModelState.AddModelError(nameof(request.LNURLW), "The LNURLW should contains a 'p=' parameter");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
if (issuerKey.TryDecrypt(p) is not BoltcardPICCData picc)
|
||||
{
|
||||
_logger.LogInformation("The LNURLW 'p=' parameter cannot be decrypted");
|
||||
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)
|
||||
{
|
||||
_logger.LogInformation("The UID is required and should be 7 bytes");
|
||||
ModelState.AddModelError(nameof(request.UID), "The UID is required and should be 7 bytes");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
// Passing onExisting as a query parameter is used by deeplink
|
||||
request.OnExisting = onExisting switch
|
||||
{
|
||||
nameof(OnExistingBehavior.UpdateVersion) => OnExistingBehavior.UpdateVersion,
|
||||
nameof(OnExistingBehavior.KeepVersion) => OnExistingBehavior.KeepVersion,
|
||||
_ => request.OnExisting
|
||||
};
|
||||
|
||||
int version;
|
||||
string ppId;
|
||||
var registration = await _dbContextFactory.GetBoltcardRegistration(issuerKey, request.UID);
|
||||
|
||||
if (request.OnExisting == OnExistingBehavior.UpdateVersion)
|
||||
{
|
||||
var req = app.GetSettings<CreatePullPaymentRequest>();
|
||||
ppId = await _ppService.CreatePullPayment(app.StoreDataId, req);
|
||||
version = await _dbContextFactory.LinkBoltcardToPullPayment(ppId, issuerKey, request.UID, request.OnExisting);
|
||||
}
|
||||
// If it's a reset, do not create a new pull payment
|
||||
else
|
||||
{
|
||||
if (registration?.PullPaymentId is null)
|
||||
{
|
||||
_logger.LogInformation("This card isn't registered");
|
||||
ModelState.AddModelError(nameof(request.UID), "This card isn't registered");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
ppId = registration.PullPaymentId;
|
||||
version = registration.Version;
|
||||
}
|
||||
|
||||
var keys = issuerKey.CreatePullPaymentCardKey(request.UID, version, ppId).DeriveBoltcardKeys(issuerKey);
|
||||
var boltcardUrl = Url.Action(nameof(UIBoltcardController.GetWithdrawRequest), "UIBoltcard");
|
||||
boltcardUrl = Request.GetAbsoluteUri(boltcardUrl);
|
||||
boltcardUrl = Regex.Replace(boltcardUrl, "^https?://", "lnurlw://");
|
||||
|
||||
var resp = new RegisterBoltcardResponse()
|
||||
{
|
||||
LNURLW = boltcardUrl,
|
||||
Version = version,
|
||||
K0 = Encoders.Hex.EncodeData(keys.AppMasterKey.ToBytes()).ToUpperInvariant(),
|
||||
K1 = Encoders.Hex.EncodeData(keys.EncryptionKey.ToBytes()).ToUpperInvariant(),
|
||||
K2 = Encoders.Hex.EncodeData(keys.AuthenticationKey.ToBytes()).ToUpperInvariant(),
|
||||
K3 = Encoders.Hex.EncodeData(keys.K3.ToBytes()).ToUpperInvariant(),
|
||||
K4 = Encoders.Hex.EncodeData(keys.K4.ToBytes()).ToUpperInvariant(),
|
||||
};
|
||||
|
||||
return Ok(resp);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -47,11 +47,6 @@ namespace BTCPayServer.Plugins.BoltcardFactory.Controllers
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly CurrencyNameTable _currencyNameTable;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly SettingsRepository _settingsRepository;
|
||||
private readonly BTCPayServerEnvironment _env;
|
||||
private readonly PullPaymentHostedService _ppService;
|
||||
private readonly ApplicationDbContextFactory _dbContextFactory;
|
||||
private readonly ILogger<UIBoltcardFactoryController> _logger;
|
||||
|
||||
public UIBoltcardFactoryController(
|
||||
IEnumerable<IPayoutHandler> payoutHandlers,
|
||||
@ -59,12 +54,7 @@ namespace BTCPayServer.Plugins.BoltcardFactory.Controllers
|
||||
AppService appService,
|
||||
StoreRepository storeRepository,
|
||||
CurrencyNameTable currencyNameTable,
|
||||
IAuthorizationService authorizationService,
|
||||
SettingsRepository settingsRepository,
|
||||
BTCPayServerEnvironment env,
|
||||
PullPaymentHostedService ppService,
|
||||
ApplicationDbContextFactory dbContextFactory,
|
||||
ILogger<UIBoltcardFactoryController> logger)
|
||||
IAuthorizationService authorizationService)
|
||||
{
|
||||
_payoutHandlers = payoutHandlers;
|
||||
_currencies = currencies;
|
||||
@ -72,11 +62,6 @@ namespace BTCPayServer.Plugins.BoltcardFactory.Controllers
|
||||
_storeRepository = storeRepository;
|
||||
_currencyNameTable = currencyNameTable;
|
||||
_authorizationService = authorizationService;
|
||||
_settingsRepository = settingsRepository;
|
||||
_env = env;
|
||||
_ppService = ppService;
|
||||
_dbContextFactory = dbContextFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
public Data.StoreData CurrentStore => HttpContext.GetStoreData();
|
||||
private AppData GetCurrentApp() => HttpContext.GetAppData();
|
||||
@ -212,7 +197,7 @@ namespace BTCPayServer.Plugins.BoltcardFactory.Controllers
|
||||
[HttpGet("/apps/{appId}/boltcardfactory")]
|
||||
[DomainMappingConstraint(BoltcardFactoryPlugin.AppType)]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> ViewBoltcardFactory(string appId)
|
||||
public IActionResult ViewBoltcardFactory(string appId)
|
||||
{
|
||||
var vm = new ViewBoltcardFactoryViewModel();
|
||||
vm.SetupDeepLink = $"boltcard://program?url={GetBoltcardDeeplinkUrl(appId, OnExistingBehavior.UpdateVersion)}";
|
||||
@ -222,7 +207,7 @@ namespace BTCPayServer.Plugins.BoltcardFactory.Controllers
|
||||
|
||||
private string GetBoltcardDeeplinkUrl(string appId, OnExistingBehavior onExisting)
|
||||
{
|
||||
var registerUrl = Url.Action(nameof(UIBoltcardFactoryController.RegisterBoltcard), "UIBoltcardFactory",
|
||||
var registerUrl = Url.Action(nameof(APIBoltcardFactoryController.RegisterBoltcard), "APIBoltcardFactory",
|
||||
new
|
||||
{
|
||||
appId = appId,
|
||||
@ -230,112 +215,6 @@ namespace BTCPayServer.Plugins.BoltcardFactory.Controllers
|
||||
}, Request.Scheme, Request.Host.ToString());
|
||||
registerUrl = Uri.EscapeDataString(registerUrl!);
|
||||
return registerUrl;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("~/apps/{appId}/boltcards")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> RegisterBoltcard(string appId, RegisterBoltcardRequest request, string? onExisting = null)
|
||||
{
|
||||
var app = await _appService.GetApp(appId, BoltcardFactoryPlugin.AppType);
|
||||
if (app is null)
|
||||
return NotFound();
|
||||
var issuerKey = await _settingsRepository.GetIssuerKey(_env);
|
||||
|
||||
// LNURLW is used by deeplinks
|
||||
if (request?.LNURLW is not null)
|
||||
{
|
||||
if (request.UID is not null)
|
||||
{
|
||||
_logger.LogInformation("You should pass either LNURLW or UID but not both");
|
||||
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)
|
||||
{
|
||||
_logger.LogInformation("The LNURLW should contains a 'p=' parameter");
|
||||
ModelState.AddModelError(nameof(request.LNURLW), "The LNURLW should contains a 'p=' parameter");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
if (issuerKey.TryDecrypt(p) is not BoltcardPICCData picc)
|
||||
{
|
||||
_logger.LogInformation("The LNURLW 'p=' parameter cannot be decrypted");
|
||||
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)
|
||||
{
|
||||
_logger.LogInformation("The UID is required and should be 7 bytes");
|
||||
ModelState.AddModelError(nameof(request.UID), "The UID is required and should be 7 bytes");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
|
||||
// Passing onExisting as a query parameter is used by deeplink
|
||||
request.OnExisting = onExisting switch
|
||||
{
|
||||
nameof(OnExistingBehavior.UpdateVersion) => OnExistingBehavior.UpdateVersion,
|
||||
nameof(OnExistingBehavior.KeepVersion) => OnExistingBehavior.KeepVersion,
|
||||
_ => request.OnExisting
|
||||
};
|
||||
|
||||
int version;
|
||||
string ppId;
|
||||
var registration = await _dbContextFactory.GetBoltcardRegistration(issuerKey, request.UID);
|
||||
|
||||
if (request.OnExisting == OnExistingBehavior.UpdateVersion)
|
||||
{
|
||||
var req = app.GetSettings<CreatePullPaymentRequest>();
|
||||
ppId = await _ppService.CreatePullPayment(app.StoreDataId, req);
|
||||
version = await _dbContextFactory.LinkBoltcardToPullPayment(ppId, issuerKey, request.UID, request.OnExisting);
|
||||
}
|
||||
// If it's a reset, do not create a new pull payment
|
||||
else
|
||||
{
|
||||
if (registration?.PullPaymentId is null)
|
||||
{
|
||||
_logger.LogInformation("This card isn't registered");
|
||||
ModelState.AddModelError(nameof(request.UID), "This card isn't registered");
|
||||
return this.CreateValidationError(ModelState);
|
||||
}
|
||||
ppId = registration.PullPaymentId;
|
||||
version = registration.Version;
|
||||
}
|
||||
|
||||
var keys = issuerKey.CreatePullPaymentCardKey(request.UID, version, ppId).DeriveBoltcardKeys(issuerKey);
|
||||
var boltcardUrl = Url.Action(nameof(UIBoltcardController.GetWithdrawRequest), "UIBoltcard");
|
||||
boltcardUrl = Request.GetAbsoluteUri(boltcardUrl);
|
||||
boltcardUrl = Regex.Replace(boltcardUrl, "^https?://", "lnurlw://");
|
||||
|
||||
var resp = new RegisterBoltcardResponse()
|
||||
{
|
||||
LNURLW = boltcardUrl,
|
||||
Version = version,
|
||||
K0 = Encoders.Hex.EncodeData(keys.AppMasterKey.ToBytes()).ToUpperInvariant(),
|
||||
K1 = Encoders.Hex.EncodeData(keys.EncryptionKey.ToBytes()).ToUpperInvariant(),
|
||||
K2 = Encoders.Hex.EncodeData(keys.AuthenticationKey.ToBytes()).ToUpperInvariant(),
|
||||
K3 = Encoders.Hex.EncodeData(keys.K3.ToBytes()).ToUpperInvariant(),
|
||||
K4 = Encoders.Hex.EncodeData(keys.K4.ToBytes()).ToUpperInvariant(),
|
||||
};
|
||||
|
||||
return Ok(resp);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user