mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Addressing PR review issues
This commit is contained in:
committed by
nicolas.dorier
parent
586a952480
commit
c3998fdf34
@@ -37,8 +37,10 @@ namespace BTCPayServer.Client.Models
|
|||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
// Allow payment requests to be linked to invoices outside BTCPay Server using Reference Number
|
/// <summary>
|
||||||
public string ReferenceNumber { get; set; }
|
/// Linking to invoices outside BTCPay Server using & user defined ids
|
||||||
|
/// </summary>
|
||||||
|
public string ReferenceId { get; set; }
|
||||||
public bool AllowCustomPaymentAmounts { get; set; }
|
public bool AllowCustomPaymentAmounts { get; set; }
|
||||||
|
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using BTCPayServer.Client.Models;
|
using BTCPayServer.Client.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -16,6 +17,11 @@ namespace BTCPayServer.Data
|
|||||||
public string Currency { get; set; }
|
public string Currency { get; set; }
|
||||||
public decimal Amount { get; set; }
|
public decimal Amount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Linking to invoices outside BTCPay Server using & user defined ids
|
||||||
|
/// </summary>
|
||||||
|
public string ReferenceId { get; set; }
|
||||||
|
|
||||||
public StoreData StoreData { get; set; }
|
public StoreData StoreData { get; set; }
|
||||||
|
|
||||||
public Client.Models.PaymentRequestStatus Status { get; set; }
|
public Client.Models.PaymentRequestStatus Status { get; set; }
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace BTCPayServer.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20250407133937_AddingReferenceIdToPaymentRequest")]
|
||||||
|
public partial class AddingReferenceIdToPaymentRequest : Migration
|
||||||
|
{
|
||||||
|
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ReferenceId",
|
||||||
|
table: "PaymentRequests",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
CREATE UNIQUE INDEX IX_PaymentRequests_StoreDataId_ReferenceId
|
||||||
|
ON "PaymentRequests" ("StoreDataId", "ReferenceId")
|
||||||
|
WHERE "ReferenceId" IS NOT NULL;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ReferenceId",
|
||||||
|
table: "PaymentRequests");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
@@ -544,6 +544,9 @@ namespace BTCPayServer.Migrations
|
|||||||
b.Property<DateTimeOffset?>("Expiry")
|
b.Property<DateTimeOffset?>("Expiry")
|
||||||
.HasColumnType("timestamp with time zone");
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("ReferenceId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("Status")
|
b.Property<string>("Status")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|||||||
@@ -2048,7 +2048,7 @@ namespace BTCPayServer.Tests
|
|||||||
new() { Title = "A", Currency = "helloinvalid", Amount = 1 });
|
new() { Title = "A", Currency = "helloinvalid", Amount = 1 });
|
||||||
});
|
});
|
||||||
var newPaymentRequest = await client.CreatePaymentRequest(user.StoreId,
|
var newPaymentRequest = await client.CreatePaymentRequest(user.StoreId,
|
||||||
new() { Title = "A", Currency = "USD", Amount = 1 });
|
new() { Title = "A", Currency = "USD", Amount = 1, ReferenceId = "1234"});
|
||||||
|
|
||||||
//list payment request
|
//list payment request
|
||||||
var paymentRequests = await viewOnly.GetPaymentRequests(user.StoreId);
|
var paymentRequests = await viewOnly.GetPaymentRequests(user.StoreId);
|
||||||
@@ -2061,10 +2061,12 @@ namespace BTCPayServer.Tests
|
|||||||
var paymentRequest = await viewOnly.GetPaymentRequest(user.StoreId, newPaymentRequest.Id);
|
var paymentRequest = await viewOnly.GetPaymentRequest(user.StoreId, newPaymentRequest.Id);
|
||||||
Assert.Equal(newPaymentRequest.Title, paymentRequest.Title);
|
Assert.Equal(newPaymentRequest.Title, paymentRequest.Title);
|
||||||
Assert.Equal(newPaymentRequest.StoreId, user.StoreId);
|
Assert.Equal(newPaymentRequest.StoreId, user.StoreId);
|
||||||
|
Assert.Equal(newPaymentRequest.ReferenceId, paymentRequest.ReferenceId);
|
||||||
|
|
||||||
//update payment request
|
//update payment request
|
||||||
var updateRequest = paymentRequest;
|
var updateRequest = paymentRequest;
|
||||||
updateRequest.Title = "B";
|
updateRequest.Title = "B";
|
||||||
|
updateRequest.ReferenceId = "EmperorNicolasGeneralRockstar";
|
||||||
await AssertHttpError(403, async () =>
|
await AssertHttpError(403, async () =>
|
||||||
{
|
{
|
||||||
await viewOnly.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest);
|
await viewOnly.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest);
|
||||||
@@ -2072,6 +2074,7 @@ namespace BTCPayServer.Tests
|
|||||||
await client.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest);
|
await client.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest);
|
||||||
paymentRequest = await client.GetPaymentRequest(user.StoreId, newPaymentRequest.Id);
|
paymentRequest = await client.GetPaymentRequest(user.StoreId, newPaymentRequest.Id);
|
||||||
Assert.Equal(updateRequest.Title, paymentRequest.Title);
|
Assert.Equal(updateRequest.Title, paymentRequest.Title);
|
||||||
|
Assert.Equal(updateRequest.ReferenceId, paymentRequest.ReferenceId);
|
||||||
|
|
||||||
//archive payment request
|
//archive payment request
|
||||||
await AssertHttpError(403, async () =>
|
await AssertHttpError(403, async () =>
|
||||||
|
|||||||
@@ -43,12 +43,20 @@ namespace BTCPayServer.Tests
|
|||||||
Currency = "BTC",
|
Currency = "BTC",
|
||||||
Amount = 1,
|
Amount = 1,
|
||||||
StoreId = user.StoreId,
|
StoreId = user.StoreId,
|
||||||
Description = "description"
|
Description = "description",
|
||||||
|
ReferenceId = "custom-id-1"
|
||||||
};
|
};
|
||||||
var id = Assert
|
var id = Assert
|
||||||
.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(null, request))
|
.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(null, request))
|
||||||
.RouteValues.Values.Last().ToString();
|
.RouteValues.Values.Last().ToString();
|
||||||
|
|
||||||
|
// Assert initial Title and ReferenceId
|
||||||
|
var repo = tester.PayTester.GetService<PaymentRequestRepository>();
|
||||||
|
var prData = await repo.FindPaymentRequest(id, user.UserId);
|
||||||
|
Assert.NotNull(prData);
|
||||||
|
Assert.Equal("original juice", prData.GetBlob().Title);
|
||||||
|
Assert.Equal("custom-id-1", prData.ReferenceId);
|
||||||
|
|
||||||
paymentRequestController.HttpContext.SetPaymentRequestData(new PaymentRequestData { Id = id, StoreDataId = request.StoreId });
|
paymentRequestController.HttpContext.SetPaymentRequestData(new PaymentRequestData { Id = id, StoreDataId = request.StoreId });
|
||||||
|
|
||||||
// Permission guard for guests editing
|
// Permission guard for guests editing
|
||||||
@@ -56,8 +64,15 @@ namespace BTCPayServer.Tests
|
|||||||
.IsType<NotFoundResult>(await guestpaymentRequestController.EditPaymentRequest(user.StoreId, id));
|
.IsType<NotFoundResult>(await guestpaymentRequestController.EditPaymentRequest(user.StoreId, id));
|
||||||
|
|
||||||
request.Title = "update";
|
request.Title = "update";
|
||||||
|
request.ReferenceId = "custom-id-2";
|
||||||
Assert.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(id, request));
|
Assert.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(id, request));
|
||||||
|
|
||||||
|
// Assert updated Title and ReferenceId
|
||||||
|
prData = await repo.FindPaymentRequest(id, user.UserId);
|
||||||
|
Assert.NotNull(prData);
|
||||||
|
Assert.Equal("update", prData.GetBlob().Title);
|
||||||
|
Assert.Equal("custom-id-2", prData.ReferenceId);
|
||||||
|
|
||||||
Assert.Equal(request.Title,
|
Assert.Equal(request.Title,
|
||||||
Assert.IsType<ViewPaymentRequestViewModel>(Assert
|
Assert.IsType<ViewPaymentRequestViewModel>(Assert
|
||||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Title);
|
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Title);
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
pr = new PaymentRequestData()
|
pr = new PaymentRequestData()
|
||||||
{
|
{
|
||||||
StoreDataId = storeId,
|
StoreDataId = storeId,
|
||||||
Status = Client.Models.PaymentRequestStatus.Pending,
|
Status = PaymentRequestStatus.Pending,
|
||||||
Created = DateTimeOffset.UtcNow,
|
Created = DateTimeOffset.UtcNow,
|
||||||
Amount = request.Amount,
|
Amount = request.Amount,
|
||||||
Currency = request.Currency ?? StoreData.GetStoreBlob().DefaultCurrency,
|
Currency = request.Currency ?? StoreData.GetStoreBlob().DefaultCurrency,
|
||||||
@@ -213,17 +213,19 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pr.ReferenceId = string.IsNullOrEmpty(request.ReferenceId) ? null : request.ReferenceId;
|
||||||
|
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
return this.CreateValidationError(ModelState);
|
return this.CreateValidationError(ModelState);
|
||||||
|
|
||||||
var blob = pr.GetBlob();
|
var blob = pr.GetBlob();
|
||||||
pr.SetBlob(new()
|
pr.SetBlob(new()
|
||||||
{
|
{
|
||||||
|
Title = request.Title,
|
||||||
AllowCustomPaymentAmounts = request.AllowCustomPaymentAmounts,
|
AllowCustomPaymentAmounts = request.AllowCustomPaymentAmounts,
|
||||||
Description = request.Description,
|
Description = request.Description,
|
||||||
Email = request.Email,
|
Email = request.Email,
|
||||||
FormId = request.FormId,
|
FormId = request.FormId,
|
||||||
Title = request.Title,
|
|
||||||
FormResponse = blob.FormId != request.FormId ? null : blob.FormResponse
|
FormResponse = blob.FormId != request.FormId ? null : blob.FormResponse
|
||||||
});
|
});
|
||||||
pr = await _paymentRequestRepository.CreateOrUpdatePaymentRequest(pr);
|
pr = await _paymentRequestRepository.CreateOrUpdatePaymentRequest(pr);
|
||||||
@@ -251,6 +253,7 @@ namespace BTCPayServer.Controllers.Greenfield
|
|||||||
Title = blob.Title,
|
Title = blob.Title,
|
||||||
ExpiryDate = data.Expiry,
|
ExpiryDate = data.Expiry,
|
||||||
Email = blob.Email,
|
Email = blob.Email,
|
||||||
|
ReferenceId = data.ReferenceId,
|
||||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts,
|
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts,
|
||||||
FormResponse = blob.FormResponse,
|
FormResponse = blob.FormResponse,
|
||||||
FormId = blob.FormId
|
FormId = blob.FormId
|
||||||
|
|||||||
@@ -163,8 +163,8 @@ namespace BTCPayServer.Controllers
|
|||||||
Checkout = { RedirectURL = redirectUrl },
|
Checkout = { RedirectURL = redirectUrl },
|
||||||
Receipt = new InvoiceDataBase.ReceiptOptions { Enabled = false }
|
Receipt = new InvoiceDataBase.ReceiptOptions { Enabled = false }
|
||||||
};
|
};
|
||||||
if (prBlob.ReferenceNumber is not null or "")
|
if (prData.ReferenceId is not null or "")
|
||||||
invoiceRequest.AdditionalSearchTerms = new[] { prBlob.ReferenceNumber };
|
invoiceRequest.AdditionalSearchTerms = [prData.ReferenceId];
|
||||||
var additionalTags = new List<string> { PaymentRequestRepository.GetInternalTag(id) };
|
var additionalTags = new List<string> { PaymentRequestRepository.GetInternalTag(id) };
|
||||||
return await CreateInvoiceCoreRaw(invoiceRequest, storeData, request.GetAbsoluteRoot(), additionalTags, cancellationToken);
|
return await CreateInvoiceCoreRaw(invoiceRequest, storeData, request.GetAbsoluteRoot(), additionalTags, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ namespace BTCPayServer.Controllers
|
|||||||
data.Amount = viewModel.Amount;
|
data.Amount = viewModel.Amount;
|
||||||
data.Currency = viewModel.Currency ?? store.GetStoreBlob().DefaultCurrency;
|
data.Currency = viewModel.Currency ?? store.GetStoreBlob().DefaultCurrency;
|
||||||
data.Expiry = viewModel.ExpiryDate?.ToUniversalTime();
|
data.Expiry = viewModel.ExpiryDate?.ToUniversalTime();
|
||||||
blob.ReferenceNumber = viewModel.ReferenceNumber;
|
data.ReferenceId = viewModel.ReferenceId;
|
||||||
blob.AllowCustomPaymentAmounts = viewModel.AllowCustomPaymentAmounts;
|
blob.AllowCustomPaymentAmounts = viewModel.AllowCustomPaymentAmounts;
|
||||||
blob.FormId = viewModel.FormId;
|
blob.FormId = viewModel.FormId;
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class PaymentRequestWebhookDeliveryRequest : WebhookSender.WebhookDeliver
|
|||||||
.Replace("{PaymentRequest.Currency}", data.Currency)
|
.Replace("{PaymentRequest.Currency}", data.Currency)
|
||||||
.Replace("{PaymentRequest.Title}", blob.Title)
|
.Replace("{PaymentRequest.Title}", blob.Title)
|
||||||
.Replace("{PaymentRequest.Description}", blob.Description)
|
.Replace("{PaymentRequest.Description}", blob.Description)
|
||||||
.Replace("{PaymentRequest.ReferenceNumber}", blob.ReferenceNumber)
|
.Replace("{PaymentRequest.ReferenceId}", data.ReferenceId)
|
||||||
.Replace("{PaymentRequest.Status}", _evt.Data.Status.ToString());
|
.Replace("{PaymentRequest.Status}", _evt.Data.Status.ToString());
|
||||||
|
|
||||||
res = InterpolateJsonField(res, "PaymentRequest.FormResponse", blob.FormResponse);
|
res = InterpolateJsonField(res, "PaymentRequest.FormResponse", blob.FormResponse);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
Description = blob.Description;
|
Description = blob.Description;
|
||||||
ExpiryDate = data.Expiry?.UtcDateTime;
|
ExpiryDate = data.Expiry?.UtcDateTime;
|
||||||
Email = blob.Email;
|
Email = blob.Email;
|
||||||
ReferenceNumber = blob.ReferenceNumber;
|
ReferenceId = data.ReferenceId;
|
||||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
||||||
FormResponse = blob.FormResponse is null
|
FormResponse = blob.FormResponse is null
|
||||||
? null
|
? null
|
||||||
@@ -85,10 +85,9 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
[MailboxAddress]
|
[MailboxAddress]
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
|
||||||
[Display(Name = "Reference Number")]
|
[Display(Name = "Reference Id")]
|
||||||
[MaxLength(50)]
|
public string ReferenceId { get; set; }
|
||||||
public string ReferenceNumber { get; set; }
|
|
||||||
|
|
||||||
[Display(Name = "Allow payee to create invoices with custom amounts")]
|
[Display(Name = "Allow payee to create invoices with custom amounts")]
|
||||||
public bool AllowCustomPaymentAmounts { get; set; }
|
public bool AllowCustomPaymentAmounts { get; set; }
|
||||||
|
|
||||||
@@ -111,7 +110,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
Description = blob.Description;
|
Description = blob.Description;
|
||||||
ExpiryDate = data.Expiry?.UtcDateTime;
|
ExpiryDate = data.Expiry?.UtcDateTime;
|
||||||
Email = blob.Email;
|
Email = blob.Email;
|
||||||
ReferenceNumber = blob.ReferenceNumber;
|
ReferenceId = data.ReferenceId;
|
||||||
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
AllowCustomPaymentAmounts = blob.AllowCustomPaymentAmounts;
|
||||||
switch (data.Status)
|
switch (data.Status)
|
||||||
{
|
{
|
||||||
@@ -133,7 +132,7 @@ namespace BTCPayServer.Models.PaymentRequestViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public StoreBrandingViewModel StoreBranding { get; set; }
|
public StoreBrandingViewModel StoreBranding { get; set; }
|
||||||
public string ReferenceNumber { get; set; }
|
public string ReferenceId { get; set; }
|
||||||
public bool AllowCustomPaymentAmounts { get; set; }
|
public bool AllowCustomPaymentAmounts { get; set; }
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public string Status { get; set; }
|
public string Status { get; set; }
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ namespace BTCPayServer.Services
|
|||||||
pathBase: request.PathBase
|
pathBase: request.PathBase
|
||||||
) ?? throw Bug();
|
) ?? throw Bug();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string PaymentRequestByIdLink(string payReqId, HttpRequest request)
|
public string PaymentRequestByIdLink(string payReqId, HttpRequest request)
|
||||||
{
|
{
|
||||||
return LinkGenerator.GetUriByAction(
|
return LinkGenerator.GetUriByAction(
|
||||||
@@ -104,7 +104,7 @@ namespace BTCPayServer.Services
|
|||||||
pathBase: request.PathBase
|
pathBase: request.PathBase
|
||||||
) ?? throw Bug();
|
) ?? throw Bug();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string PaymentRequestListLink(string storeId, HttpRequest request)
|
public string PaymentRequestListLink(string storeId, HttpRequest request)
|
||||||
{
|
{
|
||||||
return LinkGenerator.GetUriByAction(
|
return LinkGenerator.GetUriByAction(
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Client.Models;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Lightning.Eclair;
|
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Stores;
|
using BTCPayServer.Services.Stores;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -115,13 +114,13 @@ namespace BTCPayServer.Services.PaymentRequests
|
|||||||
Type = PaymentRequestEvent.StatusChanged
|
Type = PaymentRequestEvent.StatusChanged
|
||||||
});
|
});
|
||||||
|
|
||||||
if (status == Client.Models.PaymentRequestData.PaymentRequestStatus.Completed)
|
if (status == PaymentRequestStatus.Completed)
|
||||||
{
|
{
|
||||||
_eventAggregator.Publish(new PaymentRequestEvent()
|
_eventAggregator.Publish(new PaymentRequestEvent()
|
||||||
{
|
{
|
||||||
Data = paymentRequestData,
|
Data = paymentRequestData,
|
||||||
Type = PaymentRequestEvent.Completed
|
Type = PaymentRequestEvent.Completed
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async Task<PaymentRequestData[]> GetExpirablePaymentRequests(CancellationToken cancellationToken = default)
|
public async Task<PaymentRequestData[]> GetExpirablePaymentRequests(CancellationToken cancellationToken = default)
|
||||||
@@ -138,30 +137,23 @@ namespace BTCPayServer.Services.PaymentRequests
|
|||||||
public async Task<PaymentRequestData[]> FindPaymentRequests(PaymentRequestQuery query, CancellationToken cancellationToken = default)
|
public async Task<PaymentRequestData[]> FindPaymentRequests(PaymentRequestQuery query, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
await using var context = _ContextFactory.CreateContext();
|
await using var context = _ContextFactory.CreateContext();
|
||||||
IQueryable<PaymentRequestData> queryable;
|
IQueryable<PaymentRequestData> queryable = context.PaymentRequests.AsQueryable();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(query.StoreId))
|
||||||
|
queryable = queryable.Where(data => data.StoreDataId == query.StoreId);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(query.SearchText))
|
if (!string.IsNullOrEmpty(query.SearchText))
|
||||||
{
|
{
|
||||||
var searchText = query.SearchText;
|
if (string.IsNullOrEmpty(query.StoreId))
|
||||||
|
throw new InvalidOperationException("PaymentRequestQuery.StoreId should be specified");
|
||||||
queryable = context.PaymentRequests.FromSqlInterpolated($@"
|
// We are repeating the StoreId on purpose here, so Postgres can use the index
|
||||||
SELECT * FROM public.""PaymentRequests""
|
queryable = context.PaymentRequests.Where(p => (p.StoreDataId == query.StoreId && p.ReferenceId == query.SearchText) || p.Id == query.SearchText);
|
||||||
WHERE ""Blob2"" ->> 'referenceNumber' = {searchText}
|
|
||||||
OR ""Blob2"" ->> 'title' ILIKE {'%' + searchText + '%'}
|
|
||||||
");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
queryable = context.PaymentRequests.AsQueryable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
queryable = queryable.Include(data => data.StoreData);
|
queryable = queryable.Include(data => data.StoreData);
|
||||||
|
|
||||||
if (!query.IncludeArchived)
|
if (!query.IncludeArchived)
|
||||||
queryable = queryable.Where(data => !data.Archived);
|
queryable = queryable.Where(data => !data.Archived);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(query.StoreId))
|
|
||||||
queryable = queryable.Where(data => data.StoreDataId == query.StoreId);
|
|
||||||
|
|
||||||
if (query.Status != null && query.Status.Any())
|
if (query.Status != null && query.Status.Any())
|
||||||
queryable = queryable.Where(data => query.Status.Contains(data.Status));
|
queryable = queryable.Where(data => query.Status.Contains(data.Status));
|
||||||
@@ -171,7 +163,7 @@ namespace BTCPayServer.Services.PaymentRequests
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(query.UserId))
|
if (!string.IsNullOrEmpty(query.UserId))
|
||||||
queryable = queryable.Where(data =>
|
queryable = queryable.Where(data =>
|
||||||
data.StoreData != null && data.StoreData.UserStores.Any(u => u.ApplicationUserId == query.UserId));
|
data.StoreData.UserStores.Any(u => u.ApplicationUserId == query.UserId));
|
||||||
|
|
||||||
queryable = queryable.OrderByDescending(u => u.Created);
|
queryable = queryable.OrderByDescending(u => u.Created);
|
||||||
|
|
||||||
@@ -180,7 +172,7 @@ namespace BTCPayServer.Services.PaymentRequests
|
|||||||
|
|
||||||
if (query.Count.HasValue)
|
if (query.Count.HasValue)
|
||||||
queryable = queryable.Take(query.Count.Value);
|
queryable = queryable.Take(query.Count.Value);
|
||||||
|
|
||||||
var items = await queryable.ToArrayAsync(cancellationToken);
|
var items = await queryable.ToArrayAsync(cancellationToken);
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,9 +84,9 @@
|
|||||||
<span asp-validation-for="AllowCustomPaymentAmounts" class="text-danger"></span>
|
<span asp-validation-for="AllowCustomPaymentAmounts" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="ReferenceNumber" class="form-label"></label>
|
<label asp-for="ReferenceId" class="form-label"></label>
|
||||||
<input asp-for="ReferenceNumber" class="form-control" />
|
<input asp-for="ReferenceId" class="form-control" />
|
||||||
<span asp-validation-for="ReferenceNumber" class="text-danger"></span>
|
<span asp-validation-for="ReferenceId" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="ExpiryDate" class="form-label"></label>
|
<label asp-for="ExpiryDate" class="form-label"></label>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
<input type="hidden" asp-for="Count" />
|
<input type="hidden" asp-for="Count" />
|
||||||
<input type="hidden" asp-for="TimezoneOffset" />
|
<input type="hidden" asp-for="TimezoneOffset" />
|
||||||
<input asp-for="SearchTerm" type="hidden" value="@Model.Search.WithoutSearchText()"/>
|
<input asp-for="SearchTerm" type="hidden" value="@Model.Search.WithoutSearchText()"/>
|
||||||
<input asp-for="SearchText" class="form-control" placeholder="@StringLocalizer["Search…"]" />
|
<input asp-for="SearchText" class="form-control" placeholder="@StringLocalizer["Search by Id..."]" />
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button id="StatusOptionsToggle" class="btn btn-secondary dropdown-toggle dropdown-toggle-custom-caret" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<button id="StatusOptionsToggle" class="btn btn-secondary dropdown-toggle dropdown-toggle-custom-caret" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
@if (statusFilterCount > 0)
|
@if (statusFilterCount > 0)
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th text-translate="true">Number</th>
|
<th text-translate="true">Reference Id</th>
|
||||||
<th text-translate="true">Status</th>
|
<th text-translate="true">Status</th>
|
||||||
<th class="amount-col" text-translate="true">Amount</th>
|
<th class="amount-col" text-translate="true">Amount</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
@(item.ExpiryDate?.ToBrowserDate() ?? new HtmlString("<span class=\"text-muted\">No Expiry</span>"))
|
@(item.ExpiryDate?.ToBrowserDate() ?? new HtmlString("<span class=\"text-muted\">No Expiry</span>"))
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@item.ReferenceNumber
|
@item.ReferenceId
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge badge-@item.Status.ToLower() status-badge">@item.Status</span>
|
<span class="badge badge-@item.Status.ToLower() status-badge">@item.Status</span>
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
<code>{PaymentRequest.Currency}</code>,
|
<code>{PaymentRequest.Currency}</code>,
|
||||||
<code>{PaymentRequest.Title}</code>,
|
<code>{PaymentRequest.Title}</code>,
|
||||||
<code>{PaymentRequest.Description}</code>,
|
<code>{PaymentRequest.Description}</code>,
|
||||||
<code>{PaymentRequest.ReferenceNumber}</code>,
|
<code>{PaymentRequest.ReferenceId}</code>,
|
||||||
<code>{PaymentRequest.Status}</code>
|
<code>{PaymentRequest.Status}</code>
|
||||||
<code>{PaymentRequest.FormResponse}*</code>
|
<code>{PaymentRequest.FormResponse}*</code>
|
||||||
</td>
|
</td>
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
},
|
},
|
||||||
@{ var paymentRequestsLink = CallbackGenerator.PaymentRequestListLink(storeId, this.Context.Request); }
|
@{ var paymentRequestsLink = CallbackGenerator.PaymentRequestListLink(storeId, this.Context.Request); }
|
||||||
@WebhookEventType.PaymentRequestCompleted: {
|
@WebhookEventType.PaymentRequestCompleted: {
|
||||||
subject: 'Payment Request {PaymentRequest.Title} {PaymentRequest.ReferenceNumber} Completed',
|
subject: 'Payment Request {PaymentRequest.Title} {PaymentRequest.ReferenceId} Completed',
|
||||||
body: 'The total of {PaymentRequest.Amount} {PaymentRequest.Currency} has been received and Payment Request {PaymentRequest.Id} is completed. ' +
|
body: 'The total of {PaymentRequest.Amount} {PaymentRequest.Currency} has been received and Payment Request {PaymentRequest.Id} is completed. ' +
|
||||||
'\nReview the payment requests: ' + @Safe.Json(paymentRequestsLink)
|
'\nReview the payment requests: ' + @Safe.Json(paymentRequestsLink)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -461,6 +461,12 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"referenceId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "An optional user-defined identifier for this payment request.",
|
||||||
|
"nullable": true,
|
||||||
|
"example": "INV-123493"
|
||||||
|
},
|
||||||
"allowCustomPaymentAmounts": {
|
"allowCustomPaymentAmounts": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Whether to allow users to create invoices that partially pay the payment request ",
|
"description": "Whether to allow users to create invoices that partially pay the payment request ",
|
||||||
|
|||||||
Reference in New Issue
Block a user