mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-22 22:25:28 +01:00
Merge branch 'master' into MonetaryUnit
This commit is contained in:
commit
b5b32cacfc
139 changed files with 11314 additions and 8683 deletions
|
@ -124,7 +124,6 @@ workflows:
|
|||
filters:
|
||||
branches:
|
||||
only: master
|
||||
|
||||
publish:
|
||||
jobs:
|
||||
- amd64:
|
||||
|
@ -134,21 +133,22 @@ workflows:
|
|||
ignore: /.*/
|
||||
# only act on version tags v1.0.0.88 or v1.0.2-1
|
||||
# OR feature tags like vlndseedbackup
|
||||
# OR features on specific versions like v1.0.0.88-lndseedbackup-1
|
||||
tags:
|
||||
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[0-9]+)?)|(v[a-z]+)/
|
||||
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- arm32v7:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[0-9]+)?)|(v[a-z]+)/
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- arm64v8:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[0-9]+)?)|(v[a-z]+)/
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- multiarch:
|
||||
requires:
|
||||
- amd64
|
||||
|
@ -158,4 +158,4 @@ workflows:
|
|||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[0-9]+)?)|(v[a-z]+)/
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
|
|
38
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: File a bug report
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Your BTCPay Environment (please complete the following information):**
|
||||
- BTCPay Server Version [available in the right bottom corner of footer]
|
||||
- Deployment Method: [e.g. Docker, Manual, Third-Party-hoist]
|
||||
- Browser [e.g. chrome, safari]
|
||||
|
||||
**Logs (if applicable)**
|
||||
Basic logs can be found in Server Settings > Logs. More logs https://docs.btcpayserver.org/Troubleshooting/#2-looking-through-the-logs
|
||||
|
||||
**Setup Parameters**
|
||||
If you're reporting a deployment issue run `. btcpay-setup.sh -i` and paste your the parameters by obscuring private information.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
37
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,37 +0,0 @@
|
|||
---
|
||||
name: Report a problem
|
||||
about: File a technical problem or report a bug
|
||||
---
|
||||
|
||||
**Describe the problem/bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Your environment**
|
||||
* Version of BTCPay Server:
|
||||
* Deployment method:
|
||||
* Other relevant environment details:
|
||||
|
||||
**Logs (if applicable)**
|
||||
Basic logs can be found in Server Settings > Logs.
|
||||
|
||||
**Setup Parameters**
|
||||
If you're reporting a deployment issue run `. btcpay-setup.sh -i` and paste your the paremeters by obscuring private information.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Actual behavior**
|
||||
Tell us what happens instead
|
||||
|
||||
**Screenshots/Links**
|
||||
If applicable, add screenshots or links to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Community Support Chat
|
||||
url: https://chat.btcpayserver.org/
|
||||
about: Ask general questions and get community support in real-time.
|
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
11
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,6 +1,9 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Ideas and feature requests
|
||||
about: Suggest a new feature or enhancement
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
@ -10,11 +13,11 @@ A clear and concise description of what the problem is. Ex. I'm always frustrate
|
|||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Sketch/Image/Wireframe/Mockup**
|
||||
If applicable provide examples, wireframes, sketches or images to better explain your idea.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Provide examples**
|
||||
If applicable provide examples, wireframes, sketches or images to better explain your idea.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NBitcoin" Version="5.0.43" />
|
||||
<PackageReference Include="NBitcoin" Version="5.0.51" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.2.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -6,11 +6,11 @@ namespace BTCPayServer
|
|||
{
|
||||
public static class LiquidExtensions
|
||||
{
|
||||
public static IEnumerable<string> GetAllElementsSubChains(this BTCPayNetworkProvider networkProvider)
|
||||
public static IEnumerable<string> GetAllElementsSubChains(this BTCPayNetworkProvider networkProvider, BTCPayNetworkProvider unfilteredNetworkProvider)
|
||||
{
|
||||
var elementsBased = networkProvider.GetAll().OfType<ElementsBTCPayNetwork>();
|
||||
var parentChains = elementsBased.Select(network => network.NetworkCryptoCode.ToUpperInvariant()).Distinct();
|
||||
return networkProvider.GetAll().OfType<ElementsBTCPayNetwork>()
|
||||
return unfilteredNetworkProvider.GetAll().OfType<ElementsBTCPayNetwork>()
|
||||
.Where(network => parentChains.Contains(network.NetworkCryptoCode)).Select(network => network.CryptoCode.ToUpperInvariant());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,4 +9,4 @@
|
|||
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
||||
<Compile Remove="Altcoins\**\*.cs"></Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -6,7 +6,7 @@
|
|||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
|
||||
<PackageReference Include="NBitcoin" Version="5.0.43" />
|
||||
<PackageReference Include="NBitcoin" Version="5.0.51" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.6.3" />
|
||||
</ItemGroup>
|
||||
|
|
39
BTCPayServer.Rating/Providers/HitBTCRateProvider.cs
Normal file
39
BTCPayServer.Rating/Providers/HitBTCRateProvider.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Rating
|
||||
{
|
||||
public class HitBTCRateProvider : IRateProvider
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
public HitBTCRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.hitbtc.com/api/2/public/ticker", cancellationToken);
|
||||
var jarray = await response.Content.ReadAsAsync<JArray>(cancellationToken);
|
||||
return jarray
|
||||
.Children<JObject>()
|
||||
.Where(p => CurrencyPair.TryParse(p["symbol"].Value<string>(), out _))
|
||||
.Select(p => new PairRate(CurrencyPair.Parse(p["symbol"].Value<string>()), CreateBidAsk(p)))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private BidAsk CreateBidAsk(JObject p)
|
||||
{
|
||||
var bid = p["bid"].Value<decimal>();
|
||||
var ask = p["ask"].Value<decimal>();
|
||||
return new BidAsk(bid, ask);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -90,10 +90,10 @@ namespace BTCPayServer.Services.Rates
|
|||
AddExchangeSharpProviders<ExchangeBinanceAPI>("binance");
|
||||
AddExchangeSharpProviders<ExchangeBittrexAPI>("bittrex");
|
||||
AddExchangeSharpProviders<ExchangePoloniexAPI>("poloniex");
|
||||
AddExchangeSharpProviders<ExchangeHitBTCAPI>("hitbtc");
|
||||
AddExchangeSharpProviders<ExchangeNDAXAPI>("ndax");
|
||||
|
||||
// Handmade providers
|
||||
Providers.Add("hitbtc", new HitBTCRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_HITBTC")));
|
||||
Providers.Add("coingecko", new CoinGeckoRateProvider(_httpClientFactory));
|
||||
Providers.Add("kraken", new KrakenExchangeRateProvider() { HttpClient = _httpClientFactory?.CreateClient("EXCHANGE_KRAKEN") });
|
||||
Providers.Add("bylls", new ByllsRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BYLLS")));
|
||||
|
|
|
@ -3,9 +3,13 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Models.PaymentRequestViewModels;
|
||||
using BTCPayServer.PaymentRequest;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.PaymentRequests;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
@ -16,7 +20,7 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
public PaymentRequestTests(ITestOutputHelper helper)
|
||||
{
|
||||
Logs.Tester = new XUnitLog(helper) { Name = "Tests" };
|
||||
Logs.Tester = new XUnitLog(helper) {Name = "Tests"};
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
}
|
||||
|
||||
|
@ -46,8 +50,8 @@ namespace BTCPayServer.Tests
|
|||
Description = "description"
|
||||
};
|
||||
var id = (Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(null, request)).RouteValues.Values.First().ToString());
|
||||
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(null, request))
|
||||
.RouteValues.Values.First().ToString());
|
||||
|
||||
|
||||
//permission guard for guests editing
|
||||
|
@ -57,7 +61,9 @@ namespace BTCPayServer.Tests
|
|||
request.Title = "update";
|
||||
Assert.IsType<RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(id, request));
|
||||
|
||||
Assert.Equal(request.Title, Assert.IsType<ViewPaymentRequestViewModel>(Assert.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Title);
|
||||
Assert.Equal(request.Title,
|
||||
Assert.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Title);
|
||||
|
||||
Assert.False(string.IsNullOrEmpty(id));
|
||||
|
||||
|
@ -68,16 +74,24 @@ namespace BTCPayServer.Tests
|
|||
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id));
|
||||
Assert.True(Assert.IsType<ViewPaymentRequestViewModel>(Assert.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived);
|
||||
Assert.True(Assert
|
||||
.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived);
|
||||
|
||||
Assert.Empty(Assert.IsType<ListPaymentRequestsViewModel>(Assert.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests()).Model).Items);
|
||||
Assert.Empty(Assert
|
||||
.IsType<ListPaymentRequestsViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests()).Model).Items);
|
||||
//unarchive
|
||||
Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id));
|
||||
|
||||
Assert.False(Assert.IsType<ViewPaymentRequestViewModel>(Assert.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived);
|
||||
Assert.False(Assert
|
||||
.IsType<ViewPaymentRequestViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived);
|
||||
|
||||
Assert.Single(Assert.IsType<ListPaymentRequestsViewModel>(Assert.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests()).Model).Items);
|
||||
Assert.Single(Assert
|
||||
.IsType<ListPaymentRequestsViewModel>(Assert
|
||||
.IsType<ViewResult>(await paymentRequestController.GetPaymentRequests()).Model).Items);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +108,8 @@ namespace BTCPayServer.Tests
|
|||
|
||||
var paymentRequestController = user.GetController<PaymentRequestController>();
|
||||
|
||||
Assert.IsType<NotFoundResult>(await paymentRequestController.PayPaymentRequest(Guid.NewGuid().ToString()));
|
||||
Assert.IsType<NotFoundResult>(
|
||||
await paymentRequestController.PayPaymentRequest(Guid.NewGuid().ToString()));
|
||||
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel()
|
||||
|
@ -110,15 +125,18 @@ namespace BTCPayServer.Tests
|
|||
.RouteValues.First();
|
||||
|
||||
var invoiceId = Assert
|
||||
.IsType<OkObjectResult>(await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false)).Value
|
||||
.IsType<OkObjectResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false)).Value
|
||||
.ToString();
|
||||
|
||||
var actionResult = Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.PayPaymentRequest(response.Value.ToString()));
|
||||
.IsType<RedirectToActionResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString()));
|
||||
|
||||
Assert.Equal("Checkout", actionResult.ActionName);
|
||||
Assert.Equal("Invoice", actionResult.ControllerName);
|
||||
Assert.Contains(actionResult.RouteValues, pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
|
||||
Assert.Contains(actionResult.RouteValues,
|
||||
pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
|
||||
|
||||
var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(1, invoice.Price);
|
||||
|
@ -138,8 +156,8 @@ namespace BTCPayServer.Tests
|
|||
.RouteValues.First();
|
||||
|
||||
Assert
|
||||
.IsType<BadRequestObjectResult>(await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false));
|
||||
|
||||
.IsType<BadRequestObjectResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,11 +174,9 @@ namespace BTCPayServer.Tests
|
|||
|
||||
var paymentRequestController = user.GetController<PaymentRequestController>();
|
||||
|
||||
|
||||
Assert.IsType<NotFoundResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(Guid.NewGuid().ToString(), false));
|
||||
|
||||
|
||||
var request = new UpdatePaymentRequestViewModel()
|
||||
{
|
||||
Title = "original juice",
|
||||
|
@ -176,15 +192,18 @@ namespace BTCPayServer.Tests
|
|||
var paymentRequestId = response.Value.ToString();
|
||||
|
||||
var invoiceId = Assert
|
||||
.IsType<OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false)).Value
|
||||
.IsType<OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false))
|
||||
.Value
|
||||
.ToString();
|
||||
|
||||
var actionResult = Assert
|
||||
.IsType<RedirectToActionResult>(await paymentRequestController.PayPaymentRequest(response.Value.ToString()));
|
||||
.IsType<RedirectToActionResult>(
|
||||
await paymentRequestController.PayPaymentRequest(response.Value.ToString()));
|
||||
|
||||
Assert.Equal("Checkout", actionResult.ActionName);
|
||||
Assert.Equal("Invoice", actionResult.ControllerName);
|
||||
Assert.Contains(actionResult.RouteValues, pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
|
||||
Assert.Contains(actionResult.RouteValues,
|
||||
pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId);
|
||||
|
||||
var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(InvoiceState.ToString(InvoiceStatus.New), invoice.Status);
|
||||
|
@ -194,11 +213,24 @@ namespace BTCPayServer.Tests
|
|||
invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
Assert.Equal(InvoiceState.ToString(InvoiceStatus.Invalid), invoice.Status);
|
||||
|
||||
|
||||
Assert.IsType<BadRequestObjectResult>(await
|
||||
paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false));
|
||||
|
||||
invoiceId = Assert
|
||||
.IsType<OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false))
|
||||
.Value
|
||||
.ToString();
|
||||
|
||||
invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant);
|
||||
|
||||
//a hack to generate invoices for the payment request is to manually create an invocie with an order id that matches:
|
||||
user.BitPay.CreateInvoice(new Invoice(1, "USD")
|
||||
{
|
||||
OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(paymentRequestId)
|
||||
});
|
||||
//shouldnt crash
|
||||
await paymentRequestController.ViewPaymentRequest(paymentRequestId);
|
||||
await paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,9 @@ If you get this message:
|
|||
|
||||
Please, run the test `CanSetLightningServer`, this will establish a channel between the customer and the merchant, then, retry.
|
||||
|
||||
Alternatively you can run the `./docker-lightning-channel-setup.sh` script to establish the channel connection.
|
||||
The `./docker-lightning-channel-teardown.sh` script closes any existing lightning channels.
|
||||
|
||||
## FAQ
|
||||
|
||||
`docker-compose up dev` failed or tests are not passing, what should I do?
|
||||
|
|
|
@ -157,7 +157,7 @@ namespace BTCPayServer.Tests
|
|||
{
|
||||
string connectionString = null;
|
||||
if (connectionType == LightningConnectionType.Charge)
|
||||
connectionString = "type=charge;server=" + Server.MerchantCharge.Client.Uri.AbsoluteUri;
|
||||
connectionString = $"type=charge;server={Server.MerchantCharge.Client.Uri.AbsoluteUri};allowinsecure=true";
|
||||
else if (connectionType == LightningConnectionType.CLightning)
|
||||
connectionString = "type=clightning;server=" + ((CLightningClient)Server.MerchantLightningD).Address.AbsoluteUri;
|
||||
else if (connectionType == LightningConnectionType.LndREST)
|
||||
|
|
|
@ -664,6 +664,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.Equal(parsedBip21.Address.ToString(), s.Driver.FindElement(By.Id($"Outputs_0__DestinationAddress")).GetAttribute("value"));
|
||||
|
||||
s.GoToWallet(new WalletId(storeId.storeId, "BTC"), WalletsNavPages.Settings);
|
||||
var walletUrl = s.Driver.Url;
|
||||
|
||||
s.Driver.FindElement(By.Id("SettingsMenu")).ForceClick();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=view-seed]")).Click();
|
||||
|
@ -671,7 +672,12 @@ namespace BTCPayServer.Tests
|
|||
// Seed backup page
|
||||
var recoveryPhrase = s.Driver.FindElements(By.Id("recovery-phrase")).First().GetAttribute("data-mnemonic");
|
||||
Assert.Equal(mnemonic.ToString(), recoveryPhrase);
|
||||
Assert.Contains("The recovery phrase will also be stored on a server as a hot wallet.", s.Driver.PageSource);
|
||||
Assert.Contains("The recovery phrase will also be stored on the server as a hot wallet.", s.Driver.PageSource);
|
||||
|
||||
// No confirmation, just a link to return to the wallet
|
||||
Assert.Empty(s.Driver.FindElements(By.Id("confirm")));
|
||||
s.Driver.FindElement(By.Id("proceed")).Click();
|
||||
Assert.Equal(walletUrl, s.Driver.Url);
|
||||
}
|
||||
}
|
||||
void SetTransactionOutput(SeleniumTester s, int index, BitcoinAddress dest, decimal amount, bool subtract = false)
|
||||
|
|
|
@ -84,7 +84,7 @@ namespace BTCPayServer.Tests
|
|||
var btc = NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork;
|
||||
CustomerLightningD = LightningClientFactory.CreateClient(GetEnvironment("TEST_CUSTOMERLIGHTNINGD", "type=clightning;server=tcp://127.0.0.1:30992/"), btc);
|
||||
MerchantLightningD = LightningClientFactory.CreateClient(GetEnvironment("TEST_MERCHANTLIGHTNINGD", "type=clightning;server=tcp://127.0.0.1:30993/"), btc);
|
||||
MerchantCharge = new ChargeTester(this, "TEST_MERCHANTCHARGE", "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify", "merchant_lightningd", btc);
|
||||
MerchantCharge = new ChargeTester(this, "TEST_MERCHANTCHARGE", "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify;allowinsecure=true", "merchant_lightningd", btc);
|
||||
MerchantLnd = new LndMockTester(this, "TEST_MERCHANTLND", "https://lnd:lnd@127.0.0.1:35531/", "merchant_lnd", btc);
|
||||
PayTester.UseLightning = true;
|
||||
PayTester.IntegratedLightning = MerchantCharge.Client.Uri;
|
||||
|
|
|
@ -260,7 +260,7 @@ namespace BTCPayServer.Tests
|
|||
if (connectionType == LightningConnectionType.Charge)
|
||||
{
|
||||
if (isMerchant)
|
||||
connectionString = "type=charge;server=" + parent.MerchantCharge.Client.Uri.AbsoluteUri;
|
||||
connectionString = $"type=charge;server={parent.MerchantCharge.Client.Uri.AbsoluteUri};allowinsecure=true";
|
||||
else
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
|
|
@ -735,7 +735,7 @@ namespace BTCPayServer.Tests
|
|||
|
||||
var testResult = storeController.AddLightningNode(user.StoreId, new LightningNodeViewModel()
|
||||
{
|
||||
ConnectionString = "type=charge;server=" + tester.MerchantCharge.Client.Uri.AbsoluteUri,
|
||||
ConnectionString = $"type=charge;server={tester.MerchantCharge.Client.Uri.AbsoluteUri};allowinsecure=true",
|
||||
SkipPortTest = true // We can't test this as the IP can't be resolved by the test host :(
|
||||
}, "test", "BTC").GetAwaiter().GetResult();
|
||||
Assert.False(storeController.TempData.ContainsKey(WellKnownTempData.ErrorMessage));
|
||||
|
@ -745,7 +745,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.IsType<RedirectToActionResult>(storeController.AddLightningNode(user.StoreId,
|
||||
new LightningNodeViewModel()
|
||||
{
|
||||
ConnectionString = "type=charge;server=" + tester.MerchantCharge.Client.Uri.AbsoluteUri
|
||||
ConnectionString = $"type=charge;server={tester.MerchantCharge.Client.Uri.AbsoluteUri};allowinsecure=true"
|
||||
}, "save", "BTC").GetAwaiter().GetResult());
|
||||
|
||||
// Make sure old connection string format does not work
|
||||
|
|
|
@ -23,7 +23,7 @@ services:
|
|||
TESTS_AzureBlobStorageConnectionString: ${TESTS_AzureBlobStorageConnectionString:-none}
|
||||
TEST_MERCHANTLIGHTNINGD: "type=clightning;server=unix://etc/merchant_lightningd_datadir/lightning-rpc"
|
||||
TEST_CUSTOMERLIGHTNINGD: "type=clightning;server=unix://etc/customer_lightningd_datadir/lightning-rpc"
|
||||
TEST_MERCHANTCHARGE: "type=charge;server=http://lightning-charged:9112/;api-token=foiewnccewuify"
|
||||
TEST_MERCHANTCHARGE: "type=charge;server=http://lightning-charged:9112/;api-token=foiewnccewuify;allowinsecure=true"
|
||||
TEST_MERCHANTLND: "https://lnd:lnd@merchant_lnd:8080/"
|
||||
TESTS_INCONTAINER: "true"
|
||||
TESTS_SSHCONNECTION: "root@sshd:22"
|
||||
|
@ -81,7 +81,7 @@ services:
|
|||
- customer_lnd
|
||||
- merchant_lnd
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.1.35
|
||||
image: nicolasdorier/nbxplorer:2.1.40
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
|
@ -140,7 +140,7 @@ services:
|
|||
- "bitcoin_datadir:/data"
|
||||
|
||||
customer_lightningd:
|
||||
image: btcpayserver/lightning:v0.8.2-dev
|
||||
image: btcpayserver/lightning:v0.9.0-1-dev
|
||||
stop_signal: SIGKILL
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
@ -187,7 +187,7 @@ services:
|
|||
- merchant_lightningd
|
||||
|
||||
merchant_lightningd:
|
||||
image: btcpayserver/lightning:v0.8.2-dev
|
||||
image: btcpayserver/lightning:v0.9.0-1-dev
|
||||
stop_signal: SIGKILL
|
||||
environment:
|
||||
EXPOSE_TCP: "true"
|
||||
|
|
|
@ -21,7 +21,7 @@ services:
|
|||
TESTS_AzureBlobStorageConnectionString: ${TESTS_AzureBlobStorageConnectionString:-none}
|
||||
TEST_MERCHANTLIGHTNINGD: "type=clightning;server=unix://etc/merchant_lightningd_datadir/lightning-rpc"
|
||||
TEST_CUSTOMERLIGHTNINGD: "type=clightning;server=unix://etc/customer_lightningd_datadir/lightning-rpc"
|
||||
TEST_MERCHANTCHARGE: "type=charge;server=http://lightning-charged:9112/;api-token=foiewnccewuify"
|
||||
TEST_MERCHANTCHARGE: "type=charge;server=http://lightning-charged:9112/;api-token=foiewnccewuify;allowinsecure=true"
|
||||
TEST_MERCHANTLND: "https://lnd:lnd@merchant_lnd:8080/"
|
||||
TESTS_INCONTAINER: "true"
|
||||
TESTS_SSHCONNECTION: "root@sshd:22"
|
||||
|
@ -78,7 +78,7 @@ services:
|
|||
- customer_lnd
|
||||
- merchant_lnd
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.1.37
|
||||
image: nicolasdorier/nbxplorer:2.1.40
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
|
@ -127,7 +127,7 @@ services:
|
|||
- "bitcoin_datadir:/data"
|
||||
|
||||
customer_lightningd:
|
||||
image: btcpayserver/lightning:v0.8.2-dev
|
||||
image: btcpayserver/lightning:v0.9.0-1-dev
|
||||
stop_signal: SIGKILL
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
@ -174,7 +174,7 @@ services:
|
|||
- merchant_lightningd
|
||||
|
||||
merchant_lightningd:
|
||||
image: btcpayserver/lightning:v0.8.2-dev
|
||||
image: btcpayserver/lightning:v0.9.0-1-dev
|
||||
stop_signal: SIGKILL
|
||||
environment:
|
||||
EXPOSE_TCP: "true"
|
||||
|
|
69
BTCPayServer.Tests/docker-lightning-channel-setup.sh
Executable file
69
BTCPayServer.Tests/docker-lightning-channel-setup.sh
Executable file
|
@ -0,0 +1,69 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Commands
|
||||
BCMD=./docker-bitcoin-cli.sh
|
||||
GCMD=./docker-bitcoin-generate.sh
|
||||
CCMD=./docker-customer-lightning-cli.sh
|
||||
MCMD=./docker-merchant-lightning-cli.sh
|
||||
|
||||
function channel_count () {
|
||||
local cmd=$1; local id=$2;
|
||||
local count=$($cmd listchannels | jq -r ".channels | map(select(.destination == \"$id\")) | length | tonumber") 2>/dev/null
|
||||
return $count
|
||||
}
|
||||
|
||||
function create_channel () {
|
||||
local cmd=$1; local id=$2;
|
||||
local btcaddr=$($cmd newaddr | jq -r '.address')
|
||||
$BCMD sendtoaddress $btcaddr 0.15 >/dev/null
|
||||
$GCMD 10 >/dev/null
|
||||
local fundres=$($cmd fundchannel $id 14500000 5000 | jq -r '.channel_id')
|
||||
$GCMD 20 >/dev/null
|
||||
sleep 2
|
||||
channel_count $cmd $id
|
||||
local count=$?
|
||||
return $count
|
||||
}
|
||||
|
||||
# General information
|
||||
cinfo=$($CCMD getinfo | jq '.' 2>/dev/null)
|
||||
minfo=$($MCMD getinfo | jq '.' 2>/dev/null)
|
||||
cid=$(echo $cinfo | jq -r '.id')
|
||||
mid=$(echo $minfo | jq -r '.id')
|
||||
caddr=$(echo $cinfo | jq -r '.address[] | "\(.address):\(.port)"')
|
||||
maddr=$(echo $minfo | jq -r '.address[] | "\(.address):\(.port)"')
|
||||
|
||||
printf "Customer ID: %s@%s\n\r" $cid $caddr
|
||||
printf "Merchant ID: %s@%s\n\r" $mid $maddr
|
||||
|
||||
# Connections
|
||||
printf "\n\rConnecting both parties …\n\r"
|
||||
|
||||
cconnid=$($CCMD connect "$mid@$maddr" | jq -r '.id' 2>/dev/null)
|
||||
mconnid=$($MCMD connect "$cid@$caddr" | jq -r '.id' 2>/dev/null)
|
||||
|
||||
printf "Customer to merchant %s\n\r" $([[ $cconnid == $mid ]] && echo "succeeded" || echo "failed")
|
||||
printf "Merchant to customer %s\n\r" $([[ $mconnid == $cid ]] && echo "succeeded" || echo "failed")
|
||||
|
||||
# Channels
|
||||
printf "\n\rChecking channels …\n\r"
|
||||
channel_count $CCMD $mid
|
||||
cchanscount=$?
|
||||
channel_count $MCMD $cid
|
||||
mchanscount=$?
|
||||
|
||||
printf "Customer channel count to merchant: %d\n\r" $cchanscount
|
||||
printf "Merchant channel count to customer: %d\n\r" $mchanscount
|
||||
|
||||
# Open channels if there are none, details: https://github.com/ElementsProject/lightning#opening-a-channel
|
||||
if [[ $cchanscount -eq 0 ]]; then
|
||||
create_channel $CCMD $mid
|
||||
cchanres=$?
|
||||
printf "Establishing channel from customer to merchant %s\n\r" $([[ $cchanres -gt 0 ]] && echo "succeeded" || echo "failed")
|
||||
fi
|
||||
|
||||
if [[ $mchanscount -eq 0 ]]; then
|
||||
create_channel $MCMD $cid
|
||||
mchanres=$?
|
||||
printf "Establishing channel from merchant to customer %s\n\r" $([[ $mchanres -gt 0 ]] && echo "succeeded" || echo "failed")
|
||||
fi
|
12
BTCPayServer.Tests/docker-lightning-channel-teardown.sh
Executable file
12
BTCPayServer.Tests/docker-lightning-channel-teardown.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
channels=$(./docker-merchant-lightning-cli.sh listchannels | jq -cr '.channels | map(.short_channel_id) | unique')
|
||||
printf "Channels: %s\n\r" $channels
|
||||
|
||||
for chanid in $(echo "${channels}" | jq -cr '.[]')
|
||||
do
|
||||
printf "Closing channel ID: %s\n\r" $chanid
|
||||
./docker-merchant-lightning-cli.sh close $chanid
|
||||
./docker-bitcoin-generate.sh 20 > /dev/null
|
||||
done
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
|
||||
|
@ -46,7 +46,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.Hwi" Version="1.1.3" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.2.0" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.2.4" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="3.2.435" />
|
||||
<PackageReference Include="BundlerMinifier.Core" Version="3.2.435" />
|
||||
<PackageReference Include="BundlerMinifier.TagHelpers" Version="3.2.435" />
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@model BTCPayServer.Services.Notifications.NotificationSummaryViewModel
|
||||
@inject LinkGenerator linkGenerator
|
||||
@model BTCPayServer.Components.NotificationsDropdown.NotificationSummaryViewModel
|
||||
|
||||
@if (Model.UnseenCount > 0)
|
||||
{
|
||||
|
@ -31,3 +32,36 @@ else
|
|||
</a>
|
||||
</li>
|
||||
}
|
||||
<script type="text/javascript">
|
||||
|
||||
var supportsWebSockets = 'WebSocket' in window && window.WebSocket.CLOSING === 2;
|
||||
|
||||
if (supportsWebSockets) {
|
||||
|
||||
var loc = window.location, ws_uri;
|
||||
if (loc.protocol === "https:") {
|
||||
ws_uri = "wss:";
|
||||
} else {
|
||||
ws_uri = "ws:";
|
||||
}
|
||||
ws_uri += "//" + loc.host;
|
||||
ws_uri += "@linkGenerator.GetPathByAction("SubscribeUpdates", "Notifications")";
|
||||
var newDataEndpoint = "@linkGenerator.GetPathByAction("GetNotificationDropdownUI", "Notifications")";
|
||||
|
||||
try {
|
||||
socket = new WebSocket(ws_uri);
|
||||
socket.onmessage = function (e) {
|
||||
$.get(newDataEndpoint, function(data){
|
||||
$("#notifications-nav-item").replaceWith($(data));
|
||||
});
|
||||
};
|
||||
socket.onerror = function (e) {
|
||||
console.error("Error while connecting to websocket for notifications (callback)", e);
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.error("Error while connecting to websocket for notifications", e);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Notifications;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Components.NotificationsDropdown
|
||||
{
|
||||
public class NotificationsDropdown : ViewComponent
|
||||
{
|
||||
private readonly NotificationManager _notificationManager;
|
||||
|
||||
public NotificationsDropdown(NotificationManager notificationManager)
|
||||
{
|
||||
_notificationManager = notificationManager;
|
||||
}
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync()
|
||||
{
|
||||
return View(await _notificationManager.GetSummaryNotifications(UserClaimsPrincipal));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Models.NotificationViewModels;
|
||||
|
||||
namespace BTCPayServer.Components.NotificationsDropdown
|
||||
{
|
||||
public class NotificationSummaryViewModel
|
||||
{
|
||||
public int UnseenCount { get; set; }
|
||||
public List<NotificationViewModel> Last5 { get; set; }
|
||||
}
|
||||
}
|
20
BTCPayServer/Components/Pager/Pager.cs
Normal file
20
BTCPayServer/Components/Pager/Pager.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Components
|
||||
{
|
||||
public class Pager : ViewComponent
|
||||
{
|
||||
public Pager()
|
||||
{
|
||||
}
|
||||
public IViewComponentResult Invoke(BasePagingViewModel viewModel)
|
||||
{
|
||||
return View(viewModel);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -92,7 +92,7 @@ namespace BTCPayServer.Configuration
|
|||
var networkProvider = new BTCPayNetworkProvider(NetworkType);
|
||||
var filtered = networkProvider.Filter(supportedChains.ToArray());
|
||||
#if ALTCOINS
|
||||
supportedChains.AddRange(filtered.GetAllElementsSubChains());
|
||||
supportedChains.AddRange(filtered.GetAllElementsSubChains(networkProvider));
|
||||
#endif
|
||||
#if !ALTCOINS
|
||||
var onlyBTC = supportedChains.Count == 1 && supportedChains.First() == "BTC";
|
||||
|
|
|
@ -135,7 +135,7 @@ namespace BTCPayServer.Controllers
|
|||
else
|
||||
{
|
||||
var paymentMethods = invoice.GetBlob(_NetworkProvider).GetPaymentMethods();
|
||||
var options = invoice.GetBlob(_NetworkProvider).GetPaymentMethods()
|
||||
var options = paymentMethods
|
||||
.Select(o => o.GetId())
|
||||
.Select(o => o.CryptoCode)
|
||||
.Where(o => _NetworkProvider.GetNetwork<BTCPayNetwork>(o) is BTCPayNetwork n && !n.ReadonlyWallet)
|
||||
|
@ -143,14 +143,15 @@ namespace BTCPayServer.Controllers
|
|||
.OrderBy(o => o)
|
||||
.Select(o => new PaymentMethodId(o, PaymentTypes.BTCLike))
|
||||
.ToList();
|
||||
var defaultRefund = invoice.Payments.Select(p => p.GetBlob(_NetworkProvider))
|
||||
.Select(p => p.GetPaymentMethodId().CryptoCode)
|
||||
.FirstOrDefault();
|
||||
var defaultRefund = invoice.Payments
|
||||
.Select(p => p.GetBlob(_NetworkProvider))
|
||||
.Select(p => p?.GetPaymentMethodId())
|
||||
.FirstOrDefault(p => p != null && p.PaymentType == BitcoinPaymentType.Instance);
|
||||
// TODO: What if no option?
|
||||
var refund = new RefundModel();
|
||||
refund.Title = "Select a payment method";
|
||||
refund.AvailablePaymentMethods = new SelectList(options, nameof(PaymentMethodId.CryptoCode), nameof(PaymentMethodId.CryptoCode));
|
||||
refund.SelectedPaymentMethod = defaultRefund ?? options.Select(o => o.CryptoCode).First();
|
||||
refund.SelectedPaymentMethod = defaultRefund?.ToString() ?? options.Select(o => o.CryptoCode).First();
|
||||
|
||||
// Nothing to select, skip to next
|
||||
if (refund.AvailablePaymentMethods.Count() == 1)
|
||||
|
|
|
@ -15,22 +15,6 @@ using Microsoft.AspNetCore.Mvc;
|
|||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
|
||||
public class NotificationsDropdown : ViewComponent
|
||||
{
|
||||
private readonly NotificationManager _notificationManager;
|
||||
|
||||
public NotificationsDropdown(NotificationManager notificationManager)
|
||||
{
|
||||
_notificationManager = notificationManager;
|
||||
}
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync(int noOfEmployee)
|
||||
{
|
||||
return View(await _notificationManager.GetSummaryNotifications(UserClaimsPrincipal));
|
||||
}
|
||||
}
|
||||
|
||||
[BitpayAPIConstraint(false)]
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Route("[controller]/[action]")]
|
||||
|
|
|
@ -292,18 +292,21 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound();
|
||||
}
|
||||
|
||||
var invoice = result.Invoices.SingleOrDefault(requestInvoice =>
|
||||
var invoices = result.Invoices.Where(requestInvoice =>
|
||||
requestInvoice.Status.Equals(InvoiceState.ToString(InvoiceStatus.New),
|
||||
StringComparison.InvariantCulture) && !requestInvoice.Payments.Any());
|
||||
|
||||
if (invoice == null)
|
||||
if (!invoices.Any())
|
||||
{
|
||||
return BadRequest("No unpaid pending invoice to cancel");
|
||||
}
|
||||
|
||||
await _InvoiceRepository.UpdatePaidInvoiceToInvalid(invoice.Id);
|
||||
_EventAggregator.Publish(new InvoiceEvent(await _InvoiceRepository.GetInvoice(invoice.Id), 1008,
|
||||
InvoiceEvent.MarkedInvalid));
|
||||
foreach (var invoice in invoices)
|
||||
{
|
||||
await _InvoiceRepository.UpdatePaidInvoiceToInvalid(invoice.Id);
|
||||
_EventAggregator.Publish(new InvoiceEvent(await _InvoiceRepository.GetInvoice(invoice.Id), 1008,
|
||||
InvoiceEvent.MarkedInvalid));
|
||||
}
|
||||
|
||||
if (redirect)
|
||||
{
|
||||
|
|
|
@ -133,7 +133,7 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
{
|
||||
Message = $"You posted a claim of {_currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency)} to {vm.Destination}, this will get fullfilled later.",
|
||||
Message = $"Your claim request of {_currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency)} to {vm.Destination} has been submitted and is awaiting approval.",
|
||||
Severity = StatusMessageModel.StatusSeverity.Success
|
||||
});
|
||||
}
|
||||
|
|
|
@ -285,14 +285,15 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound();
|
||||
|
||||
var admins = await _UserManager.GetUsersInRoleAsync(Roles.ServerAdmin);
|
||||
if (!viewModel.IsAdmin && admins.Count == 1)
|
||||
var roles = await _UserManager.GetRolesAsync(user);
|
||||
var wasAdmin = IsAdmin(roles);
|
||||
if (!viewModel.IsAdmin && admins.Count == 1 && wasAdmin)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = "This is the only Admin, so their role can't be removed until another Admin is added.";
|
||||
return View(viewModel); // return
|
||||
}
|
||||
|
||||
var roles = await _UserManager.GetRolesAsync(user);
|
||||
if (viewModel.IsAdmin != IsAdmin(roles))
|
||||
if (viewModel.IsAdmin != wasAdmin)
|
||||
{
|
||||
if (viewModel.IsAdmin)
|
||||
await _UserManager.AddToRoleAsync(user, Roles.ServerAdmin);
|
||||
|
@ -571,15 +572,14 @@ namespace BTCPayServer.Controllers
|
|||
[Route("server/services/{serviceName}/{cryptoCode?}")]
|
||||
public async Task<IActionResult> Service(string serviceName, string cryptoCode, bool showQR = false, uint? nonce = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(cryptoCode) && !_dashBoard.IsFullySynched(cryptoCode, out _))
|
||||
var service = GetService(serviceName, cryptoCode);
|
||||
if (service == null)
|
||||
return NotFound();
|
||||
if (!string.IsNullOrEmpty(cryptoCode) && !_dashBoard.IsFullySynched(cryptoCode, out _) && service.Type != ExternalServiceTypes.RPC)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"{cryptoCode} is not fully synched";
|
||||
return RedirectToAction(nameof(Services));
|
||||
}
|
||||
var service = GetService(serviceName, cryptoCode);
|
||||
if (service == null)
|
||||
return NotFound();
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
|
|
@ -1160,6 +1160,7 @@ namespace BTCPayServer.Controllers
|
|||
CryptoCode = walletId.CryptoCode,
|
||||
Mnemonic = seed,
|
||||
IsStored = true,
|
||||
RequireConfirm = false,
|
||||
ReturnUrl = Url.Action(nameof(WalletSettings), new { walletId })
|
||||
};
|
||||
return this.RedirectToRecoverySeedBackup(recoveryVm);
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace BTCPayServer.Data
|
|||
PaymentEntity paymentEntity = null;
|
||||
if (network == null)
|
||||
{
|
||||
paymentEntity = NBitcoin.JsonConverters.Serializer.ToObject<PaymentEntity>(unziped, null);
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -15,8 +15,7 @@ namespace BTCPayServer.Data
|
|||
public static PaymentMethodId GetDefaultPaymentId(this StoreData storeData, BTCPayNetworkProvider networks)
|
||||
{
|
||||
PaymentMethodId[] paymentMethodIds = storeData.GetEnabledPaymentIds(networks);
|
||||
|
||||
var defaultPaymentId = string.IsNullOrEmpty(storeData.DefaultCrypto) ? null : PaymentMethodId.Parse(storeData.DefaultCrypto);
|
||||
PaymentMethodId.TryParse(storeData.DefaultCrypto, out var defaultPaymentId);
|
||||
var chosen = paymentMethodIds.FirstOrDefault(f => f == defaultPaymentId) ??
|
||||
paymentMethodIds.FirstOrDefault(f => f.CryptoCode == defaultPaymentId?.CryptoCode) ??
|
||||
paymentMethodIds.FirstOrDefault();
|
||||
|
@ -80,7 +79,10 @@ namespace BTCPayServer.Data
|
|||
JObject strategies = JObject.Parse(storeData.DerivationStrategies);
|
||||
foreach (var strat in strategies.Properties())
|
||||
{
|
||||
var paymentMethodId = PaymentMethodId.Parse(strat.Name);
|
||||
if (!PaymentMethodId.TryParse(strat.Name, out var paymentMethodId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var network = networks.GetNetwork<BTCPayNetworkBase>(paymentMethodId.CryptoCode);
|
||||
if (network != null)
|
||||
{
|
||||
|
|
|
@ -117,8 +117,9 @@ namespace BTCPayServer
|
|||
public static IEnumerable<BitcoinLikePaymentData> GetAllBitcoinPaymentData(this InvoiceEntity invoice)
|
||||
{
|
||||
return invoice.GetPayments()
|
||||
.Where(p => p.GetPaymentMethodId().PaymentType == PaymentTypes.BTCLike)
|
||||
.Select(p => (BitcoinLikePaymentData)p.GetCryptoPaymentData());
|
||||
.Where(p => p.GetPaymentMethodId()?.PaymentType == PaymentTypes.BTCLike)
|
||||
.Select(p => (BitcoinLikePaymentData)p.GetCryptoPaymentData())
|
||||
.Where(data => data != null);
|
||||
}
|
||||
|
||||
public static async Task<Dictionary<uint256, TransactionResult>> GetTransactions(this BTCPayWallet client, uint256[] hashes, bool includeOffchain = false, CancellationToken cts = default(CancellationToken))
|
||||
|
@ -445,6 +446,7 @@ namespace BTCPayServer
|
|||
new KeyValuePair<string, string>("mnemonic", vm.Mnemonic),
|
||||
new KeyValuePair<string, string>("passphrase", vm.Passphrase),
|
||||
new KeyValuePair<string, string>("isStored", vm.IsStored ? "true" : "false"),
|
||||
new KeyValuePair<string, string>("requireConfirm", vm.RequireConfirm ? "true" : "false"),
|
||||
new KeyValuePair<string, string>("returnUrl", vm.ReturnUrl)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace BTCPayServer.HostedServices
|
|||
protected override async Task ProcessEvent(object evt, CancellationToken cancellationToken)
|
||||
{
|
||||
if (evt is InvoiceEvent invoiceEvent && invoiceEvent.Name == InvoiceEvent.ReceivedPayment &&
|
||||
invoiceEvent.Payment.GetPaymentMethodId().PaymentType == BitcoinPaymentType.Instance &&
|
||||
invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType == BitcoinPaymentType.Instance &&
|
||||
invoiceEvent.Payment.GetCryptoPaymentData() is BitcoinLikePaymentData bitcoinLikePaymentData)
|
||||
{
|
||||
var walletId = new WalletId(invoiceEvent.Invoice.StoreId, invoiceEvent.Payment.GetCryptoCode());
|
||||
|
|
|
@ -81,6 +81,12 @@ namespace BTCPayServer.Hosting
|
|||
return builtInFactory(context);
|
||||
};
|
||||
})
|
||||
.AddRazorOptions(o =>
|
||||
{
|
||||
// /Components/{View Component Name}/{View Name}.cshtml
|
||||
o.ViewLocationFormats.Add("/{0}.cshtml");
|
||||
o.PageViewLocationFormats.Add("/{0}.cshtml");
|
||||
})
|
||||
.AddNewtonsoftJson()
|
||||
#if RAZOR_RUNTIME_COMPILE
|
||||
.AddRazorRuntimeCompilation()
|
||||
|
|
|
@ -55,7 +55,7 @@ namespace BTCPayServer.Models.AppViewModels
|
|||
}
|
||||
public class Contribution
|
||||
{
|
||||
public PaymentMethodId PaymentMehtodId { get; set; }
|
||||
public PaymentMethodId PaymentMethodId { get; set; }
|
||||
public decimal Value { get; set; }
|
||||
public decimal CurrencyValue { get; set; }
|
||||
}
|
||||
|
|
|
@ -10,8 +10,9 @@ namespace BTCPayServer.Models.StoreViewModels
|
|||
public string CryptoCode { get; set; }
|
||||
public string Mnemonic { get; set; }
|
||||
public string Passphrase { get; set; }
|
||||
public bool IsStored { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
public bool IsStored { get; set; }
|
||||
public bool RequireConfirm { get; set; } = true;
|
||||
|
||||
public string[] Words
|
||||
{
|
||||
|
|
|
@ -159,7 +159,7 @@ namespace BTCPayServer.PaymentRequest
|
|||
{
|
||||
data.GetValue(),
|
||||
invoiceEvent.Payment.GetCryptoCode(),
|
||||
invoiceEvent.Payment.GetPaymentMethodId().PaymentType.ToString()
|
||||
invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType?.ToString()
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,8 @@ namespace BTCPayServer.PaymentRequest
|
|||
|
||||
var paymentStats = _AppService.GetContributionsByPaymentMethodId(blob.Currency, invoices, true);
|
||||
var amountDue = blob.Amount - paymentStats.TotalCurrency;
|
||||
var pendingInvoice = invoices.SingleOrDefault(entity => entity.Status == InvoiceStatus.New);
|
||||
var pendingInvoice = invoices.OrderByDescending(entity => entity.InvoiceTime)
|
||||
.FirstOrDefault(entity => entity.Status == InvoiceStatus.New);
|
||||
|
||||
return new ViewPaymentRequestViewModel(pr)
|
||||
{
|
||||
|
@ -103,10 +104,16 @@ namespace BTCPayServer.PaymentRequest
|
|||
Currency = entity.ProductInformation.Currency,
|
||||
ExpiryDate = entity.ExpirationTime.DateTime,
|
||||
Status = entity.GetInvoiceState().ToString(),
|
||||
Payments = entity.GetPayments().Select(paymentEntity =>
|
||||
Payments = entity
|
||||
.GetPayments()
|
||||
.Select(paymentEntity =>
|
||||
{
|
||||
var paymentData = paymentEntity.GetCryptoPaymentData();
|
||||
var paymentMethodId = paymentEntity.GetPaymentMethodId();
|
||||
if (paymentData is null || paymentMethodId is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string txId = paymentData.GetPaymentId();
|
||||
string link = GetTransactionLink(paymentMethodId, txId);
|
||||
|
@ -117,7 +124,9 @@ namespace BTCPayServer.PaymentRequest
|
|||
Link = link,
|
||||
Id = txId
|
||||
};
|
||||
}).ToList()
|
||||
})
|
||||
.Where(payment => payment != null)
|
||||
.ToList()
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
|||
var paymentEntitiesByPrevOut = new Dictionary<OutPoint, PaymentEntity>();
|
||||
foreach (var payment in invoice.GetPayments(wallet.Network))
|
||||
{
|
||||
if (payment.GetPaymentMethodId().PaymentType != PaymentTypes.BTCLike)
|
||||
if (payment.GetPaymentMethodId()?.PaymentType != PaymentTypes.BTCLike)
|
||||
continue;
|
||||
var paymentData = (BitcoinLikePaymentData)payment.GetCryptoPaymentData();
|
||||
if (!transactions.TryGetValue(paymentData.Outpoint.Hash, out TransactionResult tx))
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace BTCPayServer.Payments
|
|||
|
||||
public static bool TryParse(string str, out PaymentMethodId paymentMethodId)
|
||||
{
|
||||
str ??= "";
|
||||
paymentMethodId = null;
|
||||
var parts = str.Split('_', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length == 0 || parts.Length > 2)
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace BTCPayServer.Payments
|
|||
|
||||
public override CryptoPaymentData DeserializePaymentData(BTCPayNetworkBase network, string str)
|
||||
{
|
||||
return ((BTCPayNetwork)network).ToObject<BitcoinLikePaymentData>(str);
|
||||
return ((BTCPayNetwork)network)?.ToObject<BitcoinLikePaymentData>(str);
|
||||
}
|
||||
|
||||
public override string SerializePaymentData(BTCPayNetworkBase network, CryptoPaymentData paymentData)
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace BTCPayServer.Payments
|
|||
|
||||
public override CryptoPaymentData DeserializePaymentData(BTCPayNetworkBase network, string str)
|
||||
{
|
||||
return ((BTCPayNetwork)network).ToObject<LightningLikePaymentData>(str);
|
||||
return ((BTCPayNetwork)network)?.ToObject<LightningLikePaymentData>(str);
|
||||
}
|
||||
|
||||
public override string SerializePaymentData(BTCPayNetworkBase network, CryptoPaymentData paymentData)
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace BTCPayServer.Services.Apps
|
|||
{
|
||||
data.GetValue(),
|
||||
invoiceEvent.Payment.GetCryptoCode(),
|
||||
invoiceEvent.Payment.GetPaymentMethodId().PaymentType.ToString()
|
||||
invoiceEvent.Payment.GetPaymentMethodId()?.PaymentType?.ToString()
|
||||
}, cancellationToken);
|
||||
}
|
||||
await InfoUpdated(appId);
|
||||
|
|
|
@ -182,7 +182,7 @@ namespace BTCPayServer.Services.Apps
|
|||
});
|
||||
|
||||
// Old invoices may have invoices which were not tagged
|
||||
invoices = invoices.Where(inv => inv.Version < InvoiceEntity.InternalTagSupport_Version ||
|
||||
invoices = invoices.Where(inv => appData.TagAllInvoices || inv.Version < InvoiceEntity.InternalTagSupport_Version ||
|
||||
inv.InternalTags.Contains(GetAppInternalTag(appData.Id))).ToArray();
|
||||
return invoices;
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ namespace BTCPayServer.Services.Apps
|
|||
.SelectMany(p =>
|
||||
{
|
||||
var contribution = new Contribution();
|
||||
contribution.PaymentMehtodId = new PaymentMethodId(p.ProductInformation.Currency, PaymentTypes.BTCLike);
|
||||
contribution.PaymentMethodId = new PaymentMethodId(p.ProductInformation.Currency, PaymentTypes.BTCLike);
|
||||
contribution.CurrencyValue = p.ProductInformation.Price;
|
||||
contribution.Value = contribution.CurrencyValue;
|
||||
|
||||
|
@ -363,18 +363,18 @@ namespace BTCPayServer.Services.Apps
|
|||
.Select(pay =>
|
||||
{
|
||||
var paymentMethodContribution = new Contribution();
|
||||
paymentMethodContribution.PaymentMehtodId = pay.GetPaymentMethodId();
|
||||
paymentMethodContribution.PaymentMethodId = pay.GetPaymentMethodId();
|
||||
paymentMethodContribution.Value = pay.GetCryptoPaymentData().GetValue() - pay.NetworkFee;
|
||||
var rate = p.GetPaymentMethod(paymentMethodContribution.PaymentMehtodId).Rate;
|
||||
var rate = p.GetPaymentMethod(paymentMethodContribution.PaymentMethodId).Rate;
|
||||
paymentMethodContribution.CurrencyValue = rate * paymentMethodContribution.Value;
|
||||
return paymentMethodContribution;
|
||||
})
|
||||
.ToArray();
|
||||
})
|
||||
.GroupBy(p => p.PaymentMehtodId)
|
||||
.GroupBy(p => p.PaymentMethodId)
|
||||
.ToDictionary(p => p.Key, p => new Contribution()
|
||||
{
|
||||
PaymentMehtodId = p.Key,
|
||||
PaymentMethodId = p.Key,
|
||||
Value = p.Select(v => v.Value).Sum(),
|
||||
CurrencyValue = p.Select(v => v.CurrencyValue).Sum()
|
||||
});
|
||||
|
|
|
@ -47,10 +47,8 @@ namespace BTCPayServer.Services.Invoices.Export
|
|||
using StringWriter writer = new StringWriter();
|
||||
using var csvWriter = new CsvHelper.CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture), true);
|
||||
csvWriter.WriteHeader<ExportInvoiceHolder>();
|
||||
foreach (var invoice in invoices)
|
||||
{
|
||||
csvWriter.WriteRecord(invoice);
|
||||
}
|
||||
csvWriter.NextRecord();
|
||||
csvWriter.WriteRecords(invoices);
|
||||
csvWriter.Flush();
|
||||
return writer.ToString();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ using BTCPayServer.JsonConverters;
|
|||
using BTCPayServer.Models;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
using NBitpayClient;
|
||||
|
@ -266,11 +267,11 @@ namespace BTCPayServer.Services.Invoices
|
|||
#pragma warning disable CS0618
|
||||
public List<PaymentEntity> GetPayments()
|
||||
{
|
||||
return Payments?.ToList() ?? new List<PaymentEntity>();
|
||||
return Payments?.Where(entity => entity.GetPaymentMethodId() != null).ToList() ?? new List<PaymentEntity>();
|
||||
}
|
||||
public List<PaymentEntity> GetPayments(string cryptoCode)
|
||||
{
|
||||
return Payments.Where(p => p.CryptoCode == cryptoCode).ToList();
|
||||
return GetPayments().Where(p => p.CryptoCode == cryptoCode).ToList();
|
||||
}
|
||||
public List<PaymentEntity> GetPayments(BTCPayNetworkBase network)
|
||||
{
|
||||
|
@ -299,8 +300,8 @@ namespace BTCPayServer.Services.Invoices
|
|||
|
||||
private Uri FillPlaceholdersUri(string v)
|
||||
{
|
||||
var uriStr = (v ?? string.Empty).Replace("{OrderId}", OrderId ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{InvoiceId}", Id ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
var uriStr = (v ?? string.Empty).Replace("{OrderId}", System.Web.HttpUtility.UrlEncode(OrderId) ?? "", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("{InvoiceId}", System.Web.HttpUtility.UrlEncode(Id) ?? "", StringComparison.OrdinalIgnoreCase);
|
||||
if (Uri.TryCreate(uriStr, UriKind.Absolute, out var uri) && (uri.Scheme == "http" || uri.Scheme == "https"))
|
||||
return uri;
|
||||
return null;
|
||||
|
@ -550,7 +551,10 @@ namespace BTCPayServer.Services.Invoices
|
|||
foreach (var prop in PaymentMethod.Properties())
|
||||
{
|
||||
var r = serializer.ToObject<PaymentMethod>(prop.Value.ToString());
|
||||
var paymentMethodId = PaymentMethodId.Parse(prop.Name);
|
||||
if (!PaymentMethodId.TryParse(prop.Name, out var paymentMethodId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
r.CryptoCode = paymentMethodId.CryptoCode;
|
||||
r.PaymentType = paymentMethodId.PaymentType.ToString();
|
||||
r.ParentEntity = this;
|
||||
|
@ -1005,7 +1009,18 @@ namespace BTCPayServer.Services.Invoices
|
|||
}
|
||||
else
|
||||
{
|
||||
paymentData = GetPaymentMethodId().PaymentType.DeserializePaymentData(Network, CryptoPaymentData);
|
||||
var paymentMethodId = GetPaymentMethodId();
|
||||
if (paymentMethodId is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
paymentData = paymentMethodId.PaymentType.DeserializePaymentData(Network, CryptoPaymentData);
|
||||
if (paymentData is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
paymentData.Network = Network;
|
||||
if (paymentData is BitcoinLikePaymentData bitcoin)
|
||||
{
|
||||
|
@ -1050,7 +1065,16 @@ namespace BTCPayServer.Services.Invoices
|
|||
public PaymentMethodId GetPaymentMethodId()
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
return new PaymentMethodId(CryptoCode ?? "BTC", string.IsNullOrEmpty(CryptoPaymentDataType) ? PaymentTypes.BTCLike : PaymentTypes.Parse(CryptoPaymentDataType));
|
||||
PaymentType paymentType;
|
||||
if (string.IsNullOrEmpty(CryptoPaymentDataType))
|
||||
{
|
||||
paymentType = BitcoinPaymentType.Instance;;
|
||||
}
|
||||
else if(!PaymentTypes.TryParse(CryptoPaymentDataType, out paymentType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new PaymentMethodId(CryptoCode ?? "BTC", paymentType);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
|
|
|
@ -484,6 +484,8 @@ retry:
|
|||
entity.Payments = invoice.Payments.Select(p =>
|
||||
{
|
||||
var paymentEntity = p.GetBlob(_Networks);
|
||||
if (paymentEntity is null)
|
||||
return null;
|
||||
// PaymentEntity on version 0 does not have their own fee, because it was assumed that the payment method have fixed fee.
|
||||
// We want to hide this legacy detail in InvoiceRepository, so we fetch the fee from the PaymentMethod and assign it to the PaymentEntity.
|
||||
if (paymentEntity.Version == 0)
|
||||
|
@ -497,6 +499,7 @@ retry:
|
|||
|
||||
return paymentEntity;
|
||||
})
|
||||
.Where(p => p != null)
|
||||
.OrderBy(a => a.ReceivedTime).ToList();
|
||||
#pragma warning restore CS0618
|
||||
var state = invoice.GetInvoiceState();
|
||||
|
@ -701,7 +704,7 @@ retry:
|
|||
Accounted = accounted
|
||||
};
|
||||
|
||||
context.Payments.Add(data);
|
||||
await context.Payments.AddAsync(data);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace BTCPayServer.Services.Notifications.Blobs
|
|||
public override string NotificationType => "payout";
|
||||
protected override void FillViewModel(PayoutNotification notification, NotificationViewModel vm)
|
||||
{
|
||||
vm.Body = "A new payout is awaiting for payment";
|
||||
vm.Body = "A new payout is awaiting for approval";
|
||||
vm.ActionLink = _linkGenerator.GetPathByAction(nameof(WalletsController.Payouts),
|
||||
"Wallets",
|
||||
new { walletId = new WalletId(notification.StoreId, notification.PaymentMethod) }, _options.RootPath);
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Components.NotificationsDropdown;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.NotificationViewModels;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
@ -124,10 +125,4 @@ namespace BTCPayServer.Services.Notifications
|
|||
throw new InvalidOperationException($"No INotificationHandler found for {blobType.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public class NotificationSummaryViewModel
|
||||
{
|
||||
public int UnseenCount { get; set; }
|
||||
public List<NotificationViewModel> Last5 { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
@if (env.OnionUrl != null)
|
||||
{
|
||||
<div class="text-center">
|
||||
<a href="@env.OnionUrl" target="_onion" class="btn btn-sm btn-outline-onion d-inline-flex align-items-center p-2" data-clipboard="@env.OnionUrl">
|
||||
<a href="@env.OnionUrl" target="_onion" class="btn btn-sm btn-outline-onion d-inline-flex align-items-center text-nowrap p-2" data-clipboard="@env.OnionUrl">
|
||||
<img src="~/img/icons/onion-purple.svg" height="20" class="mr-2" asp-append-version="true" />
|
||||
Copy Tor URL
|
||||
</a>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
@if (env.OnionUrl != null)
|
||||
{
|
||||
<div class="text-center">
|
||||
<a href="@env.OnionUrl" target="_onion" class="btn btn-sm btn-outline-onion d-inline-flex align-items-center p-2" data-clipboard="@env.OnionUrl">
|
||||
<a href="@env.OnionUrl" target="_onion" class="btn btn-sm btn-outline-onion d-inline-flex align-items-center text-nowrap p-2" data-clipboard="@env.OnionUrl">
|
||||
<img src="~/img/icons/onion-purple.svg" height="20" class="mr-2" asp-append-version="true" />
|
||||
Copy Tor URL
|
||||
</a>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
Do not photograph it. Do not store it digitally.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The recovery phrase will also be stored on a server as a hot wallet.</strong>
|
||||
<strong>The recovery phrase will also be stored on the server as a hot wallet.</strong>
|
||||
</p>
|
||||
}
|
||||
else
|
||||
|
@ -65,11 +65,18 @@
|
|||
<p class="mt-3 mb-0">Please make sure to also write down your passphrase.</p>
|
||||
}
|
||||
</div>
|
||||
<form id="recovery-confirmation" action="@Model.ReturnUrl" class="d-flex align-items-start justify-content-center" style="margin-top:4rem;padding-bottom: 80px">
|
||||
<label class="form-check-label lead order-2" for="confirm">I have written down my recovery phrase and stored it in a secure location</label>
|
||||
<input type="checkbox" class="mt-2 mr-3 order-1" id="confirm">
|
||||
<button type="submit" class="btn btn-primary btn-lg px-5 order-3" id="submit">Done</button>
|
||||
<button type="submit" class="btn btn-primary btn-lg px-5 order-3" disabled>Done</button>
|
||||
</form>
|
||||
@if (Model.RequireConfirm)
|
||||
{
|
||||
<form id="recovery-confirmation" action="@Model.ReturnUrl" class="d-flex align-items-start justify-content-center" style="margin-top:4rem;padding-bottom: 80px">
|
||||
<label class="form-check-label lead order-2" for="confirm">I have written down my recovery phrase and stored it in a secure location</label>
|
||||
<input type="checkbox" class="mt-2 mr-3 order-1" id="confirm">
|
||||
<button type="submit" class="btn btn-primary btn-lg px-5 order-3" id="submit">Done</button>
|
||||
<button type="submit" class="btn btn-primary btn-lg px-5 order-3" disabled>Done</button>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@Model.ReturnUrl" class="btn btn-primary btn-lg mt-3 px-5 order-3" id="proceed">Done</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
<!-- needed for adaptive design -->
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
<link href="~/main/fonts/Roboto.css" rel="stylesheet" asp-append-version="true">
|
||||
<link href="~/main/fonts/Montserrat.css" rel="stylesheet" asp-append-version="true">
|
||||
|
||||
<!--
|
||||
ReDoc doesn't change outer page styles
|
||||
|
|
|
@ -5,16 +5,17 @@
|
|||
@section HeadScripts {
|
||||
<script src="~/modal/btcpay.js" asp-append-version="true"></script>
|
||||
}
|
||||
|
||||
@Html.HiddenFor(a => a.Count)
|
||||
<section>
|
||||
<div class="container">
|
||||
@if (TempData.HasStatusMessage())
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
<partial name="_StatusMessage" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 text-center">
|
||||
<partial name="_StatusMessage" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
|
@ -49,7 +50,7 @@
|
|||
<input type="hidden" asp-for="Count" />
|
||||
<div class="input-group ">
|
||||
<input asp-for="TimezoneOffset" type="hidden" />
|
||||
<input asp-for="SearchTerm" class="form-control"/>
|
||||
<input asp-for="SearchTerm" class="form-control" />
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary" title="Search invoice">
|
||||
<span class="fa fa-search"></span> Search
|
||||
|
@ -91,7 +92,7 @@
|
|||
<div>
|
||||
<a asp-action="CreateInvoice" class="btn btn-primary mb-1" role="button" id="CreateNewInvoice"><span class="fa fa-plus"></span> Create a new invoice</a>
|
||||
|
||||
<span >
|
||||
<span>
|
||||
<button class="btn btn-primary dropdown-toggle mb-1" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Actions
|
||||
</button>
|
||||
|
@ -99,7 +100,7 @@
|
|||
<button type="submit" asp-action="MassAction" class="dropdown-item" name="command" value="archive"><i class="fa fa-archive"></i> Archive</button>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
|
||||
<span>
|
||||
<a class="btn btn-primary dropdown-toggle mb-1" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Export
|
||||
|
@ -114,7 +115,7 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<br />
|
||||
@* Custom Range Modal *@
|
||||
<div class="modal fade" id="customRangeModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered" role="document" style="max-width: 550px;">
|
||||
|
@ -131,8 +132,8 @@
|
|||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="dtpStartDate" class="form-control flatdtpicker" type="datetime-local"
|
||||
data-fdtp='{ "enableTime": true, "enableSeconds": true, "dateFormat": "Y-m-d H:i:S", "time_24hr": true, "defaultHour": 0 }'
|
||||
placeholder="Start Date" />
|
||||
data-fdtp='{ "enableTime": true, "enableSeconds": true, "dateFormat": "Y-m-d H:i:S", "time_24hr": true, "defaultHour": 0 }'
|
||||
placeholder="Start Date" />
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-primary input-group-clear" title="Clear">
|
||||
<span class=" fa fa-times"></span>
|
||||
|
@ -146,8 +147,8 @@
|
|||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input id="dtpEndDate" class="form-control flatdtpicker" type="datetime-local"
|
||||
data-fdtp='{ "enableTime": true, "enableSeconds": true, "dateFormat": "Y-m-d H:i:S", "time_24hr": true, "defaultHour": 0 }'
|
||||
placeholder="End Date" />
|
||||
data-fdtp='{ "enableTime": true, "enableSeconds": true, "dateFormat": "Y-m-d H:i:S", "time_24hr": true, "defaultHour": 0 }'
|
||||
placeholder="End Date" />
|
||||
<div class="input-group-append">
|
||||
<button type="button" class="btn btn-primary input-group-clear" title="Clear">
|
||||
<span class=" fa fa-times"></span>
|
||||
|
@ -194,8 +195,7 @@
|
|||
@* Custom Range Modal *@
|
||||
|
||||
<script type="text/javascript">
|
||||
function selectAll(e)
|
||||
{
|
||||
function selectAll(e) {
|
||||
var items = document.getElementsByClassName("selector");
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
items[i].checked = e.checked;
|
||||
|
@ -210,7 +210,7 @@
|
|||
<th class="only-for-js">
|
||||
@if (Model.Total > 0)
|
||||
{
|
||||
<input id="selectAllCheckbox" type="checkbox" onclick="selectAll(this);" />
|
||||
<input id="selectAllCheckbox" type="checkbox" onclick="selectAll(this);" />
|
||||
}
|
||||
</th>
|
||||
<th style="min-width: 90px;" class="col-md-auto">
|
||||
|
@ -229,91 +229,91 @@
|
|||
<tbody>
|
||||
@foreach (var invoice in Model.Invoices)
|
||||
{
|
||||
<tr>
|
||||
<td class="only-for-js">
|
||||
<input name="selectedItems" type="checkbox" class="selector" value="@invoice.InvoiceId" />
|
||||
</td>
|
||||
<td>
|
||||
<span class="switchTimeFormat" data-switch="@invoice.Date.ToTimeAgo()">
|
||||
@invoice.Date.ToBrowserDate()
|
||||
<tr>
|
||||
<td class="only-for-js">
|
||||
<input name="selectedItems" type="checkbox" class="selector" value="@invoice.InvoiceId" />
|
||||
</td>
|
||||
<td>
|
||||
<span class="switchTimeFormat" data-switch="@invoice.Date.ToTimeAgo()">
|
||||
@invoice.Date.ToBrowserDate()
|
||||
</span>
|
||||
</td>
|
||||
<td style="max-width: 180px;">
|
||||
@if (invoice.RedirectUrl != string.Empty)
|
||||
{
|
||||
<a href="@invoice.RedirectUrl" class="wraptext200">@invoice.OrderId</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@invoice.OrderId</span>
|
||||
}
|
||||
</td>
|
||||
<td>@invoice.InvoiceId</td>
|
||||
<td>
|
||||
@if(invoice.Details.Archived)
|
||||
{
|
||||
<span class="badge badge-warning">archived</span>
|
||||
}
|
||||
@if (invoice.CanMarkStatus)
|
||||
{
|
||||
<div id="pavpill_@invoice.InvoiceId">
|
||||
<span class="dropdown-toggle dropdown-toggle-split pavpill pavpil-@invoice.Status.ToString().ToLower()"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@invoice.StatusString
|
||||
</span>
|
||||
</td>
|
||||
<td style="max-width: 180px;">
|
||||
@if (invoice.RedirectUrl != string.Empty)
|
||||
{
|
||||
<a href="@invoice.RedirectUrl" class="wraptext200">@invoice.OrderId</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>@invoice.OrderId</span>
|
||||
}
|
||||
</td>
|
||||
<td>@invoice.InvoiceId</td>
|
||||
<td>
|
||||
@if(invoice.Details.Archived)
|
||||
{
|
||||
<span class="badge badge-warning" >archived</span>
|
||||
}
|
||||
@if (invoice.CanMarkStatus)
|
||||
{
|
||||
<div id="pavpill_@invoice.InvoiceId">
|
||||
<span class="dropdown-toggle dropdown-toggle-split pavpill pavpil-@invoice.Status.ToString().ToLower()"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
@invoice.StatusString
|
||||
</span>
|
||||
<div class="dropdown-menu pull-right">
|
||||
@if (invoice.CanMarkInvalid)
|
||||
<div class="dropdown-menu pull-right">
|
||||
@if (invoice.CanMarkInvalid)
|
||||
{
|
||||
<button class="dropdown-item small cursorPointer" onclick="changeInvoiceState(this, '@invoice.InvoiceId', 'invalid')">
|
||||
Mark as invalid <span class="fa fa-times"></span>
|
||||
</button>
|
||||
<button class="dropdown-item small cursorPointer" onclick="changeInvoiceState(this, '@invoice.InvoiceId', 'invalid')">
|
||||
Mark as invalid <span class="fa fa-times"></span>
|
||||
</button>
|
||||
}
|
||||
@if (invoice.CanMarkComplete)
|
||||
@if (invoice.CanMarkComplete)
|
||||
{
|
||||
<button class="dropdown-item small cursorPointer" onclick="changeInvoiceState(this, '@invoice.InvoiceId', 'complete')">
|
||||
Mark as complete <span class="fa fa-check-circle"></span>
|
||||
</button>
|
||||
<button class="dropdown-item small cursorPointer" onclick="changeInvoiceState(this, '@invoice.InvoiceId', 'complete')">
|
||||
Mark as complete <span class="fa fa-check-circle"></span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="pavpill pavpil-@invoice.Status.ToString().ToLower()">@invoice.StatusString</span>
|
||||
}
|
||||
</td>
|
||||
<td style="text-align:right">@invoice.AmountCurrency</td>
|
||||
<td style="text-align:right">
|
||||
@if (invoice.ShowCheckout)
|
||||
{
|
||||
<span>
|
||||
<a asp-action="Checkout" class="invoice-checkout-link" id="invoice-checkout-@invoice.InvoiceId" asp-route-invoiceId="@invoice.InvoiceId">Checkout</a>
|
||||
<a href="javascript:btcpay.showInvoice('@invoice.InvoiceId')">[^]</a>
|
||||
@if (!invoice.CanMarkStatus)
|
||||
{
|
||||
<span>-</span>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
|
||||
<a asp-action="Invoice" class="invoice-details-link" asp-route-invoiceId="@invoice.InvoiceId">Details</a>
|
||||
<a href="javascript:void(0);" onclick="detailsToggle(this, '@invoice.InvoiceId')">
|
||||
<span title="Invoice Details Toggle" class="fa fa-1x fa-angle-double-down"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="invoice_@invoice.InvoiceId" style="display:none;">
|
||||
<td colspan="99" class="border-top-0">
|
||||
<div style="margin-left: 15px; margin-bottom: 0;">
|
||||
@* Leaving this as partial because it abstracts complexity of Invoice Payments *@
|
||||
<partial name="ListInvoicesPaymentsPartial" model="(invoice.Details, true)" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="pavpill pavpil-@invoice.Status.ToString().ToLower()">@invoice.StatusString</span>
|
||||
}
|
||||
</td>
|
||||
<td style="text-align:right">@invoice.AmountCurrency</td>
|
||||
<td style="text-align:right">
|
||||
@if (invoice.ShowCheckout)
|
||||
{
|
||||
<span>
|
||||
<a asp-action="Checkout" class="invoice-checkout-link" id="invoice-checkout-@invoice.InvoiceId" asp-route-invoiceId="@invoice.InvoiceId">Checkout</a>
|
||||
<a href="javascript:btcpay.showInvoice('@invoice.InvoiceId')">[^]</a>
|
||||
@if (!invoice.CanMarkStatus)
|
||||
{
|
||||
<span>-</span>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
|
||||
<a asp-action="Invoice" class="invoice-details-link" asp-route-invoiceId="@invoice.InvoiceId">Details</a>
|
||||
<a href="javascript:void(0);" onclick="detailsToggle(this, '@invoice.InvoiceId')">
|
||||
<span title="Invoice Details Toggle" class="fa fa-1x fa-angle-double-down"></span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="invoice_@invoice.InvoiceId" style="display:none;">
|
||||
<td colspan="99" class="border-top-0">
|
||||
<div style="margin-left: 15px; margin-bottom: 0;">
|
||||
@* Leaving this as partial because it abstracts complexity of Invoice Payments *@
|
||||
<partial name="ListInvoicesPaymentsPartial" model="(invoice.Details, true)" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<partial name="_TableFooterPager" />
|
||||
<vc:pager view-model="Model"></vc:pager>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -324,7 +324,7 @@
|
|||
$(".export-link, a.dropdown-item").each(function () {
|
||||
this.href = this.href.replace("timezoneoffset=0", "timezoneoffset=" + timezoneOffset);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
function getDateStringWithOffset(hoursDiff) {
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
</div>
|
||||
</div>
|
||||
@{
|
||||
var grouped = invoice.Payments.GroupBy(payment => payment.GetPaymentMethodId().PaymentType);
|
||||
var grouped = invoice.Payments.GroupBy(payment => payment.GetPaymentMethodId()?.PaymentType).Where(entities => entities.Key!= null);
|
||||
}
|
||||
@foreach (var paymentGroup in grouped)
|
||||
{
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<partial name="_TableFooterPager" />
|
||||
<vc:pager view-model="Model"></vc:pager>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<div></div>
|
||||
@await Component.InvokeAsync("NotificationsDropdown")
|
||||
<script>
|
||||
|
||||
var supportsWebSockets = 'WebSocket' in window && window.WebSocket.CLOSING === 2;
|
||||
|
||||
if (supportsWebSockets) {
|
||||
var ws_uri = "@Url.Action("SubscribeUpdates", "Notifications", new {}, Context.Request.Scheme)".replace("http", "ws");
|
||||
var newDataEndpoint = "@Url.Action("GetNotificationDropdownUI", "Notifications", new {}, Context.Request.Scheme)";
|
||||
|
||||
try {
|
||||
socket = new WebSocket(ws_uri);
|
||||
socket.onmessage = function (e) {
|
||||
$.get(newDataEndpoint, function(data){
|
||||
$("#notifications-nav-item").replaceWith($(data));
|
||||
});
|
||||
};
|
||||
socket.onerror = function (e) {
|
||||
console.error("Error while connecting to websocket for notifications (callback)", e);
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.error("Error while connecting to websocket for notifications", e);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
|
@ -5,10 +5,14 @@
|
|||
|
||||
@{
|
||||
PayjoinInformation payjoinIformation = null;
|
||||
var onchainPayments = Model.Where(entity => entity.GetPaymentMethodId().PaymentType == BitcoinPaymentType.Instance).Select(payment =>
|
||||
var onchainPayments = Model.Where(entity => entity.GetPaymentMethodId()?.PaymentType == BitcoinPaymentType.Instance).Select(payment =>
|
||||
{
|
||||
var m = new OnchainPaymentViewModel();
|
||||
var onChainPaymentData = payment.GetCryptoPaymentData() as BitcoinLikePaymentData;
|
||||
if (onChainPaymentData is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
m.Crypto = payment.GetPaymentMethodId().CryptoCode;
|
||||
m.DepositAddress = onChainPaymentData.GetDestination();
|
||||
|
||||
|
@ -38,7 +42,7 @@
|
|||
m.Replaced = !payment.Accounted;
|
||||
m.CryptoPaymentData = onChainPaymentData;
|
||||
return m;
|
||||
});
|
||||
}).Where(model => model != null);
|
||||
}
|
||||
|
||||
@if (onchainPayments.Any())
|
||||
|
|
|
@ -3,15 +3,19 @@
|
|||
@model IEnumerable<BTCPayServer.Services.Invoices.PaymentEntity>
|
||||
|
||||
@{
|
||||
var offchainPayments = Model.Where(entity => entity.GetPaymentMethodId().PaymentType == LightningPaymentType.Instance).Select(payment =>
|
||||
var offchainPayments = Model.Where(entity => entity.GetPaymentMethodId()?.PaymentType == LightningPaymentType.Instance).Select(payment =>
|
||||
{
|
||||
var offChainPaymentData = payment.GetCryptoPaymentData() as LightningLikePaymentData;
|
||||
if (offChainPaymentData is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new OffChainPaymentViewModel()
|
||||
{
|
||||
Crypto = payment.Network.CryptoCode,
|
||||
BOLT11 = offChainPaymentData.BOLT11
|
||||
};
|
||||
});
|
||||
}).Where(model => model != null);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,37 +2,37 @@
|
|||
Our Supporters
|
||||
</h5>
|
||||
<div class="row justify-content-center mb-2">
|
||||
<div class="p-3 text-center">
|
||||
<div class="p-3 text-center" style="flex-basis:105px;">
|
||||
<a href="https://kraken.com" target="_blank" class="text-muted small">
|
||||
<img src="~/img/kraken.svg" alt="Sponsor Kraken" height="50" asp-append-version="true"/>
|
||||
<span class="d-block mt-3">Kraken</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-3 text-center">
|
||||
<div class="p-3 text-center" style="flex-basis:105px;">
|
||||
<a href="https://twitter.com/sqcrypto" target="_blank" class="text-muted small">
|
||||
<img src="~/img/squarecrypto.svg" alt="Sponsor Square Crypto" height="50" asp-append-version="true"/>
|
||||
<span class="d-block mt-3">Square Crypto</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-3 text-center">
|
||||
<div class="p-3 text-center" style="flex-basis:105px;">
|
||||
<a href="https://www.btse.com" target="_blank" class="text-muted small">
|
||||
<img src="~/img/btse.svg" alt="Sponsor BTSE" height="50" asp-append-version="true"/>
|
||||
<span class="d-block mt-3">BTSE</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-3 text-center">
|
||||
<div class="p-3 text-center" style="flex-basis:105px;">
|
||||
<a href="https://www.dglab.com/en/" target="_blank" class="text-muted small">
|
||||
<img src="~/img/dglab.svg" alt="Sponsor DG lab" height="50" asp-append-version="true"/>
|
||||
<span class="d-block mt-3">DG Lab</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-3 text-center">
|
||||
<div class="p-3 text-center" style="flex-basis:105px;">
|
||||
<a href="https://www.okcoin.com/" target="_blank" class="text-muted small">
|
||||
<img src="~/img/okcoin.svg" alt="Sponsor OKCoin" height="50" asp-append-version="true"/>
|
||||
<span class="d-block mt-3">OKCoin</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="p-3 text-center">
|
||||
<div class="p-3 text-center" style="flex-basis:105px;">
|
||||
<a href="https://acinq.co/" target="_blank" class="text-muted small">
|
||||
<img src="~/img/acinq-logo.svg" alt="Sponsor ACINQ" height="50" asp-append-version="true"/>
|
||||
<span class="d-block mt-3">ACINQ</span>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject SignInManager<ApplicationUser> SignInManager
|
||||
@inject UserManager<ApplicationUser> UserManager
|
||||
@inject RoleManager<IdentityRole> RoleManager
|
||||
@inject BTCPayServer.Services.BTCPayServerEnvironment env
|
||||
|
@ -52,12 +52,6 @@
|
|||
<span class="badge badge-warning" style="font-size:10px;">@env.NetworkType.ToString()</span>
|
||||
}
|
||||
</a>
|
||||
@if (env.OnionUrl != null)
|
||||
{
|
||||
<a class="onion" href="@env.OnionUrl" target="_blank">
|
||||
<img src="~/img/icons/onion.svg" width="26" height="32" asp-append-version="true" />
|
||||
</a>
|
||||
}
|
||||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<svg class="navbar-toggler-icon" viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'><path stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22' /></svg>
|
||||
</button>
|
||||
|
@ -77,8 +71,7 @@
|
|||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Manage" asp-action="Index" title="My settings" class="nav-link js-scroll-trigger" id="MySettings"><i class="fa fa-user"></i></a>
|
||||
</li>
|
||||
|
||||
<partial name="LayoutPartials/NotificationsNavItem" />
|
||||
<vc:notifications-dropdown></vc:notifications-dropdown>
|
||||
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="Account" asp- asp-action="Logout" title="Logout" class="nav-link js-scroll-trigger" id="Logout"><i class="fa fa-sign-out"></i></a>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
You are not an admin on this server. While you are able to import or generate a wallet via seed with your account, please understand that you are trusting the server admins not just with your <a href="https://docs.btcpayserver.org/ThirdPartyHosting/#privacy-concerns" target="_blank" class="alert-link">privacy</a> but also with <a href="https://docs.btcpayserver.org/ThirdPartyHosting/#trust-concerns" target="_blank" class="alert-link">trivial access to your funds.</a> If you NEED to use this feature, please reconsider hosting your own BTCPay Server instance.
|
||||
</div>
|
||||
}
|
||||
<p>You may generate a wallet with a seed and import the xpub it into BTCPay. You can optionally also tell NBX to import the keys to the node wallet to be able to view & spend received funds from it.</p>
|
||||
<p>You may generate a wallet with a seed and import the xpub into BTCPay. You can optionally also tell NBX to import the keys to the node wallet to be able to view & spend received funds from it.</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="ExistingMnemonic">Existing Seed</label>
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
}
|
||||
<td class="text-right">
|
||||
<div class="dropdown d-inline-block" >
|
||||
<span class="fa fa-tags" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<span class="fa fa-tags cursor-pointer" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<div class="dropdown-menu">
|
||||
<form asp-action="ModifyTransaction" method="post"
|
||||
asp-route-walletId="@this.Context.GetRouteValue("walletId")">
|
||||
|
@ -180,11 +180,11 @@
|
|||
<div class="dropdown d-inline-block">
|
||||
@if (string.IsNullOrEmpty(transaction.Comment))
|
||||
{
|
||||
<span class="fa fa-comment" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<span class="fa fa-comment cursor-pointer" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="fa fa-commenting" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
<span class="fa fa-commenting cursor-pointer" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
|
||||
}
|
||||
<div class="dropdown-menu">
|
||||
<form asp-action="ModifyTransaction" method="post"
|
||||
|
|
|
@ -10,3 +10,4 @@
|
|||
@using Microsoft.AspNetCore.Routing;
|
||||
@inject BTCPayServer.Services.Safe Safe
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@addTagHelper *, BTCPayServer
|
|
@ -37,6 +37,8 @@
|
|||
"inputFiles": [
|
||||
"wwwroot/vendor/font-awesome/css/font-awesome.css",
|
||||
"wwwroot/vendor/vex/css/vex.css",
|
||||
"wwwroot/main/fonts/Roboto.css",
|
||||
"wwwroot/main/fonts/RobotoMono.css",
|
||||
"wwwroot/checkout/*/*.css",
|
||||
"wwwroot/vendor/jquery-prettydropdowns/prettydropdowns.css"
|
||||
]
|
||||
|
|
|
@ -1,280 +0,0 @@
|
|||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v16/ek4gzZ-GeXAPcSbHtCeQI_esZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v16/mErvLBYg_cXG3rLvUsKT_fesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v16/-2n2p-_Y08sg57CNWQfKNvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v16/u0TOpm082MNkS5K0Q4rhqvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v16/NdF9MtnOpLzo-noMoG0miPesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v16/Fcx7Wwv8OzT71A3E1XOAjvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v16/CWB0XYA8bzo0kSThX0UTuA.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v16/ZLqKeelYbATG60EpZBSDyxJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v16/oHi30kwQWvpCWqAhzHcCSBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v16/rGvHdJnr2l75qb0YND9NyBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v16/mx9Uck6uB63VIKFYnEMXrRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v16/mbmhprMH69Zi6eEPBYVFhRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v16/oOeFwZNlrTefzLYmlVV1UBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(https://fonts.gstatic.com/s/roboto/v16/RxZJdnzeo3R5zSexge8UUVtXRa8TVwTICgirnJhmVJw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v16/77FXFjRbGzN4aCrSFhlh3hJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v16/isZ-wbCXNKAbnjo6_TwHThJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v16/UX6i4JxQDm3fVTc1CPuwqhJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v16/jSN2CGVDbcVyCnfJfjSdfBJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v16/PwZc-YbIL414wB9rB1IAPRJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v16/97uahxiqZRoncBaCEI3aWxJtnKITppOI_IvcXXDNrsc.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(https://fonts.gstatic.com/s/roboto/v16/d-6IYplOFocCacKzxwXSOFtXRa8TVwTICgirnJhmVJw.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'), url(https://fonts.gstatic.com/s/roboto/v16/WxrXJa0C3KdtC7lMafG4dRTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'), url(https://fonts.gstatic.com/s/roboto/v16/OpXUqTo0UgQQhGj_SFdLWBTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'), url(https://fonts.gstatic.com/s/roboto/v16/1hZf02POANh32k2VkgEoUBTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'), url(https://fonts.gstatic.com/s/roboto/v16/cDKhRaXnQTOVbaoxwdOr9xTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'), url(https://fonts.gstatic.com/s/roboto/v16/K23cxWVTrIFD6DJsEVi07RTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'), url(https://fonts.gstatic.com/s/roboto/v16/vSzulfKSK0LLjjfeaxcREhTbgVql8nDJpwnrE27mub0.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'), url(https://fonts.gstatic.com/s/roboto/v16/vPcynSL0qHq_6dX7lKVByfesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Mono'), local('RobotoMono-Regular'), url(https://fonts.gstatic.com/s/robotomono/v4/hMqPNLsu_dywMa4C_DEpYzTOQ_MqJVwkKsUn0wKzc2I.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+20B4, U+2DE0-2DFF, U+A640-A69F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Mono'), local('RobotoMono-Regular'), url(https://fonts.gstatic.com/s/robotomono/v4/hMqPNLsu_dywMa4C_DEpYzUj_cnvWIuuBMVgbX098Mw.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Mono'), local('RobotoMono-Regular'), url(https://fonts.gstatic.com/s/robotomono/v4/hMqPNLsu_dywMa4C_DEpY0bcKLIaa1LC45dFaAfauRA.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Mono'), local('RobotoMono-Regular'), url(https://fonts.gstatic.com/s/robotomono/v4/hMqPNLsu_dywMa4C_DEpY2o_sUJ8uO4YLWRInS22T3Y.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Mono'), local('RobotoMono-Regular'), url(https://fonts.gstatic.com/s/robotomono/v4/hMqPNLsu_dywMa4C_DEpY76up8jxqWt8HVA3mDhkV_0.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Mono'), local('RobotoMono-Regular'), url(https://fonts.gstatic.com/s/robotomono/v4/hMqPNLsu_dywMa4C_DEpYyYE0-AqJ3nfInTTiDXDjU4.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Mono'), local('RobotoMono-Regular'), url(https://fonts.gstatic.com/s/robotomono/v4/hMqPNLsu_dywMa4C_DEpY44P5ICox8Kq3LLUNMylGO4.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue