Example #1
0
    public async Task <IViewComponentResult> InvokeAsync(StoreData store)
    {
        var userId          = _userManager.GetUserId(UserClaimsPrincipal);
        var invoiceEntities = await _invoiceRepo.GetInvoices(new InvoiceQuery
        {
            UserId  = userId,
            StoreId = new [] { store.Id },
            Take    = 5
        });

        var invoices = new List <StoreRecentInvoiceViewModel>();

        foreach (var invoice in invoiceEntities)
        {
            var state = invoice.GetInvoiceState();
            invoices.Add(new StoreRecentInvoiceViewModel
            {
                Date           = invoice.InvoiceTime,
                Status         = state,
                InvoiceId      = invoice.Id,
                OrderId        = invoice.Metadata.OrderId ?? string.Empty,
                AmountCurrency = _currencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
            });
        }
        var vm = new StoreRecentInvoicesViewModel
        {
            Store    = store,
            Invoices = invoices
        };

        return(View(vm));
    }
Example #2
0
        public async Task <IActionResult> ClaimPullPayment(string pullPaymentId, ViewPullPaymentModel vm)
        {
            using var ctx = _dbContextFactory.CreateContext();
            var pp = await ctx.PullPayments.FindAsync(pullPaymentId);

            if (pp is null)
            {
                ModelState.AddModelError(nameof(pullPaymentId), "This pull payment does not exists");
            }

            var ppBlob  = pp.GetBlob();
            var network = _networkProvider.GetNetwork <BTCPayNetwork>(ppBlob.SupportedPaymentMethods.Single().CryptoCode);

            var paymentMethodId           = ppBlob.SupportedPaymentMethods.Single();
            var payoutHandler             = _payoutHandlers.FirstOrDefault(handler => handler.CanHandle(paymentMethodId));
            IClaimDestination destination = await payoutHandler?.ParseClaimDestination(paymentMethodId, vm.Destination);

            if (destination is null)
            {
                ModelState.AddModelError(nameof(vm.Destination), $"Invalid destination");
            }

            if (!ModelState.IsValid)
            {
                return(await ViewPullPayment(pullPaymentId));
            }

            var result = await _pullPaymentHostedService.Claim(new ClaimRequest()
            {
                Destination     = destination,
                PullPaymentId   = pullPaymentId,
                Value           = vm.ClaimedAmount,
                PaymentMethodId = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike)
            });

            if (result.Result != ClaimRequest.ClaimResult.Ok)
            {
                if (result.Result == ClaimRequest.ClaimResult.AmountTooLow)
                {
                    ModelState.AddModelError(nameof(vm.ClaimedAmount), ClaimRequest.GetErrorMessage(result.Result));
                }
                else
                {
                    ModelState.AddModelError(string.Empty, ClaimRequest.GetErrorMessage(result.Result));
                }
                return(await ViewPullPayment(pullPaymentId));
            }
            else
            {
                TempData.SetStatusMessageModel(new StatusMessageModel()
                {
                    Message  = $"Your claim request of {_currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency)} to {vm.Destination} has been submitted and is awaiting approval.",
                    Severity = StatusMessageModel.StatusSeverity.Success
                });
            }
            return(RedirectToAction(nameof(ViewPullPayment), new { pullPaymentId = pullPaymentId }));
        }
    public async Task <IViewComponentResult> InvokeAsync(StoreRecentInvoicesViewModel vm)
    {
        if (vm.Store == null)
        {
            throw new ArgumentNullException(nameof(vm.Store));
        }
        if (vm.CryptoCode == null)
        {
            throw new ArgumentNullException(nameof(vm.CryptoCode));
        }
        if (vm.InitialRendering)
        {
            return(View(vm));
        }

        var userId          = _userManager.GetUserId(UserClaimsPrincipal);
        var invoiceEntities = await _invoiceRepo.GetInvoices(new InvoiceQuery
        {
            UserId          = userId,
            StoreId         = new [] { vm.Store.Id },
            IncludeArchived = false,
            IncludeRefunds  = true,
            Take            = 5
        });

        vm.Invoices = (from invoice in invoiceEntities
                       let state = invoice.GetInvoiceState()
                                   select new StoreRecentInvoiceViewModel
        {
            Date = invoice.InvoiceTime,
            Status = state,
            HasRefund = invoice.Refunds.Any(),
            InvoiceId = invoice.Id,
            OrderId = invoice.Metadata.OrderId ?? string.Empty,
            AmountCurrency = _currencyNameTable.DisplayFormatCurrency(invoice.Price, invoice.Currency),
        }).ToList();

        return(View(vm));
    }
        public async Task <IActionResult> ClaimPullPayment(string pullPaymentId, ViewPullPaymentModel vm)
        {
            using var ctx = _dbContextFactory.CreateContext();
            var pp = await ctx.PullPayments.FindAsync(pullPaymentId);

            if (pp is null)
            {
                ModelState.AddModelError(nameof(pullPaymentId), "This pull payment does not exists");
            }

            var ppBlob = pp.GetBlob();

            var paymentMethodId = ppBlob.SupportedPaymentMethods.FirstOrDefault(id => vm.SelectedPaymentMethod == id.ToString());

            var payoutHandler = paymentMethodId is null ? null : _payoutHandlers.FindPayoutHandler(paymentMethodId);

            if (payoutHandler is null)
            {
                ModelState.AddModelError(nameof(vm.SelectedPaymentMethod), "Invalid destination with selected payment method");
                return(await ViewPullPayment(pullPaymentId));
            }
            var destination = await payoutHandler.ParseAndValidateClaimDestination(paymentMethodId, vm.Destination, ppBlob);

            if (destination.destination is null)
            {
                ModelState.AddModelError(nameof(vm.Destination), destination.error ?? "Invalid destination with selected payment method");
                return(await ViewPullPayment(pullPaymentId));
            }

            if (vm.ClaimedAmount == 0)
            {
                ModelState.AddModelError(nameof(vm.ClaimedAmount), "Amount is required");
            }
            else
            {
                var amount = ppBlob.Currency == "SATS" ? new Money(vm.ClaimedAmount, MoneyUnit.Satoshi).ToUnit(MoneyUnit.BTC) : vm.ClaimedAmount;
                if (destination.destination.Amount != null && amount != destination.destination.Amount)
                {
                    var implied  = _currencyNameTable.DisplayFormatCurrency(destination.destination.Amount.Value, paymentMethodId.CryptoCode);
                    var provided = _currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency);
                    ModelState.AddModelError(nameof(vm.ClaimedAmount),
                                             $"Amount implied in destination ({implied}) does not match the payout amount provided ({provided}).");
                }
            }

            if (!ModelState.IsValid)
            {
                return(await ViewPullPayment(pullPaymentId));
            }

            var result = await _pullPaymentHostedService.Claim(new ClaimRequest()
            {
                Destination     = destination.destination,
                PullPaymentId   = pullPaymentId,
                Value           = vm.ClaimedAmount,
                PaymentMethodId = paymentMethodId
            });

            if (result.Result != ClaimRequest.ClaimResult.Ok)
            {
                if (result.Result == ClaimRequest.ClaimResult.AmountTooLow)
                {
                    ModelState.AddModelError(nameof(vm.ClaimedAmount), ClaimRequest.GetErrorMessage(result.Result));
                }
                else
                {
                    ModelState.AddModelError(string.Empty, ClaimRequest.GetErrorMessage(result.Result));
                }
                return(await ViewPullPayment(pullPaymentId));
            }

            TempData.SetStatusMessageModel(new StatusMessageModel
            {
                Message  = $"Your claim request of {_currencyNameTable.DisplayFormatCurrency(vm.ClaimedAmount, ppBlob.Currency)} to {vm.Destination} has been submitted and is awaiting {(result.PayoutData.State == PayoutState.AwaitingApproval? "approval": "payment")}.",
                Severity = StatusMessageModel.StatusSeverity.Success
            });

            return(RedirectToAction(nameof(ViewPullPayment), new { pullPaymentId }));
        }
        public async Task <IActionResult> PullPayments(string storeId, int skip = 0, int count = 50,
                                                       string sortOrder         = "desc")
        {
            await using var ctx = _dbContextFactory.CreateContext();
            var now      = DateTimeOffset.UtcNow;
            var ppsQuery = ctx.PullPayments
                           .Include(data => data.Payouts)
                           .Where(p => p.StoreId == storeId && !p.Archived);


            if (sortOrder != null)
            {
                switch (sortOrder)
                {
                case "desc":
                    ViewData["NextStartSortOrder"] = "asc";
                    ppsQuery = ppsQuery.OrderByDescending(p => p.StartDate);
                    break;

                case "asc":
                    ppsQuery = ppsQuery.OrderBy(p => p.StartDate);
                    ViewData["NextStartSortOrder"] = "desc";
                    break;
                }
            }

            var vm = this.ParseListQuery(new PullPaymentsModel()
            {
                Skip  = skip,
                Count = count,
                Total = await ppsQuery.CountAsync()
            });
            var pps = (await ppsQuery
                       .Skip(vm.Skip)
                       .Take(vm.Count)
                       .ToListAsync()
                       );

            foreach (var pp in pps)
            {
                var totalCompleted = pp.Payouts.Where(p => (p.State == PayoutState.Completed ||
                                                            p.State == PayoutState.InProgress) && p.IsInPeriod(pp, now))
                                     .Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum();
                var totalAwaiting = pp.Payouts.Where(p => (p.State == PayoutState.AwaitingPayment ||
                                                           p.State == PayoutState.AwaitingApproval) &&
                                                     p.IsInPeriod(pp, now)).Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum();
                ;
                var ppBlob = pp.GetBlob();
                var ni     = _currencyNameTable.GetCurrencyData(ppBlob.Currency, true);
                var nfi    = _currencyNameTable.GetNumberFormatInfo(ppBlob.Currency, true);
                var period = pp.GetPeriod(now);
                vm.PullPayments.Add(new PullPaymentsModel.PullPaymentModel()
                {
                    StartDate = pp.StartDate,
                    EndDate   = pp.EndDate,
                    Id        = pp.Id,
                    Name      = ppBlob.Name,
                    Progress  = new PullPaymentsModel.PullPaymentModel.ProgressModel()
                    {
                        CompletedPercent = (int)(totalCompleted / ppBlob.Limit * 100m),
                        AwaitingPercent  = (int)(totalAwaiting / ppBlob.Limit * 100m),
                        Awaiting         = totalAwaiting.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
                        Completed        = totalCompleted.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
                        Limit            = _currencyNameTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency),
                        ResetIn          = period?.End is DateTimeOffset nr ? ZeroIfNegative(nr - now).TimeString() : null,
                        EndIn            = pp.EndDate is DateTimeOffset end ? ZeroIfNegative(end - now).TimeString() : null
                    }
                });
        public async Task <IActionResult> PullPayments(
            string storeId,
            PullPaymentState pullPaymentState,
            int skip         = 0,
            int count        = 50,
            string sortOrder = "desc"
            )
        {
            await using var ctx = _dbContextFactory.CreateContext();
            var now      = DateTimeOffset.UtcNow;
            var ppsQuery = ctx.PullPayments
                           .Include(data => data.Payouts)
                           .Where(p => p.StoreId == storeId);

            if (sortOrder != null)
            {
                switch (sortOrder)
                {
                case "desc":
                    ViewData["NextStartSortOrder"] = "asc";
                    ppsQuery = ppsQuery.OrderByDescending(p => p.StartDate);
                    break;

                case "asc":
                    ppsQuery = ppsQuery.OrderBy(p => p.StartDate);
                    ViewData["NextStartSortOrder"] = "desc";
                    break;
                }
            }

            var paymentMethods = await _payoutHandlers.GetSupportedPaymentMethods(HttpContext.GetStoreData());

            if (!paymentMethods.Any())
            {
                TempData.SetStatusMessageModel(new StatusMessageModel
                {
                    Message  = "You must enable at least one payment method before creating a pull payment.",
                    Severity = StatusMessageModel.StatusSeverity.Error
                });
                return(RedirectToAction(nameof(UIStoresController.GeneralSettings), "UIStores", new { storeId }));
            }

            var vm = this.ParseListQuery(new PullPaymentsModel
            {
                Skip        = skip,
                Count       = count,
                Total       = await ppsQuery.CountAsync(),
                ActiveState = pullPaymentState
            });

            switch (pullPaymentState)
            {
            case PullPaymentState.Active:
                ppsQuery = ppsQuery
                           .Where(
                    p => !p.Archived &&
                    (p.EndDate != null ? p.EndDate > DateTimeOffset.UtcNow : true) &&
                    p.StartDate <= DateTimeOffset.UtcNow
                    );
                break;

            case PullPaymentState.Archived:
                ppsQuery = ppsQuery.Where(p => p.Archived);
                break;

            case PullPaymentState.Expired:
                ppsQuery = ppsQuery.Where(p => DateTimeOffset.UtcNow > p.EndDate);
                break;

            case PullPaymentState.Future:
                ppsQuery = ppsQuery.Where(p => p.StartDate > DateTimeOffset.UtcNow);
                break;
            }

            var pps = (await ppsQuery
                       .Skip(vm.Skip)
                       .Take(vm.Count)
                       .ToListAsync()
                       );

            foreach (var pp in pps)
            {
                var totalCompleted = pp.Payouts.Where(p => (p.State == PayoutState.Completed ||
                                                            p.State == PayoutState.InProgress) && p.IsInPeriod(pp, now))
                                     .Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum();
                var totalAwaiting = pp.Payouts.Where(p => (p.State == PayoutState.AwaitingPayment ||
                                                           p.State == PayoutState.AwaitingApproval) &&
                                                     p.IsInPeriod(pp, now)).Select(o => o.GetBlob(_jsonSerializerSettings).Amount).Sum();
                ;
                var ppBlob = pp.GetBlob();
                var ni     = _currencyNameTable.GetCurrencyData(ppBlob.Currency, true);
                var nfi    = _currencyNameTable.GetNumberFormatInfo(ppBlob.Currency, true);
                var period = pp.GetPeriod(now);
                vm.PullPayments.Add(new PullPaymentsModel.PullPaymentModel()
                {
                    StartDate = pp.StartDate,
                    EndDate   = pp.EndDate,
                    Id        = pp.Id,
                    Name      = ppBlob.Name,
                    Progress  = new PullPaymentsModel.PullPaymentModel.ProgressModel()
                    {
                        CompletedPercent = (int)(totalCompleted / ppBlob.Limit * 100m),
                        AwaitingPercent  = (int)(totalAwaiting / ppBlob.Limit * 100m),
                        Awaiting         = totalAwaiting.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
                        Completed        = totalCompleted.RoundToSignificant(ni.Divisibility).ToString("C", nfi),
                        Limit            = _currencyNameTable.DisplayFormatCurrency(ppBlob.Limit, ppBlob.Currency),
                        ResetIn          = period?.End is DateTimeOffset nr ? ZeroIfNegative(nr - now).TimeString() : null,
                        EndIn            = pp.EndDate is DateTimeOffset end ? ZeroIfNegative(end - now).TimeString() : null,
                    },
Example #7
0
        public async Task <IActionResult> ViewCustodianAccount(string storeId, string accountId)
        {
            var vm = new ViewCustodianAccountViewModel();
            var custodianAccount = await _custodianAccountRepository.FindById(storeId, accountId);

            if (custodianAccount == null)
            {
                return(NotFound());
            }

            var custodian = _custodianRegistry.GetCustodianByCode(custodianAccount.CustodianCode);

            if (custodian == null)
            {
                // TODO The custodian account is broken. The custodian is no longer available. Maybe delete the custodian account?
                return(NotFound());
            }

            vm.Custodian        = custodian;
            vm.CustodianAccount = custodianAccount;
            var store           = GetCurrentStore();
            var storeBlob       = BTCPayServer.Data.StoreDataExtensions.GetStoreBlob(store);
            var defaultCurrency = storeBlob.DefaultCurrency;

            vm.DefaultCurrency = defaultCurrency;


            try
            {
                var assetBalances     = new Dictionary <string, AssetBalanceInfo>();
                var assetBalancesData =
                    await custodian.GetAssetBalancesAsync(custodianAccount.GetBlob(), cancellationToken : default);

                foreach (var pair in assetBalancesData)
                {
                    var asset = pair.Key;

                    assetBalances.Add(asset,
                                      new AssetBalanceInfo {
                        Asset = asset, Qty = pair.Value
                    }
                                      );
                }


                if (custodian is ICanTrade tradingCustodian)
                {
                    var config             = custodianAccount.GetBlob();
                    var tradableAssetPairs = tradingCustodian.GetTradableAssetPairs();

                    foreach (var pair in assetBalances)
                    {
                        var asset        = pair.Key;
                        var assetBalance = assetBalances[asset];

                        if (asset.Equals(defaultCurrency))
                        {
                            assetBalance.FormattedFiatValue = _currencyNameTable.DisplayFormatCurrency(pair.Value.Qty, defaultCurrency);
                        }
                        else
                        {
                            try
                            {
                                var quote = await tradingCustodian.GetQuoteForAssetAsync(defaultCurrency, asset,
                                                                                         config, default);

                                assetBalance.Bid                = quote.Bid;
                                assetBalance.Ask                = quote.Ask;
                                assetBalance.FiatAsset          = defaultCurrency;
                                assetBalance.FormattedBid       = _currencyNameTable.DisplayFormatCurrency(quote.Bid, quote.FromAsset);
                                assetBalance.FormattedAsk       = _currencyNameTable.DisplayFormatCurrency(quote.Ask, quote.FromAsset);
                                assetBalance.FormattedFiatValue = _currencyNameTable.DisplayFormatCurrency(pair.Value.Qty * quote.Bid, pair.Value.FiatAsset);
                                assetBalance.TradableAssetPairs = tradableAssetPairs.Where(o => o.AssetBought == asset || o.AssetSold == asset);
                            }
                            catch (WrongTradingPairException)
                            {
                                // Cannot trade this asset, just ignore
                            }
                        }
                    }
                }

                if (custodian is ICanWithdraw withdrawableCustodian)
                {
                    var withdrawableePaymentMethods = withdrawableCustodian.GetWithdrawablePaymentMethods();
                    foreach (var withdrawableePaymentMethod in withdrawableePaymentMethods)
                    {
                        var withdrawableAsset = withdrawableePaymentMethod.Split("-")[0];
                        if (assetBalances.ContainsKey(withdrawableAsset))
                        {
                            var assetBalance = assetBalances[withdrawableAsset];
                            assetBalance.CanWithdraw = true;
                        }
                    }
                }

                if (custodian is ICanDeposit depositableCustodian)
                {
                    var depositablePaymentMethods = depositableCustodian.GetDepositablePaymentMethods();
                    foreach (var depositablePaymentMethod in depositablePaymentMethods)
                    {
                        var depositableAsset = depositablePaymentMethod.Split("-")[0];
                        if (assetBalances.ContainsKey(depositableAsset))
                        {
                            var assetBalance = assetBalances[depositableAsset];
                            assetBalance.CanDeposit = true;
                        }
                    }
                }

                vm.AssetBalances = assetBalances;
            }
            catch (Exception e)
            {
                vm.GetAssetBalanceException = e;
            }

            return(View(vm));
        }