2017-09-13 08:47:34 +02:00
using BTCPayServer.Tests.Logging ;
using System.Linq ;
using NBitcoin ;
using NBitcoin.DataEncoders ;
using NBitcoin.Payment ;
using NBitpayClient ;
using System ;
using System.Threading ;
using Xunit ;
using Xunit.Abstractions ;
using Xunit.Sdk ;
2017-10-20 21:06:37 +02:00
using BTCPayServer.Services.Invoices ;
2017-09-25 18:31:43 +02:00
using Newtonsoft.Json ;
using System.IO ;
using Newtonsoft.Json.Linq ;
2017-10-11 05:20:44 +02:00
using BTCPayServer.Controllers ;
using Microsoft.AspNetCore.Mvc ;
2017-10-13 11:06:46 +02:00
using BTCPayServer.Authentication ;
2017-10-20 21:00:38 +02:00
using System.Diagnostics ;
2017-10-24 18:41:01 +02:00
using Microsoft.EntityFrameworkCore.Extensions ;
using BTCPayServer.Data ;
using Microsoft.EntityFrameworkCore ;
2017-10-27 04:39:11 +02:00
using BTCPayServer.Services.Rates ;
using Microsoft.Extensions.Caching.Memory ;
2018-01-09 02:54:19 +01:00
using System.Collections.Generic ;
2018-01-17 07:59:31 +01:00
using BTCPayServer.Models.StoreViewModels ;
2018-01-18 10:12:01 +01:00
using System.Threading.Tasks ;
2018-02-17 05:18:16 +01:00
using System.Globalization ;
2018-02-18 18:38:03 +01:00
using BTCPayServer.Payments ;
2018-02-19 10:54:21 +01:00
using BTCPayServer.Payments.Bitcoin ;
using BTCPayServer.HostedServices ;
2018-02-25 16:48:12 +01:00
using BTCPayServer.Payments.Lightning ;
2018-04-03 09:53:55 +02:00
using BTCPayServer.Models.AppViewModels ;
using BTCPayServer.Services.Apps ;
2018-04-29 11:28:04 +02:00
using BTCPayServer.Services.Stores ;
using System.Net.Http ;
using System.Text ;
2018-05-29 17:12:07 +02:00
using BTCPayServer.Models ;
2018-05-02 20:32:42 +02:00
using BTCPayServer.Rating ;
2018-05-14 09:32:04 +02:00
using BTCPayServer.Validation ;
2018-05-04 08:35:39 +02:00
using ExchangeSharp ;
2018-07-08 13:58:37 +02:00
using System.Security.Cryptography.X509Certificates ;
2017-09-13 08:47:34 +02:00
namespace BTCPayServer.Tests
{
2017-10-27 10:53:04 +02:00
public class UnitTest1
{
public UnitTest1 ( ITestOutputHelper helper )
{
Logs . Tester = new XUnitLog ( helper ) { Name = "Tests" } ;
Logs . LogProvider = new XUnitLogProvider ( helper ) ;
}
2018-05-14 09:32:04 +02:00
[Fact]
public void CanHandleUriValidation ( )
{
var attribute = new UriAttribute ( ) ;
Assert . True ( attribute . IsValid ( "http://localhost" ) ) ;
Assert . True ( attribute . IsValid ( "http://localhost:1234" ) ) ;
Assert . True ( attribute . IsValid ( "https://localhost" ) ) ;
Assert . True ( attribute . IsValid ( "https://127.0.0.1" ) ) ;
Assert . True ( attribute . IsValid ( "http://127.0.0.1" ) ) ;
Assert . True ( attribute . IsValid ( "http://127.0.0.1:1234" ) ) ;
Assert . True ( attribute . IsValid ( "http://gozo.com" ) ) ;
Assert . True ( attribute . IsValid ( "https://gozo.com" ) ) ;
Assert . True ( attribute . IsValid ( "https://gozo.com:1234" ) ) ;
2018-05-14 09:34:19 +02:00
Assert . True ( attribute . IsValid ( "https://gozo.com:1234/test.css" ) ) ;
Assert . True ( attribute . IsValid ( "https://gozo.com:1234/test.png" ) ) ;
2018-05-14 09:32:04 +02:00
Assert . False ( attribute . IsValid ( "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud e" ) ) ;
Assert . False ( attribute . IsValid ( 2 ) ) ;
Assert . False ( attribute . IsValid ( "http://" ) ) ;
Assert . False ( attribute . IsValid ( "httpdsadsa.com" ) ) ;
}
2018-02-19 10:54:21 +01:00
[Fact]
public void CanCalculateCryptoDue2 ( )
{
2018-03-13 07:28:39 +01:00
var dummy = new Key ( ) . PubKey . GetAddress ( Network . RegTest ) . ToString ( ) ;
2018-02-19 10:54:21 +01:00
#pragma warning disable CS0618
InvoiceEntity invoiceEntity = new InvoiceEntity ( ) ;
invoiceEntity . Payments = new System . Collections . Generic . List < PaymentEntity > ( ) ;
invoiceEntity . ProductInformation = new ProductInformation ( ) { Price = 100 } ;
PaymentMethodDictionary paymentMethods = new PaymentMethodDictionary ( ) ;
paymentMethods . Add ( new PaymentMethod ( )
{
CryptoCode = "BTC" ,
Rate = 10513.44 m ,
} . SetPaymentMethodDetails ( new BTCPayServer . Payments . Bitcoin . BitcoinLikeOnChainPaymentMethod ( )
{
TxFee = Money . Coins ( 0.00000100 m ) ,
DepositAddress = dummy
} ) ) ;
paymentMethods . Add ( new PaymentMethod ( )
{
CryptoCode = "LTC" ,
Rate = 216.79 m
} . SetPaymentMethodDetails ( new BTCPayServer . Payments . Bitcoin . BitcoinLikeOnChainPaymentMethod ( )
{
TxFee = Money . Coins ( 0.00010000 m ) ,
DepositAddress = dummy
} ) ) ;
invoiceEntity . SetPaymentMethods ( paymentMethods ) ;
var btc = invoiceEntity . GetPaymentMethod ( new PaymentMethodId ( "BTC" , PaymentTypes . BTCLike ) , null ) ;
var accounting = btc . Calculate ( ) ;
invoiceEntity . Payments . Add ( new PaymentEntity ( ) { Accounted = true , CryptoCode = "BTC" } . SetCryptoPaymentData ( new BitcoinLikePaymentData ( )
{
Output = new TxOut ( ) { Value = Money . Coins ( 0.00151263 m ) }
} ) ) ;
accounting = btc . Calculate ( ) ;
invoiceEntity . Payments . Add ( new PaymentEntity ( ) { Accounted = true , CryptoCode = "BTC" } . 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 ) ;
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 ) ;
Assert . Equal ( btc . CryptoCode , paymentMethod . CryptoCode ) ;
#pragma warning restore CS0618
}
2017-10-27 10:53:04 +02:00
[Fact]
public void CanCalculateCryptoDue ( )
{
var entity = new InvoiceEntity ( ) ;
2017-12-21 07:52:04 +01:00
#pragma warning disable CS0618
2018-01-12 09:04:47 +01:00
entity . Payments = new System . Collections . Generic . List < PaymentEntity > ( ) ;
2018-05-02 20:32:42 +02:00
entity . SetPaymentMethod ( new PaymentMethod ( ) { CryptoCode = "BTC" , Rate = 5000 , TxFee = Money . Coins ( 0.1 m ) } ) ;
2018-01-12 09:04:47 +01:00
entity . ProductInformation = new ProductInformation ( ) { Price = 5000 } ;
2018-05-02 20:32:42 +02:00
var paymentMethod = entity . GetPaymentMethods ( null ) . TryGet ( "BTC" , PaymentTypes . BTCLike ) ;
2018-02-19 07:09:05 +01:00
var accounting = paymentMethod . Calculate ( ) ;
2017-12-21 10:01:26 +01:00
Assert . Equal ( Money . Coins ( 1.1 m ) , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.1 m ) , accounting . TotalDue ) ;
2017-10-27 10:53:04 +02:00
2017-11-06 09:31:02 +01:00
entity . Payments . Add ( new PaymentEntity ( ) { Output = new TxOut ( Money . Coins ( 0.5 m ) , new Key ( ) ) , Accounted = true } ) ;
2017-10-27 10:53:04 +02:00
2018-02-19 07:09:05 +01:00
accounting = paymentMethod . Calculate ( ) ;
2017-10-27 10:53:04 +02:00
//Since we need to spend one more txout, it should be 1.1 - 0,5 + 0.1
2017-12-21 10:01:26 +01:00
Assert . Equal ( Money . Coins ( 0.7 m ) , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.2 m ) , accounting . TotalDue ) ;
2017-10-27 10:53:04 +02:00
2017-11-06 09:31:02 +01:00
entity . Payments . Add ( new PaymentEntity ( ) { Output = new TxOut ( Money . Coins ( 0.2 m ) , new Key ( ) ) , Accounted = true } ) ;
2017-12-21 10:01:26 +01:00
2018-02-19 07:09:05 +01:00
accounting = paymentMethod . Calculate ( ) ;
2017-12-21 10:01:26 +01:00
Assert . Equal ( Money . Coins ( 0.6 m ) , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.3 m ) , accounting . TotalDue ) ;
2017-10-27 10:53:04 +02:00
2017-11-06 09:31:02 +01:00
entity . Payments . Add ( new PaymentEntity ( ) { Output = new TxOut ( Money . Coins ( 0.6 m ) , new Key ( ) ) , Accounted = true } ) ;
2017-10-27 10:53:04 +02:00
2018-02-19 07:09:05 +01:00
accounting = paymentMethod . Calculate ( ) ;
2017-12-21 10:01:26 +01:00
Assert . Equal ( Money . Zero , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.3 m ) , accounting . TotalDue ) ;
2017-10-27 10:53:04 +02:00
2017-11-06 09:31:02 +01:00
entity . Payments . Add ( new PaymentEntity ( ) { Output = new TxOut ( Money . Coins ( 0.2 m ) , new Key ( ) ) , Accounted = true } ) ;
2017-10-27 10:53:04 +02:00
2018-02-19 07:09:05 +01:00
accounting = paymentMethod . Calculate ( ) ;
2017-12-21 10:01:26 +01:00
Assert . Equal ( Money . Zero , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.3 m ) , accounting . TotalDue ) ;
2018-01-09 02:54:19 +01:00
entity = new InvoiceEntity ( ) ;
entity . ProductInformation = new ProductInformation ( ) { Price = 5000 } ;
2018-02-19 07:09:05 +01:00
PaymentMethodDictionary paymentMethods = new PaymentMethodDictionary ( ) ;
paymentMethods . Add ( new PaymentMethod ( )
2018-02-18 18:38:03 +01:00
{
CryptoCode = "BTC" ,
Rate = 1000 ,
TxFee = Money . Coins ( 0.1 m )
} ) ;
2018-02-19 07:09:05 +01:00
paymentMethods . Add ( new PaymentMethod ( )
2018-02-18 18:38:03 +01:00
{
CryptoCode = "LTC" ,
Rate = 500 ,
TxFee = Money . Coins ( 0.01 m )
} ) ;
2018-02-19 07:09:05 +01:00
entity . SetPaymentMethods ( paymentMethods ) ;
2018-01-09 02:54:19 +01:00
entity . Payments = new List < PaymentEntity > ( ) ;
2018-02-19 07:09:05 +01:00
paymentMethod = entity . GetPaymentMethod ( new PaymentMethodId ( "BTC" , PaymentTypes . BTCLike ) , null ) ;
accounting = paymentMethod . Calculate ( ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( Money . Coins ( 5.1 m ) , accounting . Due ) ;
2018-02-19 07:09:05 +01:00
paymentMethod = entity . GetPaymentMethod ( new PaymentMethodId ( "LTC" , PaymentTypes . BTCLike ) , null ) ;
accounting = paymentMethod . Calculate ( ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( Money . Coins ( 10.01 m ) , accounting . TotalDue ) ;
entity . Payments . Add ( new PaymentEntity ( ) { CryptoCode = "BTC" , Output = new TxOut ( Money . Coins ( 1.0 m ) , new Key ( ) ) , Accounted = true } ) ;
2018-02-19 07:09:05 +01:00
paymentMethod = entity . GetPaymentMethod ( new PaymentMethodId ( "BTC" , PaymentTypes . BTCLike ) , null ) ;
accounting = paymentMethod . Calculate ( ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( Money . Coins ( 4.2 m ) , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.0 m ) , accounting . CryptoPaid ) ;
Assert . Equal ( Money . Coins ( 1.0 m ) , accounting . Paid ) ;
Assert . Equal ( Money . Coins ( 5.2 m ) , accounting . TotalDue ) ;
2018-02-25 16:48:12 +01:00
Assert . Equal ( 2 , accounting . TxRequired ) ;
2018-01-09 02:54:19 +01:00
2018-02-19 07:09:05 +01:00
paymentMethod = entity . GetPaymentMethod ( new PaymentMethodId ( "LTC" , PaymentTypes . BTCLike ) , null ) ;
accounting = paymentMethod . Calculate ( ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( Money . Coins ( 10.01 m + 0.1 m * 2 - 2.0 m /* 8.21m */ ) , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 0.0 m ) , accounting . CryptoPaid ) ;
Assert . Equal ( Money . Coins ( 2.0 m ) , accounting . Paid ) ;
Assert . Equal ( Money . Coins ( 10.01 m + 0.1 m * 2 ) , accounting . TotalDue ) ;
entity . Payments . Add ( new PaymentEntity ( ) { CryptoCode = "LTC" , Output = new TxOut ( Money . Coins ( 1.0 m ) , new Key ( ) ) , Accounted = true } ) ;
2018-02-19 07:09:05 +01:00
paymentMethod = entity . GetPaymentMethod ( new PaymentMethodId ( "BTC" , PaymentTypes . BTCLike ) , null ) ;
accounting = paymentMethod . Calculate ( ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( Money . Coins ( 4.2 m - 0.5 m + 0.01 m / 2 ) , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.0 m ) , accounting . CryptoPaid ) ;
Assert . Equal ( Money . Coins ( 1.5 m ) , accounting . Paid ) ;
Assert . Equal ( Money . Coins ( 5.2 m + 0.01 m / 2 ) , accounting . TotalDue ) ; // The fee for LTC added
2018-02-25 16:48:12 +01:00
Assert . Equal ( 2 , accounting . TxRequired ) ;
2018-01-09 02:54:19 +01:00
2018-02-19 07:09:05 +01:00
paymentMethod = entity . GetPaymentMethod ( new PaymentMethodId ( "LTC" , PaymentTypes . BTCLike ) , null ) ;
accounting = paymentMethod . Calculate ( ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( Money . Coins ( 8.21 m - 1.0 m + 0.01 m ) , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.0 m ) , accounting . CryptoPaid ) ;
Assert . Equal ( Money . Coins ( 3.0 m ) , accounting . Paid ) ;
Assert . Equal ( Money . Coins ( 10.01 m + 0.1 m * 2 + 0.01 m ) , accounting . TotalDue ) ;
2018-02-25 16:48:12 +01:00
Assert . Equal ( 2 , accounting . TxRequired ) ;
2018-01-09 02:54:19 +01:00
var remaining = Money . Coins ( 4.2 m - 0.5 m + 0.01 m / 2 ) ;
entity . Payments . Add ( new PaymentEntity ( ) { CryptoCode = "BTC" , Output = new TxOut ( remaining , new Key ( ) ) , Accounted = true } ) ;
2018-02-19 07:09:05 +01:00
paymentMethod = entity . GetPaymentMethod ( new PaymentMethodId ( "BTC" , PaymentTypes . BTCLike ) , null ) ;
accounting = paymentMethod . Calculate ( ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( Money . Zero , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.0 m ) + remaining , accounting . CryptoPaid ) ;
Assert . Equal ( Money . Coins ( 1.5 m ) + remaining , accounting . Paid ) ;
Assert . Equal ( Money . Coins ( 5.2 m + 0.01 m / 2 ) , accounting . TotalDue ) ;
Assert . Equal ( accounting . Paid , accounting . TotalDue ) ;
2018-02-25 16:48:12 +01:00
Assert . Equal ( 2 , accounting . TxRequired ) ;
2018-01-09 02:54:19 +01:00
2018-02-19 07:09:05 +01:00
paymentMethod = entity . GetPaymentMethod ( new PaymentMethodId ( "LTC" , PaymentTypes . BTCLike ) , null ) ;
accounting = paymentMethod . Calculate ( ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( Money . Zero , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.0 m ) , accounting . CryptoPaid ) ;
Assert . Equal ( Money . Coins ( 3.0 m ) + remaining * 2 , accounting . Paid ) ;
// Paying 2 BTC fee, LTC fee removed because fully paid
Assert . Equal ( Money . Coins ( 10.01 m + 0.1 m * 2 + 0.1 m * 2 /* + 0.01m no need to pay this fee anymore */ ) , accounting . TotalDue ) ;
2018-02-25 16:48:12 +01:00
Assert . Equal ( 1 , accounting . TxRequired ) ;
2018-01-09 02:54:19 +01:00
Assert . Equal ( accounting . Paid , accounting . TotalDue ) ;
2017-12-21 07:52:04 +01:00
#pragma warning restore CS0618
2017-10-27 10:53:04 +02:00
}
2018-05-05 16:07:22 +02:00
[Fact]
public void CanAcceptInvoiceWithTolerance ( )
{
var entity = new InvoiceEntity ( ) ;
#pragma warning disable CS0618
entity . Payments = new List < PaymentEntity > ( ) ;
entity . SetPaymentMethod ( new PaymentMethod ( ) { CryptoCode = "BTC" , Rate = 5000 , TxFee = Money . Coins ( 0.1 m ) } ) ;
entity . ProductInformation = new ProductInformation ( ) { Price = 5000 } ;
entity . PaymentTolerance = 0 ;
var paymentMethod = entity . GetPaymentMethods ( null ) . TryGet ( "BTC" , PaymentTypes . BTCLike ) ;
var accounting = paymentMethod . Calculate ( ) ;
Assert . Equal ( Money . Coins ( 1.1 m ) , accounting . Due ) ;
Assert . Equal ( Money . Coins ( 1.1 m ) , accounting . TotalDue ) ;
Assert . Equal ( Money . Coins ( 1.1 m ) , accounting . MinimumTotalDue ) ;
entity . PaymentTolerance = 10 ;
accounting = paymentMethod . Calculate ( ) ;
Assert . Equal ( Money . Coins ( 0.99 m ) , accounting . MinimumTotalDue ) ;
entity . PaymentTolerance = 100 ;
accounting = paymentMethod . Calculate ( ) ;
2018-05-08 10:57:53 +02:00
Assert . Equal ( Money . Satoshis ( 1 ) , accounting . MinimumTotalDue ) ;
2018-05-05 16:07:22 +02:00
}
2018-05-05 17:40:44 +02:00
[Fact]
public void CanAcceptInvoiceWithTolerance2 ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
user . RegisterDerivationScheme ( "BTC" ) ;
2018-05-13 08:09:17 +02:00
2018-05-05 17:40:44 +02:00
// Set tolerance to 50%
var stores = user . GetController < StoresController > ( ) ;
var vm = Assert . IsType < StoreViewModel > ( Assert . IsType < ViewResult > ( stores . UpdateStore ( ) ) . Model ) ;
Assert . Equal ( 0.0 , vm . PaymentTolerance ) ;
vm . PaymentTolerance = 50.0 ;
Assert . IsType < RedirectToActionResult > ( stores . UpdateStore ( vm ) . Result ) ;
var invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
Buyer = new Buyer ( ) { email = "test@fwf.com" } ,
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2018-05-05 17:40:44 +02:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
// Pays 75%
var invoiceAddress = BitcoinAddress . Create ( invoice . CryptoInfo [ 0 ] . Address , tester . ExplorerNode . Network ) ;
tester . ExplorerNode . SendToAddress ( invoiceAddress , Money . Satoshis ( ( decimal ) invoice . BtcDue . Satoshi * 0.75 m ) ) ;
Eventually ( ( ) = >
{
var localInvoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( "paid" , localInvoice . Status ) ;
} ) ;
}
}
2018-05-13 08:09:17 +02:00
[Fact]
public void RoundupCurrenciesCorrectly ( )
{
2018-06-18 16:07:05 +02:00
foreach ( var test in new [ ]
2018-05-13 08:09:17 +02:00
{
( 0.0005 m , "$0.0005 (USD)" ) ,
( 0.001 m , "$0.001 (USD)" ) ,
( 0.01 m , "$0.01 (USD)" ) ,
( 0.1 m , "$0.10 (USD)" ) ,
} )
{
var actual = InvoiceController . FormatCurrency ( test . Item1 , "USD" , new CurrencyNameTable ( ) ) ;
Assert . Equal ( test . Item2 , actual ) ;
}
}
2017-10-27 10:53:04 +02:00
[Fact]
public void CanPayUsingBIP70 ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
2018-02-23 07:21:42 +01:00
user . RegisterDerivationScheme ( "BTC" ) ;
2018-06-06 07:46:41 +02:00
Assert . True ( user . BitPay . TestAccess ( Facade . Merchant ) ) ;
2017-10-27 10:53:04 +02:00
var invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
Buyer = new Buyer ( ) { email = "test@fwf.com" } ,
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2017-10-27 10:53:04 +02:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
//RedirectURL = redirect + "redirect",
//NotificationURL = CallbackUri + "/notification",
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
Assert . False ( invoice . Refundable ) ;
var url = new BitcoinUrlBuilder ( invoice . PaymentUrls . BIP72 ) ;
var request = url . GetPaymentRequest ( ) ;
var payment = request . CreatePayment ( ) ;
Transaction tx = new Transaction ( ) ;
tx . Outputs . AddRange ( request . Details . Outputs . Select ( o = > new TxOut ( o . Amount , o . Script ) ) ) ;
var cashCow = tester . ExplorerNode ;
tx = cashCow . FundRawTransaction ( tx ) . Transaction ;
tx = cashCow . SignRawTransaction ( tx ) ;
payment . Transactions . Add ( tx ) ;
payment . RefundTo . Add ( new PaymentOutput ( Money . Coins ( 1.0 m ) , new Key ( ) . ScriptPubKey ) ) ;
var ack = payment . SubmitPayment ( ) ;
Assert . NotNull ( ack ) ;
Eventually ( ( ) = >
{
var localInvoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( "paid" , localInvoice . Status ) ;
Assert . True ( localInvoice . Refundable ) ;
} ) ;
}
}
2017-12-13 07:49:19 +01:00
[Fact]
public void CanUseLightMoney ( )
{
var light = LightMoney . MilliSatoshis ( 1 ) ;
Assert . Equal ( "0.00000000001" , light . ToString ( ) ) ;
2018-02-25 16:48:12 +01:00
light = LightMoney . MilliSatoshis ( 200000 ) ;
Assert . Equal ( 200 m , light . ToDecimal ( LightMoneyUnit . Satoshi ) ) ;
Assert . Equal ( 0.00000001 m * 200 m , light . ToDecimal ( LightMoneyUnit . BTC ) ) ;
}
[Fact]
public void CanSetLightningServer ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
2018-04-29 19:33:42 +02:00
var storeController = user . GetController < StoresController > ( ) ;
2018-05-03 18:46:52 +02:00
Assert . IsType < ViewResult > ( storeController . UpdateStore ( ) ) ;
2018-04-29 19:33:42 +02:00
Assert . IsType < ViewResult > ( storeController . AddLightningNode ( user . StoreId , "BTC" ) ) ;
2018-02-25 16:48:12 +01:00
var testResult = storeController . AddLightningNode ( user . StoreId , new LightningNodeViewModel ( )
{
2018-07-19 07:49:30 +02:00
ConnectionString = "type=charge;server=" + tester . MerchantCharge . Client . Uri . AbsoluteUri ,
SkipPortTest = true // We can't test this as the IP can't be resolved by the test host :(
2018-03-20 18:48:11 +01:00
} , "test" , "BTC" ) . GetAwaiter ( ) . GetResult ( ) ;
2018-02-25 16:48:12 +01:00
Assert . DoesNotContain ( "Error" , ( ( LightningNodeViewModel ) Assert . IsType < ViewResult > ( testResult ) . Model ) . StatusMessage , StringComparison . OrdinalIgnoreCase ) ;
Assert . True ( storeController . ModelState . IsValid ) ;
Assert . IsType < RedirectToActionResult > ( storeController . AddLightningNode ( user . StoreId , new LightningNodeViewModel ( )
{
2018-07-01 09:10:17 +02:00
ConnectionString = "type=charge;server=" + tester . MerchantCharge . Client . Uri . AbsoluteUri
} , "save" , "BTC" ) . GetAwaiter ( ) . GetResult ( ) ) ;
// Make sure old connection string format does not work
Assert . IsType < ViewResult > ( storeController . AddLightningNode ( user . StoreId , new LightningNodeViewModel ( )
2018-02-25 16:48:12 +01:00
{
2018-07-01 08:45:08 +02:00
ConnectionString = tester . MerchantCharge . Client . Uri . AbsoluteUri
2018-03-20 18:48:11 +01:00
} , "save" , "BTC" ) . GetAwaiter ( ) . GetResult ( ) ) ;
2018-02-25 16:48:12 +01:00
2018-05-03 18:46:52 +02:00
var storeVm = Assert . IsType < Models . StoreViewModels . StoreViewModel > ( Assert . IsType < ViewResult > ( storeController . UpdateStore ( ) ) . Model ) ;
2018-03-20 18:48:11 +01:00
Assert . Single ( storeVm . LightningNodes . Where ( l = > ! string . IsNullOrEmpty ( l . Address ) ) ) ;
2018-02-25 16:48:12 +01:00
}
2017-12-13 07:49:19 +01:00
}
2018-03-20 16:31:19 +01:00
[Fact]
public void CanParseLightningURL ( )
{
LightningConnectionString conn = null ;
2018-07-01 08:45:08 +02:00
Assert . True ( LightningConnectionString . TryParse ( "/test/a" , true , out conn ) ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
if ( i = = 1 )
Assert . True ( LightningConnectionString . TryParse ( conn . ToString ( ) , false , out conn ) ) ;
Assert . Equal ( i = = 0 , conn . IsLegacy ) ;
Assert . Equal ( "type=clightning;server=unix://test/a" , conn . ToString ( ) ) ;
Assert . Equal ( "unix://test/a" , conn . ToUri ( true ) . AbsoluteUri ) ;
Assert . Equal ( "unix://test/a" , conn . ToUri ( false ) . AbsoluteUri ) ;
Assert . Equal ( LightningConnectionType . CLightning , conn . ConnectionType ) ;
}
2018-03-20 16:31:19 +01:00
2018-07-01 08:45:08 +02:00
Assert . True ( LightningConnectionString . TryParse ( "unix://test/a" , true , out conn ) ) ;
for ( int i = 0 ; i < 2 ; i + + )
2018-03-20 16:31:19 +01:00
{
2018-07-01 08:45:08 +02:00
if ( i = = 1 )
Assert . True ( LightningConnectionString . TryParse ( conn . ToString ( ) , false , out conn ) ) ;
Assert . Equal ( "type=clightning;server=unix://test/a" , conn . ToString ( ) ) ;
Assert . Equal ( "unix://test/a" , conn . ToUri ( true ) . AbsoluteUri ) ;
Assert . Equal ( "unix://test/a" , conn . ToUri ( false ) . AbsoluteUri ) ;
Assert . Equal ( LightningConnectionType . CLightning , conn . ConnectionType ) ;
}
2018-03-20 16:31:19 +01:00
2018-07-01 08:45:08 +02:00
Assert . True ( LightningConnectionString . TryParse ( "unix://test/a" , true , out conn ) ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
if ( i = = 1 )
Assert . True ( LightningConnectionString . TryParse ( conn . ToString ( ) , false , out conn ) ) ;
Assert . Equal ( "type=clightning;server=unix://test/a" , conn . ToString ( ) ) ;
Assert . Equal ( "unix://test/a" , conn . ToUri ( true ) . AbsoluteUri ) ;
Assert . Equal ( "unix://test/a" , conn . ToUri ( false ) . AbsoluteUri ) ;
Assert . Equal ( LightningConnectionType . CLightning , conn . ConnectionType ) ;
}
2018-03-20 16:31:19 +01:00
2018-07-01 08:45:08 +02:00
Assert . True ( LightningConnectionString . TryParse ( "tcp://test/a" , true , out conn ) ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
if ( i = = 1 )
Assert . True ( LightningConnectionString . TryParse ( conn . ToString ( ) , false , out conn ) ) ;
Assert . Equal ( "type=clightning;server=tcp://test/a" , conn . ToString ( ) ) ;
Assert . Equal ( "tcp://test/a" , conn . ToUri ( true ) . AbsoluteUri ) ;
Assert . Equal ( "tcp://test/a" , conn . ToUri ( false ) . AbsoluteUri ) ;
Assert . Equal ( LightningConnectionType . CLightning , conn . ConnectionType ) ;
}
2018-03-20 16:31:19 +01:00
2018-07-01 08:45:08 +02:00
Assert . True ( LightningConnectionString . TryParse ( "http://aaa:bbb@test/a" , true , out conn ) ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
if ( i = = 1 )
Assert . True ( LightningConnectionString . TryParse ( conn . ToString ( ) , false , out conn ) ) ;
Assert . Equal ( "type=charge;server=http://aaa:bbb@test/a" , conn . ToString ( ) ) ;
Assert . Equal ( "http://aaa:bbb@test/a" , conn . ToUri ( true ) . AbsoluteUri ) ;
Assert . Equal ( "http://test/a" , conn . ToUri ( false ) . AbsoluteUri ) ;
Assert . Equal ( LightningConnectionType . Charge , conn . ConnectionType ) ;
Assert . Equal ( "aaa" , conn . Username ) ;
Assert . Equal ( "bbb" , conn . Password ) ;
}
Assert . True ( LightningConnectionString . TryParse ( "http://api-token:bbb@test/a" , true , out conn ) ) ;
for ( int i = 0 ; i < 2 ; i + + )
{
if ( i = = 1 )
Assert . True ( LightningConnectionString . TryParse ( conn . ToString ( ) , false , out conn ) ) ;
Assert . Equal ( "type=charge;server=http://test/a;api-token=bbb" , conn . ToString ( ) ) ;
2018-03-20 16:31:19 +01:00
}
2018-07-01 08:45:08 +02:00
Assert . False ( LightningConnectionString . TryParse ( "lol://aaa:bbb@test/a" , true , out conn ) ) ;
Assert . False ( LightningConnectionString . TryParse ( "https://test/a" , true , out conn ) ) ;
Assert . False ( LightningConnectionString . TryParse ( "unix://dwewoi:dwdwqd@test/a" , true , out conn ) ) ;
Assert . False ( LightningConnectionString . TryParse ( "tcp://test/a" , false , out conn ) ) ;
Assert . False ( LightningConnectionString . TryParse ( "type=charge;server=http://aaa:bbb@test/a;unk=lol" , false , out conn ) ) ;
Assert . False ( LightningConnectionString . TryParse ( "type=charge;server=tcp://aaa:bbb@test/a" , false , out conn ) ) ;
Assert . False ( LightningConnectionString . TryParse ( "type=charge" , false , out conn ) ) ;
Assert . False ( LightningConnectionString . TryParse ( "type=clightning" , false , out conn ) ) ;
Assert . True ( LightningConnectionString . TryParse ( "type=clightning;server=tcp://aaa:bbb@test/a" , false , out conn ) ) ;
Assert . True ( LightningConnectionString . TryParse ( "type=clightning;server=/aaa:bbb@test/a" , false , out conn ) ) ;
Assert . True ( LightningConnectionString . TryParse ( "type=clightning;server=unix://aaa:bbb@test/a" , false , out conn ) ) ;
Assert . False ( LightningConnectionString . TryParse ( "type=clightning;server=wtf://aaa:bbb@test/a" , false , out conn ) ) ;
2018-07-01 14:41:06 +02:00
2018-05-15 05:18:08 +02:00
var macaroon = "0201036c6e640247030a10b0dbbde28f009f83d330bde05075ca251201301a160a0761646472657373120472656164120577726974651a170a08696e766f6963657312047265616412057772697465000006200ae088692e67cf14e767c3d2a4a67ce489150bf810654ff980e1b7a7e263d5e8" ;
2018-05-14 22:54:44 +02:00
2018-07-08 13:58:37 +02:00
var certthumbprint = "c51bb1d402306d0da00e85581b32aa56166bcbab7eb888ff925d7167eb436d06" ;
// We get this format from "openssl x509 -noout -fingerprint -sha256 -inform pem -in <certificate>"
var certthumbprint2 = "C5:1B:B1:D4:02:30:6D:0D:A0:0E:85:58:1B:32:AA:56:16:6B:CB:AB:7E:B8:88:FF:92:5D:71:67:EB:43:6D:06" ;
2018-07-27 11:04:41 +02:00
2018-07-08 13:58:37 +02:00
var lndUri = $"type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/;macaroon={macaroon};certthumbprint={certthumbprint}" ;
var lndUri2 = $"type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/;macaroon={macaroon};certthumbprint={certthumbprint2}" ;
var certificateHash = new X509Certificate2 ( Encoders . Hex . DecodeData ( "2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d494942396a4343415a7967417749424167495156397a62474252724e54716b4e4b55676d72524d377a414b42676771686b6a4f50515144416a41784d5238770a485159445651514b45785a73626d5167595856306232646c626d56795958526c5a43426a5a584a304d51347744415944565151444577564754304e56557a41650a467730784f4441304d6a55794d7a517a4d6a4261467730784f5441324d6a41794d7a517a4d6a42614d444578487a416442674e5642416f54466d78755a4342680a645852765a3256755a584a686447566b49474e6c636e5178446a414d42674e5642414d5442555a50513156544d466b77457759484b6f5a497a6a3043415159490a4b6f5a497a6a304441516344516741454b7557424568564f75707965434157476130766e713262712f59396b41755a78616865646d454553482b753936436d450a397577486b4b2b4a7667547a66385141783550513741357254637155374b57595170303175364f426c5443426b6a414f42674e56485138424166384542414d430a4171517744775944565230544151482f42415577417745422f7a427642674e56485245456144426d6767564754304e565534494a6247396a5957786f62334e300a6877522f4141414268784141414141414141414141414141414141414141414268775373474f69786877514b41457342687753702f717473687754417141724c0a687753702f6d4a72687753702f754f77687753702f714e59687753702f6874436877514b70514157687753702f6c42514d416f4743437147534d343942414d430a413067414d45554349464866716d595a5043647a4a5178386b47586859473834394c31766541364c784d6f7a4f5774356d726835416945413662756e51556c710a6558553070474168776c3041654d726a4d4974394c7652736179756162565a593278343d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a" ) )
. GetCertHash ( System . Security . Cryptography . HashAlgorithmName . SHA256 ) ;
2018-07-27 11:04:41 +02:00
2018-07-01 14:41:06 +02:00
Assert . True ( LightningConnectionString . TryParse ( lndUri , false , out conn ) ) ;
2018-07-08 13:58:37 +02:00
Assert . True ( LightningConnectionString . TryParse ( lndUri2 , false , out var conn2 ) ) ;
Assert . Equal ( conn2 . ToString ( ) , conn . ToString ( ) ) ;
2018-07-01 14:41:06 +02:00
Assert . Equal ( lndUri , conn . ToString ( ) ) ;
2018-07-08 08:33:42 +02:00
Assert . Equal ( LightningConnectionType . LndREST , conn . ConnectionType ) ;
2018-07-01 14:41:06 +02:00
Assert . Equal ( macaroon , Encoders . Hex . EncodeData ( conn . Macaroon ) ) ;
2018-07-08 13:58:37 +02:00
Assert . Equal ( certthumbprint . Replace ( ":" , "" , StringComparison . OrdinalIgnoreCase ) . ToLowerInvariant ( ) , Encoders . Hex . EncodeData ( conn . CertificateThumbprint ) ) ;
Assert . True ( certificateHash . SequenceEqual ( conn . CertificateThumbprint ) ) ;
// AllowInsecure can be set to allow http
Assert . False ( LightningConnectionString . TryParse ( $"type=lnd-rest;server=http://127.0.0.1:53280/;macaroon={macaroon};allowinsecure=false" , false , out conn2 ) ) ;
Assert . True ( LightningConnectionString . TryParse ( $"type=lnd-rest;server=http://127.0.0.1:53280/;macaroon={macaroon};allowinsecure=true" , false , out conn2 ) ) ;
2018-07-10 12:33:54 +02:00
Assert . True ( LightningConnectionString . TryParse ( $"type=lnd-rest;server=http://127.0.0.1:53280/;macaroon={macaroon};allowinsecure=true" , false , out conn2 ) ) ;
2018-03-20 16:31:19 +01:00
}
[Fact]
2018-06-26 07:18:47 +02:00
public void CanSendLightningPaymentCLightning ( )
2018-03-20 16:31:19 +01:00
{
2018-05-25 19:18:47 +02:00
ProcessLightningPayment ( LightningConnectionType . CLightning ) ;
2018-03-20 16:31:19 +01:00
}
2018-02-23 07:21:42 +01:00
[Fact]
2018-06-26 07:18:47 +02:00
public void CanSendLightningPaymentCharge ( )
2018-02-23 07:21:42 +01:00
{
2018-05-25 19:18:47 +02:00
ProcessLightningPayment ( LightningConnectionType . Charge ) ;
}
2018-02-23 07:21:42 +01:00
2018-05-31 23:07:59 +02:00
[Fact]
public void CanSendLightningPaymentLnd ( )
{
2018-07-08 08:33:42 +02:00
ProcessLightningPayment ( LightningConnectionType . LndREST ) ;
2018-05-31 23:07:59 +02:00
}
2018-05-25 19:18:47 +02:00
void ProcessLightningPayment ( LightningConnectionType type )
{
2018-06-15 20:57:39 +02:00
// For easier debugging and testing
2018-06-16 00:20:56 +02:00
// LightningLikePaymentHandler.LIGHTNING_TIMEOUT = int.MaxValue;
2018-02-23 07:21:42 +01:00
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
2018-02-25 16:48:12 +01:00
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
2018-05-25 19:18:47 +02:00
user . RegisterLightningNode ( "BTC" , type ) ;
2018-02-25 16:48:12 +01:00
user . RegisterDerivationScheme ( "BTC" ) ;
2018-07-27 11:04:41 +02:00
2018-06-15 22:02:40 +02:00
tester . PrepareLightning ( type ) ;
2017-12-21 10:06:21 +01:00
2018-05-25 19:18:47 +02:00
Task . WaitAll ( CanSendLightningPaymentCore ( tester , user ) ) ;
2018-02-26 05:29:23 +01:00
Task . WaitAll ( Enumerable . Range ( 0 , 5 )
. Select ( _ = > CanSendLightningPaymentCore ( tester , user ) )
. ToArray ( ) ) ;
2018-02-23 07:21:42 +01:00
}
}
2017-12-13 07:49:19 +01:00
2018-02-26 05:29:23 +01:00
async Task CanSendLightningPaymentCore ( ServerTester tester , TestAccount user )
{
2018-06-16 01:27:37 +02:00
// TODO: If this parameter is less than 1 second we start having concurrency problems
await Task . Delay ( TimeSpan . FromMilliseconds ( 1000 ) ) ;
//
2018-02-26 05:29:23 +01:00
var invoice = await user . BitPay . CreateInvoiceAsync ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 0.01 m ,
2018-02-26 05:29:23 +01:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description"
} ) ;
await tester . SendLightningPaymentAsync ( invoice ) ;
await EventuallyAsync ( async ( ) = >
{
var localInvoice = await user . BitPay . GetInvoiceAsync ( invoice . Id ) ;
Assert . Equal ( "complete" , localInvoice . Status ) ;
Assert . Equal ( "False" , localInvoice . ExceptionStatus . ToString ( ) ) ;
} ) ;
}
2017-10-27 10:53:04 +02:00
[Fact]
public void CanUseServerInitiatedPairingCode ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var acc = tester . NewAccount ( ) ;
acc . Register ( ) ;
acc . CreateStore ( ) ;
2018-04-29 19:33:42 +02:00
var controller = acc . GetController < StoresController > ( ) ;
2018-04-30 15:00:43 +02:00
var token = ( RedirectToActionResult ) controller . CreateToken ( new Models . StoreViewModels . CreateTokenViewModel ( )
2017-10-27 10:53:04 +02:00
{
Facade = Facade . Merchant . ToString ( ) ,
Label = "bla" ,
PublicKey = null
} ) . GetAwaiter ( ) . GetResult ( ) ;
var pairingCode = ( string ) token . RouteValues [ "pairingCode" ] ;
acc . BitPay . AuthorizeClient ( new PairingCode ( pairingCode ) ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . True ( acc . BitPay . TestAccess ( Facade . Merchant ) ) ;
}
}
[Fact]
public void CanSendIPN ( )
{
using ( var callbackServer = new CustomServer ( ) )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var acc = tester . NewAccount ( ) ;
acc . GrantAccess ( ) ;
2018-02-23 07:21:42 +01:00
acc . RegisterDerivationScheme ( "BTC" ) ;
2017-10-27 10:53:04 +02:00
var invoice = acc . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5.0 m ,
2017-10-27 10:53:04 +02:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
NotificationURL = callbackServer . GetUri ( ) . AbsoluteUri ,
ItemDesc = "Some description" ,
2018-01-18 12:56:55 +01:00
FullNotifications = true ,
ExtendedNotifications = true
2017-10-27 10:53:04 +02:00
} ) ;
BitcoinUrlBuilder url = new BitcoinUrlBuilder ( invoice . PaymentUrls . BIP21 ) ;
tester . ExplorerNode . SendToAddress ( url . Address , url . Amount ) ;
Thread . Sleep ( 5000 ) ;
callbackServer . ProcessNextRequest ( ( ctx ) = >
{
var ipn = new StreamReader ( ctx . Request . Body ) . ReadToEnd ( ) ;
JsonConvert . DeserializeObject < InvoicePaymentNotification > ( ipn ) ; //can deserialize
} ) ;
var invoice2 = acc . BitPay . GetInvoice ( invoice . Id ) ;
Assert . NotNull ( invoice2 ) ;
}
}
}
[Fact]
public void CantPairTwiceWithSamePubkey ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var acc = tester . NewAccount ( ) ;
acc . Register ( ) ;
2018-04-30 15:28:00 +02:00
acc . CreateStore ( ) ;
var store = acc . GetController < StoresController > ( ) ;
2017-10-27 10:53:04 +02:00
var pairingCode = acc . BitPay . RequestClientAuthorization ( "test" , Facade . Merchant ) ;
Assert . IsType < RedirectToActionResult > ( store . Pair ( pairingCode . ToString ( ) , acc . StoreId ) . GetAwaiter ( ) . GetResult ( ) ) ;
pairingCode = acc . BitPay . RequestClientAuthorization ( "test1" , Facade . Merchant ) ;
2018-04-30 15:28:00 +02:00
acc . CreateStore ( ) ;
var store2 = acc . GetController < StoresController > ( ) ;
store2 . Pair ( pairingCode . ToString ( ) , store2 . StoreData . Id ) . GetAwaiter ( ) . GetResult ( ) ;
2018-02-17 05:18:16 +01:00
Assert . Contains ( nameof ( PairingResult . ReusedKey ) , store2 . StatusMessage , StringComparison . CurrentCultureIgnoreCase ) ;
2017-10-27 10:53:04 +02:00
}
}
2018-05-06 06:16:39 +02:00
[Fact]
public void CanListInvoices ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var acc = tester . NewAccount ( ) ;
acc . GrantAccess ( ) ;
acc . RegisterDerivationScheme ( "BTC" ) ;
// First we try payment with a merchant having only BTC
var invoice = acc . BitPay . CreateInvoice ( new Invoice ( )
{
Price = 500 ,
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
2018-05-13 08:09:17 +02:00
2018-05-06 06:16:39 +02:00
var cashCow = tester . ExplorerNode ;
var invoiceAddress = BitcoinAddress . Create ( invoice . CryptoInfo [ 0 ] . Address , cashCow . Network ) ;
var firstPayment = invoice . CryptoInfo [ 0 ] . TotalDue - Money . Satoshis ( 10 ) ;
cashCow . SendToAddress ( invoiceAddress , firstPayment ) ;
Eventually ( ( ) = >
{
invoice = acc . BitPay . GetInvoice ( invoice . Id ) ;
Assert . Equal ( firstPayment , invoice . CryptoInfo [ 0 ] . Paid ) ;
} ) ;
AssertSearchInvoice ( acc , true , invoice . Id , $"storeid:{acc.StoreId}" ) ;
AssertSearchInvoice ( acc , false , invoice . Id , $"storeid:blah" ) ;
AssertSearchInvoice ( acc , true , invoice . Id , $"{invoice.Id}" ) ;
AssertSearchInvoice ( acc , true , invoice . Id , $"exceptionstatus:paidPartial" ) ;
AssertSearchInvoice ( acc , false , invoice . Id , $"exceptionstatus:paidOver" ) ;
AssertSearchInvoice ( acc , true , invoice . Id , $"unusual:true" ) ;
AssertSearchInvoice ( acc , false , invoice . Id , $"unusual:false" ) ;
}
}
2018-05-29 17:12:07 +02:00
[Fact]
public void CanGetRates ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var acc = tester . NewAccount ( ) ;
acc . GrantAccess ( ) ;
acc . RegisterDerivationScheme ( "BTC" ) ;
acc . RegisterDerivationScheme ( "LTC" ) ;
var rateController = acc . GetController < RateController > ( ) ;
var GetBaseCurrencyRatesResult = JObject . Parse ( ( ( JsonResult ) rateController . GetBaseCurrencyRates ( "BTC" , acc . StoreId )
2018-07-30 16:22:26 +02:00
. GetAwaiter ( ) . GetResult ( ) ) . Value . ToJson ( ) ) . ToObject < DataWrapper < Rate [ ] > > ( ) ;
2018-05-29 17:12:07 +02:00
Assert . NotNull ( GetBaseCurrencyRatesResult ) ;
Assert . NotNull ( GetBaseCurrencyRatesResult . Data ) ;
2018-07-30 16:22:26 +02:00
Assert . Equal ( 2 , GetBaseCurrencyRatesResult . Data . Length ) ;
Assert . Single ( GetBaseCurrencyRatesResult . Data . Where ( o = > o . Code = = "LTC" ) ) ;
2018-05-29 17:12:07 +02:00
var GetRatesResult = JObject . Parse ( ( ( JsonResult ) rateController . GetRates ( null , acc . StoreId )
2018-07-30 16:22:26 +02:00
. GetAwaiter ( ) . GetResult ( ) ) . Value . ToJson ( ) ) . ToObject < DataWrapper < Rate [ ] > > ( ) ;
2018-05-29 17:12:07 +02:00
Assert . NotNull ( GetRatesResult ) ;
Assert . NotNull ( GetRatesResult . Data ) ;
Assert . Equal ( 2 , GetRatesResult . Data . Length ) ;
var GetCurrencyPairRateResult = JObject . Parse ( ( ( JsonResult ) rateController . GetCurrencyPairRate ( "BTC" , "LTC" , acc . StoreId )
2018-07-30 16:22:26 +02:00
. GetAwaiter ( ) . GetResult ( ) ) . Value . ToJson ( ) ) . ToObject < DataWrapper < Rate > > ( ) ;
2018-05-29 17:12:07 +02:00
Assert . NotNull ( GetCurrencyPairRateResult ) ;
Assert . NotNull ( GetCurrencyPairRateResult . Data ) ;
Assert . Equal ( "LTC" , GetCurrencyPairRateResult . Data . Code ) ;
}
}
2018-05-06 06:16:39 +02:00
private void AssertSearchInvoice ( TestAccount acc , bool expected , string invoiceId , string filter )
{
var result = ( Models . InvoicingModels . InvoicesModel ) ( ( ViewResult ) acc . GetController < InvoiceController > ( ) . ListInvoices ( filter ) . Result ) . Model ;
Assert . Equal ( expected , result . Invoices . Any ( i = > i . InvoiceId = = invoiceId ) ) ;
}
2017-11-06 09:31:02 +01:00
[Fact]
public void CanRBFPayment ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
2018-02-23 07:21:42 +01:00
user . RegisterDerivationScheme ( "BTC" ) ;
2017-11-06 09:31:02 +01:00
var invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2017-11-06 09:31:02 +01:00
Currency = "USD"
} , Facade . Merchant ) ;
2018-02-19 07:09:05 +01:00
var payment1 = invoice . BtcDue + Money . Coins ( 0.0001 m ) ;
var payment2 = invoice . BtcDue ;
2017-11-06 09:31:02 +01:00
var tx1 = new uint256 ( tester . ExplorerNode . SendCommand ( "sendtoaddress" , new object [ ]
{
2018-02-17 05:18:16 +01:00
invoice . BitcoinAddress ,
2017-11-06 09:31:02 +01:00
payment1 . ToString ( ) ,
null , //comment
null , //comment_to
false , //subtractfeefromamount
true , //replaceable
} ) . ResultString ) ;
2018-01-11 14:52:28 +01:00
var invoiceAddress = BitcoinAddress . Create ( invoice . BitcoinAddress , user . SupportedNetwork . NBitcoinNetwork ) ;
2017-11-06 09:31:02 +01:00
Eventually ( ( ) = >
{
invoice = user . BitPay . GetInvoice ( invoice . Id ) ;
Assert . Equal ( payment1 , invoice . BtcPaid ) ;
2018-02-19 07:09:05 +01:00
Assert . Equal ( "paid" , invoice . Status ) ;
Assert . Equal ( "paidOver" , invoice . ExceptionStatus . ToString ( ) ) ;
2018-01-11 14:52:28 +01:00
invoiceAddress = BitcoinAddress . Create ( invoice . BitcoinAddress , user . SupportedNetwork . NBitcoinNetwork ) ;
2017-11-06 09:31:02 +01:00
} ) ;
var tx = tester . ExplorerNode . GetRawTransaction ( new uint256 ( tx1 ) ) ;
foreach ( var input in tx . Inputs )
{
input . ScriptSig = Script . Empty ; //Strip signatures
}
var output = tx . Outputs . First ( o = > o . Value = = payment1 ) ;
output . Value = payment2 ;
output . ScriptPubKey = invoiceAddress . ScriptPubKey ;
var replaced = tester . ExplorerNode . SignRawTransaction ( tx ) ;
tester . ExplorerNode . SendRawTransaction ( replaced ) ;
2018-01-17 07:02:53 +01:00
var test = tester . ExplorerClient . GetUTXOs ( user . DerivationScheme , null ) ;
2017-11-06 09:31:02 +01:00
Eventually ( ( ) = >
{
invoice = user . BitPay . GetInvoice ( invoice . Id ) ;
Assert . Equal ( payment2 , invoice . BtcPaid ) ;
2018-02-19 07:09:05 +01:00
Assert . Equal ( "False" , invoice . ExceptionStatus . ToString ( ) ) ;
2017-11-06 09:31:02 +01:00
} ) ;
}
}
2017-12-03 15:35:52 +01:00
[Fact]
public void CanParseFilter ( )
{
var filter = "storeid:abc status:abed blabhbalh " ;
var search = new SearchString ( filter ) ;
Assert . Equal ( "storeid:abc status:abed blabhbalh" , search . ToString ( ) ) ;
Assert . Equal ( "blabhbalh" , search . TextSearch ) ;
2018-04-26 04:01:59 +02:00
Assert . Single ( search . Filters [ "storeid" ] ) ;
Assert . Single ( search . Filters [ "status" ] ) ;
Assert . Equal ( "abc" , search . Filters [ "storeid" ] . First ( ) ) ;
Assert . Equal ( "abed" , search . Filters [ "status" ] . First ( ) ) ;
filter = "status:abed status:abed2" ;
search = new SearchString ( filter ) ;
Assert . Equal ( "status:abed status:abed2" , search . ToString ( ) ) ;
Assert . Throws < KeyNotFoundException > ( ( ) = > search . Filters [ "test" ] ) ;
Assert . Equal ( 2 , search . Filters [ "status" ] . Count ) ;
Assert . Equal ( "abed" , search . Filters [ "status" ] . First ( ) ) ;
Assert . Equal ( "abed2" , search . Filters [ "status" ] . Skip ( 1 ) . First ( ) ) ;
2017-12-03 15:35:52 +01:00
}
2017-10-27 10:53:04 +02:00
[Fact]
2018-01-07 18:36:41 +01:00
public void TestAccessBitpayAPI ( )
2017-10-27 10:53:04 +02:00
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
Assert . False ( user . BitPay . TestAccess ( Facade . Merchant ) ) ;
user . GrantAccess ( ) ;
2018-02-23 07:21:42 +01:00
user . RegisterDerivationScheme ( "BTC" ) ;
2018-06-06 09:02:37 +02:00
2017-10-27 10:53:04 +02:00
Assert . True ( user . BitPay . TestAccess ( Facade . Merchant ) ) ;
2018-04-29 11:28:04 +02:00
2018-06-06 07:46:41 +02:00
// Test request pairing code client side
var storeController = user . GetController < StoresController > ( ) ;
storeController . CreateToken ( new CreateTokenViewModel ( )
{
Facade = Facade . Merchant . ToString ( ) ,
Label = "test2" ,
StoreId = user . StoreId
} ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . NotNull ( storeController . GeneratedPairingCode ) ;
2018-06-06 09:02:37 +02:00
var k = new Key ( ) ;
var bitpay = new Bitpay ( k , tester . PayTester . ServerUri ) ;
2018-06-06 07:46:41 +02:00
bitpay . AuthorizeClient ( new PairingCode ( storeController . GeneratedPairingCode ) ) . Wait ( ) ;
Assert . True ( bitpay . TestAccess ( Facade . Merchant ) ) ;
Assert . True ( bitpay . TestAccess ( Facade . PointOfSale ) ) ;
2018-06-06 09:02:37 +02:00
// Same with new instance
bitpay = new Bitpay ( k , tester . PayTester . ServerUri ) ;
Assert . True ( bitpay . TestAccess ( Facade . Merchant ) ) ;
Assert . True ( bitpay . TestAccess ( Facade . PointOfSale ) ) ;
2018-06-06 07:46:41 +02:00
2018-04-29 11:28:04 +02:00
// Can generate API Key
var repo = tester . PayTester . GetService < TokenRepository > ( ) ;
Assert . Empty ( repo . GetLegacyAPIKeys ( user . StoreId ) . GetAwaiter ( ) . GetResult ( ) ) ;
2018-04-30 15:00:43 +02:00
Assert . IsType < RedirectToActionResult > ( user . GetController < StoresController > ( ) . GenerateAPIKey ( ) . GetAwaiter ( ) . GetResult ( ) ) ;
2018-04-29 11:28:04 +02:00
var apiKey = Assert . Single ( repo . GetLegacyAPIKeys ( user . StoreId ) . GetAwaiter ( ) . GetResult ( ) ) ;
///////
// Generating a new one remove the previous
2018-04-30 15:00:43 +02:00
Assert . IsType < RedirectToActionResult > ( user . GetController < StoresController > ( ) . GenerateAPIKey ( ) . GetAwaiter ( ) . GetResult ( ) ) ;
2018-04-29 11:28:04 +02:00
var apiKey2 = Assert . Single ( repo . GetLegacyAPIKeys ( user . StoreId ) . GetAwaiter ( ) . GetResult ( ) ) ;
Assert . NotEqual ( apiKey , apiKey2 ) ;
////////
apiKey = apiKey2 ;
// Can create an invoice with this new API Key
HttpClient client = new HttpClient ( ) ;
HttpRequestMessage message = new HttpRequestMessage ( HttpMethod . Post , tester . PayTester . ServerUri . AbsoluteUri + "invoices" ) ;
message . Headers . Authorization = new System . Net . Http . Headers . AuthenticationHeaderValue ( "Basic" , Encoders . Base64 . EncodeData ( Encoders . ASCII . DecodeData ( apiKey ) ) ) ;
var invoice = new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2018-04-29 11:28:04 +02:00
Currency = "USD"
} ;
message . Content = new StringContent ( JsonConvert . SerializeObject ( invoice ) , Encoding . UTF8 , "application/json" ) ;
var result = client . SendAsync ( message ) . GetAwaiter ( ) . GetResult ( ) ;
result . EnsureSuccessStatusCode ( ) ;
/////////////////////
2018-01-07 18:36:41 +01:00
}
}
2018-01-13 14:01:09 +01:00
2018-04-15 14:18:51 +02:00
[Fact]
public void CanUseExchangeSpecificRate ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . PayTester . MockRates = false ;
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
user . RegisterDerivationScheme ( "BTC" ) ;
List < decimal > rates = new List < decimal > ( ) ;
rates . Add ( CreateInvoice ( tester , user , "coinaverage" ) ) ;
2018-04-15 14:29:44 +02:00
var bitflyer = CreateInvoice ( tester , user , "bitflyer" ) ;
var bitflyer2 = CreateInvoice ( tester , user , "bitflyer" ) ;
Assert . Equal ( bitflyer , bitflyer2 ) ; // Should be equal because cache
rates . Add ( bitflyer ) ;
2018-04-18 09:07:16 +02:00
2018-04-23 09:44:59 +02:00
foreach ( var rate in rates )
2018-04-15 14:18:51 +02:00
{
Assert . Single ( rates . Where ( r = > r = = rate ) ) ;
}
}
}
private static decimal CreateInvoice ( ServerTester tester , TestAccount user , string exchange )
{
2018-04-29 19:33:42 +02:00
var storeController = user . GetController < StoresController > ( ) ;
2018-05-03 18:46:52 +02:00
var vm = ( RatesViewModel ) ( ( ViewResult ) storeController . Rates ( ) ) . Model ;
2018-04-15 14:18:51 +02:00
vm . PreferredExchange = exchange ;
2018-05-03 18:46:52 +02:00
storeController . Rates ( vm ) . Wait ( ) ;
2018-04-15 14:18:51 +02:00
var invoice2 = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2018-04-15 14:18:51 +02:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
return invoice2 . CryptoInfo [ 0 ] . Rate ;
}
2018-01-17 07:59:31 +01:00
[Fact]
public void CanTweakRate ( )
{
using ( var tester = ServerTester . Create ( ) )
{
2018-04-15 14:18:51 +02:00
tester . PayTester . MockRates = false ;
2018-01-17 07:59:31 +01:00
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
2018-02-23 07:21:42 +01:00
user . RegisterDerivationScheme ( "BTC" ) ;
2018-01-17 07:59:31 +01:00
// First we try payment with a merchant having only BTC
var invoice1 = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2018-01-17 07:59:31 +01:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
2018-04-29 19:33:42 +02:00
var storeController = user . GetController < StoresController > ( ) ;
2018-05-03 18:46:52 +02:00
var vm = ( RatesViewModel ) ( ( ViewResult ) storeController . Rates ( ) ) . Model ;
2018-01-17 07:59:31 +01:00
Assert . Equal ( 1.0 , vm . RateMultiplier ) ;
vm . RateMultiplier = 0.5 ;
2018-05-03 18:46:52 +02:00
storeController . Rates ( vm ) . Wait ( ) ;
2018-01-17 07:59:31 +01:00
var invoice2 = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2018-01-17 07:59:31 +01:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
Assert . True ( invoice2 . BtcPrice . Almost ( invoice1 . BtcPrice * 2 , 0.00001 m ) ) ;
}
}
2018-01-13 14:01:09 +01:00
[Fact]
public void CanHaveLTCOnlyStore ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
2018-02-23 07:21:42 +01:00
user . RegisterDerivationScheme ( "LTC" ) ;
2018-01-13 14:01:09 +01:00
// First we try payment with a merchant having only BTC
var invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
Price = 500 ,
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
Assert . Single ( invoice . CryptoInfo ) ;
Assert . Equal ( "LTC" , invoice . CryptoInfo [ 0 ] . CryptoCode ) ;
2018-05-15 16:18:26 +02:00
Assert . True ( invoice . PaymentCodes . ContainsKey ( "LTC" ) ) ;
Assert . True ( invoice . SupportedTransactionCurrencies . ContainsKey ( "LTC" ) ) ;
Assert . True ( invoice . SupportedTransactionCurrencies [ "LTC" ] . Enabled ) ;
Assert . True ( invoice . PaymentSubtotals . ContainsKey ( "LTC" ) ) ;
Assert . True ( invoice . PaymentTotals . ContainsKey ( "LTC" ) ) ;
2018-01-13 14:01:09 +01:00
var cashCow = tester . LTCExplorerNode ;
var invoiceAddress = BitcoinAddress . Create ( invoice . CryptoInfo [ 0 ] . Address , cashCow . Network ) ;
var firstPayment = Money . Coins ( 0.1 m ) ;
cashCow . SendToAddress ( invoiceAddress , firstPayment ) ;
Eventually ( ( ) = >
{
invoice = user . BitPay . GetInvoice ( invoice . Id ) ;
Assert . Equal ( firstPayment , invoice . CryptoInfo [ 0 ] . Paid ) ;
} ) ;
Assert . Single ( invoice . CryptoInfo ) ; // Only BTC should be presented
var controller = tester . PayTester . GetController < InvoiceController > ( null ) ;
var checkout = ( Models . InvoicingModels . PaymentModel ) ( ( JsonResult ) controller . GetStatus ( invoice . Id , null ) . GetAwaiter ( ) . GetResult ( ) ) . Value ;
Assert . Single ( checkout . AvailableCryptos ) ;
Assert . Equal ( "LTC" , checkout . CryptoCode ) ;
//////////////////////
// Despite it is called BitcoinAddress it should be LTC because BTC is not available
Assert . Null ( invoice . BitcoinAddress ) ;
2018-05-11 15:38:31 +02:00
Assert . NotEqual ( 1.0 m , invoice . Rate ) ;
2018-01-13 14:01:09 +01:00
Assert . NotEqual ( invoice . BtcDue , invoice . CryptoInfo [ 0 ] . Due ) ; // Should be BTC rate
cashCow . SendToAddress ( invoiceAddress , invoice . CryptoInfo [ 0 ] . Due ) ;
Eventually ( ( ) = >
{
invoice = user . BitPay . GetInvoice ( invoice . Id ) ;
Assert . Equal ( "paid" , invoice . Status ) ;
checkout = ( Models . InvoicingModels . PaymentModel ) ( ( JsonResult ) controller . GetStatus ( invoice . Id , null ) . GetAwaiter ( ) . GetResult ( ) ) . Value ;
Assert . Equal ( "paid" , checkout . Status ) ;
} ) ;
}
}
2018-01-07 18:36:41 +01:00
2018-05-03 18:46:52 +02:00
[Fact]
public void CanModifyRates ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
user . RegisterDerivationScheme ( "BTC" ) ;
var store = user . GetController < StoresController > ( ) ;
var rateVm = Assert . IsType < RatesViewModel > ( Assert . IsType < ViewResult > ( store . Rates ( ) ) . Model ) ;
Assert . False ( rateVm . ShowScripting ) ;
Assert . Equal ( "coinaverage" , rateVm . PreferredExchange ) ;
Assert . Equal ( 1.0 , rateVm . RateMultiplier ) ;
Assert . Null ( rateVm . TestRateRules ) ;
rateVm . PreferredExchange = "bitflyer" ;
Assert . IsType < RedirectToActionResult > ( store . Rates ( rateVm , "Save" ) . Result ) ;
rateVm = Assert . IsType < RatesViewModel > ( Assert . IsType < ViewResult > ( store . Rates ( ) ) . Model ) ;
Assert . Equal ( "bitflyer" , rateVm . PreferredExchange ) ;
rateVm . ScriptTest = "BTC_JPY,BTC_CAD" ;
rateVm . RateMultiplier = 1.1 ;
store = user . GetController < StoresController > ( ) ;
rateVm = Assert . IsType < RatesViewModel > ( Assert . IsType < ViewResult > ( store . Rates ( rateVm , "Test" ) . Result ) . Model ) ;
Assert . NotNull ( rateVm . TestRateRules ) ;
Assert . Equal ( 2 , rateVm . TestRateRules . Count ) ;
Assert . False ( rateVm . TestRateRules [ 0 ] . Error ) ;
Assert . StartsWith ( "(bitflyer(BTC_JPY)) * 1.10 =" , rateVm . TestRateRules [ 0 ] . Rule , StringComparison . OrdinalIgnoreCase ) ;
Assert . True ( rateVm . TestRateRules [ 1 ] . Error ) ;
Assert . IsType < RedirectToActionResult > ( store . Rates ( rateVm , "Save" ) . Result ) ;
Assert . IsType < RedirectToActionResult > ( store . ShowRateRulesPost ( true ) . Result ) ;
Assert . IsType < RedirectToActionResult > ( store . Rates ( rateVm , "Save" ) . Result ) ;
store = user . GetController < StoresController > ( ) ;
rateVm = Assert . IsType < RatesViewModel > ( Assert . IsType < ViewResult > ( store . Rates ( ) ) . Model ) ;
Assert . Equal ( rateVm . DefaultScript , rateVm . Script ) ;
Assert . True ( rateVm . ShowScripting ) ;
rateVm . ScriptTest = "BTC_JPY" ;
rateVm = Assert . IsType < RatesViewModel > ( Assert . IsType < ViewResult > ( store . Rates ( rateVm , "Test" ) . Result ) . Model ) ;
Assert . True ( rateVm . ShowScripting ) ;
Assert . Contains ( "(bitflyer(BTC_JPY)) * 1.10 = " , rateVm . TestRateRules [ 0 ] . Rule , StringComparison . OrdinalIgnoreCase ) ;
rateVm . ScriptTest = "BTC_USD,BTC_CAD,DOGE_USD,DOGE_CAD" ;
rateVm . Script = "DOGE_X = bittrex(DOGE_BTC) * BTC_X;\n" +
"X_CAD = quadrigacx(X_CAD);\n" +
"X_X = gdax(X_X);" ;
rateVm . RateMultiplier = 0.5 ;
rateVm = Assert . IsType < RatesViewModel > ( Assert . IsType < ViewResult > ( store . Rates ( rateVm , "Test" ) . Result ) . Model ) ;
Assert . True ( rateVm . TestRateRules . All ( t = > ! t . Error ) ) ;
Assert . IsType < RedirectToActionResult > ( store . Rates ( rateVm , "Save" ) . Result ) ;
store = user . GetController < StoresController > ( ) ;
rateVm = Assert . IsType < RatesViewModel > ( Assert . IsType < ViewResult > ( store . Rates ( ) ) . Model ) ;
Assert . Equal ( 0.5 , rateVm . RateMultiplier ) ;
Assert . True ( rateVm . ShowScripting ) ;
Assert . Contains ( "DOGE_X" , rateVm . Script , StringComparison . OrdinalIgnoreCase ) ;
}
}
2018-01-11 09:29:48 +01:00
[Fact]
public void CanPayWithTwoCurrencies ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
2018-02-23 07:21:42 +01:00
user . RegisterDerivationScheme ( "BTC" ) ;
2018-01-11 09:29:48 +01:00
// First we try payment with a merchant having only BTC
var invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2018-01-11 09:29:48 +01:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
var cashCow = tester . ExplorerNode ;
2018-01-20 06:09:57 +01:00
cashCow . Generate ( 2 ) ; // get some money in case
2018-01-11 09:29:48 +01:00
var invoiceAddress = BitcoinAddress . Create ( invoice . BitcoinAddress , cashCow . Network ) ;
var firstPayment = Money . Coins ( 0.04 m ) ;
cashCow . SendToAddress ( invoiceAddress , firstPayment ) ;
Eventually ( ( ) = >
{
invoice = user . BitPay . GetInvoice ( invoice . Id ) ;
Assert . True ( invoice . BtcPaid = = firstPayment ) ;
} ) ;
Assert . Single ( invoice . CryptoInfo ) ; // Only BTC should be presented
var controller = tester . PayTester . GetController < InvoiceController > ( null ) ;
var checkout = ( Models . InvoicingModels . PaymentModel ) ( ( JsonResult ) controller . GetStatus ( invoice . Id , null ) . GetAwaiter ( ) . GetResult ( ) ) . Value ;
Assert . Single ( checkout . AvailableCryptos ) ;
Assert . Equal ( "BTC" , checkout . CryptoCode ) ;
2018-05-15 16:25:43 +02:00
Assert . Single ( invoice . PaymentCodes ) ;
Assert . Single ( invoice . SupportedTransactionCurrencies ) ;
Assert . Single ( invoice . SupportedTransactionCurrencies ) ;
Assert . Single ( invoice . PaymentSubtotals ) ;
Assert . Single ( invoice . PaymentTotals ) ;
Assert . True ( invoice . PaymentCodes . ContainsKey ( "BTC" ) ) ;
Assert . True ( invoice . SupportedTransactionCurrencies . ContainsKey ( "BTC" ) ) ;
Assert . True ( invoice . SupportedTransactionCurrencies [ "BTC" ] . Enabled ) ;
Assert . True ( invoice . PaymentSubtotals . ContainsKey ( "BTC" ) ) ;
Assert . True ( invoice . PaymentTotals . ContainsKey ( "BTC" ) ) ;
2018-01-11 09:29:48 +01:00
//////////////////////
// Retry now with LTC enabled
user . RegisterDerivationScheme ( "LTC" ) ;
invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2018-01-11 09:29:48 +01:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
cashCow = tester . ExplorerNode ;
invoiceAddress = BitcoinAddress . Create ( invoice . BitcoinAddress , cashCow . Network ) ;
firstPayment = Money . Coins ( 0.04 m ) ;
cashCow . SendToAddress ( invoiceAddress , firstPayment ) ;
2018-01-20 06:09:57 +01:00
Logs . Tester . LogInformation ( "First payment sent to " + invoiceAddress ) ;
2018-01-11 09:29:48 +01:00
Eventually ( ( ) = >
{
invoice = user . BitPay . GetInvoice ( invoice . Id ) ;
Assert . True ( invoice . BtcPaid = = firstPayment ) ;
} ) ;
cashCow = tester . LTCExplorerNode ;
var ltcCryptoInfo = invoice . CryptoInfo . FirstOrDefault ( c = > c . CryptoCode = = "LTC" ) ;
Assert . NotNull ( ltcCryptoInfo ) ;
invoiceAddress = BitcoinAddress . Create ( ltcCryptoInfo . Address , cashCow . Network ) ;
2018-02-17 05:18:16 +01:00
var secondPayment = Money . Coins ( decimal . Parse ( ltcCryptoInfo . Due , CultureInfo . InvariantCulture ) ) ;
2018-01-11 09:29:48 +01:00
cashCow . Generate ( 2 ) ; // LTC is not worth a lot, so just to make sure we have money...
cashCow . SendToAddress ( invoiceAddress , secondPayment ) ;
2018-01-20 06:09:57 +01:00
Logs . Tester . LogInformation ( "Second payment sent to " + invoiceAddress ) ;
2018-01-11 09:29:48 +01:00
Eventually ( ( ) = >
{
invoice = user . BitPay . GetInvoice ( invoice . Id ) ;
Assert . Equal ( Money . Zero , invoice . BtcDue ) ;
var ltcPaid = invoice . CryptoInfo . First ( c = > c . CryptoCode = = "LTC" ) ;
Assert . Equal ( Money . Zero , ltcPaid . Due ) ;
Assert . Equal ( secondPayment , ltcPaid . CryptoPaid ) ;
Assert . Equal ( "paid" , invoice . Status ) ;
Assert . False ( ( bool ) ( ( JValue ) invoice . ExceptionStatus ) . Value ) ;
} ) ;
controller = tester . PayTester . GetController < InvoiceController > ( null ) ;
checkout = ( Models . InvoicingModels . PaymentModel ) ( ( JsonResult ) controller . GetStatus ( invoice . Id , "LTC" ) . GetAwaiter ( ) . GetResult ( ) ) . Value ;
Assert . Equal ( 2 , checkout . AvailableCryptos . Count ) ;
Assert . Equal ( "LTC" , checkout . CryptoCode ) ;
2018-05-15 16:25:43 +02:00
Assert . Equal ( 2 , invoice . PaymentCodes . Count ( ) ) ;
Assert . Equal ( 2 , invoice . SupportedTransactionCurrencies . Count ( ) ) ;
Assert . Equal ( 2 , invoice . SupportedTransactionCurrencies . Count ( ) ) ;
Assert . Equal ( 2 , invoice . PaymentSubtotals . Count ( ) ) ;
Assert . Equal ( 2 , invoice . PaymentTotals . Count ( ) ) ;
Assert . True ( invoice . PaymentCodes . ContainsKey ( "LTC" ) ) ;
Assert . True ( invoice . SupportedTransactionCurrencies . ContainsKey ( "LTC" ) ) ;
Assert . True ( invoice . SupportedTransactionCurrencies [ "LTC" ] . Enabled ) ;
Assert . True ( invoice . PaymentSubtotals . ContainsKey ( "LTC" ) ) ;
Assert . True ( invoice . PaymentTotals . ContainsKey ( "LTC" ) ) ;
2018-01-11 09:29:48 +01:00
}
}
2018-03-25 18:57:44 +02:00
[Fact]
public void CanParseCurrencyValue ( )
{
Assert . True ( CurrencyValue . TryParse ( "1.50USD" , out var result ) ) ;
Assert . Equal ( "1.50 USD" , result . ToString ( ) ) ;
Assert . True ( CurrencyValue . TryParse ( "1.50 USD" , out result ) ) ;
Assert . Equal ( "1.50 USD" , result . ToString ( ) ) ;
Assert . True ( CurrencyValue . TryParse ( "1.50 usd" , out result ) ) ;
Assert . Equal ( "1.50 USD" , result . ToString ( ) ) ;
Assert . True ( CurrencyValue . TryParse ( "1 usd" , out result ) ) ;
Assert . Equal ( "1 USD" , result . ToString ( ) ) ;
Assert . True ( CurrencyValue . TryParse ( "1usd" , out result ) ) ;
Assert . Equal ( "1 USD" , result . ToString ( ) ) ;
Assert . True ( CurrencyValue . TryParse ( "1.501 usd" , out result ) ) ;
Assert . Equal ( "1.50 USD" , result . ToString ( ) ) ;
Assert . False ( CurrencyValue . TryParse ( "1.501 WTFF" , out result ) ) ;
Assert . False ( CurrencyValue . TryParse ( "1,501 usd" , out result ) ) ;
Assert . False ( CurrencyValue . TryParse ( "1.501" , out result ) ) ;
}
2018-03-24 12:40:26 +01:00
[Fact]
public void CanParseDerivationScheme ( )
{
2018-04-19 09:54:25 +02:00
var parser = new DerivationSchemeParser ( Network . TestNet ) ;
2018-03-24 12:40:26 +01:00
NBXplorer . DerivationStrategy . DerivationStrategyBase result ;
// Passing electrum stuff
// Native
result = parser . Parse ( "zpub6nL6PUGurpU3DfPDSZaRS6WshpbNc9ctCFFzrCn54cssnheM31SZJZUcFHKtjJJNhAueMbh6ptFMfy1aeiMQJr3RJ4DDt1hAPx7sMTKV48t" ) ;
Assert . Equal ( "tpubD93CJNkmGjLXnsBqE2zGDqfEh1Q8iJ8wueordy3SeWt1RngbbuxXCsqASuVWFywmfoCwUE1rSfNJbaH4cBNcbp8WcyZgPiiRSTazLGL8U9w" , result . ToString ( ) ) ;
// P2SH
result = parser . Parse ( "ypub6QqdH2c5z79681jUgdxjGJzGW9zpL4ryPCuhtZE4GpvrJoZqM823XQN6iSQeVbbbp2uCRQ9UgpeMcwiyV6qjvxTWVcxDn2XEAnioMUwsrQ5" ) ;
Assert . Equal ( "tpubD6NzVbkrYhZ4YWjDJUACG9E8fJx2NqNY1iynTiPKEjJrzzRKAgha3nNnwGXr2BtvCJKJHW4nmG7rRqc2AGGy2AECgt16seMyV2FZivUmaJg-[p2sh]" , result . ToString ( ) ) ;
result = parser . Parse ( "xpub661MyMwAqRbcGeVGU5e5KBcau1HHEUGf9Wr7k4FyLa8yRPNQrrVa7Ndrgg8Afbe2UYXMSL6tJBFd2JewwWASsePPLjkcJFL1tTVEs3UQ23X" ) ;
Assert . Equal ( "tpubD6NzVbkrYhZ4YSg7vGdAX6wxE8NwDrmih9SR6cK7gUtsAg37w5LfFpJgviCxC6bGGT4G3uckqH5fiV9ZLN1gm5qgQLVuymzFUR5ed7U7ksu-[legacy]" , result . ToString ( ) ) ;
////////////////
2018-03-26 06:52:14 +02:00
var tpub = "tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o" ;
result = parser . Parse ( tpub ) ;
Assert . Equal ( tpub , result . ToString ( ) ) ;
2018-03-24 12:40:26 +01:00
parser . HintScriptPubKey = BitcoinAddress . Create ( "tb1q4s33amqm8l7a07zdxcunqnn3gcsjcfz3xc573l" , parser . Network ) . ScriptPubKey ;
2018-03-26 06:52:14 +02:00
result = parser . Parse ( tpub ) ;
Assert . Equal ( tpub , result . ToString ( ) ) ;
2018-03-24 12:40:26 +01:00
parser . HintScriptPubKey = BitcoinAddress . Create ( "2N2humNio3YTApSfY6VztQ9hQwDnhDvaqFQ" , parser . Network ) . ScriptPubKey ;
2018-03-26 06:52:14 +02:00
result = parser . Parse ( tpub ) ;
Assert . Equal ( $"{tpub}-[p2sh]" , result . ToString ( ) ) ;
2018-03-24 12:40:26 +01:00
parser . HintScriptPubKey = BitcoinAddress . Create ( "mwD8bHS65cdgUf6rZUUSoVhi3wNQFu1Nfi" , parser . Network ) . ScriptPubKey ;
2018-03-26 06:52:14 +02:00
result = parser . Parse ( tpub ) ;
Assert . Equal ( $"{tpub}-[legacy]" , result . ToString ( ) ) ;
2018-03-24 12:40:26 +01:00
parser . HintScriptPubKey = BitcoinAddress . Create ( "2N2humNio3YTApSfY6VztQ9hQwDnhDvaqFQ" , parser . Network ) . ScriptPubKey ;
2018-03-26 06:52:14 +02:00
result = parser . Parse ( $"{tpub}-[legacy]" ) ;
Assert . Equal ( $"{tpub}-[p2sh]" , result . ToString ( ) ) ;
2018-03-24 12:40:26 +01:00
2018-03-26 06:52:14 +02:00
result = parser . Parse ( tpub ) ;
Assert . Equal ( $"{tpub}-[p2sh]" , result . ToString ( ) ) ;
2018-03-24 12:40:26 +01:00
}
2018-07-30 17:18:58 +02:00
[Fact]
public void CanDisablePaymentMethods ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
user . RegisterDerivationScheme ( "BTC" ) ;
user . RegisterDerivationScheme ( "LTC" ) ;
user . RegisterLightningNode ( "BTC" , LightningConnectionType . CLightning ) ;
var invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
Price = 1.5 m ,
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
Assert . Equal ( 3 , invoice . CryptoInfo . Length ) ;
var controller = user . GetController < StoresController > ( ) ;
var lightningVM = ( LightningNodeViewModel ) Assert . IsType < ViewResult > ( controller . AddLightningNode ( user . StoreId , "BTC" ) ) . Model ;
Assert . True ( lightningVM . Enabled ) ;
lightningVM . Enabled = false ;
controller . AddLightningNode ( user . StoreId , lightningVM , "save" , "BTC" ) . GetAwaiter ( ) . GetResult ( ) ;
lightningVM = ( LightningNodeViewModel ) Assert . IsType < ViewResult > ( controller . AddLightningNode ( user . StoreId , "BTC" ) ) . Model ;
Assert . False ( lightningVM . Enabled ) ;
var derivationVM = ( DerivationSchemeViewModel ) Assert . IsType < ViewResult > ( controller . AddDerivationScheme ( user . StoreId , "BTC" ) ) . Model ;
Assert . True ( derivationVM . Enabled ) ;
derivationVM . Enabled = false ;
derivationVM = ( DerivationSchemeViewModel ) Assert . IsType < ViewResult > ( controller . AddDerivationScheme ( user . StoreId , derivationVM , "BTC" ) . GetAwaiter ( ) . GetResult ( ) ) . Model ;
// Confirmation
controller . AddDerivationScheme ( user . StoreId , derivationVM , "BTC" ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . False ( derivationVM . Enabled ) ;
derivationVM = ( DerivationSchemeViewModel ) Assert . IsType < ViewResult > ( controller . AddDerivationScheme ( user . StoreId , "BTC" ) ) . Model ;
Assert . False ( derivationVM . Enabled ) ;
invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
Price = 1.5 m ,
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
Assert . Single ( invoice . CryptoInfo ) ;
Assert . Equal ( "LTC" , invoice . CryptoInfo [ 0 ] . CryptoCode ) ;
}
}
2018-04-03 10:39:28 +02:00
[Fact]
public void CanSetPaymentMethodLimits ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
user . RegisterDerivationScheme ( "BTC" ) ;
user . RegisterLightningNode ( "BTC" , LightningConnectionType . Charge ) ;
2018-04-30 15:00:43 +02:00
var vm = Assert . IsType < CheckoutExperienceViewModel > ( Assert . IsType < ViewResult > ( user . GetController < StoresController > ( ) . CheckoutExperience ( ) ) . Model ) ;
2018-04-03 10:39:28 +02:00
vm . LightningMaxValue = "2 USD" ;
vm . OnChainMinValue = "5 USD" ;
2018-04-30 15:00:43 +02:00
Assert . IsType < RedirectToActionResult > ( user . GetController < StoresController > ( ) . CheckoutExperience ( vm ) . Result ) ;
2018-04-03 10:39:28 +02:00
var invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 1.5 m ,
2018-04-03 10:39:28 +02:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
Assert . Single ( invoice . CryptoInfo ) ;
Assert . Equal ( PaymentTypes . LightningLike . ToString ( ) , invoice . CryptoInfo [ 0 ] . PaymentType ) ;
invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5.5 m ,
2018-04-03 10:39:28 +02:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
Assert . Single ( invoice . CryptoInfo ) ;
Assert . Equal ( PaymentTypes . BTCLike . ToString ( ) , invoice . CryptoInfo [ 0 ] . PaymentType ) ;
}
}
2018-04-03 09:53:55 +02:00
[Fact]
public void CanUsePoSApp ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
user . RegisterDerivationScheme ( "BTC" ) ;
var apps = user . GetController < AppsController > ( ) ;
var vm = Assert . IsType < CreateAppViewModel > ( Assert . IsType < ViewResult > ( apps . CreateApp ( ) . Result ) . Model ) ;
vm . Name = "test" ;
vm . SelectedAppType = AppType . PointOfSale . ToString ( ) ;
Assert . IsType < RedirectToActionResult > ( apps . CreateApp ( vm ) . Result ) ;
var appId = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps . ListApps ( ) . Result ) . Model ) . Apps [ 0 ] . Id ;
var vmpos = Assert . IsType < UpdatePointOfSaleViewModel > ( Assert . IsType < ViewResult > ( apps . UpdatePointOfSale ( appId ) . Result ) . Model ) ;
vmpos . Title = "hello" ;
vmpos . Currency = "CAD" ;
vmpos . Template =
"apple:\n" +
" price: 5.0\n" +
" title: good apple\n" +
"orange:\n" +
" price: 10.0\n" ;
Assert . IsType < RedirectToActionResult > ( apps . UpdatePointOfSale ( appId , vmpos ) . Result ) ;
vmpos = Assert . IsType < UpdatePointOfSaleViewModel > ( Assert . IsType < ViewResult > ( apps . UpdatePointOfSale ( appId ) . Result ) . Model ) ;
Assert . Equal ( "hello" , vmpos . Title ) ;
var vmview = Assert . IsType < ViewPointOfSaleViewModel > ( Assert . IsType < ViewResult > ( apps . ViewPointOfSale ( appId ) . Result ) . Model ) ;
Assert . Equal ( "hello" , vmview . Title ) ;
Assert . Equal ( 2 , vmview . Items . Length ) ;
Assert . Equal ( "good apple" , vmview . Items [ 0 ] . Title ) ;
Assert . Equal ( "orange" , vmview . Items [ 1 ] . Title ) ;
Assert . Equal ( 10.0 m , vmview . Items [ 1 ] . Price . Value ) ;
Assert . Equal ( "$5.00" , vmview . Items [ 0 ] . Price . Formatted ) ;
2018-05-24 16:54:48 +02:00
Assert . IsType < RedirectResult > ( apps . ViewPointOfSale ( appId , 0 , null , null , null , null , "orange" ) . Result ) ;
2018-04-03 09:53:55 +02:00
var invoice = user . BitPay . GetInvoices ( ) . First ( ) ;
2018-05-11 15:38:31 +02:00
Assert . Equal ( 10.00 m , invoice . Price ) ;
2018-04-03 09:53:55 +02:00
Assert . Equal ( "CAD" , invoice . Currency ) ;
Assert . Equal ( "orange" , invoice . ItemDesc ) ;
}
}
[Fact]
public void CanCreateAndDeleteApps ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
var user2 = tester . NewAccount ( ) ;
user2 . GrantAccess ( ) ;
var apps = user . GetController < AppsController > ( ) ;
var apps2 = user2 . GetController < AppsController > ( ) ;
var vm = Assert . IsType < CreateAppViewModel > ( Assert . IsType < ViewResult > ( apps . CreateApp ( ) . Result ) . Model ) ;
Assert . NotNull ( vm . SelectedAppType ) ;
Assert . Null ( vm . Name ) ;
vm . Name = "test" ;
2018-07-08 08:33:42 +02:00
vm . SelectedAppType = AppType . PointOfSale . ToString ( ) ;
2018-04-03 09:53:55 +02:00
var redirectToAction = Assert . IsType < RedirectToActionResult > ( apps . CreateApp ( vm ) . Result ) ;
2018-07-08 08:33:42 +02:00
Assert . Equal ( nameof ( apps . UpdatePointOfSale ) , redirectToAction . ActionName ) ;
2018-04-03 09:53:55 +02:00
var appList = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps . ListApps ( ) . Result ) . Model ) ;
var appList2 = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps2 . ListApps ( ) . Result ) . Model ) ;
Assert . Single ( appList . Apps ) ;
Assert . Empty ( appList2 . Apps ) ;
Assert . Equal ( "test" , appList . Apps [ 0 ] . AppName ) ;
Assert . True ( appList . Apps [ 0 ] . IsOwner ) ;
Assert . Equal ( user . StoreId , appList . Apps [ 0 ] . StoreId ) ;
Assert . IsType < NotFoundResult > ( apps2 . DeleteApp ( appList . Apps [ 0 ] . Id ) . Result ) ;
Assert . IsType < ViewResult > ( apps . DeleteApp ( appList . Apps [ 0 ] . Id ) . Result ) ;
redirectToAction = Assert . IsType < RedirectToActionResult > ( apps . DeleteAppPost ( appList . Apps [ 0 ] . Id ) . Result ) ;
Assert . Equal ( nameof ( apps . ListApps ) , redirectToAction . ActionName ) ;
appList = Assert . IsType < ListAppsViewModel > ( Assert . IsType < ViewResult > ( apps . ListApps ( ) . Result ) . Model ) ;
Assert . Empty ( appList . Apps ) ;
}
}
2018-01-07 18:36:41 +01:00
[Fact]
public void InvoiceFlowThroughDifferentStatesCorrectly ( )
{
using ( var tester = ServerTester . Create ( ) )
{
tester . Start ( ) ;
var user = tester . NewAccount ( ) ;
user . GrantAccess ( ) ;
2018-02-23 07:21:42 +01:00
user . RegisterDerivationScheme ( "BTC" ) ;
2017-10-27 10:53:04 +02:00
var invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2017-10-27 10:53:04 +02:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
var repo = tester . PayTester . GetService < InvoiceRepository > ( ) ;
var ctx = tester . PayTester . GetService < ApplicationDbContextFactory > ( ) . CreateContext ( ) ;
2018-02-25 16:48:12 +01:00
Assert . Equal ( 0 , invoice . CryptoInfo [ 0 ] . TxCount ) ;
2018-05-25 15:49:49 +02:00
Assert . True ( invoice . MinerFees . ContainsKey ( "BTC" ) ) ;
Assert . Equal ( 100 m , invoice . MinerFees [ "BTC" ] . SatoshiPerBytes ) ;
2017-11-12 15:03:33 +01:00
Eventually ( ( ) = >
2017-10-27 10:53:04 +02:00
{
2017-12-16 17:04:20 +01:00
var textSearchResult = tester . PayTester . InvoiceRepository . GetInvoices ( new InvoiceQuery ( )
2017-11-12 15:03:33 +01:00
{
2018-04-26 04:01:59 +02:00
StoreId = new [ ] { user . StoreId } ,
2017-11-12 15:03:33 +01:00
TextSearch = invoice . OrderId
} ) . GetAwaiter ( ) . GetResult ( ) ;
2017-12-21 07:52:04 +01:00
Assert . Single ( textSearchResult ) ;
2017-12-16 17:04:20 +01:00
textSearchResult = tester . PayTester . InvoiceRepository . GetInvoices ( new InvoiceQuery ( )
2017-11-12 15:03:33 +01:00
{
2018-04-26 04:01:59 +02:00
StoreId = new [ ] { user . StoreId } ,
2017-11-12 15:03:33 +01:00
TextSearch = invoice . Id
} ) . GetAwaiter ( ) . GetResult ( ) ;
2017-10-27 10:53:04 +02:00
2017-12-21 07:52:04 +01:00
Assert . Single ( textSearchResult ) ;
2017-11-12 15:03:33 +01:00
} ) ;
2017-10-27 10:53:04 +02:00
invoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( Money . Coins ( 0 ) , invoice . BtcPaid ) ;
Assert . Equal ( "new" , invoice . Status ) ;
2017-12-21 07:52:04 +01:00
Assert . False ( ( bool ) ( ( JValue ) invoice . ExceptionStatus ) . Value ) ;
2017-10-27 10:53:04 +02:00
2018-02-16 17:34:40 +01:00
Assert . Single ( user . BitPay . GetInvoices ( invoice . InvoiceTime . UtcDateTime ) ) ;
Assert . Empty ( user . BitPay . GetInvoices ( invoice . InvoiceTime . UtcDateTime + TimeSpan . FromDays ( 2 ) ) ) ;
Assert . Single ( user . BitPay . GetInvoices ( invoice . InvoiceTime . UtcDateTime - TimeSpan . FromDays ( 5 ) ) ) ;
Assert . Single ( user . BitPay . GetInvoices ( invoice . InvoiceTime . UtcDateTime - TimeSpan . FromDays ( 5 ) , invoice . InvoiceTime . DateTime + TimeSpan . FromDays ( 1.0 ) ) ) ;
Assert . Empty ( user . BitPay . GetInvoices ( invoice . InvoiceTime . UtcDateTime - TimeSpan . FromDays ( 5 ) , invoice . InvoiceTime . DateTime - TimeSpan . FromDays ( 1 ) ) ) ;
2017-10-27 10:53:04 +02:00
var firstPayment = Money . Coins ( 0.04 m ) ;
var txFee = Money . Zero ;
var cashCow = tester . ExplorerNode ;
var invoiceAddress = BitcoinAddress . Create ( invoice . BitcoinAddress , cashCow . Network ) ;
var iii = ctx . AddressInvoices . ToArray ( ) ;
Assert . True ( IsMapped ( invoice , ctx ) ) ;
cashCow . SendToAddress ( invoiceAddress , firstPayment ) ;
var invoiceEntity = repo . GetInvoice ( null , invoice . Id , true ) . GetAwaiter ( ) . GetResult ( ) ;
2017-12-21 07:52:04 +01:00
Assert . Single ( invoiceEntity . HistoricalAddresses ) ;
2017-10-27 10:53:04 +02:00
Assert . Null ( invoiceEntity . HistoricalAddresses [ 0 ] . UnAssigned ) ;
Money secondPayment = Money . Zero ;
Eventually ( ( ) = >
{
var localInvoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( "new" , localInvoice . Status ) ;
Assert . Equal ( firstPayment , localInvoice . BtcPaid ) ;
txFee = localInvoice . BtcDue - invoice . BtcDue ;
2017-12-21 07:52:04 +01:00
Assert . Equal ( "paidPartial" , localInvoice . ExceptionStatus . ToString ( ) ) ;
2018-02-25 16:48:12 +01:00
Assert . Equal ( 1 , localInvoice . CryptoInfo [ 0 ] . TxCount ) ;
2017-10-27 10:53:04 +02:00
Assert . NotEqual ( localInvoice . BitcoinAddress , invoice . BitcoinAddress ) ; //New address
Assert . True ( IsMapped ( invoice , ctx ) ) ;
Assert . True ( IsMapped ( localInvoice , ctx ) ) ;
invoiceEntity = repo . GetInvoice ( null , invoice . Id , true ) . GetAwaiter ( ) . GetResult ( ) ;
2018-02-17 05:18:16 +01:00
var historical1 = invoiceEntity . HistoricalAddresses . FirstOrDefault ( h = > h . GetAddress ( ) = = invoice . BitcoinAddress ) ;
2017-10-27 10:53:04 +02:00
Assert . NotNull ( historical1 . UnAssigned ) ;
2018-02-17 05:18:16 +01:00
var historical2 = invoiceEntity . HistoricalAddresses . FirstOrDefault ( h = > h . GetAddress ( ) = = localInvoice . BitcoinAddress ) ;
2017-10-27 10:53:04 +02:00
Assert . Null ( historical2 . UnAssigned ) ;
invoiceAddress = BitcoinAddress . Create ( localInvoice . BitcoinAddress , cashCow . Network ) ;
secondPayment = localInvoice . BtcDue ;
} ) ;
cashCow . SendToAddress ( invoiceAddress , secondPayment ) ;
Eventually ( ( ) = >
{
var localInvoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( "paid" , localInvoice . Status ) ;
2018-02-25 16:48:12 +01:00
Assert . Equal ( 2 , localInvoice . CryptoInfo [ 0 ] . TxCount ) ;
2017-10-27 10:53:04 +02:00
Assert . Equal ( firstPayment + secondPayment , localInvoice . BtcPaid ) ;
Assert . Equal ( Money . Zero , localInvoice . BtcDue ) ;
Assert . Equal ( localInvoice . BitcoinAddress , invoiceAddress . ToString ( ) ) ; //no new address generated
Assert . True ( IsMapped ( localInvoice , ctx ) ) ;
2017-12-18 00:56:27 +01:00
Assert . False ( ( bool ) ( ( JValue ) localInvoice . ExceptionStatus ) . Value ) ;
2017-10-27 10:53:04 +02:00
} ) ;
cashCow . Generate ( 1 ) ; //The user has medium speed settings, so 1 conf is enough to be confirmed
Eventually ( ( ) = >
{
var localInvoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( "confirmed" , localInvoice . Status ) ;
} ) ;
cashCow . Generate ( 5 ) ; //Now should be complete
Eventually ( ( ) = >
{
var localInvoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( "complete" , localInvoice . Status ) ;
2018-05-11 15:38:31 +02:00
Assert . NotEqual ( 0.0 m , localInvoice . Rate ) ;
2017-10-27 10:53:04 +02:00
} ) ;
invoice = user . BitPay . CreateInvoice ( new Invoice ( )
{
2018-05-11 15:38:31 +02:00
Price = 5000.0 m ,
2017-10-27 10:53:04 +02:00
Currency = "USD" ,
PosData = "posData" ,
OrderId = "orderId" ,
//RedirectURL = redirect + "redirect",
//NotificationURL = CallbackUri + "/notification",
ItemDesc = "Some description" ,
FullNotifications = true
} , Facade . Merchant ) ;
invoiceAddress = BitcoinAddress . Create ( invoice . BitcoinAddress , cashCow . Network ) ;
cashCow . SendToAddress ( invoiceAddress , invoice . BtcDue + Money . Coins ( 1 ) ) ;
Eventually ( ( ) = >
{
var localInvoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( "paid" , localInvoice . Status ) ;
Assert . Equal ( Money . Zero , localInvoice . BtcDue ) ;
Assert . Equal ( "paidOver" , ( string ) ( ( JValue ) localInvoice . ExceptionStatus ) . Value ) ;
} ) ;
cashCow . Generate ( 1 ) ;
Eventually ( ( ) = >
{
var localInvoice = user . BitPay . GetInvoice ( invoice . Id , Facade . Merchant ) ;
Assert . Equal ( "confirmed" , localInvoice . Status ) ;
Assert . Equal ( Money . Zero , localInvoice . BtcDue ) ;
Assert . Equal ( "paidOver" , ( string ) ( ( JValue ) localInvoice . ExceptionStatus ) . Value ) ;
} ) ;
}
}
2018-04-23 09:44:59 +02:00
[Fact]
public void CheckQuadrigacxRateProvider ( )
{
2018-05-02 20:32:42 +02:00
var quadri = new QuadrigacxRateProvider ( ) ;
2018-04-23 09:44:59 +02:00
var rates = quadri . GetRatesAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . NotEmpty ( rates ) ;
2018-05-22 19:18:38 +02:00
Assert . NotEqual ( 0.0 m , rates . First ( ) . BidAsk . Bid ) ;
Assert . NotEqual ( 0.0 m , rates . GetRate ( QuadrigacxRateProvider . QuadrigacxName , CurrencyPair . Parse ( "BTC_CAD" ) ) . Bid ) ;
Assert . NotEqual ( 0.0 m , rates . GetRate ( QuadrigacxRateProvider . QuadrigacxName , CurrencyPair . Parse ( "BTC_USD" ) ) . Bid ) ;
Assert . NotEqual ( 0.0 m , rates . GetRate ( QuadrigacxRateProvider . QuadrigacxName , CurrencyPair . Parse ( "LTC_CAD" ) ) . Bid ) ;
2018-05-02 20:32:42 +02:00
Assert . Null ( rates . GetRate ( QuadrigacxRateProvider . QuadrigacxName , CurrencyPair . Parse ( "LTC_USD" ) ) ) ;
2018-04-23 09:44:59 +02:00
}
2018-05-04 08:35:39 +02:00
[Fact]
public void CanQueryDirectProviders ( )
{
var provider = new BTCPayNetworkProvider ( NetworkType . Mainnet ) ;
var factory = CreateBTCPayRateFactory ( provider ) ;
2018-05-13 08:09:17 +02:00
2018-05-04 08:35:39 +02:00
foreach ( var result in factory
. DirectProviders
. Select ( p = > ( ExpectedName : p . Key , ResultAsync : p . Value . GetRatesAsync ( ) ) )
. ToList ( ) )
{
var exchangeRates = result . ResultAsync . Result ;
Assert . NotNull ( exchangeRates ) ;
Assert . NotEmpty ( exchangeRates ) ;
Assert . NotEmpty ( exchangeRates . ByExchange [ result . ExpectedName ] ) ;
// This check if the currency pair is using right currency pair
2018-05-13 08:09:17 +02:00
Assert . Contains ( exchangeRates . ByExchange [ result . ExpectedName ] ,
e = > ( e . CurrencyPair = = new CurrencyPair ( "BTC" , "USD" ) | |
2018-05-04 08:35:39 +02:00
e . CurrencyPair = = new CurrencyPair ( "BTC" , "EUR" ) | |
e . CurrencyPair = = new CurrencyPair ( "BTC" , "USDT" ) )
2018-05-22 19:18:38 +02:00
& & e . BidAsk . Bid > 1.0 m // 1BTC will always be more than 1USD
2018-05-04 08:35:39 +02:00
) ;
}
}
2018-05-02 20:40:10 +02:00
[Fact]
public void CanGetRateCryptoCurrenciesByDefault ( )
{
var provider = new BTCPayNetworkProvider ( NetworkType . Mainnet ) ;
2018-05-04 08:35:39 +02:00
var factory = CreateBTCPayRateFactory ( provider ) ;
2018-05-02 20:40:10 +02:00
var pairs =
provider . GetAll ( )
. Select ( c = > new CurrencyPair ( c . CryptoCode , "USD" ) )
. ToHashSet ( ) ;
var rules = new StoreBlob ( ) . GetDefaultRateRules ( provider ) ;
var result = factory . FetchRates ( pairs , rules ) ;
2018-05-04 08:35:39 +02:00
foreach ( var value in result )
2018-05-02 20:40:10 +02:00
{
var rateResult = value . Value . GetAwaiter ( ) . GetResult ( ) ;
2018-07-27 11:04:41 +02:00
Assert . NotNull ( rateResult . BidAsk ) ;
2018-05-02 20:40:10 +02:00
}
}
2018-05-04 08:35:39 +02:00
private static BTCPayRateProviderFactory CreateBTCPayRateFactory ( BTCPayNetworkProvider provider )
{
2018-05-13 08:09:17 +02:00
return new BTCPayRateProviderFactory ( new MemoryCacheOptions ( ) { ExpirationScanFrequency = TimeSpan . FromSeconds ( 1.0 ) } , provider , new CoinAverageSettings ( ) ) ;
2018-05-04 08:35:39 +02:00
}
2017-10-27 10:53:04 +02:00
[Fact]
public void CheckRatesProvider ( )
{
2018-05-02 20:32:42 +02:00
var provider = new BTCPayNetworkProvider ( NetworkType . Mainnet ) ;
2018-05-03 18:46:52 +02:00
var coinAverage = new CoinAverageRateProvider ( ) ;
2018-05-02 20:32:42 +02:00
var rates = coinAverage . GetRatesAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . NotNull ( rates . GetRate ( "coinaverage" , new CurrencyPair ( "BTC" , "JPY" ) ) ) ;
var ratesBitpay = new BitpayRateProvider ( new Bitpay ( new Key ( ) , new Uri ( "https://bitpay.com/" ) ) ) . GetRatesAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . NotNull ( ratesBitpay . GetRate ( "bitpay" , new CurrencyPair ( "BTC" , "JPY" ) ) ) ;
RateRules . TryParse ( "X_X = coinaverage(X_X);" , out var rateRules ) ;
2018-05-04 08:35:39 +02:00
var factory = CreateBTCPayRateFactory ( provider ) ;
2018-05-02 20:32:42 +02:00
factory . CacheSpan = TimeSpan . FromSeconds ( 10 ) ;
var fetchedRate = factory . FetchRate ( CurrencyPair . Parse ( "BTC_USD" ) , rateRules ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . False ( fetchedRate . Cached ) ;
fetchedRate = factory . FetchRate ( CurrencyPair . Parse ( "BTC_USD" ) , rateRules ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . True ( fetchedRate . Cached ) ;
Thread . Sleep ( 11000 ) ;
fetchedRate = factory . FetchRate ( CurrencyPair . Parse ( "BTC_USD" ) , rateRules ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . False ( fetchedRate . Cached ) ;
fetchedRate = factory . FetchRate ( CurrencyPair . Parse ( "BTC_USD" ) , rateRules ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . True ( fetchedRate . Cached ) ;
// Should cache at exchange level so this should hit the cache
var fetchedRate2 = factory . FetchRate ( CurrencyPair . Parse ( "LTC_USD" ) , rateRules ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . True ( fetchedRate . Cached ) ;
2018-07-27 11:04:41 +02:00
Assert . NotEqual ( fetchedRate . BidAsk . Bid , fetchedRate2 . BidAsk . Bid ) ;
2018-05-02 20:32:42 +02:00
// Should cache at exchange level this should not hit the cache as it is different exchange
RateRules . TryParse ( "X_X = bittrex(X_X);" , out rateRules ) ;
fetchedRate = factory . FetchRate ( CurrencyPair . Parse ( "BTC_USD" ) , rateRules ) . GetAwaiter ( ) . GetResult ( ) ;
Assert . False ( fetchedRate . Cached ) ;
2017-10-27 10:53:04 +02:00
}
private static bool IsMapped ( Invoice invoice , ApplicationDbContext ctx )
{
2018-04-10 12:07:57 +02:00
var h = BitcoinAddress . Create ( invoice . BitcoinAddress , Network . RegTest ) . ScriptPubKey . Hash . ToString ( ) ;
2018-02-19 03:31:34 +01:00
return ctx . AddressInvoices . FirstOrDefault ( i = > i . InvoiceDataId = = invoice . Id & & i . GetAddress ( ) = = h ) ! = null ;
2017-10-27 10:53:04 +02:00
}
private void Eventually ( Action act )
{
2018-02-26 05:29:23 +01:00
CancellationTokenSource cts = new CancellationTokenSource ( 20000 ) ;
2017-10-27 10:53:04 +02:00
while ( true )
{
try
{
act ( ) ;
break ;
}
catch ( XunitException ) when ( ! cts . Token . IsCancellationRequested )
{
cts . Token . WaitHandle . WaitOne ( 500 ) ;
}
}
}
2018-02-26 05:29:23 +01:00
private async Task EventuallyAsync ( Func < Task > act )
{
CancellationTokenSource cts = new CancellationTokenSource ( 20000 ) ;
while ( true )
{
try
{
await act ( ) ;
break ;
}
catch ( XunitException ) when ( ! cts . Token . IsCancellationRequested )
{
await Task . Delay ( 500 ) ;
}
}
}
2017-10-27 10:53:04 +02:00
}
2017-09-13 08:47:34 +02:00
}