/// <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);
        }
Exemple #2
0
        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);
        }