diff --git a/BTCPayServer/Controllers/PublicController.cs b/BTCPayServer/Controllers/PublicController.cs index bec4d906b..c657138b9 100644 --- a/BTCPayServer/Controllers/PublicController.cs +++ b/BTCPayServer/Controllers/PublicController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using BTCPayServer.Filters; using BTCPayServer.Models.StoreViewModels; using BTCPayServer.Services.Stores; using Microsoft.AspNetCore.Cors; @@ -22,12 +23,13 @@ namespace BTCPayServer.Controllers private StoreRepository _StoreRepository; [HttpPost] - [Route("/pay/{storeId}")] + [Route("api/v1/invoices")] + [MediaTypeAcceptConstraintAttribute("text/html")] [IgnoreAntiforgeryToken] [EnableCors(CorsPolicies.All)] - public async Task PayButtonHandle(string storeId, [FromForm]PayButtonViewModel model) + public async Task PayButtonHandle([FromForm]PayButtonViewModel model) { - var store = await _StoreRepository.FindStore(storeId); + var store = await _StoreRepository.FindStore(model.StoreId); if (store == null) ModelState.AddModelError("Store", "Invalid store"); else @@ -36,8 +38,7 @@ namespace BTCPayServer.Controllers if (!storeBlob.AnyoneCanInvoice) ModelState.AddModelError("Store", "Store has not enabled Pay Button"); } - - // TODO: extract validation to model + if (model == null || model.Price <= 0) ModelState.AddModelError("Price", "Price must be greater than 0"); diff --git a/BTCPayServer/Filters/OnlyMediaTypeAttribute.cs b/BTCPayServer/Filters/OnlyMediaTypeAttribute.cs index afb9e597a..3353c0195 100644 --- a/BTCPayServer/Filters/OnlyMediaTypeAttribute.cs +++ b/BTCPayServer/Filters/OnlyMediaTypeAttribute.cs @@ -28,6 +28,28 @@ namespace BTCPayServer.Filters } } + public class MediaTypeAcceptConstraintAttribute : Attribute, IActionConstraint + { + public MediaTypeAcceptConstraintAttribute(string mediaType) + { + MediaType = mediaType ?? throw new ArgumentNullException(nameof(mediaType)); + } + + public string MediaType + { + get; set; + } + + public int Order => 100; + + public bool Accept(ActionConstraintContext context) + { + if (!context.RouteContext.HttpContext.Request.Headers.ContainsKey("Accept")) + return false; + return context.RouteContext.HttpContext.Request.Headers["Accept"].ToString().StartsWith(MediaType, StringComparison.Ordinal); + } + } + public class BitpayAPIConstraintAttribute : Attribute, IActionConstraint { public BitpayAPIConstraintAttribute(bool isBitpayAPI = true) diff --git a/BTCPayServer/Models/StoreViewModels/PayButtonViewModel.cs b/BTCPayServer/Models/StoreViewModels/PayButtonViewModel.cs index b8fbd306c..ccb388e39 100644 --- a/BTCPayServer/Models/StoreViewModels/PayButtonViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/PayButtonViewModel.cs @@ -9,6 +9,7 @@ namespace BTCPayServer.Models.StoreViewModels public class PayButtonViewModel { public decimal Price { get; set; } + public string InvoiceId { get; set; } [Required] public string Currency { get; set; } public string CheckoutDesc { get; set; } diff --git a/BTCPayServer/wwwroot/paybutton/paybutton.js b/BTCPayServer/wwwroot/paybutton/paybutton.js index ba544e882..a901a14fc 100644 --- a/BTCPayServer/wwwroot/paybutton/paybutton.js +++ b/BTCPayServer/wwwroot/paybutton/paybutton.js @@ -42,7 +42,8 @@ function inputChanges(event, buttonSize) { srvModel.buttonSize = buttonSize; } - var html = '
'; + var html = ''; + html += addinput("storeId", srvModel.storeId); html += addinput("price", srvModel.price); if (srvModel.currency) { html += addinput("currency", srvModel.currency);