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:
Andrew Camilleri 2021-04-13 13:19:48 +02:00 committed by GitHub
parent 2e12befb8b
commit 64e34d0ef5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 249 additions and 353 deletions

View file

@ -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);
}
}
}
}

View file

@ -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" />

View file

@ -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)

View file

@ -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"
});
}

View file

@ -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
{

View file

@ -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; }

View file

@ -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

View file

@ -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);

View file

@ -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);

View 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));
}
}
}
}

View 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);
}
}
}

View 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();
}
}
}

View file

@ -1,4 +1,4 @@
namespace BTCPayServer.Payments.CoinSwitch
namespace BTCPayServer.Plugins.CoinSwitch
{
public class CoinSwitchSettings
{

View file

@ -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())

View file

@ -1,4 +1,5 @@
@using Microsoft.AspNetCore.Mvc.Rendering
@using BTCPayServer.Views.Stores
@model UpdateCoinSwitchSettingsViewModel
@{
Layout = "../Shared/_NavLayout.cshtml";

View file

@ -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>

View file

@ -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;
}

View file

@ -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>
}
}

View file

@ -0,0 +1 @@
<script src="~/checkout/js/coinswitchComponent.js"></script>

View file

@ -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>
}

View file

@ -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>

View file

@ -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 {

View file

@ -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>

View file

@ -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">

View file

@ -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;

View file

@ -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;

View file

@ -1,4 +1,4 @@
var CoinSwitchComponent =
Vue.component("coinswitch" ,
{
props: ["toCurrency", "toCurrencyDue", "toCurrencyAddress", "merchantId", "autoload", "mode"],
data: function () {
@ -63,4 +63,4 @@
this.openDialog();
}
}
};
});