mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Fix payment request cloning and unexpire if necessary (#2820)
* Unexpire payment requests without expiry date * Unset expiry date when cloning payment request * Syntax and code improvements
This commit is contained in:
@@ -571,6 +571,22 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Name("ViewAppButton")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
Assert.Equal("Amount due", s.Driver.FindElement(By.CssSelector("[data-test='amount-due-title']")).Text);
|
||||
Assert.Equal("Pay Invoice", s.Driver.FindElement(By.CssSelector("[data-test='pay-button']")).Text.Trim());
|
||||
|
||||
// expire
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
s.Driver.ExecuteJavaScript("document.getElementById('ExpiryDate').value = '2021-01-21T21:00:00.000Z'");
|
||||
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
Assert.Equal("Expired", s.Driver.FindElement(By.CssSelector("[data-test='status']")).Text);
|
||||
|
||||
// unexpire
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.First());
|
||||
s.Driver.FindElement(By.Id("ClearExpiryDate")).Click();
|
||||
s.Driver.FindElement(By.Id("SaveButton")).Click();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
s.Driver.AssertElementNotFound(By.CssSelector("[data-test='status']"));
|
||||
Assert.Equal("Pay Invoice", s.Driver.FindElement(By.CssSelector("[data-test='pay-button']")).Text.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,8 +62,7 @@ namespace BTCPayServer.Controllers
|
||||
_linkGenerator = linkGenerator;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("")]
|
||||
[HttpGet("")]
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> GetPaymentRequests(ListPaymentRequestsViewModel model = null)
|
||||
{
|
||||
@@ -83,22 +82,20 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("edit/{id?}")]
|
||||
[HttpGet("edit/{id?}")]
|
||||
public async Task<IActionResult> EditPaymentRequest(string id)
|
||||
{
|
||||
SelectList stores = null;
|
||||
var data = await _PaymentRequestRepository.FindPaymentRequest(id, GetUserId());
|
||||
if (data == null && !string.IsNullOrEmpty(id))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
stores = new SelectList(await _StoreRepository.GetStoresByUserId(GetUserId()), nameof(StoreData.Id),
|
||||
SelectList stores = new SelectList(await _StoreRepository.GetStoresByUserId(GetUserId()), nameof(StoreData.Id),
|
||||
nameof(StoreData.StoreName), data?.StoreDataId);
|
||||
if (!stores.Any())
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Html =
|
||||
$"Error: You need to create at least one store. <a href='{Url.Action("CreateStore", "UserStores")}' class='alert-link'>Create store</a>",
|
||||
@@ -110,8 +107,7 @@ namespace BTCPayServer.Controllers
|
||||
return View(nameof(EditPaymentRequest), new UpdatePaymentRequestViewModel(data) { Stores = stores });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("edit/{id?}")]
|
||||
[HttpPost("edit/{id?}")]
|
||||
public async Task<IActionResult> EditPaymentRequest(string id, UpdatePaymentRequestViewModel viewModel)
|
||||
{
|
||||
if (string.IsNullOrEmpty(viewModel.Currency) ||
|
||||
@@ -124,7 +120,7 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (data?.Archived is true && viewModel?.Archived is true)
|
||||
if (data?.Archived is true && viewModel.Archived is true)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "You cannot edit an archived payment request.");
|
||||
}
|
||||
@@ -164,14 +160,13 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
data = await _PaymentRequestRepository.CreateOrUpdatePaymentRequest(data);
|
||||
_EventAggregator.Publish(new PaymentRequestUpdated() { Data = data, PaymentRequestId = data.Id, });
|
||||
_EventAggregator.Publish(new PaymentRequestUpdated { Data = data, PaymentRequestId = data.Id, });
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Saved";
|
||||
return RedirectToAction(nameof(EditPaymentRequest), new { id = data.Id });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{id}")]
|
||||
[HttpGet("{id}")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> ViewPaymentRequest(string id)
|
||||
{
|
||||
@@ -181,12 +176,11 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
result.HubPath = PaymentRequestHub.GetHubPath(this.Request);
|
||||
result.HubPath = PaymentRequestHub.GetHubPath(Request);
|
||||
return View(result);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{id}/pay")]
|
||||
[HttpGet("{id}/pay")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> PayPaymentRequest(string id, bool redirectToInvoice = true,
|
||||
decimal? amount = null, CancellationToken cancellationToken = default)
|
||||
@@ -212,7 +206,7 @@ namespace BTCPayServer.Controllers
|
||||
return BadRequest("Payment Request cannot be paid as it has been archived");
|
||||
}
|
||||
|
||||
result.HubPath = PaymentRequestHub.GetHubPath(this.Request);
|
||||
result.HubPath = PaymentRequestHub.GetHubPath(Request);
|
||||
if (result.AmountDue <= 0)
|
||||
{
|
||||
if (redirectToInvoice)
|
||||
@@ -233,7 +227,7 @@ namespace BTCPayServer.Controllers
|
||||
return BadRequest("Payment Request has expired");
|
||||
}
|
||||
|
||||
var stateAllowedToDisplay = new HashSet<InvoiceState>()
|
||||
var stateAllowedToDisplay = new HashSet<InvoiceState>
|
||||
{
|
||||
new InvoiceState(InvoiceStatusLegacy.New, InvoiceExceptionStatus.None),
|
||||
new InvoiceState(InvoiceStatusLegacy.New, InvoiceExceptionStatus.PaidPartial),
|
||||
@@ -245,7 +239,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (redirectToInvoice)
|
||||
{
|
||||
return RedirectToAction("Checkout", "Invoice", new { Id = currentInvoice.Id });
|
||||
return RedirectToAction("Checkout", "Invoice", new { currentInvoice.Id });
|
||||
}
|
||||
|
||||
return Ok(currentInvoice.Id);
|
||||
@@ -256,7 +250,6 @@ namespace BTCPayServer.Controllers
|
||||
else
|
||||
amount = result.AmountDue;
|
||||
|
||||
|
||||
var pr = await _PaymentRequestRepository.FindPaymentRequest(id, null, cancellationToken);
|
||||
var blob = pr.GetBlob();
|
||||
var store = pr.StoreData;
|
||||
@@ -281,12 +274,12 @@ namespace BTCPayServer.Controllers
|
||||
Checkout = {RedirectURL = redirectUrl}
|
||||
};
|
||||
|
||||
var additionalTags = new List<string>() {PaymentRequestRepository.GetInternalTag(id)};
|
||||
var additionalTags = new List<string> {PaymentRequestRepository.GetInternalTag(id)};
|
||||
var newInvoice = await _InvoiceController.CreateInvoiceCoreRaw(invoiceRequest,store, "/",additionalTags, cancellationToken);
|
||||
|
||||
if (redirectToInvoice)
|
||||
{
|
||||
return RedirectToAction("Checkout", "Invoice", new { Id = newInvoice.Id });
|
||||
return RedirectToAction("Checkout", "Invoice", new { newInvoice.Id });
|
||||
}
|
||||
|
||||
return Ok(newInvoice.Id);
|
||||
@@ -297,8 +290,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{id}/cancel")]
|
||||
[HttpGet("{id}/cancel")]
|
||||
public async Task<IActionResult> CancelUnpaidPendingInvoice(string id, bool redirect = true)
|
||||
{
|
||||
var result = await _PaymentRequestService.GetPaymentRequest(id, GetUserId());
|
||||
@@ -334,8 +326,7 @@ namespace BTCPayServer.Controllers
|
||||
return _UserManager.GetUserId(User);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{id}/clone")]
|
||||
[HttpGet("{id}/clone")]
|
||||
public async Task<IActionResult> ClonePaymentRequest(string id)
|
||||
{
|
||||
var result = await EditPaymentRequest(id);
|
||||
@@ -344,6 +335,7 @@ namespace BTCPayServer.Controllers
|
||||
var model = (UpdatePaymentRequestViewModel)viewResult.Model;
|
||||
model.Id = null;
|
||||
model.Archived = false;
|
||||
model.ExpiryDate = null;
|
||||
model.Title = $"Clone of {model.Title}";
|
||||
return View("EditPaymentRequest", model);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace BTCPayServer.PaymentRequest
|
||||
private readonly CurrencyNameTable _currencies;
|
||||
|
||||
public PaymentRequestService(
|
||||
IHubContext<PaymentRequestHub> hubContext,
|
||||
PaymentRequestRepository paymentRequestRepository,
|
||||
BTCPayNetworkProvider btcPayNetworkProvider,
|
||||
AppService appService,
|
||||
@@ -49,17 +48,19 @@ namespace BTCPayServer.PaymentRequest
|
||||
if (blob.ExpiryDate.Value <= DateTimeOffset.UtcNow)
|
||||
currentStatus = Client.Models.PaymentRequestData.PaymentRequestStatus.Expired;
|
||||
}
|
||||
else if (currentStatus != Client.Models.PaymentRequestData.PaymentRequestStatus.Completed)
|
||||
{
|
||||
currentStatus = Client.Models.PaymentRequestData.PaymentRequestStatus.Pending;
|
||||
}
|
||||
|
||||
if (currentStatus != Client.Models.PaymentRequestData.PaymentRequestStatus.Expired)
|
||||
{
|
||||
var rateRules = pr.StoreData.GetStoreBlob().GetRateRules(_BtcPayNetworkProvider);
|
||||
var invoices = await _PaymentRequestRepository.GetInvoicesForPaymentRequest(pr.Id);
|
||||
var contributions = _AppService.GetContributionsByPaymentMethodId(blob.Currency, invoices, true);
|
||||
|
||||
currentStatus = contributions.TotalCurrency >= blob.Amount
|
||||
? Client.Models.PaymentRequestData.PaymentRequestStatus.Completed
|
||||
: Client.Models.PaymentRequestData.PaymentRequestStatus.Pending;
|
||||
|
||||
}
|
||||
|
||||
if (currentStatus != pr.Status)
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
<input asp-for="ExpiryDate"
|
||||
value="@(Model.ExpiryDate?.ToString("u", CultureInfo.InvariantCulture))"
|
||||
class="form-control flatdtpicker" min="today" placeholder="No expiry date has been set for this payment request" />
|
||||
<button class="btn btn-secondary input-group-clear" type="button" title="Clear">
|
||||
<button id="ClearExpiryDate" class="btn btn-secondary input-group-clear" type="button" title="Clear">
|
||||
<span class="fa fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -116,14 +116,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col mt-2 col-12 col-sm-6 mt-sm-0 col-md-12 mt-md-2">
|
||||
<button class="btn btn-primary w-100 text-nowrap" type="submit">Pay Invoice</button>
|
||||
<button class="btn btn-primary w-100 text-nowrap" type="submit" data-test="pay-button">Pay Invoice</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="btn btn-primary d-inline-block d-print-none w-100 text-nowrap @if (!(Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)) { @("btn-lg") }" asp-action="PayPaymentRequest" asp-route-id="@Model.Id">
|
||||
<a class="btn btn-primary d-inline-block d-print-none w-100 text-nowrap @if (!(Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)) { @("btn-lg") }" asp-action="PayPaymentRequest" asp-route-id="@Model.Id" data-test="pay-button">
|
||||
Pay Invoice
|
||||
</a>
|
||||
if (Model.AnyPendingInvoice && !Model.PendingInvoiceHasPayments)
|
||||
@@ -137,7 +137,7 @@
|
||||
else
|
||||
{
|
||||
<div class="h2 text-md-end">
|
||||
<span class="badge @if (Model.Status == "Settled") { @("bg-primary") } else if (Model.Status == "Expired") { @("bg-danger") } else { @("bg-info") }">
|
||||
<span class="badge @if (Model.Status == "Settled") { @("bg-primary") } else if (Model.Status == "Expired") { @("bg-danger") } else { @("bg-info") }" data-test="status">
|
||||
@Model.Status
|
||||
@if (Model.Archived)
|
||||
{
|
||||
@@ -158,7 +158,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col mt-2 col-12 col-sm-6 mt-sm-0 col-md-12 mt-md-2">
|
||||
<button class="btn btn-primary w-100 d-flex d-print-none align-items-center justify-content-center text-nowrap" v-bind:class="{ 'btn-disabled': loading}" :disabled="loading" type="submit">
|
||||
<button class="btn btn-primary w-100 d-flex d-print-none align-items-center justify-content-center text-nowrap" v-bind:class="{ 'btn-disabled': loading}" :disabled="loading" type="submit" data-test="pay-button">
|
||||
<div v-if="loading" class="spinner-grow spinner-grow-sm me-2" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
@@ -169,7 +169,7 @@
|
||||
</form>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button class="btn btn-primary w-100 d-flex d-print-none align-items-center justify-content-center text-nowrap" :class="{ 'btn-lg': !(srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments)}" v-on:click="pay(null)" :disabled="loading">
|
||||
<button class="btn btn-primary w-100 d-flex d-print-none align-items-center justify-content-center text-nowrap" :class="{ 'btn-lg': !(srvModel.anyPendingInvoice && !srvModel.pendingInvoiceHasPayments)}" v-on:click="pay(null)" :disabled="loading" data-test="pay-button">
|
||||
<div v-if="loading" class="spinner-grow spinner-grow-sm me-2" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
@@ -185,7 +185,7 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="h2 text-md-end">
|
||||
<span class="badge" :class="{ 'bg-primary': srvModel.status === 'Settled', 'bg-danger': srvModel.status === 'Expired', 'bg-info': (srvModel.status !== 'Settled' && srvModel.status !== 'Expired') }">
|
||||
<span class="badge" :class="{ 'bg-primary': srvModel.status === 'Settled', 'bg-danger': srvModel.status === 'Expired', 'bg-info': (srvModel.status !== 'Settled' && srvModel.status !== 'Expired') }" data-test="status">
|
||||
{{srvModel.status}}
|
||||
<span v-if="srvModel.archived">(archived)</span>
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user