Make Checkout V2 the default (#4850)

* Make Checkout V2 the default

* Fix align
This commit is contained in:
Nicolas Dorier 2023-04-05 08:35:50 +09:00 committed by GitHub
parent 1b672a1ace
commit 9b8d08a668
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 64 additions and 56 deletions

View file

@ -1,5 +1,6 @@
using System;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using BTCPayServer.Payments;
using BTCPayServer.Tests.Logging;
using BTCPayServer.Views.Stores;
@ -27,6 +28,7 @@ namespace BTCPayServer.Tests
s.GoToRegister();
s.RegisterNewUser();
s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
s.AddDerivationScheme();
s.GoToStore(StoreNavPages.CheckoutAppearance);
s.Driver.FindElement(By.Id("RequiresRefundEmail")).Click();
@ -72,6 +74,7 @@ namespace BTCPayServer.Tests
s.GoToRegister();
s.RegisterNewUser();
s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
s.AddDerivationScheme();
// Now create an invoice that requires a refund email
@ -124,6 +127,7 @@ namespace BTCPayServer.Tests
s.GoToRegister();
s.RegisterNewUser();
s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
s.AddDerivationScheme();
var invoiceId = s.CreateInvoice();
@ -154,12 +158,12 @@ namespace BTCPayServer.Tests
s.GoToRegister();
s.RegisterNewUser(true);
s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
s.AddLightningNode();
s.AddDerivationScheme();
var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC_LightningLike");
s.GoToInvoiceCheckout(invoiceId);
Assert.Equal("Bitcoin (Lightning) (BTC)", s.Driver.FindElement(By.ClassName("payment__currencies")).Text);
s.Driver.Quit();
}
@ -174,6 +178,7 @@ namespace BTCPayServer.Tests
s.GoToRegister();
s.RegisterNewUser(true);
s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
s.AddLightningNode();
s.GoToLightningSettings();
s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true);
@ -193,6 +198,7 @@ namespace BTCPayServer.Tests
s.GoToRegister();
s.RegisterNewUser();
s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
s.GoToStore();
s.AddDerivationScheme();
var invoiceId = s.CreateInvoice(0.001m, "BTC", "a@x.com");

View file

@ -1,11 +1,13 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Client.Models;
using BTCPayServer.Payments;
using BTCPayServer.Tests.Logging;
using BTCPayServer.Views.Stores;
using NBitcoin;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.Extensions;
using OpenQA.Selenium.Support.UI;
using Xunit;
using Xunit.Abstractions;
@ -32,7 +34,6 @@ namespace BTCPayServer.Tests
s.GoToRegister();
s.RegisterNewUser(true);
s.CreateNewStore();
s.EnableCheckoutV2();
s.AddLightningNode();
// Use non-legacy derivation scheme
s.AddDerivationScheme("BTC", "tpubDD79XF4pzhmPSJ9AyUay9YbXAeD1c6nkUqC32pnKARJH6Ja5hGUfGc76V82ahXpsKqN6UcSGXMkzR34aZq4W23C6DAdZFaVrzWqzj24F8BC");
@ -148,7 +149,7 @@ namespace BTCPayServer.Tests
Assert.True(expiredSection.Displayed);
Assert.Contains("Invoice Expired", expiredSection.Text);
});
Assert.True(s.Driver.ElementDoesNotExist(By.Id("ReceiptLink")));
Assert.True(s.Driver.ElementDoesNotExist(By.Id("receipt-btn")));
Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href"));
// Test payment
@ -179,23 +180,16 @@ namespace BTCPayServer.Tests
await s.Server.ExplorerNode.GenerateAsync(1);
// Fake Pay
s.Driver.FindElement(By.Id("FakePayAmount")).FillIn(amountFraction);
s.Driver.FindElement(By.Id("FakePay")).Click();
TestUtils.Eventually(() =>
{
Assert.Contains("Created transaction",
s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text);
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);
});
s.Driver.Navigate().Refresh();
// 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();
s.PayInvoice();
// Processing
TestUtils.Eventually(() =>
{
@ -205,9 +199,8 @@ namespace BTCPayServer.Tests
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();
s.MineBlockOnInvoiceCheckout();
TestUtils.Eventually(() =>
{
Assert.Contains("Mined 1 block",
@ -222,7 +215,7 @@ namespace BTCPayServer.Tests
Assert.Contains("Invoice Paid", settledSection.Text);
});
s.Driver.FindElement(By.Id("confetti"));
s.Driver.FindElement(By.Id("ReceiptLink"));
s.Driver.FindElement(By.Id("receipt-btn"));
Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href"));
// BIP21
@ -412,7 +405,6 @@ namespace BTCPayServer.Tests
s.GoToRegister();
s.RegisterNewUser();
s.CreateNewStore();
s.EnableCheckoutV2();
s.GoToStore();
s.AddDerivationScheme();
var invoiceId = s.CreateInvoice(0.001m, "BTC", "a@x.com");

View file

@ -249,6 +249,7 @@ namespace BTCPayServer.Tests
await s.StartAsync();
s.RegisterNewUser(true);
var receiver = s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
var receiverSeed = s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit);
var receiverWalletId = new WalletId(receiver.storeId, "BTC");
@ -303,6 +304,7 @@ namespace BTCPayServer.Tests
{
var cryptoCode = "BTC";
var receiver = s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
var receiverSeed = s.GenerateWallet(cryptoCode, "", true, true, format);
var receiverWalletId = new WalletId(receiver.storeId, cryptoCode);

View file

@ -5,6 +5,7 @@ using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Client.Models;
using BTCPayServer.Lightning;
using BTCPayServer.Lightning.CLightning;
using BTCPayServer.Views.Manage;
@ -94,6 +95,7 @@ namespace BTCPayServer.Tests
Driver.FindElement(By.Id("test-payment-amount")).Clear();
Driver.FindElement(By.Id("test-payment-amount")).SendKeys(amount.ToString());
}
Driver.WaitUntilAvailable(By.Id("FakePayment"));
Driver.FindElement(By.Id("FakePayment")).Click();
if (mine)
{
@ -193,16 +195,22 @@ namespace BTCPayServer.Tests
StoreId = storeId;
return (name, storeId);
}
public void EnableCheckoutV2(bool bip21 = false)
public void EnableCheckout(CheckoutType checkoutType, bool bip21 = false)
{
GoToStore(StoreNavPages.CheckoutAppearance);
Driver.SetCheckbox(By.Id("UseNewCheckout"), true);
Driver.WaitForElement(By.Id("OnChainWithLnInvoiceFallback"));
Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), bip21);
if (checkoutType == CheckoutType.V2)
{
Driver.SetCheckbox(By.Id("UseClassicCheckout"), false);
Driver.WaitForElement(By.Id("OnChainWithLnInvoiceFallback"));
Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), bip21);
}
else
{
Driver.SetCheckbox(By.Id("UseClassicCheckout"), true);
}
Driver.FindElement(By.Id("Save")).SendKeys(Keys.Enter);
Assert.Contains("Store successfully updated", FindAlertMessage().Text);
Assert.True(Driver.FindElement(By.Id("UseNewCheckout")).Selected);
Assert.True(Driver.FindElement(By.Id("UseClassicCheckout")).Selected);
}
public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool? importkeys = null, bool isHotWallet = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit)

View file

@ -592,7 +592,7 @@ namespace BTCPayServer.Tests
s.GoToInvoices(s.StoreId);
s.GoToInvoiceCheckout(i);
var checkouturi = s.Driver.Url;
s.PayInvoice();
s.PayInvoice(mine: true);
TestUtils.Eventually(() =>
{
s.Driver.Navigate().Refresh();
@ -602,7 +602,7 @@ namespace BTCPayServer.Tests
{
s.Driver.Navigate().Refresh();
Assert.DoesNotContain("invoice-unsettled", s.Driver.PageSource);
Assert.Contains("invoice-processing", s.Driver.PageSource);
Assert.Contains("\"PaymentDetails\"", s.Driver.PageSource);
});
s.GoToUrl(checkouturi);
@ -1067,6 +1067,7 @@ namespace BTCPayServer.Tests
await s.StartAsync();
s.RegisterNewUser();
s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
s.AddDerivationScheme();
s.Driver.FindElement(By.Id("StoreNav-PaymentRequests")).Click();
@ -2038,6 +2039,7 @@ namespace BTCPayServer.Tests
new[] { s.Server.MerchantLnd.Client });
s.RegisterNewUser(true);
(_, string storeId) = s.CreateNewStore();
s.EnableCheckout(CheckoutType.V1);
var network = s.Server.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode).NBitcoinNetwork;
s.AddLightningNode(LightningConnectionType.CLightning, false);
s.GoToLightningSettings();
@ -2168,6 +2170,7 @@ namespace BTCPayServer.Tests
s.GoToHome();
s.CreateNewStore(false);
s.EnableCheckout(CheckoutType.V1);
s.AddLightningNode(LightningConnectionType.LndREST, false);
s.GoToLightningSettings();
s.Driver.SetCheckbox(By.Id("LNURLEnabled"), true);

View file

@ -17,7 +17,7 @@ namespace BTCPayServer.Tests
#if DEBUG && !SHORT_TIMEOUT
public const int TestTimeout = 600_000;
#else
public const int TestTimeout = 60_000;
public const int TestTimeout = 90_000;
#endif
public static DirectoryInfo TryGetSolutionDirectoryInfo(string currentPath = null)
{

View file

@ -1157,7 +1157,7 @@ namespace BTCPayServer.Controllers
{
StoreId = model.StoreId,
Currency = storeBlob?.DefaultCurrency,
UseNewCheckout = storeBlob?.CheckoutType is CheckoutType.V2,
CheckoutType = storeBlob?.CheckoutType ?? CheckoutType.V2,
AvailablePaymentMethods = GetPaymentMethodsSelectList()
};
@ -1172,7 +1172,7 @@ namespace BTCPayServer.Controllers
{
var store = HttpContext.GetStoreData();
var storeBlob = store.GetStoreBlob();
model.UseNewCheckout = storeBlob.CheckoutType == CheckoutType.V2;
model.CheckoutType = storeBlob.CheckoutType;
model.AvailablePaymentMethods = GetPaymentMethodsSelectList();
if (!ModelState.IsValid)

View file

@ -385,7 +385,7 @@ namespace BTCPayServer.Controllers
};
}).ToList();
vm.UseNewCheckout = storeBlob.CheckoutType == Client.Models.CheckoutType.V2;
vm.UseClassicCheckout = storeBlob.CheckoutType == Client.Models.CheckoutType.V1;
vm.CelebratePayment = storeBlob.CelebratePayment;
vm.OnChainWithLnInvoiceFallback = storeBlob.OnChainWithLnInvoiceFallback;
vm.ShowPayInWalletButton = storeBlob.ShowPayInWalletButton;
@ -509,7 +509,7 @@ namespace BTCPayServer.Controllers
blob.ShowPayInWalletButton = model.ShowPayInWalletButton;
blob.ShowStoreHeader = model.ShowStoreHeader;
blob.CheckoutType = model.UseNewCheckout ? Client.Models.CheckoutType.V2 : Client.Models.CheckoutType.V1;
blob.CheckoutType = model.UseClassicCheckout ? Client.Models.CheckoutType.V1 : Client.Models.CheckoutType.V2;
blob.CelebratePayment = model.CelebratePayment;
blob.OnChainWithLnInvoiceFallback = model.OnChainWithLnInvoiceFallback;
blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;

View file

@ -33,12 +33,15 @@ namespace BTCPayServer.Data
RecommendedFeeBlockTarget = 1;
PaymentMethodCriteria = new List<PaymentMethodCriteria>();
ReceiptOptions = InvoiceDataBase.ReceiptOptions.CreateDefault();
CheckoutType = CheckoutType.V2;
}
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
public NetworkFeeMode NetworkFeeMode { get; set; }
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
[DefaultValue(CheckoutType.V1)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public CheckoutType CheckoutType { get; set; }
public bool RequiresRefundEmail { get; set; }
public bool LightningAmountInSatoshi { get; set; }

View file

@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using BTCPayServer.Client.Models;
using BTCPayServer.Services.Apps;
using BTCPayServer.Validation;
using Microsoft.AspNetCore.Mvc.Rendering;
@ -89,6 +90,6 @@ namespace BTCPayServer.Models.InvoicingModels
get; set;
}
public bool UseNewCheckout { get; set; }
public CheckoutType CheckoutType { get; set; }
}
}

View file

@ -38,8 +38,8 @@ namespace BTCPayServer.Models.StoreViewModels
[Display(Name = "Default payment method on checkout")]
public string DefaultPaymentMethod { get; set; }
[Display(Name = "Use the new checkout")]
public bool UseNewCheckout { get; set; }
[Display(Name = "Use the classic checkout")]
public bool UseClassicCheckout { get; set; }
[Display(Name = "Celebrate payment with confetti")]
public bool CelebratePayment { get; set; }

View file

@ -10,13 +10,13 @@
<form id="test-payment" :action="`/i/${invoiceId}/test-payment`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'paying')" v-if="displayPayment">
<input name="CryptoCode" type="hidden" :value="cryptoCode">
<input name="PaymentMethodId" type="hidden" :value="paymentMethodId">
<label for="FakePayAmount" class="control-label form-label">Fake a {{cryptoCode}} payment for testing</label>
<label for="test-payment-amount" class="control-label form-label">Fake a {{cryptoCode}} payment for testing</label>
<div class="d-flex gap-2 mb-2">
<div class="input-group">
<input id="FakePayAmount" name="Amount" type="number" :step="isSats ? '1' : '0.00000001'" min="0" class="form-control" placeholder="Amount" v-model="amountRemaining" :disabled="paying || paymentMethodId === 'BTC_LightningLike'"/>
<input id="test-payment-amount" name="Amount" type="number" :step="isSats ? '1' : '0.00000001'" min="0" class="form-control" placeholder="Amount" v-model="amountRemaining" :disabled="paying || paymentMethodId === 'BTC_LightningLike'" />
<div id="test-payment-crypto-code" class="input-group-addon input-group-text" v-text="cryptoCode"></div>
</div>
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="paying" id="FakePay">Pay</button>
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="paying" id="FakePayment">Pay</button>
</div>
</form>
<form id="mine-block" :action="`/i/${invoiceId}/mine-blocks`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'mining')" v-if="displayMine">
@ -26,7 +26,7 @@
<input id="BlockCount" name="BlockCount" type="number" step="1" min="1" class="form-control" value="1"/>
<div class="input-group-addon input-group-text">blocks</div>
</div>
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="mining" id="Mine">Mine</button>
<button class="btn btn-secondary flex-shrink-0 px-3 w-100px" type="submit" :disabled="mining" id="mine-block">Mine</button>
</div>
</form>
<form id="expire-invoice" :action="`/i/${invoiceId}/expire`" method="post" v-on:submit.prevent="handleFormSubmit($event, 'expiring')" v-if="displayExpire">

View file

@ -172,7 +172,7 @@
</div>
</div>
<div class="buttons">
<a v-if="srvModel.receiptLink" class="btn btn-primary rounded-pill w-100" :href="srvModel.receiptLink" :target="isModal ? '_top' : null" v-t="'view_receipt'" id="ReceiptLink"></a>
<a v-if="srvModel.receiptLink" class="btn btn-primary rounded-pill w-100" :href="srvModel.receiptLink" :target="isModal ? '_top' : null" v-t="'view_receipt'" id="receipt-btn"></a>
<a v-if="storeLink" class="btn btn-secondary rounded-pill w-100" :href="storeLink" :target="isModal ? '_top' : null" v-html="$t('return_to_store', { storeName: srvModel.storeName })" id="StoreLink"></a>
<button v-else-if="isModal" class="btn btn-secondary rounded-pill w-100" v-on:click="close" v-t="'Close'"></button>
</div>

View file

@ -19,14 +19,16 @@
: ''
});
delegate('click', '#Presets_InStore', e => {
$("#UseNewCheckout").prop('checked', true);
$("#NewCheckoutSettings").addClass('show');
$("#UseClassicCheckout").prop('checked', false);
$("#CheckoutV2Settings").addClass('show');
$("#ClassicCheckoutSettings").removeClass('show');
$("#ShowPayInWalletButton").prop('checked', false);
$("#ShowStoreHeader").prop('checked', false);
});
delegate('click', '#Presets_Online', e => {
$("#UseNewCheckout").prop('checked', false);
$("#NewCheckoutSettings").removeClass('show');
$("#UseClassicCheckout").prop('checked', false);
$("#CheckoutV2Settings").addClass('show');
$("#ClassicCheckoutSettings").removeClass('show');
$("#ShowPayInWalletButton").prop('checked', true);
$("#ShowStoreHeader").prop('checked', true);
});
@ -93,22 +95,13 @@
</div>
</h3>
<div class="d-flex align-items-center mb-3">
<input asp-for="UseNewCheckout" type="checkbox" class="btcpay-toggle me-3" data-bs-toggle="collapse" data-bs-target=".checkout-settings" aria-expanded="@(Model.UseNewCheckout)" aria-controls="NewCheckoutSettings" />
<div>
<label asp-for="UseNewCheckout" class="d-flex align-items-center form-label">
Use the new checkout
<span class="badge bg-warning ms-2">Experimental</span>
</label>
<span asp-validation-for="UseNewCheckout" class="text-danger"></span>
<div class="form-text">
Since v1.7.0 a new version of the checkout is available. Note: For now, the new version is English-only.<br />
We are still collecting <a href="https://github.com/btcpayserver/btcpayserver/discussions/4308" target="_blank" rel="noreferrer noopener">feedback</a> and offer this as an opt-in feature.
</div>
</div>
<div class="d-flex align-items-center mb-4">
<input asp-for="UseClassicCheckout" type="checkbox" class="btcpay-toggle me-3" data-bs-toggle="collapse" data-bs-target=".checkout-settings" aria-expanded="@(Model.UseClassicCheckout)" aria-controls="CheckoutV2Settings" />
<label asp-for="UseClassicCheckout" class="form-label mb-0"></label>
<span asp-validation-for="UseClassicCheckout" class="text-danger"></span>
</div>
<div class="checkout-settings collapse @(Model.UseNewCheckout ? "show" : "")" id="NewCheckoutSettings">
<div class="checkout-settings collapse @(Model.UseClassicCheckout ? "" : "show")" id="CheckoutV2Settings">
<div class="form-group">
<label asp-for="DisplayExpirationTimer" class="form-label"></label>
<div class="input-group">
@ -141,7 +134,7 @@
<input asp-for="LightningAmountInSatoshi" type="checkbox" class="form-check-input" />
<label asp-for="LightningAmountInSatoshi" class="form-check-label"></label>
</div>
<div class="checkout-settings collapse @(Model.UseNewCheckout ? "" : "show")" id="OldCheckoutSettings">
<div class="checkout-settings collapse @(Model.UseClassicCheckout ? "show" : "")" id="ClassicCheckoutSettings">
<div class="form-check">
<input asp-for="RequiresRefundEmail" type="checkbox" class="form-check-input" />
<label asp-for="RequiresRefundEmail" class="form-check-label"></label>