This commit is contained in:
Kukks
2023-10-18 09:56:52 +02:00
parent 8fda803158
commit aa6f9d2848
13 changed files with 124 additions and 41 deletions

View File

@@ -9,7 +9,7 @@
<PropertyGroup>
<Product>SideShift</Product>
<Description>Allows you to embed a SideShift conversion screen to allow customers to pay with altcoins.</Description>
<Version>1.1.7</Version>
<Version>1.1.8</Version>
</PropertyGroup>
<!-- Plugin development properties -->
<PropertyGroup>

View File

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

View File

@@ -15,6 +15,7 @@ namespace BTCPayServer.Plugins.SideShift
public override void Execute(IServiceCollection applicationBuilder)
{
applicationBuilder.AddSingleton<SideShiftService>();
applicationBuilder.AddHostedService(provider => provider.GetService<SideShiftService>());
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/SideShiftNav",
"store-integrations-nav"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/PullPaymentViewInsert",

View File

@@ -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<SideShiftService> 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<InvoiceEvent>();
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<SideShiftSettings> 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<SideShiftSettings>();;
}
var storeSettingsJObject = JObject.FromObject(storeSettings, JsonSerializer.Create(_serializerSettings));
storeSettingsJObject.Merge(invoiceSettings);
return storeSettingsJObject.ToObject<SideShiftSettings>();
}
private string CreateCacheKeyForInvoice(string invoiceId) => $"{nameof(SideShiftSettings)}_{invoiceId}";
private JObject? GetSideShiftSettingsFromInvoice(InvoiceEntity invoice)
{
return invoice?.Metadata.GetAdditionalData<JObject>("sideshift");
}
public async Task<SideShiftSettings> GetSideShiftForStore(string storeId)
{
if (storeId is null)
{
return null;
}
var k = $"{nameof(SideShiftSettings)}_{storeId}";
return await _memoryCache.GetOrCreateAsync(k, async _ =>
{

View File

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

View File

@@ -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<string>();
var settings = await SideShiftService.GetSideShiftForStore(storeId);
var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId);
if (settings?.Enabled is true)
{
<div id="sideshift" class="bp-view payment manual-flow" :class="{ active: currentTab == 'undefined' || currentTab == 'sideshift' }">

View File

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

View File

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

View File

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

View File

@@ -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<string>();
var settings = await SideShiftService.GetSideShiftForStore(storeId);
var settings = await SideShiftService.GetSideShiftForInvoice(Model.InvoiceId, Model.StoreId);
if (settings?.Enabled is true)
{
<div class="payment-tabs__tab py-0" id="sideshift-tab" v-on:click="switchTab('sideshift')" v-bind:class="{ 'active': currentTab == 'sideshift'}" v-if="!srvModel.paymentMethodId.endsWith('LNURLPAY')">

View File

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

View File

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

View File

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