예제 #1
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;


            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.GetNetworkFee(),
                                                                      IsMultiCurrency     = invoice.GetPayments().Select(p => p.GetPaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1,
                                                                      ChangellyEnabled    = changelly != null,
                                                                      ChangellyMerchantId = changelly?.ChangellyMerchantId,
                                                                      ChangellyAmountDue  = changellyAmountDue,
                                                                      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);
        }
예제 #2
0
        public async Task <IActionResult> CheckoutExperience(CheckoutExperienceViewModel model)
        {
            CurrencyValue lightningMaxValue = null;

            if (!string.IsNullOrWhiteSpace(model.LightningMaxValue))
            {
                if (!CurrencyValue.TryParse(model.LightningMaxValue, out lightningMaxValue))
                {
                    ModelState.AddModelError(nameof(model.LightningMaxValue), "Invalid lightning max value");
                }
            }

            CurrencyValue onchainMinValue = null;

            if (!string.IsNullOrWhiteSpace(model.OnChainMinValue))
            {
                if (!CurrencyValue.TryParse(model.OnChainMinValue, out onchainMinValue))
                {
                    ModelState.AddModelError(nameof(model.OnChainMinValue), "Invalid on chain min value");
                }
            }
            bool needUpdate             = false;
            var  blob                   = StoreData.GetStoreBlob();
            var  defaultPaymentMethodId = model.DefaultPaymentMethod == null ? null : PaymentMethodId.Parse(model.DefaultPaymentMethod);

            if (StoreData.GetDefaultPaymentId(_NetworkProvider) != defaultPaymentMethodId)
            {
                needUpdate = true;
                StoreData.SetDefaultPaymentId(defaultPaymentMethodId);
            }
            SetCryptoCurrencies(model, StoreData);
            model.SetLanguages(_LangService, model.DefaultLang);

            if (!ModelState.IsValid)
            {
                return(View(model));
            }
            blob.CustomLogo               = string.IsNullOrWhiteSpace(model.CustomLogo) ? null : new Uri(model.CustomLogo, UriKind.Absolute);
            blob.CustomCSS                = string.IsNullOrWhiteSpace(model.CustomCSS) ? null : new Uri(model.CustomCSS, UriKind.Absolute);
            blob.HtmlTitle                = string.IsNullOrWhiteSpace(model.HtmlTitle) ? null : model.HtmlTitle;
            blob.DefaultLang              = model.DefaultLang;
            blob.RequiresRefundEmail      = model.RequiresRefundEmail;
            blob.OnChainMinValue          = onchainMinValue;
            blob.LightningMaxValue        = lightningMaxValue;
            blob.LightningAmountInSatoshi = model.LightningAmountInSatoshi;
            blob.RedirectAutomatically    = model.RedirectAutomatically;
            if (StoreData.SetStoreBlob(blob))
            {
                needUpdate = true;
            }
            if (needUpdate)
            {
                await _Repo.UpdateStore(StoreData);

                StatusMessage = "Store successfully updated";
            }

            return(RedirectToAction(nameof(CheckoutExperience), new
            {
                storeId = StoreData.Id
            }));
        }
예제 #3
0
        public async Task <IActionResult> GetLNURLForApp(string cryptoCode, string appId, string itemCode = null)
        {
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(cryptoCode);

            if (network is null || !network.SupportLightning)
            {
                return(NotFound());
            }

            var app = await _appService.GetApp(appId, null, true);

            if (app is null)
            {
                return(NotFound());
            }

            var store = app.StoreData;

            if (store is null)
            {
                return(NotFound());
            }
            if (string.IsNullOrEmpty(itemCode))
            {
                return(NotFound());
            }

            var pmi         = new PaymentMethodId(cryptoCode, PaymentTypes.LNURLPay);
            var lnpmi       = new PaymentMethodId(cryptoCode, PaymentTypes.LightningLike);
            var methods     = store.GetSupportedPaymentMethods(_btcPayNetworkProvider);
            var lnUrlMethod =
                methods.FirstOrDefault(method => method.PaymentId == pmi) as LNURLPaySupportedPaymentMethod;
            var lnMethod = methods.FirstOrDefault(method => method.PaymentId == lnpmi);

            if (lnUrlMethod is null || lnMethod is null)
            {
                return(NotFound());
            }

            ViewPointOfSaleViewModel.Item[] items = null;
            string currencyCode = null;

            switch (app.AppType)
            {
            case nameof(AppType.Crowdfund):
                var cfS = app.GetSettings <CrowdfundSettings>();
                currencyCode = cfS.TargetCurrency;
                items        = _appService.Parse(cfS.PerksTemplate, cfS.TargetCurrency);
                break;

            case nameof(AppType.PointOfSale):
                var posS = app.GetSettings <UIAppsController.PointOfSaleSettings>();
                currencyCode = posS.Currency;
                items        = _appService.Parse(posS.Template, posS.Currency);
                break;
            }

            var item = items.FirstOrDefault(item1 =>
                                            item1.Id.Equals(itemCode, StringComparison.InvariantCultureIgnoreCase));

            if (item is null ||
                item.Inventory <= 0 ||
                (item.PaymentMethods?.Any() is true &&
                 item.PaymentMethods?.Any(s => PaymentMethodId.Parse(s) == pmi) is false))
            {
                return(NotFound());
            }

            return(await GetLNURL(cryptoCode, app.StoreDataId, currencyCode, null, null,
                                  () => (null, new List <string> {
                AppService.GetAppInternalTag(appId)
            }, item.Price.Value, true)));
        }
예제 #4
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 (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();
            }

            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 currency   = invoice.ProductInformation.Currency;
            var accounting = paymentMethod.Calculate();
            var model      = new PaymentModel()
            {
                CryptoCode        = network.CryptoCode,
                PaymentMethodId   = paymentMethodId.ToString(),
                ServerUrl         = HttpContext.Request.GetAbsoluteRoot(),
                OrderId           = invoice.OrderId,
                InvoiceId         = invoice.Id,
                BtcAddress        = paymentMethodDetails.GetPaymentDestination(),
                OrderAmount       = (accounting.TotalDue - accounting.NetworkFee).ToString(),
                BtcDue            = accounting.Due.ToString(),
                CustomerEmail     = invoice.RefundMail,
                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(),
                                          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),
                                                                      NetworkFeeDescription = $"{accounting.TxRequired} transaction{(accounting.TxRequired > 1 ? "s" : "")} x {paymentMethodDetails.GetTxFee()} {network.CryptoCode}",
                                                                      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 isMultiCurrency = invoice.GetPayments().Select(p => p.GetpaymentMethodId()).Concat(new[] { paymentMethod.GetId() }).Distinct().Count() > 1;

            if (isMultiCurrency)
            {
                model.NetworkFeeDescription = $"{accounting.NetworkFee} {network.CryptoCode}";
            }

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

            model.TimeLeft = PrettyPrint(expiration);
            return(model);
        }
예제 #5
0
        public async Task <IActionResult> CheckoutNoScript(string invoiceId, string id = null, string paymentMethodId = null)
        {
            //Keep compatibility with Bitpay
            invoiceId = invoiceId ?? id;
            id        = invoiceId;
            //

            var model = await GetInvoiceModel(invoiceId, paymentMethodId == null?null : PaymentMethodId.Parse(paymentMethodId));

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

            return(View(model));
        }
예제 #6
0
        public async Task <IActionResult> GetStatus(string invoiceId, string paymentMethodId = null)
        {
            var model = await GetInvoiceModel(invoiceId, paymentMethodId == null?null : PaymentMethodId.Parse(paymentMethodId));

            if (model == null)
            {
                return(NotFound());
            }
            return(Json(model));
        }
예제 #7
0
        public async Task <IActionResult> Checkout(string invoiceId, string id = null, string paymentMethodId = null,
                                                   [FromQuery] string view     = null)
        {
            //Keep compatibility with Bitpay
            invoiceId = invoiceId ?? id;
            id        = invoiceId;
            //

            var model = await GetInvoiceModel(invoiceId, paymentMethodId == null?null : PaymentMethodId.Parse(paymentMethodId));

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

            if (view == "modal")
            {
                model.IsModal = true;
            }

            _CSP.Add(new ConsentSecurityPolicy("script-src", "'unsafe-eval'")); // Needed by Vue
            if (!string.IsNullOrEmpty(model.CustomCSSLink) &&
                Uri.TryCreate(model.CustomCSSLink, UriKind.Absolute, out var uri))
            {
                _CSP.Clear();
            }

            if (!string.IsNullOrEmpty(model.CustomLogoLink) &&
                Uri.TryCreate(model.CustomLogoLink, UriKind.Absolute, out uri))
            {
                _CSP.Clear();
            }

            return(View(nameof(Checkout), model));
        }
예제 #8
0
#pragma warning disable CS0618
        public static PaymentMethodId GetDefaultPaymentId(this StoreData storeData, BTCPayNetworkProvider networks)
        {
            PaymentMethodId[] paymentMethodIds = storeData.GetEnabledPaymentIds(networks);

            var defaultPaymentId = string.IsNullOrEmpty(storeData.DefaultCrypto) ? null : PaymentMethodId.Parse(storeData.DefaultCrypto);
            var chosen           = paymentMethodIds.FirstOrDefault(f => f == defaultPaymentId) ??
                                   paymentMethodIds.FirstOrDefault(f => f.CryptoCode == defaultPaymentId?.CryptoCode) ??
                                   paymentMethodIds.FirstOrDefault();

            return(chosen);
        }
예제 #9
0
        public async Task <IActionResult> CheckoutExperience(CheckoutExperienceViewModel model)
        {
            bool needUpdate             = false;
            var  blob                   = CurrentStore.GetStoreBlob();
            var  defaultPaymentMethodId = model.DefaultPaymentMethod == null ? null : PaymentMethodId.Parse(model.DefaultPaymentMethod);

            if (CurrentStore.GetDefaultPaymentId(_NetworkProvider) != defaultPaymentMethodId)
            {
                needUpdate = true;
                CurrentStore.SetDefaultPaymentId(defaultPaymentMethodId);
            }
            SetCryptoCurrencies(model, CurrentStore);
            model.SetLanguages(_LangService, model.DefaultLang);
            model.PaymentMethodCriteria ??= new List <PaymentMethodCriteriaViewModel>();
            for (var index = 0; index < model.PaymentMethodCriteria.Count; index++)
            {
                var methodCriterion = model.PaymentMethodCriteria[index];
                if (!string.IsNullOrWhiteSpace(methodCriterion.Value))
                {
                    if (!CurrencyValue.TryParse(methodCriterion.Value, out var value))
                    {
                        model.AddModelError(viewModel => viewModel.PaymentMethodCriteria[index].Value,
                                            $"{methodCriterion.PaymentMethod}: invalid format (1.0 USD)", this);
                    }
                }
            }


            if (!ModelState.IsValid)
            {
                return(View(model));
            }

            blob.PaymentMethodCriteria = model.PaymentMethodCriteria
                                         .Where(viewModel => !string.IsNullOrEmpty(viewModel.Value)).Select(viewModel =>
            {
                CurrencyValue.TryParse(viewModel.Value, out var cv);
                return(new PaymentMethodCriteria()
                {
                    Above = viewModel.Type == PaymentMethodCriteriaViewModel.CriteriaType.GreaterThan, Value = cv, PaymentMethod = PaymentMethodId.Parse(viewModel.PaymentMethod)
                });
            }).ToList();

            blob.RequiresRefundEmail          = model.RequiresRefundEmail;
            blob.LightningAmountInSatoshi     = model.LightningAmountInSatoshi;
            blob.LightningPrivateRouteHints   = model.LightningPrivateRouteHints;
            blob.OnChainWithLnInvoiceFallback = model.OnChainWithLnInvoiceFallback;
            blob.RedirectAutomatically        = model.RedirectAutomatically;
            blob.ShowRecommendedFee           = model.ShowRecommendedFee;
            blob.RecommendedFeeBlockTarget    = model.RecommendedFeeBlockTarget;

            blob.CustomLogo  = model.CustomLogo;
            blob.CustomCSS   = model.CustomCSS;
            blob.HtmlTitle   = string.IsNullOrWhiteSpace(model.HtmlTitle) ? null : model.HtmlTitle;
            blob.DefaultLang = model.DefaultLang;

            if (CurrentStore.SetStoreBlob(blob))
            {
                needUpdate = true;
            }
            if (needUpdate)
            {
                await _Repo.UpdateStore(CurrentStore);

                TempData[WellKnownTempData.SuccessMessage] = "Store successfully updated";
            }

            return(RedirectToAction(nameof(CheckoutExperience), new
            {
                storeId = CurrentStore.Id
            }));
        }
예제 #10
0
        public IPaymentFilter GetExcludedPaymentMethods()
        {
#pragma warning disable CS0618 // Type or member is obsolete
            if (ExcludedPaymentMethods == null || ExcludedPaymentMethods.Length == 0)
            {
                return(PaymentFilter.Never());
            }
            return(PaymentFilter.Any(ExcludedPaymentMethods.Select(p => PaymentFilter.WhereIs(PaymentMethodId.Parse(p))).ToArray()));

#pragma warning restore CS0618 // Type or member is obsolete
        }
 public static PaymentMethodId GetPaymentMethodId(this PayoutData data)
 {
     return(PaymentMethodId.Parse(data.PaymentMethodId));
 }