Example #1
0
#pragma warning disable CS0618 // Type or member is obsolete
        public void SetWalletKeyPathRoot(PaymentMethodId paymentMethodId, KeyPath keyPath)
        {
            if (keyPath == null)
            {
                WalletKeyPathRoots.Remove(paymentMethodId.ToString());
            }
            else
            {
                WalletKeyPathRoots.AddOrReplace(paymentMethodId.ToString().ToLowerInvariant(), keyPath.ToString());
            }
        }
Example #2
0
        public void SetExcluded(PaymentMethodId paymentMethodId, bool value)
        {
#pragma warning disable CS0618 // Type or member is obsolete
            var methods = new HashSet <string>(ExcludedPaymentMethods ?? Array.Empty <string>());
            if (value)
            {
                methods.Add(paymentMethodId.ToString());
            }
            else
            {
                methods.Remove(paymentMethodId.ToString());
            }
            ExcludedPaymentMethods = methods.ToArray();
#pragma warning restore CS0618 // Type or member is obsolete
        }
        private async Task <List <PayoutData> > GetPayouts(ApplicationDbContext dbContext, PaymentMethodId pmi,
                                                           string[] payoutIds)
        {
            var userId = _userManager.GetUserId(User);

            if (string.IsNullOrEmpty(userId))
            {
                return(new List <PayoutData>());
            }

            var pmiStr = pmi.ToString();

            var approvedStores = new Dictionary <string, bool>();

            return((await dbContext.Payouts
                    .Include(data => data.PullPaymentData)
                    .ThenInclude(data => data.StoreData)
                    .ThenInclude(data => data.UserStores)
                    .Where(data =>
                           payoutIds.Contains(data.Id) &&
                           data.State == PayoutState.AwaitingPayment &&
                           data.PaymentMethodId == pmiStr)
                    .ToListAsync())
                   .Where(payout =>
            {
                if (approvedStores.TryGetValue(payout.PullPaymentData.StoreId, out var value))
                {
                    return value;
                }
                value = payout.PullPaymentData.StoreData.UserStores
                        .Any(store => store.Role == StoreRoles.Owner && store.ApplicationUserId == userId);
                approvedStores.Add(payout.PullPaymentData.StoreId, value);
                return value;
            }).ToList());
        }
Example #4
0
        public override System.Collections.Specialized.NameValueCollection GetParameters()
        {
            NameValueCollection nvc = base.GetParameters();

            if (RequiresApiToken)
            {
                if (!String.IsNullOrEmpty(GetApiToken()))
                {
                    nvc.Add("apiToken", GetApiToken());
                }
            }

            if (RequiresServiceId)
            {
                if (!String.IsNullOrEmpty(GetServiceId()))
                {
                    nvc.Add("serviceID", GetServiceId());
                }
            }

            ParameterValidator.IsNotNull(PaymentMethodId, "PaymentMethodId");
            nvc.Add("paymentMethodId", PaymentMethodId.ToString());

            return(nvc);
        }
Example #5
0
 public KeyPath GetWalletKeyPathRoot(PaymentMethodId paymentMethodId)
 {
     if (WalletKeyPathRoots.TryGetValue(paymentMethodId.ToString().ToLowerInvariant(), out var k))
     {
         return(KeyPath.Parse(k));
     }
     return(null);
 }
Example #6
0
        public override NameValueCollection GetParameters(string serviceId)
        {
            var parameters = base.GetParameters(serviceId);

            ParameterValidator.IsNotNull(PaymentMethodId, "PaymentMethodId");
            parameters.Add("paymentMethodId", PaymentMethodId.ToString());

            return(parameters);
        }
Example #7
0
        public override System.Collections.Specialized.NameValueCollection GetParameters()
        {
            NameValueCollection nvc = base.GetParameters();

            ParameterValidator.IsNotNull(PaymentMethodId, "PaymentMethodId");
            nvc.Add("paymentMethodId", PaymentMethodId.ToString());

            return(nvc);
        }
    public async Task <IActionResult> InitiatePayment(PaymentMethodId paymentMethodId, string[] payoutIds)
    {
        await using var ctx = this._dbContextFactory.CreateContext();
        ctx.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
        var pmi = paymentMethodId.ToString();

        var payouts = await ctx.Payouts.Include(data => data.PullPaymentData)
                      .Where(data => payoutIds.Contains(data.Id) &&
                             pmi == data.PaymentMethodId &&
                             data.State == PayoutState.AwaitingPayment)
                      .ToListAsync();

        var           pullPaymentIds = payouts.Select(data => data.PullPaymentDataId).Distinct().Where(s => s != null).ToArray();
        var           storeId        = payouts.First().StoreDataId;
        var           network        = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(paymentMethodId.CryptoCode);
        List <string> bip21          = new List <string>();

        foreach (var payout in payouts)
        {
            if (payout.Proof != null)
            {
                continue;
            }
            var blob = payout.GetBlob(_jsonSerializerSettings);
            if (payout.GetPaymentMethodId() != paymentMethodId)
            {
                continue;
            }
            var claim = await ParseClaimDestination(paymentMethodId, blob.Destination);

            switch (claim.destination)
            {
            case UriClaimDestination uriClaimDestination:
                uriClaimDestination.BitcoinUrl.Amount = new Money(blob.CryptoAmount.Value, MoneyUnit.BTC);
                var newUri = new UriBuilder(uriClaimDestination.BitcoinUrl.Uri);
                BTCPayServerClient.AppendPayloadToQuery(newUri, new KeyValuePair <string, object>("payout", payout.Id));
                bip21.Add(newUri.Uri.ToString());
                break;

            case AddressClaimDestination addressClaimDestination:
                var bip21New = network.GenerateBIP21(addressClaimDestination.Address.ToString(), new Money(blob.CryptoAmount.Value, MoneyUnit.BTC));
                bip21New.QueryParams.Add("payout", payout.Id);
                bip21.Add(bip21New.ToString());
                break;
            }
        }
        if (bip21.Any())
        {
            return(new RedirectToActionResult("WalletSend", "UIWallets", new { walletId = new WalletId(storeId, paymentMethodId.CryptoCode).ToString(), bip21 }));
        }
        return(new RedirectToActionResult("Payouts", "UIWallets", new
        {
            walletId = new WalletId(storeId, paymentMethodId.CryptoCode).ToString(),
            pullPaymentId = pullPaymentIds.Length == 1 ? pullPaymentIds.First() : null
        }));
    }
        private static void MarkUnassigned(string invoiceId, ApplicationDbContext context,
                                           PaymentMethodId paymentMethodId)
        {
            var paymentMethodIdStr = paymentMethodId?.ToString();
            var addresses          = context.HistoricalAddressInvoices.Where(data =>
                                                                             (data.InvoiceDataId == invoiceId && paymentMethodIdStr == null ||
                                                                              data.CryptoCode == paymentMethodIdStr) &&
                                                                             data.UnAssigned == null);

            foreach (var historicalAddressInvoiceData in addresses)
            {
                historicalAddressInvoiceData.UnAssigned = DateTimeOffset.UtcNow;
            }
        }
Example #10
0
        public override NameValueCollection GetParameters()
        {
            NameValueCollection nvc = base.GetParameters();

            if (RequiresApiToken)
            {
                if (!String.IsNullOrEmpty(GetApiToken()))
                {
                    nvc.Add("token", GetApiToken());
                }
            }

            if (RequiresServiceId)
            {
                if (!String.IsNullOrEmpty(GetServiceId()))
                {
                    nvc.Add("serviceId", GetServiceId());
                }
            }

            ParameterValidator.IsNotNull(CategoryId, "CategoryId");
            nvc.Add("categoryId", CategoryId.ToString());

            if (!ParameterValidator.IsNonEmptyInt(ProgramId))
            {
                nvc.Add("programId", ProgramId.ToString());
            }

            if (!ParameterValidator.IsNonEmptyInt(PaymentMethodId))
            {
                nvc.Add("paymentMethodId", PaymentMethodId.ToString());
            }

            if (!ParameterValidator.IsNull(ShowNotAllowedOnRegistration))
            {
                nvc.Add("ShowNotAllowedOnRegistration", ((bool)ShowNotAllowedOnRegistration) ? "1" : "0");
            }

            return(nvc);
        }
    public async Task <IActionResult> InitiatePayment(PaymentMethodId paymentMethodId, string[] payoutIds)
    {
        await using var ctx = this._dbContextFactory.CreateContext();
        ctx.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
        var pmi = paymentMethodId.ToString();

        var payouts = await ctx.Payouts.Include(data => data.PullPaymentData)
                      .Where(data => payoutIds.Contains(data.Id) &&
                             pmi == data.PaymentMethodId &&
                             data.State == PayoutState.AwaitingPayment)
                      .ToListAsync();

        var           pullPaymentIds = payouts.Select(data => data.PullPaymentDataId).Distinct().ToArray();
        var           storeId        = payouts.First().PullPaymentData.StoreId;
        var           network        = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(paymentMethodId.CryptoCode);
        List <string> bip21          = new List <string>();

        foreach (var payout in payouts)
        {
            if (payout.Proof != null)
            {
                continue;
            }
            var blob = payout.GetBlob(_jsonSerializerSettings);
            if (payout.GetPaymentMethodId() != paymentMethodId)
            {
                continue;
            }
            bip21.Add(network.GenerateBIP21(payout.Destination, new Money(blob.CryptoAmount.Value, MoneyUnit.BTC)).ToString());
        }
        if (bip21.Any())
        {
            return(new RedirectToActionResult("WalletSend", "Wallets", new { walletId = new WalletId(storeId, paymentMethodId.CryptoCode).ToString(), bip21 }));
        }
        return(new RedirectToActionResult("Payouts", "Wallets", new
        {
            walletId = new WalletId(storeId, paymentMethodId.CryptoCode).ToString(),
            pullPaymentId = pullPaymentIds.Length == 1? pullPaymentIds.First(): null
        }));
    }
Example #12
0
        public override NameValueCollection GetParameters()
        {
            var nvc = base.GetParameters();

            ParameterValidator.IsNotNull(CategoryId, "CategoryId");
            nvc.Add("categoryId", CategoryId.ToString());

            if (!ParameterValidator.IsNonEmptyInt(ProgramId))
            {
                nvc.Add("programId", ProgramId.ToString());
            }

            if (!ParameterValidator.IsNonEmptyInt(PaymentMethodId))
            {
                nvc.Add("paymentMethodId", PaymentMethodId.ToString());
            }

            if (!ParameterValidator.IsNull(ShowNotAllowedOnRegistration))
            {
                nvc.Add("ShowNotAllowedOnRegistration", ((bool)ShowNotAllowedOnRegistration) ? "1" : "0");
            }

            return(nvc);
        }
Example #13
0
        private async Task <PaymentModel> GetInvoiceModel(string invoiceId, string paymentMethodIdStr)
        {
            var invoice = await _InvoiceRepository.GetInvoice(null, invoiceId);

            if (invoice == null)
            {
                return(null);
            }
            var store = await _StoreRepository.FindStore(invoice.StoreId);

            bool isDefaultCrypto = false;

            if (paymentMethodIdStr == null)
            {
                paymentMethodIdStr = store.GetDefaultCrypto();
                isDefaultCrypto    = true;
            }

            var paymentMethodId = PaymentMethodId.Parse(paymentMethodIdStr);
            var network         = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode);

            if (network == null && isDefaultCrypto)
            {
                network            = _NetworkProvider.GetAll().FirstOrDefault();
                paymentMethodId    = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
                paymentMethodIdStr = paymentMethodId.ToString();
            }
            if (invoice == null || network == null)
            {
                return(null);
            }
            if (!invoice.Support(paymentMethodId))
            {
                if (!isDefaultCrypto)
                {
                    return(null);
                }
                var paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider).First();
                network            = paymentMethodTemp.Network;
                paymentMethodId    = paymentMethodTemp.GetId();
                paymentMethodIdStr = paymentMethodId.ToString();
            }

            var paymentMethod        = invoice.GetPaymentMethod(paymentMethodId, _NetworkProvider);
            var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
            var dto        = invoice.EntityToDTO(_NetworkProvider);
            var cryptoInfo = dto.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
            var storeBlob  = store.GetStoreBlob();
            var currency   = invoice.ProductInformation.Currency;
            var accounting = paymentMethod.Calculate();
            var model      = new PaymentModel()
            {
                CryptoCode          = network.CryptoCode,
                PaymentMethodId     = paymentMethodId.ToString(),
                IsLightning         = paymentMethodId.PaymentType == PaymentTypes.LightningLike,
                ServerUrl           = HttpContext.Request.GetAbsoluteRoot(),
                OrderId             = invoice.OrderId,
                InvoiceId           = invoice.Id,
                DefaultLang         = storeBlob.DefaultLang ?? "en-US",
                HtmlTitle           = storeBlob.HtmlTitle ?? "BTCPay Invoice",
                CustomCSSLink       = storeBlob.CustomCSS?.AbsoluteUri,
                CustomLogoLink      = storeBlob.CustomLogo?.AbsoluteUri,
                BtcAddress          = paymentMethodDetails.GetPaymentDestination(),
                OrderAmount         = (accounting.TotalDue - accounting.NetworkFee).ToString(),
                BtcDue              = accounting.Due.ToString(),
                CustomerEmail       = invoice.RefundMail,
                RequiresRefundEmail = storeBlob.RequiresRefundEmail,
                ExpirationSeconds   = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
                MaxTimeSeconds      = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
                MaxTimeMinutes      = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalMinutes,
                ItemDesc            = invoice.ProductInformation.ItemDesc,
                Rate              = FormatCurrency(paymentMethod),
                MerchantRefLink   = invoice.RedirectURL ?? "/",
                StoreName         = store.StoreName,
                InvoiceBitcoinUrl = paymentMethodId.PaymentType == PaymentTypes.BTCLike ? cryptoInfo.PaymentUrls.BIP21 :
                                    paymentMethodId.PaymentType == PaymentTypes.LightningLike ? cryptoInfo.PaymentUrls.BOLT11 :
                                    throw new NotSupportedException(),
                                          PeerInfo            = (paymentMethodDetails as LightningLikePaymentMethodDetails)?.NodeInfo,
                                          InvoiceBitcoinUrlQR = paymentMethodId.PaymentType == PaymentTypes.BTCLike ? cryptoInfo.PaymentUrls.BIP21 :
                                                                paymentMethodId.PaymentType == PaymentTypes.LightningLike ? cryptoInfo.PaymentUrls.BOLT11.ToUpperInvariant() :
                                                                throw new NotSupportedException(),
                                                                      TxCount             = accounting.TxRequired,
                                                                      BtcPaid             = accounting.Paid.ToString(),
                                                                      Status              = invoice.Status,
                                                                      CryptoImage         = "/" + GetImage(paymentMethodId, network),
                                                                      NetworkFee          = paymentMethodDetails.GetTxFee(),
                                                                      IsMultiCurrency     = invoice.GetPayments().Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
                                                                      AllowCoinConversion = storeBlob.AllowCoinConversion,
                                                                      AvailableCryptos    = invoice.GetPaymentMethods(_NetworkProvider)
                                                                                            .Where(i => i.Network != null)
                                                                                            .Select(kv => new PaymentModel.AvailableCrypto()
                {
                    PaymentMethodId = kv.GetId().ToString(),
                    CryptoImage     = "/" + GetImage(kv.GetId(), kv.Network),
                    Link            = Url.Action(nameof(Checkout), new { invoiceId = invoiceId, paymentMethodId = kv.GetId().ToString() })
                }).Where(c => c.CryptoImage != "/")
                                                                                            .ToList()
            };

            var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);

            model.TimeLeft = expiration.PrettyPrint();
            return(model);
        }
 public static AddressInvoiceData Set(this AddressInvoiceData addressInvoiceData, string address, PaymentMethodId paymentMethodId)
 {
     addressInvoiceData.Address = address + "#" + paymentMethodId.ToString();
     return(addressInvoiceData);
 }
    private async Task UpdatePayoutsAwaitingForPayment(NewOnChainTransactionEvent newTransaction,
                                                       AddressTrackedSource addressTrackedSource)
    {
        try
        {
            var network        = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(newTransaction.CryptoCode);
            var destinationSum =
                newTransaction.NewTransactionEvent.Outputs.Sum(output => output.Value.GetValue(network));
            var destination     = addressTrackedSource.Address.ToString();
            var paymentMethodId = new PaymentMethodId(newTransaction.CryptoCode, BitcoinPaymentType.Instance);

            await using var ctx = _dbContextFactory.CreateContext();
            var payouts = await ctx.Payouts
                          .Include(o => o.StoreData)
                          .Include(o => o.PullPaymentData)
                          .Where(p => p.State == PayoutState.AwaitingPayment)
                          .Where(p => p.PaymentMethodId == paymentMethodId.ToString())
#pragma warning disable CA1307 // Specify StringComparison
                          .Where(p => destination.Equals(p.Destination))
#pragma warning restore CA1307 // Specify StringComparison
                          .ToListAsync();

            var payoutByDestination = payouts.ToDictionary(p => p.Destination);

            if (!payoutByDestination.TryGetValue(destination, out var payout))
            {
                return;
            }
            var payoutBlob = payout.GetBlob(_jsonSerializerSettings);
            if (payoutBlob.CryptoAmount is null ||
                // The round up here is not strictly necessary, this is temporary to fix existing payout before we
                // were properly roundup the crypto amount
                destinationSum !=
                BTCPayServer.Extensions.RoundUp(payoutBlob.CryptoAmount.Value, network.Divisibility))
            {
                return;
            }

            var derivationSchemeSettings = payout.StoreData
                                           .GetDerivationSchemeSettings(_btcPayNetworkProvider, newTransaction.CryptoCode).AccountDerivation;

            var storeWalletMatched = (await _explorerClientProvider.GetExplorerClient(newTransaction.CryptoCode)
                                      .GetTransactionAsync(derivationSchemeSettings,
                                                           newTransaction.NewTransactionEvent.TransactionData.TransactionHash));
            //if the wallet related to the store related to the payout does not have the tx: it is external
            var isInternal = storeWalletMatched is { };

            var proof = ParseProof(payout) as PayoutTransactionOnChainBlob ??
                        new PayoutTransactionOnChainBlob()
            {
                Accounted = isInternal
            };
            var txId = newTransaction.NewTransactionEvent.TransactionData.TransactionHash;
            if (!proof.Candidates.Add(txId))
            {
                return;
            }
            if (isInternal)
            {
                payout.State = PayoutState.InProgress;
                var walletId = new WalletId(payout.StoreDataId, newTransaction.CryptoCode);
                _eventAggregator.Publish(new UpdateTransactionLabel(walletId,
                                                                    newTransaction.NewTransactionEvent.TransactionData.TransactionHash,
                                                                    UpdateTransactionLabel.PayoutTemplate(new ()
                {
                    { payout.PullPaymentDataId ?? "", new List <string> {
                          payout.Id
                      } }
                }, walletId.ToString())));
            }
            else
            {
                await _notificationSender.SendNotification(new StoreScope(payout.StoreDataId),
                                                           new ExternalPayoutTransactionNotification()
                {
                    PaymentMethod = payout.PaymentMethodId,
                    PayoutId      = payout.Id,
                    StoreId       = payout.StoreDataId
                });
            }

            proof.TransactionId ??= txId;
            SetProofBlob(payout, proof);


            await ctx.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            Logs.PayServer.LogWarning(ex, "Error while processing a transaction in the pull payment hosted service");
        }
    }
Example #16
0
 public AddressInvoiceData Set(string address, PaymentMethodId paymentMethodId)
 {
     Address = address + "#" + paymentMethodId.ToString();
     return(this);
 }
Example #17
0
        private async Task <PaymentModel> GetInvoiceModel(string invoiceId, string paymentMethodIdStr)
        {
            var invoice = await _InvoiceRepository.GetInvoice(invoiceId);

            if (invoice == null)
            {
                return(null);
            }
            var store = await _StoreRepository.FindStore(invoice.StoreId);

            bool isDefaultCrypto = false;

            if (paymentMethodIdStr == null)
            {
                paymentMethodIdStr = store.GetDefaultCrypto(_NetworkProvider);
                isDefaultCrypto    = true;
            }
            var paymentMethodId = PaymentMethodId.Parse(paymentMethodIdStr);
            var network         = _NetworkProvider.GetNetwork(paymentMethodId.CryptoCode);

            if (network == null && isDefaultCrypto)
            {
                network            = _NetworkProvider.GetAll().FirstOrDefault();
                paymentMethodId    = new PaymentMethodId(network.CryptoCode, PaymentTypes.BTCLike);
                paymentMethodIdStr = paymentMethodId.ToString();
            }
            if (invoice == null || network == null)
            {
                return(null);
            }
            if (!invoice.Support(paymentMethodId))
            {
                if (!isDefaultCrypto)
                {
                    return(null);
                }
                var paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider)
                                        .Where(c => paymentMethodId.CryptoCode == c.GetId().CryptoCode)
                                        .FirstOrDefault();
                if (paymentMethodTemp == null)
                {
                    paymentMethodTemp = invoice.GetPaymentMethods(_NetworkProvider).First();
                }
                network            = paymentMethodTemp.Network;
                paymentMethodId    = paymentMethodTemp.GetId();
                paymentMethodIdStr = paymentMethodId.ToString();
            }

            var paymentMethod        = invoice.GetPaymentMethod(paymentMethodId, _NetworkProvider);
            var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
            var dto        = invoice.EntityToDTO(_NetworkProvider);
            var cryptoInfo = dto.CryptoInfo.First(o => o.GetpaymentMethodId() == paymentMethodId);
            var storeBlob  = store.GetStoreBlob();
            var currency   = invoice.ProductInformation.Currency;
            var accounting = paymentMethod.Calculate();

            ChangellySettings changelly = (storeBlob.ChangellySettings != null && storeBlob.ChangellySettings.Enabled &&
                                           storeBlob.ChangellySettings.IsConfigured())
                ? storeBlob.ChangellySettings
                : null;

            CoinSwitchSettings coinswitch = (storeBlob.CoinSwitchSettings != null && storeBlob.CoinSwitchSettings.Enabled &&
                                             storeBlob.CoinSwitchSettings.IsConfigured())
                ? storeBlob.CoinSwitchSettings
                : null;


            var changellyAmountDue = changelly != null
                ? (accounting.Due.ToDecimal(MoneyUnit.BTC) *
                   (1m + (changelly.AmountMarkupPercentage / 100m)))
                : (decimal?)null;

            var model = new PaymentModel()
            {
                CryptoCode          = network.CryptoCode,
                PaymentMethodId     = paymentMethodId.ToString(),
                PaymentMethodName   = GetDisplayName(paymentMethodId, network),
                CryptoImage         = GetImage(paymentMethodId, network),
                IsLightning         = paymentMethodId.PaymentType == PaymentTypes.LightningLike,
                ServerUrl           = HttpContext.Request.GetAbsoluteRoot(),
                OrderId             = invoice.OrderId,
                InvoiceId           = invoice.Id,
                DefaultLang         = storeBlob.DefaultLang ?? "en",
                HtmlTitle           = storeBlob.HtmlTitle ?? "BTCPay Invoice",
                CustomCSSLink       = storeBlob.CustomCSS?.AbsoluteUri,
                CustomLogoLink      = storeBlob.CustomLogo?.AbsoluteUri,
                BtcAddress          = paymentMethodDetails.GetPaymentDestination(),
                BtcDue              = accounting.Due.ToString(),
                OrderAmount         = (accounting.TotalDue - accounting.NetworkFee).ToString(),
                OrderAmountFiat     = OrderAmountFromInvoice(network.CryptoCode, invoice.ProductInformation),
                CustomerEmail       = invoice.RefundMail,
                RequiresRefundEmail = storeBlob.RequiresRefundEmail,
                ExpirationSeconds   = Math.Max(0, (int)(invoice.ExpirationTime - DateTimeOffset.UtcNow).TotalSeconds),
                MaxTimeSeconds      = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalSeconds,
                MaxTimeMinutes      = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalMinutes,
                ItemDesc            = invoice.ProductInformation.ItemDesc,
                Rate              = ExchangeRate(paymentMethod),
                MerchantRefLink   = invoice.RedirectURL ?? "/",
                StoreName         = store.StoreName,
                InvoiceBitcoinUrl = paymentMethodId.PaymentType == PaymentTypes.BTCLike ? cryptoInfo.PaymentUrls.BIP21 :
                                    paymentMethodId.PaymentType == PaymentTypes.LightningLike ? cryptoInfo.PaymentUrls.BOLT11 :
                                    throw new NotSupportedException(),
                                          PeerInfo            = (paymentMethodDetails as LightningLikePaymentMethodDetails)?.NodeInfo,
                                          InvoiceBitcoinUrlQR = paymentMethodId.PaymentType == PaymentTypes.BTCLike ? cryptoInfo.PaymentUrls.BIP21 :
                                                                paymentMethodId.PaymentType == PaymentTypes.LightningLike ? cryptoInfo.PaymentUrls.BOLT11.ToUpperInvariant() :
                                                                throw new NotSupportedException(),
                                                                      TxCount = accounting.TxRequired,
                                                                      BtcPaid = accounting.Paid.ToString(),
#pragma warning disable CS0618 // Type or member is obsolete
                                                                      Status = invoice.StatusString,
#pragma warning restore CS0618 // Type or member is obsolete
                                                                      NetworkFee           = paymentMethodDetails.GetNextNetworkFee(),
                                                                      IsMultiCurrency      = invoice.GetPayments().Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
                                                                      ChangellyEnabled     = changelly != null,
                                                                      ChangellyMerchantId  = changelly?.ChangellyMerchantId,
                                                                      ChangellyAmountDue   = changellyAmountDue,
                                                                      CoinSwitchEnabled    = coinswitch != null,
                                                                      CoinSwitchMerchantId = coinswitch?.MerchantId,
                                                                      CoinSwitchMode       = coinswitch?.Mode,
                                                                      StoreId          = store.Id,
                                                                      AvailableCryptos = invoice.GetPaymentMethods(_NetworkProvider)
                                                                                         .Where(i => i.Network != null)
                                                                                         .Select(kv => new PaymentModel.AvailableCrypto()
                {
                    PaymentMethodId   = kv.GetId().ToString(),
                    CryptoCode        = kv.GetId().CryptoCode,
                    PaymentMethodName = GetDisplayName(kv.GetId(), kv.Network),
                    IsLightning       = kv.GetId().PaymentType == PaymentTypes.LightningLike,
                    CryptoImage       = GetImage(kv.GetId(), kv.Network),
                    Link = Url.Action(nameof(Checkout), new { invoiceId = invoiceId, paymentMethodId = kv.GetId().ToString() })
                }).Where(c => c.CryptoImage != "/")
                                                                                         .OrderByDescending(a => a.CryptoCode == "BTC").ThenBy(a => a.PaymentMethodName).ThenBy(a => a.IsLightning ? 1 : 0)
                                                                                         .ToList()
            };

            var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);

            model.TimeLeft = expiration.PrettyPrint();
            return(model);
        }
Example #18
0
    private async Task UpdatePayoutsAwaitingForPayment(NewOnChainTransactionEvent newTransaction)
    {
        try
        {
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(newTransaction.CryptoCode);
            Dictionary <string, decimal> destinations;
            if (newTransaction.NewTransactionEvent.TrackedSource is AddressTrackedSource addressTrackedSource)
            {
                destinations = new Dictionary <string, decimal>()
                {
                    {
                        addressTrackedSource.Address.ToString(),
                            newTransaction.NewTransactionEvent.Outputs.Sum(output => output.Value.GetValue(network))
                    }
                };
            }
            else
            {
                destinations = newTransaction.NewTransactionEvent.TransactionData.Transaction.Outputs
                               .GroupBy(txout => txout.ScriptPubKey)
                               .ToDictionary(
                    txoutSet => txoutSet.Key.GetDestinationAddress(network.NBitcoinNetwork).ToString(),
                    txoutSet => txoutSet.Sum(txout => txout.Value.ToDecimal(MoneyUnit.BTC)));
            }

            var paymentMethodId = new PaymentMethodId(newTransaction.CryptoCode, BitcoinPaymentType.Instance);

            using var ctx = _dbContextFactory.CreateContext();
            var payouts = await ctx.Payouts
                          .Include(o => o.PullPaymentData)
                          .Where(p => p.State == PayoutState.AwaitingPayment)
                          .Where(p => p.PaymentMethodId == paymentMethodId.ToString())
                          .Where(p => destinations.Keys.Contains(p.Destination))
                          .ToListAsync();

            var payoutByDestination = payouts.ToDictionary(p => p.Destination);
            foreach (var destination in destinations)
            {
                if (!payoutByDestination.TryGetValue(destination.Key, out var payout))
                {
                    continue;
                }
                var payoutBlob = payout.GetBlob(_jsonSerializerSettings);
                if (payoutBlob.CryptoAmount is null ||
                    // The round up here is not strictly necessary, this is temporary to fix existing payout before we
                    // were properly roundup the crypto amount
                    destination.Value != BTCPayServer.Extensions.RoundUp(payoutBlob.CryptoAmount.Value, network.Divisibility))
                {
                    continue;
                }
                var proof = ParseProof(payout) as PayoutTransactionOnChainBlob;
                if (proof is null)
                {
                    proof = new PayoutTransactionOnChainBlob()
                    {
                        Accounted = !(newTransaction.NewTransactionEvent.TrackedSource is AddressTrackedSource),
                    };
                }
                var txId = newTransaction.NewTransactionEvent.TransactionData.TransactionHash;
                if (proof.Candidates.Add(txId))
                {
                    if (proof.Accounted is true)
                    {
                        payout.State = PayoutState.InProgress;
                        var walletId = new WalletId(payout.PullPaymentData.StoreId, newTransaction.CryptoCode);
                        _eventAggregator.Publish(new UpdateTransactionLabel(walletId,
                                                                            newTransaction.NewTransactionEvent.TransactionData.TransactionHash,
                                                                            UpdateTransactionLabel.PayoutTemplate(payout.Id, payout.PullPaymentDataId, walletId.ToString())));
                    }
                    if (proof.TransactionId is null)
                    {
                        proof.TransactionId = txId;
                    }
                    SetProofBlob(payout, proof);
                }
            }

            await ctx.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            Logs.PayServer.LogWarning(ex, "Error while processing a transaction in the pull payment hosted service");
        }
    }
 public static void SetDefaultPaymentId(this StoreData storeData, PaymentMethodId defaultPaymentId)
 {
     storeData.DefaultCrypto = defaultPaymentId.ToString();
 }
Example #20
0
 public void SetDefaultPaymentId(PaymentMethodId defaultPaymentId)
 {
     DefaultCrypto = defaultPaymentId.ToString();
 }