Compare commits
104 Commits
v1.0.3.104
...
v1.0.3.115
Author | SHA1 | Date | |
---|---|---|---|
019bd26c51 | |||
0e1f924fc3 | |||
15c3893aab | |||
deeab7c238 | |||
e5ba7b9e69 | |||
ca5be7e38d | |||
fb530f2b34 | |||
29cbf63346 | |||
13c03cc0c2 | |||
281280d3ec | |||
410be51951 | |||
eefe8289b3 | |||
a53a5944f8 | |||
cd009466b6 | |||
f0c106de75 | |||
03ba57cd46 | |||
bea08e5cfd | |||
01787e2662 | |||
ac76220349 | |||
796954c6e3 | |||
292c188182 | |||
1f7097ef89 | |||
b97e083017 | |||
8ffd182b98 | |||
1e77546251 | |||
8711960e74 | |||
8e2bcef824 | |||
d418cf7b07 | |||
864bcbb675 | |||
0b257b98f5 | |||
daab68d0b8 | |||
ab0511aa1d | |||
12494c3ac6 | |||
621533e050 | |||
dc334d230a | |||
b848595378 | |||
ae4b2ab1fd | |||
2ca8cc6ca3 | |||
3b57e2684e | |||
898c672193 | |||
18a7bc9278 | |||
bb29ee10c5 | |||
5441ae537a | |||
0a0ddafd67 | |||
a3b914d8b4 | |||
39f75d3742 | |||
3dd77a4f2c | |||
6782e82972 | |||
120fce0288 | |||
aa57531ed7 | |||
8f76bc0bcb | |||
189280e602 | |||
78ca26cf78 | |||
0c5c6233c7 | |||
4fe480ee55 | |||
be6560e08c | |||
0f58f6da36 | |||
dcaf0463a7 | |||
5c6643270b | |||
7b337bde49 | |||
7056aae301 | |||
1b6eb9cab0 | |||
80e23beda9 | |||
d70e120acc | |||
c877937fdf | |||
8379b07de0 | |||
c8c33245b8 | |||
0faf2fe83e | |||
19bc511f39 | |||
916323bb3b | |||
0e568e2af5 | |||
dde841383a | |||
81dae7d350 | |||
d3e3c31b0c | |||
90852fe951 | |||
a2251d245f | |||
112f9c4241 | |||
b300404bc7 | |||
19161b52f5 | |||
429170520e | |||
5571413a78 | |||
512ee16620 | |||
15dc0d60db | |||
d86cc9192e | |||
25b08b21fa | |||
ef9c2e8af1 | |||
9bee48c601 | |||
961942ff6a | |||
de1c2b0150 | |||
5a73358bca | |||
1812ea90b5 | |||
d98a416ed9 | |||
b947749382 | |||
c4d0b061c9 | |||
3bada5d443 | |||
06a35787aa | |||
88c931ec13 | |||
3d436c3b0e | |||
b4bb44d3e6 | |||
39157e6883 | |||
9b6b9e6113 | |||
87df34e064 | |||
55a48ff84a | |||
aed98f16bb |
@ -1,30 +1,41 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
machine:
|
||||
docker_layer_caching: true
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
test:
|
||||
fast_tests:
|
||||
machine:
|
||||
docker_layer_caching: true
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
cd BTCPayServer.Tests
|
||||
docker-compose -v
|
||||
docker-compose down --v
|
||||
docker-compose build
|
||||
TESTS_RUN_EXTERNAL_INTEGRATION="false"
|
||||
if [ "$CIRCLE_PROJECT_USERNAME" == "btcpayserver" ] && [ "$CIRCLE_PROJECT_REPONAME" == "btcpayserver" ]; then
|
||||
TESTS_RUN_EXTERNAL_INTEGRATION="true"
|
||||
fi
|
||||
docker-compose run -e TESTS_RUN_EXTERNAL_INTEGRATION=$TESTS_RUN_EXTERNAL_INTEGRATION tests
|
||||
cd .circleci && ./run-tests.sh "Fast=Fast"
|
||||
selenium_tests:
|
||||
machine:
|
||||
docker_layer_caching: true
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
cd .circleci && ./run-tests.sh "Selenium=Selenium"
|
||||
integration_tests:
|
||||
machine:
|
||||
docker_layer_caching: true
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
cd .circleci && ./run-tests.sh "Integration=Integration"
|
||||
external_tests:
|
||||
machine:
|
||||
docker_layer_caching: true
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
cd .circleci && ./run-tests.sh "ExternalIntegration=ExternalIntegration"
|
||||
|
||||
|
||||
# publish jobs require $DOCKERHUB_REPO, $DOCKERHUB_USER, $DOCKERHUB_PASS defined
|
||||
publish_docker_linuxamd64:
|
||||
amd64:
|
||||
machine:
|
||||
docker_layer_caching: true
|
||||
steps:
|
||||
@ -33,11 +44,11 @@ jobs:
|
||||
command: |
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
#
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f Dockerfile.linuxamd64 .
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f amd64.Dockerfile .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-amd64
|
||||
|
||||
publish_docker_linuxarm:
|
||||
arm32v7:
|
||||
machine:
|
||||
docker_layer_caching: true
|
||||
steps:
|
||||
@ -47,11 +58,11 @@ jobs:
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
#
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f Dockerfile.linuxarm32v7 .
|
||||
sudo docker build --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f arm32v7.Dockerfile .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
|
||||
|
||||
publish_docker_multiarch:
|
||||
multiarch:
|
||||
machine:
|
||||
enabled: true
|
||||
image: circleci/classic:201808-01
|
||||
@ -74,11 +85,17 @@ workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
jobs:
|
||||
- test
|
||||
- fast_tests
|
||||
- selenium_tests
|
||||
- integration_tests
|
||||
- external_tests:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
|
||||
publish:
|
||||
jobs:
|
||||
- publish_docker_linuxamd64:
|
||||
- amd64:
|
||||
filters:
|
||||
# ignore any commit on any branch by default
|
||||
branches:
|
||||
@ -86,16 +103,16 @@ workflows:
|
||||
# only act on version tags
|
||||
tags:
|
||||
only: /v[1-9]+(\.[0-9]+)*/
|
||||
- publish_docker_linuxarm:
|
||||
- arm32v7:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /v[1-9]+(\.[0-9]+)*/
|
||||
- publish_docker_multiarch:
|
||||
- multiarch:
|
||||
requires:
|
||||
- publish_docker_linuxamd64
|
||||
- publish_docker_linuxarm
|
||||
- amd64
|
||||
- arm32v7
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
|
8
.circleci/run-tests.sh
Executable file
8
.circleci/run-tests.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
cd ../BTCPayServer.Tests
|
||||
docker-compose -v
|
||||
docker-compose down --v
|
||||
docker-compose build
|
||||
docker-compose run -e "TEST_FILTERS=$1" tests
|
@ -3,10 +3,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
@ -44,9 +43,66 @@ namespace BTCPayServer
|
||||
public string DefaultConfigurationFile { get; set; }
|
||||
public int DefaultPort { get; set; }
|
||||
}
|
||||
public class BTCPayNetwork
|
||||
|
||||
public class BTCPayNetwork:BTCPayNetworkBase
|
||||
{
|
||||
public Network NBitcoinNetwork { get; set; }
|
||||
public NBXplorer.NBXplorerNetwork NBXplorerNetwork { get; set; }
|
||||
public bool SupportRBF { get; internal set; }
|
||||
public string LightningImagePath { get; set; }
|
||||
public BTCPayDefaultSettings DefaultSettings { get; set; }
|
||||
public KeyPath CoinType { get; internal set; }
|
||||
public Dictionary<uint, DerivationType> ElectrumMapping = new Dictionary<uint, DerivationType>();
|
||||
|
||||
|
||||
public KeyPath GetRootKeyPath(DerivationType type)
|
||||
{
|
||||
KeyPath baseKey;
|
||||
if (!NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
{
|
||||
baseKey = new KeyPath("44'");
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DerivationType.Legacy:
|
||||
baseKey = new KeyPath("44'");
|
||||
break;
|
||||
case DerivationType.SegwitP2SH:
|
||||
baseKey = new KeyPath("49'");
|
||||
break;
|
||||
case DerivationType.Segwit:
|
||||
baseKey = new KeyPath("84'");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
return baseKey
|
||||
.Derive(CoinType);
|
||||
}
|
||||
|
||||
public KeyPath GetRootKeyPath()
|
||||
{
|
||||
return new KeyPath(NBitcoinNetwork.Consensus.SupportSegwit ? "49'" : "44'")
|
||||
.Derive(CoinType);
|
||||
}
|
||||
|
||||
public override T ToObject<T>(string json)
|
||||
{
|
||||
return NBXplorerNetwork.Serializer.ToObject<T>(json);
|
||||
}
|
||||
|
||||
public override string ToString<T>(T obj)
|
||||
{
|
||||
return NBXplorerNetwork.Serializer.ToString(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BTCPayNetworkBase
|
||||
{
|
||||
|
||||
public string CryptoCode { get; internal set; }
|
||||
public string BlockExplorerLink { get; internal set; }
|
||||
public string UriScheme { get; internal set; }
|
||||
@ -62,24 +118,22 @@ namespace BTCPayServer
|
||||
}
|
||||
|
||||
public string CryptoImagePath { get; set; }
|
||||
public string LightningImagePath { get; set; }
|
||||
public NBXplorer.NBXplorerNetwork NBXplorerNetwork { get; set; }
|
||||
|
||||
public BTCPayDefaultSettings DefaultSettings { get; set; }
|
||||
public KeyPath CoinType { get; internal set; }
|
||||
public int MaxTrackedConfirmation { get; internal set; } = 6;
|
||||
public string[] DefaultRateRules { get; internal set; } = Array.Empty<string>();
|
||||
public bool SupportRBF { get; internal set; }
|
||||
public Dictionary<uint, DerivationType> ElectrumMapping = new Dictionary<uint, DerivationType>();
|
||||
public override string ToString()
|
||||
{
|
||||
return CryptoCode;
|
||||
}
|
||||
|
||||
internal KeyPath GetRootKeyPath()
|
||||
public virtual T ToObject<T>(string json)
|
||||
{
|
||||
return new KeyPath(NBitcoinNetwork.Consensus.SupportSegwit ? "49'" : "44'")
|
||||
.Derive(CoinType);
|
||||
return JsonConvert.DeserializeObject<T>(json);
|
||||
}
|
||||
|
||||
public virtual string ToString<T>(T obj)
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
@ -18,7 +16,7 @@ namespace BTCPayServer
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Bitcoin",
|
||||
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://www.smartbit.com.au/tx/{0}" : "https://testnet.smartbit.com.au/tx/{0}",
|
||||
BlockExplorerLink = NetworkType == NetworkType.Mainnet ? "https://blockstream.info/tx/{0}" : "https://blockstream.info/testnet/tx/{0}",
|
||||
NBitcoinNetwork = nbxplorerNetwork.NBitcoinNetwork,
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
UriScheme = "bitcoin",
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
@ -3,17 +3,14 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
Dictionary<string, BTCPayNetwork> _Networks = new Dictionary<string, BTCPayNetwork>();
|
||||
Dictionary<string, BTCPayNetworkBase> _Networks = new Dictionary<string, BTCPayNetworkBase>();
|
||||
|
||||
|
||||
private readonly NBXplorerNetworkProvider _NBXplorerNetworkProvider;
|
||||
@ -25,13 +22,14 @@ namespace BTCPayServer
|
||||
}
|
||||
}
|
||||
|
||||
BTCPayNetworkProvider(BTCPayNetworkProvider filtered, string[] cryptoCodes)
|
||||
BTCPayNetworkProvider(BTCPayNetworkProvider unfiltered, string[] cryptoCodes)
|
||||
{
|
||||
NetworkType = filtered.NetworkType;
|
||||
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(filtered.NetworkType);
|
||||
_Networks = new Dictionary<string, BTCPayNetwork>();
|
||||
UnfilteredNetworks = unfiltered.UnfilteredNetworks ?? unfiltered;
|
||||
NetworkType = unfiltered.NetworkType;
|
||||
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(unfiltered.NetworkType);
|
||||
_Networks = new Dictionary<string, BTCPayNetworkBase>();
|
||||
cryptoCodes = cryptoCodes.Select(c => c.ToUpperInvariant()).ToArray();
|
||||
foreach (var network in filtered._Networks)
|
||||
foreach (var network in unfiltered._Networks)
|
||||
{
|
||||
if(cryptoCodes.Contains(network.Key))
|
||||
{
|
||||
@ -40,9 +38,12 @@ namespace BTCPayServer
|
||||
}
|
||||
}
|
||||
|
||||
public BTCPayNetworkProvider UnfilteredNetworks { get; }
|
||||
|
||||
public NetworkType NetworkType { get; private set; }
|
||||
public BTCPayNetworkProvider(NetworkType networkType)
|
||||
{
|
||||
UnfilteredNetworks = this;
|
||||
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(networkType);
|
||||
NetworkType = networkType;
|
||||
InitBitcoin();
|
||||
@ -57,15 +58,15 @@ namespace BTCPayServer
|
||||
InitViacoin();
|
||||
|
||||
// Assume that electrum mappings are same as BTC if not specified
|
||||
foreach (var network in _Networks)
|
||||
foreach (var network in _Networks.Values.OfType<BTCPayNetwork>())
|
||||
{
|
||||
if(network.Value.ElectrumMapping.Count == 0)
|
||||
if(network.ElectrumMapping.Count == 0)
|
||||
{
|
||||
network.Value.ElectrumMapping = GetNetwork("BTC").ElectrumMapping;
|
||||
if (!network.Value.NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
network.ElectrumMapping = GetNetwork<BTCPayNetwork>("BTC").ElectrumMapping;
|
||||
if (!network.NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
{
|
||||
network.Value.ElectrumMapping =
|
||||
network.Value.ElectrumMapping
|
||||
network.ElectrumMapping =
|
||||
network.ElectrumMapping
|
||||
.Where(kv => kv.Value == DerivationType.Legacy)
|
||||
.ToDictionary(k => k.Key, k => k.Value);
|
||||
}
|
||||
@ -89,20 +90,14 @@ namespace BTCPayServer
|
||||
}
|
||||
|
||||
[Obsolete("To use only for legacy stuff")]
|
||||
public BTCPayNetwork BTC
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetNetwork("BTC");
|
||||
}
|
||||
}
|
||||
public BTCPayNetwork BTC => GetNetwork<BTCPayNetwork>("BTC");
|
||||
|
||||
public void Add(BTCPayNetwork network)
|
||||
public void Add(BTCPayNetworkBase network)
|
||||
{
|
||||
_Networks.Add(network.CryptoCode.ToUpperInvariant(), network);
|
||||
}
|
||||
|
||||
public IEnumerable<BTCPayNetwork> GetAll()
|
||||
public IEnumerable<BTCPayNetworkBase> GetAll()
|
||||
{
|
||||
return _Networks.Values.ToArray();
|
||||
}
|
||||
@ -111,15 +106,20 @@ namespace BTCPayServer
|
||||
{
|
||||
return _Networks.ContainsKey(cryptoCode.ToUpperInvariant());
|
||||
}
|
||||
|
||||
public BTCPayNetwork GetNetwork(string cryptoCode)
|
||||
public BTCPayNetworkBase GetNetwork(string cryptoCode)
|
||||
{
|
||||
if(!_Networks.TryGetValue(cryptoCode.ToUpperInvariant(), out BTCPayNetwork network))
|
||||
return GetNetwork<BTCPayNetworkBase>(cryptoCode);
|
||||
}
|
||||
public T GetNetwork<T>(string cryptoCode) where T: BTCPayNetworkBase
|
||||
{
|
||||
if (cryptoCode == null)
|
||||
throw new ArgumentNullException(nameof(cryptoCode));
|
||||
if(!_Networks.TryGetValue(cryptoCode.ToUpperInvariant(), out BTCPayNetworkBase network))
|
||||
{
|
||||
if (cryptoCode == "XBT")
|
||||
return GetNetwork("BTC");
|
||||
return GetNetwork<T>("BTC");
|
||||
}
|
||||
return network;
|
||||
return network as T;
|
||||
}
|
||||
}
|
||||
}
|
10
BTCPayServer.Common/BTCPayServer.Common.csproj
Normal file
10
BTCPayServer.Common/BTCPayServer.Common.csproj
Normal file
@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../Version.csproj" Condition="Exists('../Version.csproj')" />
|
||||
<Import Project="../Common.csproj" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" />
|
||||
<PackageReference Include="NBitcoin" Version="4.1.2.35" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="2.0.0.17" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
class CustomThreadPool : IDisposable
|
||||
public class CustomThreadPool : IDisposable
|
||||
{
|
||||
CancellationTokenSource _Cancel = new CancellationTokenSource();
|
||||
TaskCompletionSource<bool> _Exited;
|
17
BTCPayServer.Common/Extensions.cs
Normal file
17
BTCPayServer.Common/Extensions.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public static class UtilitiesExtensions
|
||||
{
|
||||
public static void AddRange<T>(this HashSet<T> hashSet, IEnumerable<T> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
hashSet.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ using System.Text;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
class ZipUtils
|
||||
public class ZipUtils
|
||||
{
|
||||
public static byte[] Zip(string unzipped)
|
||||
{
|
24
BTCPayServer.Rating/BTCPayServer.Rating.csproj
Normal file
24
BTCPayServer.Rating/BTCPayServer.Rating.csproj
Normal file
@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../Version.csproj" Condition="Exists('../Version.csproj')" />
|
||||
<Import Project="../Common.csproj" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Providers\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.5.3" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.34" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Common\BTCPayServer.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
@ -53,7 +53,7 @@ namespace BTCPayServer.Rating
|
||||
for (int i = 3; i < 5; i++)
|
||||
{
|
||||
var potentialCryptoName = currencyPair.Substring(0, i);
|
||||
var network = _NetworkProvider.GetNetwork(potentialCryptoName);
|
||||
var network = _NetworkProvider.GetNetwork<BTCPayNetworkBase>(potentialCryptoName);
|
||||
if (network != null)
|
||||
{
|
||||
value = new CurrencyPair(network.CryptoCode, currencyPair.Substring(i));
|
@ -4,10 +4,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Rating;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using BTCPayServer.Logging;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
@ -39,6 +39,7 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
|
||||
IRateProvider _Inner;
|
||||
|
||||
public BackgroundFetcherRateProvider(IRateProvider inner)
|
||||
{
|
||||
if (inner == null)
|
@ -4,7 +4,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NBitcoin;
|
||||
using BTCPayServer.Rating;
|
||||
using System.Threading;
|
||||
|
@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using BTCPayServer.Rating;
|
||||
using ExchangeSharp;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
@ -33,6 +33,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Rating\BTCPayServer.Rating.csproj" />
|
||||
<ProjectReference Include="..\BTCPayServer\BTCPayServer.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -33,6 +33,7 @@ using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using Xunit;
|
||||
using BTCPayServer.Services;
|
||||
using System.Net.Http;
|
||||
@ -274,6 +275,8 @@ namespace BTCPayServer.Tests
|
||||
return _Host.Services.GetRequiredService<T>();
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider => _Host.Services;
|
||||
|
||||
public T GetController<T>(string userId = null, string storeId = null, Claim[] additionalClaims = null) where T : Controller
|
||||
{
|
||||
var context = new DefaultHttpContext();
|
||||
@ -283,7 +286,7 @@ namespace BTCPayServer.Tests
|
||||
if (userId != null)
|
||||
{
|
||||
List<Claim> claims = new List<Claim>();
|
||||
claims.Add(new Claim(ClaimTypes.NameIdentifier, userId));
|
||||
claims.Add(new Claim(OpenIdConnectConstants.Claims.Subject, userId));
|
||||
if (additionalClaims != null)
|
||||
claims.AddRange(additionalClaims);
|
||||
context.User = new ClaimsPrincipal(new ClaimsIdentity(claims.ToArray(), Policies.CookieAuthentication));
|
||||
|
@ -5,7 +5,10 @@ ENV LC_ALL en_US.UTF-8
|
||||
ENV LANG en_US.UTF-8
|
||||
|
||||
WORKDIR /source
|
||||
COPY Common.csproj Common.csproj
|
||||
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer/BTCPayServer.csproj
|
||||
COPY BTCPayServer.Common/BTCPayServer.Common.csproj BTCPayServer.Common/BTCPayServer.Common.csproj
|
||||
COPY BTCPayServer.Rating/BTCPayServer.Rating.csproj BTCPayServer.Rating/BTCPayServer.Rating.csproj
|
||||
COPY BTCPayServer.Tests/BTCPayServer.Tests.csproj BTCPayServer.Tests/BTCPayServer.Tests.csproj
|
||||
RUN dotnet restore BTCPayServer.Tests/BTCPayServer.Tests.csproj
|
||||
|
||||
@ -20,6 +23,6 @@ ENV SCREEN_HEIGHT 600 \
|
||||
SCREEN_WIDTH 1200
|
||||
|
||||
COPY . .
|
||||
RUN dotnet build
|
||||
RUN cd BTCPayServer.Tests && dotnet build
|
||||
WORKDIR /source/BTCPayServer.Tests
|
||||
ENTRYPOINT ["./docker-entrypoint.sh"]
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -53,8 +54,14 @@ namespace BTCPayServer.Tests
|
||||
var sendDestination = new Key().PubKey.Hash.GetAddress(user.SupportedNetwork.NBitcoinNetwork).ToString();
|
||||
var sendModel = new WalletSendModel()
|
||||
{
|
||||
Destination = sendDestination,
|
||||
Amount = 0.1m,
|
||||
Outputs = new List<WalletSendModel.TransactionOutput>()
|
||||
{
|
||||
new WalletSendModel.TransactionOutput()
|
||||
{
|
||||
DestinationAddress = sendDestination,
|
||||
Amount = 0.1m,
|
||||
}
|
||||
},
|
||||
FeeSatoshiPerByte = 1,
|
||||
CurrentBalance = 1.5m
|
||||
};
|
||||
@ -65,7 +72,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.NotNull(vmLedger.WebsocketPath);
|
||||
|
||||
var redirectedPSBT = (string)Assert.IsType<RedirectToActionResult>(await walletController.WalletSend(walletId, sendModel, command: "analyze-psbt")).RouteValues["psbt"];
|
||||
var vmPSBT = walletController.WalletPSBT(walletId, new WalletPSBTViewModel() { PSBT = redirectedPSBT }).AssertViewModel<WalletPSBTViewModel>();
|
||||
var vmPSBT = await walletController.WalletPSBT(walletId, new WalletPSBTViewModel() { PSBT = redirectedPSBT }).AssertViewModelAsync<WalletPSBTViewModel>();
|
||||
var unsignedPSBT = PSBT.Parse(vmPSBT.PSBT, user.SupportedNetwork.NBitcoinNetwork);
|
||||
Assert.NotNull(vmPSBT.Decoded);
|
||||
|
||||
@ -73,16 +80,15 @@ namespace BTCPayServer.Tests
|
||||
PSBT.Load(filePSBT.FileContents, user.SupportedNetwork.NBitcoinNetwork);
|
||||
|
||||
await walletController.WalletPSBT(walletId, vmPSBT, "ledger").AssertViewModelAsync<WalletSendLedgerModel>();
|
||||
var vmPSBT2 = await walletController.WalletPSBT(walletId, vmPSBT, "broadcast").AssertViewModelAsync<WalletPSBTViewModel>();
|
||||
Assert.NotEmpty(vmPSBT2.Errors);
|
||||
Assert.Equal(vmPSBT.Decoded, vmPSBT2.Decoded);
|
||||
var vmPSBT2 = await walletController.WalletPSBT(walletId, vmPSBT, "broadcast").AssertViewModelAsync<WalletPSBTReadyViewModel>();
|
||||
Assert.NotEmpty(vmPSBT2.Inputs.Where(i => i.Error != null));
|
||||
Assert.Equal(vmPSBT.PSBT, vmPSBT2.PSBT);
|
||||
|
||||
var signedPSBT = unsignedPSBT.Clone();
|
||||
signedPSBT.SignAll(user.ExtKey);
|
||||
signedPSBT.SignAll(user.DerivationScheme, user.ExtKey);
|
||||
vmPSBT.PSBT = signedPSBT.ToBase64();
|
||||
var psbtReady = await walletController.WalletPSBT(walletId, vmPSBT, "broadcast").AssertViewModelAsync<WalletPSBTReadyViewModel>();
|
||||
Assert.Equal(2, psbtReady.Destinations.Count);
|
||||
Assert.Equal(2 + 1, psbtReady.Destinations.Count); // The fee is a destination
|
||||
Assert.Contains(psbtReady.Destinations, d => d.Destination == sendDestination && !d.Positive);
|
||||
Assert.Contains(psbtReady.Destinations, d => d.Positive);
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, psbtReady, command: "broadcast"));
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using BTCPayServer;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
@ -80,7 +80,7 @@ namespace BTCPayServer.Tests
|
||||
public void AddDerivationScheme(string derivationScheme = "xpub661MyMwAqRbcGABgHMUXDzPzH1tU7eZaAaJQXhDXsSxsqyQzQeU6kznNfSuAyqAK9UaWSaZaMFdNiY5BCF4zBPAzSnwfUAwUhwttuAKwfRX-[legacy]")
|
||||
{
|
||||
Driver.FindElement(By.Id("ModifyBTC")).ForceClick();
|
||||
Driver.FindElement(By.Id("DerivationScheme")).SendKeys(derivationScheme);
|
||||
Driver.FindElement(By.ClassName("store-derivation-scheme")).SendKeys(derivationScheme);
|
||||
Driver.FindElement(By.Id("Continue")).ForceClick();
|
||||
Driver.FindElement(By.Id("Confirm")).ForceClick();
|
||||
Driver.FindElement(By.Id("Save")).ForceClick();
|
||||
|
@ -287,8 +287,7 @@ namespace BTCPayServer.Tests
|
||||
// Send to bob
|
||||
s.Driver.FindElement(By.Id("WalletSend")).Click();
|
||||
var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);
|
||||
s.Driver.FindElement(By.Id("Destination")).SendKeys(bob.ToString());
|
||||
s.Driver.FindElement(By.Id("Amount")).SendKeys("1");
|
||||
SetTransactionOutput(0, bob, 1);
|
||||
s.Driver.ScrollTo(By.Id("SendMenu"));
|
||||
s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=seed]")).Click();
|
||||
@ -299,9 +298,22 @@ namespace BTCPayServer.Tests
|
||||
// Broadcast
|
||||
Assert.Contains(bob.ToString(), s.Driver.PageSource);
|
||||
Assert.Contains("1.00000000", s.Driver.PageSource);
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
|
||||
Assert.Equal(walletTransactionLink, s.Driver.Url);
|
||||
}
|
||||
|
||||
void SetTransactionOutput(int index, BitcoinAddress dest, decimal amount, bool subtract = false)
|
||||
{
|
||||
s.Driver.FindElement(By.Id($"Outputs_{index}__DestinationAddress")).SendKeys(dest.ToString());
|
||||
var amountElement = s.Driver.FindElement(By.Id($"Outputs_{index}__Amount"));
|
||||
amountElement.Clear();
|
||||
amountElement.SendKeys(amount.ToString());
|
||||
var checkboxElement = s.Driver.FindElement(By.Id($"Outputs_{index}__SubtractFeesFromOutput"));
|
||||
if (checkboxElement.Selected != subtract)
|
||||
{
|
||||
checkboxElement.Click();
|
||||
}
|
||||
}
|
||||
SignWith(mnemonic);
|
||||
var accountKey = root.Derive(new KeyPath("m/49'/0'/0'")).GetWif(Network.RegTest).ToString();
|
||||
SignWith(accountKey);
|
||||
|
@ -44,14 +44,14 @@ namespace BTCPayServer.Tests
|
||||
Directory.CreateDirectory(_Directory);
|
||||
|
||||
NetworkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_BTCRPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork("BTC").NBitcoinNetwork);
|
||||
ExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_BTCRPCCONNECTION", "server=http://127.0.0.1:43782;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBitcoinNetwork);
|
||||
ExplorerNode.ScanRPCCapabilities();
|
||||
LTCExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_LTCRPCCONNECTION", "server=http://127.0.0.1:43783;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork("LTC").NBitcoinNetwork);
|
||||
LTCExplorerNode = new RPCClient(RPCCredentialString.Parse(GetEnvironment("TESTS_LTCRPCCONNECTION", "server=http://127.0.0.1:43783;ceiwHEbqWI83:DwubwWsoo3")), NetworkProvider.GetNetwork<BTCPayNetwork>("LTC").NBitcoinNetwork);
|
||||
|
||||
ExplorerClient = new ExplorerClient(NetworkProvider.GetNetwork("BTC").NBXplorerNetwork, new Uri(GetEnvironment("TESTS_BTCNBXPLORERURL", "http://127.0.0.1:32838/")));
|
||||
LTCExplorerClient = new ExplorerClient(NetworkProvider.GetNetwork("LTC").NBXplorerNetwork, new Uri(GetEnvironment("TESTS_LTCNBXPLORERURL", "http://127.0.0.1:32838/")));
|
||||
ExplorerClient = new ExplorerClient(NetworkProvider.GetNetwork<BTCPayNetwork>("BTC").NBXplorerNetwork, new Uri(GetEnvironment("TESTS_BTCNBXPLORERURL", "http://127.0.0.1:32838/")));
|
||||
LTCExplorerClient = new ExplorerClient(NetworkProvider.GetNetwork<BTCPayNetwork>("LTC").NBXplorerNetwork, new Uri(GetEnvironment("TESTS_LTCNBXPLORERURL", "http://127.0.0.1:32838/")));
|
||||
|
||||
var btc = NetworkProvider.GetNetwork("BTC").NBitcoinNetwork;
|
||||
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);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
@ -9,6 +10,7 @@ using BTCPayServer.Storage.Services.Providers.AzureBlobStorage.Configuration;
|
||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage.Configuration;
|
||||
using BTCPayServer.Storage.ViewModels;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using DBriize.Utils;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -38,15 +40,6 @@ namespace BTCPayServer.Tests
|
||||
user.GrantAccess();
|
||||
var controller = tester.PayTester.GetController<ServerController>(user.UserId, user.StoreId);
|
||||
|
||||
// //For some reason, the tests cache something on circleci and this is set by default
|
||||
// //Initially, there is no configuration, make sure we display the choices available to configure
|
||||
// Assert.IsType<StorageSettings>(Assert.IsType<ViewResult>(await controller.Storage()).Model);
|
||||
//
|
||||
// //the file list should tell us it's not configured:
|
||||
// var viewFilesViewModelInitial =
|
||||
// Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files()).Model);
|
||||
// Assert.False(viewFilesViewModelInitial.StorageConfigured);
|
||||
|
||||
|
||||
//Once we select a provider, redirect to its view
|
||||
var localResult = Assert
|
||||
@ -196,6 +189,7 @@ namespace BTCPayServer.Tests
|
||||
var fileId = uploadFormFileResult.RouteValues["fileId"].ToString();
|
||||
Assert.Equal("Files", uploadFormFileResult.ActionName);
|
||||
|
||||
//check if file was uploaded and saved in db
|
||||
var viewFilesViewModel =
|
||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files(fileId)).Model);
|
||||
|
||||
@ -203,21 +197,48 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(fileId, viewFilesViewModel.SelectedFileId);
|
||||
Assert.NotEmpty(viewFilesViewModel.DirectFileUrl);
|
||||
|
||||
|
||||
|
||||
//verify file is available and the same
|
||||
var net = new System.Net.WebClient();
|
||||
var data = await net.DownloadStringTaskAsync(new Uri(viewFilesViewModel.DirectFileUrl));
|
||||
Assert.Equal(fileContent, data);
|
||||
|
||||
|
||||
//create a temporary link to file
|
||||
var tmpLinkGenerate = Assert.IsType<RedirectToActionResult>(await controller.CreateTemporaryFileUrl(fileId,
|
||||
new ServerController.CreateTemporaryFileUrlViewModel()
|
||||
{
|
||||
IsDownload = true,
|
||||
TimeAmount = 1,
|
||||
TimeType = ServerController.CreateTemporaryFileUrlViewModel.TmpFileTimeType.Minutes
|
||||
}));
|
||||
Assert.True(tmpLinkGenerate.RouteValues.ContainsKey("StatusMessage"));
|
||||
var statusMessageModel = new StatusMessageModel(tmpLinkGenerate.RouteValues["StatusMessage"].ToString());
|
||||
Assert.Equal(StatusMessageModel.StatusSeverity.Success, statusMessageModel.Severity);
|
||||
var index = statusMessageModel.Html.IndexOf("target='_blank'>");
|
||||
var url = statusMessageModel.Html.Substring(index).ReplaceMultiple(new Dictionary<string, string>()
|
||||
{
|
||||
{"</a>", string.Empty}, {"target='_blank'>", string.Empty}
|
||||
});
|
||||
//verify tmpfile is available and the same
|
||||
data = await net.DownloadStringTaskAsync(new Uri(url));
|
||||
Assert.Equal(fileContent, data);
|
||||
|
||||
|
||||
//delete file
|
||||
Assert.Equal(StatusMessageModel.StatusSeverity.Success, new StatusMessageModel(Assert
|
||||
.IsType<RedirectToActionResult>(await controller.DeleteFile(fileId))
|
||||
.RouteValues["statusMessage"].ToString()).Severity);
|
||||
|
||||
|
||||
//attempt to fetch deleted file
|
||||
viewFilesViewModel =
|
||||
Assert.IsType<ViewFilesViewModel>(Assert.IsType<ViewResult>(await controller.Files(fileId)).Model);
|
||||
|
||||
Assert.Null(viewFilesViewModel.DirectFileUrl);
|
||||
Assert.Null(viewFilesViewModel.SelectedFileId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -95,7 +95,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
public async Task<WalletId> RegisterDerivationSchemeAsync(string cryptoCode, bool segwit = false)
|
||||
{
|
||||
SupportedNetwork = parent.NetworkProvider.GetNetwork(cryptoCode);
|
||||
SupportedNetwork = parent.NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
var store = parent.PayTester.GetController<StoresController>(UserId, StoreId);
|
||||
ExtKey = new ExtKey().GetWif(SupportedNetwork.NBitcoinNetwork);
|
||||
DerivationScheme = new DerivationStrategyFactory(SupportedNetwork.NBitcoinNetwork).Parse(ExtKey.Neuter().ToString() + (segwit ? "" : "-[legacy]"));
|
||||
|
@ -60,6 +60,7 @@ using BTCPayServer.Models.AccountViewModels;
|
||||
using BTCPayServer.Services.U2F.Models;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
@ -98,56 +99,68 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanCalculateCryptoDue2()
|
||||
{
|
||||
var dummy = new Key().PubKey.GetAddress(Network.RegTest).ToString();
|
||||
#pragma warning disable CS0618
|
||||
var dummy = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest).ToString();
|
||||
var networkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
|
||||
{
|
||||
new BitcoinLikePaymentHandler(null, networkProvider, null, null),
|
||||
new LightningLikePaymentHandler(null, null, networkProvider, null),
|
||||
});
|
||||
InvoiceEntity invoiceEntity = new InvoiceEntity();
|
||||
invoiceEntity.Payments = new System.Collections.Generic.List<PaymentEntity>();
|
||||
invoiceEntity.ProductInformation = new ProductInformation() { Price = 100 };
|
||||
invoiceEntity.ProductInformation = new ProductInformation() {Price = 100};
|
||||
PaymentMethodDictionary paymentMethods = new PaymentMethodDictionary();
|
||||
paymentMethods.Add(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "BTC",
|
||||
Rate = 10513.44m,
|
||||
}.SetPaymentMethodDetails(new BTCPayServer.Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
NextNetworkFee = Money.Coins(0.00000100m),
|
||||
DepositAddress = dummy
|
||||
}));
|
||||
paymentMethods.Add(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "LTC",
|
||||
Rate = 216.79m
|
||||
}.SetPaymentMethodDetails(new BTCPayServer.Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
NextNetworkFee = Money.Coins(0.00010000m),
|
||||
DepositAddress = dummy
|
||||
}));
|
||||
paymentMethods.Add(new PaymentMethod() {CryptoCode = "BTC", Rate = 10513.44m,}.SetPaymentMethodDetails(
|
||||
new BTCPayServer.Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
NextNetworkFee = Money.Coins(0.00000100m), DepositAddress = dummy
|
||||
}));
|
||||
paymentMethods.Add(new PaymentMethod() {CryptoCode = "LTC", Rate = 216.79m}.SetPaymentMethodDetails(
|
||||
new BTCPayServer.Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod()
|
||||
{
|
||||
NextNetworkFee = Money.Coins(0.00010000m), DepositAddress = dummy
|
||||
}));
|
||||
invoiceEntity.SetPaymentMethods(paymentMethods);
|
||||
|
||||
var btc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
var btc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
|
||||
var accounting = btc.Calculate();
|
||||
|
||||
invoiceEntity.Payments.Add(new PaymentEntity() { Accounted = true, CryptoCode = "BTC", NetworkFee = 0.00000100m }.SetCryptoPaymentData(new BitcoinLikePaymentData()
|
||||
{
|
||||
Output = new TxOut() { Value = Money.Coins(0.00151263m) }
|
||||
}));
|
||||
invoiceEntity.Payments.Add(
|
||||
new PaymentEntity()
|
||||
{
|
||||
Accounted = true,
|
||||
CryptoCode = "BTC",
|
||||
NetworkFee = 0.00000100m
|
||||
}
|
||||
.SetCryptoPaymentData(new BitcoinLikePaymentData()
|
||||
{
|
||||
Output = new TxOut() {Value = Money.Coins(0.00151263m)}
|
||||
}));
|
||||
accounting = btc.Calculate();
|
||||
invoiceEntity.Payments.Add(new PaymentEntity() { Accounted = true, CryptoCode = "BTC", NetworkFee = 0.00000100m }.SetCryptoPaymentData(new BitcoinLikePaymentData()
|
||||
{
|
||||
Output = new TxOut() { Value = accounting.Due }
|
||||
}));
|
||||
invoiceEntity.Payments.Add(
|
||||
new PaymentEntity()
|
||||
{
|
||||
Accounted = true,
|
||||
CryptoCode = "BTC",
|
||||
NetworkFee = 0.00000100m
|
||||
}
|
||||
.SetCryptoPaymentData(new BitcoinLikePaymentData()
|
||||
{
|
||||
Output = new TxOut() {Value = accounting.Due}
|
||||
}));
|
||||
accounting = btc.Calculate();
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
Assert.Equal(Money.Zero, accounting.DueUncapped);
|
||||
|
||||
var ltc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
|
||||
var ltc = invoiceEntity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
|
||||
accounting = ltc.Calculate();
|
||||
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
// LTC might have over paid due to BTC paying above what it should (round 1 satoshi up)
|
||||
Assert.True(accounting.DueUncapped < Money.Zero);
|
||||
|
||||
var paymentMethod = InvoiceWatcher.GetNearestClearedPayment(paymentMethods, out var accounting2, null);
|
||||
var paymentMethod = InvoiceWatcher.GetNearestClearedPayment(paymentMethods, out var accounting2);
|
||||
Assert.Equal(btc.CryptoCode, paymentMethod.CryptoCode);
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
@ -195,70 +208,97 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanCalculateCryptoDue()
|
||||
{
|
||||
var networkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
|
||||
{
|
||||
new BitcoinLikePaymentHandler(null, networkProvider, null, null),
|
||||
new LightningLikePaymentHandler(null, null, networkProvider, null),
|
||||
});
|
||||
var entity = new InvoiceEntity();
|
||||
#pragma warning disable CS0618
|
||||
entity.Payments = new System.Collections.Generic.List<PaymentEntity>();
|
||||
entity.SetPaymentMethod(new PaymentMethod() { CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m) });
|
||||
entity.ProductInformation = new ProductInformation() { Price = 5000 };
|
||||
entity.SetPaymentMethod(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m)
|
||||
});
|
||||
entity.ProductInformation = new ProductInformation() {Price = 5000};
|
||||
|
||||
var paymentMethod = entity.GetPaymentMethods(null).TryGet("BTC", PaymentTypes.BTCLike);
|
||||
var paymentMethod = entity.GetPaymentMethods().TryGet("BTC", PaymentTypes.BTCLike);
|
||||
var accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { Output = new TxOut(Money.Coins(0.5m), new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
Output = new TxOut(Money.Coins(0.5m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m
|
||||
});
|
||||
|
||||
accounting = paymentMethod.Calculate();
|
||||
//Since we need to spend one more txout, it should be 1.1 - 0,5 + 0.1
|
||||
Assert.Equal(Money.Coins(0.7m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.2m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { Output = new TxOut(Money.Coins(0.2m), new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
Output = new TxOut(Money.Coins(0.2m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m
|
||||
});
|
||||
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(0.6m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.3m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { Output = new TxOut(Money.Coins(0.6m), new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
Output = new TxOut(Money.Coins(0.6m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m
|
||||
});
|
||||
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.3m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { Output = new TxOut(Money.Coins(0.2m), new Key()), Accounted = true });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
Output = new TxOut(Money.Coins(0.2m), new Key()),
|
||||
Accounted = true
|
||||
});
|
||||
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.3m), accounting.TotalDue);
|
||||
|
||||
entity = new InvoiceEntity();
|
||||
entity.ProductInformation = new ProductInformation() { Price = 5000 };
|
||||
entity.ProductInformation = new ProductInformation() {Price = 5000};
|
||||
PaymentMethodDictionary paymentMethods = new PaymentMethodDictionary();
|
||||
paymentMethods.Add(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "BTC",
|
||||
Rate = 1000,
|
||||
NextNetworkFee = Money.Coins(0.1m)
|
||||
});
|
||||
paymentMethods.Add(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "LTC",
|
||||
Rate = 500,
|
||||
NextNetworkFee = Money.Coins(0.01m)
|
||||
});
|
||||
paymentMethods.Add(
|
||||
new PaymentMethod() {CryptoCode = "BTC", Rate = 1000, NextNetworkFee = Money.Coins(0.1m)});
|
||||
paymentMethods.Add(
|
||||
new PaymentMethod() {CryptoCode = "LTC", Rate = 500, NextNetworkFee = Money.Coins(0.01m)});
|
||||
entity.SetPaymentMethods(paymentMethods);
|
||||
entity.Payments = new List<PaymentEntity>();
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(5.1m), accounting.Due);
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
|
||||
accounting = paymentMethod.Calculate();
|
||||
|
||||
Assert.Equal(Money.Coins(10.01m), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
CryptoCode = "BTC",
|
||||
Output = new TxOut(Money.Coins(1.0m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m
|
||||
});
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(4.2m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||
@ -266,17 +306,22 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(Money.Coins(5.2m), accounting.TotalDue);
|
||||
Assert.Equal(2, accounting.TxRequired);
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 - 2.0m /* 8.21m */), accounting.Due);
|
||||
Assert.Equal(Money.Coins(0.0m), accounting.CryptoPaid);
|
||||
Assert.Equal(Money.Coins(2.0m), accounting.Paid);
|
||||
Assert.Equal(Money.Coins(10.01m + 0.1m * 2), accounting.TotalDue);
|
||||
|
||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "LTC", Output = new TxOut(Money.Coins(1.0m), new Key()), Accounted = true, NetworkFee = 0.01m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
CryptoCode = "LTC",
|
||||
Output = new TxOut(Money.Coins(1.0m), new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.01m
|
||||
});
|
||||
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(4.2m - 0.5m + 0.01m / 2), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||
@ -284,7 +329,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(Money.Coins(5.2m + 0.01m / 2), accounting.TotalDue); // The fee for LTC added
|
||||
Assert.Equal(2, accounting.TxRequired);
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(8.21m - 1.0m + 0.01m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||
@ -293,9 +338,15 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(2, accounting.TxRequired);
|
||||
|
||||
var remaining = Money.Coins(4.2m - 0.5m + 0.01m / 2);
|
||||
entity.Payments.Add(new PaymentEntity() { CryptoCode = "BTC", Output = new TxOut(remaining, new Key()), Accounted = true, NetworkFee = 0.1m });
|
||||
entity.Payments.Add(new PaymentEntity()
|
||||
{
|
||||
CryptoCode = "BTC",
|
||||
Output = new TxOut(remaining, new Key()),
|
||||
Accounted = true,
|
||||
NetworkFee = 0.1m
|
||||
});
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike), null);
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("BTC", PaymentTypes.BTCLike));
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.0m) + remaining, accounting.CryptoPaid);
|
||||
@ -304,13 +355,14 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(accounting.Paid, accounting.TotalDue);
|
||||
Assert.Equal(2, accounting.TxRequired);
|
||||
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike), null);
|
||||
paymentMethod = entity.GetPaymentMethod(new PaymentMethodId("LTC", PaymentTypes.BTCLike));
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Zero, accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.0m), accounting.CryptoPaid);
|
||||
Assert.Equal(Money.Coins(3.0m) + remaining * 2, accounting.Paid);
|
||||
// Paying 2 BTC fee, LTC fee removed because fully paid
|
||||
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 + 0.1m * 2 /* + 0.01m no need to pay this fee anymore */), accounting.TotalDue);
|
||||
Assert.Equal(Money.Coins(10.01m + 0.1m * 2 + 0.1m * 2 /* + 0.01m no need to pay this fee anymore */),
|
||||
accounting.TotalDue);
|
||||
Assert.Equal(1, accounting.TxRequired);
|
||||
Assert.Equal(accounting.Paid, accounting.TotalDue);
|
||||
#pragma warning restore CS0618
|
||||
@ -353,30 +405,39 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanAcceptInvoiceWithTolerance()
|
||||
{
|
||||
var networkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
|
||||
{
|
||||
new BitcoinLikePaymentHandler(null, networkProvider, null, null),
|
||||
new LightningLikePaymentHandler(null, null, networkProvider, null),
|
||||
});
|
||||
var entity = new InvoiceEntity();
|
||||
#pragma warning disable CS0618
|
||||
entity.Payments = new List<PaymentEntity>();
|
||||
entity.SetPaymentMethod(new PaymentMethod() { CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m) });
|
||||
entity.ProductInformation = new ProductInformation() { Price = 5000 };
|
||||
entity.SetPaymentMethod(new PaymentMethod()
|
||||
{
|
||||
CryptoCode = "BTC", Rate = 5000, NextNetworkFee = Money.Coins(0.1m)
|
||||
});
|
||||
entity.ProductInformation = new ProductInformation() {Price = 5000};
|
||||
entity.PaymentTolerance = 0;
|
||||
|
||||
|
||||
var paymentMethod = entity.GetPaymentMethods(null).TryGet("BTC", PaymentTypes.BTCLike);
|
||||
var paymentMethod = entity.GetPaymentMethods().TryGet("BTC", PaymentTypes.BTCLike);
|
||||
var accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.Due);
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.TotalDue);
|
||||
Assert.Equal(Money.Coins(1.1m), accounting.MinimumTotalDue);
|
||||
|
||||
entity.PaymentTolerance = 10;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(0.99m), accounting.MinimumTotalDue);
|
||||
entity.PaymentTolerance = 10;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Coins(0.99m), accounting.MinimumTotalDue);
|
||||
|
||||
entity.PaymentTolerance = 100;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Satoshis(1), accounting.MinimumTotalDue);
|
||||
entity.PaymentTolerance = 100;
|
||||
accounting = paymentMethod.Calculate();
|
||||
Assert.Equal(Money.Satoshis(1), accounting.MinimumTotalDue);
|
||||
|
||||
}
|
||||
|
||||
@ -1499,8 +1560,8 @@ namespace BTCPayServer.Tests
|
||||
var testnetNetworkProvider = new BTCPayNetworkProvider(NetworkType.Testnet);
|
||||
var regtestNetworkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
var mainnetNetworkProvider = new BTCPayNetworkProvider(NetworkType.Mainnet);
|
||||
var testnetParser = new DerivationSchemeParser(testnetNetworkProvider.GetNetwork("BTC"));
|
||||
var mainnetParser = new DerivationSchemeParser(mainnetNetworkProvider.GetNetwork("BTC"));
|
||||
var testnetParser = new DerivationSchemeParser(testnetNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
var mainnetParser = new DerivationSchemeParser(mainnetNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
NBXplorer.DerivationStrategy.DerivationStrategyBase result;
|
||||
// Passing electrum stuff
|
||||
// Passing a native segwit from mainnet to a testnet parser, means the testnet parser will try to convert it into segwit
|
||||
@ -1543,16 +1604,16 @@ namespace BTCPayServer.Tests
|
||||
result = testnetParser.Parse(tpub);
|
||||
Assert.Equal($"{tpub}-[p2sh]", result.ToString());
|
||||
|
||||
var regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork("BTC"));
|
||||
var regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
var parsed = regtestParser.Parse("xpub6DG1rMYXiQtCc6CfdLFD9CtxqhzzRh7j6Sq6EdE9abgYy3cfDRrniLLv2AdwqHL1exiLnnKR5XXcaoiiexf3Y9R6J6rxkJtqJHzNzMW9QMZ-[p2sh]");
|
||||
Assert.Equal("tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[p2sh]", parsed.ToString());
|
||||
|
||||
// Let's make sure we can't generate segwit with dogecoin
|
||||
regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork("DOGE"));
|
||||
regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("DOGE"));
|
||||
parsed = regtestParser.Parse("xpub6DG1rMYXiQtCc6CfdLFD9CtxqhzzRh7j6Sq6EdE9abgYy3cfDRrniLLv2AdwqHL1exiLnnKR5XXcaoiiexf3Y9R6J6rxkJtqJHzNzMW9QMZ-[p2sh]");
|
||||
Assert.Equal("tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[legacy]", parsed.ToString());
|
||||
|
||||
regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork("DOGE"));
|
||||
regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("DOGE"));
|
||||
parsed = regtestParser.Parse("tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[p2sh]");
|
||||
Assert.Equal("tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[legacy]", parsed.ToString());
|
||||
}
|
||||
@ -1569,7 +1630,7 @@ namespace BTCPayServer.Tests
|
||||
user.RegisterDerivationScheme("BTC");
|
||||
user.RegisterDerivationScheme("LTC");
|
||||
user.RegisterLightningNode("BTC", LightningConnectionType.CLightning);
|
||||
var btcNetwork = tester.PayTester.Networks.GetNetwork("BTC");
|
||||
var btcNetwork = tester.PayTester.Networks.GetNetwork<BTCPayNetwork>("BTC");
|
||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||
{
|
||||
Price = 1.5m,
|
||||
@ -1672,8 +1733,14 @@ namespace BTCPayServer.Tests
|
||||
var wallet = tester.PayTester.GetController<WalletsController>();
|
||||
var psbt = wallet.CreatePSBT(btcNetwork, onchainBTC, new WalletSendModel()
|
||||
{
|
||||
Amount = 0.5m,
|
||||
Destination = new Key().PubKey.GetAddress(btcNetwork.NBitcoinNetwork).ToString(),
|
||||
Outputs = new List<WalletSendModel.TransactionOutput>()
|
||||
{
|
||||
new WalletSendModel.TransactionOutput()
|
||||
{
|
||||
Amount = 0.5m,
|
||||
DestinationAddress = new Key().PubKey.GetAddress(btcNetwork.NBitcoinNetwork).ToString(),
|
||||
}
|
||||
},
|
||||
FeeSatoshiPerByte = 1
|
||||
}, default).GetAwaiter().GetResult();
|
||||
|
||||
@ -2176,14 +2243,13 @@ donation:
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
|
||||
var firstPayment = invoice.CryptoInfo[0].TotalDue - Money.Coins(0.001m);
|
||||
cashCow.SendToAddress(invoiceAddress, firstPayment);
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
var exportResultPaid = user.GetController<InvoiceController>().Export("csv").GetAwaiter().GetResult();
|
||||
var paidresult = Assert.IsType<ContentResult>(exportResultPaid);
|
||||
Assert.Equal("application/csv", paidresult.ContentType);
|
||||
Assert.Contains($",\"orderId\",\"{invoice.Id}\",", paidresult.Content);
|
||||
Assert.Contains($",\"OnChain\",\"BTC\",\"0.0991\",\"0.0001\",\"5000.0\"", paidresult.Content);
|
||||
Assert.Contains($",\"On-Chain\",\"BTC\",\"0.0991\",\"0.0001\",\"5000.0\"", paidresult.Content);
|
||||
Assert.Contains($",\"USD\",\"5.00", paidresult.Content); // Seems hacky but some plateform does not render this decimal the same
|
||||
Assert.Contains($"0\",\"500.0\",\"\",\"Some ``, description\",\"new (paidPartial)\"", paidresult.Content);
|
||||
});
|
||||
@ -2591,27 +2657,28 @@ donation:
|
||||
{
|
||||
var unusedUri = new Uri("https://toto.com");
|
||||
Assert.True(ExternalConnectionString.TryParse("server=/test", out var connStr, out var error));
|
||||
var expanded = await connStr.Expand(new Uri("https://toto.com"), ExternalServiceTypes.Charge);
|
||||
var expanded = await connStr.Expand(new Uri("https://toto.com"), ExternalServiceTypes.Charge, NetworkType.Mainnet);
|
||||
Assert.Equal(new Uri("https://toto.com/test"), expanded.Server);
|
||||
expanded = await connStr.Expand(new Uri("http://toto.onion"), ExternalServiceTypes.Charge);
|
||||
expanded = await connStr.Expand(new Uri("http://toto.onion"), ExternalServiceTypes.Charge, NetworkType.Mainnet);
|
||||
Assert.Equal(new Uri("http://toto.onion/test"), expanded.Server);
|
||||
await Assert.ThrowsAsync<SecurityException>(() => connStr.Expand(new Uri("http://toto.com"), ExternalServiceTypes.Charge));
|
||||
await Assert.ThrowsAsync<SecurityException>(() => connStr.Expand(new Uri("http://toto.com"), ExternalServiceTypes.Charge, NetworkType.Mainnet));
|
||||
await connStr.Expand(new Uri("http://toto.com"), ExternalServiceTypes.Charge, NetworkType.Testnet);
|
||||
|
||||
// Make sure absolute paths are not expanded
|
||||
Assert.True(ExternalConnectionString.TryParse("server=https://tow/test", out connStr, out error));
|
||||
expanded = await connStr.Expand(new Uri("https://toto.com"), ExternalServiceTypes.Charge);
|
||||
expanded = await connStr.Expand(new Uri("https://toto.com"), ExternalServiceTypes.Charge, NetworkType.Mainnet);
|
||||
Assert.Equal(new Uri("https://tow/test"), expanded.Server);
|
||||
|
||||
// Error if directory not exists
|
||||
Assert.True(ExternalConnectionString.TryParse($"server={unusedUri};macaroondirectorypath=pouet", out connStr, out error));
|
||||
await Assert.ThrowsAsync<DirectoryNotFoundException>(() => connStr.Expand(unusedUri, ExternalServiceTypes.LNDGRPC));
|
||||
await Assert.ThrowsAsync<DirectoryNotFoundException>(() => connStr.Expand(unusedUri, ExternalServiceTypes.LNDRest));
|
||||
await connStr.Expand(unusedUri, ExternalServiceTypes.Charge);
|
||||
await Assert.ThrowsAsync<DirectoryNotFoundException>(() => connStr.Expand(unusedUri, ExternalServiceTypes.LNDGRPC, NetworkType.Mainnet));
|
||||
await Assert.ThrowsAsync<DirectoryNotFoundException>(() => connStr.Expand(unusedUri, ExternalServiceTypes.LNDRest, NetworkType.Mainnet));
|
||||
await connStr.Expand(unusedUri, ExternalServiceTypes.Charge, NetworkType.Mainnet);
|
||||
|
||||
var macaroonDirectory = CreateDirectory();
|
||||
Assert.True(ExternalConnectionString.TryParse($"server={unusedUri};macaroondirectorypath={macaroonDirectory}", out connStr, out error));
|
||||
await connStr.Expand(unusedUri, ExternalServiceTypes.LNDGRPC);
|
||||
expanded = await connStr.Expand(unusedUri, ExternalServiceTypes.LNDRest);
|
||||
await connStr.Expand(unusedUri, ExternalServiceTypes.LNDGRPC, NetworkType.Mainnet);
|
||||
expanded = await connStr.Expand(unusedUri, ExternalServiceTypes.LNDRest, NetworkType.Mainnet);
|
||||
Assert.NotNull(expanded.Macaroons);
|
||||
Assert.Null(expanded.MacaroonFilePath);
|
||||
Assert.Null(expanded.Macaroons.AdminMacaroon);
|
||||
@ -2621,7 +2688,7 @@ donation:
|
||||
File.WriteAllBytes($"{macaroonDirectory}/admin.macaroon", new byte[] { 0xaa });
|
||||
File.WriteAllBytes($"{macaroonDirectory}/invoice.macaroon", new byte[] { 0xab });
|
||||
File.WriteAllBytes($"{macaroonDirectory}/readonly.macaroon", new byte[] { 0xac });
|
||||
expanded = await connStr.Expand(unusedUri, ExternalServiceTypes.LNDRest);
|
||||
expanded = await connStr.Expand(unusedUri, ExternalServiceTypes.LNDRest, NetworkType.Mainnet);
|
||||
Assert.NotNull(expanded.Macaroons.AdminMacaroon);
|
||||
Assert.NotNull(expanded.Macaroons.InvoiceMacaroon);
|
||||
Assert.Equal("ab", expanded.Macaroons.InvoiceMacaroon.Hex);
|
||||
@ -2630,7 +2697,7 @@ donation:
|
||||
|
||||
Assert.True(ExternalConnectionString.TryParse($"server={unusedUri};cookiefilepath={macaroonDirectory}/charge.cookie", out connStr, out error));
|
||||
File.WriteAllText($"{macaroonDirectory}/charge.cookie", "apitoken");
|
||||
expanded = await connStr.Expand(unusedUri, ExternalServiceTypes.Charge);
|
||||
expanded = await connStr.Expand(unusedUri, ExternalServiceTypes.Charge, NetworkType.Mainnet);
|
||||
Assert.Equal("apitoken", expanded.APIToken);
|
||||
}
|
||||
|
||||
@ -2703,7 +2770,7 @@ donation:
|
||||
[Trait("Fast", "Fast")]
|
||||
public void ParseDerivationSchemeSettings()
|
||||
{
|
||||
var mainnet = new BTCPayNetworkProvider(NetworkType.Mainnet).GetNetwork("BTC");
|
||||
var mainnet = new BTCPayNetworkProvider(NetworkType.Mainnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var root = new Mnemonic("usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage").DeriveExtKey();
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromColdcard("{\"keystore\": {\"ckcc_xpub\": \"xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw\", \"xpub\": \"ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}", mainnet, out var settings));
|
||||
Assert.Equal(root.GetPublicKey().GetHDFingerPrint(), settings.AccountKeySettings[0].RootFingerprint);
|
||||
@ -2713,7 +2780,7 @@ donation:
|
||||
Assert.Equal("ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD", settings.AccountOriginal);
|
||||
Assert.Equal(root.Derive(new KeyPath("m/49'/0'/0'")).Neuter().PubKey.WitHash.ScriptPubKey.Hash.ScriptPubKey, settings.AccountDerivation.Derive(new KeyPath()).ScriptPubKey);
|
||||
|
||||
var testnet = new BTCPayNetworkProvider(NetworkType.Testnet).GetNetwork("BTC");
|
||||
var testnet = new BTCPayNetworkProvider(NetworkType.Testnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
|
||||
// Should be legacy
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromColdcard("{\"keystore\": {\"ckcc_xpub\": \"tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS\", \"xpub\": \"tpubDDWYqT3P24znfsaGX7kZcQhNc5LAjnQiKQvUCHF2jS6dsgJBRtymopEU5uGpMaR5YChjuiExZG1X2aTbqXkp82KqH5qnqwWHp6EWis9ZvKr\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/44'/1'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}", testnet, out settings));
|
||||
|
@ -71,7 +71,7 @@ services:
|
||||
|
||||
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.0.0.41
|
||||
image: nicolasdorier/nbxplorer:2.0.0.48
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
@ -134,6 +134,7 @@ services:
|
||||
bind-addr=0.0.0.0
|
||||
announce-addr=customer_lightningd
|
||||
log-level=debug
|
||||
funding-confirms=1
|
||||
dev-broadcast-interval=1000
|
||||
dev-bitcoind-poll=1
|
||||
ports:
|
||||
@ -177,6 +178,7 @@ services:
|
||||
bitcoin-rpcconnect=bitcoind
|
||||
bind-addr=0.0.0.0
|
||||
announce-addr=merchant_lightningd
|
||||
funding-confirms=1
|
||||
network=regtest
|
||||
log-level=debug
|
||||
dev-broadcast-interval=1000
|
||||
@ -240,6 +242,7 @@ services:
|
||||
bitcoind.zmqpubrawblock=tcp://bitcoind:28332
|
||||
bitcoind.zmqpubrawtx=tcp://bitcoind:28333
|
||||
externalip=merchant_lnd:9735
|
||||
bitcoin.defaultchanconfs=1
|
||||
no-macaroons=1
|
||||
debuglevel=debug
|
||||
noseedbackup=1
|
||||
@ -270,6 +273,7 @@ services:
|
||||
bitcoind.zmqpubrawblock=tcp://bitcoind:28332
|
||||
bitcoind.zmqpubrawtx=tcp://bitcoind:28333
|
||||
externalip=customer_lnd:10009
|
||||
bitcoin.defaultchanconfs=1
|
||||
no-macaroons=1
|
||||
debuglevel=debug
|
||||
noseedbackup=1
|
||||
|
@ -1,9 +1,9 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
dotnet test --filter Fast=Fast --no-build
|
||||
dotnet test --filter Selenium=Selenium --no-build -v n
|
||||
dotnet test --filter Integration=Integration --no-build -v n
|
||||
if [[ "$TESTS_RUN_EXTERNAL_INTEGRATION" == "true" ]]; then
|
||||
dotnet test --filter ExternalIntegration=ExternalIntegration --no-build -v n
|
||||
FILTERS=" "
|
||||
if [[ "$TEST_FILTERS" ]]; then
|
||||
FILTERS="--filter $TEST_FILTERS"
|
||||
fi
|
||||
|
||||
dotnet test $FILTERS --no-build -v n
|
||||
|
@ -0,0 +1,6 @@
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId.Models
|
||||
{
|
||||
public class BTCPayOpenIdAuthorization : OpenIddictAuthorization<string, BTCPayOpenIdClient, BTCPayOpenIdToken> { }
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using BTCPayServer.Models;
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId.Models
|
||||
{
|
||||
public class BTCPayOpenIdClient: OpenIddictApplication<string, BTCPayOpenIdAuthorization, BTCPayOpenIdToken>
|
||||
{
|
||||
public string ApplicationUserId { get; set; }
|
||||
public ApplicationUser ApplicationUser { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId.Models
|
||||
{
|
||||
public class BTCPayOpenIdToken : OpenIddictToken<string, BTCPayOpenIdClient, BTCPayOpenIdAuthorization> { }
|
||||
}
|
@ -1,12 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="../Version.csproj" Condition="Exists('../Version.csproj')" />
|
||||
<Import Project="../Common.csproj" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Version>1.0.3.104</Version>
|
||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Build\dockerfiles\**" />
|
||||
@ -34,11 +30,10 @@
|
||||
<EmbeddedResource Include="Currencies.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.0.18" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.0.19" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="2.9.406" />
|
||||
<PackageReference Include="BundlerMinifier.Core" Version="2.9.406" />
|
||||
<PackageReference Include="BundlerMinifier.TagHelpers" Version="2.9.406" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.5.3" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="4.0.207" />
|
||||
<PackageReference Include="LedgerWallet" Version="2.0.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
@ -47,10 +42,8 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NBitcoin" Version="4.1.2.28" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.34" />
|
||||
<PackageReference Include="DBriize" Version="1.0.0.4" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="2.0.0.13" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.2" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
|
||||
@ -72,11 +65,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" />
|
||||
<PackageReference Include="TwentyTwenty.Storage" Version="2.10.1" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Amazon" Version="2.10.1" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Azure" Version="2.10.1" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Google" Version="2.10.1" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Local" Version="2.10.1" />
|
||||
<PackageReference Include="TwentyTwenty.Storage" Version="2.11.2" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Amazon" Version="2.11.2" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Azure" Version="2.11.2" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Google" Version="2.11.2" />
|
||||
<PackageReference Include="TwentyTwenty.Storage.Local" Version="2.11.2" />
|
||||
<PackageReference Include="U2F.Core" Version="1.0.4" />
|
||||
<PackageReference Include="YamlDotNet" Version="5.2.1" />
|
||||
</ItemGroup>
|
||||
@ -140,6 +133,11 @@
|
||||
<Folder Include="wwwroot\vendor\u2f" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Rating\BTCPayServer.Rating.csproj" />
|
||||
<ProjectReference Include="..\BTCPayServer.Common\BTCPayServer.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Views\Apps\_ViewImports.cshtml">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
|
@ -81,12 +81,12 @@ namespace BTCPayServer.Configuration
|
||||
NetworkProvider = new BTCPayNetworkProvider(NetworkType).Filter(supportedChains.ToArray());
|
||||
foreach (var chain in supportedChains)
|
||||
{
|
||||
if (NetworkProvider.GetNetwork(chain) == null)
|
||||
if (NetworkProvider.GetNetwork<BTCPayNetworkBase>(chain) == null)
|
||||
throw new ConfigException($"Invalid chains \"{chain}\"");
|
||||
}
|
||||
|
||||
var validChains = new List<string>();
|
||||
foreach (var net in NetworkProvider.GetAll())
|
||||
foreach (var net in NetworkProvider.GetAll().OfType<BTCPayNetwork>())
|
||||
{
|
||||
NBXplorerConnectionSetting setting = new NBXplorerConnectionSetting();
|
||||
setting.CryptoCode = net.CryptoCode;
|
||||
@ -105,6 +105,8 @@ namespace BTCPayServer.Configuration
|
||||
$"If you have a lightning charge server: 'type=charge;server=https://charge.example.com;api-token=yourapitoken'" + Environment.NewLine +
|
||||
$"If you have a lnd server: 'type=lnd-rest;server=https://lnd:lnd@lnd.example.com;macaroon=abf239...;certthumbprint=2abdf302...'" + Environment.NewLine +
|
||||
$" lnd server: 'type=lnd-rest;server=https://lnd:lnd@lnd.example.com;macaroonfilepath=/root/.lnd/admin.macaroon;certthumbprint=2abdf302...'" + Environment.NewLine +
|
||||
$"If you have an eclair server: 'type=eclair;server=http://eclair.com:4570;password=eclairpassword;bitcoin-host=bitcoind:37393;bitcoin-auth=bitcoinrpcuser:bitcoinrpcpassword" + Environment.NewLine +
|
||||
$" eclair server: 'type=eclair;server=http://eclair.com:4570;password=eclairpassword;bitcoin-host=bitcoind:37393" + Environment.NewLine +
|
||||
$"Error: {error}" + Environment.NewLine +
|
||||
"This service will not be exposed through BTCPay Server");
|
||||
}
|
||||
|
@ -46,13 +46,15 @@ namespace BTCPayServer.Configuration
|
||||
app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue);
|
||||
app.Option("--debugloglevel", "The severity you log (default:information)", CommandOptionType.SingleValue);
|
||||
app.Option("--disable-registration", "Disables new user registrations (default:true)", CommandOptionType.SingleValue);
|
||||
foreach (var network in provider.GetAll())
|
||||
foreach (var network in provider.GetAll().OfType<BTCPayNetwork>())
|
||||
{
|
||||
var crypto = network.CryptoCode.ToLowerInvariant();
|
||||
app.Option($"--{crypto}explorerurl", $"URL of the NBXplorer for {network.CryptoCode} (default: {network.NBXplorerNetwork.DefaultSettings.DefaultUrl})", CommandOptionType.SingleValue);
|
||||
app.Option($"--{crypto}explorercookiefile", $"Path to the cookie file (default: {network.NBXplorerNetwork.DefaultSettings.DefaultCookieFile})", CommandOptionType.SingleValue);
|
||||
app.Option($"--{crypto}lightning", $"Easy configuration of lightning for the server administrator: Must be a UNIX socket of c-lightning (lightning-rpc) or URL to a charge server (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option($"--{crypto}externallndgrpc", $"The LND gRPC configuration BTCPay will expose to easily connect to the internal lnd wallet from Zap wallet (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option($"--{crypto}externallndgrpc", $"The LND gRPC configuration BTCPay will expose to easily connect to the internal lnd wallet from an external wallet (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option($"--{crypto}externallndrest", $"The LND REST configuration BTCPay will expose to easily connect to the internal lnd wallet from an external wallet (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option($"--{crypto}externalrtl", $"The Ride the Lightning configuration so BTCPay will expose to easily open it in server settings (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option($"--{crypto}externalspark", $"Show spark information in Server settings / Server. The connection string to spark server (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option($"--{crypto}externalcharge", $"Show lightning charge information in Server settings/Server. The connection string to charge server (default: empty)", CommandOptionType.SingleValue);
|
||||
}
|
||||
@ -121,7 +123,7 @@ namespace BTCPayServer.Configuration
|
||||
builder.AppendLine("#mysql=User ID=root;Password=myPassword;Host=localhost;Port=3306;Database=myDataBase;");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("### NBXplorer settings ###");
|
||||
foreach (var n in new BTCPayNetworkProvider(networkType).GetAll())
|
||||
foreach (var n in new BTCPayNetworkProvider(networkType).GetAll().OfType<BTCPayNetwork>())
|
||||
{
|
||||
builder.AppendLine($"#{n.CryptoCode}.explorer.url={n.NBXplorerNetwork.DefaultSettings.DefaultUrl}");
|
||||
builder.AppendLine($"#{n.CryptoCode}.explorer.cookiefile={ n.NBXplorerNetwork.DefaultSettings.DefaultCookieFile}");
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer.Configuration
|
||||
{
|
||||
@ -30,13 +31,16 @@ namespace BTCPayServer.Configuration
|
||||
/// Return a connectionString which does not depends on external resources or information like relative path or file path
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<ExternalConnectionString> Expand(Uri absoluteUrlBase, ExternalServiceTypes serviceType)
|
||||
public async Task<ExternalConnectionString> Expand(Uri absoluteUrlBase, ExternalServiceTypes serviceType, NetworkType network)
|
||||
{
|
||||
var connectionString = this.Clone();
|
||||
// Transform relative URI into absolute URI
|
||||
var serviceUri = connectionString.Server.IsAbsoluteUri ? connectionString.Server : ToRelative(absoluteUrlBase, connectionString.Server.ToString());
|
||||
if (!serviceUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) &&
|
||||
!serviceUri.DnsSafeHost.EndsWith(".onion", StringComparison.OrdinalIgnoreCase))
|
||||
var isSecure = network != NetworkType.Mainnet ||
|
||||
serviceUri.Scheme == "https" ||
|
||||
serviceUri.DnsSafeHost.EndsWith(".onion", StringComparison.OrdinalIgnoreCase) ||
|
||||
Extensions.IsLocalNetwork(serviceUri.DnsSafeHost);
|
||||
if (!isSecure)
|
||||
{
|
||||
throw new System.Security.SecurityException($"Insecure transport protocol to access this service, please use HTTPS or TOR");
|
||||
}
|
||||
|
@ -91,6 +91,77 @@ namespace BTCPayServer.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("/apps/{appId}/pos")]
|
||||
[XFrameOptionsAttribute(XFrameOptionsAttribute.XFrameOptions.AllowAll)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
[EnableCors(CorsPolicies.All)]
|
||||
public async Task<IActionResult> ViewPointOfSale(string appId,
|
||||
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal amount,
|
||||
string email,
|
||||
string orderId,
|
||||
string notificationUrl,
|
||||
string redirectUrl,
|
||||
string choiceKey,
|
||||
string posData = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var app = await _AppService.GetApp(appId, AppType.PointOfSale);
|
||||
if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
|
||||
}
|
||||
if (app == null)
|
||||
return NotFound();
|
||||
var settings = app.GetSettings<PointOfSaleSettings>();
|
||||
if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount && !settings.EnableShoppingCart)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
|
||||
}
|
||||
string title = null;
|
||||
var price = 0.0m;
|
||||
ViewPointOfSaleViewModel.Item choice = null;
|
||||
if (!string.IsNullOrEmpty(choiceKey))
|
||||
{
|
||||
var choices = _AppService.Parse(settings.Template, settings.Currency);
|
||||
choice = choices.FirstOrDefault(c => c.Id == choiceKey);
|
||||
if (choice == null)
|
||||
return NotFound();
|
||||
title = choice.Title;
|
||||
price = choice.Price.Value;
|
||||
if (amount > price)
|
||||
price = amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!settings.ShowCustomAmount && !settings.EnableShoppingCart)
|
||||
return NotFound();
|
||||
price = amount;
|
||||
title = settings.Title;
|
||||
}
|
||||
var store = await _AppService.GetStore(app);
|
||||
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
|
||||
var invoice = await _InvoiceController.CreateInvoiceCore(new CreateInvoiceRequest()
|
||||
{
|
||||
ItemCode = choice?.Id,
|
||||
ItemDesc = title,
|
||||
Currency = settings.Currency,
|
||||
Price = price,
|
||||
BuyerEmail = email,
|
||||
OrderId = orderId,
|
||||
NotificationURL =
|
||||
string.IsNullOrEmpty(notificationUrl) ? settings.NotificationUrl : notificationUrl,
|
||||
NotificationEmail = settings.NotificationEmail,
|
||||
RedirectURL = redirectUrl ?? Request.GetDisplayUrl(),
|
||||
FullNotifications = true,
|
||||
ExtendedNotifications = true,
|
||||
PosData = string.IsNullOrEmpty(posData) ? null : posData,
|
||||
RedirectAutomatically = settings.RedirectAutomatically,
|
||||
}, store, HttpContext.Request.GetAbsoluteRoot(),
|
||||
new List<string>() { AppService.GetAppInternalTag(appId) },
|
||||
cancellationToken);
|
||||
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
|
||||
}
|
||||
|
||||
|
||||
[HttpGet]
|
||||
[Route("/apps/{appId}/crowdfund")]
|
||||
@ -215,77 +286,6 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("/apps/{appId}/pos")]
|
||||
[XFrameOptionsAttribute(XFrameOptionsAttribute.XFrameOptions.AllowAll)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
[EnableCors(CorsPolicies.All)]
|
||||
public async Task<IActionResult> ViewPointOfSale(string appId,
|
||||
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal amount,
|
||||
string email,
|
||||
string orderId,
|
||||
string notificationUrl,
|
||||
string redirectUrl,
|
||||
string choiceKey,
|
||||
string posData = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var app = await _AppService.GetApp(appId, AppType.PointOfSale);
|
||||
if (string.IsNullOrEmpty(choiceKey) && amount <= 0)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
|
||||
}
|
||||
if (app == null)
|
||||
return NotFound();
|
||||
var settings = app.GetSettings<PointOfSaleSettings>();
|
||||
if (string.IsNullOrEmpty(choiceKey) && !settings.ShowCustomAmount && !settings.EnableShoppingCart)
|
||||
{
|
||||
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
|
||||
}
|
||||
string title = null;
|
||||
var price = 0.0m;
|
||||
ViewPointOfSaleViewModel.Item choice = null;
|
||||
if (!string.IsNullOrEmpty(choiceKey))
|
||||
{
|
||||
var choices = _AppService.Parse(settings.Template, settings.Currency);
|
||||
choice = choices.FirstOrDefault(c => c.Id == choiceKey);
|
||||
if (choice == null)
|
||||
return NotFound();
|
||||
title = choice.Title;
|
||||
price = choice.Price.Value;
|
||||
if (amount > price)
|
||||
price = amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!settings.ShowCustomAmount && !settings.EnableShoppingCart)
|
||||
return NotFound();
|
||||
price = amount;
|
||||
title = settings.Title;
|
||||
}
|
||||
var store = await _AppService.GetStore(app);
|
||||
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
|
||||
var invoice = await _InvoiceController.CreateInvoiceCore(new CreateInvoiceRequest()
|
||||
{
|
||||
ItemCode = choice?.Id,
|
||||
ItemDesc = title,
|
||||
Currency = settings.Currency,
|
||||
Price = price,
|
||||
BuyerEmail = email,
|
||||
OrderId = orderId,
|
||||
NotificationURL =
|
||||
string.IsNullOrEmpty(notificationUrl) ? settings.NotificationUrl : notificationUrl,
|
||||
NotificationEmail = settings.NotificationEmail,
|
||||
RedirectURL = redirectUrl ?? Request.GetDisplayUrl(),
|
||||
FullNotifications = true,
|
||||
ExtendedNotifications = true,
|
||||
PosData = string.IsNullOrEmpty(posData) ? null : posData,
|
||||
RedirectAutomatically = settings.RedirectAutomatically,
|
||||
}, store, HttpContext.Request.GetAbsoluteRoot(),
|
||||
new List<string>() { AppService.GetAppInternalTag(appId) },
|
||||
cancellationToken);
|
||||
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
|
||||
}
|
||||
|
||||
|
||||
private string GetUserId()
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ namespace BTCPayServer.Controllers
|
||||
HttpClientFactory = httpClientFactory;
|
||||
_cachedServerSettings = cachedServerSettings;
|
||||
}
|
||||
|
||||
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
if (_cachedServerSettings.RootAppType is Services.Apps.AppType.Crowdfund)
|
||||
@ -40,6 +40,20 @@ namespace BTCPayServer.Controllers
|
||||
return res; // return
|
||||
}
|
||||
}
|
||||
else if (_cachedServerSettings.RootAppType is Services.Apps.AppType.PointOfSale)
|
||||
{
|
||||
var serviceProvider = HttpContext.RequestServices;
|
||||
var controller = (AppsPublicController)serviceProvider.GetService(typeof(AppsPublicController));
|
||||
controller.Url = Url;
|
||||
controller.ControllerContext = ControllerContext;
|
||||
var res = await controller.ViewPointOfSale(_cachedServerSettings.RootAppId) as ViewResult;
|
||||
if (res != null)
|
||||
{
|
||||
res.ViewName = "/Views/AppsPublic/ViewPointOfSale.cshtml";
|
||||
return res; // return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return View("Home");
|
||||
}
|
||||
|
@ -7,9 +7,7 @@ using BTCPayServer.Models;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBitpayClient;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
@ -19,15 +17,12 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
private InvoiceController _InvoiceController;
|
||||
private InvoiceRepository _InvoiceRepository;
|
||||
private BTCPayNetworkProvider _NetworkProvider;
|
||||
|
||||
public InvoiceControllerAPI(InvoiceController invoiceController,
|
||||
InvoiceRepository invoceRepository,
|
||||
BTCPayNetworkProvider networkProvider)
|
||||
InvoiceRepository invoceRepository)
|
||||
{
|
||||
this._InvoiceController = invoiceController;
|
||||
this._InvoiceRepository = invoceRepository;
|
||||
this._NetworkProvider = networkProvider;
|
||||
_InvoiceController = invoiceController;
|
||||
_InvoiceRepository = invoceRepository;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -51,8 +46,7 @@ namespace BTCPayServer.Controllers
|
||||
})).FirstOrDefault();
|
||||
if (invoice == null)
|
||||
throw new BitpayHttpException(404, "Object not found");
|
||||
var resp = invoice.EntityToDTO(_NetworkProvider);
|
||||
return new DataWrapper<InvoiceResponse>(resp);
|
||||
return new DataWrapper<InvoiceResponse>(invoice.EntityToDTO());
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("invoices")]
|
||||
@ -82,7 +76,7 @@ namespace BTCPayServer.Controllers
|
||||
};
|
||||
|
||||
var entities = (await _InvoiceRepository.GetInvoices(query))
|
||||
.Select((o) => o.EntityToDTO(_NetworkProvider)).ToArray();
|
||||
.Select((o) => o.EntityToDTO()).ToArray();
|
||||
|
||||
return DataWrapper.Create(entities);
|
||||
}
|
||||
|
@ -19,10 +19,8 @@ using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Invoices.Export;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using NBitcoin;
|
||||
using NBitpayClient;
|
||||
using NBXplorer;
|
||||
@ -77,12 +75,13 @@ namespace BTCPayServer.Controllers
|
||||
StatusMessage = StatusMessage
|
||||
};
|
||||
|
||||
model.Addresses = invoice.HistoricalAddresses.Select(h => new InvoiceDetailsModel.AddressModel
|
||||
{
|
||||
Destination = h.GetAddress(),
|
||||
PaymentMethod = ToString(h.GetPaymentMethodId()),
|
||||
Current = !h.UnAssigned.HasValue
|
||||
}).ToArray();
|
||||
model.Addresses = invoice.HistoricalAddresses.Select(h =>
|
||||
new InvoiceDetailsModel.AddressModel
|
||||
{
|
||||
Destination = h.GetAddress(),
|
||||
PaymentMethod = h.GetPaymentMethodId().ToPrettyString(),
|
||||
Current = !h.UnAssigned.HasValue
|
||||
}).ToArray();
|
||||
|
||||
var details = InvoicePopulatePayments(invoice);
|
||||
model.CryptoPayments = details.CryptoPayments;
|
||||
@ -91,44 +90,39 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
private InvoiceDetailsModel InvoicePopulatePayments(InvoiceEntity invoice)
|
||||
{
|
||||
var model = new InvoiceDetailsModel();
|
||||
|
||||
foreach (var data in invoice.GetPaymentMethods(null))
|
||||
foreach (var data in invoice.GetPaymentMethods())
|
||||
{
|
||||
var accounting = data.Calculate();
|
||||
var paymentMethodId = data.GetId();
|
||||
var cryptoPayment = new InvoiceDetailsModel.CryptoPayment();
|
||||
cryptoPayment.PaymentMethod = ToString(paymentMethodId);
|
||||
cryptoPayment.PaymentMethod = paymentMethodId.ToPrettyString();
|
||||
cryptoPayment.Due = _CurrencyNameTable.DisplayFormatCurrency(accounting.Due.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode);
|
||||
cryptoPayment.Paid = _CurrencyNameTable.DisplayFormatCurrency(accounting.CryptoPaid.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode);
|
||||
cryptoPayment.Overpaid = _CurrencyNameTable.DisplayFormatCurrency(accounting.OverpaidHelper.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode);
|
||||
|
||||
var onchainMethod = data.GetPaymentMethodDetails() as Payments.Bitcoin.BitcoinLikeOnChainPaymentMethod;
|
||||
if (onchainMethod != null)
|
||||
{
|
||||
cryptoPayment.Address = onchainMethod.DepositAddress;
|
||||
}
|
||||
var paymentMethodDetails = data.GetPaymentMethodDetails();
|
||||
cryptoPayment.Address = paymentMethodDetails.GetPaymentDestination();
|
||||
cryptoPayment.Rate = ExchangeRate(data);
|
||||
model.CryptoPayments.Add(cryptoPayment);
|
||||
}
|
||||
|
||||
foreach (var payment in invoice.GetPayments())
|
||||
{
|
||||
var paymentNetwork = _NetworkProvider.GetNetwork(payment.GetCryptoCode());
|
||||
var paymentData = payment.GetCryptoPaymentData();
|
||||
//TODO: abstract
|
||||
if (paymentData is Payments.Bitcoin.BitcoinLikePaymentData onChainPaymentData)
|
||||
{
|
||||
var m = new InvoiceDetailsModel.Payment();
|
||||
m.Crypto = payment.GetPaymentMethodId().CryptoCode;
|
||||
m.DepositAddress = onChainPaymentData.GetDestination(paymentNetwork);
|
||||
m.DepositAddress = onChainPaymentData.GetDestination();
|
||||
|
||||
int confirmationCount = onChainPaymentData.ConfirmationCount;
|
||||
if (confirmationCount >= paymentNetwork.MaxTrackedConfirmation)
|
||||
if (confirmationCount >= payment.Network.MaxTrackedConfirmation)
|
||||
{
|
||||
m.Confirmations = "At least " + (paymentNetwork.MaxTrackedConfirmation);
|
||||
m.Confirmations = "At least " + (payment.Network.MaxTrackedConfirmation);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -137,7 +131,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
m.TransactionId = onChainPaymentData.Outpoint.Hash.ToString();
|
||||
m.ReceivedTime = payment.ReceivedTime;
|
||||
m.TransactionLink = string.Format(CultureInfo.InvariantCulture, paymentNetwork.BlockExplorerLink, m.TransactionId);
|
||||
m.TransactionLink = string.Format(CultureInfo.InvariantCulture, payment.Network.BlockExplorerLink, m.TransactionId);
|
||||
m.Replaced = !payment.Accounted;
|
||||
model.OnChainPayments.Add(m);
|
||||
}
|
||||
@ -146,7 +140,7 @@ namespace BTCPayServer.Controllers
|
||||
var lightningPaymentData = (LightningLikePaymentData)paymentData;
|
||||
model.OffChainPayments.Add(new InvoiceDetailsModel.OffChainPayment()
|
||||
{
|
||||
Crypto = paymentNetwork.CryptoCode,
|
||||
Crypto = payment.Network.CryptoCode,
|
||||
BOLT11 = lightningPaymentData.BOLT11
|
||||
});
|
||||
}
|
||||
@ -154,21 +148,6 @@ namespace BTCPayServer.Controllers
|
||||
return model;
|
||||
}
|
||||
|
||||
private string ToString(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var type = paymentMethodId.PaymentType.ToString();
|
||||
switch (paymentMethodId.PaymentType)
|
||||
{
|
||||
case PaymentTypes.BTCLike:
|
||||
type = "On-Chain";
|
||||
break;
|
||||
case PaymentTypes.LightningLike:
|
||||
type = "Off-Chain";
|
||||
break;
|
||||
}
|
||||
return $"{paymentMethodId.CryptoCode} ({type})";
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("i/{invoiceId}")]
|
||||
[Route("i/{invoiceId}/{paymentMethodId}")]
|
||||
@ -223,7 +202,7 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
|
||||
//TODO: abstract
|
||||
private async Task<PaymentModel> GetInvoiceModel(string invoiceId, PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
|
||||
@ -236,10 +215,11 @@ namespace BTCPayServer.Controllers
|
||||
paymentMethodId = store.GetDefaultPaymentId(_NetworkProvider);
|
||||
isDefaultPaymentId = true;
|
||||
}
|
||||
var network = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode);
|
||||
BTCPayNetworkBase network = _NetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
if (network == null && isDefaultPaymentId)
|
||||
{
|
||||
network = _NetworkProvider.GetAll().FirstOrDefault();
|
||||
//TODO: need to look into a better way for this as it does not scale
|
||||
network = _NetworkProvider.GetAll().OfType<BTCPayNetwork>().FirstOrDefault();
|
||||
paymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
|
||||
}
|
||||
if (invoice == null || network == null)
|
||||
@ -248,18 +228,18 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (!isDefaultPaymentId)
|
||||
return null;
|
||||
var paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider)
|
||||
var paymentMethodTemp = invoice.GetPaymentMethods()
|
||||
.Where(c => paymentMethodId.CryptoCode == c.GetId().CryptoCode)
|
||||
.FirstOrDefault();
|
||||
if (paymentMethodTemp == null)
|
||||
paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider).First();
|
||||
paymentMethodTemp = invoice.GetPaymentMethods().First();
|
||||
network = paymentMethodTemp.Network;
|
||||
paymentMethodId = paymentMethodTemp.GetId();
|
||||
}
|
||||
|
||||
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId, _NetworkProvider);
|
||||
var paymentMethod = invoice.GetPaymentMethod(paymentMethodId);
|
||||
var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
var dto = invoice.EntityToDTO(_NetworkProvider);
|
||||
var dto = invoice.EntityToDTO();
|
||||
var cryptoInfo = dto.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var currency = invoice.ProductInformation.Currency;
|
||||
@ -281,20 +261,19 @@ namespace BTCPayServer.Controllers
|
||||
(1m + (changelly.AmountMarkupPercentage / 100m)))
|
||||
: (decimal?)null;
|
||||
|
||||
|
||||
var paymentMethodHandler = _paymentMethodHandlerDictionary[paymentMethodId];
|
||||
var model = new PaymentModel()
|
||||
{
|
||||
CryptoCode = network.CryptoCode,
|
||||
RootPath = this.Request.PathBase.Value.WithTrailingSlash(),
|
||||
PaymentMethodId = paymentMethodId.ToString(),
|
||||
PaymentMethodName = GetDisplayName(paymentMethodId, network),
|
||||
CryptoImage = GetImage(paymentMethodId, network),
|
||||
IsLightning = paymentMethodId.PaymentType == PaymentTypes.LightningLike,
|
||||
OrderId = invoice.OrderId,
|
||||
InvoiceId = invoice.Id,
|
||||
DefaultLang = storeBlob.DefaultLang ?? "en",
|
||||
HtmlTitle = storeBlob.HtmlTitle ?? "BTCPay Invoice",
|
||||
CustomCSSLink = storeBlob.CustomCSS?.AbsoluteUri,
|
||||
CustomLogoLink = storeBlob.CustomLogo?.AbsoluteUri,
|
||||
CryptoImage = Request.GetRelativePathOrAbsolute(paymentMethodHandler.GetCryptoImage(paymentMethodId)),
|
||||
LightningAmountInSatoshi = storeBlob.LightningAmountInSatoshi,
|
||||
BtcAddress = paymentMethodDetails.GetPaymentDestination(),
|
||||
BtcDue = accounting.Due.ToString(),
|
||||
@ -310,13 +289,7 @@ namespace BTCPayServer.Controllers
|
||||
MerchantRefLink = invoice.RedirectURL ?? "/",
|
||||
RedirectAutomatically = invoice.RedirectAutomatically,
|
||||
StoreName = store.StoreName,
|
||||
InvoiceBitcoinUrl = paymentMethodId.PaymentType == PaymentTypes.BTCLike ? cryptoInfo.PaymentUrls.BIP21 :
|
||||
paymentMethodId.PaymentType == PaymentTypes.LightningLike ? cryptoInfo.PaymentUrls.BOLT11 :
|
||||
throw new NotSupportedException(),
|
||||
PeerInfo = (paymentMethodDetails as LightningLikePaymentMethodDetails)?.NodeInfo,
|
||||
InvoiceBitcoinUrlQR = paymentMethodId.PaymentType == PaymentTypes.BTCLike ? cryptoInfo.PaymentUrls.BIP21 :
|
||||
paymentMethodId.PaymentType == PaymentTypes.LightningLike ? cryptoInfo.PaymentUrls.BOLT11.ToUpperInvariant() :
|
||||
throw new NotSupportedException(),
|
||||
TxCount = accounting.TxRequired,
|
||||
BtcPaid = accounting.Paid.ToString(),
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
@ -332,38 +305,39 @@ namespace BTCPayServer.Controllers
|
||||
CoinSwitchMerchantId = coinswitch?.MerchantId,
|
||||
CoinSwitchMode = coinswitch?.Mode,
|
||||
StoreId = store.Id,
|
||||
AvailableCryptos = invoice.GetPaymentMethods(_NetworkProvider)
|
||||
AvailableCryptos = invoice.GetPaymentMethods()
|
||||
.Where(i => i.Network != null)
|
||||
.Select(kv => new PaymentModel.AvailableCrypto()
|
||||
.Select(kv =>
|
||||
{
|
||||
PaymentMethodId = kv.GetId().ToString(),
|
||||
CryptoCode = kv.GetId().CryptoCode,
|
||||
PaymentMethodName = GetDisplayName(kv.GetId(), kv.Network),
|
||||
IsLightning = kv.GetId().PaymentType == PaymentTypes.LightningLike,
|
||||
CryptoImage = GetImage(kv.GetId(), kv.Network),
|
||||
Link = Url.Action(nameof(Checkout), new { invoiceId = invoiceId, paymentMethodId = kv.GetId().ToString() })
|
||||
var availableCryptoPaymentMethodId = kv.GetId();
|
||||
var availableCryptoHandler = _paymentMethodHandlerDictionary[availableCryptoPaymentMethodId];
|
||||
return new PaymentModel.AvailableCrypto()
|
||||
{
|
||||
PaymentMethodId = kv.GetId().ToString(),
|
||||
CryptoCode = kv.GetId().CryptoCode,
|
||||
PaymentMethodName = availableCryptoHandler.GetPaymentMethodName(availableCryptoPaymentMethodId),
|
||||
IsLightning =
|
||||
kv.GetId().PaymentType == PaymentTypes.LightningLike,
|
||||
CryptoImage = Request.GetRelativePathOrAbsolute(availableCryptoHandler.GetCryptoImage(availableCryptoPaymentMethodId)),
|
||||
Link = Url.Action(nameof(Checkout),
|
||||
new
|
||||
{
|
||||
invoiceId = invoiceId,
|
||||
paymentMethodId = kv.GetId().ToString()
|
||||
})
|
||||
};
|
||||
}).Where(c => c.CryptoImage != "/")
|
||||
.OrderByDescending(a => a.CryptoCode == "BTC").ThenBy(a => a.PaymentMethodName).ThenBy(a => a.IsLightning ? 1 : 0)
|
||||
.ToList()
|
||||
};
|
||||
|
||||
paymentMethodHandler.PreparePaymentModel(model, dto);
|
||||
model.PaymentMethodId = paymentMethodId.ToString();
|
||||
var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);
|
||||
model.TimeLeft = expiration.PrettyPrint();
|
||||
return model;
|
||||
}
|
||||
|
||||
private string GetDisplayName(PaymentMethodId paymentMethodId, BTCPayNetwork network)
|
||||
{
|
||||
return paymentMethodId.PaymentType == PaymentTypes.BTCLike ?
|
||||
network.DisplayName : network.DisplayName + " (Lightning)";
|
||||
}
|
||||
|
||||
private string GetImage(PaymentMethodId paymentMethodId, BTCPayNetwork network)
|
||||
{
|
||||
return paymentMethodId.PaymentType == PaymentTypes.BTCLike ?
|
||||
this.Request.GetRelativePathOrAbsolute(network.CryptoImagePath) : this.Request.GetRelativePathOrAbsolute(network.LightningImagePath);
|
||||
}
|
||||
|
||||
private string OrderAmountFromInvoice(string cryptoCode, ProductInformation productInformation)
|
||||
{
|
||||
// if invoice source currency is the same as currently display currency, no need for "order amount from invoice"
|
||||
@ -520,7 +494,7 @@ namespace BTCPayServer.Controllers
|
||||
[BitpayAPIConstraint(false)]
|
||||
public async Task<IActionResult> Export(string format, string searchTerm = null, int timezoneOffset = 0)
|
||||
{
|
||||
var model = new InvoiceExport(_NetworkProvider, _CurrencyNameTable);
|
||||
var model = new InvoiceExport(_CurrencyNameTable);
|
||||
|
||||
InvoiceQuery invoiceQuery = GetInvoiceQuery(searchTerm, timezoneOffset);
|
||||
invoiceQuery.Skip = 0;
|
||||
@ -539,6 +513,14 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
|
||||
private SelectList GetPaymentMethodsSelectList()
|
||||
{
|
||||
return new SelectList(_paymentMethodHandlerDictionary.Distinct().SelectMany(handler =>
|
||||
handler.GetSupportedPaymentMethods()
|
||||
.Select(id => new SelectListItem(id.ToPrettyString(), id.ToString()))),
|
||||
nameof(SelectListItem.Value),
|
||||
nameof(SelectListItem.Text));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("invoices/create")]
|
||||
@ -553,15 +535,7 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction(nameof(UserStoresController.ListStores), "UserStores");
|
||||
}
|
||||
|
||||
var paymentMethods = new SelectList(_NetworkProvider.GetAll().SelectMany(network => new[]
|
||||
{
|
||||
new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike),
|
||||
new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike)
|
||||
}).Select(id => new SelectListItem(id.ToString(true), id.ToString(false))),
|
||||
nameof(SelectListItem.Value),
|
||||
nameof(SelectListItem.Text));
|
||||
|
||||
return View(new CreateInvoiceModel() { Stores = stores, AvailablePaymentMethods = paymentMethods });
|
||||
return View(new CreateInvoiceModel() { Stores = stores, AvailablePaymentMethods = GetPaymentMethodsSelectList() });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
@ -573,14 +547,7 @@ namespace BTCPayServer.Controllers
|
||||
var stores = await _StoreRepository.GetStoresByUserId(GetUserId());
|
||||
model.Stores = new SelectList(stores, nameof(StoreData.Id), nameof(StoreData.StoreName), model.StoreId);
|
||||
|
||||
var paymentMethods = new SelectList(_NetworkProvider.GetAll().SelectMany(network => new[]
|
||||
{
|
||||
new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike),
|
||||
new PaymentMethodId(network.CryptoCode, PaymentTypes.LightningLike)
|
||||
}).Select(id => new SelectListItem(id.ToString(true), id.ToString(false))),
|
||||
nameof(SelectListItem.Value),
|
||||
nameof(SelectListItem.Text));
|
||||
model.AvailablePaymentMethods = paymentMethods;
|
||||
model.AvailablePaymentMethods = GetPaymentMethodsSelectList();
|
||||
|
||||
var store = stores.FirstOrDefault(s => s.Id == model.StoreId);
|
||||
if (store == null)
|
||||
|
@ -36,7 +36,7 @@ namespace BTCPayServer.Controllers
|
||||
private CurrencyNameTable _CurrencyNameTable;
|
||||
EventAggregator _EventAggregator;
|
||||
BTCPayNetworkProvider _NetworkProvider;
|
||||
private readonly BTCPayWalletProvider _WalletProvider;
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
IServiceProvider _ServiceProvider;
|
||||
public InvoiceController(
|
||||
IServiceProvider serviceProvider,
|
||||
@ -46,9 +46,9 @@ namespace BTCPayServer.Controllers
|
||||
RateFetcher rateProvider,
|
||||
StoreRepository storeRepository,
|
||||
EventAggregator eventAggregator,
|
||||
BTCPayWalletProvider walletProvider,
|
||||
ContentSecurityPolicies csp,
|
||||
BTCPayNetworkProvider networkProvider)
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
|
||||
{
|
||||
_ServiceProvider = serviceProvider;
|
||||
_CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
|
||||
@ -58,7 +58,7 @@ namespace BTCPayServer.Controllers
|
||||
_UserManager = userManager;
|
||||
_EventAggregator = eventAggregator;
|
||||
_NetworkProvider = networkProvider;
|
||||
_WalletProvider = walletProvider;
|
||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||
_CSP = csp;
|
||||
}
|
||||
|
||||
@ -67,13 +67,10 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (!store.HasClaim(Policies.CanCreateInvoice.Key))
|
||||
throw new UnauthorizedAccessException();
|
||||
invoice.Currency = invoice.Currency?.ToUpperInvariant() ?? "USD";
|
||||
InvoiceLogs logs = new InvoiceLogs();
|
||||
logs.Write("Creation of invoice starting");
|
||||
var entity = new InvoiceEntity
|
||||
{
|
||||
Version = InvoiceEntity.Lastest_Version,
|
||||
InvoiceTime = DateTimeOffset.UtcNow
|
||||
};
|
||||
var entity = _InvoiceRepository.CreateNewInvoice();
|
||||
|
||||
var getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id);
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
@ -148,7 +145,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
foreach (var network in store.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.Where(s => !excludeFilter.Match(s.PaymentId))
|
||||
.Select(c => _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode))
|
||||
.Select(c => _NetworkProvider.GetNetwork<BTCPayNetworkBase>(c.PaymentId.CryptoCode))
|
||||
.Where(c => c != null))
|
||||
{
|
||||
currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, invoice.Currency));
|
||||
@ -162,11 +159,11 @@ namespace BTCPayServer.Controllers
|
||||
var fetchingByCurrencyPair = _RateProvider.FetchRates(currencyPairsToFetch, rateRules, cancellationToken);
|
||||
var fetchingAll = WhenAllFetched(logs, fetchingByCurrencyPair);
|
||||
var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.Where(s => !excludeFilter.Match(s.PaymentId))
|
||||
.Where(s => !excludeFilter.Match(s.PaymentId) && _paymentMethodHandlerDictionary.Support(s.PaymentId))
|
||||
.Select(c =>
|
||||
(Handler: (IPaymentMethodHandler)_ServiceProvider.GetService(typeof(IPaymentMethodHandler<>).MakeGenericType(c.GetType())),
|
||||
(Handler: _paymentMethodHandlerDictionary[c.PaymentId],
|
||||
SupportedPaymentMethod: c,
|
||||
Network: _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode)))
|
||||
Network: _NetworkProvider.GetNetwork<BTCPayNetworkBase>(c.PaymentId.CryptoCode)))
|
||||
.Where(c => c.Network != null)
|
||||
.Select(o =>
|
||||
(SupportedPaymentMethod: o.SupportedPaymentMethod,
|
||||
@ -205,7 +202,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
using (logs.Measure("Saving invoice"))
|
||||
{
|
||||
entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, _NetworkProvider);
|
||||
entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity);
|
||||
}
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
@ -220,7 +217,7 @@ namespace BTCPayServer.Controllers
|
||||
await _InvoiceRepository.AddInvoiceLogs(entity.Id, logs);
|
||||
});
|
||||
_EventAggregator.Publish(new Events.InvoiceEvent(entity, 1001, InvoiceEvent.Created));
|
||||
var resp = entity.EntityToDTO(_NetworkProvider);
|
||||
var resp = entity.EntityToDTO();
|
||||
return new DataWrapper<InvoiceResponse>(resp) { Facade = "pos/invoice" };
|
||||
}
|
||||
|
||||
@ -243,11 +240,11 @@ namespace BTCPayServer.Controllers
|
||||
}).ToArray());
|
||||
}
|
||||
|
||||
private async Task<PaymentMethod> CreatePaymentMethodAsync(Dictionary<CurrencyPair, Task<RateResult>> fetchingByCurrencyPair, IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetwork network, InvoiceEntity entity, StoreData store, InvoiceLogs logs)
|
||||
private async Task<PaymentMethod> CreatePaymentMethodAsync(Dictionary<CurrencyPair, Task<RateResult>> fetchingByCurrencyPair, IPaymentMethodHandler handler, ISupportedPaymentMethod supportedPaymentMethod, BTCPayNetworkBase network, InvoiceEntity entity, StoreData store, InvoiceLogs logs)
|
||||
{
|
||||
try
|
||||
{
|
||||
var logPrefix = $"{supportedPaymentMethod.PaymentId.ToString(true)}:";
|
||||
var logPrefix = $"{supportedPaymentMethod.PaymentId.ToPrettyString()}:";
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
var preparePayment = handler.PreparePayment(supportedPaymentMethod, store, network);
|
||||
var rate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, entity.ProductInformation.Currency)];
|
||||
@ -268,38 +265,15 @@ namespace BTCPayServer.Controllers
|
||||
paymentMethod.SetPaymentMethodDetails(paymentDetails);
|
||||
}
|
||||
|
||||
Func<Money, Money, bool> compare = null;
|
||||
CurrencyValue limitValue = null;
|
||||
string errorMessage = null;
|
||||
if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.LightningLike &&
|
||||
storeBlob.LightningMaxValue != null)
|
||||
var errorMessage = await
|
||||
handler
|
||||
.IsPaymentMethodAllowedBasedOnInvoiceAmount(storeBlob, fetchingByCurrencyPair,
|
||||
paymentMethod.Calculate().Due, supportedPaymentMethod.PaymentId);
|
||||
if (errorMessage != null)
|
||||
{
|
||||
compare = (a, b) => a > b;
|
||||
limitValue = storeBlob.LightningMaxValue;
|
||||
errorMessage = "The amount of the invoice is too high to be paid with lightning";
|
||||
logs.Write($"{logPrefix} {errorMessage}");
|
||||
return null;
|
||||
}
|
||||
else if (supportedPaymentMethod.PaymentId.PaymentType == PaymentTypes.BTCLike &&
|
||||
storeBlob.OnChainMinValue != null)
|
||||
{
|
||||
compare = (a, b) => a < b;
|
||||
limitValue = storeBlob.OnChainMinValue;
|
||||
errorMessage = "The amount of the invoice is too low to be paid on chain";
|
||||
}
|
||||
|
||||
if (compare != null)
|
||||
{
|
||||
var limitValueRate = await fetchingByCurrencyPair[new CurrencyPair(network.CryptoCode, limitValue.Currency)];
|
||||
if (limitValueRate.BidAsk != null)
|
||||
{
|
||||
var limitValueCrypto = Money.Coins(limitValue.Value / limitValueRate.BidAsk.Bid);
|
||||
if (compare(paymentMethod.Calculate().Due, limitValueCrypto))
|
||||
{
|
||||
logs.Write($"{logPrefix} {errorMessage}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
///////////////
|
||||
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
@ -41,7 +41,7 @@ namespace BTCPayServer.Controllers
|
||||
try
|
||||
{
|
||||
var paymentMethodDetails = GetExistingLightningSupportedPaymentMethod(cryptoCode, store);
|
||||
var network = _BtcPayNetworkProvider.GetNetwork(cryptoCode);
|
||||
var network = _BtcPayNetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
var nodeInfo =
|
||||
await _LightningLikePaymentHandler.GetNodeInfo(this.Request.IsOnion(), paymentMethodDetails,
|
||||
network);
|
||||
|
@ -27,7 +27,7 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<IActionResult> Files(string fileId = null, string statusMessage = null)
|
||||
{
|
||||
TempData["StatusMessage"] = statusMessage;
|
||||
var fileUrl = string.IsNullOrEmpty(fileId) ? null : await _FileService.GetFileUrl(fileId);
|
||||
var fileUrl = string.IsNullOrEmpty(fileId) ? null : await _FileService.GetFileUrl(Request.GetAbsoluteRootUri(), fileId);
|
||||
|
||||
return View(new ViewFilesViewModel()
|
||||
{
|
||||
@ -116,12 +116,13 @@ namespace BTCPayServer.Controllers
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
var url = await _FileService.GetTemporaryFileUrl(fileId, expiry, viewModel.IsDownload);
|
||||
var url = await _FileService.GetTemporaryFileUrl(Request.GetAbsoluteRootUri(), fileId, expiry, viewModel.IsDownload);
|
||||
|
||||
return RedirectToAction(nameof(Files), new
|
||||
{
|
||||
StatusMessage = new StatusMessageModel()
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Success,
|
||||
Html =
|
||||
$"Generated Temporary Url for file {file.FileName} which expires at {expiry.ToBrowserDate()}. <a href='{url}' target='_blank'>{url}</a>"
|
||||
}.ToString(),
|
||||
|
@ -604,7 +604,7 @@ namespace BTCPayServer.Controllers
|
||||
ServiceLink = service.ConnectionString.Server.AbsoluteUri.WithoutEndingSlash()
|
||||
});
|
||||
}
|
||||
var connectionString = await service.ConnectionString.Expand(this.Request.GetAbsoluteUriNoPathBase(), service.Type);
|
||||
var connectionString = await service.ConnectionString.Expand(this.Request.GetAbsoluteUriNoPathBase(), service.Type, _Options.NetworkType);
|
||||
switch (service.Type)
|
||||
{
|
||||
case ExternalServiceTypes.Charge:
|
||||
@ -720,7 +720,7 @@ namespace BTCPayServer.Controllers
|
||||
ExternalConnectionString connectionString = null;
|
||||
try
|
||||
{
|
||||
connectionString = await service.ConnectionString.Expand(this.Request.GetAbsoluteUriNoPathBase(), service.Type);
|
||||
connectionString = await service.ConnectionString.Expand(this.Request.GetAbsoluteUriNoPathBase(), service.Type, _Options.NetworkType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -875,14 +875,16 @@ namespace BTCPayServer.Controllers
|
||||
.ToList();
|
||||
vm.LogFileOffset = offset;
|
||||
|
||||
if (string.IsNullOrEmpty(file))
|
||||
if (string.IsNullOrEmpty(file) || !file.EndsWith(fileExtension, StringComparison.Ordinal))
|
||||
return View("Logs", vm);
|
||||
vm.Log = "";
|
||||
var path = Path.Combine(di.FullName, file);
|
||||
var fi = vm.LogFiles.FirstOrDefault(o => o.Name == file);
|
||||
if (fi == null)
|
||||
return NotFound();
|
||||
try
|
||||
{
|
||||
using (var fileStream = new FileStream(
|
||||
path,
|
||||
fi.FullName,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.ReadWrite))
|
||||
|
@ -1,23 +1,28 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Storage.Services;
|
||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BTCPayServer.Storage
|
||||
{
|
||||
[Route("Storage")]
|
||||
public class StorageController
|
||||
public class StorageController : Controller
|
||||
{
|
||||
private readonly FileService _FileService;
|
||||
private string _dir;
|
||||
|
||||
public StorageController(FileService fileService)
|
||||
public StorageController(FileService fileService, BTCPayServerOptions serverOptions)
|
||||
{
|
||||
_FileService = fileService;
|
||||
_dir =FileSystemFileProviderService.GetTempStorageDir(serverOptions);
|
||||
}
|
||||
|
||||
[HttpGet("{fileId}")]
|
||||
public async Task<IActionResult> GetFile(string fileId)
|
||||
{
|
||||
var url = await _FileService.GetFileUrl(fileId);
|
||||
var url = await _FileService.GetFileUrl(Request.GetAbsoluteRootUri(), fileId);
|
||||
return new RedirectResult(url);
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ namespace BTCPayServer.Controllers
|
||||
DerivationSchemeViewModel vm = new DerivationSchemeViewModel();
|
||||
vm.CryptoCode = cryptoCode;
|
||||
vm.RootKeyPath = network.GetRootKeyPath();
|
||||
vm.Network = network;
|
||||
SetExistingValues(store, vm);
|
||||
return View(vm);
|
||||
}
|
||||
@ -65,7 +66,7 @@ namespace BTCPayServer.Controllers
|
||||
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
var hw = new LedgerHardwareWalletService(webSocket);
|
||||
object result = null;
|
||||
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
||||
var network = _NetworkProvider.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
|
||||
using (var normalOperationTimeout = new CancellationTokenSource())
|
||||
{
|
||||
@ -119,6 +120,8 @@ namespace BTCPayServer.Controllers
|
||||
return new EmptyResult();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void SetExistingValues(StoreData store, DerivationSchemeViewModel vm)
|
||||
{
|
||||
var derivation = GetExistingDerivationStrategy(vm.CryptoCode, store);
|
||||
@ -157,6 +160,7 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
vm.Network = network;
|
||||
vm.RootKeyPath = network.GetRootKeyPath();
|
||||
DerivationSchemeSettings strategy = null;
|
||||
|
||||
@ -337,6 +341,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
vm.Confirmation = true;
|
||||
ModelState.Remove(nameof(vm.Config)); // Remove the cached value
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ namespace BTCPayServer.Controllers
|
||||
ModelState.AddModelError(nameof(vm.ConnectionString), "Missing url parameter");
|
||||
return View(vm);
|
||||
case "test":
|
||||
var handler = (LightningLikePaymentHandler)_ServiceProvider.GetRequiredService<IPaymentMethodHandler<Payments.Lightning.LightningSupportedPaymentMethod>>();
|
||||
var handler = _ServiceProvider.GetRequiredService<LightningLikePaymentHandler>();
|
||||
try
|
||||
{
|
||||
var info = await handler.GetNodeInfo(this.Request.IsOnion(), paymentMethod, network);
|
||||
|
@ -17,6 +17,7 @@ using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
@ -34,7 +35,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Route("stores")]
|
||||
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key, AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
[AutoValidateAntiforgeryToken]
|
||||
public partial class StoresController : Controller
|
||||
{
|
||||
@ -56,7 +57,8 @@ namespace BTCPayServer.Controllers
|
||||
LanguageService langService,
|
||||
ChangellyClientProvider changellyClientProvider,
|
||||
IOptions<MvcJsonOptions> mvcJsonOptions,
|
||||
IHostingEnvironment env, IHttpClientFactory httpClientFactory)
|
||||
IHostingEnvironment env, IHttpClientFactory httpClientFactory,
|
||||
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
|
||||
{
|
||||
_RateFactory = rateFactory;
|
||||
_Repo = repo;
|
||||
@ -69,6 +71,7 @@ namespace BTCPayServer.Controllers
|
||||
_WalletProvider = walletProvider;
|
||||
_Env = env;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
|
||||
_NetworkProvider = networkProvider;
|
||||
_ExplorerProvider = explorerProvider;
|
||||
_FeeRateProvider = feeRateProvider;
|
||||
@ -91,6 +94,7 @@ namespace BTCPayServer.Controllers
|
||||
private readonly ChangellyClientProvider _changellyClientProvider;
|
||||
IHostingEnvironment _Env;
|
||||
private IHttpClientFactory _httpClientFactory;
|
||||
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
|
||||
|
||||
[TempData]
|
||||
public string StatusMessage
|
||||
@ -362,7 +366,7 @@ namespace BTCPayServer.Controllers
|
||||
void SetCryptoCurrencies(CheckoutExperienceViewModel vm, Data.StoreData storeData)
|
||||
{
|
||||
var choices = storeData.GetEnabledPaymentIds(_NetworkProvider)
|
||||
.Select(o => new CheckoutExperienceViewModel.Format() { Name = GetDisplayName(o), Value = o.ToString(), PaymentId = o }).ToArray();
|
||||
.Select(o => new CheckoutExperienceViewModel.Format() { Name = o.ToPrettyString(), Value = o.ToString(), PaymentId = o }).ToArray();
|
||||
|
||||
var defaultPaymentId = storeData.GetDefaultPaymentId(_NetworkProvider);
|
||||
var chosen = choices.FirstOrDefault(c => c.PaymentId == defaultPaymentId);
|
||||
@ -370,13 +374,6 @@ namespace BTCPayServer.Controllers
|
||||
vm.DefaultPaymentMethod = chosen?.Value;
|
||||
}
|
||||
|
||||
private string GetDisplayName(PaymentMethodId paymentMethodId)
|
||||
{
|
||||
var display = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode)?.DisplayName ?? paymentMethodId.CryptoCode;
|
||||
return paymentMethodId.PaymentType == PaymentTypes.BTCLike ?
|
||||
display : $"{display} (Lightning)";
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{storeId}/checkout")]
|
||||
public async Task<IActionResult> CheckoutExperience(CheckoutExperienceViewModel model)
|
||||
@ -472,36 +469,38 @@ namespace BTCPayServer.Controllers
|
||||
.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<DerivationSchemeSettings>()
|
||||
.ToDictionary(c => c.Network.CryptoCode);
|
||||
foreach (var network in _NetworkProvider.GetAll())
|
||||
{
|
||||
var strategy = derivationByCryptoCode.TryGet(network.CryptoCode);
|
||||
vm.DerivationSchemes.Add(new StoreViewModel.DerivationScheme()
|
||||
{
|
||||
Crypto = network.CryptoCode,
|
||||
Value = strategy?.ToPrettyString() ?? string.Empty,
|
||||
WalletId = new WalletId(store.Id, network.CryptoCode),
|
||||
Enabled = !excludeFilters.Match(new Payments.PaymentMethodId(network.CryptoCode, Payments.PaymentTypes.BTCLike))
|
||||
});
|
||||
}
|
||||
|
||||
var lightningByCryptoCode = store
|
||||
.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<Payments.Lightning.LightningSupportedPaymentMethod>()
|
||||
.ToDictionary(c => c.CryptoCode);
|
||||
.GetSupportedPaymentMethods(_NetworkProvider)
|
||||
.OfType<LightningSupportedPaymentMethod>()
|
||||
.ToDictionary(c => c.CryptoCode);
|
||||
|
||||
foreach (var network in _NetworkProvider.GetAll())
|
||||
foreach (var paymentMethodId in _paymentMethodHandlerDictionary.Distinct().SelectMany(handler => handler.GetSupportedPaymentMethods()))
|
||||
{
|
||||
var lightning = lightningByCryptoCode.TryGet(network.CryptoCode);
|
||||
var paymentId = new Payments.PaymentMethodId(network.CryptoCode, Payments.PaymentTypes.LightningLike);
|
||||
vm.LightningNodes.Add(new StoreViewModel.LightningNode()
|
||||
switch (paymentMethodId.PaymentType)
|
||||
{
|
||||
CryptoCode = network.CryptoCode,
|
||||
Address = lightning?.GetLightningUrl()?.BaseUri.AbsoluteUri ?? string.Empty,
|
||||
Enabled = !excludeFilters.Match(paymentId)
|
||||
});
|
||||
case BitcoinPaymentType _:
|
||||
var strategy = derivationByCryptoCode.TryGet(paymentMethodId.CryptoCode);
|
||||
vm.DerivationSchemes.Add(new StoreViewModel.DerivationScheme()
|
||||
{
|
||||
Crypto = paymentMethodId.CryptoCode,
|
||||
Value = strategy?.ToPrettyString() ?? string.Empty,
|
||||
WalletId = new WalletId(store.Id, paymentMethodId.CryptoCode),
|
||||
Enabled = !excludeFilters.Match(paymentMethodId)
|
||||
});
|
||||
break;
|
||||
case LightningPaymentType _:
|
||||
var lightning = lightningByCryptoCode.TryGet(paymentMethodId.CryptoCode);
|
||||
vm.LightningNodes.Add(new StoreViewModel.LightningNode()
|
||||
{
|
||||
CryptoCode = paymentMethodId.CryptoCode,
|
||||
Address = lightning?.GetLightningUrl()?.BaseUri.AbsoluteUri ?? string.Empty,
|
||||
Enabled = !excludeFilters.Match(paymentMethodId)
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var changellyEnabled = storeBlob.ChangellySettings != null && storeBlob.ChangellySettings.Enabled;
|
||||
vm.ThirdPartyPaymentMethods.Add(new StoreViewModel.ThirdPartyPaymentMethod()
|
||||
{
|
||||
|
@ -19,21 +19,28 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
var nbx = ExplorerClientProvider.GetExplorerClient(network);
|
||||
CreatePSBTRequest psbtRequest = new CreatePSBTRequest();
|
||||
CreatePSBTDestination psbtDestination = new CreatePSBTDestination();
|
||||
psbtRequest.Destinations.Add(psbtDestination);
|
||||
|
||||
foreach (var transactionOutput in sendModel.Outputs)
|
||||
{
|
||||
var psbtDestination = new CreatePSBTDestination();
|
||||
psbtRequest.Destinations.Add(psbtDestination);
|
||||
psbtDestination.Destination = BitcoinAddress.Create(transactionOutput.DestinationAddress, network.NBitcoinNetwork);
|
||||
psbtDestination.Amount = Money.Coins(transactionOutput.Amount.Value);
|
||||
psbtDestination.SubstractFees = transactionOutput.SubtractFeesFromOutput;
|
||||
}
|
||||
|
||||
if (network.SupportRBF)
|
||||
{
|
||||
psbtRequest.RBF = !sendModel.DisableRBF;
|
||||
}
|
||||
psbtDestination.Destination = BitcoinAddress.Create(sendModel.Destination, network.NBitcoinNetwork);
|
||||
psbtDestination.Amount = Money.Coins(sendModel.Amount.Value);
|
||||
|
||||
psbtRequest.FeePreference = new FeePreference();
|
||||
psbtRequest.FeePreference.ExplicitFeeRate = new FeeRate(Money.Satoshis(sendModel.FeeSatoshiPerByte), 1);
|
||||
if (sendModel.NoChange)
|
||||
{
|
||||
psbtRequest.ExplicitChangeAddress = psbtDestination.Destination;
|
||||
psbtRequest.ExplicitChangeAddress = psbtRequest.Destinations.First().Destination;
|
||||
}
|
||||
psbtDestination.SubstractFees = sendModel.SubstractFees;
|
||||
|
||||
var psbt = (await nbx.CreatePSBTAsync(derivationSettings.AccountDerivation, psbtRequest, cancellationToken));
|
||||
if (psbt == null)
|
||||
throw new NotSupportedException("You need to update your version of NBXplorer");
|
||||
@ -42,13 +49,14 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
[HttpGet]
|
||||
[Route("{walletId}/psbt")]
|
||||
public IActionResult WalletPSBT([ModelBinder(typeof(WalletIdModelBinder))]
|
||||
public async Task<IActionResult> WalletPSBT([ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, WalletPSBTViewModel vm)
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
|
||||
if (vm?.PSBT != null)
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
if (await vm.GetPSBT(network.NBitcoinNetwork) is PSBT psbt)
|
||||
{
|
||||
vm.Decoded = vm.GetPSBT(network.NBitcoinNetwork)?.ToString();
|
||||
vm.Decoded = psbt.ToString();
|
||||
vm.PSBT = psbt.ToBase64();
|
||||
}
|
||||
return View(vm ?? new WalletPSBTViewModel());
|
||||
}
|
||||
@ -59,26 +67,37 @@ namespace BTCPayServer.Controllers
|
||||
WalletId walletId,
|
||||
WalletPSBTViewModel vm, string command = null)
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
|
||||
var psbt = vm.GetPSBT(network.NBitcoinNetwork);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
var psbt = await vm.GetPSBT(network.NBitcoinNetwork);
|
||||
if (psbt == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case null:
|
||||
vm.Decoded = psbt.ToString();
|
||||
vm.FileName = string.Empty;
|
||||
ModelState.Remove(nameof(vm.PSBT));
|
||||
ModelState.Remove(nameof(vm.FileName));
|
||||
ModelState.Remove(nameof(vm.UploadedPSBTFile));
|
||||
vm.PSBT = psbt.ToBase64();
|
||||
vm.FileName = vm.UploadedPSBTFile?.FileName;
|
||||
return View(vm);
|
||||
case "ledger":
|
||||
return ViewWalletSendLedger(psbt);
|
||||
case "update":
|
||||
var derivationSchemeSettings = await GetDerivationSchemeSettings(walletId);
|
||||
psbt = await UpdatePSBT(derivationSchemeSettings, psbt, network);
|
||||
if (psbt == null)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.PSBT), "You need to update your version of NBXplorer");
|
||||
return View(vm);
|
||||
}
|
||||
StatusMessage = "PSBT updated!";
|
||||
return RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.ToBase64(), FileName = vm.FileName });
|
||||
case "seed":
|
||||
return SignWithSeed(walletId, psbt.ToBase64());
|
||||
case "broadcast" when !psbt.IsAllFinalized() && !psbt.TryFinalize(out var errors):
|
||||
return ViewPSBT(psbt, errors);
|
||||
case "broadcast":
|
||||
{
|
||||
return await WalletPSBTReady(walletId, psbt.ToBase64());
|
||||
@ -93,6 +112,19 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<PSBT> UpdatePSBT(DerivationSchemeSettings derivationSchemeSettings, PSBT psbt, BTCPayNetwork network)
|
||||
{
|
||||
var result = await ExplorerClientProvider.GetExplorerClient(network).UpdatePSBTAsync(new UpdatePSBTRequest()
|
||||
{
|
||||
PSBT = psbt,
|
||||
DerivationScheme = derivationSchemeSettings.AccountDerivation,
|
||||
});
|
||||
if (result == null)
|
||||
return null;
|
||||
derivationSchemeSettings.RebaseKeyPaths(result.PSBT);
|
||||
return result.PSBT;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{walletId}/psbt/ready")]
|
||||
public async Task<IActionResult> WalletPSBTReady(
|
||||
@ -101,20 +133,24 @@ namespace BTCPayServer.Controllers
|
||||
string signingKey = null,
|
||||
string signingKeyPath = null)
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
var vm = new WalletPSBTReadyViewModel() { PSBT = psbt };
|
||||
vm.SigningKey = signingKey;
|
||||
vm.SigningKeyPath = signingKeyPath;
|
||||
await FetchTransactionDetails(walletId, vm, network);
|
||||
|
||||
var derivationSchemeSettings = await GetDerivationSchemeSettings(walletId);
|
||||
if (derivationSchemeSettings == null)
|
||||
return NotFound();
|
||||
await FetchTransactionDetails(derivationSchemeSettings, vm, network);
|
||||
return View(nameof(WalletPSBTReady), vm);
|
||||
}
|
||||
|
||||
private async Task FetchTransactionDetails(WalletId walletId, WalletPSBTReadyViewModel vm, BTCPayNetwork network)
|
||||
private async Task FetchTransactionDetails(DerivationSchemeSettings derivationSchemeSettings, WalletPSBTReadyViewModel vm, BTCPayNetwork network)
|
||||
{
|
||||
var psbtObject = PSBT.Parse(vm.PSBT, network.NBitcoinNetwork);
|
||||
psbtObject = await UpdatePSBT(derivationSchemeSettings, psbtObject, network) ?? psbtObject;
|
||||
IHDKey signingKey = null;
|
||||
RootedKeyPath signingKeyPath = null;
|
||||
|
||||
try
|
||||
{
|
||||
signingKey = new BitcoinExtPubKey(vm.SigningKey, network.NBitcoinNetwork);
|
||||
@ -134,7 +170,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (signingKey == null || signingKeyPath == null)
|
||||
{
|
||||
var signingKeySettings = (await GetDerivationSchemeSettings(walletId)).GetSigningAccountKeySettings();
|
||||
var signingKeySettings = derivationSchemeSettings.GetSigningAccountKeySettings();
|
||||
if (signingKey == null)
|
||||
{
|
||||
signingKey = signingKeySettings.AccountKey;
|
||||
@ -147,16 +183,36 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
var balanceChange = psbtObject.GetBalance(signingKey, signingKeyPath);
|
||||
if (psbtObject.IsAllFinalized())
|
||||
{
|
||||
vm.CanCalculateBalance = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var balanceChange = psbtObject.GetBalance(derivationSchemeSettings.AccountDerivation, signingKey, signingKeyPath);
|
||||
vm.BalanceChange = ValueToString(balanceChange, network);
|
||||
vm.CanCalculateBalance = true;
|
||||
vm.Positive = balanceChange >= Money.Zero;
|
||||
}
|
||||
|
||||
vm.BalanceChange = ValueToString(balanceChange, network);
|
||||
vm.Positive = balanceChange >= Money.Zero;
|
||||
foreach (var input in psbtObject.Inputs)
|
||||
{
|
||||
var inputVm = new WalletPSBTReadyViewModel.InputViewModel();
|
||||
vm.Inputs.Add(inputVm);
|
||||
var mine = input.HDKeysFor(derivationSchemeSettings.AccountDerivation, signingKey, signingKeyPath).Any();
|
||||
var balanceChange2 = input.GetTxOut()?.Value ?? Money.Zero;
|
||||
if (mine)
|
||||
balanceChange2 = -balanceChange2;
|
||||
inputVm.BalanceChange = ValueToString(balanceChange2, network);
|
||||
inputVm.Positive = balanceChange2 >= Money.Zero;
|
||||
inputVm.Index = (int)input.Index;
|
||||
}
|
||||
|
||||
foreach (var output in psbtObject.Outputs)
|
||||
{
|
||||
var dest = new WalletPSBTReadyViewModel.DestinationViewModel();
|
||||
vm.Destinations.Add(dest);
|
||||
var mine = output.HDKeysFor(signingKey, signingKeyPath).Any();
|
||||
var mine = output.HDKeysFor(derivationSchemeSettings.AccountDerivation, signingKey, signingKeyPath).Any();
|
||||
var balanceChange2 = output.Value;
|
||||
if (!mine)
|
||||
balanceChange2 = -balanceChange2;
|
||||
@ -167,7 +223,26 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (psbtObject.TryGetFee(out var fee))
|
||||
{
|
||||
vm.Fee = ValueToString(fee, network);
|
||||
vm.Destinations.Add(new WalletPSBTReadyViewModel.DestinationViewModel()
|
||||
{
|
||||
Positive = false,
|
||||
Balance = ValueToString(- fee, network),
|
||||
Destination = "Mining fees"
|
||||
});
|
||||
}
|
||||
if (psbtObject.TryGetEstimatedFeeRate(out var feeRate))
|
||||
{
|
||||
vm.FeeRate = feeRate.ToString();
|
||||
}
|
||||
|
||||
var sanityErrors = psbtObject.CheckSanity();
|
||||
if (sanityErrors.Count != 0)
|
||||
{
|
||||
vm.SetErrors(sanityErrors);
|
||||
}
|
||||
else if (!psbtObject.IsAllFinalized() && !psbtObject.TryFinalize(out var errors))
|
||||
{
|
||||
vm.SetErrors(errors);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,24 +253,25 @@ namespace BTCPayServer.Controllers
|
||||
WalletId walletId, WalletPSBTReadyViewModel vm, string command = null)
|
||||
{
|
||||
PSBT psbt = null;
|
||||
var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
try
|
||||
{
|
||||
psbt = PSBT.Parse(vm.PSBT, network.NBitcoinNetwork);
|
||||
await FetchTransactionDetails(walletId, vm, network);
|
||||
var derivationSchemeSettings = await GetDerivationSchemeSettings(walletId);
|
||||
if (derivationSchemeSettings == null)
|
||||
return NotFound();
|
||||
await FetchTransactionDetails(derivationSchemeSettings, vm, network);
|
||||
}
|
||||
catch
|
||||
{
|
||||
vm.Errors = new List<string>();
|
||||
vm.Errors.Add("Invalid PSBT");
|
||||
vm.GlobalError = "Invalid PSBT";
|
||||
return View(vm);
|
||||
}
|
||||
if (command == "broadcast")
|
||||
{
|
||||
if (!psbt.IsAllFinalized() && !psbt.TryFinalize(out var errors))
|
||||
{
|
||||
vm.Errors = new List<string>();
|
||||
vm.Errors.AddRange(errors.Select(e => e.ToString()));
|
||||
vm.SetErrors(errors);
|
||||
return View(vm);
|
||||
}
|
||||
var transaction = psbt.ExtractTransaction();
|
||||
@ -204,15 +280,13 @@ namespace BTCPayServer.Controllers
|
||||
var broadcastResult = await ExplorerClientProvider.GetExplorerClient(network).BroadcastAsync(transaction);
|
||||
if (!broadcastResult.Success)
|
||||
{
|
||||
vm.Errors = new List<string>();
|
||||
vm.Errors.Add($"RPC Error while broadcasting: {broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}");
|
||||
vm.GlobalError = $"RPC Error while broadcasting: {broadcastResult.RPCCode} {broadcastResult.RPCCodeMessage} {broadcastResult.RPCMessage}";
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
vm.Errors = new List<string>();
|
||||
vm.Errors.Add("Error while broadcasting: " + ex.Message);
|
||||
vm.GlobalError = "Error while broadcasting: " + ex.Message;
|
||||
return View(vm);
|
||||
}
|
||||
return await RedirectToWalletTransaction(walletId, transaction);
|
||||
@ -223,8 +297,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
vm.Errors = new List<string>();
|
||||
vm.Errors.Add("Unknown command");
|
||||
vm.GlobalError = "Unknown command";
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
@ -239,6 +312,8 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
private IActionResult ViewPSBT(PSBT psbt, string fileName, IEnumerable<string> errors = null)
|
||||
{
|
||||
ModelState.Remove(nameof(WalletPSBTViewModel.PSBT));
|
||||
ModelState.Remove(nameof(WalletPSBTViewModel.FileName));
|
||||
return View(nameof(WalletPSBT), new WalletPSBTViewModel()
|
||||
{
|
||||
Decoded = psbt.ToString(),
|
||||
@ -258,7 +333,7 @@ namespace BTCPayServer.Controllers
|
||||
public async Task<IActionResult> WalletPSBTCombine([ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, WalletPSBTCombineViewModel vm)
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
var psbt = await vm.GetPSBT(network.NBitcoinNetwork);
|
||||
if (psbt == null)
|
||||
{
|
||||
|
@ -156,20 +156,27 @@ namespace BTCPayServer.Controllers
|
||||
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId, store);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode);
|
||||
var network = this.NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode);
|
||||
if (network == null)
|
||||
return NotFound();
|
||||
var storeData = store.GetStoreBlob();
|
||||
var rateRules = store.GetStoreBlob().GetRateRules(NetworkProvider);
|
||||
rateRules.Spread = 0.0m;
|
||||
var currencyPair = new Rating.CurrencyPair(paymentMethod.PaymentId.CryptoCode, GetCurrencyCode(storeData.DefaultLang) ?? "USD");
|
||||
WalletSendModel model = new WalletSendModel()
|
||||
double.TryParse(defaultAmount, out var amount);
|
||||
var model = new WalletSendModel()
|
||||
{
|
||||
Destination = defaultDestination,
|
||||
Outputs = new List<WalletSendModel.TransactionOutput>()
|
||||
{
|
||||
new WalletSendModel.TransactionOutput()
|
||||
{
|
||||
Amount = Convert.ToDecimal(amount),
|
||||
DestinationAddress = defaultDestination
|
||||
}
|
||||
},
|
||||
CryptoCode = walletId.CryptoCode
|
||||
};
|
||||
if (double.TryParse(defaultAmount, out var amount))
|
||||
model.Amount = (decimal)amount;
|
||||
|
||||
|
||||
var feeProvider = _feeRateProvider.CreateFeeProvider(network);
|
||||
var recommendedFees = feeProvider.GetFeeRateAsync();
|
||||
@ -204,28 +211,88 @@ namespace BTCPayServer.Controllers
|
||||
[Route("{walletId}/send")]
|
||||
public async Task<IActionResult> WalletSend(
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, WalletSendModel vm, string command = null, CancellationToken cancellation = default)
|
||||
WalletId walletId, WalletSendModel vm, string command = "", CancellationToken cancellation = default)
|
||||
{
|
||||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
var store = await Repository.FindStore(walletId.StoreId, GetUserId());
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode);
|
||||
var network = this.NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode);
|
||||
if (network == null)
|
||||
return NotFound();
|
||||
vm.SupportRBF = network.SupportRBF;
|
||||
var destination = ParseDestination(vm.Destination, network.NBitcoinNetwork);
|
||||
if (destination == null)
|
||||
ModelState.AddModelError(nameof(vm.Destination), "Invalid address");
|
||||
|
||||
if (vm.Amount.HasValue)
|
||||
decimal transactionAmountSum = 0;
|
||||
|
||||
if (command == "add-output")
|
||||
{
|
||||
if (vm.CurrentBalance == vm.Amount.Value && !vm.SubstractFees)
|
||||
ModelState.AddModelError(nameof(vm.Amount), "You are sending all your balance to the same destination, you should substract the fees");
|
||||
if (vm.CurrentBalance < vm.Amount.Value)
|
||||
ModelState.AddModelError(nameof(vm.Amount), "You are sending more than what you own");
|
||||
ModelState.Clear();
|
||||
vm.Outputs.Add(new WalletSendModel.TransactionOutput());
|
||||
return View(vm);
|
||||
}
|
||||
if (command.StartsWith("remove-output", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
ModelState.Clear();
|
||||
var index = int.Parse(command.Substring(command.IndexOf(":",StringComparison.InvariantCultureIgnoreCase) + 1), CultureInfo.InvariantCulture);
|
||||
vm.Outputs.RemoveAt(index);
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
|
||||
if (!vm.Outputs.Any())
|
||||
{
|
||||
ModelState.AddModelError(string.Empty,
|
||||
"Please add at least one transaction output");
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
var subtractFeesOutputsCount = new List<int>();
|
||||
var substractFees = vm.Outputs.Any(o => o.SubtractFeesFromOutput);
|
||||
for (var i = 0; i < vm.Outputs.Count; i++)
|
||||
{
|
||||
var transactionOutput = vm.Outputs[i];
|
||||
if (transactionOutput.SubtractFeesFromOutput)
|
||||
{
|
||||
subtractFeesOutputsCount.Add(i);
|
||||
}
|
||||
var destination = ParseDestination(transactionOutput.DestinationAddress, network.NBitcoinNetwork);
|
||||
if (destination == null)
|
||||
ModelState.AddModelError(nameof(transactionOutput.DestinationAddress), "Invalid address");
|
||||
|
||||
if (transactionOutput.Amount.HasValue)
|
||||
{
|
||||
transactionAmountSum += transactionOutput.Amount.Value;
|
||||
|
||||
if (vm.CurrentBalance == transactionOutput.Amount.Value &&
|
||||
!transactionOutput.SubtractFeesFromOutput)
|
||||
vm.AddModelError(model => model.Outputs[i].SubtractFeesFromOutput,
|
||||
"You are sending your entire balance to the same destination, you should subtract the fees",
|
||||
ModelState);
|
||||
}
|
||||
}
|
||||
|
||||
if (subtractFeesOutputsCount.Count > 1)
|
||||
{
|
||||
foreach (var subtractFeesOutput in subtractFeesOutputsCount)
|
||||
{
|
||||
vm.AddModelError(model => model.Outputs[subtractFeesOutput].SubtractFeesFromOutput,
|
||||
"You can only subtract fees from one output", ModelState);
|
||||
}
|
||||
}else if (vm.CurrentBalance == transactionAmountSum && !substractFees)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty,
|
||||
"You are sending your entire balance, you should subtract the fees from an output");
|
||||
}
|
||||
|
||||
if (vm.CurrentBalance < transactionAmountSum)
|
||||
{
|
||||
for (var i = 0; i < vm.Outputs.Count; i++)
|
||||
{
|
||||
vm.AddModelError(model => model.Outputs[i].Amount,
|
||||
"You are sending more than what you own", ModelState);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
|
||||
@ -238,15 +305,16 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
catch (NBXplorerException ex)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.Amount), ex.Error.Message);
|
||||
ModelState.AddModelError(string.Empty, ex.Error.Message);
|
||||
return View(vm);
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.Destination), "You need to update your version of NBXplorer");
|
||||
ModelState.AddModelError(string.Empty, "You need to update your version of NBXplorer");
|
||||
return View(vm);
|
||||
}
|
||||
derivationScheme.RebaseKeyPaths(psbt.PSBT);
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case "ledger":
|
||||
@ -254,10 +322,13 @@ namespace BTCPayServer.Controllers
|
||||
case "seed":
|
||||
return SignWithSeed(walletId, psbt.PSBT.ToBase64());
|
||||
case "analyze-psbt":
|
||||
return RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.PSBT.ToBase64(), FileName= $"Send-{vm.Amount.Value}-{network.CryptoCode}-to-{destination[0].ToString()}.psbt" });
|
||||
var name =
|
||||
$"Send-{string.Join('_', vm.Outputs.Select(output => $"{output.Amount}->{output.DestinationAddress}{(output.SubtractFeesFromOutput ? "-Fees" : string.Empty)}"))}.psbt";
|
||||
return RedirectToAction(nameof(WalletPSBT), new { walletId = walletId, psbt = psbt.PSBT.ToBase64(), FileName = name });
|
||||
default:
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ViewResult ViewWalletSendLedger(PSBT psbt, BitcoinAddress hintChange = null)
|
||||
@ -289,7 +360,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
return View(viewModel);
|
||||
}
|
||||
var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
if (network == null)
|
||||
throw new FormatException("Invalid value for crypto code");
|
||||
|
||||
@ -331,18 +402,18 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
signingKey = extKey;
|
||||
}
|
||||
var balanceChange = psbt.GetBalance(signingKey, rootedKeyPath);
|
||||
var balanceChange = psbt.GetBalance(settings.AccountDerivation, signingKey, rootedKeyPath);
|
||||
if (balanceChange == Money.Zero)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.SeedOrKey), "This seed does not seem to be able to sign this transaction. Either this is the wrong key, or Wallet Settings have not the correct account path in the wallet settings.");
|
||||
return View(viewModel);
|
||||
}
|
||||
psbt.SignAll(signingKey, rootedKeyPath);
|
||||
psbt.SignAll(settings.AccountDerivation, signingKey, rootedKeyPath);
|
||||
ModelState.Remove(nameof(viewModel.PSBT));
|
||||
return await WalletPSBTReady(walletId, psbt.ToBase64(), signingKey.GetWif(network.NBitcoinNetwork).ToString(), rootedKeyPath.ToString());
|
||||
}
|
||||
|
||||
private string ValueToString(Money v, BTCPayNetwork network)
|
||||
private string ValueToString(Money v, BTCPayNetworkBase network)
|
||||
{
|
||||
return v.ToString() + " " + network.CryptoCode;
|
||||
}
|
||||
@ -362,7 +433,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private async Task<IActionResult> RedirectToWalletTransaction(WalletId walletId, Transaction transaction)
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
if (transaction != null)
|
||||
{
|
||||
var wallet = _walletProvider.GetWallet(network);
|
||||
@ -417,7 +488,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
[HttpPost]
|
||||
[Route("{walletId}/rescan")]
|
||||
[Authorize(Policy = Policies.CanModifyServerSettings.Key)]
|
||||
[Authorize(Policy = Policies.CanModifyServerSettings.Key, AuthenticationSchemes = Policies.CookieAuthentication)]
|
||||
public async Task<IActionResult> WalletRescan(
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, RescanWalletModel vm)
|
||||
@ -507,7 +578,7 @@ namespace BTCPayServer.Controllers
|
||||
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||
return NotFound();
|
||||
|
||||
var network = NetworkProvider.GetNetwork(walletId.CryptoCode);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
if (network == null)
|
||||
throw new FormatException("Invalid value for crypto code");
|
||||
var storeData = (await Repository.FindStore(walletId.StoreId, GetUserId()));
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using BTCPayServer.Authentication.OpenId.Models;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using BTCPayServer.Models;
|
||||
@ -6,6 +7,7 @@ using BTCPayServer.Services.PaymentRequests;
|
||||
using BTCPayServer.Services.U2F.Models;
|
||||
using BTCPayServer.Storage.Models;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
@ -225,6 +227,9 @@ namespace BTCPayServer.Data
|
||||
|
||||
builder.Entity<PaymentRequestData>()
|
||||
.HasIndex(o => o.Status);
|
||||
|
||||
builder.UseOpenIddict<BTCPayOpenIdClient, BTCPayOpenIdAuthorization, OpenIddictScope<string>, BTCPayOpenIdToken, string>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,17 +64,15 @@ namespace BTCPayServer.Data
|
||||
}
|
||||
public IEnumerable<ISupportedPaymentMethod> GetSupportedPaymentMethods(BTCPayNetworkProvider networks)
|
||||
{
|
||||
networks = networks.UnfilteredNetworks;
|
||||
#pragma warning disable CS0618
|
||||
bool btcReturned = false;
|
||||
|
||||
// Legacy stuff which should go away
|
||||
if (!string.IsNullOrEmpty(DerivationStrategy))
|
||||
{
|
||||
if (networks.BTC != null)
|
||||
{
|
||||
btcReturned = true;
|
||||
yield return DerivationSchemeSettings.Parse(DerivationStrategy, networks.BTC);
|
||||
}
|
||||
btcReturned = true;
|
||||
yield return DerivationSchemeSettings.Parse(DerivationStrategy, networks.BTC);
|
||||
}
|
||||
|
||||
|
||||
@ -84,14 +82,15 @@ namespace BTCPayServer.Data
|
||||
foreach (var strat in strategies.Properties())
|
||||
{
|
||||
var paymentMethodId = PaymentMethodId.Parse(strat.Name);
|
||||
var network = networks.GetNetwork(paymentMethodId.CryptoCode);
|
||||
var network = networks.GetNetwork<BTCPayNetwork>(paymentMethodId.CryptoCode);
|
||||
if (network != null)
|
||||
{
|
||||
if (network == networks.BTC && paymentMethodId.PaymentType == PaymentTypes.BTCLike && btcReturned)
|
||||
continue;
|
||||
if (strat.Value.Type == JTokenType.Null)
|
||||
continue;
|
||||
yield return PaymentMethodExtensions.Deserialize(paymentMethodId, strat.Value, network);
|
||||
yield return
|
||||
paymentMethodId.PaymentType.DeserializeSupportedPaymentMethod(network, strat.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -282,7 +281,7 @@ namespace BTCPayServer.Data
|
||||
|
||||
public double Multiplier { get; set; }
|
||||
|
||||
public decimal Apply(BTCPayNetwork network, decimal rate)
|
||||
public decimal Apply(BTCPayNetworkBase network, decimal rate)
|
||||
{
|
||||
return rate * (decimal)Multiplier;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -7,7 +7,7 @@ namespace BTCPayServer.Events
|
||||
{
|
||||
public class InvoiceNewAddressEvent
|
||||
{
|
||||
public InvoiceNewAddressEvent(string invoiceId, string address, BTCPayNetwork network)
|
||||
public InvoiceNewAddressEvent(string invoiceId, string address, BTCPayNetworkBase network)
|
||||
{
|
||||
Address = address;
|
||||
InvoiceId = invoiceId;
|
||||
@ -16,7 +16,7 @@ namespace BTCPayServer.Events
|
||||
|
||||
public string Address { get; set; }
|
||||
public string InvoiceId { get; set; }
|
||||
public BTCPayNetwork Network { get; set; }
|
||||
public BTCPayNetworkBase Network { get; set; }
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Network.CryptoCode}: New address {Address} for invoice {InvoiceId}";
|
||||
|
@ -8,14 +8,14 @@ namespace BTCPayServer.Events
|
||||
{
|
||||
public class NBXplorerStateChangedEvent
|
||||
{
|
||||
public NBXplorerStateChangedEvent(BTCPayNetwork network, NBXplorerState old, NBXplorerState newState)
|
||||
public NBXplorerStateChangedEvent(BTCPayNetworkBase network, NBXplorerState old, NBXplorerState newState)
|
||||
{
|
||||
Network = network;
|
||||
NewState = newState;
|
||||
OldState = old;
|
||||
}
|
||||
|
||||
public BTCPayNetwork Network { get; set; }
|
||||
public BTCPayNetworkBase Network { get; set; }
|
||||
public NBXplorerState NewState { get; set; }
|
||||
public NBXplorerState OldState { get; set; }
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace BTCPayServer
|
||||
Logs.Configuration.LogInformation($"{setting.CryptoCode}: Cookie file is {(setting.CookieFile ?? "not set")}");
|
||||
if (setting.ExplorerUri != null)
|
||||
{
|
||||
_Clients.TryAdd(setting.CryptoCode, CreateExplorerClient(httpClientFactory.CreateClient($"NBXPLORER_{setting.CryptoCode}"), _NetworkProviders.GetNetwork(setting.CryptoCode), setting.ExplorerUri, setting.CookieFile));
|
||||
_Clients.TryAdd(setting.CryptoCode, CreateExplorerClient(httpClientFactory.CreateClient($"NBXPLORER_{setting.CryptoCode}"), _NetworkProviders.GetNetwork<BTCPayNetwork>(setting.CryptoCode), setting.ExplorerUri, setting.CookieFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,21 +58,21 @@ namespace BTCPayServer
|
||||
|
||||
public ExplorerClient GetExplorerClient(string cryptoCode)
|
||||
{
|
||||
var network = _NetworkProviders.GetNetwork(cryptoCode);
|
||||
var network = _NetworkProviders.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
if (network == null)
|
||||
return null;
|
||||
_Clients.TryGetValue(network.CryptoCode, out ExplorerClient client);
|
||||
return client;
|
||||
}
|
||||
|
||||
public ExplorerClient GetExplorerClient(BTCPayNetwork network)
|
||||
public ExplorerClient GetExplorerClient(BTCPayNetworkBase network)
|
||||
{
|
||||
if (network == null)
|
||||
throw new ArgumentNullException(nameof(network));
|
||||
return GetExplorerClient(network.CryptoCode);
|
||||
}
|
||||
|
||||
public bool IsAvailable(BTCPayNetwork network)
|
||||
public bool IsAvailable(BTCPayNetworkBase network)
|
||||
{
|
||||
return IsAvailable(network.CryptoCode);
|
||||
}
|
||||
@ -84,7 +84,7 @@ namespace BTCPayServer
|
||||
|
||||
public BTCPayNetwork GetNetwork(string cryptoCode)
|
||||
{
|
||||
var network = _NetworkProviders.GetNetwork(cryptoCode);
|
||||
var network = _NetworkProviders.GetNetwork<BTCPayNetwork>(cryptoCode);
|
||||
if (network == null)
|
||||
return null;
|
||||
if (_Clients.ContainsKey(network.CryptoCode))
|
||||
@ -94,7 +94,7 @@ namespace BTCPayServer
|
||||
|
||||
public IEnumerable<(BTCPayNetwork, ExplorerClient)> GetAll()
|
||||
{
|
||||
foreach (var net in _NetworkProviders.GetAll())
|
||||
foreach (var net in _NetworkProviders.GetAll().OfType<BTCPayNetwork>())
|
||||
{
|
||||
if (_Clients.TryGetValue(net.CryptoCode, out ExplorerClient explorer))
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ namespace BTCPayServer
|
||||
}
|
||||
public static PaymentMethodId GetpaymentMethodId(this InvoiceCryptoInfo info)
|
||||
{
|
||||
return new PaymentMethodId(info.CryptoCode, Enum.Parse<PaymentTypes>(info.PaymentType));
|
||||
return new PaymentMethodId(info.CryptoCode, PaymentTypes.Parse(info.PaymentType));
|
||||
}
|
||||
public static async Task CloseSocket(this WebSocket webSocket)
|
||||
{
|
||||
@ -206,6 +206,11 @@ namespace BTCPayServer
|
||||
request.PathBase.ToUriComponent());
|
||||
}
|
||||
|
||||
public static Uri GetAbsoluteRootUri(this HttpRequest request)
|
||||
{
|
||||
return new Uri(request.GetAbsoluteRoot());
|
||||
}
|
||||
|
||||
public static string GetCurrentUrl(this HttpRequest request)
|
||||
{
|
||||
return string.Concat(
|
||||
@ -317,13 +322,6 @@ namespace BTCPayServer
|
||||
NBitcoin.Extensions.TryAdd(ctx.Items, "IsBitpayAPI", value);
|
||||
}
|
||||
|
||||
public static void AddRange<T>(this HashSet<T> hashSet, IEnumerable<T> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
hashSet.Add(item);
|
||||
}
|
||||
}
|
||||
public static bool GetIsBitpayAPI(this HttpContext ctx)
|
||||
{
|
||||
return ctx.Items.TryGetValue("IsBitpayAPI", out object obj) &&
|
||||
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Mails;
|
||||
|
||||
namespace BTCPayServer.Services
|
||||
|
29
BTCPayServer/Extensions/ModelStateExtensions.cs
Normal file
29
BTCPayServer/Extensions/ModelStateExtensions.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public static class ModelStateExtensions
|
||||
{
|
||||
public static void AddModelError<TModel, TProperty>(
|
||||
this ModelStateDictionary modelState,
|
||||
Expression<Func<TModel, TProperty>> ex,
|
||||
string message
|
||||
)
|
||||
{
|
||||
var key = ExpressionHelper.GetExpressionText(ex);
|
||||
modelState.AddModelError(key, message);
|
||||
}
|
||||
|
||||
public static void AddModelError<TModel, TProperty>(this TModel source,
|
||||
Expression<Func<TModel, TProperty>> ex,
|
||||
string message,
|
||||
ModelStateDictionary modelState)
|
||||
{
|
||||
var key = ExpressionHelper.GetExpressionText(ex);
|
||||
modelState.AddModelError(key, message);
|
||||
}
|
||||
}
|
||||
}
|
37
BTCPayServer/Extensions/OpenIddictExtensions.cs
Normal file
37
BTCPayServer/Extensions/OpenIddictExtensions.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using BTCPayServer.Configuration;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using NETCore.Encrypt.Extensions.Internal;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public static class OpenIddictExtensions
|
||||
{
|
||||
public static OpenIddictServerBuilder ConfigureSigningKey(this OpenIddictServerBuilder builder,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
|
||||
var file = Path.Combine(configuration.GetDataDir(), "rsaparams");
|
||||
|
||||
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(2048);
|
||||
RsaSecurityKey key = null;
|
||||
|
||||
if (File.Exists(file))
|
||||
{
|
||||
RSA.FromXmlString2( File.ReadAllText(file));
|
||||
}
|
||||
else
|
||||
{
|
||||
var contents = RSA.ToXmlString2(true);
|
||||
File.WriteAllText(file,contents );
|
||||
}
|
||||
|
||||
RSAParameters KeyParam = RSA.ExportParameters(true);
|
||||
key = new RsaSecurityKey(KeyParam);
|
||||
return builder.AddSigningKey(key);
|
||||
}
|
||||
}
|
||||
}
|
97
BTCPayServer/Extensions/RsaKeyExtensions.cs
Normal file
97
BTCPayServer/Extensions/RsaKeyExtensions.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml;
|
||||
|
||||
namespace NETCore.Encrypt.Extensions.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// .net core's implementatiosn are still marked as unsupported because of stupid decisions( https://github.com/dotnet/corefx/issues/23686)
|
||||
/// </summary>
|
||||
internal static class RsaKeyExtensions
|
||||
{
|
||||
#region XML
|
||||
|
||||
public static void FromXmlString2(this RSA rsa, string xmlString)
|
||||
{
|
||||
RSAParameters parameters = new RSAParameters();
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.LoadXml(xmlString);
|
||||
|
||||
if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue", StringComparison.InvariantCulture))
|
||||
{
|
||||
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
|
||||
{
|
||||
switch (node.Name)
|
||||
{
|
||||
case "Modulus":
|
||||
parameters.Modulus = (string.IsNullOrEmpty(node.InnerText)
|
||||
? null
|
||||
: Convert.FromBase64String(node.InnerText));
|
||||
break;
|
||||
case "Exponent":
|
||||
parameters.Exponent = (string.IsNullOrEmpty(node.InnerText)
|
||||
? null
|
||||
: Convert.FromBase64String(node.InnerText));
|
||||
break;
|
||||
case "P":
|
||||
parameters.P = (string.IsNullOrEmpty(node.InnerText)
|
||||
? null
|
||||
: Convert.FromBase64String(node.InnerText));
|
||||
break;
|
||||
case "Q":
|
||||
parameters.Q = (string.IsNullOrEmpty(node.InnerText)
|
||||
? null
|
||||
: Convert.FromBase64String(node.InnerText));
|
||||
break;
|
||||
case "DP":
|
||||
parameters.DP = (string.IsNullOrEmpty(node.InnerText)
|
||||
? null
|
||||
: Convert.FromBase64String(node.InnerText));
|
||||
break;
|
||||
case "DQ":
|
||||
parameters.DQ = (string.IsNullOrEmpty(node.InnerText)
|
||||
? null
|
||||
: Convert.FromBase64String(node.InnerText));
|
||||
break;
|
||||
case "InverseQ":
|
||||
parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText)
|
||||
? null
|
||||
: Convert.FromBase64String(node.InnerText));
|
||||
break;
|
||||
case "D":
|
||||
parameters.D = (string.IsNullOrEmpty(node.InnerText)
|
||||
? null
|
||||
: Convert.FromBase64String(node.InnerText));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Invalid XML RSA key.");
|
||||
}
|
||||
|
||||
rsa.ImportParameters(parameters);
|
||||
}
|
||||
|
||||
public static string ToXmlString2(this RSA rsa, bool includePrivateParameters)
|
||||
{
|
||||
RSAParameters parameters = rsa.ExportParameters(includePrivateParameters);
|
||||
|
||||
return string.Format(CultureInfo.InvariantCulture,
|
||||
"<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent><P>{2}</P><Q>{3}</Q><DP>{4}</DP><DQ>{5}</DQ><InverseQ>{6}</InverseQ><D>{7}</D></RSAKeyValue>",
|
||||
parameters.Modulus != null ? Convert.ToBase64String(parameters.Modulus) : null,
|
||||
parameters.Exponent != null ? Convert.ToBase64String(parameters.Exponent) : null,
|
||||
parameters.P != null ? Convert.ToBase64String(parameters.P) : null,
|
||||
parameters.Q != null ? Convert.ToBase64String(parameters.Q) : null,
|
||||
parameters.DP != null ? Convert.ToBase64String(parameters.DP) : null,
|
||||
parameters.DQ != null ? Convert.ToBase64String(parameters.DQ) : null,
|
||||
parameters.InverseQ != null ? Convert.ToBase64String(parameters.InverseQ) : null,
|
||||
parameters.D != null ? Convert.ToBase64String(parameters.D) : null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -42,7 +42,6 @@ namespace BTCPayServer.HostedServices
|
||||
IBackgroundJobClient _JobClient;
|
||||
EventAggregator _EventAggregator;
|
||||
InvoiceRepository _InvoiceRepository;
|
||||
BTCPayNetworkProvider _NetworkProvider;
|
||||
private readonly EmailSenderFactory _EmailSenderFactory;
|
||||
|
||||
public InvoiceNotificationManager(
|
||||
@ -51,20 +50,18 @@ namespace BTCPayServer.HostedServices
|
||||
EventAggregator eventAggregator,
|
||||
InvoiceRepository invoiceRepository,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
ILogger<InvoiceNotificationManager> logger,
|
||||
EmailSenderFactory emailSenderFactory)
|
||||
{
|
||||
_Client = httpClientFactory.CreateClient();
|
||||
_JobClient = jobClient;
|
||||
_EventAggregator = eventAggregator;
|
||||
_InvoiceRepository = invoiceRepository;
|
||||
_NetworkProvider = networkProvider;
|
||||
_EmailSenderFactory = emailSenderFactory;
|
||||
}
|
||||
|
||||
void Notify(InvoiceEntity invoice, InvoiceEvent invoiceEvent, bool extendedNotification)
|
||||
{
|
||||
var dto = invoice.EntityToDTO(_NetworkProvider);
|
||||
var dto = invoice.EntityToDTO();
|
||||
var notification = new InvoicePaymentNotificationEventWrapper()
|
||||
{
|
||||
Data = new InvoicePaymentNotification()
|
||||
|
@ -1,7 +1,5 @@
|
||||
using NBXplorer;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using NBXplorer.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -11,12 +9,8 @@ using BTCPayServer.Logging;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.Collections.Concurrent;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Events;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services;
|
||||
|
||||
namespace BTCPayServer.HostedServices
|
||||
{
|
||||
@ -42,18 +36,15 @@ namespace BTCPayServer.HostedServices
|
||||
|
||||
InvoiceRepository _InvoiceRepository;
|
||||
EventAggregator _EventAggregator;
|
||||
BTCPayNetworkProvider _NetworkProvider;
|
||||
ExplorerClientProvider _ExplorerClientProvider;
|
||||
|
||||
public InvoiceWatcher(
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
InvoiceRepository invoiceRepository,
|
||||
EventAggregator eventAggregator,
|
||||
ExplorerClientProvider explorerClientProvider)
|
||||
{
|
||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||
_EventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||
_NetworkProvider = networkProvider;
|
||||
_ExplorerClientProvider = explorerClientProvider;
|
||||
}
|
||||
CompositeDisposable leases = new CompositeDisposable();
|
||||
@ -74,11 +65,10 @@ namespace BTCPayServer.HostedServices
|
||||
}
|
||||
|
||||
var payments = invoice.GetPayments().Where(p => p.Accounted).ToArray();
|
||||
var allPaymentMethods = invoice.GetPaymentMethods(_NetworkProvider);
|
||||
var paymentMethod = GetNearestClearedPayment(allPaymentMethods, out var accounting, _NetworkProvider);
|
||||
var allPaymentMethods = invoice.GetPaymentMethods();
|
||||
var paymentMethod = GetNearestClearedPayment(allPaymentMethods, out var accounting);
|
||||
if (paymentMethod == null)
|
||||
return;
|
||||
var network = _NetworkProvider.GetNetwork(paymentMethod.GetId().CryptoCode);
|
||||
if (invoice.Status == InvoiceStatus.New || invoice.Status == InvoiceStatus.Expired)
|
||||
{
|
||||
if (accounting.Paid >= accounting.MinimumTotalDue)
|
||||
@ -131,7 +121,7 @@ namespace BTCPayServer.HostedServices
|
||||
|
||||
if (invoice.Status == InvoiceStatus.Paid)
|
||||
{
|
||||
var confirmedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy, network));
|
||||
var confirmedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentConfirmed(p, invoice.SpeedPolicy));
|
||||
|
||||
if (// Is after the monitoring deadline
|
||||
(invoice.MonitoringExpiration < DateTimeOffset.UtcNow)
|
||||
@ -155,7 +145,7 @@ namespace BTCPayServer.HostedServices
|
||||
|
||||
if (invoice.Status == InvoiceStatus.Confirmed)
|
||||
{
|
||||
var completedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentCompleted(p, network));
|
||||
var completedAccounting = paymentMethod.Calculate(p => p.GetCryptoPaymentData().PaymentCompleted(p));
|
||||
if (completedAccounting.Paid >= accounting.MinimumTotalDue)
|
||||
{
|
||||
context.Events.Add(new InvoiceEvent(invoice, 1006, InvoiceEvent.Completed));
|
||||
@ -166,15 +156,13 @@ namespace BTCPayServer.HostedServices
|
||||
|
||||
}
|
||||
|
||||
public static PaymentMethod GetNearestClearedPayment(PaymentMethodDictionary allPaymentMethods, out PaymentMethodAccounting accounting, BTCPayNetworkProvider networkProvider)
|
||||
public static PaymentMethod GetNearestClearedPayment(PaymentMethodDictionary allPaymentMethods, out PaymentMethodAccounting accounting)
|
||||
{
|
||||
PaymentMethod result = null;
|
||||
accounting = null;
|
||||
decimal nearestToZero = 0.0m;
|
||||
foreach (var paymentMethod in allPaymentMethods)
|
||||
{
|
||||
if (networkProvider != null && networkProvider.GetNetwork(paymentMethod.GetId().CryptoCode) == null)
|
||||
continue;
|
||||
var currentAccounting = paymentMethod.Calculate();
|
||||
var distanceFromZero = Math.Abs(currentAccounting.DueUncapped.ToDecimal(MoneyUnit.BTC));
|
||||
if (result == null || distanceFromZero < nearestToZero)
|
||||
@ -288,31 +276,18 @@ namespace BTCPayServer.HostedServices
|
||||
if (invoice.Status == InvoiceStatus.Complete ||
|
||||
((invoice.Status == InvoiceStatus.Invalid || invoice.Status == InvoiceStatus.Expired) && invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
|
||||
{
|
||||
var updateConfirmationCountIfNeeded = invoice
|
||||
.GetPayments()
|
||||
.Select<PaymentEntity, Task>(async payment =>
|
||||
{
|
||||
var paymentNetwork = _NetworkProvider.GetNetwork(payment.GetCryptoCode());
|
||||
var paymentData = payment.GetCryptoPaymentData();
|
||||
if (paymentData is Payments.Bitcoin.BitcoinLikePaymentData onChainPaymentData)
|
||||
{
|
||||
// Do update if confirmation count in the paymentData is not up to date
|
||||
if ((onChainPaymentData.ConfirmationCount < paymentNetwork.MaxTrackedConfirmation && payment.Accounted)
|
||||
&& (onChainPaymentData.Legacy || invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
|
||||
{
|
||||
var transactionResult = await _ExplorerClientProvider.GetExplorerClient(payment.GetCryptoCode())?.GetTransactionAsync(onChainPaymentData.Outpoint.Hash);
|
||||
var confirmationCount = transactionResult?.Confirmations ?? 0;
|
||||
onChainPaymentData.ConfirmationCount = confirmationCount;
|
||||
payment.SetCryptoPaymentData(onChainPaymentData);
|
||||
await _InvoiceRepository.UpdatePayments(new List<PaymentEntity> { payment });
|
||||
}
|
||||
}
|
||||
})
|
||||
.ToArray();
|
||||
await Task.WhenAll(updateConfirmationCountIfNeeded);
|
||||
var extendInvoiceMonitoring = await UpdateConfirmationCount(invoice);
|
||||
|
||||
if (await _InvoiceRepository.RemovePendingInvoice(invoice.Id))
|
||||
// we extend monitor time if we haven't reached max confirmation count
|
||||
// say user used low fee and we only got 3 confirmations right before it's time to remove
|
||||
if (extendInvoiceMonitoring)
|
||||
{
|
||||
await _InvoiceRepository.ExtendInvoiceMonitor(invoice.Id);
|
||||
}
|
||||
else if (await _InvoiceRepository.RemovePendingInvoice(invoice.Id))
|
||||
{
|
||||
_EventAggregator.Publish(new InvoiceStopWatchedEvent(invoice.Id));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -330,6 +305,45 @@ namespace BTCPayServer.HostedServices
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateConfirmationCount(InvoiceEntity invoice)
|
||||
{
|
||||
bool extendInvoiceMonitoring = false;
|
||||
var updateConfirmationCountIfNeeded = invoice
|
||||
.GetPayments()
|
||||
.Select<PaymentEntity, Task<PaymentEntity>>(async payment =>
|
||||
{
|
||||
var paymentData = payment.GetCryptoPaymentData();
|
||||
if (paymentData is Payments.Bitcoin.BitcoinLikePaymentData onChainPaymentData)
|
||||
{
|
||||
// Do update if confirmation count in the paymentData is not up to date
|
||||
if ((onChainPaymentData.ConfirmationCount < payment.Network.MaxTrackedConfirmation && payment.Accounted)
|
||||
&& (onChainPaymentData.Legacy || invoice.MonitoringExpiration < DateTimeOffset.UtcNow))
|
||||
{
|
||||
var transactionResult = await _ExplorerClientProvider.GetExplorerClient(payment.GetCryptoCode())?.GetTransactionAsync(onChainPaymentData.Outpoint.Hash);
|
||||
var confirmationCount = transactionResult?.Confirmations ?? 0;
|
||||
onChainPaymentData.ConfirmationCount = confirmationCount;
|
||||
payment.SetCryptoPaymentData(onChainPaymentData);
|
||||
|
||||
// we want to extend invoice monitoring until we reach max confirmations on all onchain payment methods
|
||||
if (confirmationCount < payment.Network.MaxTrackedConfirmation)
|
||||
extendInvoiceMonitoring = true;
|
||||
|
||||
return payment;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.ToArray();
|
||||
await Task.WhenAll(updateConfirmationCountIfNeeded);
|
||||
var updatedPaymentData = updateConfirmationCountIfNeeded.Where(a => a.Result != null).Select(a => a.Result).ToList();
|
||||
if (updatedPaymentData.Count > 0)
|
||||
{
|
||||
await _InvoiceRepository.UpdatePayments(updatedPaymentData);
|
||||
}
|
||||
|
||||
return extendInvoiceMonitoring;
|
||||
}
|
||||
|
||||
public async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_Cts == null)
|
||||
|
@ -24,13 +24,13 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
public class NBXplorerSummary
|
||||
{
|
||||
public BTCPayNetwork Network { get; set; }
|
||||
public BTCPayNetworkBase Network { get; set; }
|
||||
public NBXplorerState State { get; set; }
|
||||
public StatusResult Status { get; set; }
|
||||
public string Error { get; set; }
|
||||
}
|
||||
ConcurrentDictionary<string, NBXplorerSummary> _Summaries = new ConcurrentDictionary<string, NBXplorerSummary>();
|
||||
public void Publish(BTCPayNetwork network, NBXplorerState state, StatusResult status, string error)
|
||||
public void Publish(BTCPayNetworkBase network, NBXplorerState state, StatusResult status, string error)
|
||||
{
|
||||
var summary = new NBXplorerSummary() { Network = network, State = state, Status = status, Error = error };
|
||||
_Summaries.AddOrUpdate(network.CryptoCode, summary, (k, v) => summary);
|
||||
|
@ -1,9 +1,7 @@
|
||||
using BTCPayServer.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -12,7 +10,6 @@ using NBitcoin;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.IO;
|
||||
using NBXplorer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using BTCPayServer.Services;
|
||||
@ -21,35 +18,35 @@ using BTCPayServer.Services.Rates;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Services.Fees;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Rewrite;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Services.Mails;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using BTCPayServer.Models;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Authentication;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using BTCPayServer.Authentication.OpenId.Models;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.HostedServices;
|
||||
using System.Security.Claims;
|
||||
using BTCPayServer.PaymentRequest;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Payments.Changelly;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.PaymentRequests;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
using NicolasDorier.RateLimits;
|
||||
using Npgsql;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
using BTCPayServer.Services.U2F;
|
||||
using BundlerMinifier.TagHelpers;
|
||||
|
||||
using System.Collections.Generic;
|
||||
namespace BTCPayServer.Hosting
|
||||
{
|
||||
public static class BTCPayServerServices
|
||||
@ -60,6 +57,7 @@ namespace BTCPayServer.Hosting
|
||||
{
|
||||
var factory = provider.GetRequiredService<ApplicationDbContextFactory>();
|
||||
factory.ConfigureBuilder(o);
|
||||
o.UseOpenIddict<BTCPayOpenIdClient, BTCPayOpenIdAuthorization, OpenIddictScope<string>, BTCPayOpenIdToken, string>();
|
||||
});
|
||||
services.AddHttpClient();
|
||||
services.TryAddSingleton<SettingsRepository>();
|
||||
@ -76,7 +74,7 @@ namespace BTCPayServer.Hosting
|
||||
var dbpath = Path.Combine(opts.DataDir, "InvoiceDB");
|
||||
if (!Directory.Exists(dbpath))
|
||||
Directory.CreateDirectory(dbpath);
|
||||
return new InvoiceRepository(dbContext, dbpath);
|
||||
return new InvoiceRepository(dbContext, dbpath, o.GetRequiredService<BTCPayNetworkProvider>());
|
||||
});
|
||||
services.AddSingleton<BTCPayServerEnvironment>();
|
||||
services.TryAddSingleton<TokenRepository>();
|
||||
@ -177,15 +175,18 @@ namespace BTCPayServer.Hosting
|
||||
services.AddSingleton<IHostedService, CssThemeManagerHostedService>();
|
||||
services.AddSingleton<IHostedService, MigratorHostedService>();
|
||||
|
||||
services.AddSingleton<Payments.IPaymentMethodHandler<DerivationSchemeSettings>, Payments.Bitcoin.BitcoinLikePaymentHandler>();
|
||||
services.AddSingleton<IHostedService, Payments.Bitcoin.NBXplorerListener>();
|
||||
|
||||
services.AddSingleton<IHostedService, HostedServices.CheckConfigurationHostedService>();
|
||||
|
||||
services.AddSingleton<Payments.IPaymentMethodHandler<Payments.Lightning.LightningSupportedPaymentMethod>, Payments.Lightning.LightningLikePaymentHandler>();
|
||||
services.AddSingleton<LightningLikePaymentHandler>();
|
||||
services.AddSingleton<IHostedService, Payments.Lightning.LightningListener>();
|
||||
|
||||
services.AddSingleton<BitcoinLikePaymentHandler>();
|
||||
services.AddSingleton<IPaymentMethodHandler>(provider => provider.GetService<BitcoinLikePaymentHandler>());
|
||||
services.AddSingleton<IHostedService, NBXplorerListener>();
|
||||
|
||||
services.AddSingleton<LightningLikePaymentHandler>();
|
||||
services.AddSingleton<IPaymentMethodHandler>(provider => provider.GetService<LightningLikePaymentHandler>());
|
||||
services.AddSingleton<IHostedService, LightningListener>();
|
||||
|
||||
services.AddSingleton<PaymentMethodHandlerDictionary>();
|
||||
|
||||
services.AddSingleton<ChangellyClientProvider>();
|
||||
|
||||
services.AddSingleton<IHostedService, NBXplorerWaiters>();
|
||||
@ -245,7 +246,16 @@ namespace BTCPayServer.Hosting
|
||||
|
||||
private static void AddBtcPayServerAuthenticationSchemes(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
||||
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
// options.RequireHttpsMetadata = false;
|
||||
// options.TokenValidationParameters.ValidateAudience = false;
|
||||
options.TokenValidationParameters.ValidateIssuer = false;
|
||||
})
|
||||
.AddCookie()
|
||||
.AddBitpayAuthentication();
|
||||
}
|
||||
|
@ -1,45 +1,28 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using System;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBitpayClient;
|
||||
using BTCPayServer.Authentication;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using BTCPayServer.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using BTCPayServer.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using BTCPayServer.Logging;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using BTCPayServer.Services.Mails;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using BTCPayServer.Configuration;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.AspNetCore.Mvc.Cors.Internal;
|
||||
using AspNet.Security.OpenIdConnect.Primitives;
|
||||
using BTCPayServer.Authentication.OpenId.Models;
|
||||
using BTCPayServer.Security;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using OpenIddict.Abstractions;
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
using System.Net;
|
||||
using BTCPayServer.PaymentRequest;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Storage;
|
||||
using BTCPayServer.Storage.Services.Providers.FileSystemStorage;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace BTCPayServer.Hosting
|
||||
{
|
||||
@ -65,11 +48,14 @@ namespace BTCPayServer.Hosting
|
||||
services.AddMemoryCache();
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
services.AddSignalR();
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
ConfigureOpenIddict(services);
|
||||
|
||||
services.AddBTCPayServer(Configuration);
|
||||
services.AddProviderStorage();
|
||||
services.AddSession();
|
||||
services.AddBTCPayServer(Configuration);
|
||||
services.AddSignalR();
|
||||
services.AddMvc(o =>
|
||||
{
|
||||
o.Filters.Add(new XFrameOptionsAttribute("DENY"));
|
||||
@ -96,6 +82,13 @@ namespace BTCPayServer.Hosting
|
||||
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
|
||||
options.Lockout.MaxFailedAccessAttempts = 5;
|
||||
options.Lockout.AllowedForNewUsers = true;
|
||||
options.Password.RequireUppercase = false;
|
||||
// Configure Identity to use the same JWT claims as OpenIddict instead
|
||||
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
|
||||
// which saves you from doing the mapping in your authorization controller.
|
||||
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
|
||||
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
|
||||
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
|
||||
});
|
||||
// If the HTTPS certificate path is not set this logic will NOT be used and the default Kestrel binding logic will be.
|
||||
string httpsCertificateFilePath = Configuration.GetOrDefault<string>("HttpsCertificateFilePath", null);
|
||||
@ -135,6 +128,50 @@ namespace BTCPayServer.Hosting
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureOpenIddict(IServiceCollection services)
|
||||
{
|
||||
// Register the OpenIddict services.
|
||||
services.AddOpenIddict()
|
||||
.AddCore(options =>
|
||||
{
|
||||
// Configure OpenIddict to use the Entity Framework Core stores and entities.
|
||||
options.UseEntityFrameworkCore()
|
||||
.UseDbContext<ApplicationDbContext>()
|
||||
.ReplaceDefaultEntities<BTCPayOpenIdClient, BTCPayOpenIdAuthorization, OpenIddictScope<string>,
|
||||
BTCPayOpenIdToken, string>();
|
||||
})
|
||||
.AddServer(options =>
|
||||
{
|
||||
// Register the ASP.NET Core MVC binder used by OpenIddict.
|
||||
// Note: if you don't call this method, you won't be able to
|
||||
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
|
||||
options.UseMvc();
|
||||
|
||||
// Enable the token endpoint (required to use the password flow).
|
||||
options.EnableTokenEndpoint("/connect/token");
|
||||
options.EnableAuthorizationEndpoint("/connect/authorize");
|
||||
options.EnableAuthorizationEndpoint("/connect/logout");
|
||||
|
||||
// Allow client applications various flows
|
||||
options.AllowImplicitFlow();
|
||||
options.AllowClientCredentialsFlow();
|
||||
options.AllowRefreshTokenFlow();
|
||||
options.AllowPasswordFlow();
|
||||
options.AllowAuthorizationCodeFlow();
|
||||
options.UseRollingTokens();
|
||||
options.UseJsonWebTokens();
|
||||
|
||||
options.RegisterScopes(
|
||||
OpenIdConnectConstants.Scopes.OpenId,
|
||||
OpenIdConnectConstants.Scopes.OfflineAccess,
|
||||
OpenIdConnectConstants.Scopes.Email,
|
||||
OpenIdConnectConstants.Scopes.Profile,
|
||||
OpenIddictConstants.Scopes.Roles);
|
||||
|
||||
options.ConfigureSigningKey(Configuration);
|
||||
});
|
||||
}
|
||||
|
||||
public void Configure(
|
||||
IApplicationBuilder app,
|
||||
IHostingEnvironment env,
|
||||
@ -162,6 +199,8 @@ namespace BTCPayServer.Hosting
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseCors();
|
||||
|
||||
var forwardingOptions = new ForwardedHeadersOptions()
|
||||
{
|
||||
|
787
BTCPayServer/Migrations/20190225091644_AddOpenIddict.Designer.cs
generated
Normal file
787
BTCPayServer/Migrations/20190225091644_AddOpenIddict.Designer.cs
generated
Normal file
@ -0,0 +1,787 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20190225091644_AddOpenIddict")]
|
||||
partial class AddOpenIddict
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdAuthorization", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ApplicationId");
|
||||
|
||||
b.Property<string>("ConcurrencyToken")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("Properties");
|
||||
|
||||
b.Property<string>("Scopes");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasMaxLength(450);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
|
||||
|
||||
b.ToTable("OpenIddictAuthorizations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdClient", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ApplicationUserId");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<string>("ClientSecret");
|
||||
|
||||
b.Property<string>("ConcurrencyToken")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("ConsentType");
|
||||
|
||||
b.Property<string>("DisplayName");
|
||||
|
||||
b.Property<string>("Permissions");
|
||||
|
||||
b.Property<string>("PostLogoutRedirectUris");
|
||||
|
||||
b.Property<string>("Properties");
|
||||
|
||||
b.Property<string>("RedirectUris");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApplicationUserId");
|
||||
|
||||
b.HasIndex("ClientId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OpenIddictApplications");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdToken", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ApplicationId");
|
||||
|
||||
b.Property<string>("AuthorizationId");
|
||||
|
||||
b.Property<string>("ConcurrencyToken")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<DateTimeOffset?>("CreationDate");
|
||||
|
||||
b.Property<DateTimeOffset?>("ExpirationDate");
|
||||
|
||||
b.Property<string>("Payload");
|
||||
|
||||
b.Property<string>("Properties");
|
||||
|
||||
b.Property<string>("ReferenceId")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasMaxLength(450);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorizationId");
|
||||
|
||||
b.HasIndex("ReferenceId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
|
||||
|
||||
b.ToTable("OpenIddictTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||
{
|
||||
b.Property<string>("Address")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedTime");
|
||||
|
||||
b.Property<string>("InvoiceDataId");
|
||||
|
||||
b.HasKey("Address");
|
||||
|
||||
b.HasIndex("InvoiceDataId");
|
||||
|
||||
b.ToTable("AddressInvoices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.APIKeyData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("StoreId")
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StoreId");
|
||||
|
||||
b.ToTable("ApiKeys");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AppData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("AppType");
|
||||
|
||||
b.Property<DateTimeOffset>("Created");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("Settings");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.Property<bool>("TagAllInvoices");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.ToTable("Apps");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b =>
|
||||
{
|
||||
b.Property<string>("InvoiceDataId");
|
||||
|
||||
b.Property<string>("Address");
|
||||
|
||||
b.Property<DateTimeOffset>("Assigned");
|
||||
|
||||
b.Property<string>("CryptoCode");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnAssigned");
|
||||
|
||||
b.HasKey("InvoiceDataId", "Address");
|
||||
|
||||
b.ToTable("HistoricalAddressInvoices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<byte[]>("Blob");
|
||||
|
||||
b.Property<DateTimeOffset>("Created");
|
||||
|
||||
b.Property<string>("CustomerEmail");
|
||||
|
||||
b.Property<string>("ExceptionStatus");
|
||||
|
||||
b.Property<string>("ItemCode");
|
||||
|
||||
b.Property<string>("OrderId");
|
||||
|
||||
b.Property<string>("Status");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.ToTable("Invoices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.InvoiceEventData", b =>
|
||||
{
|
||||
b.Property<string>("InvoiceDataId");
|
||||
|
||||
b.Property<string>("UniqueId");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp");
|
||||
|
||||
b.HasKey("InvoiceDataId", "UniqueId");
|
||||
|
||||
b.ToTable("InvoiceEvents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PairedSINData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Facade");
|
||||
|
||||
b.Property<string>("Label");
|
||||
|
||||
b.Property<DateTimeOffset>("PairingTime");
|
||||
|
||||
b.Property<string>("SIN");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SIN");
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.ToTable("PairedSINData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PairingCodeData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<DateTime>("DateCreated");
|
||||
|
||||
b.Property<DateTimeOffset>("Expiration");
|
||||
|
||||
b.Property<string>("Facade");
|
||||
|
||||
b.Property<string>("Label");
|
||||
|
||||
b.Property<string>("SIN");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.Property<string>("TokenValue");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PairingCodes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Accounted");
|
||||
|
||||
b.Property<byte[]>("Blob");
|
||||
|
||||
b.Property<string>("InvoiceDataId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InvoiceDataId");
|
||||
|
||||
b.ToTable("Payments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PendingInvoiceData", b =>
|
||||
{
|
||||
b.Property<string>("Id");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PendingInvoices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<byte[]>("Blob");
|
||||
|
||||
b.Property<string>("InvoiceDataId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("InvoiceDataId");
|
||||
|
||||
b.ToTable("RefundAddresses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.SettingData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.StoreData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("DefaultCrypto");
|
||||
|
||||
b.Property<string>("DerivationStrategies");
|
||||
|
||||
b.Property<string>("DerivationStrategy");
|
||||
|
||||
b.Property<int>("SpeedPolicy");
|
||||
|
||||
b.Property<byte[]>("StoreBlob");
|
||||
|
||||
b.Property<byte[]>("StoreCertificate");
|
||||
|
||||
b.Property<string>("StoreName");
|
||||
|
||||
b.Property<string>("StoreWebsite");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Stores");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
|
||||
{
|
||||
b.Property<string>("ApplicationUserId");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.Property<string>("Role");
|
||||
|
||||
b.HasKey("ApplicationUserId", "StoreDataId");
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.ToTable("UserStore");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Models.ApplicationUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int>("AccessFailedCount");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<bool>("EmailConfirmed");
|
||||
|
||||
b.Property<bool>("LockoutEnabled");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("PasswordHash");
|
||||
|
||||
b.Property<string>("PhoneNumber");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed");
|
||||
|
||||
b.Property<bool>("RequiresEmailConfirmation");
|
||||
|
||||
b.Property<string>("SecurityStamp");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Services.PaymentRequests.PaymentRequestData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<byte[]>("Blob");
|
||||
|
||||
b.Property<int>("Status");
|
||||
|
||||
b.Property<string>("StoreDataId");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Status");
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.ToTable("PaymentRequests");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken();
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ClaimType");
|
||||
|
||||
b.Property<string>("ClaimValue");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("ProviderKey");
|
||||
|
||||
b.Property<string>("ProviderDisplayName");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("RoleId");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId");
|
||||
|
||||
b.Property<string>("LoginProvider");
|
||||
|
||||
b.Property<string>("Name");
|
||||
|
||||
b.Property<string>("Value");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictScope<string>", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyToken")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<string>("DisplayName");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Properties");
|
||||
|
||||
b.Property<string>("Resources");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OpenIddictScopes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdAuthorization", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdClient", "Application")
|
||||
.WithMany("Authorizations")
|
||||
.HasForeignKey("ApplicationId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdClient", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Models.ApplicationUser", "ApplicationUser")
|
||||
.WithMany("OpenIdClients")
|
||||
.HasForeignKey("ApplicationUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdToken", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdClient", "Application")
|
||||
.WithMany("Tokens")
|
||||
.HasForeignKey("ApplicationId");
|
||||
|
||||
b.HasOne("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdAuthorization", "Authorization")
|
||||
.WithMany("Tokens")
|
||||
.HasForeignKey("AuthorizationId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
.WithMany("AddressInvoices")
|
||||
.HasForeignKey("InvoiceDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.APIKeyData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
.WithMany("APIKeys")
|
||||
.HasForeignKey("StoreId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AppData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
.WithMany("Apps")
|
||||
.HasForeignKey("StoreDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
.WithMany("HistoricalAddressInvoices")
|
||||
.HasForeignKey("InvoiceDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
.WithMany("Invoices")
|
||||
.HasForeignKey("StoreDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.InvoiceEventData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
.WithMany("Events")
|
||||
.HasForeignKey("InvoiceDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PairedSINData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
.WithMany("PairedSINs")
|
||||
.HasForeignKey("StoreDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
.WithMany("Payments")
|
||||
.HasForeignKey("InvoiceDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.PendingInvoiceData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
.WithMany("PendingInvoices")
|
||||
.HasForeignKey("Id")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
.WithMany("RefundAddresses")
|
||||
.HasForeignKey("InvoiceDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Models.ApplicationUser", "ApplicationUser")
|
||||
.WithMany("UserStores")
|
||||
.HasForeignKey("ApplicationUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
.WithMany("UserStores")
|
||||
.HasForeignKey("StoreDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Services.PaymentRequests.PaymentRequestData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
.WithMany("PaymentRequests")
|
||||
.HasForeignKey("StoreDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("BTCPayServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Models.ApplicationUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
167
BTCPayServer/Migrations/20190225091644_AddOpenIddict.cs
Normal file
167
BTCPayServer/Migrations/20190225091644_AddOpenIddict.cs
Normal file
@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
public partial class AddOpenIddict : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OpenIddictApplications",
|
||||
columns: table => new
|
||||
{
|
||||
ClientId = table.Column<string>(maxLength: 100, nullable: false),
|
||||
ClientSecret = table.Column<string>(nullable: true),
|
||||
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
|
||||
ConsentType = table.Column<string>(nullable: true),
|
||||
DisplayName = table.Column<string>(nullable: true),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Permissions = table.Column<string>(nullable: true),
|
||||
PostLogoutRedirectUris = table.Column<string>(nullable: true),
|
||||
Properties = table.Column<string>(nullable: true),
|
||||
RedirectUris = table.Column<string>(nullable: true),
|
||||
Type = table.Column<string>(maxLength: 25, nullable: false),
|
||||
ApplicationUserId = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OpenIddictApplications", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OpenIddictApplications_AspNetUsers_ApplicationUserId",
|
||||
column: x => x.ApplicationUserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OpenIddictScopes",
|
||||
columns: table => new
|
||||
{
|
||||
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
|
||||
Description = table.Column<string>(nullable: true),
|
||||
DisplayName = table.Column<string>(nullable: true),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||
Properties = table.Column<string>(nullable: true),
|
||||
Resources = table.Column<string>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OpenIddictScopes", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OpenIddictAuthorizations",
|
||||
columns: table => new
|
||||
{
|
||||
ApplicationId = table.Column<string>(nullable: true),
|
||||
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Properties = table.Column<string>(nullable: true),
|
||||
Scopes = table.Column<string>(nullable: true),
|
||||
Status = table.Column<string>(maxLength: 25, nullable: false),
|
||||
Subject = table.Column<string>(maxLength: 450, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 25, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OpenIddictAuthorizations_OpenIddictApplications_ApplicationId",
|
||||
column: x => x.ApplicationId,
|
||||
principalTable: "OpenIddictApplications",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OpenIddictTokens",
|
||||
columns: table => new
|
||||
{
|
||||
ApplicationId = table.Column<string>(nullable: true),
|
||||
AuthorizationId = table.Column<string>(nullable: true),
|
||||
ConcurrencyToken = table.Column<string>(maxLength: 50, nullable: true),
|
||||
CreationDate = table.Column<DateTimeOffset>(nullable: true),
|
||||
ExpirationDate = table.Column<DateTimeOffset>(nullable: true),
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Payload = table.Column<string>(nullable: true),
|
||||
Properties = table.Column<string>(nullable: true),
|
||||
ReferenceId = table.Column<string>(maxLength: 100, nullable: true),
|
||||
Status = table.Column<string>(maxLength: 25, nullable: false),
|
||||
Subject = table.Column<string>(maxLength: 450, nullable: false),
|
||||
Type = table.Column<string>(maxLength: 25, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OpenIddictTokens", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId",
|
||||
column: x => x.ApplicationId,
|
||||
principalTable: "OpenIddictApplications",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
table.ForeignKey(
|
||||
name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId",
|
||||
column: x => x.AuthorizationId,
|
||||
principalTable: "OpenIddictAuthorizations",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OpenIddictApplications_ApplicationUserId",
|
||||
table: "OpenIddictApplications",
|
||||
column: "ApplicationUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OpenIddictApplications_ClientId",
|
||||
table: "OpenIddictApplications",
|
||||
column: "ClientId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type",
|
||||
table: "OpenIddictAuthorizations",
|
||||
columns: new[] { "ApplicationId", "Status", "Subject", "Type" });
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OpenIddictScopes_Name",
|
||||
table: "OpenIddictScopes",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OpenIddictTokens_AuthorizationId",
|
||||
table: "OpenIddictTokens",
|
||||
column: "AuthorizationId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OpenIddictTokens_ReferenceId",
|
||||
table: "OpenIddictTokens",
|
||||
column: "ReferenceId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type",
|
||||
table: "OpenIddictTokens",
|
||||
columns: new[] { "ApplicationId", "Status", "Subject", "Type" });
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "OpenIddictScopes");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OpenIddictTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OpenIddictAuthorizations");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OpenIddictApplications");
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,131 @@ namespace BTCPayServer.Migrations
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.8-servicing-32085");
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdAuthorization", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ApplicationId");
|
||||
|
||||
b.Property<string>("ConcurrencyToken")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("Properties");
|
||||
|
||||
b.Property<string>("Scopes");
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasMaxLength(450);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
|
||||
|
||||
b.ToTable("OpenIddictAuthorizations");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdClient", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ApplicationUserId");
|
||||
|
||||
b.Property<string>("ClientId")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<string>("ClientSecret");
|
||||
|
||||
b.Property<string>("ConcurrencyToken")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("ConsentType");
|
||||
|
||||
b.Property<string>("DisplayName");
|
||||
|
||||
b.Property<string>("Permissions");
|
||||
|
||||
b.Property<string>("PostLogoutRedirectUris");
|
||||
|
||||
b.Property<string>("Properties");
|
||||
|
||||
b.Property<string>("RedirectUris");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApplicationUserId");
|
||||
|
||||
b.HasIndex("ClientId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OpenIddictApplications");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdToken", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ApplicationId");
|
||||
|
||||
b.Property<string>("AuthorizationId");
|
||||
|
||||
b.Property<string>("ConcurrencyToken")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<DateTimeOffset?>("CreationDate");
|
||||
|
||||
b.Property<DateTimeOffset?>("ExpirationDate");
|
||||
|
||||
b.Property<string>("Payload");
|
||||
|
||||
b.Property<string>("Properties");
|
||||
|
||||
b.Property<string>("ReferenceId")
|
||||
.HasMaxLength(100);
|
||||
|
||||
b.Property<string>("Status")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.Property<string>("Subject")
|
||||
.IsRequired()
|
||||
.HasMaxLength(450);
|
||||
|
||||
b.Property<string>("Type")
|
||||
.IsRequired()
|
||||
.HasMaxLength(25);
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorizationId");
|
||||
|
||||
b.HasIndex("ReferenceId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
|
||||
|
||||
b.ToTable("OpenIddictTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||
{
|
||||
b.Property<string>("Address")
|
||||
@ -500,6 +625,60 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictScope<string>", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<string>("ConcurrencyToken")
|
||||
.IsConcurrencyToken()
|
||||
.HasMaxLength(50);
|
||||
|
||||
b.Property<string>("Description");
|
||||
|
||||
b.Property<string>("DisplayName");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200);
|
||||
|
||||
b.Property<string>("Properties");
|
||||
|
||||
b.Property<string>("Resources");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OpenIddictScopes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdAuthorization", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdClient", "Application")
|
||||
.WithMany("Authorizations")
|
||||
.HasForeignKey("ApplicationId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdClient", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Models.ApplicationUser", "ApplicationUser")
|
||||
.WithMany("OpenIdClients")
|
||||
.HasForeignKey("ApplicationUserId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdToken", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdClient", "Application")
|
||||
.WithMany("Tokens")
|
||||
.HasForeignKey("ApplicationId");
|
||||
|
||||
b.HasOne("BTCPayServer.Authentication.OpenId.Models.BTCPayOpenIdAuthorization", "Authorization")
|
||||
.WithMany("Tokens")
|
||||
.HasForeignKey("AuthorizationId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
|
@ -39,7 +39,7 @@ namespace BTCPayServer.ModelBinders
|
||||
|
||||
var networkProvider = (BTCPayNetworkProvider)bindingContext.HttpContext.RequestServices.GetService(typeof(BTCPayNetworkProvider));
|
||||
var cryptoCode = bindingContext.ValueProvider.GetValue("cryptoCode").FirstValue;
|
||||
var network = networkProvider.GetNetwork(cryptoCode ?? "BTC");
|
||||
var network = networkProvider.GetNetwork<BTCPayNetwork>(cryptoCode ?? "BTC");
|
||||
try
|
||||
{
|
||||
var data = new DerivationStrategyFactory(network.NBitcoinNetwork).Parse(key);
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Authentication.OpenId.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.U2F.Models;
|
||||
@ -23,6 +24,8 @@ namespace BTCPayServer.Models
|
||||
get; set;
|
||||
}
|
||||
|
||||
public List<BTCPayOpenIdClient> OpenIdClients { get; set; }
|
||||
|
||||
public List<StoredFile> StoredFiles
|
||||
{
|
||||
get;
|
||||
|
@ -23,6 +23,7 @@ namespace BTCPayServer.Models
|
||||
Html = model.Html;
|
||||
Message = model.Message;
|
||||
Severity = model.Severity;
|
||||
AllowDismiss = model.AllowDismiss;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -38,6 +39,7 @@ namespace BTCPayServer.Models
|
||||
public string Message { get; set; }
|
||||
public string Html { get; set; }
|
||||
public StatusSeverity Severity { get; set; }
|
||||
public bool AllowDismiss { get; set; } = true;
|
||||
|
||||
public string SeverityCSS
|
||||
{
|
||||
@ -51,6 +53,8 @@ namespace BTCPayServer.Models
|
||||
return "danger";
|
||||
case StatusSeverity.Success:
|
||||
return "success";
|
||||
case StatusSeverity.Warning:
|
||||
return "warning";
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
@ -74,7 +78,8 @@ namespace BTCPayServer.Models
|
||||
{
|
||||
Info,
|
||||
Error,
|
||||
Success
|
||||
Success,
|
||||
Warning
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
public IFormFile ColdcardPublicFile{ get; set; }
|
||||
public string Config { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string DerivationSchemeFormat { get; set; }
|
||||
public string AccountKey { get; set; }
|
||||
public BTCPayNetwork Network { get; set; }
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user