<commit_before>#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.HostedServices;
using BTCPayServer.Logging;
using BTCPayServer.Models;
using BTCPayServer.Models.PaymentRequestViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Rating;
using BTCPayServer.Security;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.PaymentRequests;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using BTCPayServer.Validation;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using NBitpayClient;
using BitpayCreateInvoiceRequest = BTCPayServer.Models.BitpayCreateInvoiceRequest;
using StoreData = BTCPayServer.Data.StoreData;

namespace BTCPayServer.Controllers
{
    [Filters.BitpayAPIConstraint(false)]
    public partial class UIInvoiceController : Controller
    {
        readonly InvoiceRepository _InvoiceRepository;
        readonly RateFetcher _RateProvider;
        readonly StoreRepository _StoreRepository;
        readonly UserManager<ApplicationUser> _UserManager;
        private readonly CurrencyNameTable _CurrencyNameTable;
        readonly EventAggregator _EventAggregator;
        readonly BTCPayNetworkProvider _NetworkProvider;
        private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
        private readonly ApplicationDbContextFactory _dbContextFactory;
        private readonly PullPaymentHostedService _paymentHostedService;
        private readonly LanguageService _languageService;
        private readonly ExplorerClientProvider _ExplorerClients;
        private readonly UIWalletsController _walletsController;
        private readonly LinkGenerator _linkGenerator;

        public WebhookSender WebhookNotificationManager { get; }

        public UIInvoiceController(
            InvoiceRepository invoiceRepository,
            CurrencyNameTable currencyNameTable,
            UserManager<ApplicationUser> userManager,
            RateFetcher rateProvider,
            StoreRepository storeRepository,
            EventAggregator eventAggregator,
            ContentSecurityPolicies csp,
            BTCPayNetworkProvider networkProvider,
            PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
            ApplicationDbContextFactory dbContextFactory,
            PullPaymentHostedService paymentHostedService,
            WebhookSender webhookNotificationManager,
            LanguageService languageService,
            ExplorerClientProvider explorerClients,
            UIWalletsController walletsController,
            LinkGenerator linkGenerator)
        {
            _CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
            _StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
            _InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
            _RateProvider = rateProvider ?? throw new ArgumentNullException(nameof(rateProvider));
            _UserManager = userManager;
            _EventAggregator = eventAggregator;
            _NetworkProvider = networkProvider;
            _paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
            _dbContextFactory = dbContextFactory;
            _paymentHostedService = paymentHostedService;
            WebhookNotificationManager = webhookNotificationManager;
            _languageService = languageService;
            this._ExplorerClients = explorerClients;
            _walletsController = walletsController;
            _linkGenerator = linkGenerator;
        }


        internal async Task<DataWrapper<InvoiceResponse>> CreateInvoiceCore(BitpayCreateInvoiceRequest invoice,
            StoreData store, string serverUrl, List<string>? additionalTags = null,
            CancellationToken cancellationToken = default, Action<InvoiceEntity>? entityManipulator = null)
        {
            var entity = await CreateInvoiceCoreRaw(invoice, store, serverUrl, additionalTags, cancellationToken, entityManipulator);
            var resp = entity.EntityToDTO();
            return new DataWrapper<InvoiceResponse>(resp) { Facade = "pos/invoice" };
        }

        internal async Task<InvoiceEntity> CreateInvoiceCoreRaw(BitpayCreateInvoiceRequest invoice, StoreData store, string serverUrl, List<string>? additionalTags = null, CancellationToken cancellationToken = default,  Action<InvoiceEntity>? entityManipulator = null)
        {
            var storeBlob = store.GetStoreBlob();
            var entity = _InvoiceRepository.CreateNewInvoice();
            entity.ExpirationTime = invoice.ExpirationTime is DateTimeOffset v ? v : entity.InvoiceTime + storeBlob.InvoiceExpiration;
            entity.MonitoringExpiration = entity.ExpirationTime + storeBlob.MonitoringExpiration;
            if (entity.ExpirationTime - TimeSpan.FromSeconds(30.0) < entity.InvoiceTime)
            {
                throw new BitpayHttpException(400, "The expirationTime is set too soon");
            }
            entity.Metadata.OrderId = invoice.OrderId;
            entity.Metadata.PosData = invoice.PosData;
            entity.ServerUrl = serverUrl;
            entity.FullNotifications = invoice.FullNotifications || invoice.ExtendedNotifications;
            entity.ExtendedNotifications = invoice.ExtendedNotifications;
            entity.NotificationURLTemplate = invoice.NotificationURL;
            entity.NotificationEmail = invoice.NotificationEmail;
            if (additionalTags != null)
                entity.InternalTags.AddRange(additionalTags);
            FillBuyerInfo(invoice, entity);

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

            entity.Metadata.ItemCode = invoice.ItemCode;
            entity.Metadata.ItemDesc = invoice.ItemDesc;
            entity.Metadata.Physical = invoice.Physical;
            entity.Metadata.TaxIncluded = invoice.TaxIncluded;
            entity.Currency = invoice.Currency;
            if (price is decimal vv)
            {
                entity.Price = vv;
                entity.Type = InvoiceType.Standard;
            }
            else
            {
                entity.Price = 0m;
                entity.Type = InvoiceType.TopUp;
            }

            entity.RedirectURLTemplate = invoice.RedirectURL ?? store.StoreWebsite;
            entity.RedirectAutomatically =
                invoice.RedirectAutomatically.GetValueOrDefault(storeBlob.RedirectAutomatically);
            entity.RequiresRefundEmail = invoice.RequiresRefundEmail;
            entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            IPaymentFilter? excludeFilter = null;
            if (invoice.PaymentCurrencies?.Any() is true)
            {
                invoice.SupportedTransactionCurrencies ??=
                    new Dictionary<string, InvoiceSupportedTransactionCurrency>();
                foreach (string paymentCurrency in invoice.PaymentCurrencies)
                {
                    invoice.SupportedTransactionCurrencies.TryAdd(paymentCurrency,
                        new InvoiceSupportedTransactionCurrency() { Enabled = true });
                }
            }
            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)
                                                            .Where(c => c != null)
                                                            .ToHashSet();
                excludeFilter = PaymentFilter.Where(p => !supportedTransactionCurrencies.Contains(p));
            }
            entity.PaymentTolerance = storeBlob.PaymentTolerance;
            entity.DefaultPaymentMethod = invoice.DefaultPaymentMethod;
            entity.RequiresRefundEmail = invoice.RequiresRefundEmail;

            return await CreateInvoiceCoreRaw(entity, store, excludeFilter, null, cancellationToken, entityManipulator);
        }

        internal async Task<InvoiceEntity> CreatePaymentRequestInvoice(ViewPaymentRequestViewModel pr, decimal? amount, StoreData storeData, HttpRequest request, CancellationToken cancellationToken)
        {
            if (pr.AllowCustomPaymentAmounts && amount != null)
                amount = Math.Min(pr.AmountDue, amount.Value);
            else
                amount = pr.AmountDue;
            var redirectUrl = _linkGenerator.PaymentRequestLink(pr.Id, request.Scheme, request.Host, request.PathBase);

            var invoiceMetadata =
                new InvoiceMetadata
                {
                    OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(pr.Id),
                    PaymentRequestId = pr.Id,
                    BuyerEmail = pr.Email
                };

            var invoiceRequest =
                new CreateInvoiceRequest
                {
                    Metadata = invoiceMetadata.ToJObject(),
                    Currency = pr.Currency,
                    Amount = amount,
                    Checkout = { RedirectURL = redirectUrl }
                };

            var additionalTags = new List<string> { PaymentRequestRepository.GetInternalTag(pr.Id) };
            return await CreateInvoiceCoreRaw(invoiceRequest, storeData, request.GetAbsoluteRoot(), additionalTags, cancellationToken);
        }<commit_msg>Do not enable receipts for payment requests (#4342)<commit_after>#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Extensions;
using BTCPayServer.Client.Models;
using BTCPayServer.Data;
using BTCPayServer.Events;
using BTCPayServer.HostedServices;
using BTCPayServer.Logging;
using BTCPayServer.Models;
using BTCPayServer.Models.PaymentRequestViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Rating;
using BTCPayServer.Security;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.PaymentRequests;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using BTCPayServer.Validation;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using NBitpayClient;
using BitpayCreateInvoiceRequest = BTCPayServer.Models.BitpayCreateInvoiceRequest;
using StoreData = BTCPayServer.Data.StoreData;

namespace BTCPayServer.Controllers
{
    [Filters.BitpayAPIConstraint(false)]
    public partial class UIInvoiceController : Controller
    {
        readonly InvoiceRepository _InvoiceRepository;
        readonly RateFetcher _RateProvider;
        readonly StoreRepository _StoreRepository;
        readonly UserManager<ApplicationUser> _UserManager;
        private readonly CurrencyNameTable _CurrencyNameTable;
        readonly EventAggregator _EventAggregator;
        readonly BTCPayNetworkProvider _NetworkProvider;
        private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
        private readonly ApplicationDbContextFactory _dbContextFactory;
        private readonly PullPaymentHostedService _paymentHostedService;
        private readonly LanguageService _languageService;
        private readonly ExplorerClientProvider _ExplorerClients;
        private readonly UIWalletsController _walletsController;
        private readonly LinkGenerator _linkGenerator;

        public WebhookSender WebhookNotificationManager { get; }

        public UIInvoiceController(
            InvoiceRepository invoiceRepository,
            CurrencyNameTable currencyNameTable,
            UserManager<ApplicationUser> userManager,
            RateFetcher rateProvider,
            StoreRepository storeRepository,
            EventAggregator eventAggregator,
            ContentSecurityPolicies csp,
            BTCPayNetworkProvider networkProvider,
            PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
            ApplicationDbContextFactory dbContextFactory,
            PullPaymentHostedService paymentHostedService,
            WebhookSender webhookNotificationManager,
            LanguageService languageService,
            ExplorerClientProvider explorerClients,
            UIWalletsController walletsController,
            LinkGenerator linkGenerator)
        {
            _CurrencyNameTable = currencyNameTable ?? throw new ArgumentNullException(nameof(currencyNameTable));
            _StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
            _InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
            _RateProvider = rateProvider ?? throw new ArgumentNullException(nameof(rateProvider));
            _UserManager = userManager;
            _EventAggregator = eventAggregator;
            _NetworkProvider = networkProvider;
            _paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
            _dbContextFactory = dbContextFactory;
            _paymentHostedService = paymentHostedService;
            WebhookNotificationManager = webhookNotificationManager;
            _languageService = languageService;
            this._ExplorerClients = explorerClients;
            _walletsController = walletsController;
            _linkGenerator = linkGenerator;
        }


        internal async Task<DataWrapper<InvoiceResponse>> CreateInvoiceCore(BitpayCreateInvoiceRequest invoice,
            StoreData store, string serverUrl, List<string>? additionalTags = null,
            CancellationToken cancellationToken = default, Action<InvoiceEntity>? entityManipulator = null)
        {
            var entity = await CreateInvoiceCoreRaw(invoice, store, serverUrl, additionalTags, cancellationToken, entityManipulator);
            var resp = entity.EntityToDTO();
            return new DataWrapper<InvoiceResponse>(resp) { Facade = "pos/invoice" };
        }

        internal async Task<InvoiceEntity> CreateInvoiceCoreRaw(BitpayCreateInvoiceRequest invoice, StoreData store, string serverUrl, List<string>? additionalTags = null, CancellationToken cancellationToken = default,  Action<InvoiceEntity>? entityManipulator = null)
        {
            var storeBlob = store.GetStoreBlob();
            var entity = _InvoiceRepository.CreateNewInvoice();
            entity.ExpirationTime = invoice.ExpirationTime is DateTimeOffset v ? v : entity.InvoiceTime + storeBlob.InvoiceExpiration;
            entity.MonitoringExpiration = entity.ExpirationTime + storeBlob.MonitoringExpiration;
            if (entity.ExpirationTime - TimeSpan.FromSeconds(30.0) < entity.InvoiceTime)
            {
                throw new BitpayHttpException(400, "The expirationTime is set too soon");
            }
            entity.Metadata.OrderId = invoice.OrderId;
            entity.Metadata.PosData = invoice.PosData;
            entity.ServerUrl = serverUrl;
            entity.FullNotifications = invoice.FullNotifications || invoice.ExtendedNotifications;
            entity.ExtendedNotifications = invoice.ExtendedNotifications;
            entity.NotificationURLTemplate = invoice.NotificationURL;
            entity.NotificationEmail = invoice.NotificationEmail;
            if (additionalTags != null)
                entity.InternalTags.AddRange(additionalTags);
            FillBuyerInfo(invoice, entity);

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

            entity.Metadata.ItemCode = invoice.ItemCode;
            entity.Metadata.ItemDesc = invoice.ItemDesc;
            entity.Metadata.Physical = invoice.Physical;
            entity.Metadata.TaxIncluded = invoice.TaxIncluded;
            entity.Currency = invoice.Currency;
            if (price is decimal vv)
            {
                entity.Price = vv;
                entity.Type = InvoiceType.Standard;
            }
            else
            {
                entity.Price = 0m;
                entity.Type = InvoiceType.TopUp;
            }

            entity.RedirectURLTemplate = invoice.RedirectURL ?? store.StoreWebsite;
            entity.RedirectAutomatically =
                invoice.RedirectAutomatically.GetValueOrDefault(storeBlob.RedirectAutomatically);
            entity.RequiresRefundEmail = invoice.RequiresRefundEmail;
            entity.SpeedPolicy = ParseSpeedPolicy(invoice.TransactionSpeed, store.SpeedPolicy);

            IPaymentFilter? excludeFilter = null;
            if (invoice.PaymentCurrencies?.Any() is true)
            {
                invoice.SupportedTransactionCurrencies ??=
                    new Dictionary<string, InvoiceSupportedTransactionCurrency>();
                foreach (string paymentCurrency in invoice.PaymentCurrencies)
                {
                    invoice.SupportedTransactionCurrencies.TryAdd(paymentCurrency,
                        new InvoiceSupportedTransactionCurrency() { Enabled = true });
                }
            }
            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)
                                                            .Where(c => c != null)
                                                            .ToHashSet();
                excludeFilter = PaymentFilter.Where(p => !supportedTransactionCurrencies.Contains(p));
            }
            entity.PaymentTolerance = storeBlob.PaymentTolerance;
            entity.DefaultPaymentMethod = invoice.DefaultPaymentMethod;
            entity.RequiresRefundEmail = invoice.RequiresRefundEmail;

            return await CreateInvoiceCoreRaw(entity, store, excludeFilter, null, cancellationToken, entityManipulator);
        }

        internal async Task<InvoiceEntity> CreatePaymentRequestInvoice(ViewPaymentRequestViewModel pr, decimal? amount, StoreData storeData, HttpRequest request, CancellationToken cancellationToken)
        {
            if (pr.AllowCustomPaymentAmounts && amount != null)
                amount = Math.Min(pr.AmountDue, amount.Value);
            else
                amount = pr.AmountDue;
            var redirectUrl = _linkGenerator.PaymentRequestLink(pr.Id, request.Scheme, request.Host, request.PathBase);

            var invoiceMetadata =
                new InvoiceMetadata
                {
                    OrderId = PaymentRequestRepository.GetOrderIdForPaymentRequest(pr.Id),
                    PaymentRequestId = pr.Id,
                    BuyerEmail = pr.Email
                };

            var invoiceRequest =
                new CreateInvoiceRequest
                {
                    Metadata = invoiceMetadata.ToJObject(),
                    Currency = pr.Currency,
                    Amount = amount,
                    Checkout = { RedirectURL = redirectUrl },
                    Receipt = new InvoiceDataBase.ReceiptOptions { Enabled = false }
                };

            var additionalTags = new List<string> { PaymentRequestRepository.GetInternalTag(pr.Id) };
            return await CreateInvoiceCoreRaw(invoiceRequest, storeData, request.GetAbsoluteRoot(), additionalTags, cancellationToken);
        }
{'fraction_non_alphanumeric': 0.062328364064338955, 'fraction_numerical': 0.0010788544527265593, 'mean_word_length': 2.5639636490737505, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5555522', 'n_tokens_mistral': 4741, 'n_tokens_neox': 4581, 'n_words': 958}