private DonationBatchDTO CreateBatchDTOFromCharges(List <StripeCharge> charges, string batchName, DateTime?eventTimestamp, StripeTransfer transfer, ref TransferPaidResponseDTO response)
        {
            var now = DateTime.Now;

            var batch = new DonationBatchDTO()
            {
                BatchName           = batchName,
                SetupDateTime       = now,
                BatchTotalAmount    = 0,
                ItemCount           = 0,
                BatchEntryType      = _batchEntryTypePaymentProcessor,
                FinalizedDateTime   = now,
                DepositId           = null,
                ProcessorTransferId = transfer.Id
            };

            response.TotalTransactionCount += charges.Count;

            // Sort charges so we process refunds for payments in the same transfer after the actual payment is processed
            var sortedCharges = charges.OrderBy(charge => charge.Type);

            foreach (var charge in sortedCharges)
            {
                try
                {
                    var          paymentId = charge.Id;
                    StripeRefund refund    = null;
                    if ("refund".Equals(charge.Type)) // Credit Card Refund
                    {
                        refund    = _paymentProcessorService.GetChargeRefund(charge.Id);
                        paymentId = refund.Data[0].Id;
                    }
                    else if ("payment_refund".Equals(charge.Type)) // Bank Account Refund
                    {
                        var refundData = _paymentProcessorService.GetRefund(charge.Id);
                        refund = new StripeRefund
                        {
                            Data = new List <StripeRefundData>
                            {
                                refundData
                            }
                        };
                    }
                    //TODO: Pull these out into methods?
                    if (charge.Metadata != null && charge.Metadata.ContainsKey("crossroads_transaction_type") && charge.Metadata["crossroads_transaction_type"].ToString() == "payment")
                    {
                        PaymentDTO payment;
                        try
                        {
                            payment = _paymentService.GetPaymentByTransactionCode(paymentId);
                        }
                        catch (PaymentNotFoundException e)
                        {
                            payment = HandlePaymentNotFoundException(transfer, refund, paymentId, e, charge);
                        }

                        if (payment.BatchId != null)
                        {
                            var b = _paymentService.GetPaymentBatch(payment.BatchId.Value);
                            if (string.IsNullOrWhiteSpace(b.ProcessorTransferId))
                            {
                                // If this payment exists on another batch that does not have a Stripe transfer ID, we'll move it to our batch instead
                                var msg = $"Charge {charge.Id} already exists on batch {b.Id}, moving to new batch";
                                _logger.Debug(msg);
                            }
                            else
                            {
                                // If this payment exists on another batch that has a Stripe transfer ID, skip it
                                var msg = $"Charge {charge.Id} already exists on batch {b.Id} with transfer id {b.ProcessorTransferId}";
                                _logger.Debug(msg);
                                response.FailedUpdates.Add(new KeyValuePair <string, string>(charge.Id, msg));
                                continue;
                            }
                        }

                        if (payment.Status != DonationStatus.Declined && payment.Status != DonationStatus.Refunded)
                        {
                            _logger.Debug($"Updating charge id {charge.Id} to Deposited status");
                            _paymentService.UpdatePaymentStatus(payment.PaymentId, _donationStatusDeposited, eventTimestamp);
                        }
                        else
                        {
                            _logger.Debug($"Not updating charge id {charge.Id} to Deposited status - it was already {System.Enum.GetName(typeof(DonationStatus), payment.Status)}");
                        }
                        response.SuccessfulUpdates.Add(charge.Id);
                        batch.ItemCount++;
                        batch.BatchTotalAmount += (charge.Amount / Constants.StripeDecimalConversionValue);
                        batch.Payments.Add(new PaymentDTO {
                            PaymentId = payment.PaymentId, Amount = charge.Amount, ProcessorFee = charge.Fee, BatchId = payment.BatchId, ContactId = payment.ContactId, InvoiceId = payment.InvoiceId, StripeTransactionId = payment.StripeTransactionId
                        });
                        batch.BatchFeeTotal = batch.Payments.Sum(f => f.ProcessorFee);
                    }
                    else
                    {
                        DonationDTO donation;
                        try
                        {
                            donation = _donationService.GetDonationByProcessorPaymentId(paymentId);
                        }
                        catch (DonationNotFoundException e)
                        {
                            donation = HandleDonationNotFoundException(transfer, refund, paymentId, e, charge);
                        }

                        if (donation.BatchId != null)
                        {
                            var b = _donationService.GetDonationBatch(donation.BatchId.Value);
                            if (string.IsNullOrWhiteSpace(b.ProcessorTransferId))
                            {
                                // If this donation exists on another batch that does not have a Stripe transfer ID, we'll move it to our batch instead
                                var msg = $"Charge {charge.Id} already exists on batch {b.Id}, moving to new batch";
                                _logger.Debug(msg);
                            }
                            else
                            {
                                // If this donation exists on another batch that has a Stripe transfer ID, skip it
                                var msg = $"Charge {charge.Id} already exists on batch {b.Id} with transfer id {b.ProcessorTransferId}";
                                _logger.Debug(msg);
                                response.FailedUpdates.Add(new KeyValuePair <string, string>(charge.Id, msg));
                                continue;
                            }
                        }

                        if (donation.Status != DonationStatus.Declined && donation.Status != DonationStatus.Refunded)
                        {
                            _logger.Debug($"Updating charge id {charge.Id} to Deposited status");
                            _donationService.UpdateDonationStatus(int.Parse(donation.Id), _donationStatusDeposited, eventTimestamp);
                        }
                        else
                        {
                            _logger.Debug($"Not updating charge id {charge.Id} to Deposited status - it was already {System.Enum.GetName(typeof(DonationStatus), donation.Status)}");
                        }
                        response.SuccessfulUpdates.Add(charge.Id);
                        batch.ItemCount++;
                        batch.BatchTotalAmount += (charge.Amount / Constants.StripeDecimalConversionValue);
                        batch.Donations.Add(new DonationDTO {
                            Id = donation.Id, Amount = charge.Amount, Fee = charge.Fee
                        });
                        batch.BatchFeeTotal = batch.Donations.Sum(f => f.Fee);
                    }
                }
                catch (Exception e)
                {
                    _logger.Warn("Error updating charge " + charge, e);
                    response.FailedUpdates.Add(new KeyValuePair <string, string>(charge.Id, e.Message));
                }
            }

            if (response.FailedUpdates.Count > 0)
            {
                response.Exception = new ApplicationException("Could not update all charges to 'deposited' status, see message for details");
            }



            return(batch);
        }