public async Task CanToggleChangellyPaymentMethod() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); var controller = tester.PayTester.GetController <StoresController>(user.UserId, user.StoreId); var updateModel = new UpdateChangellySettingsViewModel() { ApiSecret = "secret", ApiKey = "key", ApiUrl = "http://gozo.com", ChangellyMerchantId = "aaa", Enabled = true }; Assert.Equal("UpdateStore", Assert.IsType <RedirectToActionResult>( await controller.UpdateChangellySettings(user.StoreId, updateModel, "save")).ActionName); var store = await tester.PayTester.StoreRepository.FindStore(user.StoreId); Assert.True(store.GetStoreBlob().ChangellySettings.Enabled); updateModel.Enabled = false; Assert.Equal("UpdateStore", Assert.IsType <RedirectToActionResult>( await controller.UpdateChangellySettings(user.StoreId, updateModel, "save")).ActionName); store = await tester.PayTester.StoreRepository.FindStore(user.StoreId); Assert.False(store.GetStoreBlob().ChangellySettings.Enabled); } }
public async Task ApiKeysControllerTests() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(); var client = await user.CreateClient(Policies.Unrestricted); //Get current api key var apiKeyData = await client.GetCurrentAPIKeyInfo(); Assert.NotNull(apiKeyData); Assert.Equal(client.APIKey, apiKeyData.ApiKey); Assert.Single(apiKeyData.Permissions); //revoke current api key await client.RevokeCurrentAPIKeyInfo(); await AssertHttpError(401, async() => await client.GetCurrentAPIKeyInfo()); } }
public async Task CanCreateAndDeleteCrowdfundApp() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); 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"; vm.SelectedAppType = AppType.Crowdfund.ToString(); var redirectToAction = Assert.IsType <RedirectToActionResult>(apps.CreateApp(vm).Result); Assert.Equal(nameof(apps.UpdateCrowdfund), redirectToAction.ActionName); 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.Equal(apps.CreatedAppId, appList.Apps[0].Id); 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); } }
public async Task StoresControllerTests() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(); var client = await user.CreateClient(Policies.Unrestricted); //create store var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" }); //update store var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B" }); Assert.Equal("B", updatedStore.Name); Assert.Equal("B", (await client.GetStore(newStore.Id)).Name); //list stores var stores = await client.GetStores(); var storeIds = stores.Select(data => data.Id); var storeNames = stores.Select(data => data.Name); Assert.NotNull(stores); Assert.Equal(2, stores.Count()); Assert.Contains(newStore.Id, storeIds); Assert.Contains(user.StoreId, storeIds); //get store var store = await client.GetStore(user.StoreId); Assert.Equal(user.StoreId, store.Id); Assert.Contains(store.Name, storeNames); //remove store await client.RemoveStore(newStore.Id); await AssertHttpError(403, async() => { await client.GetStore(newStore.Id); }); Assert.Single(await client.GetStores()); newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" }); var scopedClient = await user.CreateClient(Permission.Create(Policies.CanViewStoreSettings, user.StoreId).ToString()); Assert.Single(await scopedClient.GetStores()); } }
public void CanPayWithTwoCurrencies() { using (var tester = ServerTester.Create()) { tester.Start(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); // First we try payment with a merchant having only BTC var invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 5000.0, Currency = "USD", PosData = "posData", OrderId = "orderId", ItemDesc = "Some description", FullNotifications = true }, Facade.Merchant); var cashCow = tester.ExplorerNode; cashCow.Generate(2); // get some money in case var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network); var firstPayment = Money.Coins(0.04m); 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); ////////////////////// // Retry now with LTC enabled user.RegisterDerivationScheme("LTC"); invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 5000.0, 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.04m); cashCow.SendToAddress(invoiceAddress, firstPayment); Logs.Tester.LogInformation("First payment sent to " + invoiceAddress); 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); var secondPayment = Money.Coins(decimal.Parse(ltcCryptoInfo.Due, CultureInfo.InvariantCulture)); cashCow.Generate(2); // LTC is not worth a lot, so just to make sure we have money... cashCow.SendToAddress(invoiceAddress, secondPayment); Logs.Tester.LogInformation("Second payment sent to " + invoiceAddress); 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); } }
public async Task CanUseBIP79() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); ////var payJoinStateProvider = tester.PayTester.GetService<PayJoinStateProvider>(); var btcPayNetwork = tester.NetworkProvider.GetNetwork <BTCPayNetwork>("BTC"); var btcPayWallet = tester.PayTester.GetService <BTCPayWalletProvider>().GetWallet(btcPayNetwork); var cashCow = tester.ExplorerNode; cashCow.Generate(2); // get some money in case var senderUser = tester.NewAccount(); senderUser.GrantAccess(true); senderUser.RegisterDerivationScheme("BTC", true, true); var invoice = senderUser.BitPay.CreateInvoice( new Invoice() { Price = 100, Currency = "USD", FullNotifications = true }); //payjoin is not enabled by default. Assert.DoesNotContain("bpu", invoice.CryptoInfo.First().PaymentUrls.BIP21); cashCow.SendToAddress(BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network), Money.Coins(0.06m)); var receiverUser = tester.NewAccount(); receiverUser.GrantAccess(true); receiverUser.RegisterDerivationScheme("BTC", true, true); await receiverUser.EnablePayJoin(); // payjoin is enabled, with a segwit wallet, and the keys are available in nbxplorer invoice = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.02m, Currency = "BTC", FullNotifications = true }); cashCow.SendToAddress(BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network), Money.Coins(0.06m)); var receiverWalletId = new WalletId(receiverUser.StoreId, "BTC"); //give the cow some cash await cashCow.GenerateAsync(1); //let's get some more utxos first await receiverUser.ReceiveUTXO(Money.Coins(0.011m), btcPayNetwork); await receiverUser.ReceiveUTXO(Money.Coins(0.012m), btcPayNetwork); await receiverUser.ReceiveUTXO(Money.Coins(0.013m), btcPayNetwork); await receiverUser.ReceiveUTXO(Money.Coins(0.014m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.021m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.022m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.023m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.024m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.025m), btcPayNetwork); await senderUser.ReceiveUTXO(Money.Coins(0.026m), btcPayNetwork); var senderChange = await senderUser.GetNewAddress(btcPayNetwork); //Let's start the harassment invoice = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.02m, Currency = "BTC", FullNotifications = true }); var parsedBip21 = new BitcoinUrlBuilder(invoice.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var invoice2 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.02m, Currency = "BTC", FullNotifications = true }); var secondInvoiceParsedBip21 = new BitcoinUrlBuilder(invoice2.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var senderStore = await tester.PayTester.StoreRepository.FindStore(senderUser.StoreId); var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike); var derivationSchemeSettings = senderStore.GetSupportedPaymentMethods(tester.NetworkProvider) .OfType <DerivationSchemeSettings>().SingleOrDefault(settings => settings.PaymentId == paymentMethodId); ReceivedCoin[] senderCoins = null; await TestUtils.EventuallyAsync(async() => { senderCoins = await btcPayWallet.GetUnspentCoins(senderUser.DerivationScheme); Assert.Contains(senderCoins, coin => coin.Value.GetValue(btcPayNetwork) == 0.026m); }); var coin = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.021m); var coin2 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.022m); var coin3 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.023m); var coin4 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.024m); var coin5 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.025m); var coin6 = senderCoins.Single(coin => coin.Value.GetValue(btcPayNetwork) == 0.026m); var signingKeySettings = derivationSchemeSettings.GetSigningAccountKeySettings(); signingKeySettings.RootFingerprint = senderUser.GenerateWalletResponseV.MasterHDKey.GetPublicKey().GetHDFingerPrint(); var extKey = senderUser.GenerateWalletResponseV.MasterHDKey.Derive(signingKeySettings.GetRootedKeyPath() .KeyPath); var n = tester.ExplorerClient.Network.NBitcoinNetwork; var Invoice1Coin1 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(parsedBip21.Address, parsedBip21.Amount) .AddCoins(coin.Coin) .AddKeys(extKey.Derive(coin.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); var Invoice1Coin2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(parsedBip21.Address, parsedBip21.Amount) .AddCoins(coin2.Coin) .AddKeys(extKey.Derive(coin2.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); var Invoice2Coin1 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(secondInvoiceParsedBip21.Address, secondInvoiceParsedBip21.Amount) .AddCoins(coin.Coin) .AddKeys(extKey.Derive(coin.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); var Invoice2Coin2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(secondInvoiceParsedBip21.Address, secondInvoiceParsedBip21.Amount) .AddCoins(coin2.Coin) .AddKeys(extKey.Derive(coin2.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); //Attempt 1: Send a signed tx to invoice 1 that does not pay the invoice at all //Result: reject // Assert.False((await tester.PayTester.HttpClient.PostAsync(endpoint, // new StringContent(Invoice2Coin1.ToHex(), Encoding.UTF8, "text/plain"))).IsSuccessStatusCode); //Attempt 2: Create two transactions using different inputs and send them to the same invoice. //Result: Second Tx should be rejected. var Invoice1Coin1ResponseTx = await senderUser.SubmitPayjoin(invoice, Invoice1Coin1, btcPayNetwork); await senderUser.SubmitPayjoin(invoice, Invoice1Coin1, btcPayNetwork, "already-paid"); var contributedInputsInvoice1Coin1ResponseTx = Invoice1Coin1ResponseTx.Inputs.Where(txin => coin.OutPoint != txin.PrevOut); Assert.Single(contributedInputsInvoice1Coin1ResponseTx); //Attempt 3: Send the same inputs from invoice 1 to invoice 2 while invoice 1 tx has not been broadcasted //Result: Reject Tx1 but accept tx 2 as its inputs were never accepted by invoice 1 await senderUser.SubmitPayjoin(invoice2, Invoice2Coin1, btcPayNetwork, "inputs-already-used"); var Invoice2Coin2ResponseTx = await senderUser.SubmitPayjoin(invoice2, Invoice2Coin2, btcPayNetwork); var contributedInputsInvoice2Coin2ResponseTx = Invoice2Coin2ResponseTx.Inputs.Where(txin => coin2.OutPoint != txin.PrevOut); Assert.Single(contributedInputsInvoice2Coin2ResponseTx); //Attempt 4: Make tx that pays invoice 3 and 4 and submit to both //Result: reject on 4: the protocol should not worry about this complexity var invoice3 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice3ParsedBip21 = new BitcoinUrlBuilder(invoice3.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var invoice4 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice4ParsedBip21 = new BitcoinUrlBuilder(invoice4.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var Invoice3AndInvoice4Coin3 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(invoice3ParsedBip21.Address, invoice3ParsedBip21.Amount) .Send(invoice4ParsedBip21.Address, invoice4ParsedBip21.Amount) .AddCoins(coin3.Coin) .AddKeys(extKey.Derive(coin3.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .BuildTransaction(true); await senderUser.SubmitPayjoin(invoice3, Invoice3AndInvoice4Coin3, btcPayNetwork); await senderUser.SubmitPayjoin(invoice4, Invoice3AndInvoice4Coin3, btcPayNetwork, "already-paid"); //Attempt 5: Make tx that pays invoice 5 with 2 outputs //Result: proposed tx consolidates the outputs var invoice5 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice5ParsedBip21 = new BitcoinUrlBuilder(invoice5.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var Invoice5Coin4TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(invoice5ParsedBip21.Address, invoice5ParsedBip21.Amount / 2) .Send(invoice5ParsedBip21.Address, invoice5ParsedBip21.Amount / 2) .AddCoins(coin4.Coin) .AddKeys(extKey.Derive(coin4.KeyPath)) .SendEstimatedFees(new FeeRate(100m)); var Invoice5Coin4 = Invoice5Coin4TxBuilder.BuildTransaction(true); var Invoice5Coin4ResponseTx = await senderUser.SubmitPayjoin(invoice5, Invoice5Coin4, btcPayNetwork); Assert.Single(Invoice5Coin4ResponseTx.Outputs.To(invoice5ParsedBip21.Address)); //Attempt 10: send tx with rbf, broadcast payjoin tx, bump the rbf payjoin , attempt to submit tx again //Result: same tx gets sent back //give the receiver some more utxos Assert.NotNull(await tester.ExplorerNode.SendToAddressAsync( (await btcPayWallet.ReserveAddressAsync(receiverUser.DerivationScheme)).Address, new Money(0.1m, MoneyUnit.BTC))); var invoice6 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice6ParsedBip21 = new BitcoinUrlBuilder(invoice6.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var invoice6Coin5TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(invoice6ParsedBip21.Address, invoice6ParsedBip21.Amount) .AddCoins(coin5.Coin) .AddKeys(extKey.Derive(coin5.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .SetLockTime(0); var invoice6Coin5 = invoice6Coin5TxBuilder .BuildTransaction(true); var Invoice6Coin5Response1Tx = await senderUser.SubmitPayjoin(invoice6, invoice6Coin5, btcPayNetwork); var Invoice6Coin5Response1TxSigned = invoice6Coin5TxBuilder.SignTransaction(Invoice6Coin5Response1Tx); //broadcast the first payjoin await tester.ExplorerClient.BroadcastAsync(Invoice6Coin5Response1TxSigned); // invoice6Coin5TxBuilder = invoice6Coin5TxBuilder.SendEstimatedFees(new FeeRate(100m)); // var invoice6Coin5Bumpedfee = invoice6Coin5TxBuilder // .BuildTransaction(true); // // var Invoice6Coin5Response3 = await tester.PayTester.HttpClient.PostAsync(invoice6Endpoint, // new StringContent(invoice6Coin5Bumpedfee.ToHex(), Encoding.UTF8, "text/plain")); // Assert.True(Invoice6Coin5Response3.IsSuccessStatusCode); // var Invoice6Coin5Response3Tx = // Transaction.Parse(await Invoice6Coin5Response3.Content.ReadAsStringAsync(), n); // Assert.True(invoice6Coin5Bumpedfee.Inputs.All(txin => // Invoice6Coin5Response3Tx.Inputs.Any(txin2 => txin2.PrevOut == txin.PrevOut))); //Attempt 11: //send tx with rbt, broadcast payjoin, //create tx spending the original tx inputs with rbf to self, //Result: the exposed utxos are priorized in the next p2ep //give the receiver some more utxos Assert.NotNull(await tester.ExplorerNode.SendToAddressAsync( (await btcPayWallet.ReserveAddressAsync(receiverUser.DerivationScheme)).Address, new Money(0.1m, MoneyUnit.BTC))); var invoice7 = receiverUser.BitPay.CreateInvoice( new Invoice() { Price = 0.01m, Currency = "BTC", FullNotifications = true }); var invoice7ParsedBip21 = new BitcoinUrlBuilder(invoice7.CryptoInfo.First().PaymentUrls.BIP21, tester.ExplorerClient.Network.NBitcoinNetwork); var invoice7Coin6TxBuilder = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .Send(invoice7ParsedBip21.Address, invoice7ParsedBip21.Amount) .AddCoins(coin6.Coin) .AddKeys(extKey.Derive(coin6.KeyPath)) .SendEstimatedFees(new FeeRate(100m)) .SetLockTime(0); var invoice7Coin6Tx = invoice7Coin6TxBuilder .BuildTransaction(true); var invoice7Coin6Response1Tx = await senderUser.SubmitPayjoin(invoice7, invoice7Coin6Tx, btcPayNetwork); var Invoice7Coin6Response1TxSigned = invoice7Coin6TxBuilder.SignTransaction(invoice7Coin6Response1Tx); var contributedInputsInvoice7Coin6Response1TxSigned = Invoice7Coin6Response1TxSigned.Inputs.Single(txin => coin6.OutPoint != txin.PrevOut); ////var receiverWalletPayJoinState = payJoinStateProvider.Get(receiverWalletId); ////Assert.Contains(receiverWalletPayJoinState.GetRecords(), item => item.InvoiceId == invoice7.Id); //broadcast the payjoin var res = (await tester.ExplorerClient.BroadcastAsync(Invoice7Coin6Response1TxSigned)); Assert.True(res.Success); // Paid with coinjoin await TestUtils.EventuallyAsync(async() => { var invoiceEntity = await tester.PayTester.GetService <InvoiceRepository>().GetInvoice(invoice7.Id); Assert.Equal(InvoiceStatus.Paid, invoiceEntity.Status); Assert.Contains(invoiceEntity.GetPayments(), p => p.Accounted && ((BitcoinLikePaymentData)p.GetCryptoPaymentData()).PayjoinInformation is null); }); ////Assert.Contains(receiverWalletPayJoinState.GetRecords(), item => item.InvoiceId == invoice7.Id && item.TxSeen); var invoice7Coin6Tx2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder() .SetChange(senderChange) .AddCoins(coin6.Coin) .SendAll(senderChange) .SubtractFees() .AddKeys(extKey.Derive(coin6.KeyPath)) .SendEstimatedFees(new FeeRate(200m)) .SetLockTime(0) .BuildTransaction(true); //broadcast the "rbf cancel" tx res = (await tester.ExplorerClient.BroadcastAsync(invoice7Coin6Tx2)); Assert.True(res.Success); // Make a block, this should put back the invoice to new var blockhash = tester.ExplorerNode.Generate(1)[0]; Assert.NotNull(await tester.ExplorerNode.GetRawTransactionAsync(invoice7Coin6Tx2.GetHash(), blockhash)); Assert.Null(await tester.ExplorerNode.GetRawTransactionAsync(Invoice7Coin6Response1TxSigned.GetHash(), blockhash, false)); // Now we should return to New OutPoint ourOutpoint = null; await TestUtils.EventuallyAsync(async() => { var invoiceEntity = await tester.PayTester.GetService <InvoiceRepository>().GetInvoice(invoice7.Id); Assert.Equal(InvoiceStatus.New, invoiceEntity.Status); Assert.True(invoiceEntity.GetPayments().All(p => !p.Accounted)); ourOutpoint = invoiceEntity.GetAllBitcoinPaymentData().First().PayjoinInformation.ContributedOutPoints[0]; }); var payjoinRepository = tester.PayTester.GetService <PayJoinRepository>(); // The outpoint should now be available for next pj selection Assert.False(await payjoinRepository.TryUnlock(ourOutpoint)); } }
public static SeleniumTester Create([CallerMemberNameAttribute] string scope = null, bool newDb = false) => new SeleniumTester { Server = ServerTester.Create(scope, newDb) };
public void InvoiceFlowThroughDifferentStatesCorrectly() { using (var tester = ServerTester.Create()) { tester.Start(); var user = tester.NewAccount(); Assert.False(user.BitPay.TestAccess(Facade.Merchant)); user.GrantAccess(); Assert.True(user.BitPay.TestAccess(Facade.Merchant)); var invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 5000.0, Currency = "USD", PosData = "posData", OrderId = "orderId", //RedirectURL = redirect + "redirect", //NotificationURL = CallbackUri + "/notification", ItemDesc = "Some description", FullNotifications = true }, Facade.Merchant); var textSearchResult = tester.PayTester.Runtime.InvoiceRepository.GetInvoices(new InvoiceQuery() { StoreId = user.StoreId, TextSearch = invoice.OrderId }).GetAwaiter().GetResult(); Assert.Equal(1, textSearchResult.Length); textSearchResult = tester.PayTester.Runtime.InvoiceRepository.GetInvoices(new InvoiceQuery() { StoreId = user.StoreId, TextSearch = invoice.Id }).GetAwaiter().GetResult(); Assert.Equal(1, textSearchResult.Length); invoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal(Money.Coins(0), invoice.BtcPaid); Assert.Equal("new", invoice.Status); Assert.Equal(false, (bool)((JValue)invoice.ExceptionStatus).Value); Assert.Equal(1, user.BitPay.GetInvoices(invoice.InvoiceTime.DateTime).Length); Assert.Equal(0, user.BitPay.GetInvoices(invoice.InvoiceTime.DateTime + TimeSpan.FromDays(1)).Length); Assert.Equal(1, user.BitPay.GetInvoices(invoice.InvoiceTime.DateTime - TimeSpan.FromDays(5)).Length); Assert.Equal(1, user.BitPay.GetInvoices(invoice.InvoiceTime.DateTime - TimeSpan.FromDays(5), invoice.InvoiceTime.DateTime).Length); Assert.Equal(0, user.BitPay.GetInvoices(invoice.InvoiceTime.DateTime - TimeSpan.FromDays(5), invoice.InvoiceTime.DateTime - TimeSpan.FromDays(1)).Length); var firstPayment = Money.Coins(0.04m); var txFee = Money.Zero; var rate = user.BitPay.GetRates(); var cashCow = tester.ExplorerNode; var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network); cashCow.SendToAddress(invoiceAddress, firstPayment); Money secondPayment = Money.Zero; Eventually(() => { tester.SimulateCallback(invoiceAddress); var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal("paidPartial", localInvoice.Status); Assert.Equal(firstPayment, localInvoice.BtcPaid); txFee = localInvoice.BtcDue - invoice.BtcDue; Assert.Equal("paidPartial", localInvoice.ExceptionStatus); secondPayment = localInvoice.BtcDue; }); cashCow.SendToAddress(invoiceAddress, secondPayment); Eventually(() => { tester.SimulateCallback(invoiceAddress); var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal("paid", localInvoice.Status); Assert.Equal(firstPayment + secondPayment, localInvoice.BtcPaid); Assert.Equal(Money.Zero, localInvoice.BtcDue); Assert.Equal(false, (bool)((JValue)localInvoice.ExceptionStatus).Value); }); cashCow.Generate(1); //The user has medium speed settings, so 1 conf is enough to be confirmed Eventually(() => { tester.SimulateCallback(); var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal("confirmed", localInvoice.Status); }); cashCow.Generate(5); //Now should be complete Eventually(() => { tester.SimulateCallback(); var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal("complete", localInvoice.Status); }); invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 5000.0, 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(() => { tester.SimulateCallback(invoiceAddress); var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal("paidOver", localInvoice.Status); Assert.Equal(Money.Zero, localInvoice.BtcDue); Assert.Equal("paidOver", (string)((JValue)localInvoice.ExceptionStatus).Value); }); cashCow.Generate(1); Eventually(() => { tester.SimulateCallback(); 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); }); } }
public async Task CanContributeOnlyWhenAllowed() { 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.Crowdfund.ToString(); Assert.IsType <RedirectToActionResult>(apps.CreateApp(vm).Result); var appId = Assert.IsType <ListAppsViewModel>(Assert.IsType <ViewResult>(apps.ListApps().Result).Model) .Apps[0].Id; //Scenario 1: Not Enabled - Not Allowed var crowdfundViewModel = Assert.IsType <UpdateCrowdfundViewModel>(Assert .IsType <ViewResult>(apps.UpdateCrowdfund(appId).Result).Model); crowdfundViewModel.TargetCurrency = "BTC"; crowdfundViewModel.Enabled = false; crowdfundViewModel.EndDate = null; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); var anonAppPubsController = tester.PayTester.GetController <AppsPublicController>(); var publicApps = user.GetController <AppsPublicController>(); Assert.IsType <NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(appId, new ContributeToCrowdfund() { Amount = new decimal(0.01) })); Assert.IsType <NotFoundResult>(await anonAppPubsController.ViewCrowdfund(appId, string.Empty)); //Scenario 2: Not Enabled But Admin - Allowed Assert.IsType <OkObjectResult>(await publicApps.ContributeToCrowdfund(appId, new ContributeToCrowdfund() { RedirectToCheckout = false, Amount = new decimal(0.01) })); Assert.IsType <ViewResult>(await publicApps.ViewCrowdfund(appId, string.Empty)); Assert.IsType <NotFoundResult>(await anonAppPubsController.ViewCrowdfund(appId, string.Empty)); //Scenario 3: Enabled But Start Date > Now - Not Allowed crowdfundViewModel.StartDate = DateTime.Today.AddDays(2); crowdfundViewModel.Enabled = true; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); Assert.IsType <NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(appId, new ContributeToCrowdfund() { Amount = new decimal(0.01) })); //Scenario 4: Enabled But End Date < Now - Not Allowed crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2); crowdfundViewModel.EndDate = DateTime.Today.AddDays(-1); crowdfundViewModel.Enabled = true; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); Assert.IsType <NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(appId, new ContributeToCrowdfund() { Amount = new decimal(0.01) })); //Scenario 5: Enabled and within correct timeframe, however target is enforced and Amount is Over - Not Allowed crowdfundViewModel.StartDate = DateTime.Today.AddDays(-2); crowdfundViewModel.EndDate = DateTime.Today.AddDays(2); crowdfundViewModel.Enabled = true; crowdfundViewModel.TargetAmount = 1; crowdfundViewModel.TargetCurrency = "BTC"; crowdfundViewModel.EnforceTargetAmount = true; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); Assert.IsType <NotFoundObjectResult>(await anonAppPubsController.ContributeToCrowdfund(appId, new ContributeToCrowdfund() { Amount = new decimal(1.01) })); //Scenario 6: Allowed Assert.IsType <OkObjectResult>(await anonAppPubsController.ContributeToCrowdfund(appId, new ContributeToCrowdfund() { Amount = new decimal(0.05) })); } }
public void CanComputeCrowdfundModel() { 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.Crowdfund.ToString(); Assert.IsType <RedirectToActionResult>(apps.CreateApp(vm).Result); var appId = Assert.IsType <ListAppsViewModel>(Assert.IsType <ViewResult>(apps.ListApps().Result).Model) .Apps[0].Id; var crowdfundViewModel = Assert.IsType <UpdateCrowdfundViewModel>(Assert .IsType <ViewResult>(apps.UpdateCrowdfund(appId).Result).Model); crowdfundViewModel.Enabled = true; crowdfundViewModel.EndDate = null; crowdfundViewModel.TargetAmount = 100; crowdfundViewModel.TargetCurrency = "BTC"; crowdfundViewModel.UseAllStoreInvoices = true; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); var anonAppPubsController = tester.PayTester.GetController <AppsPublicController>(); var publicApps = user.GetController <AppsPublicController>(); var model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(crowdfundViewModel.TargetAmount, model.TargetAmount); Assert.Equal(crowdfundViewModel.EndDate, model.EndDate); Assert.Equal(crowdfundViewModel.StartDate, model.StartDate); Assert.Equal(crowdfundViewModel.TargetCurrency, model.TargetCurrency); Assert.Equal(0m, model.Info.CurrentAmount); Assert.Equal(0m, model.Info.CurrentPendingAmount); Assert.Equal(0m, model.Info.ProgressPercentage); var invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "*****@*****.**" }, Price = 1m, Currency = "BTC", PosData = "posData", OrderId = $"{CrowdfundHubStreamer.CrowdfundInvoiceOrderIdPrefix}{appId}", ItemDesc = "Some description", TransactionSpeed = "high", FullNotifications = true }, Facade.Merchant); model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network); tester.ExplorerNode.SendToAddress(invoiceAddress, invoice.BtcDue); Assert.Equal(0m, model.Info.CurrentAmount); Assert.Equal(1m, model.Info.CurrentPendingAmount); Assert.Equal(0m, model.Info.ProgressPercentage); Assert.Equal(1m, model.Info.PendingProgressPercentage); } }
public async Task CanCreateViewUpdateAndDeletePaymentRequest() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); var user2 = tester.NewAccount(); user2.GrantAccess(); var paymentRequestController = user.GetController <PaymentRequestController>(); var guestpaymentRequestController = user2.GetController <PaymentRequestController>(); var request = new UpdatePaymentRequestViewModel() { Title = "original juice", Currency = "BTC", Amount = 1, StoreId = user.StoreId, Description = "description" }; var id = (Assert .IsType <RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(null, request)) .RouteValues.Values.First().ToString()); //permission guard for guests editing Assert .IsType <NotFoundResult>(await guestpaymentRequestController.EditPaymentRequest(id)); request.Title = "update"; Assert.IsType <RedirectToActionResult>(await paymentRequestController.EditPaymentRequest(id, request)); Assert.Equal(request.Title, Assert.IsType <ViewPaymentRequestViewModel>(Assert .IsType <ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Title); Assert.False(string.IsNullOrEmpty(id)); Assert.IsType <ViewPaymentRequestViewModel>(Assert .IsType <ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model); //Archive Assert .IsType <RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id)); Assert.True(Assert .IsType <ViewPaymentRequestViewModel>(Assert .IsType <ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived); Assert.Empty(Assert .IsType <ListPaymentRequestsViewModel>(Assert .IsType <ViewResult>(await paymentRequestController.GetPaymentRequests()).Model).Items); //unarchive Assert .IsType <RedirectToActionResult>(await paymentRequestController.TogglePaymentRequestArchival(id)); Assert.False(Assert .IsType <ViewPaymentRequestViewModel>(Assert .IsType <ViewResult>(await paymentRequestController.ViewPaymentRequest(id)).Model).Archived); Assert.Single(Assert .IsType <ListPaymentRequestsViewModel>(Assert .IsType <ViewResult>(await paymentRequestController.GetPaymentRequests()).Model).Items); } }
public async Task CanCancelPaymentWhenPossible() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); var paymentRequestController = user.GetController <PaymentRequestController>(); Assert.IsType <NotFoundResult>(await paymentRequestController.CancelUnpaidPendingInvoice(Guid.NewGuid().ToString(), false)); var request = new UpdatePaymentRequestViewModel() { Title = "original juice", Currency = "BTC", Amount = 1, StoreId = user.StoreId, Description = "description" }; var response = Assert .IsType <RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result) .RouteValues.First(); var invoiceId = response.Value.ToString(); await paymentRequestController.PayPaymentRequest(invoiceId, false); Assert.IsType <BadRequestObjectResult>(await paymentRequestController.CancelUnpaidPendingInvoice(invoiceId, false)); request.AllowCustomPaymentAmounts = true; response = Assert .IsType <RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result) .RouteValues.First(); var paymentRequestId = response.Value.ToString(); invoiceId = Assert .IsType <OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false)) .Value .ToString(); var actionResult = Assert .IsType <RedirectToActionResult>( await paymentRequestController.PayPaymentRequest(response.Value.ToString())); Assert.Equal("Checkout", actionResult.ActionName); Assert.Equal("Invoice", actionResult.ControllerName); Assert.Contains(actionResult.RouteValues, pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId); var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant); Assert.Equal(InvoiceState.ToString(InvoiceStatusLegacy.New), invoice.Status); Assert.IsType <OkObjectResult>(await paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false)); invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant); Assert.Equal(InvoiceState.ToString(InvoiceStatusLegacy.Invalid), invoice.Status); Assert.IsType <BadRequestObjectResult>(await paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId, false)); invoiceId = Assert .IsType <OkObjectResult>(await paymentRequestController.PayPaymentRequest(paymentRequestId, false)) .Value .ToString(); invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant); //a hack to generate invoices for the payment request is to manually create an invoice with an order id that matches: user.BitPay.CreateInvoice(new Invoice(1, "USD") { OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(paymentRequestId) }); //shouldn't crash await paymentRequestController.ViewPaymentRequest(paymentRequestId); await paymentRequestController.CancelUnpaidPendingInvoice(paymentRequestId); } }
public async Task CanPayPaymentRequestWhenPossible() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); var paymentRequestController = user.GetController <PaymentRequestController>(); Assert.IsType <NotFoundResult>( await paymentRequestController.PayPaymentRequest(Guid.NewGuid().ToString())); var request = new UpdatePaymentRequestViewModel() { Title = "original juice", Currency = "BTC", Amount = 1, StoreId = user.StoreId, Description = "description" }; var response = Assert .IsType <RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result) .RouteValues.First(); var invoiceId = Assert .IsType <OkObjectResult>( await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false)).Value .ToString(); var actionResult = Assert .IsType <RedirectToActionResult>( await paymentRequestController.PayPaymentRequest(response.Value.ToString())); Assert.Equal("Checkout", actionResult.ActionName); Assert.Equal("Invoice", actionResult.ControllerName); Assert.Contains(actionResult.RouteValues, pair => pair.Key == "Id" && pair.Value.ToString() == invoiceId); var invoice = user.BitPay.GetInvoice(invoiceId, Facade.Merchant); Assert.Equal(1, invoice.Price); request = new UpdatePaymentRequestViewModel() { Title = "original juice with expiry", Currency = "BTC", Amount = 1, ExpiryDate = DateTime.Today.Subtract(TimeSpan.FromDays(2)), StoreId = user.StoreId, Description = "description" }; response = Assert .IsType <RedirectToActionResult>(paymentRequestController.EditPaymentRequest(null, request).Result) .RouteValues.First(); Assert .IsType <BadRequestObjectResult>( await paymentRequestController.PayPaymentRequest(response.Value.ToString(), false)); } }
public async Task PaymentControllerTests() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(); var client = await user.CreateClient(Policies.Unrestricted); var viewOnly = await user.CreateClient(Policies.CanViewPaymentRequests); //create payment request //validation errors await AssertHttpError(400, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A" }); }); await AssertHttpError(400, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "BTC", Amount = 0 }); }); await AssertHttpError(400, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "helloinvalid", Amount = 1 }); }); await AssertHttpError(403, async() => { await viewOnly.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "helloinvalid", Amount = 1 }); }); var newPaymentRequest = await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "USD", Amount = 1 }); //list payment request var paymentRequests = await viewOnly.GetPaymentRequests(user.StoreId); Assert.NotNull(paymentRequests); Assert.Single(paymentRequests); Assert.Equal(newPaymentRequest.Id, paymentRequests.First().Id); //get payment request var paymentRequest = await viewOnly.GetPaymentRequest(user.StoreId, newPaymentRequest.Id); Assert.Equal(newPaymentRequest.Title, paymentRequest.Title); //update payment request var updateRequest = JObject.FromObject(paymentRequest).ToObject <UpdatePaymentRequestRequest>(); updateRequest.Title = "B"; await AssertHttpError(403, async() => { await viewOnly.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest); }); await client.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest); paymentRequest = await client.GetPaymentRequest(user.StoreId, newPaymentRequest.Id); Assert.Equal(updateRequest.Title, paymentRequest.Title); //archive payment request await AssertHttpError(403, async() => { await viewOnly.ArchivePaymentRequest(user.StoreId, paymentRequest.Id); }); await client.ArchivePaymentRequest(user.StoreId, paymentRequest.Id); Assert.DoesNotContain(paymentRequest.Id, (await client.GetPaymentRequests(user.StoreId)).Select(data => data.Id)); //let's test some payment stuff await user.RegisterDerivationSchemeAsync("BTC"); var paymentTestPaymentRequest = await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Amount = 0.1m, Currency = "BTC", Title = "Payment test title" }); var invoiceId = Assert.IsType <string>(Assert.IsType <OkObjectResult>(await user.GetController <PaymentRequestController>() .PayPaymentRequest(paymentTestPaymentRequest.Id, false)).Value); var invoice = user.BitPay.GetInvoice(invoiceId); await tester.WaitForEvent <InvoiceDataChangedEvent>(async() => { await tester.ExplorerNode.SendToAddressAsync( BitcoinAddress.Create(invoice.BitcoinAddress, tester.ExplorerNode.Network), invoice.BtcDue); }); await TestUtils.EventuallyAsync(async() => { Assert.Equal(Invoice.STATUS_PAID, user.BitPay.GetInvoice(invoiceId).Status); Assert.Equal(PaymentRequestData.PaymentRequestStatus.Completed, (await client.GetPaymentRequest(user.StoreId, paymentTestPaymentRequest.Id)).Status); }); } }
public async Task StoresControllerTests() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(); var client = await user.CreateClient(Policies.Unrestricted); //create store var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" }); //update store var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B" }); Assert.Equal("B", updatedStore.Name); Assert.Equal("B", (await client.GetStore(newStore.Id)).Name); //list stores var stores = await client.GetStores(); var storeIds = stores.Select(data => data.Id); var storeNames = stores.Select(data => data.Name); Assert.NotNull(stores); Assert.Equal(2, stores.Count()); Assert.Contains(newStore.Id, storeIds); Assert.Contains(user.StoreId, storeIds); //get store var store = await client.GetStore(user.StoreId); Assert.Equal(user.StoreId, store.Id); Assert.Contains(store.Name, storeNames); //remove store await client.RemoveStore(newStore.Id); await AssertHttpError(403, async() => { await client.GetStore(newStore.Id); }); Assert.Single(await client.GetStores()); newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" }); var scopedClient = await user.CreateClient(Permission.Create(Policies.CanViewStoreSettings, user.StoreId).ToString()); Assert.Single(await scopedClient.GetStores()); // We strip the user's Owner right, so the key should not work using var ctx = tester.PayTester.GetService <Data.ApplicationDbContextFactory>().CreateContext(); var storeEntity = await ctx.UserStore.SingleAsync(u => u.ApplicationUserId == user.UserId && u.StoreDataId == newStore.Id); storeEntity.Role = "Guest"; await ctx.SaveChangesAsync(); await AssertHttpError(403, async() => await client.UpdateStore(newStore.Id, new UpdateStoreRequest() { Name = "B" })); } }
public async Task UsersControllerTests() { using (var tester = ServerTester.Create(newDb: true)) { tester.PayTester.DisableRegistration = true; await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(); var clientProfile = await user.CreateClient(Policies.CanModifyProfile); var clientServer = await user.CreateClient(Policies.CanCreateUser, Policies.CanViewProfile); var clientInsufficient = await user.CreateClient(Policies.CanModifyStoreSettings); var clientBasic = await user.CreateClient(); var apiKeyProfileUserData = await clientProfile.GetCurrentUser(); Assert.NotNull(apiKeyProfileUserData); Assert.Equal(apiKeyProfileUserData.Id, user.UserId); Assert.Equal(apiKeyProfileUserData.Email, user.RegisterDetails.Email); await Assert.ThrowsAsync <HttpRequestException>(async() => await clientInsufficient.GetCurrentUser()); await clientServer.GetCurrentUser(); await clientProfile.GetCurrentUser(); await clientBasic.GetCurrentUser(); await Assert.ThrowsAsync <HttpRequestException>(async() => await clientInsufficient.CreateUser(new CreateApplicationUserRequest() { Email = $"{Guid.NewGuid()}@g.com", Password = Guid.NewGuid().ToString() })); var newUser = await clientServer.CreateUser(new CreateApplicationUserRequest() { Email = $"{Guid.NewGuid()}@g.com", Password = Guid.NewGuid().ToString() }); Assert.NotNull(newUser); var newUser2 = await clientBasic.CreateUser(new CreateApplicationUserRequest() { Email = $"{Guid.NewGuid()}@g.com", Password = Guid.NewGuid().ToString() }); Assert.NotNull(newUser2); await Assert.ThrowsAsync <HttpRequestException>(async() => await clientServer.CreateUser(new CreateApplicationUserRequest() { Email = $"{Guid.NewGuid()}", Password = Guid.NewGuid().ToString() })); await Assert.ThrowsAsync <HttpRequestException>(async() => await clientServer.CreateUser(new CreateApplicationUserRequest() { Email = $"{Guid.NewGuid()}@g.com", })); await Assert.ThrowsAsync <HttpRequestException>(async() => await clientServer.CreateUser(new CreateApplicationUserRequest() { Password = Guid.NewGuid().ToString() })); } }
public void CanComputeCrowdfundModel() { using (var tester = ServerTester.Create()) { tester.Start(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); user.ModifyStore(s => s.NetworkFeeMode = NetworkFeeMode.Never); var apps = user.GetController <AppsController>(); var vm = Assert.IsType <CreateAppViewModel>(Assert.IsType <ViewResult>(apps.CreateApp().Result).Model); vm.Name = "test"; vm.SelectedAppType = AppType.Crowdfund.ToString(); Assert.IsType <RedirectToActionResult>(apps.CreateApp(vm).Result); var appId = Assert.IsType <ListAppsViewModel>(Assert.IsType <ViewResult>(apps.ListApps().Result).Model) .Apps[0].Id; Logs.Tester.LogInformation("We create an invoice with a hardcap"); var crowdfundViewModel = Assert.IsType <UpdateCrowdfundViewModel>(Assert .IsType <ViewResult>(apps.UpdateCrowdfund(appId).Result).Model); crowdfundViewModel.Enabled = true; crowdfundViewModel.EndDate = null; crowdfundViewModel.TargetAmount = 100; crowdfundViewModel.TargetCurrency = "BTC"; crowdfundViewModel.UseAllStoreInvoices = true; crowdfundViewModel.EnforceTargetAmount = true; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); var anonAppPubsController = tester.PayTester.GetController <AppsPublicController>(); var publicApps = user.GetController <AppsPublicController>(); var model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(crowdfundViewModel.TargetAmount, model.TargetAmount); Assert.Equal(crowdfundViewModel.EndDate, model.EndDate); Assert.Equal(crowdfundViewModel.StartDate, model.StartDate); Assert.Equal(crowdfundViewModel.TargetCurrency, model.TargetCurrency); Assert.Equal(0m, model.Info.CurrentAmount); Assert.Equal(0m, model.Info.CurrentPendingAmount); Assert.Equal(0m, model.Info.ProgressPercentage); Logs.Tester.LogInformation("Unpaid invoices should show as pending contribution because it is hardcap"); Logs.Tester.LogInformation("Because UseAllStoreInvoices is true, we can manually create an invoice and it should show as contribution"); var invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "*****@*****.**" }, Price = 1m, Currency = "BTC", PosData = "posData", ItemDesc = "Some description", TransactionSpeed = "high", FullNotifications = true }, Facade.Merchant); model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(0m, model.Info.CurrentAmount); Assert.Equal(1m, model.Info.CurrentPendingAmount); Assert.Equal(0m, model.Info.ProgressPercentage); Assert.Equal(1m, model.Info.PendingProgressPercentage); Logs.Tester.LogInformation("Let's check current amount change once payment is confirmed"); var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network); tester.ExplorerNode.SendToAddress(invoiceAddress, invoice.BtcDue); tester.ExplorerNode.Generate(1); // By default invoice confirmed at 1 block TestUtils.Eventually(() => { model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(1m, model.Info.CurrentAmount); Assert.Equal(0m, model.Info.CurrentPendingAmount); }); Logs.Tester.LogInformation("Because UseAllStoreInvoices is true, let's make sure the invoice is tagged"); var invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult(); Assert.True(invoiceEntity.Version >= InvoiceEntity.InternalTagSupport_Version); Assert.Contains(AppService.GetAppInternalTag(appId), invoiceEntity.InternalTags); crowdfundViewModel.UseAllStoreInvoices = false; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); Logs.Tester.LogInformation("Because UseAllStoreInvoices is false, let's make sure the invoice is not tagged"); invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "*****@*****.**" }, Price = 1m, Currency = "BTC", PosData = "posData", ItemDesc = "Some description", TransactionSpeed = "high", FullNotifications = true }, Facade.Merchant); invoiceEntity = tester.PayTester.InvoiceRepository.GetInvoice(invoice.Id).GetAwaiter().GetResult(); Assert.DoesNotContain(AppService.GetAppInternalTag(appId), invoiceEntity.InternalTags); Logs.Tester.LogInformation("After turning setting a softcap, let's check that only actual payments are counted"); crowdfundViewModel.EnforceTargetAmount = false; crowdfundViewModel.UseAllStoreInvoices = true; Assert.IsType <RedirectToActionResult>(apps.UpdateCrowdfund(appId, crowdfundViewModel).Result); invoice = user.BitPay.CreateInvoice(new Invoice() { Buyer = new Buyer() { email = "*****@*****.**" }, Price = 1m, Currency = "BTC", PosData = "posData", ItemDesc = "Some description", TransactionSpeed = "high", FullNotifications = true }, Facade.Merchant); Assert.Equal(0m, model.Info.CurrentPendingAmount); invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, tester.ExplorerNode.Network); tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(0.5m)); tester.ExplorerNode.SendToAddress(invoiceAddress, Money.Coins(0.2m)); TestUtils.Eventually(() => { model = Assert.IsType <ViewCrowdfundViewModel>(Assert .IsType <ViewResult>(publicApps.ViewCrowdfund(appId, String.Empty).Result).Model); Assert.Equal(0.7m, model.Info.CurrentPendingAmount); }); } }
public async Task CanPlayWithPSBT() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); var invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 10, Currency = "USD", PosData = "posData", OrderId = "orderId", ItemDesc = "Some \", description", FullNotifications = true }, Facade.Merchant); var cashCow = tester.ExplorerNode; var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network); cashCow.SendToAddress(invoiceAddress, Money.Coins(1.5m)); TestUtils.Eventually(() => { invoice = user.BitPay.GetInvoice(invoice.Id); Assert.Equal("paid", invoice.Status); }); var walletController = user.GetController <WalletsController>(); var walletId = new WalletId(user.StoreId, "BTC"); var sendDestination = new Key().PubKey.Hash.GetAddress(user.SupportedNetwork.NBitcoinNetwork).ToString(); var sendModel = new WalletSendModel() { Outputs = new List <WalletSendModel.TransactionOutput>() { new WalletSendModel.TransactionOutput() { DestinationAddress = sendDestination, Amount = 0.1m, } }, FeeSatoshiPerByte = 1, CurrentBalance = 1.5m }; string redirectedPSBT = AssertRedirectedPSBT(await walletController.WalletSend(walletId, sendModel, command: "analyze-psbt"), nameof(walletController.WalletPSBT)); 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); var filePSBT = (FileContentResult)(await walletController.WalletPSBT(walletId, vmPSBT, "save-psbt")); PSBT.Load(filePSBT.FileContents, user.SupportedNetwork.NBitcoinNetwork); var vmPSBT2 = await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel() { SigningContext = new SigningContextModel() { PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady)) } }).AssertViewModelAsync <WalletPSBTReadyViewModel>(); Assert.NotEmpty(vmPSBT2.Inputs.Where(i => i.Error != null)); Assert.Equal(vmPSBT.PSBT, vmPSBT2.SigningContext.PSBT); var signedPSBT = unsignedPSBT.Clone(); signedPSBT.SignAll(user.DerivationScheme, user.GenerateWalletResponseV.AccountHDKey, user.GenerateWalletResponseV.AccountKeyPath); vmPSBT.PSBT = signedPSBT.ToBase64(); var psbtReady = await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel { SigningContext = new SigningContextModel { PSBT = AssertRedirectedPSBT(await walletController.WalletPSBT(walletId, vmPSBT, "broadcast"), nameof(walletController.WalletPSBTReady)) } }).AssertViewModelAsync <WalletPSBTReadyViewModel>(); 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); vmPSBT.PSBT = unsignedPSBT.ToBase64(); var combineVM = await walletController.WalletPSBT(walletId, vmPSBT, "combine").AssertViewModelAsync <WalletPSBTCombineViewModel>(); Assert.Equal(vmPSBT.PSBT, combineVM.OtherPSBT); combineVM.PSBT = signedPSBT.ToBase64(); var psbt = AssertRedirectedPSBT(await walletController.WalletPSBTCombine(walletId, combineVM), nameof(walletController.WalletPSBT)); var signedPSBT2 = PSBT.Parse(psbt, user.SupportedNetwork.NBitcoinNetwork); Assert.True(signedPSBT.TryFinalize(out _)); Assert.True(signedPSBT2.TryFinalize(out _)); Assert.Equal(signedPSBT, signedPSBT2); // Can use uploaded file? combineVM.PSBT = null; combineVM.UploadedPSBTFile = TestUtils.GetFormFile("signedPSBT", signedPSBT.ToBytes()); psbt = AssertRedirectedPSBT(await walletController.WalletPSBTCombine(walletId, combineVM), nameof(walletController.WalletPSBT)); signedPSBT2 = PSBT.Parse(psbt, user.SupportedNetwork.NBitcoinNetwork); Assert.True(signedPSBT.TryFinalize(out _)); Assert.True(signedPSBT2.TryFinalize(out _)); Assert.Equal(signedPSBT, signedPSBT2); var ready = (await walletController.WalletPSBTReady(walletId, new WalletPSBTReadyViewModel { SigningContext = new SigningContextModel(signedPSBT) })).AssertViewModel <WalletPSBTReadyViewModel>(); Assert.Equal(signedPSBT.ToBase64(), ready.SigningContext.PSBT); psbt = AssertRedirectedPSBT(await walletController.WalletPSBTReady(walletId, ready, command: "analyze-psbt"), nameof(walletController.WalletPSBT)); Assert.Equal(signedPSBT.ToBase64(), psbt); var redirect = Assert.IsType <RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "broadcast")); Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName); //test base64 psbt file Assert.False(string.IsNullOrEmpty(Assert.IsType <WalletPSBTViewModel>( Assert.IsType <ViewResult>( await walletController.WalletPSBT(walletId, new WalletPSBTViewModel() { UploadedPSBTFile = TestUtils.GetFormFile("base64", signedPSBT.ToBase64()) })).Model).PSBT)); } }
public async Task ElementsAssetsAreHandledCorrectly() { using (var tester = ServerTester.Create()) { tester.ActivateLBTC(); await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("LBTC"); user.RegisterDerivationScheme("USDT"); user.RegisterDerivationScheme("ETB"); await tester.LBTCExplorerNode.GenerateAsync(4); //no tether on our regtest, lets create it and set it var tether = tester.NetworkProvider.GetNetwork <ElementsBTCPayNetwork>("USDT"); var lbtc = tester.NetworkProvider.GetNetwork <ElementsBTCPayNetwork>("LBTC"); var etb = tester.NetworkProvider.GetNetwork <ElementsBTCPayNetwork>("ETB"); var issueAssetResult = await tester.LBTCExplorerNode.SendCommandAsync("issueasset", 100000, 0); tether.AssetId = uint256.Parse(issueAssetResult.Result["asset"].ToString()); ((ElementsBTCPayNetwork)tester.PayTester.GetService <BTCPayWalletProvider>().GetWallet("USDT").Network) .AssetId = tether.AssetId; Assert.Equal(tether.AssetId, tester.NetworkProvider.GetNetwork <ElementsBTCPayNetwork>("USDT").AssetId); Assert.Equal(tether.AssetId, ((ElementsBTCPayNetwork)tester.PayTester.GetService <BTCPayWalletProvider>().GetWallet("USDT").Network).AssetId); var issueAssetResult2 = await tester.LBTCExplorerNode.SendCommandAsync("issueasset", 100000, 0); etb.AssetId = uint256.Parse(issueAssetResult2.Result["asset"].ToString()); ((ElementsBTCPayNetwork)tester.PayTester.GetService <BTCPayWalletProvider>().GetWallet("ETB").Network) .AssetId = etb.AssetId; //test: register 2 assets on the same elements network and make sure paying an invoice on one does not affect the other in any way var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(0.1m, "BTC")); Assert.Equal(3, invoice.SupportedTransactionCurrencies.Count); var ci = invoice.CryptoInfo.Single(info => info.CryptoCode.Equals("LBTC")); //1 lbtc = 1 btc Assert.Equal(1, ci.Rate); var star = await tester.LBTCExplorerNode.SendCommandAsync("sendtoaddress", ci.Address, ci.Due, "", "", false, true, 1, "UNSET", lbtc.AssetId); TestUtils.Eventually(() => { var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal("paid", localInvoice.Status); Assert.Single(localInvoice.CryptoInfo.Single(info => info.CryptoCode.Equals("LBTC")).Payments); }); invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(0.1m, "BTC")); ci = invoice.CryptoInfo.Single(info => info.CryptoCode.Equals("USDT")); Assert.Equal(3, invoice.SupportedTransactionCurrencies.Count); star = await tester.LBTCExplorerNode.SendCommandAsync("sendtoaddress", ci.Address, ci.Due, "", "", false, true, 1, "UNSET", tether.AssetId); TestUtils.Eventually(() => { var localInvoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal("paid", localInvoice.Status); Assert.Single(localInvoice.CryptoInfo.Single(info => info.CryptoCode.Equals("USDT", StringComparison.InvariantCultureIgnoreCase)).Payments); }); //test precision based on https://github.com/ElementsProject/elements/issues/805#issuecomment-601277606 var etbBip21 = new BitcoinUrlBuilder(invoice.CryptoInfo.Single(info => info.CryptoCode == "ETB").PaymentUrls.BIP21.Replace(etb.UriScheme, "bitcoin"), etb.NBitcoinNetwork); //precision = 2, 1ETB = 0.00000100 Assert.Equal(100, etbBip21.Amount.Satoshi); var lbtcBip21 = new BitcoinUrlBuilder(invoice.CryptoInfo.Single(info => info.CryptoCode == "LBTC").PaymentUrls.BIP21.Replace(lbtc.UriScheme, "bitcoin"), lbtc.NBitcoinNetwork); //precision = 8, 0.1 = 0.1 Assert.Equal(0.1m, lbtcBip21.Amount.ToDecimal(MoneyUnit.BTC)); } }
public async Task CanOnlyUseCorrectAddressFormatsForPayjoin() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var broadcaster = tester.PayTester.GetService <DelayedTransactionBroadcaster>(); var payjoinRepository = tester.PayTester.GetService <PayJoinRepository>(); broadcaster.Disable(); var network = tester.NetworkProvider.GetNetwork <BTCPayNetwork>("BTC"); var btcPayWallet = tester.PayTester.GetService <BTCPayWalletProvider>().GetWallet(network); var cashCow = tester.ExplorerNode; cashCow.Generate(2); // get some money in case var unsupportedFormats = Enum.GetValues(typeof(ScriptPubKeyType)) .AssertType <ScriptPubKeyType[]>() .Where(type => !PayjoinClient.SupportedFormats.Contains(type)); foreach (ScriptPubKeyType senderAddressType in Enum.GetValues(typeof(ScriptPubKeyType))) { var senderUser = tester.NewAccount(); senderUser.GrantAccess(true); senderUser.RegisterDerivationScheme("BTC", senderAddressType); foreach (ScriptPubKeyType receiverAddressType in Enum.GetValues(typeof(ScriptPubKeyType))) { var senderCoin = await senderUser.ReceiveUTXO(Money.Satoshis(100000), network); Logs.Tester.LogInformation($"Testing payjoin with sender: {senderAddressType} receiver: {receiverAddressType}"); var receiverUser = tester.NewAccount(); receiverUser.GrantAccess(true); receiverUser.RegisterDerivationScheme("BTC", receiverAddressType, true); await receiverUser.EnablePayJoin(); var receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network); var clientShouldError = unsupportedFormats.Contains(senderAddressType); string errorCode = null; if (unsupportedFormats.Contains(receiverAddressType)) { errorCode = "unsupported-inputs"; } else if (receiverAddressType != senderAddressType) { errorCode = "out-of-utxos"; } var invoice = receiverUser.BitPay.CreateInvoice(new Invoice() { Price = 50000, Currency = "sats", FullNotifications = true }); var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network); var txBuilder = network.NBitcoinNetwork.CreateTransactionBuilder(); txBuilder.AddCoins(senderCoin); txBuilder.Send(invoiceAddress, invoice.BtcDue); txBuilder.SetChange(await senderUser.GetNewAddress(network)); txBuilder.SendEstimatedFees(new FeeRate(50m)); var psbt = txBuilder.BuildPSBT(false); psbt = await senderUser.Sign(psbt); var pj = await senderUser.SubmitPayjoin(invoice, psbt, errorCode, clientShouldError); } } } }
public async Task CanPlayWithPSBT() { using (var tester = ServerTester.Create()) { tester.Start(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); var invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 10, Currency = "USD", PosData = "posData", OrderId = "orderId", ItemDesc = "Some \", description", FullNotifications = true }, Facade.Merchant); var cashCow = tester.ExplorerNode; var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network); cashCow.SendToAddress(invoiceAddress, Money.Coins(1.5m)); TestUtils.Eventually(() => { invoice = user.BitPay.GetInvoice(invoice.Id); Assert.Equal("paid", invoice.Status); }); var walletController = tester.PayTester.GetController <WalletsController>(user.UserId); var walletId = new WalletId(user.StoreId, "BTC"); var sendDestination = new Key().PubKey.Hash.GetAddress(user.SupportedNetwork.NBitcoinNetwork).ToString(); var sendModel = new WalletSendModel() { Destination = sendDestination, Amount = 0.1m, FeeSatoshiPerByte = 1, CurrentBalance = 1.5m }; var vmLedger = await walletController.WalletSend(walletId, sendModel, command : "ledger").AssertViewModelAsync <WalletSendLedgerModel>(); PSBT.Parse(vmLedger.PSBT, user.SupportedNetwork.NBitcoinNetwork); BitcoinAddress.Create(vmLedger.HintChange, user.SupportedNetwork.NBitcoinNetwork); Assert.NotNull(vmLedger.SuccessPath); 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 unsignedPSBT = PSBT.Parse(vmPSBT.PSBT, user.SupportedNetwork.NBitcoinNetwork); Assert.NotNull(vmPSBT.Decoded); var filePSBT = (FileContentResult)(await walletController.WalletPSBT(walletId, vmPSBT, "save-psbt")); 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); Assert.Equal(vmPSBT.PSBT, vmPSBT2.PSBT); var signedPSBT = unsignedPSBT.Clone(); signedPSBT.SignAll(user.ExtKey); vmPSBT.PSBT = signedPSBT.ToBase64(); var psbtReady = await walletController.WalletPSBT(walletId, vmPSBT, "broadcast").AssertViewModelAsync <WalletPSBTReadyViewModel>(); Assert.Equal(2, psbtReady.Destinations.Count); 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")); Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName); vmPSBT.PSBT = unsignedPSBT.ToBase64(); var combineVM = await walletController.WalletPSBT(walletId, vmPSBT, "combine").AssertViewModelAsync <WalletPSBTCombineViewModel>(); Assert.Equal(vmPSBT.PSBT, combineVM.OtherPSBT); combineVM.PSBT = signedPSBT.ToBase64(); vmPSBT = await walletController.WalletPSBTCombine(walletId, combineVM).AssertViewModelAsync <WalletPSBTViewModel>(); var signedPSBT2 = PSBT.Parse(vmPSBT.PSBT, user.SupportedNetwork.NBitcoinNetwork); Assert.True(signedPSBT.TryFinalize(out _)); Assert.True(signedPSBT2.TryFinalize(out _)); Assert.Equal(signedPSBT, signedPSBT2); // Can use uploaded file? combineVM.PSBT = null; combineVM.UploadedPSBTFile = TestUtils.GetFormFile("signedPSBT", signedPSBT.ToBytes()); vmPSBT = await walletController.WalletPSBTCombine(walletId, combineVM).AssertViewModelAsync <WalletPSBTViewModel>(); signedPSBT2 = PSBT.Parse(vmPSBT.PSBT, user.SupportedNetwork.NBitcoinNetwork); Assert.True(signedPSBT.TryFinalize(out _)); Assert.True(signedPSBT2.TryFinalize(out _)); Assert.Equal(signedPSBT, signedPSBT2); var ready = (await walletController.WalletPSBTReady(walletId, signedPSBT.ToBase64())).AssertViewModel <WalletPSBTReadyViewModel>(); Assert.Equal(signedPSBT.ToBase64(), ready.PSBT); redirect = Assert.IsType <RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "analyze-psbt")); Assert.Equal(signedPSBT.ToBase64(), (string)redirect.RouteValues["psbt"]); redirect = Assert.IsType <RedirectToActionResult>(await walletController.WalletPSBTReady(walletId, ready, command: "broadcast")); Assert.Equal(nameof(walletController.WalletTransactions), redirect.ActionName); } }
public async Task CanUsePullPaymentViaAPI() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var acc = tester.NewAccount(); acc.Register(); acc.CreateStore(); var storeId = (await acc.RegisterDerivationSchemeAsync("BTC", importKeysToNBX: true)).StoreId; var client = await acc.CreateClient(); var result = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest() { Name = "Test", Amount = 12.3m, Currency = "BTC", PaymentMethods = new[] { "BTC" } }); void VerifyResult() { Assert.Equal("Test", result.Name); Assert.Null(result.Period); // If it contains ? it means that we are resolving an unknown route with the link generator Assert.DoesNotContain("?", result.ViewLink); Assert.False(result.Archived); Assert.Equal("BTC", result.Currency); Assert.Equal(12.3m, result.Amount); } VerifyResult(); var unauthenticated = new BTCPayServerClient(tester.PayTester.ServerUri); result = await unauthenticated.GetPullPayment(result.Id); VerifyResult(); await AssertHttpError(404, async() => await unauthenticated.GetPullPayment("lol")); // Can't list pull payments unauthenticated await AssertHttpError(401, async() => await unauthenticated.GetPullPayments(storeId)); var pullPayments = await client.GetPullPayments(storeId); result = Assert.Single(pullPayments); VerifyResult(); Thread.Sleep(1000); var test2 = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest() { Name = "Test 2", Amount = 12.3m, Currency = "BTC", PaymentMethods = new[] { "BTC" } }); Logs.Tester.LogInformation("Can't archive without knowing the walletId"); await Assert.ThrowsAsync <HttpRequestException>(async() => await client.ArchivePullPayment("lol", result.Id)); Logs.Tester.LogInformation("Can't archive without permission"); await Assert.ThrowsAsync <HttpRequestException>(async() => await unauthenticated.ArchivePullPayment(storeId, result.Id)); await client.ArchivePullPayment(storeId, result.Id); result = await unauthenticated.GetPullPayment(result.Id); Assert.True(result.Archived); var pps = await client.GetPullPayments(storeId); result = Assert.Single(pps); Assert.Equal("Test 2", result.Name); pps = await client.GetPullPayments(storeId, true); Assert.Equal(2, pps.Length); Assert.Equal("Test 2", pps[0].Name); Assert.Equal("Test", pps[1].Name); var payouts = await unauthenticated.GetPayouts(pps[0].Id); Assert.Empty(payouts); var destination = (await tester.ExplorerNode.GetNewAddressAsync()).ToString(); await this.AssertAPIError("overdraft", async() => await unauthenticated.CreatePayout(pps[0].Id, new CreatePayoutRequest() { Destination = destination, Amount = 1_000_000m, PaymentMethod = "BTC", }));
public async Task CanUseBIP79FeeCornerCase() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var broadcaster = tester.PayTester.GetService <DelayedTransactionBroadcaster>(); var payjoinRepository = tester.PayTester.GetService <PayJoinRepository>(); broadcaster.Disable(); var network = tester.NetworkProvider.GetNetwork <BTCPayNetwork>("BTC"); var btcPayWallet = tester.PayTester.GetService <BTCPayWalletProvider>().GetWallet(network); var cashCow = tester.ExplorerNode; cashCow.Generate(2); // get some money in case var senderUser = tester.NewAccount(); senderUser.GrantAccess(true); senderUser.RegisterDerivationScheme("BTC", true); var receiverUser = tester.NewAccount(); receiverUser.GrantAccess(true); receiverUser.RegisterDerivationScheme("BTC", true, true); await receiverUser.EnablePayJoin(); var receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network); string lastInvoiceId = null; var vector = (SpentCoin : Money.Satoshis(810), InvoiceAmount : Money.Satoshis(700), Paid : Money.Satoshis(700), Fee : Money.Satoshis(110), ExpectLocked : false, ExpectedError : "not-enough-money"); async Task <PSBT> RunVector() { var coin = await senderUser.ReceiveUTXO(vector.SpentCoin, network); var invoice = receiverUser.BitPay.CreateInvoice(new Invoice() { Price = vector.InvoiceAmount.ToDecimal(MoneyUnit.BTC), Currency = "BTC", FullNotifications = true }); lastInvoiceId = invoice.Id; var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network); var txBuilder = network.NBitcoinNetwork.CreateTransactionBuilder(); txBuilder.AddCoins(coin); txBuilder.Send(invoiceAddress, vector.Paid); txBuilder.SendFees(vector.Fee); txBuilder.SetChange(await senderUser.GetNewAddress(network)); var psbt = txBuilder.BuildPSBT(false); psbt = await senderUser.Sign(psbt); var pj = await senderUser.SubmitPayjoin(invoice, psbt, vector.ExpectedError); if (vector.ExpectLocked) { Assert.True(await payjoinRepository.TryUnlock(receiverCoin.Outpoint)); } else { Assert.False(await payjoinRepository.TryUnlock(receiverCoin.Outpoint)); } return(pj); } async Task LockNewReceiverCoin() { var coins = await btcPayWallet.GetUnspentCoins(receiverUser.DerivationScheme); foreach (var coin in coins.Where(c => c.OutPoint != receiverCoin.Outpoint)) { await payjoinRepository.TryLock(coin.OutPoint); } } Logs.Tester.LogInformation("Here we send exactly the right amount. This should fails as\n" + "there is not enough to pay the additional payjoin input. (going below the min relay fee" + "However, the original tx has been broadcasted!"); vector = (SpentCoin : Money.Satoshis(810), InvoiceAmount : Money.Satoshis(700), Paid : Money.Satoshis(700), Fee : Money.Satoshis(110), ExpectLocked : false, ExpectedError : "not-enough-money"); await RunVector(); await LockNewReceiverCoin(); Logs.Tester.LogInformation("We don't pay enough"); vector = (SpentCoin : Money.Satoshis(810), InvoiceAmount : Money.Satoshis(700), Paid : Money.Satoshis(690), Fee : Money.Satoshis(110), ExpectLocked : false, ExpectedError : "invoice-not-fully-paid"); await RunVector(); Logs.Tester.LogInformation("We pay correctly"); vector = (SpentCoin : Money.Satoshis(810), InvoiceAmount : Money.Satoshis(500), Paid : Money.Satoshis(500), Fee : Money.Satoshis(110), ExpectLocked : true, ExpectedError : null as string); await RunVector(); Logs.Tester.LogInformation("We pay correctly, but no utxo\n" + "However, this has the side effect of having the receiver broadcasting the original tx"); await payjoinRepository.TryLock(receiverCoin.Outpoint); vector = (SpentCoin : Money.Satoshis(810), InvoiceAmount : Money.Satoshis(500), Paid : Money.Satoshis(500), Fee : Money.Satoshis(110), ExpectLocked : true, ExpectedError : "out-of-utxos"); await RunVector(); await LockNewReceiverCoin(); var originalSenderUser = senderUser; retry: // Additional fee is 96 , minrelaytx is 294 // We pay correctly, fees partially taken from what is overpaid // We paid 510, the receiver pay 10 sat // The send pay remaining 86 sat from his pocket // So total paid by sender should be 86 + 510 + 200 so we should get 1090 - (86 + 510 + 200) == 294 back) Logs.Tester.LogInformation($"Check if we can take fee on overpaid utxo{(senderUser == receiverUser ? " (to self)" : "")}"); vector = (SpentCoin : Money.Satoshis(1090), InvoiceAmount : Money.Satoshis(500), Paid : Money.Satoshis(510), Fee : Money.Satoshis(200), ExpectLocked : true, ExpectedError : null as string); var proposedPSBT = await RunVector(); Assert.Equal(2, proposedPSBT.Outputs.Count); Assert.Contains(proposedPSBT.Outputs, o => o.Value == Money.Satoshis(500) + receiverCoin.Amount); Assert.Contains(proposedPSBT.Outputs, o => o.Value == Money.Satoshis(294)); proposedPSBT = await senderUser.Sign(proposedPSBT); proposedPSBT = proposedPSBT.Finalize(); var explorerClient = tester.PayTester.GetService <ExplorerClientProvider>().GetExplorerClient(proposedPSBT.Network.NetworkSet.CryptoCode); var result = await explorerClient.BroadcastAsync(proposedPSBT.ExtractTransaction()); Assert.True(result.Success); Logs.Tester.LogInformation($"We broadcasted the payjoin {proposedPSBT.ExtractTransaction().GetHash()}"); Logs.Tester.LogInformation($"Let's make sure that the coinjoin is not over paying, since the 10 overpaid sats have gone to fee"); await TestUtils.EventuallyAsync(async() => { var invoice = await tester.PayTester.GetService <InvoiceRepository>().GetInvoice(lastInvoiceId); Assert.Equal(InvoiceStatus.Paid, invoice.Status); Assert.Equal(InvoiceExceptionStatus.None, invoice.ExceptionStatus); var coins = await btcPayWallet.GetUnspentCoins(receiverUser.DerivationScheme); foreach (var coin in coins) { await payjoinRepository.TryLock(coin.OutPoint); } }); tester.ExplorerNode.Generate(1); receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network); if (senderUser != receiverUser) { Logs.Tester.LogInformation("Let's do the same, this time paying to ourselves"); senderUser = receiverUser; goto retry; } else { senderUser = originalSenderUser; } // Same as above. Except the sender send one satoshi less, so the change // output get below dust and should be removed completely. vector = (SpentCoin : Money.Satoshis(1089), InvoiceAmount : Money.Satoshis(500), Paid : Money.Satoshis(510), Fee : Money.Satoshis(200), ExpectLocked : true, ExpectedError : null as string); proposedPSBT = await RunVector(); var output = Assert.Single(proposedPSBT.Outputs); // With the output removed, the user should have largely pay all the needed fee Assert.Equal(Money.Satoshis(510) + receiverCoin.Amount, output.Value); proposedPSBT = await senderUser.Sign(proposedPSBT); proposedPSBT = proposedPSBT.Finalize(); explorerClient = tester.PayTester.GetService <ExplorerClientProvider>().GetExplorerClient(proposedPSBT.Network.NetworkSet.CryptoCode); result = await explorerClient.BroadcastAsync(proposedPSBT.ExtractTransaction(), true); Assert.True(result.Success); } }
public async Task U2ftest() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); var accountController = tester.PayTester.GetController <AccountController>(); var manageController = user.GetController <ManageController>(); var mock = new MockU2FService(tester.PayTester.GetService <ApplicationDbContextFactory>()); manageController._u2FService = mock; accountController._u2FService = mock; Assert .IsType <RedirectToActionResult>(await accountController.Login(new LoginViewModel() { Email = user.RegisterDetails.Email, Password = user.RegisterDetails.Password })); Assert.Empty(Assert.IsType <U2FAuthenticationViewModel>(Assert .IsType <ViewResult>(await manageController.U2FAuthentication()).Model).Devices); var addDeviceVM = Assert.IsType <AddU2FDeviceViewModel>(Assert .IsType <ViewResult>(manageController.AddU2FDevice("testdevice")).Model); Assert.NotEmpty(addDeviceVM.Challenge); Assert.Equal("testdevice", addDeviceVM.Name); Assert.NotEmpty(addDeviceVM.Version); Assert.Null(addDeviceVM.DeviceResponse); var devReg = new DeviceRegistration(Guid.NewGuid().ToByteArray(), Guid.NewGuid().ToByteArray(), Guid.NewGuid().ToByteArray(), 1); mock.GetDevReg = () => devReg; mock.StartedAuthentication = () => new StartedAuthentication("chocolate", addDeviceVM.AppId, devReg.KeyHandle.ByteArrayToBase64String()); addDeviceVM.DeviceResponse = new RegisterResponse("ss", Convert.ToBase64String(Encoding.UTF8.GetBytes("{typ:'x', challenge: 'fff'}"))).ToJson(); Assert .IsType <RedirectToActionResult>(await manageController.AddU2FDevice(addDeviceVM)); Assert.Single(Assert.IsType <U2FAuthenticationViewModel>(Assert .IsType <ViewResult>(await manageController.U2FAuthentication()).Model).Devices); var secondaryLoginViewModel = Assert.IsType <SecondaryLoginViewModel>(Assert .IsType <ViewResult>(await accountController.Login(new LoginViewModel() { Email = user.RegisterDetails.Email, Password = user.RegisterDetails.Password })).Model); Assert.NotNull(secondaryLoginViewModel.LoginWithU2FViewModel); Assert.Single(secondaryLoginViewModel.LoginWithU2FViewModel.Challenges); Assert.Equal(secondaryLoginViewModel.LoginWithU2FViewModel.Challenge, secondaryLoginViewModel.LoginWithU2FViewModel.Challenges.First().challenge); secondaryLoginViewModel.LoginWithU2FViewModel.DeviceResponse = new AuthenticateResponse( Convert.ToBase64String(Encoding.UTF8.GetBytes( "{typ:'x', challenge: '" + secondaryLoginViewModel.LoginWithU2FViewModel.Challenge + "'}")), "dd", devReg.KeyHandle.ByteArrayToBase64String()).ToJson(); Assert .IsType <RedirectToActionResult>( await accountController.LoginWithU2F(secondaryLoginViewModel.LoginWithU2FViewModel)); } }
public async Task CanConfigureStorage() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); var controller = tester.PayTester.GetController <ServerController>(user.UserId, user.StoreId); //Once we select a provider, redirect to its view var localResult = Assert .IsType <RedirectToActionResult>(controller.Storage(new StorageSettings() { Provider = StorageProvider.FileSystem })); Assert.Equal(nameof(ServerController.StorageProvider), localResult.ActionName); Assert.Equal(StorageProvider.FileSystem.ToString(), localResult.RouteValues["provider"]); var AmazonS3result = Assert .IsType <RedirectToActionResult>(controller.Storage(new StorageSettings() { Provider = StorageProvider.AmazonS3 })); Assert.Equal(nameof(ServerController.StorageProvider), AmazonS3result.ActionName); Assert.Equal(StorageProvider.AmazonS3.ToString(), AmazonS3result.RouteValues["provider"]); var GoogleResult = Assert .IsType <RedirectToActionResult>(controller.Storage(new StorageSettings() { Provider = StorageProvider.GoogleCloudStorage })); Assert.Equal(nameof(ServerController.StorageProvider), GoogleResult.ActionName); Assert.Equal(StorageProvider.GoogleCloudStorage.ToString(), GoogleResult.RouteValues["provider"]); var AzureResult = Assert .IsType <RedirectToActionResult>(controller.Storage(new StorageSettings() { Provider = StorageProvider.AzureBlobStorage })); Assert.Equal(nameof(ServerController.StorageProvider), AzureResult.ActionName); Assert.Equal(StorageProvider.AzureBlobStorage.ToString(), AzureResult.RouteValues["provider"]); //Cool, we get redirected to the config pages //Let's configure this stuff //Let's try and cheat and go to an invalid storage provider config Assert.Equal(nameof(Storage), (Assert .IsType <RedirectToActionResult>(await controller.StorageProvider("I am not a real provider")) .ActionName)); //ok no more messing around, let's configure this shit. var fileSystemStorageConfiguration = Assert.IsType <FileSystemStorageConfiguration>(Assert .IsType <ViewResult>(await controller.StorageProvider(StorageProvider.FileSystem.ToString())) .Model); //local file system does not need config, easy days! Assert.IsType <ViewResult>( await controller.EditFileSystemStorageProvider(fileSystemStorageConfiguration)); //ok cool, let's see if this got set right var shouldBeRedirectingToLocalStorageConfigPage = Assert.IsType <RedirectToActionResult>(await controller.Storage()); Assert.Equal(nameof(StorageProvider), shouldBeRedirectingToLocalStorageConfigPage.ActionName); Assert.Equal(StorageProvider.FileSystem, shouldBeRedirectingToLocalStorageConfigPage.RouteValues["provider"]); //if we tell the settings page to force, it should allow us to select a new provider Assert.IsType <ChooseStorageViewModel>(Assert.IsType <ViewResult>(await controller.Storage(true)).Model); //awesome, now let's see if the files result says we're all set up var viewFilesViewModel = Assert.IsType <ViewFilesViewModel>(Assert.IsType <ViewResult>(await controller.Files()).Model); Assert.True(viewFilesViewModel.StorageConfigured); Assert.Empty(viewFilesViewModel.Files); } }
public async Task CanCreateUsersViaAPI() { using (var tester = ServerTester.Create(newDb: true)) { tester.PayTester.DisableRegistration = true; await tester.StartAsync(); var unauthClient = new BTCPayServerClient(tester.PayTester.ServerUri); await AssertHttpError(400, async() => await unauthClient.CreateUser(new CreateApplicationUserRequest())); await AssertHttpError(400, async() => await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**" })); // Pass too simple await AssertHttpError(400, async() => await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" })); // We have no admin, so it should work var user1 = await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" }); // We have no admin, so it should work var user2 = await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" }); // Duplicate email await AssertHttpError(400, async() => await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" })); // Let's make an admin var admin = await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******", IsAdministrator = true }); // Creating a new user without proper creds is now impossible (unauthorized) // Because if registration are locked and that an admin exists, we don't accept unauthenticated connection await AssertHttpError(401, async() => await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" })); // But should be ok with subscriptions unlocked var settings = tester.PayTester.GetService <SettingsRepository>(); await settings.UpdateSetting <PoliciesSettings>(new PoliciesSettings() { LockSubscription = false }); await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" }); // But it should be forbidden to create an admin without being authenticated await AssertHttpError(403, async() => await unauthClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******", IsAdministrator = true })); await settings.UpdateSetting <PoliciesSettings>(new PoliciesSettings() { LockSubscription = true }); var adminAcc = tester.NewAccount(); adminAcc.UserId = admin.Id; adminAcc.IsAdmin = true; var adminClient = await adminAcc.CreateClient(Policies.CanModifyProfile); // We should be forbidden to create a new user without proper admin permissions await AssertHttpError(403, async() => await adminClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" })); await AssertHttpError(403, async() => await adminClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******", IsAdministrator = true })); // However, should be ok with the unrestricted permissions of an admin adminClient = await adminAcc.CreateClient(Policies.Unrestricted); await adminClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" }); // Even creating new admin should be ok await adminClient.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******", IsAdministrator = true }); var user1Acc = tester.NewAccount(); user1Acc.UserId = user1.Id; user1Acc.IsAdmin = false; var user1Client = await user1Acc.CreateClient(Policies.CanModifyServerSettings); // User1 trying to get server management would still fail to create user await AssertHttpError(403, async() => await user1Client.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" })); // User1 should be able to create user if subscription unlocked await settings.UpdateSetting <PoliciesSettings>(new PoliciesSettings() { LockSubscription = false }); await user1Client.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******" }); // But not an admin await AssertHttpError(403, async() => await user1Client.CreateUser(new CreateApplicationUserRequest() { Email = "*****@*****.**", Password = "******", IsAdministrator = true })); } }
public void InvoiceFlowThroughDifferentStatesCorrectly() { using (var tester = ServerTester.Create()) { tester.Start(); var user = tester.NewAccount(); user.GrantAccess(); user.RegisterDerivationScheme("BTC"); var invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 5000.0, 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(); Assert.Equal(0, invoice.CryptoInfo[0].TxCount); Eventually(() => { var textSearchResult = tester.PayTester.InvoiceRepository.GetInvoices(new InvoiceQuery() { StoreId = user.StoreId, TextSearch = invoice.OrderId }).GetAwaiter().GetResult(); Assert.Single(textSearchResult); textSearchResult = tester.PayTester.InvoiceRepository.GetInvoices(new InvoiceQuery() { StoreId = user.StoreId, TextSearch = invoice.Id }).GetAwaiter().GetResult(); Assert.Single(textSearchResult); }); invoice = user.BitPay.GetInvoice(invoice.Id, Facade.Merchant); Assert.Equal(Money.Coins(0), invoice.BtcPaid); Assert.Equal("new", invoice.Status); Assert.False((bool)((JValue)invoice.ExceptionStatus).Value); 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))); var firstPayment = Money.Coins(0.04m); var txFee = Money.Zero; var rate = user.BitPay.GetRates(); 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(); Assert.Single(invoiceEntity.HistoricalAddresses); 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; Assert.Equal("paidPartial", localInvoice.ExceptionStatus.ToString()); Assert.Equal(1, localInvoice.CryptoInfo[0].TxCount); 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(); var historical1 = invoiceEntity.HistoricalAddresses.FirstOrDefault(h => h.GetAddress() == invoice.BitcoinAddress); Assert.NotNull(historical1.UnAssigned); var historical2 = invoiceEntity.HistoricalAddresses.FirstOrDefault(h => h.GetAddress() == localInvoice.BitcoinAddress); 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); Assert.Equal(2, localInvoice.CryptoInfo[0].TxCount); 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)); Assert.False((bool)((JValue)localInvoice.ExceptionStatus).Value); }); 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); Assert.NotEqual(0.0, localInvoice.Rate); }); invoice = user.BitPay.CreateInvoice(new Invoice() { Price = 5000.0, 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); }); } }
public async Task PaymentControllerTests() { using (var tester = ServerTester.Create()) { await tester.StartAsync(); var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(); var client = await user.CreateClient(Policies.Unrestricted); var viewOnly = await user.CreateClient(Policies.CanViewPaymentRequests); //create payment request //validation errors await AssertHttpError(400, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A" }); }); await AssertHttpError(400, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "BTC", Amount = 0 }); }); await AssertHttpError(400, async() => { await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "helloinvalid", Amount = 1 }); }); await AssertHttpError(403, async() => { await viewOnly.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "helloinvalid", Amount = 1 }); }); var newPaymentRequest = await client.CreatePaymentRequest(user.StoreId, new CreatePaymentRequestRequest() { Title = "A", Currency = "USD", Amount = 1 }); //list payment request var paymentRequests = await viewOnly.GetPaymentRequests(user.StoreId); Assert.NotNull(paymentRequests); Assert.Single(paymentRequests); Assert.Equal(newPaymentRequest.Id, paymentRequests.First().Id); //get payment request var paymentRequest = await viewOnly.GetPaymentRequest(user.StoreId, newPaymentRequest.Id); Assert.Equal(newPaymentRequest.Title, paymentRequest.Title); //update payment request var updateRequest = JObject.FromObject(paymentRequest).ToObject <UpdatePaymentRequestRequest>(); updateRequest.Title = "B"; await AssertHttpError(403, async() => { await viewOnly.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest); }); await client.UpdatePaymentRequest(user.StoreId, paymentRequest.Id, updateRequest); paymentRequest = await client.GetPaymentRequest(user.StoreId, newPaymentRequest.Id); Assert.Equal(updateRequest.Title, paymentRequest.Title); //archive payment request await AssertHttpError(403, async() => { await viewOnly.ArchivePaymentRequest(user.StoreId, paymentRequest.Id); }); await client.ArchivePaymentRequest(user.StoreId, paymentRequest.Id); Assert.DoesNotContain(paymentRequest.Id, (await client.GetPaymentRequests(user.StoreId)).Select(data => data.Id)); } }