mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 06:21:44 +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>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Views\Stores\Integrations" />
|
|
||||||
<Folder Include="wwwroot\vendor\clipboard.js\" />
|
<Folder Include="wwwroot\vendor\clipboard.js\" />
|
||||||
<Folder Include="wwwroot\vendor\highlightjs\" />
|
<Folder Include="wwwroot\vendor\highlightjs\" />
|
||||||
<Folder Include="wwwroot\vendor\summernote" />
|
<Folder Include="wwwroot\vendor\summernote" />
|
||||||
|
|
|
@ -19,8 +19,8 @@ using BTCPayServer.HostedServices;
|
||||||
using BTCPayServer.Models;
|
using BTCPayServer.Models;
|
||||||
using BTCPayServer.Models.InvoicingModels;
|
using BTCPayServer.Models.InvoicingModels;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Payments.CoinSwitch;
|
|
||||||
using BTCPayServer.Payments.Lightning;
|
using BTCPayServer.Payments.Lightning;
|
||||||
|
using BTCPayServer.Plugins.CoinSwitch;
|
||||||
using BTCPayServer.Rating;
|
using BTCPayServer.Rating;
|
||||||
using BTCPayServer.Security;
|
using BTCPayServer.Security;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
|
@ -525,12 +525,6 @@ namespace BTCPayServer.Controllers
|
||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
var accounting = paymentMethod.Calculate();
|
var accounting = paymentMethod.Calculate();
|
||||||
|
|
||||||
CoinSwitchSettings coinswitch = (storeBlob.CoinSwitchSettings != null && storeBlob.CoinSwitchSettings.Enabled &&
|
|
||||||
storeBlob.CoinSwitchSettings.IsConfigured())
|
|
||||||
? storeBlob.CoinSwitchSettings
|
|
||||||
: null;
|
|
||||||
|
|
||||||
|
|
||||||
var paymentMethodHandler = _paymentMethodHandlerDictionary[paymentMethodId];
|
var paymentMethodHandler = _paymentMethodHandlerDictionary[paymentMethodId];
|
||||||
|
|
||||||
var divisibility = _CurrencyNameTable.GetNumberFormatInfo(paymentMethod.GetId().CryptoCode, false)?.CurrencyDecimalDigits;
|
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
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
NetworkFee = paymentMethodDetails.GetNextNetworkFee(),
|
NetworkFee = paymentMethodDetails.GetNextNetworkFee(),
|
||||||
IsMultiCurrency = invoice.GetPayments().Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
|
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,
|
StoreId = store.Id,
|
||||||
AvailableCryptos = invoice.GetPaymentMethods()
|
AvailableCryptos = invoice.GetPaymentMethods()
|
||||||
.Where(i => i.Network != null)
|
.Where(i => i.Network != null)
|
||||||
|
|
|
@ -553,15 +553,6 @@ namespace BTCPayServer.Controllers
|
||||||
break;
|
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.Client.Models;
|
||||||
using BTCPayServer.JsonConverters;
|
using BTCPayServer.JsonConverters;
|
||||||
using BTCPayServer.Payments;
|
using BTCPayServer.Payments;
|
||||||
using BTCPayServer.Payments.CoinSwitch;
|
using BTCPayServer.Plugins.CoinSwitch;
|
||||||
using BTCPayServer.Rating;
|
using BTCPayServer.Rating;
|
||||||
using BTCPayServer.Services.Mails;
|
using BTCPayServer.Services.Mails;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
|
@ -90,8 +90,6 @@ namespace BTCPayServer.Data
|
||||||
|
|
||||||
public bool AnyoneCanInvoice { get; set; }
|
public bool AnyoneCanInvoice { get; set; }
|
||||||
|
|
||||||
public CoinSwitchSettings CoinSwitchSettings { get; set; }
|
|
||||||
|
|
||||||
string _LightningDescriptionTemplate;
|
string _LightningDescriptionTemplate;
|
||||||
public string LightningDescriptionTemplate
|
public string LightningDescriptionTemplate
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,12 +62,7 @@ namespace BTCPayServer.Models.InvoicingModels
|
||||||
public string CryptoImage { get; set; }
|
public string CryptoImage { get; set; }
|
||||||
public string StoreId { get; set; }
|
public string StoreId { get; set; }
|
||||||
public string PeerInfo { 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 string RootPath { get; set; }
|
||||||
public decimal CoinSwitchAmountMarkupPercentage { get; set; }
|
|
||||||
public bool RedirectAutomatically { get; set; }
|
public bool RedirectAutomatically { get; set; }
|
||||||
public bool Activated { get; set; }
|
public bool Activated { get; set; }
|
||||||
public string InvoiceCurrency { 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<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 …")]
|
[Display(Name = "Invoice expires if the full amount has not been paid after …")]
|
||||||
[Range(1, 60 * 24 * 24)]
|
[Range(1, 60 * 24 * 24)]
|
||||||
public int InvoiceExpiration
|
public int InvoiceExpiration
|
||||||
|
|
|
@ -104,7 +104,7 @@ namespace BTCPayServer.Payments.Lightning
|
||||||
}
|
}
|
||||||
private Task<List<ListenedInvoice>> GetListenedInvoices(string invoiceId)
|
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 listenedInvoices = new List<ListenedInvoice>();
|
||||||
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
|
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Abstractions.Constants;
|
||||||
|
using BTCPayServer.Client;
|
||||||
using BTCPayServer.Data;
|
using BTCPayServer.Data;
|
||||||
using BTCPayServer.Models.StoreViewModels;
|
using BTCPayServer.Models.StoreViewModels;
|
||||||
using BTCPayServer.Payments.CoinSwitch;
|
using BTCPayServer.Services.Stores;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
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]
|
private readonly StoreRepository _storeRepository;
|
||||||
[Route("{storeId}/coinswitch")]
|
|
||||||
|
public CoinSwitchController(StoreRepository storeRepository)
|
||||||
|
{
|
||||||
|
_storeRepository = storeRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("")]
|
||||||
public IActionResult UpdateCoinSwitchSettings(string storeId)
|
public IActionResult UpdateCoinSwitchSettings(string storeId)
|
||||||
{
|
{
|
||||||
var store = HttpContext.GetStoreData();
|
var store = HttpContext.GetStoreData();
|
||||||
|
@ -22,8 +34,7 @@ namespace BTCPayServer.Controllers
|
||||||
|
|
||||||
private void SetExistingValues(StoreData store, UpdateCoinSwitchSettingsViewModel vm)
|
private void SetExistingValues(StoreData store, UpdateCoinSwitchSettingsViewModel vm)
|
||||||
{
|
{
|
||||||
|
var existing = store.GetStoreBlob().GetCoinSwitchSettings();
|
||||||
var existing = store.GetStoreBlob().CoinSwitchSettings;
|
|
||||||
if (existing == null)
|
if (existing == null)
|
||||||
return;
|
return;
|
||||||
vm.MerchantId = existing.MerchantId;
|
vm.MerchantId = existing.MerchantId;
|
||||||
|
@ -32,8 +43,7 @@ namespace BTCPayServer.Controllers
|
||||||
vm.AmountMarkupPercentage = existing.AmountMarkupPercentage;
|
vm.AmountMarkupPercentage = existing.AmountMarkupPercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost("")]
|
||||||
[Route("{storeId}/coinswitch")]
|
|
||||||
public async Task<IActionResult> UpdateCoinSwitchSettings(string storeId, UpdateCoinSwitchSettingsViewModel vm,
|
public async Task<IActionResult> UpdateCoinSwitchSettings(string storeId, UpdateCoinSwitchSettingsViewModel vm,
|
||||||
string command)
|
string command)
|
||||||
{
|
{
|
||||||
|
@ -60,14 +70,11 @@ namespace BTCPayServer.Controllers
|
||||||
{
|
{
|
||||||
case "save":
|
case "save":
|
||||||
var storeBlob = store.GetStoreBlob();
|
var storeBlob = store.GetStoreBlob();
|
||||||
storeBlob.CoinSwitchSettings = coinSwitchSettings;
|
storeBlob.SetCoinSwitchSettings(coinSwitchSettings);
|
||||||
store.SetStoreBlob(storeBlob);
|
store.SetStoreBlob(storeBlob);
|
||||||
await _Repo.UpdateStore(store);
|
await _storeRepository.UpdateStore(store);
|
||||||
TempData[WellKnownTempData.SuccessMessage] = "CoinSwitch settings modified";
|
TempData[WellKnownTempData.SuccessMessage] = "CoinSwitch settings modified";
|
||||||
return RedirectToAction(nameof(UpdateStore), new
|
return RedirectToAction(nameof(UpdateCoinSwitchSettings), new {storeId});
|
||||||
{
|
|
||||||
storeId
|
|
||||||
});
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return View(vm);
|
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
|
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)
|
public async Task<bool> AddStoreUser(string storeId, string userId, string role)
|
||||||
{
|
{
|
||||||
using (var ctx = _ContextFactory.CreateContext())
|
using (var ctx = _ContextFactory.CreateContext())
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@using Microsoft.AspNetCore.Mvc.Rendering
|
@using Microsoft.AspNetCore.Mvc.Rendering
|
||||||
|
@using BTCPayServer.Views.Stores
|
||||||
@model UpdateCoinSwitchSettingsViewModel
|
@model UpdateCoinSwitchSettingsViewModel
|
||||||
@{
|
@{
|
||||||
Layout = "../Shared/_NavLayout.cshtml";
|
Layout = "../Shared/_NavLayout.cshtml";
|
|
@ -328,6 +328,8 @@
|
||||||
this.endDate = newEnd;
|
this.endDate = newEnd;
|
||||||
// updating ui
|
// updating ui
|
||||||
this.srvModel = jsonData;
|
this.srvModel = jsonData;
|
||||||
|
|
||||||
|
eventBus.$emit("data-fetched", this.srvModel);
|
||||||
if (this.invoicePaid && jsonData.redirectAutomatically && jsonData.merchantRefLink) {
|
if (this.invoicePaid && jsonData.redirectAutomatically && jsonData.merchantRefLink) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
|
@ -371,5 +373,6 @@
|
||||||
{
|
{
|
||||||
<partial name="@paymentMethodHandler.ExtensionPartial" model="@Model"/>
|
<partial name="@paymentMethodHandler.ExtensionPartial" model="@Model"/>
|
||||||
}
|
}
|
||||||
|
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-end"})
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -51,50 +51,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@if (Model.CoinSwitchEnabled)
|
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-bitcoin-post-content"})
|
||||||
{
|
|
||||||
<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>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
@if (Model.ShowRecommendedFee) {
|
@if (Model.ShowRecommendedFee) {
|
||||||
<div id="recommended-fee" class="recommended-fee" v-bind:class="{ hide: !srvModel.feeRate }">
|
<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'}" >
|
<div class="payment-tabs__tab" id="copy-tab" v-on:click="switchTab('copy')" v-bind:class="{ 'active': currentTab == 'copy'}" >
|
||||||
<span>{{$t("Copy")}}</span>
|
<span>{{$t("Copy")}}</span>
|
||||||
</div>
|
</div>
|
||||||
@if (Model.CoinSwitchEnabled)
|
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-bitcoin-post-tabs"})
|
||||||
{
|
|
||||||
<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>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -132,12 +79,10 @@
|
||||||
props: ["srvModel"],
|
props: ["srvModel"],
|
||||||
template: "#bitcoin-method-checkout-template",
|
template: "#bitcoin-method-checkout-template",
|
||||||
components: {
|
components: {
|
||||||
qrcode: VueQrcode,
|
qrcode: VueQrcode
|
||||||
coinswitch: CoinSwitchComponent
|
|
||||||
},
|
},
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
selectedThirdPartyProcessor: "",
|
|
||||||
currentTab: "scan"
|
currentTab: "scan"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -145,11 +90,6 @@
|
||||||
hasPayjoin: function(){
|
hasPayjoin: function(){
|
||||||
return this.srvModel.invoiceBitcoinUrl.indexOf('@PayjoinClient.BIP21EndpointKey=') === -1;
|
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() {
|
scanDisplayQr: function() {
|
||||||
return this.srvModel.invoiceBitcoinUrlQR;
|
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
|
@model BTCPayServer.Models.InvoicingModels.PaymentModel
|
||||||
@inject BTCPayNetworkProvider BTCPayNetworkProvider
|
@inject BTCPayNetworkProvider BTCPayNetworkProvider
|
||||||
@{
|
@{
|
||||||
|
@ -50,50 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@if (Model.CoinSwitchEnabled)
|
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-ethereum-post-content"})
|
||||||
{
|
|
||||||
<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>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -105,17 +61,7 @@
|
||||||
<div class="payment-tabs__tab" id="copy-tab" v-on:click="switchTab('copy')" v-bind:class="{ 'active': currentTab == 'copy'}" >
|
<div class="payment-tabs__tab" id="copy-tab" v-on:click="switchTab('copy')" v-bind:class="{ 'active': currentTab == 'copy'}" >
|
||||||
<span>{{$t("Copy")}}</span>
|
<span>{{$t("Copy")}}</span>
|
||||||
</div>
|
</div>
|
||||||
@if (Model.CoinSwitchEnabled)
|
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-ethereum-post-tabs"})
|
||||||
{
|
|
||||||
<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>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -125,8 +71,7 @@
|
||||||
props: ["srvModel"],
|
props: ["srvModel"],
|
||||||
template: "#ethereum-method-checkout-template",
|
template: "#ethereum-method-checkout-template",
|
||||||
components: {
|
components: {
|
||||||
qrcode: VueQrcode,
|
qrcode: VueQrcode
|
||||||
coinswitch: CoinSwitchComponent
|
|
||||||
},
|
},
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
@await Component.InvokeAsync("UiExtensionPoint" , new { location="checkout-lightning-post-content"})
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@
|
||||||
<div class="payment-tabs__tab" id="copy-tab" v-on:click="switchTab('copy')" v-bind:class="{ 'active': currentTab == 'copy'}" >
|
<div class="payment-tabs__tab" id="copy-tab" v-on:click="switchTab('copy')" v-bind:class="{ 'active': currentTab == 'copy'}" >
|
||||||
<span>{{$t("Copy")}}</span>
|
<span>{{$t("Copy")}}</span>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -266,42 +266,6 @@
|
||||||
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save Store Settings</button>
|
<button name="command" type="submit" class="btn btn-primary" value="Save" id="Save">Save Store Settings</button>
|
||||||
</form>
|
</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>
|
<h4 class="mt-5 mb-3">Services</h4>
|
||||||
<div class="table-responsive-md">
|
<div class="table-responsive-md">
|
||||||
<table class="table table-sm mt-1 mb-5">
|
<table class="table table-sm mt-1 mb-5">
|
||||||
|
|
|
@ -8558,34 +8558,10 @@ strong {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
transition: all .2s ease;
|
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 {
|
.manual__step-one__header {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
|
|
@ -8558,34 +8558,10 @@ strong {
|
||||||
.payment-tabs__tab.active {
|
.payment-tabs__tab.active {
|
||||||
color: #214497;
|
color: #214497;
|
||||||
font-weight: 400;
|
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 {
|
.manual__step-one__header {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var CoinSwitchComponent =
|
Vue.component("coinswitch" ,
|
||||||
{
|
{
|
||||||
props: ["toCurrency", "toCurrencyDue", "toCurrencyAddress", "merchantId", "autoload", "mode"],
|
props: ["toCurrency", "toCurrencyDue", "toCurrencyAddress", "merchantId", "autoload", "mode"],
|
||||||
data: function () {
|
data: function () {
|
||||||
|
@ -63,4 +63,4 @@
|
||||||
this.openDialog();
|
this.openDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue