mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Make Pay Button a plugin (#3862)
* Move files * Fix potentially missing default payment method Before, it got removed if any other value was changed besides the default payment method. * Fix missing store data * Update BTCPayServer/Plugins/PayButton/PayButtonPlugin.cs Co-authored-by: Pavlenex <pavle@pavle.org> * Update pay button warning Closes #3535. Co-authored-by: Pavlenex <pavle@pavle.org>
This commit is contained in:
parent
8a144f3c35
commit
f0e013e1f8
14 changed files with 199 additions and 118 deletions
|
@ -64,6 +64,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.Contains("Starting listening NBXplorer", s.Driver.PageSource);
|
||||
s.Driver.Quit();
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
public async Task CanUseCPFP()
|
||||
{
|
||||
|
|
|
@ -182,12 +182,6 @@
|
|||
<Content Update="Views\UIStores\ShowToken.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\UIStores\PayButtonEnable.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\UIStores\PayButton.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\UIPublic\PayButtonHandle.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
|
|
|
@ -129,12 +129,6 @@
|
|||
<span>Payouts</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<a asp-area="" asp-controller="UIStores" asp-action="PayButton" asp-route-storeId="@Model.Store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" id="StoreNav-PayButton">
|
||||
<vc:icon symbol="pay-button"/>
|
||||
<span>Pay Button</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@ using BTCPayServer.Abstractions.Extensions;
|
|||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Plugins.PayButton.Models;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
|
|
@ -6,7 +6,6 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Data;
|
||||
|
@ -61,7 +60,6 @@ namespace BTCPayServer.Controllers
|
|||
AppService appService,
|
||||
WebhookSender webhookNotificationManager,
|
||||
IDataProtectionProvider dataProtector,
|
||||
NBXplorerDashboard Dashboard,
|
||||
IOptions<ExternalServicesOptions> externalServiceOptions)
|
||||
{
|
||||
_RateFactory = rateFactory;
|
||||
|
@ -83,7 +81,6 @@ namespace BTCPayServer.Controllers
|
|||
_ServiceProvider = serviceProvider;
|
||||
_BtcpayServerOptions = btcpayServerOptions;
|
||||
_BTCPayEnv = btcpayEnv;
|
||||
_Dashboard = Dashboard;
|
||||
_externalServiceOptions = externalServiceOptions;
|
||||
}
|
||||
|
||||
|
@ -104,7 +101,6 @@ namespace BTCPayServer.Controllers
|
|||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly AppService _appService;
|
||||
private readonly EventAggregator _EventAggregator;
|
||||
private readonly NBXplorerDashboard _Dashboard;
|
||||
private readonly IOptions<ExternalServicesOptions> _externalServiceOptions;
|
||||
|
||||
[TempData]
|
||||
|
@ -443,7 +439,7 @@ namespace BTCPayServer.Controllers
|
|||
vm.DefaultPaymentMethod = chosen?.Value;
|
||||
}
|
||||
|
||||
PaymentMethodOptionViewModel.Format[] GetEnabledPaymentMethodChoices(Data.StoreData storeData)
|
||||
public PaymentMethodOptionViewModel.Format[] GetEnabledPaymentMethodChoices(StoreData storeData)
|
||||
{
|
||||
var enabled = storeData.GetEnabledPaymentIds(_NetworkProvider);
|
||||
|
||||
|
@ -457,7 +453,7 @@ namespace BTCPayServer.Controllers
|
|||
}).ToArray();
|
||||
}
|
||||
|
||||
PaymentMethodOptionViewModel.Format? GetDefaultPaymentMethodChoice(Data.StoreData storeData)
|
||||
PaymentMethodOptionViewModel.Format? GetDefaultPaymentMethodChoice(StoreData storeData)
|
||||
{
|
||||
var enabled = storeData.GetEnabledPaymentIds(_NetworkProvider);
|
||||
var defaultPaymentId = storeData.GetDefaultPaymentId();
|
||||
|
@ -991,65 +987,5 @@ namespace BTCPayServer.Controllers
|
|||
return null;
|
||||
return _UserManager.GetUserId(User);
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/disable-anyone-can-pay")]
|
||||
public async Task<IActionResult> DisableAnyoneCanCreateInvoice(string storeId)
|
||||
{
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
blob.AnyoneCanInvoice = false;
|
||||
CurrentStore.SetStoreBlob(blob);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Feature disabled";
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
return RedirectToAction(nameof(PayButton), new { storeId = storeId });
|
||||
}
|
||||
|
||||
[Route("{storeId}/paybutton")]
|
||||
public async Task<IActionResult> PayButton()
|
||||
{
|
||||
var store = CurrentStore;
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
if (!storeBlob.AnyoneCanInvoice)
|
||||
{
|
||||
return View("PayButtonEnable", null);
|
||||
}
|
||||
|
||||
var apps = await _appService.GetAllApps(_UserManager.GetUserId(User), false, store.Id);
|
||||
var appUrl = HttpContext.Request.GetAbsoluteRoot().WithTrailingSlash();
|
||||
var model = new PayButtonViewModel
|
||||
{
|
||||
Price = null,
|
||||
Currency = storeBlob.DefaultCurrency,
|
||||
DefaultPaymentMethod = String.Empty,
|
||||
PaymentMethods = GetEnabledPaymentMethodChoices(store),
|
||||
ButtonSize = 2,
|
||||
UrlRoot = appUrl,
|
||||
PayButtonImageUrl = appUrl + "img/paybutton/pay.svg",
|
||||
StoreId = store.Id,
|
||||
ButtonType = 0,
|
||||
Min = 1,
|
||||
Max = 20,
|
||||
Step = "1",
|
||||
Apps = apps
|
||||
};
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{storeId}/paybutton")]
|
||||
public async Task<IActionResult> PayButton(bool enableStore)
|
||||
{
|
||||
var blob = CurrentStore.GetStoreBlob();
|
||||
blob.AnyoneCanInvoice = enableStore;
|
||||
if (CurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
await _Repo.UpdateStore(CurrentStore);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Store successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(PayButton), new
|
||||
{
|
||||
storeId = CurrentStore.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Plugins.PayButton.Models;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StoreData = BTCPayServer.Data.StoreData;
|
||||
|
||||
namespace BTCPayServer.Plugins.PayButton.Controllers
|
||||
{
|
||||
[Route("stores")]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public class UIPayButtonController : Controller
|
||||
{
|
||||
public UIPayButtonController(
|
||||
StoreRepository repo,
|
||||
UIStoresController storesController,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
AppService appService)
|
||||
{
|
||||
_repo = repo;
|
||||
_userManager = userManager;
|
||||
_appService = appService;
|
||||
_storesController = storesController;
|
||||
}
|
||||
|
||||
readonly StoreRepository _repo;
|
||||
readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly AppService _appService;
|
||||
private readonly UIStoresController _storesController;
|
||||
|
||||
[HttpPost("{storeId}/disable-anyone-can-pay")]
|
||||
public async Task<IActionResult> DisableAnyoneCanCreateInvoice(string storeId)
|
||||
{
|
||||
var blob = GetCurrentStore.GetStoreBlob();
|
||||
blob.AnyoneCanInvoice = false;
|
||||
GetCurrentStore.SetStoreBlob(blob);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Feature disabled";
|
||||
await _repo.UpdateStore(GetCurrentStore);
|
||||
return RedirectToAction(nameof(PayButton), new { storeId });
|
||||
}
|
||||
|
||||
[HttpGet("{storeId}/paybutton")]
|
||||
public async Task<IActionResult> PayButton()
|
||||
{
|
||||
var store = GetCurrentStore;
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
if (!storeBlob.AnyoneCanInvoice)
|
||||
{
|
||||
return View("PayButton/Enable", null);
|
||||
}
|
||||
|
||||
var apps = await _appService.GetAllApps(_userManager.GetUserId(User), false, store.Id);
|
||||
var appUrl = HttpContext.Request.GetAbsoluteRoot().WithTrailingSlash();
|
||||
var model = new PayButtonViewModel
|
||||
{
|
||||
Price = null,
|
||||
Currency = storeBlob.DefaultCurrency,
|
||||
DefaultPaymentMethod = string.Empty,
|
||||
PaymentMethods = _storesController.GetEnabledPaymentMethodChoices(store),
|
||||
ButtonSize = 2,
|
||||
UrlRoot = appUrl,
|
||||
PayButtonImageUrl = appUrl + "img/paybutton/pay.svg",
|
||||
StoreId = store.Id,
|
||||
ButtonType = 0,
|
||||
Min = 1,
|
||||
Max = 20,
|
||||
Step = "1",
|
||||
Apps = apps
|
||||
};
|
||||
return View("PayButton/PayButton", model);
|
||||
}
|
||||
|
||||
[HttpPost("{storeId}/paybutton")]
|
||||
public async Task<IActionResult> PayButton(bool enableStore)
|
||||
{
|
||||
var blob = GetCurrentStore.GetStoreBlob();
|
||||
blob.AnyoneCanInvoice = enableStore;
|
||||
if (GetCurrentStore.SetStoreBlob(blob))
|
||||
{
|
||||
await _repo.UpdateStore(GetCurrentStore);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Store successfully updated";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(PayButton), new
|
||||
{
|
||||
storeId = GetCurrentStore.Id
|
||||
});
|
||||
}
|
||||
|
||||
private StoreData GetCurrentStore => HttpContext.GetStoreData();
|
||||
}
|
||||
}
|
|
@ -2,9 +2,10 @@ using System.Collections.Generic;
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServer.ModelBinders;
|
||||
using BTCPayServer.Models.AppViewModels;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Models.StoreViewModels
|
||||
namespace BTCPayServer.Plugins.PayButton.Models
|
||||
{
|
||||
public class PayButtonViewModel
|
||||
{
|
20
BTCPayServer/Plugins/PayButton/PayButtonPlugin.cs
Normal file
20
BTCPayServer/Plugins/PayButton/PayButtonPlugin.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Abstractions.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BTCPayServer.Plugins.PayButton
|
||||
{
|
||||
public class PayButtonPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
public override string Identifier => "BTCPayServer.Plugins.PayButton";
|
||||
public override string Name => "Pay Button";
|
||||
public override string Description => "Easily-embeddable HTML button for accepting tips and donations .";
|
||||
|
||||
public override void Execute(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IUIExtension>(new UIExtension("PayButton/NavExtension", "header-nav"));
|
||||
base.Execute(services);
|
||||
}
|
||||
}
|
||||
}
|
36
BTCPayServer/Views/Shared/PayButton/Enable.cshtml
Normal file
36
BTCPayServer/Views/Shared/PayButton/Enable.cshtml
Normal file
|
@ -0,0 +1,36 @@
|
|||
@using BTCPayServer.Views.Stores
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@{
|
||||
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" />
|
||||
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-8 col-xxl-constrain">
|
||||
<div class="alert alert-warning alert-dismissible mb-4" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
<h5 class="alert-heading">Warning: Payment button should only be used for tips and donations</h5>
|
||||
<p>
|
||||
Using the payment button for e-commerce integrations is not recommended since order relevant information can be modified by the user.
|
||||
For e-commerce, you should use our
|
||||
<a href="https://docs.btcpayserver.org/API/Greenfield/v1/" class="alert-link" target="_blank" rel="noreferrer noopener">Greenfield API</a>.
|
||||
If this store process commercial transactions, we advise you to
|
||||
<a asp-controller="UIUserStores" asp-action="CreateStore" class="alert-link">create a separate store</a> before using the payment button.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
To start using Pay Button, you need to enable this feature explicitly.
|
||||
Once you do so, anyone could create an invoice on your store (via API, for example).
|
||||
</p>
|
||||
<form method="post">
|
||||
@Html.Hidden("EnableStore", true)
|
||||
<button name="command" id="enable-pay-button" type="submit" value="save" class="btn btn-primary">
|
||||
Enable
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
17
BTCPayServer/Views/Shared/PayButton/NavExtension.cshtml
Normal file
17
BTCPayServer/Views/Shared/PayButton/NavExtension.cshtml
Normal file
|
@ -0,0 +1,17 @@
|
|||
@using BTCPayServer.Client
|
||||
@using BTCPayServer.Views.Stores
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.TagHelpers
|
||||
|
||||
@{ var store = Context.GetStoreData(); }
|
||||
|
||||
@if (store != null)
|
||||
{
|
||||
<li class="nav-item" permission="@Policies.CanModifyStoreSettings">
|
||||
<a asp-area="" asp-controller="UIPayButton" asp-action="PayButton" asp-route-storeId="@store.Id" class="nav-link @ViewData.IsActivePage(StoreNavPages.PayButton)" id="StoreNav-PayButton">
|
||||
<vc:icon symbol="pay-button"/>
|
||||
<span>Pay Button</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
@inject BTCPayServer.Security.ContentSecurityPolicies csp
|
||||
@model PayButtonViewModel
|
||||
@inject Security.ContentSecurityPolicies csp
|
||||
@using BTCPayServer.Views.Stores
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.TagHelpers
|
||||
@model BTCPayServer.Plugins.PayButton.Models.PayButtonViewModel
|
||||
@{
|
||||
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().Id);
|
||||
csp.AllowUnsafeHashes("onBTCPayFormSubmit(event);return false");
|
||||
|
@ -137,9 +140,14 @@
|
|||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
<p><strong>Warning:</strong> This feature should not be activated on a BTCPay Server store processing commercial transactions.</p>
|
||||
<p>By activating this feature, a malicious user can trick you into thinking an order has been processed by creating a new invoice, reusing the same Order Id of another valid order but different amount or currency.</p>
|
||||
<p>If this store process commercial transactions, we advise you to <a asp-controller="UIUserStores" asp-action="CreateStore" class="alert-link">create a separate store</a> before using the payment button.</p>
|
||||
<h5 class="alert-heading">Warning: Payment button should only be used for tips and donations</h5>
|
||||
<p>
|
||||
Using the payment button for e-commerce integrations is not recommended since order relevant information can be modified by the user.
|
||||
For e-commerce, you should use our
|
||||
<a href="https://docs.btcpayserver.org/API/Greenfield/v1/" class="alert-link" target="_blank" rel="noreferrer noopener">Greenfield API</a>.
|
||||
If this store process commercial transactions, we advise you to
|
||||
<a asp-controller="UIUserStores" asp-action="CreateStore" class="alert-link">create a separate store</a> before using the payment button.
|
||||
</p>
|
||||
<form asp-action="DisableAnyoneCanCreateInvoice" asp-route-storeId="@Context.GetRouteValue("storeId")" method="post">
|
||||
<button name="command" id="disable-pay-button" type="submit" class="btn btn-danger mt-0" value="Save">Disable payment button</button>
|
||||
</form>
|
4
BTCPayServer/Views/Shared/PayButton/_ViewImports.cshtml
Normal file
4
BTCPayServer/Views/Shared/PayButton/_ViewImports.cshtml
Normal file
|
@ -0,0 +1,4 @@
|
|||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using BTCPayServer.Plugins.PayButton.Views
|
||||
@namespace BTCPayServer.Plugins.PayButton.Views
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
|
@ -1,29 +0,0 @@
|
|||
@{
|
||||
ViewData.SetActivePage(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().Id);
|
||||
}
|
||||
|
||||
<partial name="_StatusMessage" />
|
||||
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<div class="alert alert-warning alert-dismissible mb-4" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
<p><strong>Warning:</strong> This feature should not be activated on a BTCPay Server store processing commercial transactions.</p>
|
||||
<p>By activating this feature, a malicious user can trick you into thinking an order has been processed by creating a new invoice, reusing the same Order Id of another valid order but different amount or currency.</p>
|
||||
<p class="mb-0">If this store process commercial transactions, we advise you to <a asp-controller="UIUserStores" asp-action="CreateStore" class="alert-link">create a separate store</a> before using the payment button.</p>
|
||||
</div>
|
||||
<p>
|
||||
To start using Pay Button, you need to enable this feature explicitly.
|
||||
Once you do so, anyone could create an invoice on your store (via API, for example).
|
||||
</p>
|
||||
<form method="post">
|
||||
@Html.Hidden("EnableStore", true)
|
||||
<button name="command" id="enable-pay-button" type="submit" value="save" class="btn btn-primary">
|
||||
Enable
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
|
@ -153,11 +153,7 @@ function inputChanges(event, buttonSize) {
|
|||
html += ' </div>\n';
|
||||
}
|
||||
|
||||
if (
|
||||
allowDefaultPaymentMethodSelection &&
|
||||
// Only add default payment method to HTML if user explicitly selected it
|
||||
event && event.target.id === 'default-payment-method' && event.target.value !== ""
|
||||
)
|
||||
if (allowDefaultPaymentMethodSelection && srvModel.defaultPaymentMethod !== "")
|
||||
{
|
||||
html += addInput("defaultPaymentMethod", srvModel.defaultPaymentMethod)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue