public TransferPaidResponseDTO TransferPaid(DateTime?eventTimestamp, StripeTransfer transfer) { _logger.Debug("Processing transfer.paid event for transfer id " + transfer.Id); var response = new TransferPaidResponseDTO(); // Don't process this transfer if we already have a deposit for the same transfer id var existingDeposit = _donationService.GetDepositByProcessorTransferId(transfer.Id); if (existingDeposit != null) { var msg = $"Deposit {existingDeposit.Id} already created for transfer {existingDeposit.ProcessorTransferId}"; _logger.Debug(msg); response.TotalTransactionCount = 0; response.Message = msg; response.Exception = new ApplicationException(msg); return(response); } // Don't process this transfer if we can't find any charges for the transfer var charges = _paymentProcessorService.GetChargesForTransfer(transfer.Id); if (charges == null || charges.Count <= 0) { var msg = "No charges found for transfer: " + transfer.Id; _logger.Debug(msg); response.TotalTransactionCount = 0; response.Message = msg; response.Exception = new ApplicationException(msg); return(response); } var depositName = DateTime.Now.ToString(BatchNameDateFormat); var paymentcharges = charges.Where(m => m.Metadata != null && m.Metadata.ContainsKey("crossroads_transaction_type") && m.Metadata["crossroads_transaction_type"].ToString() == "payment").ToList(); var donationcharges = charges.Except(paymentcharges).ToList(); if (paymentcharges.Count + donationcharges.Count != charges.Count) { var msg = "Donation and Payment charges count error for transfer: " + transfer.Id; _logger.Debug(msg); response.TotalTransactionCount = 0; response.Message = msg; response.Exception = new ApplicationException(msg); return(response); } var paymentBatch = CreateBatchDTOFromCharges(paymentcharges, depositName + "P", eventTimestamp, transfer, ref response); var donationBatch = CreateBatchDTOFromCharges(donationcharges, depositName + "D", eventTimestamp, transfer, ref response); var stripeTotalFees = paymentBatch.BatchFeeTotal + donationBatch.BatchFeeTotal; // Create the deposit var deposit = new DepositDTO { // Account number must be non-null, and non-empty; using a single space to fulfill this requirement AccountNumber = " ", BatchCount = paymentBatch.ItemCount > 0 && donationBatch.ItemCount > 0 ? 2 : 1, DepositDateTime = DateTime.Now, DepositName = depositName, // This is the amount from Stripe - will show out of balance if does not match batch total above DepositTotalAmount = ((transfer.Amount / Constants.StripeDecimalConversionValue) + (stripeTotalFees / Constants.StripeDecimalConversionValue)), ProcessorFeeTotal = stripeTotalFees / Constants.StripeDecimalConversionValue, DepositAmount = transfer.Amount / Constants.StripeDecimalConversionValue, Exported = false, Notes = null, ProcessorTransferId = transfer.Id }; try { response.Deposit = _donationService.CreateDeposit(deposit); } catch (Exception e) { _logger.Error("Failed to create batch deposit", e); throw; } // Create the batch, with the above deposit id paymentBatch.DepositId = response.Deposit.Id; donationBatch.DepositId = response.Deposit.Id; //donation Batch try { if (donationBatch.ItemCount > 0) { response.Batch.Add(_donationService.CreateDonationBatch(donationBatch)); } } catch (Exception e) { _logger.Error("Failed to create donation batch", e); throw; } // payment Batch try { if (paymentBatch.ItemCount > 0) { response.Batch.Add(_paymentService.CreatePaymentBatch(paymentBatch)); } } catch (Exception e) { _logger.Error("Failed to create payment batch", e); throw; } return(response); }
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); }