/// <summary> /// Gets a reference payment info record. /// </summary> /// <returns></returns> public ReferencePaymentInfo GetReferencePayment() { var reference = new ReferencePaymentInfo(); reference.TransactionCode = this.TransactionCode; reference.ReferenceNumber = this.ReferenceNumber; reference.GatewayPersonIdentifier = this.GatewayPersonIdentifier; if (this.FinancialPaymentDetail != null) { reference.MaskedAccountNumber = this.FinancialPaymentDetail.AccountNumberMasked; // if the ExpirationMonth and ExpirationYear are valid, set the reference.PaymentExpirationDate from that if (this.FinancialPaymentDetail.ExpirationMonth.HasValue && this.FinancialPaymentDetail.ExpirationYear.HasValue) { if (this.FinancialPaymentDetail.ExpirationMonth.Value >= 1 && this.FinancialPaymentDetail.ExpirationMonth.Value <= 12) { reference.PaymentExpirationDate = new DateTime(this.FinancialPaymentDetail.ExpirationYear.Value, this.FinancialPaymentDetail.ExpirationMonth.Value, 1); } } if (this.FinancialPaymentDetail.CurrencyTypeValueId.HasValue) { reference.InitialCurrencyTypeValue = DefinedValueCache.Get(this.FinancialPaymentDetail.CurrencyTypeValueId.Value); if (reference.InitialCurrencyTypeValue != null && reference.InitialCurrencyTypeValue.Guid.Equals(new Guid(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD)) && this.FinancialPaymentDetail.CreditCardTypeValueId.HasValue) { reference.InitialCreditCardTypeValue = DefinedValueCache.Get(this.FinancialPaymentDetail.CreditCardTypeValueId.Value); } } } return(reference); }
/// <summary> /// Gets a reference payment info record. /// </summary> /// <returns></returns> public ReferencePaymentInfo GetReferencePayment() { if (this.FinancialPaymentDetail != null) { var reference = new ReferencePaymentInfo(); reference.TransactionCode = this.TransactionCode; reference.ReferenceNumber = this.ReferenceNumber; if (this.FinancialPaymentDetail != null) { reference.MaskedAccountNumber = this.FinancialPaymentDetail.AccountNumberMasked; if (this.FinancialPaymentDetail.CurrencyTypeValueId.HasValue) { reference.InitialCurrencyTypeValue = DefinedValueCache.Read(this.FinancialPaymentDetail.CurrencyTypeValueId.Value); if (reference.InitialCurrencyTypeValue != null && reference.InitialCurrencyTypeValue.Guid.Equals(new Guid(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD)) && this.FinancialPaymentDetail.CreditCardTypeValueId.HasValue) { reference.InitialCreditCardTypeValue = DefinedValueCache.Read(this.FinancialPaymentDetail.CreditCardTypeValueId.Value); } } } return(reference); } return(null); }
/// <summary> /// Gets a reference payment info record. /// </summary> /// <returns></returns> public ReferencePaymentInfo GetReferencePayment() { var reference = new ReferencePaymentInfo(); reference.TransactionCode = this.TransactionCode; reference.ReferenceNumber = this.ReferenceNumber; if (this.GatewayPersonIdentifier.IsNotNullOrWhiteSpace()) { reference.GatewayPersonIdentifier = this.GatewayPersonIdentifier; } else { // if GatewayPersonIdentifier is unknown, this is probably from an older NMI gateway transaction that only saved the GatewayPersonIdentifier to ReferenceNumber reference.GatewayPersonIdentifier = this.ReferenceNumber; } if (this.Id > 0) { reference.FinancialPersonSavedAccountId = this.Id; } if (this.FinancialPaymentDetail != null) { reference.MaskedAccountNumber = this.FinancialPaymentDetail.AccountNumberMasked; // if the ExpirationMonth and ExpirationYear are valid, set the reference.PaymentExpirationDate from that if (this.FinancialPaymentDetail.ExpirationMonth.HasValue && this.FinancialPaymentDetail.ExpirationYear.HasValue) { if (this.FinancialPaymentDetail.ExpirationMonth.Value >= 1 && this.FinancialPaymentDetail.ExpirationMonth.Value <= 12) { reference.PaymentExpirationDate = new DateTime(this.FinancialPaymentDetail.ExpirationYear.Value, this.FinancialPaymentDetail.ExpirationMonth.Value, 1); } } if (this.FinancialPaymentDetail.CurrencyTypeValueId.HasValue) { reference.InitialCurrencyTypeValue = DefinedValueCache.Get(this.FinancialPaymentDetail.CurrencyTypeValueId.Value); if (reference.InitialCurrencyTypeValue != null && reference.InitialCurrencyTypeValue.Guid.Equals(new Guid(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD)) && this.FinancialPaymentDetail.CreditCardTypeValueId.HasValue) { reference.InitialCreditCardTypeValue = DefinedValueCache.Get(this.FinancialPaymentDetail.CreditCardTypeValueId.Value); } } } reference.AmountCurrencyCodeValueId = this.PreferredForeignCurrencyCodeValueId; return(reference); }
/// <summary> /// Gets a reference payment info record. /// </summary> /// <returns></returns> public ReferencePaymentInfo GetReferencePayment() { var reference = new ReferencePaymentInfo(); reference.TransactionCode = this.TransactionCode; reference.ReferenceNumber = this.ReferenceNumber; reference.MaskedAccountNumber = this.MaskedAccountNumber; reference.InitialCurrencyTypeValue = DefinedValueCache.Read(this.CurrencyTypeValue); if (reference.InitialCurrencyTypeValue.Guid.Equals(new Guid(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD))) { reference.InitialCreditCardTypeValue = DefinedValueCache.Read(this.CreditCardTypeValue); } return(reference); }
/// <summary> /// Updates the scheduled payment. /// </summary> /// <param name="usePaymentToken">if set to <c>true</c> [use payment token].</param> /// <param name="paymentToken">The payment token.</param> protected void UpdateScheduledPayment(bool usePaymentToken, string paymentToken = null) { var giftTerm = this.GetAttributeValue(AttributeKey.GiftTerm); if (dtpStartDate.SelectedDate <= RockDateTime.Today) { nbUpdateScheduledPaymentWarning.Visible = true; nbUpdateScheduledPaymentWarning.Text = string.Format("When scheduling a {0}, make sure the starting date is in the future (after today)", giftTerm.ToLower()); return; } var rockContext = new RockContext(); var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext); int scheduledTransactionId = hfScheduledTransactionId.Value.AsInteger(); var financialScheduledTransaction = financialScheduledTransactionService.Get(scheduledTransactionId); financialScheduledTransaction.StartDate = dtpStartDate.SelectedDate.Value; financialScheduledTransaction.TransactionFrequencyValueId = ddlFrequency.SelectedValue.AsInteger(); ReferencePaymentInfo referencePaymentInfo; var person = financialScheduledTransaction.AuthorizedPersonAlias.Person; string errorMessage; var financialGateway = this.FinancialGateway; var financialGatewayComponent = this.FinancialGatewayComponent; if (usePaymentToken) { referencePaymentInfo = new ReferencePaymentInfo(); referencePaymentInfo.FirstName = person.FirstName; referencePaymentInfo.LastName = person.LastName; referencePaymentInfo.UpdateAddressFieldsFromAddressControl(acBillingAddress); referencePaymentInfo.ReferenceNumber = paymentToken; var customerToken = financialGatewayComponent.CreateCustomerAccount(this.FinancialGateway, referencePaymentInfo, out errorMessage); if (errorMessage.IsNotNullOrWhiteSpace() || customerToken.IsNullOrWhiteSpace()) { nbMessage.NotificationBoxType = NotificationBoxType.Danger; nbMessage.Text = errorMessage ?? "Unknown Error"; nbMessage.Visible = true; return; } referencePaymentInfo.GatewayPersonIdentifier = customerToken; } else { var savedAccountId = ddlPersonSavedAccount.SelectedValue.AsInteger(); var savedAccount = new FinancialPersonSavedAccountService(rockContext).Get(savedAccountId); if (savedAccount != null) { referencePaymentInfo = savedAccount.GetReferencePayment(); } else { throw new Exception("Unable to determine Saved Account"); } } var selectedAccountAmounts = caapPromptForAccountAmounts.AccountAmounts.Where(a => a.Amount.HasValue && a.Amount.Value != 0).Select(a => new { a.AccountId, Amount = a.Amount.Value }).ToArray(); referencePaymentInfo.Amount = selectedAccountAmounts.Sum(a => a.Amount); var successfullyUpdated = financialGatewayComponent.UpdateScheduledPayment(financialScheduledTransaction, referencePaymentInfo, out errorMessage); if (!successfullyUpdated) { nbMessage.NotificationBoxType = NotificationBoxType.Danger; nbMessage.Text = errorMessage ?? "Unknown Error"; nbMessage.Visible = true; return; } financialScheduledTransaction.FinancialPaymentDetail.SetFromPaymentInfo(referencePaymentInfo, financialGatewayComponent as GatewayComponent, rockContext); var selectedAccountIds = selectedAccountAmounts.Select(a => a.AccountId).ToArray(); var deletedTransactionDetails = financialScheduledTransaction.ScheduledTransactionDetails.ToList().Where(a => selectedAccountIds.Contains(a.AccountId)).ToList(); foreach (var deletedTransactionDetail in deletedTransactionDetails) { financialScheduledTransaction.ScheduledTransactionDetails.Remove(deletedTransactionDetail); } foreach (var selectedAccountAmount in selectedAccountAmounts) { var scheduledTransactionDetail = financialScheduledTransaction.ScheduledTransactionDetails.FirstOrDefault(a => a.AccountId == selectedAccountAmount.AccountId); if (scheduledTransactionDetail == null) { scheduledTransactionDetail = new FinancialScheduledTransactionDetail(); scheduledTransactionDetail.AccountId = selectedAccountAmount.AccountId; financialScheduledTransaction.ScheduledTransactionDetails.Add(scheduledTransactionDetail); } scheduledTransactionDetail.Amount = selectedAccountAmount.Amount; } rockContext.SaveChanges(); var mergeFields = LavaHelper.GetCommonMergeFields(this.RockPage, this.CurrentPerson, new CommonMergeFieldsOptions { GetLegacyGlobalMergeFields = false }); var finishLavaTemplate = this.GetAttributeValue(AttributeKey.FinishLavaTemplate); mergeFields.Add("Transaction", financialScheduledTransaction); mergeFields.Add("Person", financialScheduledTransaction.AuthorizedPersonAlias.Person); mergeFields.Add("PaymentDetail", financialScheduledTransaction.FinancialPaymentDetail); mergeFields.Add("BillingLocation", financialScheduledTransaction.FinancialPaymentDetail.BillingLocation); pnlPromptForChanges.Visible = false; pnlTransactionSummary.Visible = true; lTransactionSummaryHTML.Text = finishLavaTemplate.ResolveMergeFields(mergeFields); }
/// <summary> /// Handles the Click event of the btnMigrateScheduledTransactions control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void btnMigrateScheduledTransactions_Click(object sender, EventArgs e) { var rockContext = new RockContext(); var binaryFileService = new BinaryFileService(rockContext); var binaryFileId = fuScheduleImportFile.BinaryFileId; BinaryFile binaryFile = null; if (binaryFileId.HasValue) { binaryFile = binaryFileService.Get(binaryFileId.Value); } Dictionary <string, string> subscriptionImportRecordLookup = null; var importData = binaryFile.ContentsToString(); StringReader stringReader = new StringReader(importData); CsvReader csvReader = new CsvReader(stringReader); csvReader.Configuration.HasHeaderRecord = false; subscriptionImportRecordLookup = csvReader.GetRecords <SubscriptionCustomerImportRecord>().ToDictionary(k => k.NMISubscriptionId, v => v.PiCustomerId); var financialGatewayService = new FinancialGatewayService(rockContext); var nmiFinancialGatewayId = ddlNMIGateway.SelectedValue.AsInteger(); var nmiFinancialGateway = financialGatewayService.Get(nmiFinancialGatewayId); var nmiGatewayComponent = nmiFinancialGateway.GetGatewayComponent(); var piFinancialGatewayId = ddlPiGateway.SelectedValue.AsInteger(); var piFinancialGateway = financialGatewayService.Get(piFinancialGatewayId); var piGatewayComponent = piFinancialGateway.GetGatewayComponent() as IHostedGatewayComponent; var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext); // Get the ScheduledTransaction with NoTracking. If we need to update it, we'll track it with a different rockContext then save it. // Limit to active subscriptions that have a NextPaymentDate (onetime or canceled schedules might not have a NextPaymentDate) var scheduledTransactions = financialScheduledTransactionService.Queryable().Where(a => a.FinancialGatewayId == nmiFinancialGatewayId & a.IsActive && a.NextPaymentDate.HasValue).AsNoTracking().ToList(); var earliestPiStartDate = piGatewayComponent.GetEarliestScheduledStartDate(piFinancialGateway); var oneTimeFrequencyId = DefinedValueCache.GetId(Rock.SystemGuid.DefinedValue.TRANSACTION_FREQUENCY_ONE_TIME.AsGuid()); string errorMessage; var scheduledTransactionResultsBuilder = new StringBuilder(); var scheduledTransactionCount = scheduledTransactions.Count(); var scheduledTransactionProgress = 0; // Migrating Scheduled Transactions might take a while. Each migrated Scheduled Payment may take a half second or so to create on the Pi Gateway. var importTask = new Task(() => { // wait a little so the browser can render and start listening to events Task.Delay(1000).Wait(); _hubContext.Clients.All.setButtonVisibilty(this.SignalRNotificationKey, false); foreach (var scheduledTransaction in scheduledTransactions) { System.Threading.Thread.Sleep(1000); UpdateProgressMessage(string.Format("Migrating Scheduled Transactions: {0} of {1}", scheduledTransactionProgress, scheduledTransactionCount), " "); scheduledTransactionProgress++; var nmiSubscriptionId = scheduledTransaction.GatewayScheduleId; var nmiCustomerId = scheduledTransaction.ForeignKey; var piCustomerId = subscriptionImportRecordLookup.GetValueOrNull(nmiSubscriptionId); if (piCustomerId == null) { scheduledTransactionResultsBuilder.AppendFormat( "WARNING: No Pi CustomerId found for Financial Scheduled Transaction with Id: {0} which is associated NMI SubscriptionId: '{1}'" + Environment.NewLine, scheduledTransaction.Id, nmiSubscriptionId ); continue; } // Pi requires that NextPaymentDate is in the Future (using UTC). That math is done in the gateway implementation... // if the NextPayment null or earlier than whatever Pi considers the earliest start date, see if we can fix that up by calling GetStatus if (scheduledTransaction.NextPaymentDate == null || scheduledTransaction.NextPaymentDate < earliestPiStartDate) { financialScheduledTransactionService.GetStatus(scheduledTransaction, out errorMessage); } if (scheduledTransaction.NextPaymentDate == null) { // Shouldn't happen, but just in case scheduledTransactionResultsBuilder.AppendFormat( "WARNING: Unknown NextPaymentDate for FinancialScheduledTransaction.Id: {0} NMI SubscriptionId: '{1}'" + Environment.NewLine, scheduledTransaction.Id, nmiSubscriptionId ); continue; } if (scheduledTransaction.NextPaymentDate < earliestPiStartDate) { if ((scheduledTransaction.NextPaymentDate > RockDateTime.Today) && earliestPiStartDate.Subtract(scheduledTransaction.NextPaymentDate.Value).TotalDays <= 2) { // if the NextPaymentDate is after Today but before the Earliest Pi Start Date, it'll be off by less than 24 hrs, so just reschedule it for the Earliest Pi Start Date scheduledTransaction.NextPaymentDate = earliestPiStartDate; } else { // if the NextPaymentDate is still too early AFTER getting the most recent status, then we can't safely figure it out, so report it scheduledTransactionResultsBuilder.AppendFormat( "WARNING: NextPaymentDate of {0} for FinancialScheduledTransaction.Id: {1} and NMI SubscriptionId: '{2}' must have a NextPaymentDate of at least {3}." + Environment.NewLine, scheduledTransaction.NextPaymentDate, scheduledTransaction.Id, nmiSubscriptionId, earliestPiStartDate ); } } // create a subscription in the Pi System, then cancel the one on the NMI system PaymentSchedule paymentSchedule = new PaymentSchedule { TransactionFrequencyValue = DefinedValueCache.Get(scheduledTransaction.TransactionFrequencyValueId), StartDate = scheduledTransaction.NextPaymentDate.Value, PersonId = scheduledTransaction.AuthorizedPersonAlias.PersonId }; ReferencePaymentInfo referencePaymentInfo = new ReferencePaymentInfo { GatewayPersonIdentifier = piCustomerId, Description = string.Format("Migrated from NMI SubscriptionID:{0}", nmiSubscriptionId) }; var piGateway = (piGatewayComponent as PiGateway); string alreadyMigratedPiSubscriptionId = null; if (piGateway != null) { var customerPiSubscriptions = piGateway.SearchCustomerSubscriptions(piFinancialGateway, piCustomerId); alreadyMigratedPiSubscriptionId = customerPiSubscriptions.Data.Where(a => a.Description.Contains(referencePaymentInfo.Description)).Select(a => a.Customer.Id).FirstOrDefault(); } if (string.IsNullOrEmpty(alreadyMigratedPiSubscriptionId)) { // hasn't already been migrated, so go ahead and migrate it var tempFinancialScheduledTransaction = piGatewayComponent.AddScheduledPayment(piFinancialGateway, paymentSchedule, referencePaymentInfo, out errorMessage); if (tempFinancialScheduledTransaction != null) { ////////////#### DISABLE this when debugger ##### nmiGatewayComponent.CancelScheduledPayment(scheduledTransaction, out errorMessage); // update the scheduled transaction to point to the Pi scheduled transaction using (var updateRockContext = new RockContext()) { // Attach the person to the updateRockContext so that it'll be tracked/saved using updateRockContext updateRockContext.FinancialScheduledTransactions.Attach(scheduledTransaction); scheduledTransaction.TransactionCode = tempFinancialScheduledTransaction.TransactionCode; scheduledTransaction.GatewayScheduleId = tempFinancialScheduledTransaction.GatewayScheduleId; scheduledTransaction.FinancialGatewayId = tempFinancialScheduledTransaction.FinancialGatewayId; updateRockContext.SaveChanges(); } scheduledTransactionResultsBuilder.AppendFormat( "SUCCESS: Scheduled Transaction migration succeeded. (FinancialScheduledTransaction.Id: {0}, NMI SubscriptionId: '{1}', Pi CustomerId: {2}, Pi SubscriptionId: {3})" + Environment.NewLine, scheduledTransaction.Id, nmiSubscriptionId, piCustomerId, scheduledTransaction.GatewayScheduleId ); } else { scheduledTransactionResultsBuilder.AppendFormat( "ERROR: Scheduled Transaction migration failed. ErrorMessage: {0}, FinancialScheduledTransaction.Id: {1}, NMI SubscriptionId: '{2}', Pi CustomerId: {3}" + Environment.NewLine, errorMessage, scheduledTransaction.Id, nmiSubscriptionId, piCustomerId ); } } else { scheduledTransactionResultsBuilder.AppendFormat( "INFO: Scheduled Transaction already migrated to PI. FinancialScheduledTransaction.Id: {0}, NMI SubscriptionId: '{1}', Pi SubscriptionId: '{2}', Pi CustomerId: {3}" + Environment.NewLine, scheduledTransaction.Id, nmiSubscriptionId, alreadyMigratedPiSubscriptionId, piCustomerId ); } } }); string importResult = string.Empty; importTask.ContinueWith((c) => { if (c.Exception != null) { ExceptionLogService.LogException(c.Exception); scheduledTransactionResultsBuilder.AppendLine(string.Format("EXCEPTION: {0}", c.Exception.Flatten().Message)); importResult = "EXCEPTION"; UpdateProgressMessage(importResult, scheduledTransactionResultsBuilder.ToString()); } else { importResult = "Migrate Scheduled Transactions Completed Successfully"; UpdateProgressMessage(importResult, scheduledTransactionResultsBuilder.ToString()); } this.SetBlockUserPreference("MigrateScheduledTransactionsResultSummary", importResult); this.SetBlockUserPreference("MigrateScheduledTransactionsResultDetails", scheduledTransactionResultsBuilder.ToString()); }); importTask.Start(); nbMigrateScheduledTransactions.Visible = false; // wait for 5 seconds to see if this happens fast enough to do without Signal R. Otherwise, let the importTask continue and send progress to Signal R. var waitResult = importTask.Wait(5000); if (waitResult) { // wait just a little bit to make sure the importResult gets set System.Threading.Thread.Sleep(1000); nbMigrateScheduledTransactions.Visible = true; nbMigrateScheduledTransactions.Title = "Success"; nbMigrateScheduledTransactions.NotificationBoxType = Rock.Web.UI.Controls.NotificationBoxType.Success; var resultDetails = scheduledTransactionResultsBuilder.ToString(); if (resultDetails.Contains("ERROR") || resultDetails.Contains("WARNING")) { nbMigrateScheduledTransactions.Title = "Completed with Warnings"; nbMigrateScheduledTransactions.NotificationBoxType = Rock.Web.UI.Controls.NotificationBoxType.Info; } nbMigrateScheduledTransactions.Text = importResult; nbMigrateScheduledTransactions.Details = resultDetails.ConvertCrLfToHtmlBr(); } }
/// <summary> /// Updates the scheduled payment. /// </summary> /// <param name="usePaymentToken">if set to <c>true</c> [use payment token].</param> /// <param name="paymentToken">The payment token.</param> protected void UpdateScheduledPayment(bool usePaymentToken, string paymentToken = null) { var giftTerm = this.GetAttributeValue(AttributeKey.GiftTerm); if (dtpStartDate.SelectedDate <= RockDateTime.Today) { nbUpdateScheduledPaymentWarning.Visible = true; nbUpdateScheduledPaymentWarning.Text = string.Format("When scheduling a {0}, make sure the starting date is in the future (after today)", giftTerm.ToLower()); return; } var rockContext = new RockContext(); var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext); var financialScheduledTransactionDetailService = new FinancialScheduledTransactionDetailService(rockContext); Guid scheduledTransactionGuid = hfScheduledTransactionGuid.Value.AsGuid(); var financialScheduledTransaction = financialScheduledTransactionService.Get(scheduledTransactionGuid); financialScheduledTransaction.StartDate = dtpStartDate.SelectedDate.Value; financialScheduledTransaction.TransactionFrequencyValueId = ddlFrequency.SelectedValue.AsInteger(); ReferencePaymentInfo referencePaymentInfo; var person = financialScheduledTransaction.AuthorizedPersonAlias.Person; string errorMessage; var financialGateway = this.FinancialGateway; var financialGatewayComponent = this.FinancialGatewayComponent; var existingPaymentOrPersonSavedAccountId = rblExistingPaymentOrPersonSavedAccount.SelectedValue.AsInteger(); bool useExistingPaymentMethod = pnlUseExistingPaymentNoSavedAccounts.Visible || existingPaymentOrPersonSavedAccountId == 0; bool useSavedAccount = pnlUseExistingPaymentWithSavedAccounts.Visible && existingPaymentOrPersonSavedAccountId > 0; if (usePaymentToken) { referencePaymentInfo = new ReferencePaymentInfo(); referencePaymentInfo.FirstName = person.FirstName; referencePaymentInfo.LastName = person.LastName; referencePaymentInfo.UpdateAddressFieldsFromAddressControl(acBillingAddress); referencePaymentInfo.ReferenceNumber = paymentToken; var customerToken = financialGatewayComponent.CreateCustomerAccount(this.FinancialGateway, referencePaymentInfo, out errorMessage); if (errorMessage.IsNotNullOrWhiteSpace() || customerToken.IsNullOrWhiteSpace()) { nbMessage.NotificationBoxType = NotificationBoxType.Danger; nbMessage.Text = errorMessage ?? "Unknown Error"; nbMessage.Visible = true; return; } referencePaymentInfo.GatewayPersonIdentifier = customerToken; } else if (useExistingPaymentMethod) { // use save payment method as original transaction referencePaymentInfo = new ReferencePaymentInfo(); referencePaymentInfo.GatewayPersonIdentifier = financialScheduledTransaction.FinancialPaymentDetail.GatewayPersonIdentifier; referencePaymentInfo.FinancialPersonSavedAccountId = financialScheduledTransaction.FinancialPaymentDetail.FinancialPersonSavedAccountId; } else if (useSavedAccount) { var savedAccount = new FinancialPersonSavedAccountService(rockContext).Get(existingPaymentOrPersonSavedAccountId); if (savedAccount != null) { referencePaymentInfo = savedAccount.GetReferencePayment(); } else { // shouldn't happen throw new Exception("Unable to determine Saved Account"); } } else { // shouldn't happen throw new Exception("Unable to determine payment method"); } var selectedAccountAmounts = caapPromptForAccountAmounts.AccountAmounts.Where(a => a.Amount.HasValue && a.Amount.Value != 0).Select(a => new { a.AccountId, Amount = a.Amount.Value }).ToArray(); referencePaymentInfo.Amount = selectedAccountAmounts.Sum(a => a.Amount); var originalGatewayScheduleId = financialScheduledTransaction.GatewayScheduleId; try { financialScheduledTransaction.FinancialPaymentDetail.ClearPaymentInfo(); var successfullyUpdated = financialGatewayComponent.UpdateScheduledPayment(financialScheduledTransaction, referencePaymentInfo, out errorMessage); if (!successfullyUpdated) { nbMessage.NotificationBoxType = NotificationBoxType.Danger; nbMessage.Text = errorMessage ?? "Unknown Error"; nbMessage.Visible = true; return; } financialScheduledTransaction.FinancialPaymentDetail.SetFromPaymentInfo(referencePaymentInfo, financialGatewayComponent as GatewayComponent, rockContext); var selectedAccountIds = selectedAccountAmounts.Select(a => a.AccountId).ToArray(); var deletedTransactionDetails = financialScheduledTransaction.ScheduledTransactionDetails.ToList().Where(a => !selectedAccountIds.Contains(a.AccountId)).ToList(); foreach (var deletedTransactionDetail in deletedTransactionDetails) { financialScheduledTransaction.ScheduledTransactionDetails.Remove(deletedTransactionDetail); financialScheduledTransactionDetailService.Delete(deletedTransactionDetail); } foreach (var selectedAccountAmount in selectedAccountAmounts) { var scheduledTransactionDetail = financialScheduledTransaction.ScheduledTransactionDetails.FirstOrDefault(a => a.AccountId == selectedAccountAmount.AccountId); if (scheduledTransactionDetail == null) { scheduledTransactionDetail = new FinancialScheduledTransactionDetail(); scheduledTransactionDetail.AccountId = selectedAccountAmount.AccountId; financialScheduledTransaction.ScheduledTransactionDetails.Add(scheduledTransactionDetail); } scheduledTransactionDetail.Amount = selectedAccountAmount.Amount; } rockContext.SaveChanges(); Task.Run(() => ScheduledGiftWasModifiedMessage.PublishScheduledTransactionEvent(financialScheduledTransaction.Id, ScheduledGiftEventTypes.ScheduledGiftUpdated)); } catch (Exception) { // if the GatewayScheduleId was updated, but there was an exception, // make sure we save the financialScheduledTransaction record with the updated GatewayScheduleId so we don't orphan it if (financialScheduledTransaction.GatewayScheduleId.IsNotNullOrWhiteSpace() && (originalGatewayScheduleId != financialScheduledTransaction.GatewayScheduleId)) { rockContext.SaveChanges(); } throw; } var mergeFields = LavaHelper.GetCommonMergeFields(this.RockPage, this.CurrentPerson, new CommonMergeFieldsOptions { GetLegacyGlobalMergeFields = false }); var finishLavaTemplate = this.GetAttributeValue(AttributeKey.FinishLavaTemplate); // re-fetch financialScheduledTransaction with a new RockContext from database to ensure that lazy loaded fields will be populated using (var rockContextForSummary = new RockContext()) { if (pnlHostedPaymentControl.Visible && hfSaveNewAccount.Value.AsInteger() == 1 && tbSaveAccount.Text.IsNotNullOrWhiteSpace()) { SaveNewFinancialPersonSavedAccount(financialScheduledTransaction); } financialScheduledTransaction = new FinancialScheduledTransactionService(rockContextForSummary).Get(scheduledTransactionGuid); mergeFields.Add("Transaction", financialScheduledTransaction); mergeFields.Add("Person", financialScheduledTransaction.AuthorizedPersonAlias.Person); mergeFields.Add("PaymentDetail", financialScheduledTransaction.FinancialPaymentDetail); mergeFields.Add("BillingLocation", financialScheduledTransaction.FinancialPaymentDetail.BillingLocation); pnlPromptForChanges.Visible = false; pnlTransactionSummary.Visible = true; lTransactionSummaryHTML.Text = finishLavaTemplate.ResolveMergeFields(mergeFields); } }