private PaymentDTO HandlePaymentNotFoundException(StripeTransfer transfer, StripeRefund refund, string paymentId, PaymentNotFoundException e, StripeCharge charge) { PaymentDTO payment; if (refund != null) { _logger.Warn($"Payment not found for refund {paymentId} on transfer {transfer.Id} - may be a refund due to a bank account error"); // If this is a refund that doesn't exist in MP, create it, assuming it is a refund due to a bank account error (NSF, etc) if (_paymentService.CreatePaymentForBankAccountErrorRefund(refund) != null) { payment = _paymentService.GetPaymentByTransactionCode(paymentId); payment.Status = DonationStatus.Declined; // make sure to set the correct status. _logger.Debug($"Updating charge id {charge.Id} to Declined status"); _paymentService.UpdatePaymentStatus(payment.PaymentId, _donationStatusDeclined, refund.Data[0].BalanceTransaction.Created); } else { _logger.Error($"Payment not found for refund {paymentId} on transfer {transfer.Id}, probably not a bank account error", e); // ReSharper disable once PossibleIntendedRethrow throw e; } } else { _logger.Warn($"Payment not found for charge {paymentId} on transfer {transfer.Id} - may be an ACH recurring gift that has not yet processed"); _logger.Error($"Donation not found for charge {charge.Id} on transfer {transfer.Id}, charge does not appear to be related to an ACH recurring gift"); // ReSharper disable once PossibleIntendedRethrow throw e; } return(payment); }
private DonationDTO HandleDonationNotFoundException(StripeTransfer transfer, StripeRefund refund, string paymentId, DonationNotFoundException e, StripeCharge charge) { DonationDTO donation; if (refund != null) { _logger.Warn($"Payment not found for refund {paymentId} on transfer {transfer.Id} - may be a refund due to a bank account error"); // If this is a refund that doesn't exist in MP, create it, assuming it is a refund due to a bank account error (NSF, etc) if (_donationService.CreateDonationForBankAccountErrorRefund(refund) != null) { donation = _donationService.GetDonationByProcessorPaymentId(paymentId); _logger.Debug($"Updating charge id {charge.Id} to Declined status"); _donationService.UpdateDonationStatus(refund.Data[0].ChargeId, _donationStatusDeclined, refund.Data[0].BalanceTransaction.Created); } else { _logger.Error($"Payment not found for refund {paymentId} on transfer {transfer.Id}, probably not a bank account error", e); // ReSharper disable once PossibleIntendedRethrow throw e; } } else { _logger.Warn($"Payment not found for charge {paymentId} on transfer {transfer.Id} - may be an ACH recurring gift that has not yet processed"); var stripeCharge = _paymentProcessorService.GetCharge(charge.Id); if (stripeCharge?.Source != null && "bank_account".Equals(stripeCharge.Source.Object) && stripeCharge.HasInvoice()) { // We're making an assumption that if an ACH payment is included in a transfer, // and if we don't have the corresponding Donation in our system yet, that // this is a mistake. For instance, events delivered out of order from Stripe, and // we received the transfer.paid before the invoice.payment_succeeded. // In this scenario, we will go ahead and create the donation. if (_donationService.CreateDonationForInvoice(stripeCharge.Invoice) != null) { _logger.Debug($"Creating donation for recurring gift payment {charge.Id}"); donation = _donationService.GetDonationByProcessorPaymentId(paymentId); } else { _logger.Error($"Donation not found for charge {charge.Id} on transfer {transfer.Id}, and failed to create a donation", e); // ReSharper disable once PossibleIntendedRethrow throw e; } } else { _logger.Error($"Donation not found for charge {charge.Id} on transfer {transfer.Id}, charge does not appear to be related to an ACH recurring gift"); // ReSharper disable once PossibleIntendedRethrow throw e; } } return(donation); }
public StripeTransfer CreateTransfer(Reimbursement reimbursement) { StripeConfiguration.SetApiKey(_stripe_key); var options = new StripeTransferCreateOptions { // TODO }; var service = new StripeTransferService(); StripeTransfer transfer = service.Create(options); return(transfer); }
internal static Transfer TransformStripeTransferToTransfer(StripeTransfer stripeTransfer) { var transfer = new Transfer { TransferID = stripeTransfer.Id, Amount = Sahara.Core.Common.Methods.Billing.ConvertStripeAmountToDecimals(stripeTransfer.Amount.ToString()), Status = stripeTransfer.Status.ToString(), Type = stripeTransfer.Type.ToString(), Date = stripeTransfer.Date, Created = stripeTransfer.Created, }; if (stripeTransfer.Description != null) { transfer.Description = stripeTransfer.Description; } if (stripeTransfer.Destination != null) { transfer.Destination = stripeTransfer.Destination; } if (stripeTransfer.DestinationPayment != null) { transfer.DestinationPayment = stripeTransfer.DestinationPayment; } if (stripeTransfer.BalanceTransactionId != null) { transfer.BalanceTransactionID = stripeTransfer.BalanceTransactionId; } //if(stripeTransfer.BalanceTransaction != null) //{ //transfer.BalanceTransaction = TransformStripeBalanceTransactionToBalanceTransaction(stripeTransfer.BalanceTransaction); //} if (stripeTransfer.FailureCode != null) { transfer.FailureCode = stripeTransfer.FailureCode; } if (stripeTransfer.FailureMessage != null) { transfer.FailureMessage = stripeTransfer.FailureMessage; } return(transfer); }
public async Task <DisperseFundsResult> TransferCreated(Transfer transfer) { _logger.LogInformation(GetLogMessage("Transfer ID: {0}"), transfer.Id); var retVal = new DisperseFundsResult() { TransferId = transfer.Id, Amount = 0, }; if (transfer.Metadata.ContainsKey("invoice-id")) { string invoiceId = transfer.Metadata["invoice-id"]; _logger.LogDebug(GetLogMessage("Invoice Id: {0}"), invoiceId); retVal.InvoiceId = invoiceId; var stripeTransfer = new StripeTransfer { ObjectState = ObjectState.Added, Id = transfer.Id, Amount = transfer.Amount / 100m, Created = transfer.Created, DestinationId = transfer.DestinationId, AmountReversed = transfer.AmountReversed / 100m, Description = transfer.Description, InvoiceTransfer = new InvoiceTransfer() { ObjectState = ObjectState.Added, InvoiceId = invoiceId, TransferId = transfer.Id, } }; stripeTransfer.InjectFrom(transfer); var firstUpdate = _transfers.InsertOrUpdateGraph(stripeTransfer, true); _logger.LogDebug(GetLogMessage("{0} Transfer Records Created"), firstUpdate); if (firstUpdate > 0) { stripeTransfer.ObjectState = ObjectState.Modified; if (transfer.Metadata.ContainsKey("individual-payout-ids")) { if (Guid.TryParse(transfer.Metadata["person-id"], out var personId)) { _logger.LogInformation(GetLogMessage("This is an individual payout transaction request")); var payoutIds = transfer.Metadata["individual-payout-ids"].Split(",") .Select(Guid.Parse).ToArray(); _logger.LogDebug(GetLogMessage("Person Payout Ids: {0}"), payoutIds); var payouts = await _individualPayoutIntents.Queryable() .Where(x => payoutIds.Contains(x.Id) && x.InvoiceTransferId == null) .ToListAsync(); _logger.LogDebug(GetLogMessage("{0} Individual Payouts to process"), payouts.Count); foreach (var x in payouts) { _logger.LogInformation(GetLogMessage("Intent Id: {0}; Transfer Id: {1}"), x.Id, transfer.Id); x.InvoiceTransferId = transfer.Id; x.ObjectState = ObjectState.Modified; _individualPayoutIntents.InsertOrUpdateGraph(x); } var updatedPayouts = _individualPayoutIntents.Commit(); _logger.LogDebug(GetLogMessage("{0} Individual Payouts records updated"), updatedPayouts); if (updatedPayouts > 0) { retVal.Succeeded = true; retVal.Amount = stripeTransfer.Amount; await Task.Run(() => { RaiseEvent(new FundsDispersedToPersonEvent() { InvoiceId = invoiceId, PersonId = personId, Amount = stripeTransfer.Amount }); }); } } } var organizationPayouts = 0; if (transfer.Metadata.ContainsKey("organization-payout-ids")) { if (Guid.TryParse(transfer.Metadata["organization-id"], out var organizationId)) { _logger.LogInformation(GetLogMessage("Processing Organization Payouts")); var payoutIds = transfer.Metadata["organization-payout-ids"].Split(",").Select(Guid.Parse).ToArray(); var payouts = await _organizationPayoutIntents.Queryable() .Where(x => payoutIds.Contains(x.Id) && x.OrganizationId == organizationId && x.InvoiceTransferId == null) .ToListAsync(); _logger.LogDebug(GetLogMessage("PayoutIds:{0}"), payoutIds); foreach (var x in payouts) { _logger.LogInformation(GetLogMessage("Intent Id: {0}; Transfer Id: {1}"), x.Id, x.InvoiceTransferId); x.InvoiceTransferId = transfer.Id; x.ObjectState = ObjectState.Modified; _organizationPayoutIntents.InsertOrUpdateGraph(x); } var records = _organizationPayoutIntents.Commit(); _logger.LogDebug(GetLogMessage("{0} Organization Payout records updated"), records); if (records > 0) { organizationPayouts++; } _logger.LogDebug(GetLogMessage("{0} Organization payouts updated"), organizationPayouts); if (organizationPayouts > 0) { retVal.Succeeded = true; retVal.Amount = stripeTransfer.Amount; await Task.Run(() => { RaiseEvent(new FundsDispersedToOrganizationEvent() { InvoiceId = invoiceId, OrganizationId = organizationId, Amount = stripeTransfer.Amount }); }); } } ; } } else { _logger.LogDebug(GetLogMessage("First update failed")); } } else { _logger.LogDebug(GetLogMessage("Not an invoice transfer")); } return(retVal); }
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); }
public void TestTransferPaid() { var transfer = new StripeTransfer { Id = "tx9876", Amount = 1443 }; var e = new StripeEvent { LiveMode = true, Type = "transfer.paid", Created = DateTime.Now.AddDays(-1), Data = new StripeEventData { Object = JObject.FromObject(transfer) } }; var paymentmetadata = new Dictionary <string, object> { { "crossroads_transaction_type", "payment" } }; var donationmetadata = new Dictionary <string, object> { { "crossroads_transaction_type", "donation" } }; var dateCreated = DateTime.Now; var charges = new List <StripeCharge> { new StripeCharge { Id = "ch111", Amount = 111, Fee = 1, Type = "charge", Created = dateCreated.AddDays(1), Metadata = paymentmetadata }, new StripeCharge { Id = "ch222", Amount = 222, Fee = 2, Type = "charge", Created = dateCreated.AddDays(2), Metadata = paymentmetadata }, new StripeCharge { Id = "ch333", Amount = 333, Fee = 3, Type = "charge", Created = dateCreated.AddDays(3), Metadata = donationmetadata }, new StripeCharge { Id = "ch777", Amount = 777, Fee = 7, Type = "charge", Created = dateCreated.AddDays(4), Metadata = donationmetadata }, new StripeCharge { Id = "ch888", Amount = 888, Fee = 8, Type = "charge", Created = dateCreated.AddDays(5), Metadata = donationmetadata }, new StripeCharge { Id = "re999", Amount = 999, Fee = 9, Type = "payment_refund", Created = dateCreated.AddDays(8), Metadata = donationmetadata }, new StripeCharge { Id = "ch444", Amount = 444, Fee = 4, Type = "charge", Created = dateCreated.AddDays(6), Metadata = donationmetadata }, new StripeCharge { Id = "ch555", Amount = 555, Fee = 5, Type = "refund", Created = dateCreated.AddDays(7), Metadata = donationmetadata } }; _paymentService.Setup(mocked => mocked.GetPaymentByTransactionCode("ch111")).Returns(new PaymentDTO { PaymentId = 1111, BatchId = null, Status = DonationStatus.Pending }); _paymentService.Setup(mocked => mocked.GetPaymentByTransactionCode("ch222")).Returns(new PaymentDTO { PaymentId = 2222, BatchId = null, Status = DonationStatus.Pending }); _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch333")).Returns(new DonationDTO { Id = "3333", BatchId = null, Status = DonationStatus.Pending }); _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch444")).Throws(new Exception("Not gonna do it, wouldn't be prudent.")); _paymentProcessorService.Setup(mocked => mocked.GetChargeRefund("ch555")).Returns(new StripeRefund { Data = new List <StripeRefundData> { new StripeRefundData() { Id = "ch555", Amount = "987", Charge = new StripeCharge { Id = "re_123456" } } } }); _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch555")).Returns(new DonationDTO { Id = "5555", BatchId = 1984 }); _donationService.Setup(mocked => mocked.GetDonationBatch(1984)).Returns(new DonationBatchDTO { Id = 5150, ProcessorTransferId = "OU812" }); var stripeRefundData = new StripeRefundData { Id = "re999", Amount = "999", ChargeId = "ch999", Charge = new StripeCharge { Id = "ch999" }, BalanceTransaction = new StripeBalanceTransaction { Created = DateTime.Now } }; var refund = new StripeRefund { Data = new List <StripeRefundData> { stripeRefundData } }; _paymentProcessorService.Setup(mocked => mocked.GetRefund("re999")).Returns(stripeRefundData); _donationService.Setup( mocked => mocked.CreateDonationForBankAccountErrorRefund(It.Is <StripeRefund>(r => r.Data != null && r.Data.Count == 1 && r.Data[0].Equals(stripeRefundData)))) .Returns(876); var firstRefund = true; _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("re999")).Returns(() => { if (firstRefund) { firstRefund = false; throw (new DonationNotFoundException("re999")); } return(new DonationDTO() { Id = "9999", BatchId = null, Amount = 999 }); }); _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch777")).Returns(new DonationDTO { Id = "7777", BatchId = 2112 }); _donationService.Setup(mocked => mocked.GetDonationBatch(2112)).Returns(new DonationBatchDTO { Id = 2112, ProcessorTransferId = null }); var first = true; _donationService.Setup(mocked => mocked.GetDonationByProcessorPaymentId("ch888")).Returns(() => { if (first) { first = false; throw (new DonationNotFoundException("ch888")); } return(new DonationDTO() { Id = "8888", BatchId = null, Amount = 888, Status = DonationStatus.Declined }); }); _donationService.Setup(mocked => mocked.UpdateDonationStatus(refund.Data[0].ChargeId, 777, refund.Data[0].BalanceTransaction.Created, null)).Returns(9999); var invoice = new StripeInvoice { Amount = 100, Id = "in_888" }; _paymentProcessorService.Setup(mocked => mocked.GetCharge("ch888")).Returns(new StripeCharge { Source = new StripeSource { Object = "bank_account" }, Invoice = invoice }); _donationService.Setup(mocked => mocked.CreateDonationForInvoice(invoice)).Returns(88); _paymentProcessorService.Setup(mocked => mocked.GetChargesForTransfer("tx9876")).Returns(charges); _donationService.Setup(mocked => mocked.GetDepositByProcessorTransferId("tx9876")).Returns((DepositDTO)null); _donationService.Setup( mocked => mocked.CreatePaymentProcessorEventError(e, It.IsAny <StripeEventResponseDTO>())); _paymentService.Setup(mocked => mocked.UpdatePaymentStatus(1111, 999, e.Created, null)).Returns(1111); _paymentService.Setup(mocked => mocked.UpdatePaymentStatus(2222, 999, e.Created, null)).Returns(2222); _donationService.Setup(mocked => mocked.UpdateDonationStatus(3333, 999, e.Created, null)).Returns(3333); _donationService.Setup(mocked => mocked.UpdateDonationStatus(7777, 999, e.Created, null)).Returns(7777); _donationService.Setup(mocked => mocked.UpdateDonationStatus(9999, 999, e.Created, null)).Returns(9999); _donationService.Setup(mocked => mocked.CreateDeposit(It.IsAny <DepositDTO>())).Returns( (DepositDTO o) => { o.Id = 98765; return(o); }); _donationService.Setup(mocked => mocked.CreateDonationBatch(It.IsAny <DonationBatchDTO>())).Returns((DonationBatchDTO o) => o); _paymentService.Setup(mocked => mocked.CreatePaymentBatch(It.IsAny <DonationBatchDTO>())).Returns((DonationBatchDTO o) => o); var result = _fixture.ProcessStripeEvent(e); Assert.IsNotNull(result); Assert.IsInstanceOf <TransferPaidResponseDTO>(result); var tp = (TransferPaidResponseDTO)result; Assert.AreEqual(8, tp.TotalTransactionCount); Assert.AreEqual(6, tp.SuccessfulUpdates.Count); Assert.AreEqual(charges.Take(6).Select(charge => charge.Id), tp.SuccessfulUpdates); Assert.AreEqual(2, tp.FailedUpdates.Count); Assert.AreEqual("ch555", tp.FailedUpdates[1].Key); Assert.AreEqual("ch444", tp.FailedUpdates[0].Key); Assert.AreEqual("Not gonna do it, wouldn't be prudent.", tp.FailedUpdates[0].Value); Assert.IsNotNull(tp.Batch); Assert.IsNotNull(tp.Deposit); Assert.IsNotNull(tp.Exception); _donationService.Verify(mocked => mocked.CreateDonationBatch(It.Is <DonationBatchDTO>(o => o.BatchName.Matches(@"MP\d{12}D") && o.SetupDateTime == o.FinalizedDateTime && o.BatchEntryType == 555 && o.ItemCount == 4 && o.BatchTotalAmount == ((333 + 777 + 888 + 999) / Constants.StripeDecimalConversionValue) && o.Donations != null && o.Donations.Count == 4 && o.DepositId == 98765 && o.ProcessorTransferId.Equals("tx9876") ))); _donationService.Verify(mocked => mocked.CreateDeposit(It.Is <DepositDTO>(o => o.DepositName.Matches(@"MP\d{12}") && !o.Exported && o.AccountNumber.Equals(" ") && o.BatchCount == 2 && o.DepositDateTime != null && o.DepositTotalAmount == ((transfer.Amount + 30) / Constants.StripeDecimalConversionValue) && o.ProcessorFeeTotal == (30 / Constants.StripeDecimalConversionValue) && o.DepositAmount == (transfer.Amount / Constants.StripeDecimalConversionValue) && o.Notes == null && o.ProcessorTransferId.Equals("tx9876") ))); _paymentProcessorService.VerifyAll(); _donationService.VerifyAll(); }