示例#1
0
        public async Task <IActionResult> UpdateStoreEmailSettings(string storeId, EmailSettings request)
        {
            var store = HttpContext.GetStoreData();

            if (store == null)
            {
                return(StoreNotFound());
            }

            if (!string.IsNullOrEmpty(request.From) && !EmailValidator.IsEmail(request.From))
            {
                request.AddModelError(e => e.From,
                                      "Invalid email address", this);
                return(this.CreateValidationError(ModelState));
            }
            var blob = store.GetStoreBlob();

            blob.EmailSettings = request;
            if (store.SetStoreBlob(blob))
            {
                await _storeRepository.UpdateStore(store);
            }

            return(Ok(FromModel(store)));
        }
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(Invoice invoice, StoreData store, string serverUrl, double expiryMinutes = 15)
        {
            //TODO: expiryMinutes (time before a new invoice can become paid) and monitoringMinutes (time before a paid invoice becomes invalid)  should be configurable at store level
            var derivationStrategy = store.DerivationStrategy;
            var entity             = new InvoiceEntity
            {
                InvoiceTime        = DateTimeOffset.UtcNow,
                DerivationStrategy = derivationStrategy ?? throw new BitpayHttpException(400, "This store has not configured the derivation strategy")
            };
            var storeBlob       = store.GetStoreBlob(_Network);
            Uri notificationUri = Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute) ? new Uri(invoice.NotificationURL, UriKind.Absolute) : null;

            if (notificationUri == null || (notificationUri.Scheme != "http" && notificationUri.Scheme != "https")) //TODO: Filer non routable addresses ?
            {
                notificationUri = null;
            }
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime       = entity.InvoiceTime.AddMinutes(expiryMinutes);
            entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration);
            entity.OrderId           = invoice.OrderId;
            entity.ServerUrl         = serverUrl;
            entity.FullNotifications = invoice.FullNotifications;
            entity.NotificationURL   = notificationUri?.AbsoluteUri;
            entity.BuyerInformation  = Map <Invoice, BuyerInformation>(invoice);
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }
            entity.ProductInformation = Map <Invoice, ProductInformation>(invoice);
            entity.RedirectURL        = invoice.RedirectURL ?? store.StoreWebsite;
            entity.Status             = "new";
            entity.SpeedPolicy        = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            var getFeeRate = _FeeProvider.GetFeeRateAsync();
            var getRate    = _RateProvider.GetRateAsync(invoice.Currency);
            var getAddress = _Wallet.ReserveAddressAsync(ParseDerivationStrategy(derivationStrategy));

            entity.TxFee          = storeBlob.NetworkFeeDisabled ? Money.Zero : (await getFeeRate).GetFee(100); // assume price for 100 bytes
            entity.Rate           = (double)await getRate;
            entity.PosData        = invoice.PosData;
            entity.DepositAddress = await getAddress;
            entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity);

            _Watcher.Watch(entity.Id);
            var resp = entity.EntityToDTO();

            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }
示例#3
0
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(Invoice invoice, StoreData store, string serverUrl)
        {
            var derivationStrategy = store.DerivationStrategy;
            var entity             = new InvoiceEntity
            {
                InvoiceTime        = DateTimeOffset.UtcNow,
                DerivationStrategy = derivationStrategy ?? throw new BitpayHttpException(400, "This store has not configured the derivation strategy")
            };
            Uri notificationUri = Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute) ? new Uri(invoice.NotificationURL, UriKind.Absolute) : null;

            if (notificationUri == null || (notificationUri.Scheme != "http" && notificationUri.Scheme != "https"))            //TODO: Filer non routable addresses ?
            {
                notificationUri = null;
            }
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime    = entity.InvoiceTime + TimeSpan.FromMinutes(15.0);
            entity.ServerUrl         = serverUrl;
            entity.FullNotifications = invoice.FullNotifications;
            entity.NotificationURL   = notificationUri?.AbsoluteUri;
            entity.BuyerInformation  = Map <Invoice, BuyerInformation>(invoice);
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }
            entity.ProductInformation = Map <Invoice, ProductInformation>(invoice);
            entity.RedirectURL        = invoice.RedirectURL ?? store.StoreWebsite;
            entity.Status             = "new";
            entity.SpeedPolicy        = store.SpeedPolicy;
            entity.TxFee = (await _FeeProvider.GetFeeRateAsync()).GetFee(100);             // assume price for 100 bytes
            entity.Rate  = (double)await _RateProvider.GetRateAsync(invoice.Currency);

            entity.PosData        = invoice.PosData;
            entity.DepositAddress = await _Wallet.ReserveAddressAsync(ParseDerivationStrategy(derivationStrategy));

            entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity);

            await _Wallet.MapAsync(entity.DepositAddress.ScriptPubKey, entity.Id);

            await _Watcher.WatchAsync(entity.Id);

            var resp = entity.EntityToDTO();

            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }
        public async Task <(IClaimDestination destination, string error)> ParseClaimDestination(PaymentMethodId paymentMethodId, string destination, bool validate)
        {
            destination = destination.Trim();
            var network = _btcPayNetworkProvider.GetNetwork <BTCPayNetwork>(paymentMethodId.CryptoCode);

            try
            {
                string lnurlTag = null;
                var    lnurl    = EmailValidator.IsEmail(destination)
                    ? LNURL.LNURL.ExtractUriFromInternetIdentifier(destination)
                    : LNURL.LNURL.Parse(destination, out lnurlTag);

                if (lnurlTag is null)
                {
                    var info = (LNURLPayRequest)(await LNURL.LNURL.FetchInformation(lnurl, CreateClient(lnurl)));
                    lnurlTag = info.Tag;
                }

                if (lnurlTag.Equals("payRequest", StringComparison.InvariantCultureIgnoreCase))
                {
                    return(new LNURLPayClaimDestinaton(destination), null);
                }
            }
            catch (FormatException)
            {
            }
            catch
            {
                return(null, "The LNURL / Lightning Address provided was not online.");
            }

            var result =
                BOLT11PaymentRequest.TryParse(destination, out var invoice, network.NBitcoinNetwork)
                    ? new BoltInvoiceClaimDestination(destination, invoice)
                    : null;

            if (result == null)
            {
                return(null, "A valid BOLT11 invoice (with 30+ day expiry) or LNURL Pay or Lightning address was not provided.");
            }
            if (validate && (invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow).Days < 30)
            {
                return(null,
                       $"The BOLT11 invoice must have an expiry date of at least 30 days from submission (Provided was only {(invoice.ExpiryDate.UtcDateTime - DateTime.UtcNow).Days}).");
            }
            if (invoice.ExpiryDate.UtcDateTime < DateTime.UtcNow)
            {
                return(null,
                       "The BOLT11 invoice submitted has expired.");
            }

            return(result, null);
        }
示例#5
0
        public virtual JsonResult Submit(SendMessageModel model)
        {
            var response         = new ActionResponse <bool>();
            var validMessageType = new[] { MessagingType.Email, MessagingType.Sms };

            if (!ModelState.IsValid)
            {
                response.Message = LocalMessage.InvalidFormData;
            }
            else if (!validMessageType.Contains(model.MessagingType))
            {
                response.Message = LocalMessage.InvalidEmailAddress;
                return(Json(response, JsonRequestBehavior.AllowGet));
            }
            else if ((model.MessagingType == MessagingType.Sms).And(!CellPhoneNumberValidator.IsCellPhoneNumber(model.Reciver)))
            {
                response.Message = LocalMessage.InvalidMobileNumber;
                return(Json(response, JsonRequestBehavior.AllowGet));
            }
            else if ((model.MessagingType == MessagingType.Email).And(!EmailValidator.IsEmail(model.Reciver)))
            {
                response.Message = LocalMessage.InvalidEmailAddress;
                return(Json(response, JsonRequestBehavior.AllowGet));
            }
            else
            {
                var result = _messageBusiness.Insert(new Message
                {
                    Content  = model.Message,
                    Receiver = model.Reciver,
                    Type     = model.MessagingType
                });
                response.IsSuccessful = response.Result = result.IsSuccessful;
                response.Message      = result.Message;
            }
            return(Json(response, JsonRequestBehavior.AllowGet));
        }
示例#6
0
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(CreateInvoiceRequest invoice, StoreData store, string serverUrl, List <string> additionalTags = null, CancellationToken cancellationToken = default)
        {
            if (!store.HasClaim(Policies.CanCreateInvoice.Key))
            {
                throw new UnauthorizedAccessException();
            }
            InvoiceLogs logs = new InvoiceLogs();

            logs.Write("Creation of invoice starting");
            var entity = new InvoiceEntity
            {
                Version     = InvoiceEntity.Lastest_Version,
                InvoiceTime = DateTimeOffset.UtcNow,
                Networks    = _NetworkProvider
            };

            var getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id);
            var storeBlob           = store.GetStoreBlob();
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime       = entity.InvoiceTime.AddMinutes(storeBlob.InvoiceExpiration);
            entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration);
            entity.OrderId               = invoice.OrderId;
            entity.ServerUrl             = serverUrl;
            entity.FullNotifications     = invoice.FullNotifications || invoice.ExtendedNotifications;
            entity.ExtendedNotifications = invoice.ExtendedNotifications;

            if (invoice.NotificationURL != null &&
                Uri.TryCreate(invoice.NotificationURL, UriKind.Absolute, out var notificationUri) &&
                (notificationUri.Scheme == "http" || notificationUri.Scheme == "https"))
            {
                entity.NotificationURL = notificationUri.AbsoluteUri;
            }
            entity.NotificationEmail = invoice.NotificationEmail;
            entity.BuyerInformation  = Map <CreateInvoiceRequest, BuyerInformation>(invoice);
            entity.PaymentTolerance  = storeBlob.PaymentTolerance;
            if (additionalTags != null)
            {
                entity.InternalTags.AddRange(additionalTags);
            }
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }

            var taxIncluded = invoice.TaxIncluded.HasValue ? invoice.TaxIncluded.Value : 0m;

            var currencyInfo = _CurrencyNameTable.GetNumberFormatInfo(invoice.Currency, false);

            if (currencyInfo != null)
            {
                int divisibility = currencyInfo.CurrencyDecimalDigits;
                invoice.Price       = invoice.Price.RoundToSignificant(ref divisibility);
                divisibility        = currencyInfo.CurrencyDecimalDigits;
                invoice.TaxIncluded = taxIncluded.RoundToSignificant(ref divisibility);
            }
            invoice.Price       = Math.Max(0.0m, invoice.Price);
            invoice.TaxIncluded = Math.Max(0.0m, taxIncluded);
            invoice.TaxIncluded = Math.Min(taxIncluded, invoice.Price);

            entity.ProductInformation = Map <CreateInvoiceRequest, ProductInformation>(invoice);


            entity.RedirectURL = invoice.RedirectURL ?? store.StoreWebsite;
            if (!Uri.IsWellFormedUriString(entity.RedirectURL, UriKind.Absolute))
            {
                entity.RedirectURL = null;
            }

            entity.RedirectAutomatically =
                invoice.RedirectAutomatically.GetValueOrDefault(storeBlob.RedirectAutomatically);

            entity.Status      = InvoiceStatus.New;
            entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            HashSet <CurrencyPair> currencyPairsToFetch = new HashSet <CurrencyPair>();
            var rules         = storeBlob.GetRateRules(_NetworkProvider);
            var excludeFilter = storeBlob.GetExcludedPaymentMethods(); // Here we can compose filters from other origin with PaymentFilter.Any()

            if (invoice.SupportedTransactionCurrencies != null && invoice.SupportedTransactionCurrencies.Count != 0)
            {
                var supportedTransactionCurrencies = invoice.SupportedTransactionCurrencies
                                                     .Where(c => c.Value.Enabled)
                                                     .Select(c => PaymentMethodId.TryParse(c.Key, out var p) ? p : null)
                                                     .ToHashSet();
                excludeFilter = PaymentFilter.Or(excludeFilter,
                                                 PaymentFilter.Where(p => !supportedTransactionCurrencies.Contains(p)));
            }

            foreach (var network in store.GetSupportedPaymentMethods(_NetworkProvider)
                     .Where(s => !excludeFilter.Match(s.PaymentId))
                     .Select(c => _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode))
                     .Where(c => c != null))
            {
                currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, invoice.Currency));
                if (storeBlob.LightningMaxValue != null)
                {
                    currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.LightningMaxValue.Currency));
                }
                if (storeBlob.OnChainMinValue != null)
                {
                    currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.OnChainMinValue.Currency));
                }
            }

            var rateRules = storeBlob.GetRateRules(_NetworkProvider);
            var fetchingByCurrencyPair  = _RateProvider.FetchRates(currencyPairsToFetch, rateRules, cancellationToken);
            var fetchingAll             = WhenAllFetched(logs, fetchingByCurrencyPair);
            var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider)
                                          .Where(s => !excludeFilter.Match(s.PaymentId))
                                          .Select(c =>
                                                  (Handler: (IPaymentMethodHandler)_ServiceProvider.GetService(typeof(IPaymentMethodHandler <>).MakeGenericType(c.GetType())),
                                                   SupportedPaymentMethod: c,
                                                   Network: _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode)))
                                          .Where(c => c.Network != null)
                                          .Select(o =>
                                                  (SupportedPaymentMethod: o.SupportedPaymentMethod,
                                                   PaymentMethod: CreatePaymentMethodAsync(fetchingByCurrencyPair, o.Handler, o.SupportedPaymentMethod, o.Network, entity, store, logs)))
                                          .ToList();
            List <ISupportedPaymentMethod> supported = new List <ISupportedPaymentMethod>();
            var paymentMethods = new PaymentMethodDictionary();

            foreach (var o in supportedPaymentMethods)
            {
                var paymentMethod = await o.PaymentMethod;
                if (paymentMethod == null)
                {
                    continue;
                }
                supported.Add(o.SupportedPaymentMethod);
                paymentMethods.Add(paymentMethod);
            }

            if (supported.Count == 0)
            {
                StringBuilder errors = new StringBuilder();
                errors.AppendLine("Warning: No wallet has been linked to your BTCPay Store. See the following link for more information on how to connect your store and wallet. (https://docs.btcpayserver.org/btcpay-basics/gettingstarted#connecting-btcpay-store-to-your-wallet)");
                foreach (var error in logs.ToList())
                {
                    errors.AppendLine(error.ToString());
                }
                throw new BitpayHttpException(400, errors.ToString());
            }

            entity.SetSupportedPaymentMethods(supported);
            entity.SetPaymentMethods(paymentMethods);
            entity.PosData = invoice.PosData;

            foreach (var app in await getAppsTaggingStore)
            {
                entity.InternalTags.Add(AppService.GetAppInternalTag(app.Id));
            }

            using (logs.Measure("Saving invoice"))
            {
                entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity);
            }
            _ = Task.Run(async() =>
            {
                try
                {
                    await fetchingAll;
                }
                catch (AggregateException ex)
                {
                    ex.Handle(e => { logs.Write($"Error while fetching rates {ex}"); return(true); });
                }
                await _InvoiceRepository.AddInvoiceLogs(entity.Id, logs);
            });
            _EventAggregator.Publish(new Events.InvoiceEvent(entity, 1001, InvoiceEvent.Created));
            var resp = entity.EntityToDTO();

            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }
        internal async Task <InvoiceEntity> CreateInvoiceCoreRaw(InvoiceEntity entity, StoreData store, IPaymentFilter?invoicePaymentMethodFilter, string[]?additionalSearchTerms = null, CancellationToken cancellationToken = default)
        {
            InvoiceLogs logs = new InvoiceLogs();

            logs.Write("Creation of invoice starting", InvoiceEventData.EventSeverity.Info);
            var storeBlob = store.GetStoreBlob();

            if (string.IsNullOrEmpty(entity.Currency))
            {
                entity.Currency = storeBlob.DefaultCurrency;
            }
            entity.Currency = entity.Currency.Trim().ToUpperInvariant();
            entity.Price    = Math.Max(0.0m, entity.Price);
            var currencyInfo = _CurrencyNameTable.GetNumberFormatInfo(entity.Currency, false);

            if (currencyInfo != null)
            {
                entity.Price = entity.Price.RoundToSignificant(currencyInfo.CurrencyDecimalDigits);
            }
            if (entity.Metadata.TaxIncluded is decimal taxIncluded)
            {
                if (currencyInfo != null)
                {
                    taxIncluded = taxIncluded.RoundToSignificant(currencyInfo.CurrencyDecimalDigits);
                }
                taxIncluded = Math.Max(0.0m, taxIncluded);
                taxIncluded = Math.Min(taxIncluded, entity.Price);
                entity.Metadata.TaxIncluded = taxIncluded;
            }

            var getAppsTaggingStore = _InvoiceRepository.GetAppsTaggingStore(store.Id);

            if (entity.Metadata.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.Metadata.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.Metadata.BuyerEmail;
            }
            entity.Status = InvoiceStatusLegacy.New;
            HashSet <CurrencyPair> currencyPairsToFetch = new HashSet <CurrencyPair>();
            var rules         = storeBlob.GetRateRules(_NetworkProvider);
            var excludeFilter = storeBlob.GetExcludedPaymentMethods(); // Here we can compose filters from other origin with PaymentFilter.Any()

            if (invoicePaymentMethodFilter != null)
            {
                excludeFilter = PaymentFilter.Or(excludeFilter,
                                                 invoicePaymentMethodFilter);
            }
            foreach (var network in store.GetSupportedPaymentMethods(_NetworkProvider)
                     .Where(s => !excludeFilter.Match(s.PaymentId))
                     .Select(c => _NetworkProvider.GetNetwork <BTCPayNetworkBase>(c.PaymentId.CryptoCode))
                     .Where(c => c != null))
            {
                currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, entity.Currency));
                foreach (var paymentMethodCriteria in storeBlob.PaymentMethodCriteria)
                {
                    if (paymentMethodCriteria.Value != null)
                    {
                        currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, paymentMethodCriteria.Value.Currency));
                    }
                }
            }

            var rateRules = storeBlob.GetRateRules(_NetworkProvider);
            var fetchingByCurrencyPair = _RateProvider.FetchRates(currencyPairsToFetch, rateRules, cancellationToken);
            var fetchingAll            = WhenAllFetched(logs, fetchingByCurrencyPair);

            List <ISupportedPaymentMethod> supported = new List <ISupportedPaymentMethod>();
            var paymentMethods = new PaymentMethodDictionary();

            bool noNeedForMethods = entity.Type != InvoiceType.TopUp && entity.Price == 0m;

            if (!noNeedForMethods)
            {
                // This loop ends with .ToList so we are querying all payment methods at once
                // instead of sequentially to improve response time
                foreach (var o in store.GetSupportedPaymentMethods(_NetworkProvider)
                         .Where(s => !excludeFilter.Match(s.PaymentId) &&
                                _paymentMethodHandlerDictionary.Support(s.PaymentId))
                         .Select(c =>
                                 (Handler: _paymentMethodHandlerDictionary[c.PaymentId],
                                  SupportedPaymentMethod: c,
                                  Network: _NetworkProvider.GetNetwork <BTCPayNetworkBase>(c.PaymentId.CryptoCode)))
                         .Where(c => c.Network != null)
                         .Select(o =>
                                 (SupportedPaymentMethod: o.SupportedPaymentMethod,
                                  PaymentMethod: CreatePaymentMethodAsync(fetchingByCurrencyPair, o.Handler,
                                                                          o.SupportedPaymentMethod, o.Network, entity, store, logs)))
                         .ToList())
                {
                    var paymentMethod = await o.PaymentMethod;
                    if (paymentMethod == null)
                    {
                        continue;
                    }
                    supported.Add(o.SupportedPaymentMethod);
                    paymentMethods.Add(paymentMethod);
                }

                if (supported.Count == 0)
                {
                    StringBuilder errors = new StringBuilder();
                    if (!store.GetSupportedPaymentMethods(_NetworkProvider).Any())
                    {
                        errors.AppendLine(
                            "Warning: No wallet has been linked to your BTCPay Store. See the following link for more information on how to connect your store and wallet. (https://docs.btcpayserver.org/WalletSetup/)");
                    }
                    foreach (var error in logs.ToList())
                    {
                        errors.AppendLine(error.ToString());
                    }

                    throw new BitpayHttpException(400, errors.ToString());
                }
            }
            entity.SetSupportedPaymentMethods(supported);
            entity.SetPaymentMethods(paymentMethods);
            foreach (var app in await getAppsTaggingStore)
            {
                entity.InternalTags.Add(AppService.GetAppInternalTag(app.Id));
            }

            using (logs.Measure("Saving invoice"))
            {
                entity = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, additionalSearchTerms);
            }
            _ = Task.Run(async() =>
            {
                try
                {
                    await fetchingAll;
                }
                catch (AggregateException ex)
                {
                    ex.Handle(e => { logs.Write($"Error while fetching rates {ex}", InvoiceEventData.EventSeverity.Error); return(true); });
                }
                await _InvoiceRepository.AddInvoiceLogs(entity.Id, logs);
            });
            _EventAggregator.Publish(new Events.InvoiceEvent(entity, InvoiceEvent.Created));
            return(entity);
        }
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(Invoice invoice, StoreData store, string serverUrl)
        {
            if (!store.HasClaim(Policies.CanCreateInvoice.Key))
            {
                throw new UnauthorizedAccessException();
            }
            InvoiceLogs logs = new InvoiceLogs();

            logs.Write("Creation of invoice starting");
            var entity = new InvoiceEntity
            {
                InvoiceTime = DateTimeOffset.UtcNow
            };

            var storeBlob       = store.GetStoreBlob();
            Uri notificationUri = Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute) ? new Uri(invoice.NotificationURL, UriKind.Absolute) : null;

            if (notificationUri == null || (notificationUri.Scheme != "http" && notificationUri.Scheme != "https")) //TODO: Filer non routable addresses ?
            {
                notificationUri = null;
            }
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime       = entity.InvoiceTime.AddMinutes(storeBlob.InvoiceExpiration);
            entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration);
            entity.OrderId               = invoice.OrderId;
            entity.ServerUrl             = serverUrl;
            entity.FullNotifications     = invoice.FullNotifications || invoice.ExtendedNotifications;
            entity.ExtendedNotifications = invoice.ExtendedNotifications;
            entity.NotificationURL       = notificationUri?.AbsoluteUri;
            entity.NotificationEmail     = invoice.NotificationEmail;
            entity.BuyerInformation      = Map <Invoice, BuyerInformation>(invoice);
            entity.PaymentTolerance      = storeBlob.PaymentTolerance;
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }
            entity.ProductInformation = Map <Invoice, ProductInformation>(invoice);
            entity.RedirectURL        = invoice.RedirectURL ?? store.StoreWebsite;
            if (!Uri.IsWellFormedUriString(entity.RedirectURL, UriKind.Absolute))
            {
                entity.RedirectURL = null;
            }

            entity.Status      = "new";
            entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            HashSet <CurrencyPair> currencyPairsToFetch = new HashSet <CurrencyPair>();
            var rules         = storeBlob.GetRateRules(_NetworkProvider);
            var excludeFilter = storeBlob.GetExcludedPaymentMethods(); // Here we can compose filters from other origin with PaymentFilter.Any()

            foreach (var network in store.GetSupportedPaymentMethods(_NetworkProvider)
                     .Where(s => !excludeFilter.Match(s.PaymentId))
                     .Select(c => _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode))
                     .Where(c => c != null))
            {
                currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, invoice.Currency));
                if (storeBlob.LightningMaxValue != null)
                {
                    currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.LightningMaxValue.Currency));
                }
                if (storeBlob.OnChainMinValue != null)
                {
                    currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.OnChainMinValue.Currency));
                }
            }

            var rateRules = storeBlob.GetRateRules(_NetworkProvider);
            var fetchingByCurrencyPair = _RateProvider.FetchRates(currencyPairsToFetch, rateRules);

            var fetchingAll             = WhenAllFetched(logs, fetchingByCurrencyPair);
            var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider)
                                          .Where(s => !excludeFilter.Match(s.PaymentId))
                                          .Select(c =>
                                                  (Handler: (IPaymentMethodHandler)_ServiceProvider.GetService(typeof(IPaymentMethodHandler <>).MakeGenericType(c.GetType())),
                                                   SupportedPaymentMethod: c,
                                                   Network: _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode)))
                                          .Where(c => c.Network != null)
                                          .Select(o =>
                                                  (SupportedPaymentMethod: o.SupportedPaymentMethod,
                                                   PaymentMethod: CreatePaymentMethodAsync(fetchingByCurrencyPair, o.Handler, o.SupportedPaymentMethod, o.Network, entity, store, logs)))
                                          .ToList();
            List <ISupportedPaymentMethod> supported = new List <ISupportedPaymentMethod>();
            var paymentMethods = new PaymentMethodDictionary();

            foreach (var o in supportedPaymentMethods)
            {
                var paymentMethod = await o.PaymentMethod;
                if (paymentMethod == null)
                {
                    continue;
                }
                supported.Add(o.SupportedPaymentMethod);
                paymentMethods.Add(paymentMethod);
            }

            if (supported.Count == 0)
            {
                StringBuilder errors = new StringBuilder();
                errors.AppendLine("No payment method available for this store");
                foreach (var error in logs.ToList())
                {
                    errors.AppendLine(error.ToString());
                }
                throw new BitpayHttpException(400, errors.ToString());
            }

            entity.SetSupportedPaymentMethods(supported);
            entity.SetPaymentMethods(paymentMethods);
            entity.PosData = invoice.PosData;
            entity         = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, logs, _NetworkProvider);

            await fetchingAll;

            _EventAggregator.Publish(new Events.InvoiceEvent(entity.EntityToDTO(_NetworkProvider), 1001, "invoice_created"));
            var resp = entity.EntityToDTO(_NetworkProvider);

            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }
示例#9
0
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(Invoice invoice, StoreData store, string serverUrl)
        {
            var entity = new InvoiceEntity
            {
                InvoiceTime = DateTimeOffset.UtcNow
            };

            var storeBlob       = store.GetStoreBlob();
            Uri notificationUri = Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute) ? new Uri(invoice.NotificationURL, UriKind.Absolute) : null;

            if (notificationUri == null || (notificationUri.Scheme != "http" && notificationUri.Scheme != "https")) //TODO: Filer non routable addresses ?
            {
                notificationUri = null;
            }
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime       = entity.InvoiceTime.AddMinutes(storeBlob.InvoiceExpiration);
            entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration);
            entity.OrderId               = invoice.OrderId;
            entity.ServerUrl             = serverUrl;
            entity.FullNotifications     = invoice.FullNotifications || invoice.ExtendedNotifications;
            entity.ExtendedNotifications = invoice.ExtendedNotifications;
            entity.NotificationURL       = notificationUri?.AbsoluteUri;
            entity.BuyerInformation      = Map <Invoice, BuyerInformation>(invoice);
            entity.PaymentTolerance      = storeBlob.PaymentTolerance;
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }
            entity.ProductInformation = Map <Invoice, ProductInformation>(invoice);
            entity.RedirectURL        = invoice.RedirectURL ?? store.StoreWebsite;
            if (!Uri.IsWellFormedUriString(entity.RedirectURL, UriKind.Absolute))
            {
                entity.RedirectURL = null;
            }

            entity.Status      = "new";
            entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            HashSet <CurrencyPair> currencyPairsToFetch = new HashSet <CurrencyPair>();
            var rules = storeBlob.GetRateRules(_NetworkProvider);

            await UpdateCLightningConnectionStringIfNeeded(store);

            foreach (var network in store.GetSupportedPaymentMethods(_NetworkProvider)
                     .Select(c => _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode))
                     .Where(c => c != null))
            {
                currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, invoice.Currency));
                if (storeBlob.LightningMaxValue != null)
                {
                    currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.LightningMaxValue.Currency));
                }
                if (storeBlob.OnChainMinValue != null)
                {
                    currencyPairsToFetch.Add(new CurrencyPair(network.CryptoCode, storeBlob.OnChainMinValue.Currency));
                }
            }

            var rateRules = storeBlob.GetRateRules(_NetworkProvider);
            var fetchingByCurrencyPair = _RateProvider.FetchRates(currencyPairsToFetch, rateRules);

            var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider)
                                          .Select(c =>
                                                  (Handler: (IPaymentMethodHandler)_ServiceProvider.GetService(typeof(IPaymentMethodHandler <>).MakeGenericType(c.GetType())),
                                                   SupportedPaymentMethod: c,
                                                   Network: _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode)))
                                          .Where(c => c.Network != null)
                                          .Select(o =>
                                                  (SupportedPaymentMethod: o.SupportedPaymentMethod,
                                                   PaymentMethod: CreatePaymentMethodAsync(fetchingByCurrencyPair, o.Handler, o.SupportedPaymentMethod, o.Network, entity, store)))
                                          .ToList();

            List <string> invoiceLogs = new List <string>();
            List <ISupportedPaymentMethod> supported = new List <ISupportedPaymentMethod>();
            var paymentMethods = new PaymentMethodDictionary();

            foreach (var pair in fetchingByCurrencyPair)
            {
                var rateResult = await pair.Value;
                invoiceLogs.Add($"{pair.Key}: The rating rule is {rateResult.Rule}");
                invoiceLogs.Add($"{pair.Key}: The evaluated rating rule is {rateResult.EvaluatedRule}");
                if (rateResult.Errors.Count != 0)
                {
                    var allRateRuleErrors = string.Join(", ", rateResult.Errors.ToArray());
                    invoiceLogs.Add($"{pair.Key}: Rate rule error ({allRateRuleErrors})");
                }
                if (rateResult.ExchangeExceptions.Count != 0)
                {
                    foreach (var ex in rateResult.ExchangeExceptions)
                    {
                        invoiceLogs.Add($"{pair.Key}: Exception reaching exchange {ex.ExchangeName} ({ex.Exception.Message})");
                    }
                }
            }

            foreach (var o in supportedPaymentMethods)
            {
                try
                {
                    var paymentMethod = await o.PaymentMethod;
                    if (paymentMethod == null)
                    {
                        throw new PaymentMethodUnavailableException("Payment method unavailable");
                    }
                    supported.Add(o.SupportedPaymentMethod);
                    paymentMethods.Add(paymentMethod);
                }
                catch (PaymentMethodUnavailableException ex)
                {
                    invoiceLogs.Add($"{o.SupportedPaymentMethod.PaymentId.CryptoCode} ({o.SupportedPaymentMethod.PaymentId.PaymentType}): Payment method unavailable ({ex.Message})");
                }
                catch (Exception ex)
                {
                    invoiceLogs.Add($"{o.SupportedPaymentMethod.PaymentId.CryptoCode} ({o.SupportedPaymentMethod.PaymentId.PaymentType}): Unexpected exception ({ex.ToString()})");
                }
            }

            if (supported.Count == 0)
            {
                StringBuilder errors = new StringBuilder();
                errors.AppendLine("No payment method available for this store");
                foreach (var error in invoiceLogs)
                {
                    errors.AppendLine(error);
                }
                throw new BitpayHttpException(400, errors.ToString());
            }

            entity.SetSupportedPaymentMethods(supported);
            entity.SetPaymentMethods(paymentMethods);
            entity.PosData = invoice.PosData;
            entity         = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, invoiceLogs, _NetworkProvider);

            _EventAggregator.Publish(new Events.InvoiceEvent(entity.EntityToDTO(_NetworkProvider), 1001, "invoice_created"));
            var resp = entity.EntityToDTO(_NetworkProvider);

            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }
示例#10
0
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(Invoice invoice, StoreData store, string serverUrl)
        {
            var entity = new InvoiceEntity
            {
                InvoiceTime = DateTimeOffset.UtcNow
            };

            var storeBlob       = store.GetStoreBlob();
            Uri notificationUri = Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute) ? new Uri(invoice.NotificationURL, UriKind.Absolute) : null;

            if (notificationUri == null || (notificationUri.Scheme != "http" && notificationUri.Scheme != "https")) //TODO: Filer non routable addresses ?
            {
                notificationUri = null;
            }
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime       = entity.InvoiceTime.AddMinutes(storeBlob.InvoiceExpiration);
            entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration);
            entity.OrderId               = invoice.OrderId;
            entity.ServerUrl             = serverUrl;
            entity.FullNotifications     = invoice.FullNotifications || invoice.ExtendedNotifications;
            entity.ExtendedNotifications = invoice.ExtendedNotifications;
            entity.NotificationURL       = notificationUri?.AbsoluteUri;
            entity.BuyerInformation      = Map <Invoice, BuyerInformation>(invoice);
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }
            entity.ProductInformation = Map <Invoice, ProductInformation>(invoice);
            entity.RedirectURL        = invoice.RedirectURL ?? store.StoreWebsite;
            entity.Status             = "new";
            entity.SpeedPolicy        = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);


            var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider)
                                          .Select(c =>
                                                  (Handler: (IPaymentMethodHandler)_ServiceProvider.GetService(typeof(IPaymentMethodHandler <>).MakeGenericType(c.GetType())),
                                                   SupportedPaymentMethod: c,
                                                   Network: _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode)))
                                          .Where(c => c.Network != null)
                                          .Select(o =>
                                                  (SupportedPaymentMethod: o.SupportedPaymentMethod,
                                                   PaymentMethod: CreatePaymentMethodAsync(o.Handler, o.SupportedPaymentMethod, o.Network, entity, store)))
                                          .ToList();

            List <string> paymentMethodErrors        = new List <string>();
            List <ISupportedPaymentMethod> supported = new List <ISupportedPaymentMethod>();
            var paymentMethods = new PaymentMethodDictionary();

            foreach (var o in supportedPaymentMethods)
            {
                try
                {
                    var paymentMethod = await o.PaymentMethod;
                    if (paymentMethod == null)
                    {
                        throw new PaymentMethodUnavailableException("Payment method unavailable (The handler returned null)");
                    }
                    supported.Add(o.SupportedPaymentMethod);
                    paymentMethods.Add(paymentMethod);
                }
                catch (PaymentMethodUnavailableException ex)
                {
                    paymentMethodErrors.Add($"{o.SupportedPaymentMethod.PaymentId.CryptoCode} ({o.SupportedPaymentMethod.PaymentId.PaymentType}): Payment method unavailable ({ex.Message})");
                }
                catch (Exception ex)
                {
                    paymentMethodErrors.Add($"{o.SupportedPaymentMethod.PaymentId.CryptoCode} ({o.SupportedPaymentMethod.PaymentId.PaymentType}): Unexpected exception ({ex.ToString()})");
                }
            }

            if (supported.Count == 0)
            {
                StringBuilder errors = new StringBuilder();
                errors.AppendLine("No payment method available for this store");
                foreach (var error in paymentMethodErrors)
                {
                    errors.AppendLine(error);
                }
                throw new BitpayHttpException(400, errors.ToString());
            }

            entity.SetSupportedPaymentMethods(supported);
            entity.SetPaymentMethods(paymentMethods);
#pragma warning disable CS0618
            // Legacy Bitpay clients expect information for BTC information, even if the store do not support it
            var legacyBTCisSet = paymentMethods.Any(p => p.GetId().IsBTCOnChain);
            if (!legacyBTCisSet && _NetworkProvider.BTC != null)
            {
                var btc          = _NetworkProvider.BTC;
                var feeProvider  = ((IFeeProviderFactory)_ServiceProvider.GetService(typeof(IFeeProviderFactory))).CreateFeeProvider(btc);
                var rateProvider = _RateProviders.GetRateProvider(btc, storeBlob.GetRateRules());
                if (feeProvider != null && rateProvider != null)
                {
                    var gettingFee  = feeProvider.GetFeeRateAsync();
                    var gettingRate = rateProvider.GetRateAsync(invoice.Currency);
                    entity.TxFee = GetTxFee(storeBlob, await gettingFee);
                    entity.Rate  = await gettingRate;
                }
#pragma warning restore CS0618
            }
            entity.PosData = invoice.PosData;
            entity         = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, paymentMethodErrors, _NetworkProvider);

            _EventAggregator.Publish(new Events.InvoiceEvent(entity, 1001, "invoice_created"));
            var resp = entity.EntityToDTO(_NetworkProvider);
            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }
示例#11
0
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(Invoice invoice, StoreData store, string serverUrl)
        {
            var derivationStrategies = store.GetDerivationStrategies(_NetworkProvider).Where(c => _ExplorerClients.IsAvailable(c.Network.CryptoCode)).ToList();

            if (derivationStrategies.Count == 0)
            {
                throw new BitpayHttpException(400, "No derivation strategy are available now for this store");
            }
            var entity = new InvoiceEntity
            {
                InvoiceTime = DateTimeOffset.UtcNow
            };

            entity.SetDerivationStrategies(derivationStrategies);

            var storeBlob       = store.GetStoreBlob();
            Uri notificationUri = Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute) ? new Uri(invoice.NotificationURL, UriKind.Absolute) : null;

            if (notificationUri == null || (notificationUri.Scheme != "http" && notificationUri.Scheme != "https")) //TODO: Filer non routable addresses ?
            {
                notificationUri = null;
            }
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime       = entity.InvoiceTime.AddMinutes(storeBlob.InvoiceExpiration);
            entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration);
            entity.OrderId               = invoice.OrderId;
            entity.ServerUrl             = serverUrl;
            entity.FullNotifications     = invoice.FullNotifications || invoice.ExtendedNotifications;
            entity.ExtendedNotifications = invoice.ExtendedNotifications;
            entity.NotificationURL       = notificationUri?.AbsoluteUri;
            entity.BuyerInformation      = Map <Invoice, BuyerInformation>(invoice);
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }
            entity.ProductInformation = Map <Invoice, ProductInformation>(invoice);
            entity.RedirectURL        = invoice.RedirectURL ?? store.StoreWebsite;
            entity.Status             = "new";
            entity.SpeedPolicy        = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            var queries = derivationStrategies
                          .Select(derivationStrategy => (Wallet: _WalletProvider.GetWallet(derivationStrategy.Network),
                                                         DerivationStrategy: derivationStrategy.DerivationStrategyBase,
                                                         Network: derivationStrategy.Network,
                                                         RateProvider: _RateProviders.GetRateProvider(derivationStrategy.Network, false),
                                                         FeeRateProvider: _FeeProviderFactory.CreateFeeProvider(derivationStrategy.Network)))
                          .Where(_ => _.Wallet != null &&
                                 _.FeeRateProvider != null &&
                                 _.RateProvider != null)
                          .Select(_ =>
            {
                return(new
                {
                    network = _.Network,
                    getFeeRate = _.FeeRateProvider.GetFeeRateAsync(),
                    getRate = storeBlob.ApplyRateRules(_.Network, _.RateProvider).GetRateAsync(invoice.Currency),
                    getAddress = _.Wallet.ReserveAddressAsync(_.DerivationStrategy)
                });
            });

            bool legacyBTCisSet = false;
            var  cryptoDatas    = new Dictionary <string, CryptoData>();

            foreach (var q in queries)
            {
                CryptoData cryptoData = new CryptoData();
                cryptoData.CryptoCode     = q.network.CryptoCode;
                cryptoData.FeeRate        = (await q.getFeeRate);
                cryptoData.TxFee          = GetTxFee(storeBlob, cryptoData.FeeRate); // assume price for 100 bytes
                cryptoData.Rate           = await q.getRate;
                cryptoData.DepositAddress = (await q.getAddress).ToString();

#pragma warning disable CS0618
                if (q.network.IsBTC)
                {
                    legacyBTCisSet        = true;
                    entity.TxFee          = cryptoData.TxFee;
                    entity.Rate           = cryptoData.Rate;
                    entity.DepositAddress = cryptoData.DepositAddress;
                }
#pragma warning restore CS0618
                cryptoDatas.Add(cryptoData.CryptoCode, cryptoData);
            }

            if (!legacyBTCisSet)
            {
                // Legacy Bitpay clients expect information for BTC information, even if the store do not support it
#pragma warning disable CS0618
                var btc          = _NetworkProvider.BTC;
                var feeProvider  = _FeeProviderFactory.CreateFeeProvider(btc);
                var rateProvider = storeBlob.ApplyRateRules(btc, _RateProviders.GetRateProvider(btc, false));
                if (feeProvider != null && rateProvider != null)
                {
                    var gettingFee  = feeProvider.GetFeeRateAsync();
                    var gettingRate = rateProvider.GetRateAsync(invoice.Currency);
                    entity.TxFee = GetTxFee(storeBlob, await gettingFee);
                    entity.Rate  = await gettingRate;
                }
#pragma warning restore CS0618
            }

            entity.SetCryptoData(cryptoDatas);
            entity.PosData = invoice.PosData;
            entity         = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, _NetworkProvider);

            _EventAggregator.Publish(new Events.InvoiceEvent(entity, 1001, "invoice_created"));
            var resp = entity.EntityToDTO(_NetworkProvider);
            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }
示例#12
0
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(Invoice invoice, StoreData store, string serverUrl, double expiryMinutes = 15)
        {
            var derivationStrategy = store.DerivationStrategy;
            var entity             = new InvoiceEntity
            {
                InvoiceTime        = DateTimeOffset.UtcNow,
                DerivationStrategy = derivationStrategy ?? throw new BitpayHttpException(400, "This store has not configured the derivation strategy")
            };
            var storeBlob       = store.GetStoreBlob();
            Uri notificationUri = Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute) ? new Uri(invoice.NotificationURL, UriKind.Absolute) : null;

            if (notificationUri == null || (notificationUri.Scheme != "http" && notificationUri.Scheme != "https")) //TODO: Filer non routable addresses ?
            {
                notificationUri = null;
            }
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime       = entity.InvoiceTime.AddMinutes(expiryMinutes);
            entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration);
            entity.OrderId           = invoice.OrderId;
            entity.ServerUrl         = serverUrl;
            entity.FullNotifications = invoice.FullNotifications;
            entity.NotificationURL   = notificationUri?.AbsoluteUri;
            entity.BuyerInformation  = Map <Invoice, BuyerInformation>(invoice);
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }
            entity.ProductInformation = Map <Invoice, ProductInformation>(invoice);
            entity.RedirectURL        = invoice.RedirectURL ?? store.StoreWebsite;
            entity.Status             = "new";
            entity.SpeedPolicy        = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            var queries = storeBlob.GetSupportedCryptoCurrencies()
                          .Select(n => _NetworkProvider.GetNetwork(n))
                          .Where(n => n != null)
                          .Select(network =>
            {
                return(new
                {
                    network = network,
                    getFeeRate = _FeeProviderFactory.CreateFeeProvider(network).GetFeeRateAsync(),
                    getRate = _RateProvider.GetRateAsync(invoice.Currency),
                    getAddress = _Wallet.ReserveAddressAsync(ParseDerivationStrategy(derivationStrategy, network))
                });
            });

            var cryptoDatas = new Dictionary <string, CryptoData>();

            foreach (var q in queries)
            {
                CryptoData cryptoData = new CryptoData();
                cryptoData.CryptoCode     = q.network.CryptoCode;
                cryptoData.FeeRate        = (await q.getFeeRate);
                cryptoData.TxFee          = storeBlob.NetworkFeeDisabled ? Money.Zero : cryptoData.FeeRate.GetFee(100); // assume price for 100 bytes
                cryptoData.Rate           = await q.getRate;
                cryptoData.DepositAddress = (await q.getAddress).ToString();

#pragma warning disable CS0618
                if (q.network.CryptoCode == "BTC")
                {
                    entity.TxFee          = cryptoData.TxFee;
                    entity.Rate           = cryptoData.Rate;
                    entity.DepositAddress = cryptoData.DepositAddress;
                }
#pragma warning restore CS0618
                cryptoDatas.Add(cryptoData.CryptoCode, cryptoData);
            }
            entity.SetCryptoData(cryptoDatas);
            entity.PosData = invoice.PosData;
            entity         = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, _NetworkProvider);

            _Watcher.Watch(entity.Id);
            var resp = entity.EntityToDTO(_NetworkProvider);
            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }
示例#13
0
        internal async Task <DataWrapper <InvoiceResponse> > CreateInvoiceCore(Invoice invoice, StoreData store, string serverUrl)
        {
            var supportedPaymentMethods = store.GetSupportedPaymentMethods(_NetworkProvider)
                                          .Select(c =>
                                                  (Handler: (IPaymentMethodHandler)_ServiceProvider.GetService(typeof(IPaymentMethodHandler <>).MakeGenericType(c.GetType())),
                                                   SupportedPaymentMethod: c,
                                                   Network: _NetworkProvider.GetNetwork(c.PaymentId.CryptoCode),
                                                   IsAvailable: Task.FromResult(false)))
                                          .Where(c => c.Network != null)
                                          .Select(c =>
            {
                c.IsAvailable = c.Handler.IsAvailable(c.SupportedPaymentMethod, c.Network);
                return(c);
            })
                                          .ToList();

            foreach (var supportedPaymentMethod in supportedPaymentMethods.ToList())
            {
                if (!await supportedPaymentMethod.IsAvailable)
                {
                    supportedPaymentMethods.Remove(supportedPaymentMethod);
                }
            }
            if (supportedPaymentMethods.Count == 0)
            {
                throw new BitpayHttpException(400, "No derivation strategy are available now for this store");
            }
            var entity = new InvoiceEntity
            {
                InvoiceTime = DateTimeOffset.UtcNow
            };

            entity.SetSupportedPaymentMethods(supportedPaymentMethods.Select(s => s.SupportedPaymentMethod));

            var storeBlob       = store.GetStoreBlob();
            Uri notificationUri = Uri.IsWellFormedUriString(invoice.NotificationURL, UriKind.Absolute) ? new Uri(invoice.NotificationURL, UriKind.Absolute) : null;

            if (notificationUri == null || (notificationUri.Scheme != "http" && notificationUri.Scheme != "https")) //TODO: Filer non routable addresses ?
            {
                notificationUri = null;
            }
            EmailAddressAttribute emailValidator = new EmailAddressAttribute();

            entity.ExpirationTime       = entity.InvoiceTime.AddMinutes(storeBlob.InvoiceExpiration);
            entity.MonitoringExpiration = entity.ExpirationTime + TimeSpan.FromMinutes(storeBlob.MonitoringExpiration);
            entity.OrderId               = invoice.OrderId;
            entity.ServerUrl             = serverUrl;
            entity.FullNotifications     = invoice.FullNotifications || invoice.ExtendedNotifications;
            entity.ExtendedNotifications = invoice.ExtendedNotifications;
            entity.NotificationURL       = notificationUri?.AbsoluteUri;
            entity.BuyerInformation      = Map <Invoice, BuyerInformation>(invoice);
            //Another way of passing buyer info to support
            FillBuyerInfo(invoice.Buyer, entity.BuyerInformation);
            if (entity?.BuyerInformation?.BuyerEmail != null)
            {
                if (!EmailValidator.IsEmail(entity.BuyerInformation.BuyerEmail))
                {
                    throw new BitpayHttpException(400, "Invalid email");
                }
                entity.RefundMail = entity.BuyerInformation.BuyerEmail;
            }
            entity.ProductInformation = Map <Invoice, ProductInformation>(invoice);
            entity.RedirectURL        = invoice.RedirectURL ?? store.StoreWebsite;
            entity.Status             = "new";
            entity.SpeedPolicy        = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            var methods = supportedPaymentMethods
                          .Select(async o =>
            {
                var rate = await storeBlob.ApplyRateRules(o.Network, _RateProviders.GetRateProvider(o.Network, false)).GetRateAsync(invoice.Currency);
                PaymentMethod paymentMethod = new PaymentMethod();
                paymentMethod.ParentEntity  = entity;
                paymentMethod.SetId(o.SupportedPaymentMethod.PaymentId);
                paymentMethod.Rate = rate;
                var paymentDetails = await o.Handler.CreatePaymentMethodDetails(o.SupportedPaymentMethod, paymentMethod, o.Network);
                if (storeBlob.NetworkFeeDisabled)
                {
                    paymentDetails.SetNoTxFee();
                }
                paymentMethod.SetPaymentMethodDetails(paymentDetails);
#pragma warning disable CS0618
                if (paymentMethod.GetId().IsBTCOnChain)
                {
                    entity.TxFee          = paymentMethod.TxFee;
                    entity.Rate           = paymentMethod.Rate;
                    entity.DepositAddress = paymentMethod.DepositAddress;
                }
#pragma warning restore CS0618
                return(paymentMethod);
            });
            var paymentMethods = new PaymentMethodDictionary();

            foreach (var method in methods)
            {
                paymentMethods.Add(await method);
            }
#pragma warning disable CS0618
            // Legacy Bitpay clients expect information for BTC information, even if the store do not support it
            var legacyBTCisSet = paymentMethods.Any(p => p.GetId().IsBTCOnChain);
            if (!legacyBTCisSet && _NetworkProvider.BTC != null)
            {
                var btc          = _NetworkProvider.BTC;
                var feeProvider  = ((IFeeProviderFactory)_ServiceProvider.GetService(typeof(IFeeProviderFactory))).CreateFeeProvider(btc);
                var rateProvider = storeBlob.ApplyRateRules(btc, _RateProviders.GetRateProvider(btc, false));
                if (feeProvider != null && rateProvider != null)
                {
                    var gettingFee  = feeProvider.GetFeeRateAsync();
                    var gettingRate = rateProvider.GetRateAsync(invoice.Currency);
                    entity.TxFee = GetTxFee(storeBlob, await gettingFee);
                    entity.Rate  = await gettingRate;
                }
#pragma warning restore CS0618
            }

            entity.SetPaymentMethods(paymentMethods);
            entity.PosData = invoice.PosData;
            entity         = await _InvoiceRepository.CreateInvoiceAsync(store.Id, entity, _NetworkProvider);

            _EventAggregator.Publish(new Events.InvoiceEvent(entity, 1001, "invoice_created"));
            var resp = entity.EntityToDTO(_NetworkProvider);
            return(new DataWrapper <InvoiceResponse>(resp)
            {
                Facade = "pos/invoice"
            });
        }