/// <summary> /// Creates or updates an MP Donor (and potentially creates a Contact) appropriately, based on the following logic: /// 1) If the given MpContactDonor is null, or if it does not represent an existing Contact, /// create a Contact and Donor in MP, and create Customer in the payment processor system. This /// Contact and Donor will be considered a Guest Giver, unrelated to any registered User. /// /// 2) If the given MpContactDonor is an existing mpContact, but does not have a payment processor customer, /// create a Customer in the payment processor system, then either create a new Donor with the /// payment processor Customer ID, or update the existing Donor (if any) with the id. /// /// 3) If the given MpContactDonor is an existing mpContact, and an existing Donor with a Customer ID in the /// payment processor system, simply return the given MpContactDonor. This is a fallback, put in place /// to take some of the decision logic out of the frontend on whether a new Donor needs to be created or not, /// whether a Customer needs to be created at the payment processor, etc. /// </summary> /// <param name="mpContactDonor">An existing MpContactDonor, looked up from either GetDonorForEmail or GetDonorForAuthenticatedUser. This may be null, indicating there is no existing mpContact or donor.</param> /// <param name="encryptedKey"> The encrypted routing and account number</param> /// <param name="dto"></param> /// <param name="lastName"></param> /// <param name="emailAddress">An email address to use when creating a Contact (#1 above).</param> /// <param name="paymentProcessorToken">The one-time-use token given by the payment processor - if not set, a donor will still be potentially created or updated, but will not be setup in Stripe.</param> /// <param name="setupDate">The date when the Donor is marked as setup, defaults to today's date.</param> /// <param name="firstName"></param> /// <returns></returns> public MpContactDonor CreateOrUpdateContactDonor(MpContactDonor mpContactDonor, string encryptedKey, string firstName, string lastName, string emailAddress, string paymentProcessorToken = null, DateTime?setupDate = null) { setupDate = setupDate ?? DateTime.Now; var contactDonorResponse = new MpContactDonor(); if (mpContactDonor == null || !mpContactDonor.ExistingContact) { var statementMethod = _statementMethodNone; var statementFrequency = _statementFrequencyNever; if (mpContactDonor != null && mpContactDonor.HasDetails) { contactDonorResponse.ContactId = _mpContactService.CreateContactForNewDonor(mpContactDonor); statementMethod = _statementMethodPostalMail; statementFrequency = _statementFrequencyQuarterly; } else { var displayName = _guestGiverDisplayName; if (!string.IsNullOrEmpty(firstName) && !string.IsNullOrEmpty(lastName)) { displayName = firstName; } contactDonorResponse.ContactId = _mpContactService.CreateContactForGuestGiver(emailAddress, displayName, firstName, lastName); } var donorAccount = mpContactDonor != null ? mpContactDonor.Account : null; if (!string.IsNullOrWhiteSpace(paymentProcessorToken)) { var stripeCustomer = _paymentService.CreateCustomer(paymentProcessorToken); if (donorAccount != null) { donorAccount.ProcessorAccountId = stripeCustomer.sources.data[0].id; } contactDonorResponse.ProcessorId = stripeCustomer.id; } contactDonorResponse.DonorId = _mpDonorService.CreateDonorRecord(contactDonorResponse.ContactId, contactDonorResponse.ProcessorId, setupDate.Value, statementFrequency, _statementTypeIndividual, statementMethod, donorAccount); contactDonorResponse.Email = emailAddress; _paymentService.UpdateCustomerDescription(contactDonorResponse.ProcessorId, contactDonorResponse.DonorId); if (donorAccount != null) { _mpDonorService.CreateDonorAccount(null /* gift type, not needed here */, donorAccount.RoutingNumber, donorAccount.AccountNumber.Right(4), donorAccount.EncryptedAccount, contactDonorResponse.DonorId, donorAccount.ProcessorAccountId, contactDonorResponse.ProcessorId); } } else if (!mpContactDonor.HasPaymentProcessorRecord) { contactDonorResponse.ContactId = mpContactDonor.ContactId; if (!string.IsNullOrWhiteSpace(paymentProcessorToken)) { var stripeCustomer = _paymentService.CreateCustomer(paymentProcessorToken); contactDonorResponse.ProcessorId = stripeCustomer.id; if (mpContactDonor.HasAccount) { mpContactDonor.Account.ProcessorAccountId = stripeCustomer.sources.data[0].id; } } if (mpContactDonor.ExistingDonor) { contactDonorResponse.DonorId = _mpDonorService.UpdatePaymentProcessorCustomerId(mpContactDonor.DonorId, contactDonorResponse.ProcessorId); contactDonorResponse.Email = mpContactDonor.Email; } else { if (mpContactDonor.RegisteredUser) { contactDonorResponse.DonorId = _mpDonorService.CreateDonorRecord(mpContactDonor.ContactId, contactDonorResponse.ProcessorId, setupDate.Value); var contact = _mpDonorService.GetEmailViaDonorId(contactDonorResponse.DonorId); contactDonorResponse.Email = contact.Email; } else { contactDonorResponse.DonorId = _mpDonorService.CreateDonorRecord(mpContactDonor.ContactId, contactDonorResponse.ProcessorId, setupDate.Value, _statementFrequencyNever, _statementTypeIndividual, _statementMethodNone); contactDonorResponse.Email = mpContactDonor.Email; } } if (mpContactDonor.HasAccount) { _mpDonorService.CreateDonorAccount(null /* gift type, not needed here */, mpContactDonor.Account.RoutingNumber, mpContactDonor.Account.AccountNumber.Right(4), mpContactDonor.Account.EncryptedAccount, mpContactDonor.DonorId, mpContactDonor.Account.ProcessorAccountId, mpContactDonor.ProcessorId); } if (contactDonorResponse.HasPaymentProcessorRecord) { _paymentService.UpdateCustomerDescription(contactDonorResponse.ProcessorId, contactDonorResponse.DonorId); } contactDonorResponse.RegisteredUser = mpContactDonor.RegisteredUser; } else if (mpContactDonor.HasAccount && mpContactDonor.Account.HasToken && AccountType.Checking == mpContactDonor.Account.Type) { var source = _paymentService.AddSourceToCustomer(mpContactDonor.ProcessorId, mpContactDonor.Account.Token); _mpDonorService.CreateDonorAccount(null /* gift type, not needed here */, mpContactDonor.Account.RoutingNumber, mpContactDonor.Account.AccountNumber.Right(4), mpContactDonor.Account.EncryptedAccount, mpContactDonor.DonorId, source.id, mpContactDonor.ProcessorId); contactDonorResponse = mpContactDonor; contactDonorResponse.Account.ProcessorAccountId = source.id; contactDonorResponse.Account.ProcessorId = mpContactDonor.ProcessorId; } else { contactDonorResponse = mpContactDonor; } return(contactDonorResponse); }
public CheckScannerBatch CreateDonationsForBatch(CheckScannerBatch batchDetails) { var checks = _checkScannerDao.GetChecksForBatch(batchDetails.Name); foreach (var check in checks) { if (check.Exported) { var previousError = string.IsNullOrWhiteSpace(check.Error) ? string.Empty : string.Format("Previous Error: {0}", check.Error); var msg = string.Format("Not exporting check {0} on batch {1}, it was already exported. {2}", check.Id, batchDetails.Name, previousError); _logger.Info(msg); check.Error = msg; batchDetails.ErrorChecks.Add(check); continue; } try { var contactDonor = CreateDonor(check); var account = _mpDonorService.DecryptCheckValue(check.AccountNumber); var routing = _mpDonorService.DecryptCheckValue(check.RoutingNumber); var encryptedKey = _mpDonorService.CreateHashedAccountAndRoutingNumber(account, routing); string donorAccountId = ""; if (contactDonor.Account.HasPaymentProcessorInfo() == false) { var stripeCustomer = _paymentService.CreateCustomer(null, contactDonor.DonorId + " Scanned Checks"); var stripeCustomerSource = _paymentService.AddSourceToCustomer(stripeCustomer.id, contactDonor.Account.Token); donorAccountId = _mpDonorService.CreateDonorAccount(null, routing, account.Right(4), encryptedKey, contactDonor.DonorId, stripeCustomerSource.id, stripeCustomer.id).ToString(); contactDonor.Account = new MpDonorAccount { DonorAccountId = int.Parse(donorAccountId), ProcessorId = stripeCustomer.id, ProcessorAccountId = stripeCustomerSource.id }; } else { donorAccountId = contactDonor.Account.DonorAccountId.ToString(); } //Always use the customer ID and source ID from the Donor Account, if it exists var charge = _paymentService.ChargeCustomer(contactDonor.Account.ProcessorId, contactDonor.Account.ProcessorAccountId, check.Amount, contactDonor.DonorId, check.CheckNumber); var fee = charge.BalanceTransaction != null ? charge.BalanceTransaction.Fee : null; // Mark the check as exported now, so we don't double-charge a community member. // If the CreateDonationAndDistributionRecord fails, we'll still consider it exported, but // it will be in error, and will have to be manually resolved. check.Exported = true; var programId = batchDetails.ProgramId == null ? null : batchDetails.ProgramId + ""; var donationAndDistribution = new MpDonationAndDistributionRecord { DonationAmt = check.Amount, FeeAmt = fee, DonorId = contactDonor.DonorId, ProgramId = programId, ChargeId = charge.Id, PymtType = "check", ProcessorId = contactDonor.Account.ProcessorId, SetupDate = check.CheckDate ?? (check.ScanDate ?? DateTime.Now), RegisteredDonor = contactDonor.RegisteredUser, DonorAcctId = donorAccountId, CheckScannerBatchName = batchDetails.Name, CheckNumber = (check.CheckNumber ?? string.Empty).TrimStart(' ', '0').Right(MinistryPlatformCheckNumberMaxLength) }; var donationId = _mpDonorService.CreateDonationAndDistributionRecord(donationAndDistribution, false); check.DonationId = donationId; _checkScannerDao.UpdateCheckStatus(check.Id, true); batchDetails.Checks.Add(check); } catch (Exception e) { check.Error = e.ToString(); check.AccountNumber = _mpDonorService.DecryptCheckValue(check.AccountNumber); check.RoutingNumber = _mpDonorService.DecryptCheckValue(check.RoutingNumber); batchDetails.ErrorChecks.Add(check); _checkScannerDao.UpdateCheckStatus(check.Id, check.Exported, check.Error); } } batchDetails.Status = BatchStatus.Exported; _checkScannerDao.UpdateBatchStatus(batchDetails.Name, batchDetails.Status); return(batchDetails); }