initial commit

This commit is contained in:
Kukks
2023-01-16 10:31:48 +01:00
parent 136273406c
commit 25ccd99558
171 changed files with 10592 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>10</LangVersion>
</PropertyGroup>
<!-- Plugin specific properties -->
<PropertyGroup>
<Title>SideShift</Title>
<Description>Allows you to embed a SideShift conversion screen to allow customers to pay with altcoins.</Description>
<Authors>Kukks</Authors>
<Version>1.0.9</Version>
</PropertyGroup>
<!-- Plugin development properties -->
<PropertyGroup>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<PreserveCompilationContext>false</PreserveCompilationContext>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<!-- This will make sure that referencing BTCPayServer doesn't put any artifact in the published directory -->
<ItemDefinitionGroup>
<ProjectReference>
<Properties>StaticWebAssetsEnabled=false</Properties>
<Private>false</Private>
<ExcludeAssets>runtime;native;build;buildTransitive;contentFiles</ExcludeAssets>
</ProjectReference>
</ItemDefinitionGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
<EmbeddedResource Include="Resources\**" />
</ItemGroup>
<ItemGroup>
<Folder Include="Views\Shared" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,2 @@
dotnet publish -c Altcoins-Release -o bin/publish/BTCPayServer.Plugins.SideShift
dotnet run -p ../../BTCPayServer.PluginPacker bin/publish/BTCPayServer.Plugins.SideShift BTCPayServer.Plugins.SideShift ../packed

View File

@@ -0,0 +1,14 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.1878 1.91295C11.7421 0.677017 9.90218 -0.00144636 8.00016 2.31513e-06C3.57671 2.31513e-06 3.18626e-06 3.57994 3.18626e-06 8.00684C-0.00169573 9.90996 0.676051 11.7512 1.91123 13.199L13.1878 1.91295Z" fill="url(#paint0_linear_4698_101169)"/>
<path d="M2.75781 14.0459C4.16396 15.262 5.99349 15.9999 8.00042 15.9999C12.4234 15.9999 16.0004 12.4199 16.0004 7.99302C16.0004 5.98435 15.2631 4.15354 14.0482 2.74609L2.75781 14.0462V14.0459Z" fill="url(#paint1_linear_4698_101169)"/>
<defs>
<linearGradient id="paint0_linear_4698_101169" x1="10.7152" y1="12.044" x2="2.73879" y2="1.16127" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1"/>
</linearGradient>
<linearGradient id="paint1_linear_4698_101169" x1="13.5175" y1="14.8401" x2="5.50803" y2="3.91232" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 959 B

View File

@@ -0,0 +1,38 @@
Vue.component("side-shift", {
props: ["toCurrency", "toCurrencyDue", "toCurrencyAddress"],
methods: {
openDialog: function (e) {
if (e && e.preventDefault) {
e.preventDefault();
}
let settleMethodId = "";
let amount = !this.$parent.srvModel.isUnsetTopUp
? this.toCurrencyDue
: undefined;
if (this.toCurrency.toLowerCase() === "lbtc") {
settleMethodId = "liquid";
} else if (this.toCurrency.toLowerCase() === "usdt") {
settleMethodId = "usdtla";
} else if (
this.toCurrency.endsWith("LightningLike") ||
this.toCurrency.endsWith("LNURLPay")
) {
settleMethodId = "ln";
} else {
settleMethodId = this.toCurrency
.replace("_BTCLike", "")
.replace("_MoneroLike", "")
.replace("_ZcashLike", "")
.toLowerCase();
}
window.__SIDESHIFT__ = {
parentAffiliateId: "qg0OrfHJV",
defaultSettleMethodId: settleMethodId,
settleAddress: this.toCurrencyAddress,
settleAmount: amount,
type: !this.$parent.srvModel.isUnsetTopUp ? "fixed" : undefined,
};
window.sideshift.show();
},
},
});

View File

@@ -0,0 +1,82 @@
using System;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Client;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Plugins.SideShift
{
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Route("plugins/{storeId}/SideShift")]
public class SideShiftController : Controller
{
private readonly BTCPayServerClient _btcPayServerClient;
private readonly SideShiftService _sideShiftService;
public SideShiftController(BTCPayServerClient btcPayServerClient, SideShiftService sideShiftService)
{
_btcPayServerClient = btcPayServerClient;
_sideShiftService = sideShiftService;
}
[HttpGet("")]
public async Task<IActionResult> UpdateSideShiftSettings(string storeId)
{
var store = await _btcPayServerClient.GetStore(storeId);
UpdateSideShiftSettingsViewModel vm = new UpdateSideShiftSettingsViewModel();
vm.StoreName = store.Name;
SideShiftSettings SideShift = null;
try
{
SideShift = await _sideShiftService.GetSideShiftForStore(storeId);
}
catch (Exception)
{
// ignored
}
SetExistingValues(SideShift, vm);
return View(vm);
}
private void SetExistingValues(SideShiftSettings existing, UpdateSideShiftSettingsViewModel vm)
{
if (existing == null)
return;
vm.Enabled = existing.Enabled;
}
[HttpPost("")]
public async Task<IActionResult> UpdateSideShiftSettings(string storeId, UpdateSideShiftSettingsViewModel vm,
string command)
{
if (vm.Enabled)
{
if (!ModelState.IsValid)
{
return View(vm);
}
}
var sideShiftSettings = new SideShiftSettings()
{
Enabled = vm.Enabled,
};
switch (command)
{
case "save":
await _sideShiftService.SetSideShiftForStore(storeId, sideShiftSettings);
TempData["SuccessMessage"] = "SideShift settings modified";
return RedirectToAction(nameof(UpdateSideShiftSettings), new {storeId});
default:
return View(vm);
}
}
}
}

View File

@@ -0,0 +1,48 @@
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Abstractions.Models;
using BTCPayServer.Abstractions.Services;
using Microsoft.Extensions.DependencyInjection;
namespace BTCPayServer.Plugins.SideShift
{
public class SideShiftPlugin : BaseBTCPayServerPlugin
{
public override string Identifier => "BTCPayServer.Plugins.SideShift";
public override string Name => "SideShift";
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
{
new IBTCPayServerPlugin.PluginDependency() { Identifier = nameof(BTCPayServer), Condition = ">=1.7.0.0" }
};
public override string Description =>
"Allows you to embed a SideShift conversion screen to allow customers to pay with altcoins.";
public override void Execute(IServiceCollection applicationBuilder)
{
applicationBuilder.AddSingleton<SideShiftService>();
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/SideShiftNav",
"store-integrations-nav"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/StoreIntegrationSideShiftOption",
"store-integrations-list"));
// Checkout v2
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutPaymentMethodExtension",
"checkout-payment-method"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutPaymentExtension",
"checkout-payment"));
// Checkout Classic
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutContentExtension",
"checkout-bitcoin-post-content"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutContentExtension",
"checkout-lightning-post-content"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutTabExtension",
"checkout-bitcoin-post-tabs"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutTabExtension",
"checkout-lightning-post-tabs"));
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("SideShift/CheckoutEnd",
"checkout-end"));
base.Execute(applicationBuilder);
}
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.Client;
using Microsoft.Extensions.Caching.Memory;
namespace BTCPayServer.Plugins.SideShift
{
public class SideShiftService
{
private readonly ISettingsRepository _settingsRepository;
private readonly IMemoryCache _memoryCache;
private readonly IStoreRepository _storeRepository;
public SideShiftService(ISettingsRepository settingsRepository, IMemoryCache memoryCache, IStoreRepository storeRepository)
{
_settingsRepository = settingsRepository;
_memoryCache = memoryCache;
_storeRepository = storeRepository;
}
public async Task<SideShiftSettings> GetSideShiftForStore(string storeId)
{
var k = $"{nameof(SideShiftSettings)}_{storeId}";
return await _memoryCache.GetOrCreateAsync(k, async _ =>
{
var res = await _storeRepository.GetSettingAsync<SideShiftSettings>(storeId,
nameof(SideShiftSettings));
if (res is not null) return res;
res = await _settingsRepository.GetSettingAsync<SideShiftSettings>(k);
if (res is not null)
{
await SetSideShiftForStore(storeId, res);
}
await _settingsRepository.UpdateSetting<SideShiftSettings>(null, k);
return res;
});
}
public async Task SetSideShiftForStore(string storeId, SideShiftSettings SideShiftSettings)
{
var k = $"{nameof(SideShiftSettings)}_{storeId}";
await _storeRepository.UpdateSetting(storeId, nameof(SideShiftSettings), SideShiftSettings);
_memoryCache.Set(k, SideShiftSettings);
}
}
}

View File

@@ -0,0 +1,8 @@
namespace BTCPayServer.Plugins.SideShift
{
public class SideShiftSettings
{
public bool Enabled { get; set; }
public decimal AmountMarkupPercentage { get; set; } = 0;
}
}

View File

@@ -0,0 +1,8 @@
namespace BTCPayServer.Plugins.SideShift
{
public class UpdateSideShiftSettingsViewModel
{
public bool Enabled { get; set; }
public string StoreName { get; set; }
}
}

View File

@@ -0,0 +1,26 @@
@using BTCPayServer.Plugins.SideShift
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@inject SideShiftService SideShiftService
@{
var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value<string>();
var settings = await SideShiftService.GetSideShiftForStore(storeId);
if (settings?.Enabled is true)
{
<div id="sideshift" class="bp-view payment manual-flow" :class="{ active: currentTab == 'undefined' || currentTab == 'sideshift' }">
<div class="manual__step-two__instructions">
<span>
{{$t("ConversionTab_BodyTop", srvModel)}}
<br/><br/>
{{$t("ConversionTab_BodyDesc", srvModel)}}
</span>
</div>
<side-shift inline-template
:to-currency="srvModel.paymentMethodId"
:to-currency-due="srvModel.btcDue * (1 + (@settings.AmountMarkupPercentage / 100)) "
:to-currency-address="srvModel.btcAddress">
<a v-on:click="openDialog($event)" href="#" class="action-button btn btn-secondary rounded-pill w-100 mt-4">{{$t("Pay with SideShift")}}</a>
</side-shift>
</div>
}
}

View File

@@ -0,0 +1,16 @@
@using BTCPayServer.Plugins.SideShift
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@inject BTCPayServer.Security.ContentSecurityPolicies csp
@inject SideShiftService SideShiftService
@{
var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value<string>();
var settings = await SideShiftService.GetSideShiftForStore(storeId);
if (settings?.Enabled is true)
{
csp.Add("script-src", "https://sideshift.ai");
csp.Add("script-src", "*.sideshift.ai");
<script src="~/Resources/js/sideShiftComponent.js"></script>
<script src="https://sideshift.ai/static/js/main.js" defer></script>
}
}

View File

@@ -0,0 +1,69 @@
@using BTCPayServer.Plugins.SideShift
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@inject BTCPayServer.Security.ContentSecurityPolicies csp
@inject SideShiftService SideShiftService
@{
var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value<string>();
var settings = await SideShiftService.GetSideShiftForStore(storeId);
}
@if (settings?.Enabled is true)
{
csp.Add("script-src", "https://sideshift.ai");
csp.Add("script-src", "*.sideshift.ai");
<template id="side-shift-checkout-template">
<div class="payment-box">
<p v-html="content"></p>
<button type="button" v-on:click="openDialog" class="btn btn-primary rounded-pill w-100">{{$t("Pay with SideShift")}}</button>
</div>
</template>
<script>
Vue.component("SideShiftCheckout", {
template: "#side-shift-checkout-template",
props: ["model"],
computed: {
content () {
return this.$i18n.i18next.t("conversion_body", this.model).replace(/\n/ig, '<br>');
},
currency () {
return this.model.paymentMethodId;
},
settleMethodId () {
if (this.currency.toLowerCase() === "lbtc") {
return 'liquid';
} else if (this.currency.toLowerCase() === "usdt") {
return "usdtla";
} else if (this.currency.endsWith('LightningLike') || this.currency.endsWith('LNURLPay')) {
return "ln";
} else {
return this.currency.replace('_BTCLike', '').replace('_MoneroLike', '').replace('_ZcashLike', '').toLowerCase();
}
},
type () {
return this.model.isUnsetTopUp
? undefined
: 'fixed';
},
amountDue () {
return this.model.isUnsetTopUp
? undefined
: this.model.btcDue * (1 + (@settings.AmountMarkupPercentage / 100));
}
},
methods: {
openDialog () {
window.__SIDESHIFT__ = {
parentAffiliateId: "qg0OrfHJV",
defaultSettleMethodId: this.settleMethodId,
settleAddress: this.model.btcAddress,
settleAmount: this.amountDue,
type: this.type
};
window.sideshift.show();
}
}
});
</script>
<script src="https://sideshift.ai/static/js/main.js" defer></script>
}

View File

@@ -0,0 +1,15 @@
@using BTCPayServer.Plugins.SideShift
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@inject SideShiftService SideShiftService
@{
const string id = "SideShift";
var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value<string>();
var settings = await SideShiftService.GetSideShiftForStore(storeId);
if (settings?.Enabled is true)
{
<a href="#@id" class="btcpay-pill m-0 payment-method" :class="{ active: pmId === '@id' }" v-on:click.prevent="changePaymentMethod('@id')">
@id
</a>
}
}

View File

@@ -0,0 +1,14 @@
@using BTCPayServer.Plugins.SideShift
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq
@inject SideShiftService SideShiftService
@{
var storeId = ((JObject)JObject.Parse(JsonConvert.SerializeObject(Model)))["StoreId"].Value<string>();
var settings = await SideShiftService.GetSideShiftForStore(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')">
<span>{{$t("Altcoins (SideShift)")}}</span>
</div>
}
}

View File

@@ -0,0 +1,16 @@
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Abstractions.Extensions
@using Microsoft.AspNetCore.Mvc.TagHelpers
@inject IScopeProvider ScopeProvider
@{
var storeId = ScopeProvider.GetCurrentStoreId();
}
@if (!string.IsNullOrEmpty(storeId))
{
<li class="nav-item">
<a asp-area="" asp-controller="SideShift" asp-action="UpdateSideShiftSettings" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("SideShift")" id="Nav-SideShift">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" alt="SideShift" class="icon"><g transform="translate(6,6)"><path d="M13.19 1.91A8 8 0 0 0 1.9 13.2L13.2 1.9Z" fill="currentColor"/><path d="M2.76 14.05a8 8 0 0 0 11.29-11.3l-11.3 11.3Z" fill="currentColor"/></g></svg>
<span>SideShift</span>
</a>
</li>
}

View File

@@ -0,0 +1,59 @@
@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)
{
}
}
}
@if (!string.IsNullOrEmpty(storeId))
{
<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="me-3">
SideShift
</strong>
<span title="" class="d-flex me-3">
<span class="text-secondary">Allows your customers to pay with altcoins that are not supported by BTCPay Server.</span>
</span>
</span>
<span class="d-flex align-items-center fw-semibold">
@if (settings?.Enabled is true)
{
<span class="d-flex align-items-center text-success">
<span class="me-2 btcpay-status btcpay-status--enabled"></span>
Enabled
</span>
<span class="text-light ms-3 me-2">|</span>
<a lass="btn btn-link px-1 py-1 fw-semibold" asp-controller="SideShift" asp-action="UpdateSideShiftSettings" asp-route-storeId="@storeId">
Modify
</a>
}
else
{
<span class="d-flex align-items-center text-danger">
<span class="me-2 btcpay-status btcpay-status--disabled"></span>
Disabled
</span>
<a class="btn btn-primary btn-sm ms-4 px-3 py-1 fw-semibold" asp-controller="SideShift" asp-action="UpdateSideShiftSettings" asp-route-storeId="@storeId">
Setup
</a>
}
</span>
</div>
</li>
}

View File

@@ -0,0 +1,28 @@
@using BTCPayServer.Abstractions.Extensions
@using Microsoft.AspNetCore.Mvc.TagHelpers
@model BTCPayServer.Plugins.SideShift.UpdateSideShiftSettingsViewModel
@{
ViewData.SetActivePage("SideShift", "SideShift", "SideShift");
}
<partial name="_StatusMessage" />
<h2 class="mb-4">@ViewData["Title"]</h2>
<div class="alert alert-warning mb-4" role="alert">
If you are enabling SideShift support, we advise that you configure the invoice expiration to a minimum of 30 minutes as it may take longer than the default 15 minutes to convert the funds.
</div>
<div class="row">
<div class="col-md-10">
<form method="post">
<div class="form-group">
<div class="d-flex align-items-center">
<input asp-for="Enabled" type="checkbox" class="btcpay-toggle me-2"/>
<label asp-for="Enabled" class="form-label mb-0 me-1"></label>
</div>
</div>
<button name="command" type="submit" value="save" class="btn btn-primary">Submit</button>
</form>
</div>
</div>

View File

@@ -0,0 +1,2 @@
@addTagHelper *, BTCPayServer.Abstractions
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers