mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 22:44:29 +01:00
fix issues
This commit is contained in:
@@ -38,6 +38,7 @@
|
|||||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.5.3" />
|
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.5.3" />
|
||||||
<PackageReference Include="Hangfire" Version="1.6.20" />
|
<PackageReference Include="Hangfire" Version="1.6.20" />
|
||||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
||||||
|
<PackageReference Include="HtmlSanitizer" Version="4.0.199" />
|
||||||
<PackageReference Include="LedgerWallet" Version="2.0.0.3" />
|
<PackageReference Include="LedgerWallet" Version="2.0.0.3" />
|
||||||
<PackageReference Include="Meziantou.AspNetCore.BundleTagHelpers" Version="2.0.0" />
|
<PackageReference Include="Meziantou.AspNetCore.BundleTagHelpers" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
|
|||||||
@@ -81,38 +81,6 @@ namespace BTCPayServer.Controllers
|
|||||||
UseAllStoreInvoices = settings.UseAllStoreInvoices,
|
UseAllStoreInvoices = settings.UseAllStoreInvoices,
|
||||||
AppId = appId
|
AppId = appId
|
||||||
};
|
};
|
||||||
|
|
||||||
if (HttpContext?.Request != null)
|
|
||||||
{
|
|
||||||
var appUrl = HttpContext.Request.GetAbsoluteRoot().WithTrailingSlash() + $"apps/{appId}/crowdfund";
|
|
||||||
var encoder = HtmlEncoder.Default;
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
builder.AppendLine($"<form method=\"POST\" action=\"{encoder.Encode(appUrl)}\">");
|
|
||||||
builder.AppendLine($" <input type=\"hidden\" name=\"amount\" value=\"100\" />");
|
|
||||||
builder.AppendLine($" <input type=\"hidden\" name=\"redirectToCheckout\" value=\"true\" />");
|
|
||||||
builder.AppendLine($" <input type=\"hidden\" name=\"email\" value=\"customer@example.com\" />");
|
|
||||||
builder.AppendLine($" <input type=\"hidden\" name=\"redirectUrl\" value=\"https://example.com/thanksyou\" />");
|
|
||||||
builder.AppendLine($" <button type=\"submit\">Contribute now</button>");
|
|
||||||
builder.AppendLine($"</form>");
|
|
||||||
vm.Example1 = builder.ToString();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var items = _AppsHelper.Parse(settings.PerksTemplate, settings.TargetCurrency);
|
|
||||||
builder = new StringBuilder();
|
|
||||||
builder.AppendLine($"<form method=\"POST\" action=\"{encoder.Encode(appUrl)}\">");
|
|
||||||
builder.AppendLine($" <input type=\"hidden\" name=\"redirectToCheckout\" value=\"true\" />");
|
|
||||||
builder.AppendLine($" <input type=\"hidden\" name=\"email\" value=\"customer@example.com\" />");
|
|
||||||
builder.AppendLine($" <input type=\"hidden\" name=\"redirectUrl\" value=\"https://example.com/thanksyou\" />");
|
|
||||||
builder.AppendLine($" <button type=\"submit\" name=\"choiceKey\" value=\"{items[0].Id}\">Buy now</button>");
|
|
||||||
builder.AppendLine($"</form>");
|
|
||||||
vm.Example2 = builder.ToString();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
vm.ExampleCallback = "{\n \"id\":\"SkdsDghkdP3D3qkj7bLq3\",\n \"url\":\"https://btcpay.example.com/invoice?id=SkdsDghkdP3D3qkj7bLq3\",\n \"status\":\"paid\",\n \"price\":10,\n \"currency\":\"EUR\",\n \"invoiceTime\":1520373130312,\n \"expirationTime\":1520374030312,\n \"currentTime\":1520373179327,\n \"exceptionStatus\":false,\n \"buyerFields\":{\n \"buyerEmail\":\"customer@example.com\",\n \"buyerNotify\":false\n },\n \"paymentSubtotals\": {\n \"BTC\":114700\n },\n \"paymentTotals\": {\n \"BTC\":118400\n },\n \"transactionCurrency\": \"BTC\",\n \"amountPaid\": \"1025900\",\n \"exchangeRates\": {\n \"BTC\": {\n \"EUR\": 8721.690715789999,\n \"USD\": 10817.99\n }\n }\n}";
|
|
||||||
|
|
||||||
return View(vm);
|
return View(vm);
|
||||||
}
|
}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@@ -124,7 +92,7 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_AppsHelper.Parse(vm.PerksTemplate, vm.TargetCurrency);
|
_AppsHelper.Parse(vm.PerksTemplate, vm.TargetCurrency).ToString();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -158,7 +126,7 @@ namespace BTCPayServer.Controllers
|
|||||||
EnforceTargetAmount = vm.EnforceTargetAmount,
|
EnforceTargetAmount = vm.EnforceTargetAmount,
|
||||||
StartDate = vm.StartDate,
|
StartDate = vm.StartDate,
|
||||||
TargetCurrency = vm.TargetCurrency,
|
TargetCurrency = vm.TargetCurrency,
|
||||||
Description = vm.Description,
|
Description = _AppsHelper.Sanitize( vm.Description),
|
||||||
EndDate = vm.EndDate,
|
EndDate = vm.EndDate,
|
||||||
TargetAmount = vm.TargetAmount,
|
TargetAmount = vm.TargetAmount,
|
||||||
CustomCSSLink = vm.CustomCSSLink,
|
CustomCSSLink = vm.CustomCSSLink,
|
||||||
@@ -186,7 +154,7 @@ namespace BTCPayServer.Controllers
|
|||||||
Settings = newSettings
|
Settings = newSettings
|
||||||
});
|
});
|
||||||
StatusMessage = "App updated";
|
StatusMessage = "App updated";
|
||||||
return RedirectToAction(nameof(ListApps));
|
return RedirectToAction(nameof(UpdateCrowdfund), new {appId});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,7 @@ using BTCPayServer.Security;
|
|||||||
using BTCPayServer.Services.Apps;
|
using BTCPayServer.Services.Apps;
|
||||||
using BTCPayServer.Services.Invoices;
|
using BTCPayServer.Services.Invoices;
|
||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
|
using Ganss.XSS;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@@ -95,8 +96,18 @@ namespace BTCPayServer.Controllers
|
|||||||
if (app == null)
|
if (app == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
var settings = app.GetSettings<CrowdfundSettings>();
|
var settings = app.GetSettings<CrowdfundSettings>();
|
||||||
if (settings.Enabled) return View(await _CrowdfundHubStreamer.GetCrowdfundInfo(appId));
|
|
||||||
var isAdmin = await _AppsHelper.GetAppDataIfOwner(GetUserId(), appId, AppType.Crowdfund) != null;
|
var isAdmin = await _AppsHelper.GetAppDataIfOwner(GetUserId(), appId, AppType.Crowdfund) != null;
|
||||||
|
|
||||||
|
var hasEnoughSettingsToLoad = !string.IsNullOrEmpty(settings.TargetCurrency );
|
||||||
|
if (!hasEnoughSettingsToLoad)
|
||||||
|
{
|
||||||
|
if(!isAdmin)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return NotFound("A Target Currency must be set for this app in order to be loadable.");
|
||||||
|
}
|
||||||
|
if (settings.Enabled) return View(await _CrowdfundHubStreamer.GetCrowdfundInfo(appId));
|
||||||
if(!isAdmin)
|
if(!isAdmin)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
@@ -116,16 +127,9 @@ namespace BTCPayServer.Controllers
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
var settings = app.GetSettings<CrowdfundSettings>();
|
var settings = app.GetSettings<CrowdfundSettings>();
|
||||||
|
|
||||||
var hasEnoughSettingsToLoad = !string.IsNullOrEmpty(settings.TargetCurrency );
|
|
||||||
|
|
||||||
var isAdmin = await _AppsHelper.GetAppDataIfOwner(GetUserId(), appId, AppType.Crowdfund) != null;
|
var isAdmin = await _AppsHelper.GetAppDataIfOwner(GetUserId(), appId, AppType.Crowdfund) != null;
|
||||||
if (!hasEnoughSettingsToLoad)
|
|
||||||
{
|
|
||||||
if(!isAdmin)
|
|
||||||
return NotFound();
|
|
||||||
|
|
||||||
return NotFound("A Target Currency must be set for this app in order to be loadable.");
|
|
||||||
}
|
|
||||||
if (!settings.Enabled)
|
if (!settings.Enabled)
|
||||||
{
|
{
|
||||||
if(!isAdmin)
|
if(!isAdmin)
|
||||||
@@ -266,12 +270,32 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
ApplicationDbContextFactory _ContextFactory;
|
ApplicationDbContextFactory _ContextFactory;
|
||||||
CurrencyNameTable _Currencies;
|
CurrencyNameTable _Currencies;
|
||||||
|
private HtmlSanitizer _HtmlSanitizer;
|
||||||
public CurrencyNameTable Currencies => _Currencies;
|
public CurrencyNameTable Currencies => _Currencies;
|
||||||
public AppsHelper(ApplicationDbContextFactory contextFactory, CurrencyNameTable currencies)
|
public AppsHelper(ApplicationDbContextFactory contextFactory, CurrencyNameTable currencies)
|
||||||
{
|
{
|
||||||
_ContextFactory = contextFactory;
|
_ContextFactory = contextFactory;
|
||||||
_Currencies = currencies;
|
_Currencies = currencies;
|
||||||
|
ConfigureSanitizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigureSanitizer()
|
||||||
|
{
|
||||||
|
|
||||||
|
_HtmlSanitizer = new HtmlSanitizer();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_HtmlSanitizer.RemovingStyle += (sender, args) => { args.Cancel = true; };
|
||||||
|
_HtmlSanitizer.AllowedAttributes.Add("class");
|
||||||
|
_HtmlSanitizer.AllowedTags.Add("iframe");
|
||||||
|
_HtmlSanitizer.AllowedAttributes.Add("webkitallowfullscreen");
|
||||||
|
_HtmlSanitizer.AllowedAttributes.Add("allowfullscreen");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Sanitize(string raw)
|
||||||
|
{
|
||||||
|
return _HtmlSanitizer.Sanitize(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<StoreData[]> GetOwnedStores(string userId)
|
public async Task<StoreData[]> GetOwnedStores(string userId)
|
||||||
@@ -356,10 +380,10 @@ namespace BTCPayServer.Controllers
|
|||||||
.Where(kv => kv.Value != null)
|
.Where(kv => kv.Value != null)
|
||||||
.Select(c => new ViewPointOfSaleViewModel.Item()
|
.Select(c => new ViewPointOfSaleViewModel.Item()
|
||||||
{
|
{
|
||||||
Description = c.GetDetailString("description"),
|
Description = Sanitize(c.GetDetailString("description")),
|
||||||
Id = c.Key,
|
Id = c.Key,
|
||||||
Image = c.GetDetailString("image"),
|
Image = Sanitize(c.GetDetailString("image")),
|
||||||
Title = c.GetDetailString("title") ?? c.Key,
|
Title = Sanitize(c.GetDetailString("title") ?? c.Key),
|
||||||
Price = c.GetDetail("price")
|
Price = c.GetDetail("price")
|
||||||
.Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice()
|
.Select(cc => new ViewPointOfSaleViewModel.Item.ItemPrice()
|
||||||
{
|
{
|
||||||
@@ -387,6 +411,7 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
public string GetDetailString(string field)
|
public string GetDetailString(string field)
|
||||||
{
|
{
|
||||||
|
|
||||||
return GetDetail(field).FirstOrDefault()?.Value?.Value;
|
return GetDetail(field).FirstOrDefault()?.Value?.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ using YamlDotNet.Core;
|
|||||||
|
|
||||||
namespace BTCPayServer.Hubs
|
namespace BTCPayServer.Hubs
|
||||||
{
|
{
|
||||||
public class CrowdfundHubStreamer
|
public class
|
||||||
|
CrowdfundHubStreamer
|
||||||
{
|
{
|
||||||
public const string CrowdfundInvoiceOrderIdPrefix = "crowdfund-app_";
|
public const string CrowdfundInvoiceOrderIdPrefix = "crowdfund-app_";
|
||||||
private readonly EventAggregator _EventAggregator;
|
private readonly EventAggregator _EventAggregator;
|
||||||
@@ -125,13 +126,9 @@ namespace BTCPayServer.Hubs
|
|||||||
|
|
||||||
private void OnInvoiceEvent(InvoiceEvent invoiceEvent)
|
private void OnInvoiceEvent(InvoiceEvent invoiceEvent)
|
||||||
{
|
{
|
||||||
if (!invoiceEvent.Invoice.OrderId.StartsWith(CrowdfundInvoiceOrderIdPrefix, StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_QuickAppInvoiceLookup.TryGetValue(invoiceEvent.Invoice.StoreId, out var quickLookup) ||
|
if (!_QuickAppInvoiceLookup.TryGetValue(invoiceEvent.Invoice.StoreId, out var quickLookup) ||
|
||||||
(!quickLookup.useAllStoreInvoices &&
|
(!quickLookup.useAllStoreInvoices &&
|
||||||
|
!string.IsNullOrEmpty(invoiceEvent.Invoice.OrderId) &&
|
||||||
!invoiceEvent.Invoice.OrderId.Equals($"{CrowdfundInvoiceOrderIdPrefix}{quickLookup.appId}", StringComparison.InvariantCulture)
|
!invoiceEvent.Invoice.OrderId.Equals($"{CrowdfundInvoiceOrderIdPrefix}{quickLookup.appId}", StringComparison.InvariantCulture)
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
@@ -153,6 +150,8 @@ namespace BTCPayServer.Hubs
|
|||||||
InvalidateCacheForApp(quickLookup.appId);
|
InvalidateCacheForApp(quickLookup.appId);
|
||||||
break;
|
break;
|
||||||
case InvoiceEvent.Created:
|
case InvoiceEvent.Created:
|
||||||
|
case InvoiceEvent.MarkedInvalid:
|
||||||
|
case InvoiceEvent.MarkedCompleted:
|
||||||
if (quickLookup.useInvoiceAmount)
|
if (quickLookup.useInvoiceAmount)
|
||||||
{
|
{
|
||||||
InvalidateCacheForApp(quickLookup.appId);
|
InvalidateCacheForApp(quickLookup.appId);
|
||||||
@@ -182,7 +181,7 @@ namespace BTCPayServer.Hubs
|
|||||||
|
|
||||||
var ratesTask = rateFetcher.FetchRates(
|
var ratesTask = rateFetcher.FetchRates(
|
||||||
stats.Keys
|
stats.Keys
|
||||||
.Select((x) => new CurrencyPair(PaymentMethodId.Parse(x).CryptoCode, primaryCurrency))
|
.Select((x) => new CurrencyPair( primaryCurrency, PaymentMethodId.Parse(x).CryptoCode))
|
||||||
.ToHashSet(),
|
.ToHashSet(),
|
||||||
rateRules);
|
rateRules);
|
||||||
|
|
||||||
@@ -194,8 +193,8 @@ namespace BTCPayServer.Hubs
|
|||||||
var tResult = await rateTask.Value;
|
var tResult = await rateTask.Value;
|
||||||
var rate = tResult.BidAsk?.Bid;
|
var rate = tResult.BidAsk?.Bid;
|
||||||
if (rate == null) return;
|
if (rate == null) return;
|
||||||
var currencyGroup = stats[rateTask.Key.Left];
|
var currencyGroup = stats[rateTask.Key.Right];
|
||||||
result += currencyGroup / rate.Value;
|
result += (1m / rate.Value) * currencyGroup;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,9 +256,6 @@ namespace BTCPayServer.Hubs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var invoices = await GetInvoicesForApp(settings.UseAllStoreInvoices? null : appData.Id, lastResetDate);
|
var invoices = await GetInvoicesForApp(settings.UseAllStoreInvoices? null : appData.Id, lastResetDate);
|
||||||
var completeInvoices = invoices.Where(entity => entity.Status == InvoiceStatus.Complete).ToArray();
|
var completeInvoices = invoices.Where(entity => entity.Status == InvoiceStatus.Complete).ToArray();
|
||||||
var pendingInvoices = invoices.Where(entity => entity.Status != InvoiceStatus.Complete).ToArray();
|
var pendingInvoices = invoices.Where(entity => entity.Status != InvoiceStatus.Complete).ToArray();
|
||||||
|
|||||||
@@ -74,10 +74,6 @@ namespace BTCPayServer.Models.AppViewModels
|
|||||||
public bool UseAllStoreInvoices { get; set; }
|
public bool UseAllStoreInvoices { get; set; }
|
||||||
|
|
||||||
public string AppId { get; set; }
|
public string AppId { get; set; }
|
||||||
|
|
||||||
public string Example1 { get; internal set; }
|
|
||||||
public string Example2 { get; internal set; }
|
|
||||||
public string ExampleCallback { get; internal set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CrowdfundResetEvery
|
public enum CrowdfundResetEvery
|
||||||
|
|||||||
@@ -163,36 +163,14 @@
|
|||||||
<input asp-for="DisqusShortname" class="form-control" />
|
<input asp-for="DisqusShortname" class="form-control" />
|
||||||
<span asp-validation-for="DisqusShortname" class="text-danger"></span>
|
<span asp-validation-for="DisqusShortname" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<h5>Host button externally</h5>
|
|
||||||
<p>You can host contribution buttons in an external website with the following code.</p>
|
|
||||||
@if (Model.Example1 != null)
|
|
||||||
{
|
|
||||||
<span>For anything with a custom amount</span>
|
|
||||||
<pre><code class="html">@Model.Example1</code></pre>
|
|
||||||
}
|
|
||||||
@if (Model.Example2 != null)
|
|
||||||
{
|
|
||||||
<span>For a specific item of your perks template</span>
|
|
||||||
<pre><code class="html">@Model.Example2</code></pre>
|
|
||||||
}
|
|
||||||
<p>A <code>POST</code> callback will be sent to notification with the following form will be sent to <code>notificationUrl</code> once the enough is paid and once again once there is enough confirmations to the payment:</p>
|
|
||||||
<pre><code class="json">@Model.ExampleCallback</code></pre>
|
|
||||||
<p><strong>Never</strong> trust anything but <code>id</code>, <strong>ignore</strong> the other fields completely, an attacker can spoof those, they are present only for backward compatibility reason:</p>
|
|
||||||
<p>
|
|
||||||
<ul>
|
|
||||||
<li>Send a <code>GET</code> request to <code>https://btcpay.example.com/invoices/{invoiceId}</code> with <code>Content-Type: application/json</code></li>
|
|
||||||
<li>Verify that the <code>orderId</code> is from your backend, that the <code>price</code> is correct and that <code>status</code> is either <code>confirmed</code> or <code>complete</code></li>
|
|
||||||
<li>You can then ship your order</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="submit" class="btn btn-primary" value="Save Settings" />
|
<input type="submit" class="btn btn-primary" value="Save Settings" />
|
||||||
|
<a class="btn btn-secondary" target="_blank" asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@($"orderid:{CrowdfundHubStreamer.CrowdfundInvoiceOrderIdPrefix}{Model.AppId}")">Invoices generated by app</a>
|
||||||
|
<a class="btn btn-secondary" target="_blank" asp-action="ViewCrowdfund" asp-controller="AppsPublic" asp-route-appId="@Model.AppId">View App</a>
|
||||||
|
<a class="btn btn-secondary" target="_blank" asp-action="ListApps">Back to the app list</a>
|
||||||
</div>
|
</div>
|
||||||
<a asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchterm="@($"orderid:{CrowdfundHubStreamer.CrowdfundInvoiceOrderIdPrefix}{Model.AppId}")">Invoices generated by app</a> 
|
|
||||||
<a asp-action="ListApps">Back to the app list</a>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user