Decouple Pull payment from wallets (#2987)

* Decouple Pull payments from wallet

* Update _Nav.cshtml

* Fixes
This commit is contained in:
Andrew Camilleri
2021-10-22 04:17:40 +02:00
committed by GitHub
parent db038723f4
commit 9b0d1a23dc
15 changed files with 147 additions and 115 deletions

View File

@@ -313,7 +313,8 @@ namespace BTCPayServer.Tests
public void GoToStore(string storeId, StoreNavPages storeNavPage = StoreNavPages.Index)
{
Driver.FindElement(By.Id("Stores")).Click();
GoToHome();
Driver.WaitForAndClick(By.Id("Stores"));
Driver.FindElement(By.Id($"update-store-{storeId}")).Click();
if (storeNavPage != StoreNavPages.Index)

View File

@@ -995,7 +995,7 @@ namespace BTCPayServer.Tests
await s.Server.ExplorerNode.GenerateAsync(1);
await s.FundStoreWallet(denomination: 50.0m);
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
s.Driver.FindElement(By.Id("Amount")).Clear();
@@ -1003,7 +1003,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.FindElement(By.LinkText("View")).Click();
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
s.Driver.FindElement(By.Id("Name")).SendKeys("PP2");
@@ -1035,13 +1035,13 @@ namespace BTCPayServer.Tests
var viewPullPaymentUrl = s.Driver.Url;
// This one should have nothing
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
var payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
Assert.Equal(2, payouts.Count);
payouts[1].Click();
Assert.Empty(s.Driver.FindElements(By.ClassName("payout")));
// PP2 should have payouts
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
payouts = s.Driver.FindElements(By.ClassName("pp-payout"));
payouts[0].Click();
@@ -1061,7 +1061,7 @@ namespace BTCPayServer.Tests
});
Assert.Equal("payout", s.Driver.FindElement(By.ClassName("transactionLabel")).Text);
s.GoToWallet(navPages: WalletsNavPages.Payouts);
s.GoToStore(s.StoreId, StoreNavPages.Payouts);
var x = s.Driver.PageSource;
s.Driver.FindElement(By.Id($"{PayoutState.InProgress}-view")).Click();
ReadOnlyCollection<IWebElement> txs;
@@ -1103,7 +1103,7 @@ namespace BTCPayServer.Tests
var newStore = s.CreateNewStore();
s.GenerateWallet("BTC", "", true, true);
var newWalletId = new WalletId(newStore.storeId, "BTC");
s.GoToWallet(newWalletId, WalletsNavPages.PullPayments);
s.GoToStore(s.StoreId, StoreNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
s.Driver.FindElement(By.Id("Name")).SendKeys("External Test");
@@ -1120,7 +1120,7 @@ namespace BTCPayServer.Tests
s.FindAlertMessage();
Assert.Contains(PayoutState.AwaitingApproval.GetStateString(), s.Driver.PageSource);
s.GoToWallet(newWalletId, WalletsNavPages.Payouts);
s.GoToStore(s.StoreId, StoreNavPages.Payouts);
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-view")).Click();
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-selectAllCheckbox")).Click();
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-actions")).Click();
@@ -1128,7 +1128,7 @@ namespace BTCPayServer.Tests
s.FindAlertMessage();
var tx =await s.Server.ExplorerNode.SendToAddressAsync(address, Money.FromUnit(0.001m, MoneyUnit.BTC));
s.GoToWallet(newWalletId, WalletsNavPages.Payouts);
s.GoToStore(s.StoreId, StoreNavPages.Payouts);
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingPayment}-view")).Click();
Assert.Contains(PayoutState.AwaitingPayment.GetStateString(), s.Driver.PageSource);
@@ -1146,8 +1146,8 @@ namespace BTCPayServer.Tests
s.AddLightningNode("BTC");
//Currently an onchain wallet is required to use the Lightning payouts feature..
s.GenerateWallet("BTC", "", true, true);
newWalletId = new WalletId(newStore.storeId, "BTC");
s.GoToWallet(newWalletId, WalletsNavPages.PullPayments);
s.GoToStore(newStore.storeId, StoreNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
@@ -1188,7 +1188,8 @@ namespace BTCPayServer.Tests
s.FindAlertMessage();
Assert.Contains(PayoutState.AwaitingApproval.GetStateString(), s.Driver.PageSource);
s.GoToWallet(newWalletId, WalletsNavPages.Payouts);
s.GoToStore(newStore.storeId, StoreNavPages.Payouts);
s.Driver.FindElement(By.Id($"{new PaymentMethodId("BTC", PaymentTypes.LightningLike )}-view")).Click();
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-view")).Click();
s.Driver.FindElement(By.Id($"{PayoutState.AwaitingApproval}-selectAllCheckbox")).Click();
@@ -1200,7 +1201,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.CssSelector("#pay-invoices-form")).Submit();
//lightning config in tests is very unstable so we can just go ahead and handle it as both
s.FindAlertMessage(new []{StatusMessageModel.StatusSeverity.Error, StatusMessageModel.StatusSeverity.Success});
s.GoToWallet(newWalletId, WalletsNavPages.Payouts);
s.GoToStore(newStore.storeId, StoreNavPages.Payouts);
s.Driver.FindElement(By.Id($"{new PaymentMethodId("BTC", PaymentTypes.LightningLike )}-view")).Click();

View File

@@ -21,6 +21,15 @@
<None Remove="Build\**" />
<None Remove="wwwroot\bundles\jqueryvalidate\**" />
<None Remove="wwwroot\vendor\jquery-nice-select\**" />
<Content Update="Views\StorePullPayments\NewPullPayment.cshtml">
<Pack>false</Pack>
</Content>
<Content Update="Views\StorePullPayments\PullPayments.cshtml">
<Pack>false</Pack>
</Content>
<Content Update="Views\StorePullPayments\Payouts.cshtml">
<Pack>false</Pack>
</Content>
</ItemGroup>
<ItemGroup>
<None Remove="Currencies.txt" />
@@ -197,12 +206,6 @@
<Content Update="Views\Wallets\ListWallets.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Wallets\NewPullPayment.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Wallets\Payouts.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Wallets\WalletPSBTCombine.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
@@ -218,9 +221,6 @@
<Content Update="Views\Wallets\WalletSendVault.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Wallets\PullPayments.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
<Content Update="Views\Wallets\WalletTransactions.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>

View File

@@ -1,39 +1,70 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client;
using BTCPayServer.Client.Models;
using BTCPayServer.Common;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.ModelBinders;
using BTCPayServer.Models;
using BTCPayServer.Models.WalletViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Rating;
using BTCPayServer.Views;
using BTCPayServer.Services;
using BTCPayServer.Services.Rates;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using NBitcoin;
using PayoutData = BTCPayServer.Data.PayoutData;
using StoreData = BTCPayServer.Data.StoreData;
namespace BTCPayServer.Controllers
{
public partial class WalletsController
[Route("stores/{storeId}/pull-payments")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[AutoValidateAntiforgeryToken]
public class StorePullPaymentsController: Controller
{
[HttpGet("{walletId}/pull-payments/new")]
public IActionResult NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId)
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly IEnumerable<IPayoutHandler> _payoutHandlers;
private readonly CurrencyNameTable _currencyNameTable;
private readonly PullPaymentHostedService _pullPaymentService;
private readonly ApplicationDbContextFactory _dbContextFactory;
private readonly BTCPayNetworkJsonSerializerSettings _jsonSerializerSettings;
public StoreData CurrentStore
{
if (GetDerivationSchemeSettings(walletId) == null)
get
{
return HttpContext.GetStoreData();
}
}
public StorePullPaymentsController(BTCPayNetworkProvider btcPayNetworkProvider,
IEnumerable<IPayoutHandler> payoutHandlers,
CurrencyNameTable currencyNameTable,
PullPaymentHostedService pullPaymentHostedService,
ApplicationDbContextFactory dbContextFactory,
BTCPayNetworkJsonSerializerSettings jsonSerializerSettings)
{
_btcPayNetworkProvider = btcPayNetworkProvider;
_payoutHandlers = payoutHandlers;
_currencyNameTable = currencyNameTable;
_pullPaymentService = pullPaymentHostedService;
_dbContextFactory = dbContextFactory;
_jsonSerializerSettings = jsonSerializerSettings;
}
[HttpGet("new")]
public IActionResult NewPullPayment(string storeId)
{
if (CurrentStore is null)
return NotFound();
var storeMethods = CurrentStore.GetSupportedPaymentMethods(NetworkProvider).Select(method => method.PaymentId).ToList();
var storeMethods = CurrentStore.GetSupportedPaymentMethods(_btcPayNetworkProvider).Select(method => method.PaymentId).ToList();
var paymentMethodOptions = _payoutHandlers.GetSupportedPaymentMethods(storeMethods);
return View(new NewPullPaymentModel
{
@@ -45,14 +76,13 @@ namespace BTCPayServer.Controllers
});
}
[HttpPost("{walletId}/pull-payments/new")]
public async Task<IActionResult> NewPullPayment([ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId, NewPullPaymentModel model)
[HttpPost("new")]
public async Task<IActionResult> NewPullPayment(string storeId, NewPullPaymentModel model)
{
if (GetDerivationSchemeSettings(walletId) == null)
if (CurrentStore is null)
return NotFound();
var storeMethods = CurrentStore.GetSupportedPaymentMethods(NetworkProvider).Select(method => method.PaymentId).ToList();
var storeMethods = CurrentStore.GetSupportedPaymentMethods(_btcPayNetworkProvider).Select(method => method.PaymentId).ToList();
var paymentMethodOptions = _payoutHandlers.GetSupportedPaymentMethods(storeMethods);
model.PaymentMethodItems =
paymentMethodOptions.Select(id => new SelectListItem(id.ToPrettyString(), id.ToString(), true));
@@ -62,7 +92,7 @@ namespace BTCPayServer.Controllers
{
ModelState.AddModelError(nameof(model.PaymentMethods), "You need at least one payment method");
}
if (_currencyTable.GetCurrencyData(model.Currency, false) is null)
if (_currencyNameTable.GetCurrencyData(model.Currency, false) is null)
{
ModelState.AddModelError(nameof(model.Currency), "Invalid currency");
}
@@ -87,7 +117,7 @@ namespace BTCPayServer.Controllers
Name = model.Name,
Amount = model.Amount,
Currency = model.Currency,
StoreId = walletId.StoreId,
StoreId = storeId,
PaymentMethodIds = selectedPaymentMethodIds,
EmbeddedCSS = model.EmbeddedCSS,
CustomCSSLink = model.CustomCSSLink
@@ -97,17 +127,14 @@ namespace BTCPayServer.Controllers
Message = "Pull payment request created",
Severity = StatusMessageModel.StatusSeverity.Success
});
return RedirectToAction(nameof(PullPayments), new { walletId = walletId.ToString() });
return RedirectToAction(nameof(PullPayments), new { storeId = storeId });
}
[HttpGet("{walletId}/pull-payments")]
public async Task<IActionResult> PullPayments(
[ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId)
[HttpGet("")]
public async Task<IActionResult> PullPayments(string storeId)
{
using var ctx = this._dbContextFactory.CreateContext();
var now = DateTimeOffset.UtcNow;
var storeId = walletId.StoreId;
var pps = await ctx.PullPayments.Where(p => p.StoreId == storeId && !p.Archived)
.OrderByDescending(p => p.StartDate)
.Select(o => new
@@ -120,8 +147,7 @@ namespace BTCPayServer.Controllers
})
.ToListAsync();
var vm = new PullPaymentsModel
{ HasDerivationSchemeSettings = GetDerivationSchemeSettings(walletId) != null };
var vm = new PullPaymentsModel();
foreach (var o in pps)
{
@@ -131,8 +157,8 @@ namespace BTCPayServer.Controllers
var totalAwaiting = o.Awaiting.Where(o => o.IsInPeriod(pp, now))
.Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum();
var ppBlob = pp.GetBlob();
var ni = _currencyTable.GetCurrencyData(ppBlob.Currency, true);
var nfi = _currencyTable.GetNumberFormatInfo(ppBlob.Currency, true);
var ni = _currencyNameTable.GetCurrencyData(ppBlob.Currency, true);
var nfi = _currencyNameTable.GetNumberFormatInfo(ppBlob.Currency, true);
var period = pp.GetPeriod(now);
vm.PullPayments.Add(new PullPaymentsModel.PullPaymentModel()
{
@@ -146,7 +172,7 @@ namespace BTCPayServer.Controllers
AwaitingPercent = (int)(totalAwaiting / ppBlob.Limit * 100m),
Awaiting = totalAwaiting.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
Completed = totalCompleted.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
Limit = _currencyTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency),
Limit = _currencyNameTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency),
ResetIn = period?.End is DateTimeOffset nr ? ZeroIfNegative(nr - now).TimeString() : null,
EndIn = pp.EndDate is DateTimeOffset end ? ZeroIfNegative(end - now).TimeString() : null
}
@@ -161,19 +187,15 @@ namespace BTCPayServer.Controllers
return time;
}
[HttpGet("{walletId}/pull-payments/{pullPaymentId}/archive")]
public IActionResult ArchivePullPayment(
[ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId,
[HttpGet("{pullPaymentId}/archive")]
public IActionResult ArchivePullPayment(string storeId,
string pullPaymentId)
{
return View("Confirm", new ConfirmModel("Archive pull payment", "Do you really want to archive the pull payment?", "Archive"));
}
[HttpPost("{walletId}/pull-payments/{pullPaymentId}/archive")]
public async Task<IActionResult> ArchivePullPaymentPost(
[ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId,
[HttpPost("{pullPaymentId}/archive")]
public async Task<IActionResult> ArchivePullPaymentPost(string storeId,
string pullPaymentId)
{
await _pullPaymentService.Cancel(new HostedServices.PullPaymentHostedService.CancelRequest(pullPaymentId));
@@ -182,18 +204,15 @@ namespace BTCPayServer.Controllers
Message = "Pull payment archived",
Severity = StatusMessageModel.StatusSeverity.Success
});
return RedirectToAction(nameof(PullPayments), new { walletId = walletId.ToString() });
return RedirectToAction(nameof(PullPayments), new { storeId = storeId });
}
[HttpPost("{walletId}/payouts")]
[HttpPost("payouts")]
public async Task<IActionResult> PayoutsPost(
[ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId, PayoutsModel vm, CancellationToken cancellationToken)
string storeId, PayoutsModel vm, CancellationToken cancellationToken)
{
if (vm is null || GetDerivationSchemeSettings(walletId) == null)
if (vm is null)
return NotFound();
var storeId = walletId.StoreId;
var paymentMethodId = PaymentMethodId.Parse(vm.PaymentMethodId);
var handler = _payoutHandlers
.FindPayoutHandler(paymentMethodId);
@@ -208,7 +227,7 @@ namespace BTCPayServer.Controllers
});
return RedirectToAction(nameof(Payouts), new
{
walletId = walletId.ToString(),
storeId = storeId,
pullPaymentId = vm.PullPaymentId,
paymentMethodId = paymentMethodId.ToString()
});
@@ -216,7 +235,7 @@ namespace BTCPayServer.Controllers
var command = vm.Command.Substring(vm.Command.IndexOf('-', StringComparison.InvariantCulture) + 1);
if (handler != null)
{
var result = await handler.DoSpecificAction(command, payoutIds, walletId.StoreId);
var result = await handler.DoSpecificAction(command, payoutIds, storeId);
if (result != null)
{
TempData.SetStatusMessageModel(result);
@@ -315,7 +334,7 @@ namespace BTCPayServer.Controllers
});
return RedirectToAction(nameof(Payouts), new
{
walletId = walletId.ToString(),
storeId = storeId,
pullPaymentId = vm.PullPaymentId,
paymentMethodId = paymentMethodId.ToString()
});
@@ -342,7 +361,7 @@ namespace BTCPayServer.Controllers
return RedirectToAction(nameof(Payouts),
new
{
walletId = walletId.ToString(),
storeId = storeId,
pullPaymentId = vm.PullPaymentId,
paymentMethodId = paymentMethodId.ToString()
});
@@ -363,15 +382,14 @@ namespace BTCPayServer.Controllers
return payouts;
}
[HttpGet("{walletId}/payouts")]
[HttpGet("payouts")]
public async Task<IActionResult> Payouts(
[ModelBinder(typeof(WalletIdModelBinder))]
WalletId walletId, string pullPaymentId, string paymentMethodId, PayoutState payoutState,
string storeId, string pullPaymentId, string paymentMethodId, PayoutState payoutState,
int skip = 0, int count = 50)
{
var vm = this.ParseListQuery(new PayoutsModel
{
PaymentMethodId = paymentMethodId?? new PaymentMethodId(walletId.CryptoCode, PaymentTypes.BTCLike).ToString(),
PaymentMethodId = paymentMethodId?? _payoutHandlers.GetSupportedPaymentMethods().First().ToString(),
PullPaymentId = pullPaymentId,
PayoutState = payoutState,
Skip = skip,
@@ -379,13 +397,13 @@ namespace BTCPayServer.Controllers
});
vm.Payouts = new List<PayoutsModel.PayoutModel>();
await using var ctx = _dbContextFactory.CreateContext();
var storeId = walletId.StoreId;
var payoutRequest = ctx.Payouts.Where(p => p.PullPaymentData.StoreId == storeId && !p.PullPaymentData.Archived);
if (pullPaymentId != null)
{
payoutRequest = payoutRequest.Where(p => p.PullPaymentDataId == vm.PullPaymentId);
vm.PullPaymentName = (await ctx.PullPayments.FindAsync(pullPaymentId)).GetBlob().Name;
}
if (vm.PaymentMethodId != null)
{
var pmiStr = vm.PaymentMethodId;
@@ -425,7 +443,7 @@ namespace BTCPayServer.Controllers
PullPaymentName = ppBlob.Name ?? item.PullPayment.Id,
Date = item.Payout.Date,
PayoutId = item.Payout.Id,
Amount = _currencyTable.DisplayFormatCurrency(payoutBlob.Amount, ppBlob.Currency),
Amount = _currencyNameTable.DisplayFormatCurrency(payoutBlob.Amount, ppBlob.Currency),
Destination = payoutBlob.Destination
};
var handler = _payoutHandlers

View File

@@ -63,12 +63,13 @@ namespace Microsoft.AspNetCore.Mvc
scheme, host, pathbase);
}
public static string PayoutLink(this LinkGenerator urlHelper, string walletId,string pullPaymentId, string scheme, HostString host, string pathbase)
public static string PayoutLink(this LinkGenerator urlHelper, string walletIdOrStoreId,string pullPaymentId, string scheme, HostString host, string pathbase)
{
WalletId.TryParse(walletIdOrStoreId, out var wallet);
return urlHelper.GetUriByAction(
action: nameof(WalletsController.Payouts),
controller: "Wallets",
values: new {walletId, pullPaymentId},
action: nameof(StorePullPaymentsController.Payouts),
controller: "StorePullPayments",
values: new {storeId= wallet?.StoreId?? walletIdOrStoreId , pullPaymentId},
scheme, host, pathbase);
}
}

View File

@@ -29,8 +29,6 @@ namespace BTCPayServer.Models.WalletViewModels
}
public List<PullPaymentModel> PullPayments { get; set; } = new List<PullPaymentModel>();
public bool HasDerivationSchemeSettings { get; set; }
}
public class NewPullPaymentModel

View File

@@ -2,6 +2,7 @@ using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client.Models;
using BTCPayServer.Configuration;
using BTCPayServer.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
namespace BTCPayServer.Services.Notifications.Blobs
@@ -36,11 +37,12 @@ namespace BTCPayServer.Services.Notifications.Blobs
{
vm.Body =
"A payment that was made to an approved payout by an external wallet is waiting for your confirmation.";
vm.ActionLink = _linkGenerator.GetPathByAction(nameof(WalletsController.Payouts),
"Wallets",
vm.ActionLink = _linkGenerator.GetPathByAction(nameof(StorePullPaymentsController.Payouts),
"StorePullPayments",
new
{
walletId = new WalletId(notification.StoreId, notification.PaymentMethod),
storeId = notification.StoreId,
paymentMethodId = notification.PaymentMethod,
payoutState = PayoutState.AwaitingPayment
}, _options.RootPath);
}

View File

@@ -33,9 +33,9 @@ namespace BTCPayServer.Services.Notifications.Blobs
protected override void FillViewModel(PayoutNotification notification, NotificationViewModel vm)
{
vm.Body = "A new payout is awaiting for approval";
vm.ActionLink = _linkGenerator.GetPathByAction(nameof(WalletsController.Payouts),
"Wallets",
new {walletId = new WalletId(notification.StoreId, notification.PaymentMethod)}, _options.RootPath);
vm.ActionLink = _linkGenerator.GetPathByAction(nameof(StorePullPaymentsController.Payouts),
"StorePullPayments",
new {storeId = notification.StoreId, paymentMethodId = notification.PaymentMethod }, _options.RootPath);
}
}

View File

@@ -1,7 +1,10 @@
@model NewPullPaymentModel
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Views.Stores
@model BTCPayServer.Models.WalletViewModels.NewPullPaymentModel
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePageAndTitle(WalletsNavPages.PullPayments, "New pull payment", Context.GetStoreData().StoreName);
ViewData["NavPartialName"] = "../Stores/_Nav";
ViewData.SetActivePageAndTitle(StoreNavPages.PullPayments, "New pull payment", Context.GetStoreData().StoreName);
}
<style type="text/css">

View File

@@ -1,19 +1,26 @@
@using BTCPayServer.Client.Models
@using BTCPayServer.Payments
@model PayoutsModel
@using BTCPayServer.Views.Stores
@using BTCPayServer.Abstractions.Extensions
@model BTCPayServer.Models.WalletViewModels.PayoutsModel
@inject IEnumerable<IPayoutHandler> PayoutHandlers;
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePageAndTitle(WalletsNavPages.Payouts, $"Manage payouts{(string.IsNullOrEmpty(Model.PullPaymentName) ? string.Empty : " for pull payment " + Model.PullPaymentName)}", Context.GetStoreData().StoreName);
ViewData["NavPartialName"] = "../Stores/_Nav";
ViewData.SetActivePageAndTitle(StoreNavPages.Payouts, $"Manage payouts{(string.IsNullOrEmpty(Model.PullPaymentName) ? string.Empty : " for pull payment " + Model.PullPaymentName)}", Context.GetStoreData().StoreName);
var paymentMethods = PayoutHandlers.GetSupportedPaymentMethods();
var stateActions = new List<(string Action, string Text)>();
var payoutHandler = PayoutHandlers.FindPayoutHandler(PaymentMethodId.Parse(Model.PaymentMethodId));
if (payoutHandler is null)
return;
stateActions.AddRange(payoutHandler.GetPayoutSpecificActions().Where(pair => pair.Key == Model.PayoutState).SelectMany(pair => pair.Value));
if (PaymentMethodId.TryParse(Model.PaymentMethodId, out var paymentMethodId))
{
var payoutHandler = PayoutHandlers.FindPayoutHandler(paymentMethodId);
if (payoutHandler is null)
return;
stateActions.AddRange(payoutHandler.GetPayoutSpecificActions().Where(pair => pair.Key == Model.PayoutState).SelectMany(pair => pair.Value));
}
switch (Model.PayoutState)
{
case PayoutState.AwaitingApproval:
@@ -27,6 +34,7 @@
stateActions.Add(("mark-paid", "Mark selected payouts as already paid"));
break;
}
}
@section PageFootContent {
@@ -51,7 +59,7 @@
@foreach (var state in paymentMethods)
{
<li class="nav-item py-0">
<a asp-action="Payouts" asp-route-walletId="@Context.GetRouteValue("walletId")"
<a asp-action="Payouts" asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-payoutState="@Model.PayoutState"
asp-route-paymentMethodId="@state.ToString()"
asp-route-pullPaymentId="@Model.PullPaymentId"
@@ -69,7 +77,7 @@
<li class="nav-item py-0">
<a id="@state.Key-view"
asp-action="Payouts"
asp-route-walletId="@Context.GetRouteValue("walletId")"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-payoutState="@state.Key"
asp-route-pullPaymentId="@Model.PullPaymentId"
asp-route-paymentMethodId="@Model.PaymentMethodId"

View File

@@ -1,7 +1,10 @@
@model PullPaymentsModel
@using BTCPayServer.Views.Stores
@using BTCPayServer.Abstractions.Extensions
@model BTCPayServer.Models.WalletViewModels.PullPaymentsModel
@{
Layout = "../Shared/_NavLayout.cshtml";
ViewData.SetActivePageAndTitle(WalletsNavPages.PullPayments, "Pull payments", Context.GetStoreData().StoreName);
ViewData["NavPartialName"] = "../Stores/_Nav";
ViewData.SetActivePageAndTitle(StoreNavPages.PullPayments, "Pull payments", Context.GetStoreData().StoreName);
}
@section PageHeadContent {
@@ -21,12 +24,9 @@
</a>
</small>
</h4>
@if (Model.HasDerivationSchemeSettings)
{
<a asp-action="NewPullPayment" asp-route-walletId="@Context.GetRouteValue("walletId")" class="btn btn-primary" role="button" id="NewPullPayment">
<span class="fa fa-plus"></span> Create a new pull payment
</a>
}
<a asp-action="NewPullPayment" asp-route-storeId="@Context.GetRouteValue("storeId")" class="btn btn-primary" role="button" id="NewPullPayment">
<span class="fa fa-plus"></span> Create a new pull payment
</a>
</div>
@if (Model.PullPayments.Any())
@@ -83,10 +83,10 @@
asp-controller="PullPayment"
asp-route-pullPaymentId="@pp.Id">View</a> -
<a class="pp-payout" asp-action="Payouts"
asp-route-walletId="@Context.GetRouteValue("walletId")"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-pullPaymentId="@pp.Id">Payouts</a> -
<a asp-action="ArchivePullPayment"
asp-route-walletId="@Context.GetRouteValue("walletId")"
asp-route-storeId="@Context.GetRouteValue("storeId")"
asp-route-pullPaymentId="@pp.Id"
data-bs-toggle="modal"
data-bs-target="#ConfirmModal"

View File

@@ -2,6 +2,8 @@ namespace BTCPayServer.Views.Stores
{
public enum StoreNavPages
{
Index, Create, Rates, Checkout, Tokens, Users, PayButton, Integrations, Wallet, Webhooks, ActivePage
Index, Create, Rates, Checkout, Tokens, Users, PayButton, Integrations, Wallet, Webhooks, ActivePage,
PullPayments,
Payouts
}
}

View File

@@ -7,5 +7,7 @@
<a id="@(nameof(StoreNavPages.PayButton))" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" asp-controller="Stores" asp-action="PayButton" asp-route-storeId="@this.Context.GetRouteValue("storeId")">Pay Button</a>
<a id="@(nameof(StoreNavPages.Integrations))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Integrations)" asp-controller="Stores" asp-action="Integrations" asp-route-storeId="@this.Context.GetRouteValue("storeId")">Integrations</a>
<a id="@(nameof(StoreNavPages.Webhooks))" class="nav-link @ViewData.IsActivePage(StoreNavPages.Webhooks)" asp-controller="Stores" asp-action="Webhooks" asp-route-storeId="@this.Context.GetRouteValue("storeId")">Webhooks</a>
<a class="nav-link @ViewData.IsActivePage(StoreNavPages.PullPayments)" asp-action="PullPayments" asp-controller="StorePullPayments" asp-route-storeId="@this.Context.GetRouteValue("storeId")" id="PullPayments">Pull payments</a>
<a class="nav-link @ViewData.IsActivePage(StoreNavPages.Payouts)" asp-action="Payouts" asp-controller="StorePullPayments" asp-route-storeId="@this.Context.GetRouteValue("storeId")" id="Payouts">Payouts</a>
<vc:ui-extension-point location="store-nav" model="@Model" />
</nav>

View File

@@ -7,8 +7,6 @@ namespace BTCPayServer.Views.Wallets
Transactions,
Rescan,
PSBT,
PullPayments,
Payouts,
Settings,
Receive
}

View File

@@ -1,4 +1,4 @@
@inject SignInManager<ApplicationUser> SignInManager
@inject SignInManager<ApplicationUser> SignInManager
@inject BTCPayNetworkProvider BtcPayNetworkProvider
@{
var wallet = WalletId.Parse(Context.GetRouteValue("walletId").ToString());
@@ -12,8 +12,6 @@
}
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Receive)" asp-action="WalletReceive" asp-route-walletId="@Context.GetRouteValue("walletId")" id="WalletReceive">Receive</a>
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Rescan)" asp-action="WalletRescan" asp-route-walletId="@Context.GetRouteValue("walletId")" id="WalletRescan">Rescan</a>
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.PullPayments)" asp-action="PullPayments" asp-route-walletId="@Context.GetRouteValue("walletId")" id="WalletPullPayments">Pull payments</a>
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.Payouts)" asp-action="Payouts" asp-route-walletId="@Context.GetRouteValue("walletId")" id="WalletPayouts">Payouts</a>
@if (!network.ReadonlyWallet)
{
<a class="nav-link @ViewData.IsActivePage(WalletsNavPages.PSBT)" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")">PSBT</a>