If pull payment opened in mobile, use deeplink to setup card

This commit is contained in:
nicolas.dorier 2023-12-25 12:43:02 +09:00
parent b7be93c569
commit 18fe420b74
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
5 changed files with 59 additions and 12 deletions

View File

@ -4,6 +4,7 @@ using System.Text;
using NBitcoin.JsonConverters;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Client.Models
{
@ -19,6 +20,8 @@ namespace BTCPayServer.Client.Models
public byte[] UID { get; set; }
[JsonConverter(typeof(StringEnumConverter))]
public OnExistingBehavior? OnExisting { get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> AdditionalData { get; set; } = new Dictionary<string, JToken>();
}
public class RegisterBoltcardResponse
{

View File

@ -1,6 +1,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Text.RegularExpressions;
@ -12,6 +13,7 @@ using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.Logging;
using BTCPayServer.NTag424;
using BTCPayServer.Payments;
using BTCPayServer.Security;
@ -22,8 +24,11 @@ using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using NBitcoin.DataEncoders;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Bcpg.OpenPgp;
using MarkPayoutRequest = BTCPayServer.HostedServices.MarkPayoutRequest;
namespace BTCPayServer.Controllers.Greenfield
@ -43,6 +48,7 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly IAuthorizationService _authorizationService;
private readonly SettingsRepository _settingsRepository;
private readonly BTCPayServerEnvironment _env;
private readonly Logs _logs;
public GreenfieldPullPaymentController(PullPaymentHostedService pullPaymentService,
LinkGenerator linkGenerator,
@ -53,7 +59,7 @@ namespace BTCPayServer.Controllers.Greenfield
BTCPayNetworkProvider btcPayNetworkProvider,
IAuthorizationService authorizationService,
SettingsRepository settingsRepository,
BTCPayServerEnvironment env)
BTCPayServerEnvironment env, Logs logs)
{
_pullPaymentService = pullPaymentService;
_linkGenerator = linkGenerator;
@ -65,6 +71,7 @@ namespace BTCPayServer.Controllers.Greenfield
_authorizationService = authorizationService;
_settingsRepository = settingsRepository;
_env = env;
_logs = logs;
}
[HttpGet("~/api/v1/stores/{storeId}/pull-payments")]
@ -200,13 +207,15 @@ namespace BTCPayServer.Controllers.Greenfield
[HttpPost]
[Route("~/api/v1/pull-payments/{pullPaymentId}/boltcards")]
[AllowAnonymous]
public async Task<IActionResult> RegisterBoltcard(string pullPaymentId, RegisterBoltcardRequest request)
public async Task<IActionResult> RegisterBoltcard(string pullPaymentId, RegisterBoltcardRequest request, string? onExisting = null)
{
if (pullPaymentId is null)
return PullPaymentNotFound();
var pp = await _pullPaymentService.GetPullPayment(pullPaymentId, false);
if (pp is null)
return PullPaymentNotFound();
_logs.PayServer.LogInformation(JsonConvert.SerializeObject(request, Formatting.Indented));
if (request?.UID is null || request.UID.Length != 7)
{
ModelState.AddModelError(nameof(request.UID), "The UID is required and should be 7 bytes");
@ -217,6 +226,14 @@ namespace BTCPayServer.Controllers.Greenfield
return this.CreateAPIError(400, "lnurl-not-supported", "This pull payment currency should be BTC or SATS and accept lightning");
}
// 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
};
var issuerKey = await _settingsRepository.GetIssuerKey(_env);
var version = await _dbContextFactory.LinkBoltcardToPullPayment(pullPaymentId, issuerKey, request.UID, request.OnExisting);
var keys = issuerKey.CreatePullPaymentCardKey(request.UID, version, pullPaymentId).DeriveBoltcardKeys(issuerKey);

View File

@ -11,6 +11,7 @@ using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Controllers.Greenfield;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.Lightning;
@ -127,13 +128,27 @@ namespace BTCPayServer.Controllers
if (_pullPaymentHostedService.SupportsLNURL(blob))
{
var url = Url.Action("GetLNURLForPullPayment", "UILNURL", new { cryptoCode = _networkProvider.DefaultNetwork.CryptoCode, pullPaymentId = vm.Id }, Request.Scheme, Request.Host.ToString());
var url = Url.Action(nameof(UILNURLController.GetLNURLForPullPayment), "UILNURL", new { cryptoCode = _networkProvider.DefaultNetwork.CryptoCode, pullPaymentId = vm.Id }, Request.Scheme, Request.Host.ToString());
vm.LnurlEndpoint = url != null ? new Uri(url) : null;
vm.SetupDeepLink = $"boltcard://program?url={GetBoltcardDeeplinkUrl(vm, OnExistingBehavior.UpdateVersion)}";
vm.ResetDeepLink = $"boltcard://reset?url={GetBoltcardDeeplinkUrl(vm, OnExistingBehavior.KeepVersion)}";
}
return View(nameof(ViewPullPayment), vm);
}
private string GetBoltcardDeeplinkUrl(ViewPullPaymentModel vm, OnExistingBehavior onExisting)
{
var registerUrl = Url.Action(nameof(GreenfieldPullPaymentController.RegisterBoltcard), "GreenfieldPullPayment",
new
{
pullPaymentId = vm.Id,
onExisting = onExisting.ToString()
}, Request.Scheme, Request.Host.ToString());
registerUrl = Uri.EscapeDataString(registerUrl);
return registerUrl;
}
[HttpGet("stores/{storeId}/pull-payments/edit/{pullPaymentId}")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> EditPullPayment(string storeId, string pullPaymentId)

View File

@ -71,6 +71,9 @@ namespace BTCPayServer.Models
public PaymentMethodId[] PaymentMethods { get; set; }
public string SetupDeepLink { get; set; }
public string ResetDeepLink { get; set; }
public string HubPath { get; set; }
public string ResetIn { get; set; }
public string Email { get; set; }

View File

@ -202,15 +202,15 @@
</p>
@if (Model.LnurlEndpoint is not null)
{
<p>
<a asp-action="SetupBoltcard" asp-controller="UIPullPayment" asp-route-pullPaymentId="@Model.Id" asp-route-command="configure-boltcard">
Setup Boltcard
</a>
<span>&nbsp;|&nbsp;</span>
<a asp-action="SetupBoltcard" asp-controller="UIPullPayment" asp-route-pullPaymentId="@Model.Id" asp-route-command="reset-boltcard">
Reset Boltcard
</a>
</p>
<p>
<a id="SetupBoltcard" asp-action="SetupBoltcard" asp-controller="UIPullPayment" asp-route-pullPaymentId="@Model.Id" asp-route-command="configure-boltcard">
Setup Boltcard
</a>
<span>&nbsp;|&nbsp;</span>
<a id="ResetBoltcard" asp-action="SetupBoltcard" asp-controller="UIPullPayment" asp-route-pullPaymentId="@Model.Id" asp-route-command="reset-boltcard">
Reset Boltcard
</a>
</p>
}
<a class="store-powered-by" href="https://btcpayserver.org" target="_blank" rel="noreferrer noopener">
Powered by <partial name="_StoreFooterLogo" />
@ -226,6 +226,15 @@
<script src="~/vendor/ur-registry/urlib.min.js" asp-append-version="true"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if (isMobile) {
document.getElementById("SetupBoltcard").setAttribute('target', '_blank');
document.getElementById("SetupBoltcard").setAttribute('href', @Safe.Json(@Model.SetupDeepLink));
document.getElementById("ResetBoltcard").setAttribute('target', '_blank');
document.getElementById("ResetBoltcard").setAttribute('href', @Safe.Json(@Model.ResetDeepLink));
}
window.qrApp = initQRShow({});
delegate('click', 'button[page-qr]', event => {
qrApp.title = "Pull Payment QR";