mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Seaparate CoinSwitch as a plugin (#2390)
* Separate coinswitch as a system plugin * Decouple Coinswitch from Checkout UI * remove dummy csproj * Remove CoinSwitchTests.cs per @NicolasDorier feedback Co-authored-by: rockstardev <rockstardev@users.noreply.github.com>
This commit is contained in:
parent
2e12befb8b
commit
64e34d0ef5
28 changed files with 249 additions and 353 deletions
|
@ -1,91 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments.CoinSwitch;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
public class CoinSwitchTests
|
||||
{
|
||||
public CoinSwitchTests(ITestOutputHelper helper)
|
||||
{
|
||||
Logs.Tester = new XUnitLog(helper) { Name = "Tests" };
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanSetCoinSwitchPaymentMethod()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var controller = tester.PayTester.GetController<StoresController>(user.UserId, user.StoreId);
|
||||
|
||||
|
||||
var storeBlob = controller.CurrentStore.GetStoreBlob();
|
||||
Assert.Null(storeBlob.CoinSwitchSettings);
|
||||
|
||||
var updateModel = new UpdateCoinSwitchSettingsViewModel()
|
||||
{
|
||||
MerchantId = "aaa",
|
||||
};
|
||||
|
||||
Assert.Equal("UpdateStore", Assert.IsType<RedirectToActionResult>(
|
||||
await controller.UpdateCoinSwitchSettings(user.StoreId, updateModel, "save")).ActionName);
|
||||
|
||||
var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
storeBlob = controller.CurrentStore.GetStoreBlob();
|
||||
Assert.NotNull(storeBlob.CoinSwitchSettings);
|
||||
Assert.NotNull(storeBlob.CoinSwitchSettings);
|
||||
Assert.IsType<CoinSwitchSettings>(storeBlob.CoinSwitchSettings);
|
||||
Assert.Equal(storeBlob.CoinSwitchSettings.MerchantId,
|
||||
updateModel.MerchantId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanToggleCoinSwitchPaymentMethod()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var controller = tester.PayTester.GetController<StoresController>(user.UserId, user.StoreId);
|
||||
|
||||
var updateModel = new UpdateCoinSwitchSettingsViewModel()
|
||||
{
|
||||
MerchantId = "aaa",
|
||||
Enabled = true
|
||||
};
|
||||
Assert.Equal("UpdateStore", Assert.IsType<RedirectToActionResult>(
|
||||
await controller.UpdateCoinSwitchSettings(user.StoreId, updateModel, "save")).ActionName);
|
||||
|
||||
|
||||
var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
|
||||
Assert.True(store.GetStoreBlob().CoinSwitchSettings.Enabled);
|
||||
|
||||
updateModel.Enabled = false;
|
||||
|
||||
Assert.Equal("UpdateStore", Assert.IsType<RedirectToActionResult>(
|
||||
await controller.UpdateCoinSwitchSettings(user.StoreId, updateModel, "save")).ActionName);
|
||||
|
||||
store = await tester.PayTester.StoreRepository.FindStore(user.StoreId);
|
||||
|
||||
Assert.False(store.GetStoreBlob().CoinSwitchSettings.Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -134,7 +134,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Views\Stores\Integrations" />
|
||||
<Folder Include="wwwroot\vendor\clipboard.js\" />
|
||||
<Folder Include="wwwroot\vendor\highlightjs\" />
|
||||
<Folder Include="wwwroot\vendor\summernote" />
|
||||
|
|
|
@ -19,8 +19,8 @@ using BTCPayServer.HostedServices;
|
|||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Models.InvoicingModels;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.CoinSwitch;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Plugins.CoinSwitch;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
|
@ -525,12 +525,6 @@ namespace BTCPayServer.Controllers
|
|||
var storeBlob = store.GetStoreBlob();
|
||||
var accounting = paymentMethod.Calculate();
|
||||
|
||||
CoinSwitchSettings coinswitch = (storeBlob.CoinSwitchSettings != null && storeBlob.CoinSwitchSettings.Enabled &&
|
||||
storeBlob.CoinSwitchSettings.IsConfigured())
|
||||
? storeBlob.CoinSwitchSettings
|
||||
: null;
|
||||
|
||||
|
||||
var paymentMethodHandler = _paymentMethodHandlerDictionary[paymentMethodId];
|
||||
|
||||
var divisibility = _CurrencyNameTable.GetNumberFormatInfo(paymentMethod.GetId().CryptoCode, false)?.CurrencyDecimalDigits;
|
||||
|
@ -568,10 +562,6 @@ namespace BTCPayServer.Controllers
|
|||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
NetworkFee = paymentMethodDetails.GetNextNetworkFee(),
|
||||
IsMultiCurrency = invoice.GetPayments().Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
|
||||
CoinSwitchEnabled = coinswitch != null,
|
||||
CoinSwitchAmountMarkupPercentage = coinswitch?.AmountMarkupPercentage ?? 0,
|
||||
CoinSwitchMerchantId = coinswitch?.MerchantId,
|
||||
CoinSwitchMode = coinswitch?.Mode,
|
||||
StoreId = store.Id,
|
||||
AvailableCryptos = invoice.GetPaymentMethods()
|
||||
.Where(i => i.Network != null)
|
||||
|
|
|
@ -553,15 +553,6 @@ namespace BTCPayServer.Controllers
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var coinSwitchEnabled = storeBlob.CoinSwitchSettings != null && storeBlob.CoinSwitchSettings.Enabled;
|
||||
vm.ThirdPartyPaymentMethods.Add(new StoreViewModel.AdditionalPaymentMethod()
|
||||
{
|
||||
Enabled = coinSwitchEnabled,
|
||||
Action = nameof(UpdateCoinSwitchSettings),
|
||||
Provider = "CoinSwitch"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ using BTCPayServer.Client.JsonConverters;
|
|||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.CoinSwitch;
|
||||
using BTCPayServer.Plugins.CoinSwitch;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services.Mails;
|
||||
using BTCPayServer.Services.Rates;
|
||||
|
@ -90,8 +90,6 @@ namespace BTCPayServer.Data
|
|||
|
||||
public bool AnyoneCanInvoice { get; set; }
|
||||
|
||||
public CoinSwitchSettings CoinSwitchSettings { get; set; }
|
||||
|
||||
string _LightningDescriptionTemplate;
|
||||
public string LightningDescriptionTemplate
|
||||
{
|
||||
|
|
|
@ -62,12 +62,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
|||
public string CryptoImage { get; set; }
|
||||
public string StoreId { get; set; }
|
||||
public string PeerInfo { get; set; }
|
||||
|
||||
public bool CoinSwitchEnabled { get; set; }
|
||||
public string CoinSwitchMode { get; set; }
|
||||
public string CoinSwitchMerchantId { get; set; }
|
||||
public string RootPath { get; set; }
|
||||
public decimal CoinSwitchAmountMarkupPercentage { get; set; }
|
||||
public bool RedirectAutomatically { get; set; }
|
||||
public bool Activated { get; set; }
|
||||
public string InvoiceCurrency { get; set; }
|
||||
|
|
|
@ -55,9 +55,6 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||
|
||||
public List<StoreViewModel.DerivationScheme> DerivationSchemes { get; set; } = new List<StoreViewModel.DerivationScheme>();
|
||||
|
||||
public List<AdditionalPaymentMethod> ThirdPartyPaymentMethods { get; set; } =
|
||||
new List<AdditionalPaymentMethod>();
|
||||
|
||||
[Display(Name = "Invoice expires if the full amount has not been paid after …")]
|
||||
[Range(1, 60 * 24 * 24)]
|
||||
public int InvoiceExpiration
|
||||
|
|
|
@ -104,7 +104,7 @@ namespace BTCPayServer.Payments.Lightning
|
|||
}
|
||||
private Task<List<ListenedInvoice>> GetListenedInvoices(string invoiceId)
|
||||
{
|
||||
return _memoryCache.GetOrCreateAsync(invoiceId, async (cacheEntry) =>
|
||||
return _memoryCache.GetOrCreateAsync($"{nameof(GetListenedInvoices)}-{invoiceId}", async (cacheEntry) =>
|
||||
{
|
||||
var listenedInvoices = new List<ListenedInvoice>();
|
||||
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.StoreViewModels;
|
||||
using BTCPayServer.Payments.CoinSwitch;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
namespace BTCPayServer.Plugins.CoinSwitch
|
||||
{
|
||||
public partial class StoresController
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Route("plugins/{storeId}/coinswitch")]
|
||||
public class CoinSwitchController : Controller
|
||||
{
|
||||
[HttpGet]
|
||||
[Route("{storeId}/coinswitch")]
|
||||
private readonly StoreRepository _storeRepository;
|
||||
|
||||
public CoinSwitchController(StoreRepository storeRepository)
|
||||
{
|
||||
_storeRepository = storeRepository;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public IActionResult UpdateCoinSwitchSettings(string storeId)
|
||||
{
|
||||
var store = HttpContext.GetStoreData();
|
||||
|
@ -22,8 +34,7 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
private void SetExistingValues(StoreData store, UpdateCoinSwitchSettingsViewModel vm)
|
||||
{
|
||||
|
||||
var existing = store.GetStoreBlob().CoinSwitchSettings;
|
||||
var existing = store.GetStoreBlob().GetCoinSwitchSettings();
|
||||
if (existing == null)
|
||||
return;
|
||||
vm.MerchantId = existing.MerchantId;
|
||||
|
@ -32,8 +43,7 @@ namespace BTCPayServer.Controllers
|
|||
vm.AmountMarkupPercentage = existing.AmountMarkupPercentage;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{storeId}/coinswitch")]
|
||||
[HttpPost("")]
|
||||
public async Task<IActionResult> UpdateCoinSwitchSettings(string storeId, UpdateCoinSwitchSettingsViewModel vm,
|
||||
string command)
|
||||
{
|
||||
|
@ -60,14 +70,11 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
case "save":
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
storeBlob.CoinSwitchSettings = coinSwitchSettings;
|
||||
storeBlob.SetCoinSwitchSettings(coinSwitchSettings);
|
||||
store.SetStoreBlob(storeBlob);
|
||||
await _Repo.UpdateStore(store);
|
||||
await _storeRepository.UpdateStore(store);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "CoinSwitch settings modified";
|
||||
return RedirectToAction(nameof(UpdateStore), new
|
||||
{
|
||||
storeId
|
||||
});
|
||||
return RedirectToAction(nameof(UpdateCoinSwitchSettings), new {storeId});
|
||||
|
||||
default:
|
||||
return View(vm);
|
32
BTCPayServer/Plugins/CoinSwitch/CoinSwitchExtensions.cs
Normal file
32
BTCPayServer/Plugins/CoinSwitch/CoinSwitchExtensions.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using BTCPayServer.Data;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Plugins.CoinSwitch
|
||||
{
|
||||
public static class CoinSwitchExtensions
|
||||
{
|
||||
public const string StoreBlobKey = "coinSwitchSettings";
|
||||
public static CoinSwitchSettings GetCoinSwitchSettings(this StoreBlob storeBlob)
|
||||
{
|
||||
if (storeBlob.AdditionalData.TryGetValue(StoreBlobKey, out var rawS) && rawS is JObject rawObj)
|
||||
{
|
||||
return new Serializer(null).ToObject<CoinSwitchSettings>(rawObj);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static void SetCoinSwitchSettings(this StoreBlob storeBlob, CoinSwitchSettings settings)
|
||||
{
|
||||
if (settings is null)
|
||||
{
|
||||
storeBlob.AdditionalData.Remove(StoreBlobKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
storeBlob.AdditionalData.AddOrReplace(StoreBlobKey, JObject.FromObject(settings));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
34
BTCPayServer/Plugins/CoinSwitch/CoinSwitchPlugin.cs
Normal file
34
BTCPayServer/Plugins/CoinSwitch/CoinSwitchPlugin.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Abstractions.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BTCPayServer.Plugins.CoinSwitch
|
||||
{
|
||||
public class CoinSwitchPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
public override string Identifier => "BTCPayServer.Plugins.CoinSwitch";
|
||||
public override string Name => "CoinSwitch";
|
||||
|
||||
public override string Description =>
|
||||
"Allows you to embed a coinswitch conversion screen to allow customers to pay with altcoins.";
|
||||
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
applicationBuilder.AddSingleton<CoinSwitchService>();
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("CoinSwitch/StoreIntegrationCoinSwitchOption",
|
||||
"store-integrations-list"));
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("CoinSwitch/CheckoutContentExtension",
|
||||
"checkout-bitcoin-post-content"));
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("CoinSwitch/CheckoutContentExtension",
|
||||
"checkout-ethereum-post-content"));
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("CoinSwitch/CheckoutTabExtension",
|
||||
"checkout-bitcoin-post-tabs"));
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("CoinSwitch/CheckoutTabExtension",
|
||||
"checkout-ethereum-post-tabs"));
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("CoinSwitch/CheckoutEnd",
|
||||
"checkout-end"));
|
||||
base.Execute(applicationBuilder);
|
||||
}
|
||||
}
|
||||
}
|
36
BTCPayServer/Plugins/CoinSwitch/CoinSwitchService.cs
Normal file
36
BTCPayServer/Plugins/CoinSwitch/CoinSwitchService.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace BTCPayServer.Plugins.CoinSwitch
|
||||
{
|
||||
public class CoinSwitchService: IDisposable
|
||||
{
|
||||
private readonly StoreRepository _storeRepository;
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
|
||||
public CoinSwitchService(StoreRepository storeRepository, IMemoryCache memoryCache)
|
||||
{
|
||||
_storeRepository = storeRepository;
|
||||
_memoryCache = memoryCache;
|
||||
}
|
||||
|
||||
public async Task<CoinSwitchSettings> GetCoinSwitchForInvoice(string id)
|
||||
{
|
||||
return await _memoryCache.GetOrCreateAsync($"{nameof(CoinSwitchService)}-{id}", async entry =>
|
||||
{
|
||||
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
|
||||
var d = await _storeRepository.GetStoreByInvoiceId(id);
|
||||
|
||||
return d?.GetStoreBlob()?.GetCoinSwitchSettings();
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_memoryCache?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace BTCPayServer.Payments.CoinSwitch
|
||||
namespace BTCPayServer.Plugins.CoinSwitch
|
||||
{
|
||||
public class CoinSwitchSettings
|
||||
{
|
|
@ -95,6 +95,14 @@ namespace BTCPayServer.Services.Stores
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<StoreData> GetStoreByInvoiceId(string invoiceId)
|
||||
{
|
||||
await using var context = _ContextFactory.CreateContext();
|
||||
var matched = await context.Invoices.Include(data => data.StoreData)
|
||||
.SingleOrDefaultAsync(data => data.Id == invoiceId);
|
||||
return matched?.StoreData;
|
||||
}
|
||||
|
||||
public async Task<bool> AddStoreUser(string storeId, string userId, string role)
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@using Microsoft.AspNetCore.Mvc.Rendering
|
||||
@using BTCPayServer.Views.Stores
|
||||
@model UpdateCoinSwitchSettingsViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
|
@ -328,6 +328,8 @@
|
|||
this.endDate = newEnd;
|
||||
// updating ui
|
||||
this.srvModel = jsonData;
|
||||
|
||||
eventBus.$emit("data-fetched", this.srvModel);
|
||||
if (this.invoicePaid && jsonData.redirectAutomatically && jsonData.merchantRefLink) {
|
||||
this.loading = true;
|
||||
setTimeout(function () {
|
||||
|
@ -371,5 +373,6 @@
|
|||
{
|
||||
<partial name="@paymentMethodHandler.ExtensionPartial" model="@Model"/>
|
||||
}
|
||||
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-end"})
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -51,50 +51,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@if (Model.CoinSwitchEnabled)
|
||||
{
|
||||
<div id="altcoins" class="bp-view payment manual-flow" v-bind:class="{ 'active': currentTab == 'altcoins'}">
|
||||
<nav>
|
||||
<div class="manual__step-two__instructions">
|
||||
<span>
|
||||
{{$t("ConversionTab_BodyTop", srvModel)}}
|
||||
<br/><br/>
|
||||
{{$t("ConversionTab_BodyDesc", srvModel)}}
|
||||
</span>
|
||||
</div>
|
||||
<center>
|
||||
@if (Model.CoinSwitchEnabled)
|
||||
{
|
||||
<template v-if="!selectedThirdPartyProcessor">
|
||||
<button v-on:click="selectedThirdPartyProcessor = 'coinswitch'" class="action-button">{{$t("Pay with CoinSwitch")}}</button>
|
||||
</template>
|
||||
}
|
||||
@if (Model.CoinSwitchEnabled)
|
||||
{
|
||||
<coinswitch inline-template
|
||||
v-if="selectedThirdPartyProcessor === 'coinswitch'"
|
||||
:mode="srvModel.coinSwitchMode"
|
||||
:merchant-id="srvModel.coinSwitchMerchantId"
|
||||
:to-currency="srvModel.paymentMethodId"
|
||||
:to-currency-due="coinswitchAmountDue"
|
||||
:autoload="selectedThirdPartyProcessor === 'coinswitch'"
|
||||
:to-currency-address="srvModel.btcAddress">
|
||||
<div>
|
||||
<a v-on:click="openDialog($event)" :href="url" class="action-button" v-show="url && !opened">{{$t("Pay with CoinSwitch")}}</a>
|
||||
<iframe
|
||||
v-if="showInlineIFrame"
|
||||
v-on:load="onLoadIframe"
|
||||
style="height: 100%; position: fixed; top: 0; width: 100%; left: 0;"
|
||||
sandbox="allow-scripts allow-forms allow-popups allow-same-origin"
|
||||
:src="url">
|
||||
</iframe>
|
||||
</div>
|
||||
</coinswitch>
|
||||
}
|
||||
</center>
|
||||
</nav>
|
||||
</div>
|
||||
}
|
||||
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-bitcoin-post-content"})
|
||||
</div>
|
||||
@if (Model.ShowRecommendedFee) {
|
||||
<div id="recommended-fee" class="recommended-fee" v-bind:class="{ hide: !srvModel.feeRate }">
|
||||
|
@ -112,17 +69,7 @@
|
|||
<div class="payment-tabs__tab" id="copy-tab" v-on:click="switchTab('copy')" v-bind:class="{ 'active': currentTab == 'copy'}" >
|
||||
<span>{{$t("Copy")}}</span>
|
||||
</div>
|
||||
@if (Model.CoinSwitchEnabled)
|
||||
{
|
||||
<div class="payment-tabs__tab" id="altcoins-tab" v-on:click="switchTab('altcoins')" v-bind:class="{ 'active': currentTab == 'altcoins'}" >
|
||||
<span>{{$t("Conversion")}}</span>
|
||||
</div>
|
||||
<div id="tabsSlider" class="payment-tabs__slider three-tabs" v-bind:class="['slide-'+currentTab]"></div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div id="tabsSlider" class="payment-tabs__slider" v-bind:class="['slide-'+currentTab]"></div>
|
||||
}
|
||||
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-bitcoin-post-tabs"})
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
@ -132,12 +79,10 @@
|
|||
props: ["srvModel"],
|
||||
template: "#bitcoin-method-checkout-template",
|
||||
components: {
|
||||
qrcode: VueQrcode,
|
||||
coinswitch: CoinSwitchComponent
|
||||
qrcode: VueQrcode
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
selectedThirdPartyProcessor: "",
|
||||
currentTab: "scan"
|
||||
}
|
||||
},
|
||||
|
@ -145,11 +90,6 @@
|
|||
hasPayjoin: function(){
|
||||
return this.srvModel.invoiceBitcoinUrl.indexOf('@PayjoinClient.BIP21EndpointKey=') === -1;
|
||||
},
|
||||
coinswitchAmountDue: function() {
|
||||
return this.srvModel.coinSwitchAmountMarkupPercentage
|
||||
? this.srvModel.btcDue * (1 + (this.srvModel.coinSwitchAmountMarkupPercentage / 100))
|
||||
: this.srvModel.btcDue;
|
||||
},
|
||||
scanDisplayQr: function() {
|
||||
return this.srvModel.invoiceBitcoinUrlQR;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
@using BTCPayServer.Plugins.CoinSwitch
|
||||
@inject CoinSwitchService CoinSwitchService
|
||||
@{
|
||||
var invoiceId = this.Context.GetRouteValue("invoiceId")?.ToString();
|
||||
var settings = await CoinSwitchService.GetCoinSwitchForInvoice(invoiceId);
|
||||
if (settings?.IsConfigured() is true && settings.Enabled)
|
||||
{
|
||||
<div id="altcoins" class="bp-view payment manual-flow" v-bind:class="{ 'active': currentTab == 'altcoins'}">
|
||||
<nav>
|
||||
<div class="manual__step-two__instructions">
|
||||
<span>
|
||||
{{$t("ConversionTab_BodyTop", srvModel)}}
|
||||
<br/><br/>
|
||||
{{$t("ConversionTab_BodyDesc", srvModel)}}
|
||||
</span>
|
||||
</div>
|
||||
<center>
|
||||
<coinswitch inline-template
|
||||
:merchant-id="srvModel.coinSwitchMerchantId"
|
||||
:to-currency="srvModel.paymentMethodId"
|
||||
:to-currency-due="srvModel.btcDue * (1 + (@settings.AmountMarkupPercentage / 100)) "
|
||||
mode = '@settings.Mode'
|
||||
merchant-id="@settings.MerchantId"
|
||||
:autoload="true"
|
||||
:to-currency-address="srvModel.btcAddress">
|
||||
<div>
|
||||
<a v-on:click="openDialog($event)" :href="url" class="action-button" v-show="url && !opened">{{$t("Pay with CoinSwitch")}}</a>
|
||||
<iframe
|
||||
v-if="showInlineIFrame"
|
||||
v-on:load="onLoadIframe"
|
||||
style="height: 100%; position: fixed; top: 0; width: 100%; left: 0;"
|
||||
sandbox="allow-scripts allow-forms allow-popups allow-same-origin"
|
||||
:src="url">
|
||||
</iframe>
|
||||
</div>
|
||||
</coinswitch>
|
||||
</center>
|
||||
</nav>
|
||||
</div>
|
||||
}
|
||||
}
|
1
BTCPayServer/Views/Shared/CoinSwitch/CheckoutEnd.cshtml
Normal file
1
BTCPayServer/Views/Shared/CoinSwitch/CheckoutEnd.cshtml
Normal file
|
@ -0,0 +1 @@
|
|||
<script src="~/checkout/js/coinswitchComponent.js"></script>
|
|
@ -0,0 +1,12 @@
|
|||
@using BTCPayServer.Plugins.CoinSwitch
|
||||
@inject CoinSwitchService CoinSwitchService
|
||||
@{
|
||||
var invoiceId = this.Context.GetRouteValue("invoiceId")?.ToString();
|
||||
var settings = await CoinSwitchService.GetCoinSwitchForInvoice(invoiceId);
|
||||
}
|
||||
@if (settings?.IsConfigured() is true)
|
||||
{
|
||||
<div class="payment-tabs__tab" id="altcoins-tab" v-on:click="switchTab('altcoins')" v-bind:class="{ 'active': currentTab == 'altcoins'}">
|
||||
<span>{{$t("Conversion")}}</span>
|
||||
</div>
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
@using BTCPayServer.Plugins.CoinSwitch
|
||||
@{
|
||||
var settings = Context.GetStoreData().GetStoreBlob().GetCoinSwitchSettings();
|
||||
}
|
||||
<li class="list-group-item bg-tile ">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="d-flex flex-wrap flex-fill flex-column flex-sm-row">
|
||||
<strong class="mr-3">
|
||||
CoinSwitch
|
||||
</strong>
|
||||
<span title="" class="d-flex mr-3" >
|
||||
<span class="text-nowrap text-secondary">Allows your customers to pay with altcoins that are not supported by BTC Pay Server.</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="d-flex align-items-center fw-semibold">
|
||||
@if (settings?.IsConfigured() is true)
|
||||
{
|
||||
<span class="d-flex align-items-center text-success">
|
||||
<span class="mr-2 btcpay-status btcpay-status--enabled"></span>
|
||||
Enabled
|
||||
</span>
|
||||
|
||||
<span class="text-light ml-3 mr-2">|</span>
|
||||
<a lass="btn btn-link px-1 py-1 fw-semibold" asp-controller="CoinSwitch" asp-action="UpdateCoinSwitchSettings" asp-route-storeId="@Context.GetRouteValue("storeId")">
|
||||
Modify
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="d-flex align-items-center text-danger">
|
||||
<span class="mr-2 btcpay-status btcpay-status--disabled"></span>
|
||||
Disabled
|
||||
</span>
|
||||
<a class="btn btn-primary btn-sm ml-4 px-3 py-1 fw-semibold" asp-controller="CoinSwitch" asp-action="UpdateCoinSwitchSettings" asp-route-storeId="@Context.GetRouteValue("storeId")">
|
||||
Setup
|
||||
</a>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
|
@ -1,4 +1,3 @@
|
|||
@using BTCPayServer.Services
|
||||
@model BTCPayServer.Models.InvoicingModels.PaymentModel
|
||||
@inject BTCPayNetworkProvider BTCPayNetworkProvider
|
||||
@{
|
||||
|
@ -50,50 +49,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@if (Model.CoinSwitchEnabled)
|
||||
{
|
||||
<div id="altcoins" class="bp-view payment manual-flow" v-bind:class="{ 'active': currentTab == 'altcoins'}">
|
||||
<nav >
|
||||
<div class="manual__step-two__instructions">
|
||||
<span>
|
||||
{{$t("ConversionTab_BodyTop", srvModel)}}
|
||||
<br/><br/>
|
||||
{{$t("ConversionTab_BodyDesc", srvModel)}}
|
||||
</span>
|
||||
</div>
|
||||
<center>
|
||||
@if (Model.CoinSwitchEnabled)
|
||||
{
|
||||
<template v-if="!selectedThirdPartyProcessor">
|
||||
<button v-on:click="selectedThirdPartyProcessor = 'coinswitch'" class="action-button">{{$t("Pay with CoinSwitch")}}</button>
|
||||
</template>
|
||||
}
|
||||
@if (Model.CoinSwitchEnabled)
|
||||
{
|
||||
<coinswitch inline-template
|
||||
v-if="selectedThirdPartyProcessor === 'coinswitch'"
|
||||
:mode="srvModel.coinSwitchMode"
|
||||
:merchant-id="srvModel.coinSwitchMerchantId"
|
||||
:to-currency="srvModel.paymentMethodId"
|
||||
:to-currency-due="coinswitchAmountDue"
|
||||
:autoload="selectedThirdPartyProcessor === 'coinswitch'"
|
||||
:to-currency-address="srvModel.btcAddress">
|
||||
<div>
|
||||
<a v-on:click="openDialog($event)" :href="url" class="action-button" v-show="url && !opened">{{$t("Pay with CoinSwitch")}}</a>
|
||||
<iframe
|
||||
v-if="showInlineIFrame"
|
||||
v-on:load="onLoadIframe"
|
||||
style="height: 100%; position: fixed; top: 0; width: 100%; left: 0;"
|
||||
sandbox="allow-scripts allow-forms allow-popups allow-same-origin"
|
||||
:src="url">
|
||||
</iframe>
|
||||
</div>
|
||||
</coinswitch>
|
||||
}
|
||||
</center>
|
||||
</nav>
|
||||
</div>
|
||||
}
|
||||
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-ethereum-post-content"})
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
@ -104,18 +60,8 @@
|
|||
</div>
|
||||
<div class="payment-tabs__tab" id="copy-tab" v-on:click="switchTab('copy')" v-bind:class="{ 'active': currentTab == 'copy'}" >
|
||||
<span>{{$t("Copy")}}</span>
|
||||
</div>
|
||||
@if (Model.CoinSwitchEnabled)
|
||||
{
|
||||
<div class="payment-tabs__tab" id="altcoins-tab" v-on:click="switchTab('altcoins')" v-bind:class="{ 'active': currentTab == 'altcoins'}" >
|
||||
<span>{{$t("Conversion")}}</span>
|
||||
</div>
|
||||
<div id="tabsSlider" class="payment-tabs__slider three-tabs" v-bind:class="['slide-'+currentTab]"></div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div id="tabsSlider" class="payment-tabs__slider" v-bind:class="['slide-'+currentTab]"></div>
|
||||
}
|
||||
</div>
|
||||
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-ethereum-post-tabs"})
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
@ -125,8 +71,7 @@
|
|||
props: ["srvModel"],
|
||||
template: "#ethereum-method-checkout-template",
|
||||
components: {
|
||||
qrcode: VueQrcode,
|
||||
coinswitch: CoinSwitchComponent
|
||||
qrcode: VueQrcode
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-lightning-post-content"})
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
@ -69,7 +70,7 @@
|
|||
<div class="payment-tabs__tab" id="copy-tab" v-on:click="switchTab('copy')" v-bind:class="{ 'active': currentTab == 'copy'}" >
|
||||
<span>{{$t("Copy")}}</span>
|
||||
</div>
|
||||
<div id="tabsSlider" class="payment-tabs__slider" v-bind:class="['slide-'+currentTab]"></div>
|
||||
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-lightning-post-tabs"})
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
|
|
@ -266,42 +266,6 @@
|
|||
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save Store Settings</button>
|
||||
</form>
|
||||
|
||||
<h4 class="mt-5 mb-3">Additional payment methods</h4>
|
||||
<div class="table-responsive-md">
|
||||
<table class="table table-sm mt-1 mb-5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Provider</th>
|
||||
<th class="text-center w-100px">Enabled</th>
|
||||
<th class="text-right w-100px">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var scheme in Model.ThirdPartyPaymentMethods)
|
||||
{
|
||||
<tr>
|
||||
<td>@scheme.Provider</td>
|
||||
<td class="text-center">
|
||||
@if (scheme.Enabled)
|
||||
{
|
||||
<span class="text-success fa fa-check"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-danger fa fa-times"></span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a asp-action="@scheme.Action" id='Modify-@scheme.Provider' asp-route-storeId="@Context.GetRouteValue("storeId")">
|
||||
@(scheme.Enabled ? "Modify" : "Setup")
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-3">Services</h4>
|
||||
<div class="table-responsive-md">
|
||||
<table class="table table-sm mt-1 mb-5">
|
||||
|
|
|
@ -8558,34 +8558,10 @@ strong {
|
|||
background: #f5f5f5;
|
||||
font-weight: 400;
|
||||
transition: all .2s ease;
|
||||
border-bottom: #51b13e 3px solid
|
||||
}
|
||||
|
||||
.payment-tabs__slider {
|
||||
position: absolute;
|
||||
height: 3px;
|
||||
width: 50%;
|
||||
background: #51b13e;
|
||||
bottom: 0;
|
||||
right: 50%;
|
||||
transition: all .2s ease;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
|
|
@ -8558,34 +8558,10 @@ strong {
|
|||
.payment-tabs__tab.active {
|
||||
color: #214497;
|
||||
font-weight: 400;
|
||||
|
||||
border-bottom: #329F80 5px solid;
|
||||
}
|
||||
|
||||
.payment-tabs__slider {
|
||||
position: absolute;
|
||||
height: 5px;
|
||||
width: 50%;
|
||||
background: #329F80;
|
||||
top: 0;
|
||||
right: 50%;
|
||||
transition: all .2s ease;
|
||||
}
|
||||
|
||||
.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;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var CoinSwitchComponent =
|
||||
Vue.component("coinswitch" ,
|
||||
{
|
||||
props: ["toCurrency", "toCurrencyDue", "toCurrencyAddress", "merchantId", "autoload", "mode"],
|
||||
data: function () {
|
||||
|
@ -63,4 +63,4 @@
|
|||
this.openDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue