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); }