diff --git a/BTCPayServer/Controllers/InvoiceController.UI.cs b/BTCPayServer/Controllers/InvoiceController.UI.cs index 9d7c620a2..b57d04858 100644 --- a/BTCPayServer/Controllers/InvoiceController.UI.cs +++ b/BTCPayServer/Controllers/InvoiceController.UI.cs @@ -229,6 +229,7 @@ namespace BTCPayServer.Controllers Status = invoice.Status, CryptoImage = "/" + GetImage(paymentMethodId, network), NetworkFeeDescription = $"{accounting.TxRequired} transaction{(accounting.TxRequired > 1 ? "s" : "")} x {paymentMethodDetails.GetTxFee()} {network.CryptoCode}", + AllowCoinConversion = store.GetStoreBlob().AllowCoinConversion, AvailableCryptos = invoice.GetPaymentMethods(_NetworkProvider) .Where(i => i.Network != null) .Select(kv=> new PaymentModel.AvailableCrypto() diff --git a/BTCPayServer/Controllers/StoresController.cs b/BTCPayServer/Controllers/StoresController.cs index 4e5a57824..9331dfab1 100644 --- a/BTCPayServer/Controllers/StoresController.cs +++ b/BTCPayServer/Controllers/StoresController.cs @@ -222,6 +222,7 @@ namespace BTCPayServer.Controllers vm.InvoiceExpiration = storeBlob.InvoiceExpiration; vm.RateMultiplier = (double)storeBlob.GetRateMultiplier(); vm.PreferredExchange = storeBlob.PreferredExchange.IsCoinAverage() ? "coinaverage" : storeBlob.PreferredExchange; + vm.AllowCoinConversion = storeBlob.AllowCoinConversion; return View(vm); } @@ -298,6 +299,7 @@ namespace BTCPayServer.Controllers blob.PreferredExchange = model.PreferredExchange; blob.SetRateMultiplier(model.RateMultiplier); + blob.AllowCoinConversion = model.AllowCoinConversion; if (store.SetStoreBlob(blob)) { diff --git a/BTCPayServer/Data/StoreData.cs b/BTCPayServer/Data/StoreData.cs index 020957db3..aec91dabf 100644 --- a/BTCPayServer/Data/StoreData.cs +++ b/BTCPayServer/Data/StoreData.cs @@ -213,6 +213,10 @@ namespace BTCPayServer.Data { get; set; } + public bool AllowCoinConversion + { + get; set; + } [DefaultValue(60)] [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] public int MonitoringExpiration diff --git a/BTCPayServer/Models/InvoicingModels/PaymentModel.cs b/BTCPayServer/Models/InvoicingModels/PaymentModel.cs index 964871636..d82f16e74 100644 --- a/BTCPayServer/Models/InvoicingModels/PaymentModel.cs +++ b/BTCPayServer/Models/InvoicingModels/PaymentModel.cs @@ -43,5 +43,7 @@ namespace BTCPayServer.Models.InvoicingModels public int MaxTimeMinutes { get; internal set; } public string PaymentType { get; internal set; } public string PaymentMethodId { get; internal set; } + + public bool AllowCoinConversion { get; set; } } } diff --git a/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs b/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs index 012132b55..0844ee874 100644 --- a/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs +++ b/BTCPayServer/Models/StoreViewModels/StoreViewModel.cs @@ -94,6 +94,12 @@ namespace BTCPayServer.Models.StoreViewModels get; set; } + [Display(Name = "Allow conversion through third party (Shapeshift, Changelly...)")] + public bool AllowCoinConversion + { + get; set; + } + public string StatusMessage { get; set; diff --git a/BTCPayServer/Views/Invoice/Checkout.cshtml b/BTCPayServer/Views/Invoice/Checkout.cshtml index dec3105f2..1dad47bee 100644 --- a/BTCPayServer/Views/Invoice/Checkout.cshtml +++ b/BTCPayServer/Views/Invoice/Checkout.cshtml @@ -170,23 +170,20 @@
Copy
-
+ @if (Model.AllowCoinConversion) + { +
+ Conversion +
+
+ } + else + { +
+ }
-
-
- - - -
- -
@@ -211,7 +208,7 @@ - +
@@ -219,6 +216,94 @@
+
+
+ + + +
+
+ + Open in wallet + + +
+
+
+
+ To complete your payment, please send {{ srvModel.btcDue }} {{ srvModel.cryptoCode }} to the address below. +
+
+
+
Amount
+ +
+ {{srvModel.btcDue}} {{ srvModel.cryptoCode }} +
+ Copied +
+
+ +
+
+
+
+
+
+
Address
+ +
+
+ +
{{srvModel.btcAddress}}
+
+
+ Copied +
+
+ +
+
+
+
+
+ @if (Model.AllowCoinConversion) + { +
+
+
+ + You can pay {{ srvModel.btcDue }} {{ srvModel.cryptoCode }} using altcoins other than the ones merchant directly supports. +

+ This service is provided by 3rd party. Please keep in mind that + we have no control over how providers will forward your funds. + Invoice will only be marked paid once funds are received on {{srvModel.cryptoCode}} Blockchain. +
+
+
+ + + + + + @*Changelly doesn't have TO_AMOUNT support so we can't include it + + + Changelly + *@ +
+
+
+
+ + No conversion providers available for BTC Lightning Network payments. + +
+
+
+ } -
-
- To complete your payment, please send {{ srvModel.btcDue }} {{ srvModel.cryptoCode }} to the address below. -
-
-
-
Amount
- -
- {{srvModel.btcDue}} {{ srvModel.cryptoCode }} -
- Copied -
-
- -
-
-
-
-
-
-
Address
- -
-
- -
{{srvModel.btcAddress}}
-
-
- Copied -
-
- -
-
-
-
-
+ -
@@ -611,8 +656,6 @@
- - +
+ + +
Derivation Scheme
The DerivationScheme represents the destination of the funds received by your invoice on chain. @@ -85,7 +89,7 @@ - @foreach(var scheme in Model.DerivationSchemes) + @foreach (var scheme in Model.DerivationSchemes) { @scheme.Crypto @@ -114,7 +118,7 @@ - @foreach(var scheme in Model.LightningNodes) + @foreach (var scheme in Model.LightningNodes) { @scheme.CryptoCode diff --git a/BTCPayServer/wwwroot/css/normalizer.css b/BTCPayServer/wwwroot/css/normalizer.css index 367f92900..3cec53042 100644 --- a/BTCPayServer/wwwroot/css/normalizer.css +++ b/BTCPayServer/wwwroot/css/normalizer.css @@ -8562,10 +8562,23 @@ strong { transition: all .2s ease; } - .payment-tabs__slider.slide-right { + .payment-tabs__slider.slide-copy { right: 0; } + .payment-tabs__slider.three-tabs { + width: 33%; + right: 67%; + } + + .payment-tabs__slider.three-tabs.slide-copy { + right: 33%; + } + + .payment-tabs__slider.three-tabs.slide-altcoins { + right: 0; + } + .manual__step-one__header { padding-top: 20px; text-align: center; diff --git a/BTCPayServer/wwwroot/js/core.js b/BTCPayServer/wwwroot/js/core.js index 8946e2181..bad65d384 100644 --- a/BTCPayServer/wwwroot/js/core.js +++ b/BTCPayServer/wwwroot/js/core.js @@ -20,11 +20,7 @@ function onDataCallback(jsonData) { $(".modal-dialog").addClass("paid"); - if ($("#scan").hasClass("active")) { - $("#scan").removeClass("active"); - } else if ($("#copy").hasClass("active")) { - $("#copy").removeClass("active"); - } + resetTabsSlider(); $("#paid").addClass("active"); } @@ -36,11 +32,7 @@ function onDataCallback(jsonData) { $(".modal-dialog").addClass("expired"); $("#expired").addClass("active"); - if ($("#scan").hasClass("active")) { - $("#scan").removeClass("active"); - } else if ($("#copy").hasClass("active")) { - $("#copy").removeClass("active"); - } + resetTabsSlider(); } if (checkoutCtrl.srvModel.status !== newStatus) { @@ -53,6 +45,7 @@ function onDataCallback(jsonData) { $(".payment__spinner").hide(); } + jsonData.shapeshiftUrl = "https://shapeshift.io/shifty.html?destination=" + jsonData.btcAddress + "&output=" + jsonData.paymentMethodId + "&amount=" + jsonData.btcDue; // updating ui checkoutCtrl.srvModel = jsonData; } @@ -143,21 +136,24 @@ $(document).ready(function () { contentType: "application/json; charset=utf-8" }).done(function () { hideEmailForm(); - }) - .fail(function (jqXHR, textStatus, errorThrown) { + }).fail(function (jqXHR, textStatus, errorThrown) { - }) + }) .always(function () { $("#emailAddressForm .input-wrapper bp-loading-button .action-button").removeClass("loading"); }); } else { - $("#emailAddressForm").addClass("ng-touched ng-dirty ng-submitted ng-invalid"); - } }); } + // Validate Email address + function validateEmail(email) { + var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(email); + } + /* =============== Even listeners =============== */ // Email @@ -171,47 +167,51 @@ $(document).ready(function () { // Scan/Copy Transitions // Scan Tab $("#scan-tab").click(function () { - if (!$(this).is(".active")) { - $(this).addClass("active"); - } - - if ($("#copy-tab").is(".active")) { - $("#copy-tab").removeClass("active"); - } - - $(".payment-tabs__slider").removeClass("slide-right"); - - if (!$("#scan").is(".active")) { - $("#copy").hide(); - $("#copy").removeClass("active"); - - $("#scan").show(); - $("#scan").addClass("active"); - } + resetTabsSlider(); + activateTab("#scan"); }); - // Main Copy tab + // Copy tab $("#copy-tab").click(function () { - if (!$(this).is(".active")) { - $(this).addClass("active"); - } + resetTabsSlider(); + activateTab("#copy"); - if ($("#scan-tab").is(".active")) { - $("#scan-tab").removeClass("active"); - } - if (!$(".payment-tabs__slider").is("slide-right")) { - $(".payment-tabs__slider").addClass("slide-right"); - } - - if (!$("#copy").is(".active")) { - $("#copy").show(); - $("#copy").addClass("active"); - - $("#scan").hide(); - $("#scan").removeClass("active"); - } + $("#tabsSlider").addClass("slide-copy"); }); + // Altcoins tab + $("#altcoins-tab").click(function () { + resetTabsSlider(); + activateTab("#altcoins"); + + $("#tabsSlider").addClass("slide-altcoins"); + }); + + function resetTabsSlider() { + $("#tabsSlider").removeClass("slide-copy"); + $("#tabsSlider").removeClass("slide-altcoins"); + + $("#scan-tab").removeClass("active"); + $("#copy-tab").removeClass("active"); + $("#altcoins-tab").removeClass("active"); + + $("#copy").hide(); + $("#copy").removeClass("active"); + + $("#scan").hide(); + $("#scan").removeClass("active"); + + $("#altcoins").hide(); + $("#altcoins").removeClass("active"); + } + + function activateTab(senderName) { + $(senderName + "-tab").addClass("active"); + + $(senderName).show(); + $(senderName).addClass("active"); + } + // Payment received // Should connect using webhook ? // If notification received @@ -244,12 +244,6 @@ $(document).ready(function () { // function to load contents in different language should go there }); - // Validate Email address - function validateEmail(email) { - var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - return re.test(email); - } - // Expand Line-Items $(".buyerTotalLine").click(function () { $("line-items").toggleClass("expanded");