From 45141d139132d4316df0075bfe23e25fb257e61a Mon Sep 17 00:00:00 2001 From: d11n Date: Mon, 27 Mar 2023 12:12:11 +0200 Subject: [PATCH] Checkout v2: Payment processing state (#4778) --- BTCPayServer.Tests/Checkoutv2Tests.cs | 33 ++++--- .../UIInvoiceController.Testing.cs | 9 +- .../Controllers/UIInvoiceController.UI.cs | 9 ++ .../Models/InvoicingModels/PaymentModel.cs | 2 + .../Views/UIInvoice/Checkout-Cheating.cshtml | 28 ++++-- .../Views/UIInvoice/CheckoutV2.cshtml | 71 ++++++++++++-- BTCPayServer/wwwroot/checkout-v2/checkout.css | 5 +- BTCPayServer/wwwroot/checkout-v2/checkout.js | 95 ++++++++++++------- BTCPayServer/wwwroot/locales/checkout/en.json | 7 +- 9 files changed, 187 insertions(+), 72 deletions(-) diff --git a/BTCPayServer.Tests/Checkoutv2Tests.cs b/BTCPayServer.Tests/Checkoutv2Tests.cs index 9fc9a5fa6..feda6daac 100644 --- a/BTCPayServer.Tests/Checkoutv2Tests.cs +++ b/BTCPayServer.Tests/Checkoutv2Tests.cs @@ -140,7 +140,7 @@ namespace BTCPayServer.Tests Assert.DoesNotContain("Please send", paymentInfo.Text); TestUtils.Eventually(() => { - var expiredSection = s.Driver.FindElement(By.Id("expired")); + var expiredSection = s.Driver.FindElement(By.Id("unpaid")); Assert.True(expiredSection.Displayed); Assert.Contains("Invoice Expired", expiredSection.Text); }); @@ -181,12 +181,27 @@ namespace BTCPayServer.Tests { Assert.Contains("Created transaction", s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text); - s.Server.ExplorerNode.Generate(1); + s.Server.ExplorerNode.Generate(2); paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); Assert.Contains("The invoice hasn't been paid in full", paymentInfo.Text); Assert.Contains("Please send", paymentInfo.Text); }); + // Pay full amount + var amountDue = s.Driver.FindElement(By.Id("AmountDue")).GetAttribute("data-amount-due"); + s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountDue); + s.Driver.FindElement(By.Id("FakePay")).Click(); + + // Processing + TestUtils.Eventually(() => + { + var processingSection = s.Driver.WaitForElement(By.Id("processing")); + Assert.True(processingSection.Displayed); + Assert.Contains("Payment Sent", processingSection.Text); + Assert.Contains("Your payment has been received and is now processing", processingSection.Text); + Assert.True(s.Driver.ElementDoesNotExist(By.Id("confetti"))); + }); + // Mine s.Driver.FindElement(By.Id("Mine")).Click(); TestUtils.Eventually(() => @@ -194,17 +209,13 @@ namespace BTCPayServer.Tests Assert.Contains("Mined 1 block", s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text); }); - - // Pay full amount - var amountDue = s.Driver.FindElement(By.Id("AmountDue")).GetAttribute("data-amount-due"); - s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountDue); - s.Driver.FindElement(By.Id("FakePay")).Click(); + + // Settled TestUtils.Eventually(() => { - s.Server.ExplorerNode.Generate(1); - var paidSection = s.Driver.WaitForElement(By.Id("paid")); - Assert.True(paidSection.Displayed); - Assert.Contains("Invoice Paid", paidSection.Text); + var settledSection = s.Driver.WaitForElement(By.Id("settled")); + Assert.True(settledSection.Displayed); + Assert.Contains("Invoice Paid", settledSection.Text); }); s.Driver.FindElement(By.Id("confetti")); s.Driver.FindElement(By.Id("ReceiptLink")); diff --git a/BTCPayServer/Controllers/UIInvoiceController.Testing.cs b/BTCPayServer/Controllers/UIInvoiceController.Testing.cs index 37f1fc086..e6693bc5e 100644 --- a/BTCPayServer/Controllers/UIInvoiceController.Testing.cs +++ b/BTCPayServer/Controllers/UIInvoiceController.Testing.cs @@ -80,15 +80,13 @@ namespace BTCPayServer.Controllers } return UnprocessableEntity(new { - ErrorMessage = response.ErrorDetail, - AmountRemaining = invoice.Price + ErrorMessage = response.ErrorDetail }); default: return UnprocessableEntity(new { - ErrorMessage = $"Payment method {paymentMethodId} is not supported", - AmountRemaining = invoice.Price + ErrorMessage = $"Payment method {paymentMethodId} is not supported" }); } @@ -97,8 +95,7 @@ namespace BTCPayServer.Controllers { return BadRequest(new { - ErrorMessage = e.Message, - AmountRemaining = invoice.Price + ErrorMessage = e.Message }); } } diff --git a/BTCPayServer/Controllers/UIInvoiceController.UI.cs b/BTCPayServer/Controllers/UIInvoiceController.UI.cs index 8490b1055..e43b18e26 100644 --- a/BTCPayServer/Controllers/UIInvoiceController.UI.cs +++ b/BTCPayServer/Controllers/UIInvoiceController.UI.cs @@ -829,6 +829,15 @@ namespace BTCPayServer.Controllers NetworkFeeMode.Never => 0, _ => throw new NotImplementedException() }, + RequiredConfirmations = invoice.SpeedPolicy switch + { + SpeedPolicy.HighSpeed => 0, + SpeedPolicy.MediumSpeed => 1, + SpeedPolicy.LowMediumSpeed => 2, + SpeedPolicy.LowSpeed => 6, + _ => null + }, + ReceivedConfirmations = invoice.GetAllBitcoinPaymentData(false).FirstOrDefault()?.ConfirmationCount, #pragma warning disable CS0618 // Type or member is obsolete Status = invoice.StatusString, #pragma warning restore CS0618 // Type or member is obsolete diff --git a/BTCPayServer/Models/InvoicingModels/PaymentModel.cs b/BTCPayServer/Models/InvoicingModels/PaymentModel.cs index ae3476832..42149139a 100644 --- a/BTCPayServer/Models/InvoicingModels/PaymentModel.cs +++ b/BTCPayServer/Models/InvoicingModels/PaymentModel.cs @@ -77,5 +77,7 @@ namespace BTCPayServer.Models.InvoicingModels public string ReceiptLink { get; set; } public bool AltcoinsBuild { get; set; } public CheckoutType CheckoutType { get; set; } + public int? RequiredConfirmations { get; set; } + public long? ReceivedConfirmations { get; set; } } } diff --git a/BTCPayServer/Views/UIInvoice/Checkout-Cheating.cshtml b/BTCPayServer/Views/UIInvoice/Checkout-Cheating.cshtml index d28f9b339..caae3dff9 100644 --- a/BTCPayServer/Views/UIInvoice/Checkout-Cheating.cshtml +++ b/BTCPayServer/Views/UIInvoice/Checkout-Cheating.cshtml @@ -1,22 +1,25 @@ @model PaymentModel +

- + - +
- -
@Model.CryptoCode
+ +
-
+
@@ -26,7 +29,7 @@
-
+
@@ -49,27 +52,32 @@ paying: false, mining: false, expiring: false, - amountRemaining: parseFloat(this.btcDue) + amountRemaining: this.btcDue } }, props: { invoiceId: String, paymentMethodId: String, + cryptoCode: String, btcDue: Number, - isPaid: Boolean + isProcessing: Boolean, + isSettled: Boolean }, computed: { display() { return this.successMessage || this.errorMessage || this.displayPayment || this.displayMine || this.displayExpire; }, displayPayment () { - return !this.isPaid; + return !this.isSettled && !this.isProcessing; }, displayExpire () { - return !this.isPaid; + return !this.isSettled && !this.isProcessing; }, displayMine () { return this.paymentMethodId === 'BTC'; + }, + isSats () { + return this.cryptoCode === 'sats'; } }, methods: { diff --git a/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml b/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml index 1221c856c..a3cae306d 100644 --- a/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml +++ b/BTCPayServer/Views/UIInvoice/CheckoutV2.cshtml @@ -76,7 +76,14 @@
- +
@if (displayedPaymentMethods.Count > 1 || hasPaymentPlugins) { @@ -99,11 +106,45 @@
- -
+
@@ -143,7 +191,14 @@
- +
@if (Env.CheatMode) { - + }