Beispiel #1
0
        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);
            }
        }
Beispiel #2
0
        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());
            }
        }
Beispiel #5
0
        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);
            }
        }
Beispiel #6
0
        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)
 };
Beispiel #8
0
        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);
                });
            }
        }
Beispiel #9
0
        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);
            }
        }
Beispiel #11
0
        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);
            }
        }
Beispiel #12
0
        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);
            }
        }
Beispiel #13
0
        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);
                });
            }
        }
Beispiel #15
0
        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()
                }));
            }
        }
Beispiel #17
0
        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);
                });
            }
        }
Beispiel #18
0
        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));
            }
        }
Beispiel #20
0
        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);
                    }
                }
            }
        }
Beispiel #21
0
        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);
            }
        }
Beispiel #22
0
        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",
                }));
Beispiel #23
0
        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);
            }
        }
Beispiel #24
0
        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
                }));
            }
        }
Beispiel #27
0
        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));
            }
        }