diff --git a/Plugins/BTCPayServer.Plugins.SideShift/BTCPayServer.Plugins.SideShift.csproj b/Plugins/BTCPayServer.Plugins.SideShift/BTCPayServer.Plugins.SideShift.csproj index 6be3099..9437f9d 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/BTCPayServer.Plugins.SideShift.csproj +++ b/Plugins/BTCPayServer.Plugins.SideShift/BTCPayServer.Plugins.SideShift.csproj @@ -9,7 +9,7 @@ SideShift Allows you to embed a SideShift conversion screen to allow customers to pay with altcoins. - 1.1.7 + 1.1.8 diff --git a/Plugins/BTCPayServer.Plugins.SideShift/README.md b/Plugins/BTCPayServer.Plugins.SideShift/README.md new file mode 100644 index 0000000..adf2c39 --- /dev/null +++ b/Plugins/BTCPayServer.Plugins.SideShift/README.md @@ -0,0 +1,27 @@ +# Sideshift for BTCPay Server plugin + +This plugin integrates the no-kyc Sideshift exchanges into various parts of BTCPay Server. + +* Invoice checkout - Let your customers pay with any coin supported by Sideshift. The settings allow you to show a payment method that loads sideshift with its various options as dropwdown, or you can explicitly show each option as a payment method on its own, or you can have both. +* Pull payments - Let your customers claim their payouts in any option supported by Sideshift. +* Prism Plugin - Allows you to use Sideshift as a destination in the Prism plugin, so that you can automatically convert incoming Bitcoin to any option Sideshift supports. + +## Usage + +For both invoices and pull payments, you will need to enable sideshift through its settings located in the plugins navigation under your store. The prism plugin integration does not require this to be on. + +## Configuring on individual invoices + +You can configure the sideshift options on individual invoices when creating them through the API by setting a json object under the Metadata property of the invoice. This will merge on top of your existing sideshift settings in your store. The json object should be in the following format, and any property not included, will use the ones on your store: + +```json +{ + "sideshift": { + "enabled": true, //whether it should be enabled/disabled for this invoice + "explicitMethods": [ "USDT_liquid"], //if you want to explicitly show certain options, you can list them here. The format is currencyCode_network. You can look at the html in the sideshift settings page to see the full list of values. + "onlyShowExplicitMethods" : true, // if you want to only show the explicit methods, and not the dropdown variant of the plugin + "preferredTargetPaymentMethodId": "BTC_LightningLike", //if you want to set a preferred payment method that you would receive the funds from sideshift on. This is the payment method format as used in the BTCPay Server Greenfield API. + "amountMarkupPercentage": 0.5 //if you want to add a markup in case you dont think that sideshift is reliable in convertint into the exact amount. + } +} +``` \ No newline at end of file diff --git a/Plugins/BTCPayServer.Plugins.SideShift/SideShiftPlugin.cs b/Plugins/BTCPayServer.Plugins.SideShift/SideShiftPlugin.cs index 94d68da..1fcbd93 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/SideShiftPlugin.cs +++ b/Plugins/BTCPayServer.Plugins.SideShift/SideShiftPlugin.cs @@ -15,6 +15,7 @@ namespace BTCPayServer.Plugins.SideShift public override void Execute(IServiceCollection applicationBuilder) { applicationBuilder.AddSingleton(); + applicationBuilder.AddHostedService(provider => provider.GetService()); applicationBuilder.AddSingleton(new UIExtension("SideShift/SideShiftNav", "store-integrations-nav")); applicationBuilder.AddSingleton(new UIExtension("SideShift/PullPaymentViewInsert", diff --git a/Plugins/BTCPayServer.Plugins.SideShift/SideShiftService.cs b/Plugins/BTCPayServer.Plugins.SideShift/SideShiftService.cs index 0744722..9b52045 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/SideShiftService.cs +++ b/Plugins/BTCPayServer.Plugins.SideShift/SideShiftService.cs @@ -2,32 +2,110 @@ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using BTCPayServer.Abstractions.Contracts; using BTCPayServer.Client; +using BTCPayServer.Events; +using BTCPayServer.HostedServices; +using BTCPayServer.Services.Invoices; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace BTCPayServer.Plugins.SideShift { - public class SideShiftService + public class SideShiftService:EventHostedServiceBase { + private readonly InvoiceRepository _invoiceRepository; private readonly ISettingsRepository _settingsRepository; private readonly IMemoryCache _memoryCache; private readonly IStoreRepository _storeRepository; private readonly IHttpClientFactory _httpClientFactory; + private readonly JsonSerializerSettings _serializerSettings; - public SideShiftService(ISettingsRepository settingsRepository, IMemoryCache memoryCache, IStoreRepository storeRepository, IHttpClientFactory httpClientFactory) + public SideShiftService( + InvoiceRepository invoiceRepository, + ISettingsRepository settingsRepository, + IMemoryCache memoryCache, + IStoreRepository storeRepository, + IHttpClientFactory httpClientFactory, + ILogger logger, + EventAggregator eventAggregator, + JsonSerializerSettings serializerSettings) : base(eventAggregator, logger) { + _invoiceRepository = invoiceRepository; _settingsRepository = settingsRepository; _memoryCache = memoryCache; _storeRepository = storeRepository; _httpClientFactory = httpClientFactory; + _serializerSettings = serializerSettings; } + + + protected override void SubscribeToEvents() + { + Subscribe(); + base.SubscribeToEvents(); + } + + + protected override Task ProcessEvent(object evt, CancellationToken cancellationToken) + { + if (evt is InvoiceEvent invoiceEvent && invoiceEvent.EventCode == InvoiceEventCode.Created) + { + var invoiceSettings = GetSideShiftSettingsFromInvoice(invoiceEvent.Invoice); + if (invoiceSettings is not null) + { + var cacheKey = CreateCacheKeyForInvoice(invoiceEvent.InvoiceId); + var entry = _memoryCache.CreateEntry(cacheKey); + entry.AbsoluteExpiration = invoiceEvent.Invoice.ExpirationTime; + entry.Value = invoiceSettings; + } + } + return base.ProcessEvent(evt, cancellationToken); + } + + public async Task GetSideShiftForInvoice(string invoiceId, string storeId) + { + var cacheKey = CreateCacheKeyForInvoice(invoiceId); + var invoiceSettings = await _memoryCache.GetOrCreateAsync(cacheKey, async entry => + { + var invoice = await _invoiceRepository.GetInvoice(invoiceId); + entry.AbsoluteExpiration = invoice?.ExpirationTime; + return GetSideShiftSettingsFromInvoice(invoice); + }); + + var storeSettings = await GetSideShiftForStore(storeId); + if (invoiceSettings is null) + { + return storeSettings; + } + if (storeSettings is null) + { + return invoiceSettings.ToObject();; + } + + var storeSettingsJObject = JObject.FromObject(storeSettings, JsonSerializer.Create(_serializerSettings)); + storeSettingsJObject.Merge(invoiceSettings); + return storeSettingsJObject.ToObject(); + } + + private string CreateCacheKeyForInvoice(string invoiceId) => $"{nameof(SideShiftSettings)}_{invoiceId}"; + + private JObject? GetSideShiftSettingsFromInvoice(InvoiceEntity invoice) + { + return invoice?.Metadata.GetAdditionalData("sideshift"); + } + public async Task GetSideShiftForStore(string storeId) { + if (storeId is null) + { + return null; + } var k = $"{nameof(SideShiftSettings)}_{storeId}"; return await _memoryCache.GetOrCreateAsync(k, async _ => { diff --git a/Plugins/BTCPayServer.Plugins.SideShift/SideShiftSettings.cs b/Plugins/BTCPayServer.Plugins.SideShift/SideShiftSettings.cs index 645eaea..2d3f450 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/SideShiftSettings.cs +++ b/Plugins/BTCPayServer.Plugins.SideShift/SideShiftSettings.cs @@ -3,11 +3,11 @@ namespace BTCPayServer.Plugins.SideShift public class SideShiftSettings { public bool Enabled { get; set; } - public decimal AmountMarkupPercentage { get; set; } = 0; + public decimal AmountMarkupPercentage { get; set; } public string? PreferredTargetPaymentMethodId { get; set; } public string[] ExplicitMethods { get; set; } - public bool OnlyShowExplicitMethods { get; set; } = false; + public bool OnlyShowExplicitMethods { get; set; } } diff --git a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutContentExtension.cshtml b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutContentExtension.cshtml index 58f5d6d..39be6f6 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutContentExtension.cshtml +++ b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutContentExtension.cshtml @@ -1,10 +1,10 @@ @using BTCPayServer.Plugins.SideShift -@using Newtonsoft.Json -@using Newtonsoft.Json.Linq @inject SideShiftService SideShiftService +@model BTCPayServer.Models.InvoicingModels.PaymentModel + @{ - var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value(); - var settings = await SideShiftService.GetSideShiftForStore(storeId); + var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId); + if (settings?.Enabled is true) {
diff --git a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutEnd.cshtml b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutEnd.cshtml index 46b0ee4..25ed496 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutEnd.cshtml +++ b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutEnd.cshtml @@ -1,11 +1,9 @@ @using BTCPayServer.Plugins.SideShift -@using Newtonsoft.Json -@using Newtonsoft.Json.Linq @inject BTCPayServer.Security.ContentSecurityPolicies csp @inject SideShiftService SideShiftService +@model BTCPayServer.Models.InvoicingModels.PaymentModel @{ - var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value(); - var settings = await SideShiftService.GetSideShiftForStore(storeId); + var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId); if (settings?.Enabled is true) { csp.Add("script-src", "https://sideshift.ai"); diff --git a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutPaymentExtension.cshtml b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutPaymentExtension.cshtml index 1863233..b360fc1 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutPaymentExtension.cshtml +++ b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutPaymentExtension.cshtml @@ -1,12 +1,9 @@ @using BTCPayServer.Plugins.SideShift -@using Newtonsoft.Json -@using Newtonsoft.Json.Linq @inject BTCPayServer.Security.ContentSecurityPolicies csp @inject SideShiftService SideShiftService @model BTCPayServer.Models.InvoicingModels.PaymentModel @{ - var storeId = Model.StoreId; - var settings = await SideShiftService.GetSideShiftForStore(storeId); + var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId); var preferredTargetPaymentMethodId = string.IsNullOrEmpty(settings?.PreferredTargetPaymentMethodId) ? null : Model.AvailableCryptos.Any(crypto => crypto.PaymentMethodId == settings.PreferredTargetPaymentMethodId) ? settings.PreferredTargetPaymentMethodId : null; } diff --git a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutPaymentMethodExtension.cshtml b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutPaymentMethodExtension.cshtml index 0b20b27..e4569ad 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutPaymentMethodExtension.cshtml +++ b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutPaymentMethodExtension.cshtml @@ -3,8 +3,7 @@ @model BTCPayServer.Models.InvoicingModels.PaymentModel @{ const string id = "SideShift"; - var storeId = Model.StoreId; - var settings = await SideShiftService.GetSideShiftForStore(storeId); + var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId); if (settings?.Enabled is true) { diff --git a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutTabExtension.cshtml b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutTabExtension.cshtml index 74a4d8d..334a917 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutTabExtension.cshtml +++ b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/CheckoutTabExtension.cshtml @@ -1,10 +1,8 @@ @using BTCPayServer.Plugins.SideShift -@using Newtonsoft.Json -@using Newtonsoft.Json.Linq @inject SideShiftService SideShiftService +@model BTCPayServer.Models.InvoicingModels.PaymentModel @{ - var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value(); - var settings = await SideShiftService.GetSideShiftForStore(storeId); + var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId); if (settings?.Enabled is true) {
diff --git a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/PrismEnhance.cshtml b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/PrismEnhance.cshtml index eee01f7..c60c279 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/PrismEnhance.cshtml +++ b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/PrismEnhance.cshtml @@ -93,7 +93,7 @@ document.addEventListener('DOMContentLoaded', (event) => { document.getElementById("ss-result-additional-info").value = ""; if (isValid()){ shiftButton.setAttribute("disabled", "disabled"); - const type = "permanent"; + const type = "";//"permanent"; if (type ==="permanent"){ fetch("https://sideshift.ai/api/v2/shifts/variable",{ diff --git a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/PullPaymentViewInsert.cshtml b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/PullPaymentViewInsert.cshtml index 40cb763..0a6ba1b 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/PullPaymentViewInsert.cshtml +++ b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/PullPaymentViewInsert.cshtml @@ -1,7 +1,5 @@ -@using System.Net.Http -@using BTCPayServer.Plugins.SideShift +@using BTCPayServer.Plugins.SideShift @model BTCPayServer.Models.ViewPullPaymentModel -@inject IHttpClientFactory HttpClientFactory @inject SideShiftService SideShiftService @{ var ss = await SideShiftService.GetSideShiftForStore(Model.StoreId); diff --git a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/StoreIntegrationSideShiftOption.cshtml b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/StoreIntegrationSideShiftOption.cshtml index 9127400..64e4665 100644 --- a/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/StoreIntegrationSideShiftOption.cshtml +++ b/Plugins/BTCPayServer.Plugins.SideShift/Views/Shared/SideShift/StoreIntegrationSideShiftOption.cshtml @@ -1,23 +1,10 @@ @using BTCPayServer.Abstractions.Contracts @using BTCPayServer.Plugins.SideShift -@using Microsoft.AspNetCore.Mvc.TagHelpers -@using Microsoft.AspNetCore.Routing @inject SideShiftService SideShiftService @inject IScopeProvider ScopeProvider @{ var storeId = ScopeProvider.GetCurrentStoreId(); - - SideShiftSettings settings = null; - if (!string.IsNullOrEmpty(storeId)) - { - try - { - settings = await SideShiftService.GetSideShiftForStore(storeId); - } - catch (Exception) - { - } - } + var settings = await SideShiftService.GetSideShiftForStore(storeId); } @if (!string.IsNullOrEmpty(storeId)) {