/// <summary> /// Adds the scheduled payment. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="schedule">The schedule.</param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialScheduledTransaction AddScheduledPayment( FinancialGateway financialGateway, PaymentSchedule schedule, PaymentInfo paymentInfo, out string errorMessage ) { errorMessage = string.Empty; var scheduledTransaction = new FinancialScheduledTransaction(); scheduledTransaction.IsActive = true; scheduledTransaction.StartDate = schedule.StartDate; scheduledTransaction.NextPaymentDate = schedule.StartDate; scheduledTransaction.TransactionCode = "T" + RockDateTime.Now.ToString("yyyyMMddHHmmssFFF"); scheduledTransaction.GatewayScheduleId = "P" + RockDateTime.Now.ToString("yyyyMMddHHmmssFFF"); scheduledTransaction.LastStatusUpdateDateTime = RockDateTime.Now; return scheduledTransaction; }
/// <summary> /// Shows the detail. /// </summary> /// <param name="gatewayId">The gateway identifier.</param> public void ShowDetail( int gatewayId ) { FinancialGateway gateway = null; bool editAllowed = IsUserAuthorized( Authorization.EDIT ); if ( !gatewayId.Equals( 0 ) ) { gateway = new FinancialGatewayService( new RockContext() ).Get( gatewayId ); editAllowed = editAllowed || gateway.IsAuthorized( Authorization.EDIT, CurrentPerson ); pdAuditDetails.SetEntity( gateway, ResolveRockUrl( "~" ) ); } if ( gateway == null ) { gateway = new FinancialGateway { Id = 0, IsActive = true }; // hide the panel drawer that show created and last modified dates pdAuditDetails.Visible = false; } GatewayId = gateway.Id; bool readOnly = false; nbEditModeMessage.Text = string.Empty; if ( !editAllowed || !IsUserAuthorized( Authorization.EDIT ) ) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed( FinancialGateway.FriendlyTypeName ); } if ( readOnly ) { ShowReadonlyDetails( gateway ); } else { ShowEditDetails( gateway ); } }
/// <summary> /// Shows the detail. /// </summary> /// <param name="gatewayId">The gateway identifier.</param> public void ShowDetail( int gatewayId ) { FinancialGateway gateway = null; bool editAllowed = IsUserAuthorized( Authorization.EDIT ); if ( !gatewayId.Equals( 0 ) ) { gateway = new FinancialGatewayService( new RockContext() ).Get( gatewayId ); editAllowed = editAllowed || gateway.IsAuthorized( Authorization.EDIT, CurrentPerson ); } if ( gateway == null ) { gateway = new FinancialGateway { Id = 0, IsActive = true }; } GatewayId = gateway.Id; bool readOnly = false; nbEditModeMessage.Text = string.Empty; if ( !editAllowed || !IsUserAuthorized( Authorization.EDIT ) ) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed( FinancialGateway.FriendlyTypeName ); } if ( readOnly ) { ShowReadonlyDetails( gateway ); } else { ShowEditDetails( gateway ); } }
/// <summary> /// Charges the specified payment info. /// </summary> /// <param name="financialGateway"></param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialTransaction Charge(FinancialGateway financialGateway, PaymentInfo paymentInfo, out string errorMessage) { errorMessage = "This gateway does not support charging transactions. Transactions should be created through the Reach interface."; return(null); }
/// <summary> /// Gets the merchant information. /// </summary> /// <returns></returns> private RequestMessage GetMerchantInfo( FinancialGateway financialGateway, bool includeEnvironment = true ) { if ( financialGateway.Attributes == null ) { financialGateway.LoadAttributes(); } RequestMessage request = new RequestMessage(); request.merchantID = GetAttributeValue( financialGateway, "MerchantID" ); request.merchantReferenceCode = Guid.NewGuid().ToString(); if ( includeEnvironment ) { request.clientLibraryVersion = Environment.Version.ToString(); request.clientApplication = VersionInfo.GetRockProductVersionFullName(); request.clientApplicationVersion = VersionInfo.GetRockProductVersionNumber(); request.clientApplicationUser = GetAttributeValue( financialGateway, "OrganizationName" ); request.clientEnvironment = Environment.OSVersion.Platform + Environment.OSVersion.Version.ToString() + "-CLR" + Environment.Version.ToString(); } return request; }
/// <summary> /// Creates the saved account. /// </summary> /// <param name="parameters">The parameters.</param> /// <param name="paymentDetail">The payment detail.</param> /// <param name="financialGateway">The financial gateway.</param> /// <param name="person">The person.</param> /// <param name="rockContext">The rock context.</param> /// <returns></returns> private FinancialPersonSavedAccount CreateSavedAccount( PaymentParameters parameters, FinancialPaymentDetail paymentDetail, FinancialGateway financialGateway, Person person, RockContext rockContext) { var lastFour = paymentDetail.AccountNumberMasked.Substring(paymentDetail.AccountNumberMasked.Length - 4); var name = string.Empty; if ( parameters.AccountType.ToLower() != "credit" ) { if ( string.IsNullOrWhiteSpace( parameters.RoutingNumber ) ) { GenerateResponse( HttpStatusCode.BadRequest, "RoutingNumber is required for ACH transactions" ); return null; } if ( string.IsNullOrWhiteSpace( parameters.AccountNumber ) ) { GenerateResponse( HttpStatusCode.BadRequest, "AccountNumber is required" ); return null; } name = "Bank card ***" + lastFour; var bankAccountService = new FinancialPersonBankAccountService( rockContext ); var accountNumberSecured = FinancialPersonBankAccount.EncodeAccountNumber( parameters.RoutingNumber, parameters.AccountNumber ); var bankAccount = bankAccountService.Queryable().Where( a => a.AccountNumberSecured == accountNumberSecured && a.PersonAliasId == person.PrimaryAliasId.Value ).FirstOrDefault(); if ( bankAccount == null ) { bankAccount = new FinancialPersonBankAccount(); bankAccount.PersonAliasId = person.PrimaryAliasId.Value; bankAccount.AccountNumberMasked = paymentDetail.AccountNumberMasked; bankAccount.AccountNumberSecured = accountNumberSecured; bankAccountService.Add( bankAccount ); } } else { name = "Credit card ***" + lastFour; } var savedAccount = new FinancialPersonSavedAccount { PersonAliasId = person.PrimaryAliasId, FinancialGatewayId = financialGateway.Id, Name = name, FinancialPaymentDetailId = paymentDetail.Id }; new FinancialPersonSavedAccountService(rockContext).Add( savedAccount ); rockContext.SaveChanges(); return savedAccount; }
private void SaveScheduledTransaction( FinancialGateway financialGateway, GatewayComponent gateway, Person person, PaymentInfo paymentInfo, PaymentSchedule schedule, FinancialScheduledTransaction scheduledTransaction, RockContext rockContext ) { scheduledTransaction.TransactionFrequencyValueId = schedule.TransactionFrequencyValue.Id; scheduledTransaction.StartDate = schedule.StartDate; scheduledTransaction.AuthorizedPersonAliasId = person.PrimaryAliasId.Value; scheduledTransaction.FinancialGatewayId = financialGateway.Id; if ( scheduledTransaction.FinancialPaymentDetail == null ) { scheduledTransaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } scheduledTransaction.FinancialPaymentDetail.SetFromPaymentInfo( paymentInfo, gateway, rockContext ); Guid sourceGuid = Guid.Empty; if ( Guid.TryParse( GetAttributeValue( "Source" ), out sourceGuid ) ) { var source = DefinedValueCache.Read( sourceGuid ); if ( source != null ) { scheduledTransaction.SourceTypeValueId = source.Id; } } var changeSummary = new StringBuilder(); changeSummary.AppendFormat( "{0} starting {1}", schedule.TransactionFrequencyValue.Value, schedule.StartDate.ToShortDateString() ); changeSummary.AppendLine(); changeSummary.Append( paymentInfo.CurrencyTypeValue.Value ); if ( paymentInfo.CreditCardTypeValue != null ) { changeSummary.AppendFormat( " - {0}", paymentInfo.CreditCardTypeValue.Value ); } changeSummary.AppendFormat( " {0}", paymentInfo.MaskedNumber ); changeSummary.AppendLine(); foreach ( var account in SelectedAccounts.Where( a => a.Amount > 0 ) ) { var transactionDetail = new FinancialScheduledTransactionDetail(); transactionDetail.Amount = account.Amount; transactionDetail.AccountId = account.Id; scheduledTransaction.ScheduledTransactionDetails.Add( transactionDetail ); changeSummary.AppendFormat( "{0}: {1}", account.Name, account.Amount.FormatAsCurrency() ); changeSummary.AppendLine(); } if ( !string.IsNullOrWhiteSpace( paymentInfo.Comment1 ) ) { changeSummary.Append( paymentInfo.Comment1 ); changeSummary.AppendLine(); } var transactionService = new FinancialScheduledTransactionService( rockContext ); transactionService.Add( scheduledTransaction ); rockContext.SaveChanges(); // If this is a transfer, now we can delete the old transaction if ( _scheduledTransactionToBeTransferred != null ) { DeleteOldTransaction( _scheduledTransactionToBeTransferred.Id ); } // Add a note about the change var noteType = NoteTypeCache.Read( Rock.SystemGuid.NoteType.SCHEDULED_TRANSACTION_NOTE.AsGuid() ); if ( noteType != null ) { var noteService = new NoteService( rockContext ); var note = new Note(); note.NoteTypeId = noteType.Id; note.EntityId = scheduledTransaction.Id; note.Caption = "Created Transaction"; note.Text = changeSummary.ToString(); noteService.Add( note ); } rockContext.SaveChanges(); ScheduleId = scheduledTransaction.GatewayScheduleId; TransactionCode = scheduledTransaction.TransactionCode; }
/// <summary> /// Charges the specified payment info. /// </summary> /// <param name="financialGateway"></param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialTransaction Charge(FinancialGateway financialGateway, PaymentInfo paymentInfo, out string errorMessage) { errorMessage = string.Empty; Response ppResponse = null; var invoice = GetInvoice(paymentInfo); // add in spark reference code BrowserInfo browser = new BrowserInfo(); browser.ButtonSource = "SparkDevelopmentNetwork_SP"; invoice.BrowserInfo = browser; var tender = GetTender(paymentInfo); if (tender != null) { if (paymentInfo is ReferencePaymentInfo) { var reference = paymentInfo as ReferencePaymentInfo; var ppTransaction = new ReferenceTransaction("Sale", reference.TransactionCode, GetUserInfo(financialGateway), GetConnection(financialGateway), invoice, tender, PayflowUtility.RequestId); ppResponse = ppTransaction.SubmitTransaction(); } else { var ppTransaction = new SaleTransaction(GetUserInfo(financialGateway), GetConnection(financialGateway), invoice, tender, PayflowUtility.RequestId); ppResponse = ppTransaction.SubmitTransaction(); } } else { errorMessage = "Could not create tender from PaymentInfo"; } if (ppResponse != null) { TransactionResponse txnResponse = ppResponse.TransactionResponse; if (txnResponse != null) { if (txnResponse.Result == 0) // Success { var transaction = new FinancialTransaction(); transaction.TransactionCode = txnResponse.Pnref; // Get the stored payment method that was used so we can update it // with the latest transaction code. We do this because Paypal // will only let us use the same transaction code in reference // transactions for up to 12 months. if (paymentInfo is ReferencePaymentInfo) { var reference = paymentInfo as ReferencePaymentInfo; var rockContext = new RockContext(); var savedAccount = new FinancialPersonSavedAccountService(rockContext) .Queryable() .Where(s => s.TransactionCode == reference.TransactionCode && s.FinancialGatewayId.HasValue && s.FinancialGatewayId.Value == financialGateway.Id) .FirstOrDefault(); if (savedAccount != null) { savedAccount.TransactionCode = txnResponse.Pnref; rockContext.SaveChanges(); } } return(transaction); } else { errorMessage = string.Format("[{0}] {1}", txnResponse.Result, txnResponse.RespMsg); } } else { errorMessage = "Invalid transaction response from the financial gateway"; } } else { errorMessage = "Invalid response from the financial gateway."; } return(null); }
/// <summary> /// Prompts for the person name associated with a bank account. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <returns></returns> public override bool PromptForBankAccountName(FinancialGateway financialGateway) { return(true); }
/// <summary> /// Performs the third step of adding a new payment schedule /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="resultQueryString">The result query string from step 2.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">tokenId</exception> public override FinancialScheduledTransaction AddScheduledPaymentStep3(FinancialGateway financialGateway, string resultQueryString, out string errorMessage) { errorMessage = string.Empty; try { var rootElement = GetRoot(financialGateway, "complete-action"); rootElement.Add(new XElement("token-id", resultQueryString.Substring(10))); XDocument xdoc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), rootElement); var result = PostToGateway(financialGateway, xdoc); if (result == null) { errorMessage = "Invalid Response from NMI!"; return(null); } if (result.GetValueOrNull("result") != "1") { errorMessage = result.GetValueOrNull("result-text"); return(null); } var scheduledTransaction = new FinancialScheduledTransaction(); scheduledTransaction.IsActive = true; scheduledTransaction.GatewayScheduleId = result.GetValueOrNull("subscription-id"); scheduledTransaction.FinancialGatewayId = financialGateway.Id; scheduledTransaction.FinancialPaymentDetail = new FinancialPaymentDetail(); string ccNumber = result.GetValueOrNull("billing_cc-number"); if (!string.IsNullOrWhiteSpace(ccNumber)) { // cc payment var curType = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD); scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId = curType != null ? curType.Id : (int?)null; scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId = CreditCardPaymentInfo.GetCreditCardType(ccNumber.Replace('*', '1').AsNumeric())?.Id; scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked = ccNumber.Masked(true); string mmyy = result.GetValueOrNull("billing_cc-exp"); if (!string.IsNullOrWhiteSpace(mmyy) && mmyy.Length == 4) { scheduledTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted = Encryption.EncryptString(mmyy.Substring(0, 2)); scheduledTransaction.FinancialPaymentDetail.ExpirationYearEncrypted = Encryption.EncryptString(mmyy.Substring(2, 2)); } } else { // ach payment var curType = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_ACH); scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId = curType != null ? curType.Id : (int?)null; scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked = result.GetValueOrNull("billing_account_number").Masked(true); } GetScheduledPaymentStatus(scheduledTransaction, out errorMessage); return(scheduledTransaction); } catch (WebException webException) { string message = GetResponseMessage(webException.Response.GetResponseStream()); errorMessage = webException.Message + " - " + message; return(null); } catch (Exception ex) { errorMessage = ex.Message; return(null); } }
/// <summary>Process all transactions (payments) from Service Reef.</summary> /// <param name="message">The message that is returned depending on the result.</param> /// <param name="state">The state of the process.</param> /// <returns><see cref="WorkerResultStatus"/></returns> public void Execute(IJobExecutionContext context) { RockContext dbContext = new RockContext(); FinancialBatchService financialBatchService = new FinancialBatchService(dbContext); PersonService personService = new PersonService(dbContext); PersonAliasService personAliasService = new PersonAliasService(dbContext); FinancialAccountService financialAccountService = new FinancialAccountService(dbContext); FinancialAccountService accountService = new FinancialAccountService(dbContext); FinancialTransactionService financialTransactionService = new FinancialTransactionService(dbContext); FinancialGatewayService financialGatewayService = new FinancialGatewayService(dbContext); DefinedValueService definedValueService = new DefinedValueService(dbContext); DefinedTypeService definedTypeService = new DefinedTypeService(dbContext); TransactionService transactionService = new TransactionService(new PayPalReporting.Data.PayPalReportingContext()); // Get the datamap for loading attributes JobDataMap dataMap = context.JobDetail.JobDataMap; String warnings = string.Empty; FinancialBatch batch = null; Double totalAmount = 0; var total = 1; var processed = 0; try { DateRange dateRange = Rock.Web.UI.Controls.SlidingDateRangePicker.CalculateDateRangeFromDelimitedValues(dataMap.GetString("DateRange") ?? "-1||"); String SRApiKey = Encryption.DecryptString(dataMap.GetString("ServiceReefAPIKey")); String SRApiSecret = Encryption.DecryptString(dataMap.GetString("ServiceReefAPISecret")); String SRApiUrl = dataMap.GetString("ServiceReefAPIURL"); DefinedValueCache transactionSource = DefinedValueCache.Read(dataMap.GetString("TransactionSource").AsGuid(), dbContext); DefinedValueCache connectionStatus = DefinedValueCache.Read(dataMap.GetString("ConnectionStatus").AsGuid(), dbContext); DefinedValueCache contribution = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION); // Setup some lookups DefinedTypeCache creditCards = DefinedTypeCache.Read(Rock.SystemGuid.DefinedType.FINANCIAL_CREDIT_CARD_TYPE.AsGuid(), dbContext); DefinedTypeCache tenderType = DefinedTypeCache.Get(Rock.SystemGuid.DefinedType.FINANCIAL_CURRENCY_TYPE.AsGuid(), dbContext); FinancialAccount specialFund = accountService.Get(dataMap.GetString("Account").AsGuid()); FinancialGateway gateway = financialGatewayService.Get(dataMap.GetString("FinancialGateway").AsGuid()); List <FinancialAccount> trips = financialAccountService.Queryable().Where(fa => fa.ParentAccountId == specialFund.Id).OrderBy(fa => fa.Order).ToList(); // Get the trips DefinedValueCache serviceReefAccountType = DefinedValueCache.Get(dataMap.Get("ServiceReefAccountType").ToString().AsGuid()); // Setup the ServiceReef API Client var client = new RestClient(SRApiUrl); client.Authenticator = new HMACAuthenticator(SRApiKey, SRApiSecret); // Get all payments from ServiceReef var request = new RestRequest("v1/payments", Method.GET); request.AddParameter("pageSize", 100); if (dateRange.Start.HasValue) { request.AddParameter("startDate", dateRange.Start.Value.ToString("o")); } if (dateRange.End.HasValue) { request.AddParameter("endDate", dateRange.End.Value.ToString("o")); } request.AddParameter("page", 1); while (total > processed) { var response = client.Execute <Contracts.Payments>(request); if (response.StatusCode != System.Net.HttpStatusCode.OK) { throw new Exception("ServiceReef API Response: " + response.StatusDescription + " Content Length: " + response.ContentLength); } if (response.Data != null && response.Data.PageInfo != null) { total = response.Data.PageInfo.TotalRecords; foreach (Contracts.Payments.Result result in response.Data.Results) { // Process the transaction if (result.PaymentProcessorTransactionId != null) { if (result.FirstName == null || result.LastName == null) { warnings += "Missing Firstname/Lastname for ServiceReef transaction Id: " + result.TransactionId + Environment.NewLine; processed++; continue; } FinancialAccount trip = null; // Make sure we have a sub-account to go with this transaction if (result.EventId > 0) { trip = trips.Where(t => t.GlCode == result.EventCode && t.Url == result.EventUrl).FirstOrDefault(); } if (trip == null) { if (result.EventCode == null) { warnings += "Event Code is missing on the Service Reef Trip for ServiceReef transaction Id: " + result.TransactionId + Environment.NewLine; processed++; continue; } // Create the trip subaccount FinancialAccount tripFA = new FinancialAccount(); tripFA.Name = result.EventName; // Name is limited to 50 if (tripFA.Name.Length > 50) { tripFA.Name = tripFA.Name.Substring(0, 50); } tripFA.Description = "Service Reef Event. Name: " + result.EventName + " ID: " + result.EventId; tripFA.GlCode = result.EventCode; tripFA.Url = result.EventUrl; tripFA.PublicName = result.EventName; // Public Name is limited to 50 if (tripFA.PublicName.Length > 50) { tripFA.PublicName = tripFA.PublicName.Substring(0, 50); } tripFA.IsTaxDeductible = true; tripFA.IsPublic = false; tripFA.ParentAccountId = specialFund.Id; tripFA.Order = specialFund.Order + 1; tripFA.AccountTypeValueId = serviceReefAccountType.Id; // Figure out what order it should be; foreach (FinancialAccount tmpTrip in trips) { if (tmpTrip.Name.CompareTo(tripFA.Name) < 0) { tripFA.Order++; } } financialAccountService.Add(tripFA); // Now save the trip dbContext.SaveChanges(); // Increment all the rest of the Orders financialAccountService.Queryable().Where(fa => fa.Order >= tripFA.Order && fa.Id != tripFA.Id).ToList().ForEach(c => c.Order++); dbContext.SaveChanges(); trips = financialAccountService.Queryable().Where(fa => fa.ParentAccountId == specialFund.Id).OrderBy(fa => fa.Order).ToList(); trip = tripFA; } FinancialTransaction tran = financialTransactionService.Queryable().Where(tx => tx.TransactionCode == result.PaymentProcessorTransactionId).FirstOrDefault(); // We haven't processed this before so get busy! if (tran == null) { tran = new FinancialTransaction(); tran.FinancialPaymentDetail = new FinancialPaymentDetail(); if (result.Type == "CreditCard") { tran.FinancialPaymentDetail.CurrencyTypeValueId = tenderType.DefinedValues.Where(t => t.Value == "Credit Card").FirstOrDefault().Id; } else { tran.TransactionTypeValueId = tenderType.DefinedValues.Where(t => t.Value == "Credit Card").FirstOrDefault().Id; } Person person = null; // Find the person this transaction belongs to // 1. First start by determining whether this was a person // paying their application fee or contributing to themselves // because then we can just use their member info if (result.UserId > 0 && result.DonatedToUserId == result.UserId && result.DonatedToFirstName == result.FirstName && result.DonatedToLastName == result.LastName) { var memberRequest = new RestRequest("v1/members/{userId}", Method.GET); memberRequest.AddUrlSegment("userId", result.UserId.ToString()); var memberResult = client.Execute <Contracts.Member>(memberRequest); if (memberResult.Data != null && memberResult.Data.ArenaId > 0) { try { Person personMatch = personAliasService.Queryable().Where(pa => pa.AliasPersonId == memberResult.Data.ArenaId).Select(pa => pa.Person).FirstOrDefault(); if (personMatch == null) { throw new Exception("Person not found: " + memberResult.Data.ArenaId); } person = personMatch; } catch (Exception e) { warnings += "Loading the person failed transaction id " + result.TransactionId + " for " + result.FirstName + " " + result.LastName + " with the following error: " + e.Message + Environment.NewLine; processed++; continue; } } } // 2. If we didn't get a person match via their Alias Id // then just use the standard person match logic if (person == null) { String street1 = null; String postalCode = null; if (result.Address != null) { street1 = result.Address.Address1; postalCode = result.Address.Zip; } List <Person> matches = personService.GetByMatch(result.FirstName.Trim(), result.LastName.Trim(), null, result.Email, null, street1, postalCode).ToList(); if (matches.Count > 1) { // Find the oldest member record in the list person = matches.Where(p => p.ConnectionStatusValue.Value == "Member").OrderBy(p => p.Id).FirstOrDefault(); if (person == null) { // Find the oldest attendee record in the list person = matches.Where(p => p.ConnectionStatusValue.Value == "Attendee").OrderBy(p => p.Id).FirstOrDefault(); if (person == null) { person = matches.OrderBy(p => p.Id).First(); } } } else if (matches.Count == 1) { person = matches.First(); } else { // Create the person person = new Person(); person.FirstName = result.FirstName.Trim(); person.LastName = result.LastName.Trim(); if (result.Email.IsValidEmail()) { person.Email = result.Email.Trim(); } person.RecordTypeValueId = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.PERSON_RECORD_TYPE_PERSON.AsGuid()).Id; person.ConnectionStatusValueId = connectionStatus.Id; person.RecordStatusValueId = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.PERSON_RECORD_STATUS_ACTIVE.AsGuid()).Id; Group family = PersonService.SaveNewPerson(person, dbContext); GroupLocation location = new GroupLocation(); location.GroupLocationTypeValueId = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.GROUP_LOCATION_TYPE_HOME).Id; location.Location = new Location() { Street1 = result.Address.Address1, Street2 = result.Address.Address2, City = result.Address.City, State = result.Address.State, PostalCode = result.Address.Zip, Country = result.Address.Country }; family.CampusId = CampusCache.All().FirstOrDefault().Id; family.GroupLocations.Add(location); dbContext.SaveChanges(); } } // Get details about the transaction from our PayPal report table Transaction tx = transactionService.Get(result.PaymentProcessorTransactionId); if (tx != null) { if (tx.TenderType.Contains("ACH")) { result.Type = "ACH"; result.Method = null; } else { result.Type = "Credit Card"; result.Method = tx.TenderType; } } else { // Defaults result.Type = "Credit Card"; result.Method = "Visa"; warnings += "Unable to find transaction in _org_secc_PaypalReporting_Transaction table: " + result.TransactionId + Environment.NewLine; } // If we don't have a batch, create one if (batch == null) { batch = new FinancialBatch(); batch.BatchStartDateTime = result.Date; batch.BatchEndDateTime = DateTime.Now; batch.Name = "Service Reef Payments"; batch.Status = BatchStatus.Open; financialBatchService.Add(batch); dbContext.SaveChanges(); } // Complete the FinancialTransaction tran.AuthorizedPersonAliasId = person.PrimaryAliasId; tran.BatchId = batch.Id; tran.Summary = "F" + specialFund.Id + ":$" + result.Amount.ToString(); tran.TransactionDateTime = result.Date; tran.FinancialGatewayId = gateway.Id; FinancialTransactionDetail financialTransactionDetail = new FinancialTransactionDetail(); financialTransactionDetail.AccountId = trip.Id; financialTransactionDetail.Amount = result.Amount.ToString().AsDecimal(); tran.TransactionDetails.Add(financialTransactionDetail); tran.TransactionTypeValueId = contribution.Id; tran.FinancialPaymentDetail = new FinancialPaymentDetail(); tran.FinancialPaymentDetail.CurrencyTypeValueId = tenderType.DefinedValues.Where(type => type.Value.ToLower() == result.Type.ToLower()).FirstOrDefault().Id; if (result.Method != null) { tran.FinancialPaymentDetail.CreditCardTypeValueId = creditCards.DefinedValues.Where(card => card.Value.ToLower() == result.Method.ToLower()).FirstOrDefault().Id; } tran.TransactionCode = result.PaymentProcessorTransactionId; tran.SourceTypeValueId = transactionSource.Id; financialTransactionService.Add(tran); dbContext.SaveChanges(); totalAmount += result.Amount; } } processed++; } } else { total = 0; } // Update the page number for the next request var pageParam = request.Parameters.Where(p => p.Name == "page").FirstOrDefault(); pageParam.Value = (int)pageParam.Value + 1; } } catch (Exception ex) { throw new Exception("ServiceReef Job Failed", ex); } finally { if (batch != null && totalAmount > 0) { batch.ControlAmount = (Decimal)totalAmount; } dbContext.SaveChanges(); } if (warnings.Length > 0) { throw new Exception(warnings); } context.Result = "Successfully imported " + processed + " transactions."; }
/// <summary> /// Adds the scheduled payment. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="schedule">The schedule.</param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialScheduledTransaction AddScheduledPayment(FinancialGateway financialGateway, PaymentSchedule schedule, PaymentInfo paymentInfo, out string errorMessage) { errorMessage = "The Payment Gateway only supports adding scheduled payment using a three-step process."; return(null); }
/// <summary> /// Performs the first step of adding a new payment schedule /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="schedule">The schedule.</param> /// <param name="paymentInfo">The payment information.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">paymentInfo</exception> public override string AddScheduledPaymentStep1(FinancialGateway financialGateway, PaymentSchedule schedule, PaymentInfo paymentInfo, out string errorMessage) { errorMessage = string.Empty; try { if (paymentInfo == null) { throw new ArgumentNullException("paymentInfo"); } var rootElement = GetRoot(financialGateway, "add-subscription"); rootElement.Add( new XElement("start-date", schedule.StartDate.ToString("yyyyMMdd")), new XElement("order-description", paymentInfo.Description), new XElement("currency", "USD"), new XElement("tax-amount", "0.00")); bool isReferencePayment = (paymentInfo is ReferencePaymentInfo); if (isReferencePayment) { var reference = paymentInfo as ReferencePaymentInfo; rootElement.Add(new XElement("customer-vault-id", reference.ReferenceNumber)); } if (paymentInfo.AdditionalParameters != null) { foreach (var keyValue in paymentInfo.AdditionalParameters) { XElement xElement = new XElement(keyValue.Key, keyValue.Value); rootElement.Add(xElement); } } rootElement.Add(GetPlan(schedule, paymentInfo)); rootElement.Add(GetBilling(paymentInfo)); XDocument xdoc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), rootElement); var result = PostToGateway(financialGateway, xdoc); if (result == null) { errorMessage = "Invalid Response from NMI!"; return(null); } if (result.GetValueOrNull("result") != "1") { errorMessage = result.GetValueOrNull("result-text"); return(null); } return(result.GetValueOrNull("form-url")); } catch (WebException webException) { string message = GetResponseMessage(webException.Response.GetResponseStream()); errorMessage = webException.Message + " - " + message; return(null); } catch (Exception ex) { errorMessage = ex.Message; return(null); } }
/// <summary> /// Performs the final step of a three-step charge. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="resultQueryString">The result query string from step 2.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialTransaction ChargeStep3(FinancialGateway financialGateway, string resultQueryString, out string errorMessage) { errorMessage = string.Empty; try { var rootElement = GetRoot(financialGateway, "complete-action"); rootElement.Add(new XElement("token-id", resultQueryString.Substring(10))); XDocument xdoc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"), rootElement); var result = PostToGateway(financialGateway, xdoc); if (result == null) { errorMessage = "Invalid Response from NMI!"; return(null); } if (result.GetValueOrNull("result") != "1") { errorMessage = result.GetValueOrNull("result-text"); string resultCodeMessage = GetResultCodeMessage(result); if (resultCodeMessage.IsNotNullOrWhitespace()) { errorMessage += string.Format(" ({0})", resultCodeMessage); } // write result error as an exception ExceptionLogService.LogException(new Exception($"Error processing NMI transaction. Result Code: {result.GetValueOrNull( "result-code" )} ({resultCodeMessage}). Result text: {result.GetValueOrNull( "result-text" )}. Card Holder Name: {result.GetValueOrNull( "first-name" )} {result.GetValueOrNull( "last-name" )}. Amount: {result.GetValueOrNull( "total-amount" )}. Transaction id: {result.GetValueOrNull( "transaction-id" )}. Descriptor: {result.GetValueOrNull( "descriptor" )}. Order description: {result.GetValueOrNull( "order-description" )}.")); return(null); } var transaction = new FinancialTransaction(); transaction.TransactionCode = result.GetValueOrNull("transaction-id"); transaction.ForeignKey = result.GetValueOrNull("customer-vault-id"); transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); string ccNumber = result.GetValueOrNull("billing_cc-number"); if (!string.IsNullOrWhiteSpace(ccNumber)) { // cc payment var curType = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD); transaction.FinancialPaymentDetail.NameOnCardEncrypted = Encryption.EncryptString($"{result.GetValueOrNull( "billing_first-name" )} {result.GetValueOrNull( "billing_last-name" )}"); transaction.FinancialPaymentDetail.CurrencyTypeValueId = curType != null ? curType.Id : (int?)null; transaction.FinancialPaymentDetail.CreditCardTypeValueId = CreditCardPaymentInfo.GetCreditCardType(ccNumber.Replace('*', '1').AsNumeric())?.Id; transaction.FinancialPaymentDetail.AccountNumberMasked = ccNumber.Masked(true); string mmyy = result.GetValueOrNull("billing_cc-exp"); if (!string.IsNullOrWhiteSpace(mmyy) && mmyy.Length == 4) { transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = Encryption.EncryptString(mmyy.Substring(0, 2)); transaction.FinancialPaymentDetail.ExpirationYearEncrypted = Encryption.EncryptString(mmyy.Substring(2, 2)); } } else { // ach payment var curType = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_ACH); transaction.FinancialPaymentDetail.CurrencyTypeValueId = curType != null ? curType.Id : (int?)null; transaction.FinancialPaymentDetail.AccountNumberMasked = result.GetValueOrNull("billing_account-number").Masked(true); } transaction.AdditionalLavaFields = new Dictionary <string, object>(); foreach (var keyVal in result) { transaction.AdditionalLavaFields.Add(keyVal.Key, keyVal.Value); } return(transaction); } catch (WebException webException) { string message = GetResponseMessage(webException.Response.GetResponseStream()); errorMessage = webException.Message + " - " + message; return(null); } catch (Exception ex) { errorMessage = ex.Message; return(null); } }
/// <summary> /// Charges the specified payment info. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialTransaction Charge(FinancialGateway financialGateway, PaymentInfo paymentInfo, out string errorMessage) { errorMessage = "The Payment Gateway only supports a three-step charge."; return(null); }
/// <summary> /// Gets a value indicating whether [address required]. /// </summary> /// <value> /// <c>true</c> if [address required]; otherwise, <c>false</c>. /// </value> public override bool PromptForBillingAddress(FinancialGateway financialGateway) { return(GetAttributeValue(financialGateway, "PromptForAddress").AsBoolean()); }
/// <summary> /// Gets the proxy client. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <returns></returns> private TransactionProcessorClient GetProxyClient( FinancialGateway financialGateway, out string errorMessage ) { errorMessage = string.Empty; string merchantID = GetAttributeValue( financialGateway, "MerchantID" ); string transactionkey = GetAttributeValue( financialGateway, "TransactionKey" ); string gatewayEndpoint = null; if ( GetAttributeValue( financialGateway, "Mode" ).Equals( "Live", StringComparison.CurrentCultureIgnoreCase ) ) { gatewayEndpoint = GetAttributeValue( financialGateway, "LiveGatewayURL" ); } else { gatewayEndpoint = GetAttributeValue( financialGateway, "TestGatewayURL" ); } EndpointAddress address = null; if ( !string.IsNullOrEmpty( gatewayEndpoint ) ) { gatewayEndpoint = gatewayEndpoint.EnsureTrailingForwardslash(); address = new EndpointAddress( string.Format( "{0}CyberSourceTransaction_{1}", gatewayEndpoint, GATEWAY_VERSION ) ); } else { errorMessage = "Financial gateway is not configured with a valid endpoint."; return null; } BasicHttpBinding binding = new BasicHttpBinding(); binding.Name = "ITransactionProcessor"; binding.MaxBufferSize = 2147483647; binding.MaxBufferPoolSize = 2147483647; binding.MaxReceivedMessageSize = 2147483647; binding.ReaderQuotas.MaxDepth = 2147483647; binding.ReaderQuotas.MaxArrayLength = 2147483647; binding.ReaderQuotas.MaxBytesPerRead = 2147483647; binding.ReaderQuotas.MaxStringContentLength = 2147483647; binding.Security.Mode = BasicHttpSecurityMode.TransportWithMessageCredential; var proxy = new TransactionProcessorClient( binding, address ); proxy.ClientCredentials.UserName.UserName = merchantID; proxy.ClientCredentials.UserName.Password = transactionkey; proxy.Endpoint.Address = address; proxy.Endpoint.Binding = binding; return proxy; }
/// <summary> /// Gets the payments that have been processed for any scheduled transactions /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="startDate">The start date.</param> /// <param name="endDate">The end date.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override List <Payment> GetPayments(FinancialGateway financialGateway, DateTime startDate, DateTime endDate, out string errorMessage) { errorMessage = string.Empty; var txns = new List <Payment>(); var restClient = new RestClient(GetAttributeValue(financialGateway, "QueryUrl")); var restRequest = new RestRequest(Method.GET); restRequest.AddParameter("username", GetAttributeValue(financialGateway, "AdminUsername")); restRequest.AddParameter("password", GetAttributeValue(financialGateway, "AdminPassword")); restRequest.AddParameter("start_date", startDate.ToString("yyyyMMddHHmmss")); restRequest.AddParameter("end_date", endDate.ToString("yyyyMMddHHmmss")); try { var response = restClient.Execute(restRequest); if (response != null) { if (response.StatusCode == HttpStatusCode.OK) { var xdocResult = GetXmlResponse(response); if (xdocResult != null) { var errorResponse = xdocResult.Root.Element("error_response"); if (errorResponse != null) { errorMessage = errorResponse.Value; } else { foreach (var xTxn in xdocResult.Root.Elements("transaction")) { Payment payment = new Payment(); payment.TransactionCode = GetXElementValue(xTxn, "transaction_id"); payment.Status = GetXElementValue(xTxn, "condition").FixCase(); payment.IsFailure = payment.Status == "Failed" || payment.Status == "Abandoned" || payment.Status == "Canceled"; payment.TransactionCode = GetXElementValue(xTxn, "transaction_id"); payment.GatewayScheduleId = GetXElementValue(xTxn, "original_transaction_id").Trim(); var statusMessage = new StringBuilder(); DateTime?txnDateTime = null; foreach (var xAction in xTxn.Elements("action")) { DateTime?actionDate = ParseDateValue(GetXElementValue(xAction, "date")); string actionType = GetXElementValue(xAction, "action_type"); string responseText = GetXElementValue(xAction, "response_text"); if (actionDate.HasValue) { statusMessage.AppendFormat("{0} {1}: {2}; Status: {3}", actionDate.Value.ToShortDateString(), actionDate.Value.ToShortTimeString(), actionType.FixCase(), responseText); statusMessage.AppendLine(); } decimal?txnAmount = GetXElementValue(xAction, "amount").AsDecimalOrNull(); if (txnAmount.HasValue && actionDate.HasValue) { payment.Amount = txnAmount.Value; } if (actionType == "sale") { txnDateTime = actionDate.Value; } if (actionType == "settle") { payment.IsSettled = true; payment.SettledGroupId = GetXElementValue(xAction, "processor_batch_id").Trim(); payment.SettledDate = actionDate; txnDateTime = txnDateTime.HasValue ? txnDateTime.Value : actionDate.Value; } } if (txnDateTime.HasValue) { payment.TransactionDateTime = txnDateTime.Value; payment.StatusMessage = statusMessage.ToString(); txns.Add(payment); } } } } else { errorMessage = "Invalid XML Document Returned From Gateway!"; } } else { errorMessage = string.Format("Invalid Response from Gateway: [{0}] {1}", response.StatusCode.ConvertToString(), response.ErrorMessage); } } else { errorMessage = "Null Response From Gateway!"; } } catch (WebException webException) { string message = GetResponseMessage(webException.Response.GetResponseStream()); throw new Exception(webException.Message + " - " + message); } return(txns); }
/// <summary> /// Safely load entities that have not yet been assigned a non-null value based on the arguments. /// </summary> private void LoadEntities() { if (_automatedPaymentArgs.ScheduledTransactionId.HasValue && _financialScheduledTransaction == null) { _financialScheduledTransaction = _financialScheduledTransactionService.Queryable() .AsNoTracking() .Include(s => s.TransactionFrequencyValue) .FirstOrDefault(s => s.Id == _automatedPaymentArgs.ScheduledTransactionId.Value); } if (_authorizedPerson == null) { _authorizedPerson = _personAliasService.GetPersonNoTracking(_automatedPaymentArgs.AuthorizedPersonAliasId); } if (_financialGateway == null) { _financialGateway = _financialGatewayService.GetNoTracking(_automatedPaymentArgs.AutomatedGatewayId); } if (_financialGateway != null && _automatedGatewayComponent == null) { _automatedGatewayComponent = _financialGateway.GetGatewayComponent(); } if (_financialAccounts == null) { var accountIds = _automatedPaymentArgs.AutomatedPaymentDetails.Select(d => d.AccountId).ToList(); _financialAccounts = _financialAccountService.GetByIds(accountIds).AsNoTracking().ToDictionary(fa => fa.Id, fa => fa); } if (_authorizedPerson != null && _financialPersonSavedAccount == null && _financialGateway != null) { // Pick the correct saved account based on args or default for the user var financialGatewayId = _financialGateway.Id; var savedAccounts = _financialPersonSavedAccountService .GetByPersonId(_authorizedPerson.Id) .AsNoTracking() .Where(sa => sa.FinancialGatewayId == financialGatewayId) .Include(sa => sa.FinancialPaymentDetail) .OrderByDescending(sa => sa.CreatedDateTime ?? DateTime.MinValue) .ToList(); if (_automatedPaymentArgs.FinancialPersonSavedAccountId.HasValue) { // If there is an indicated saved account to use, don't assume any other saved account even with a schedule var savedAccountId = _automatedPaymentArgs.FinancialPersonSavedAccountId.Value; _financialPersonSavedAccount = savedAccounts.FirstOrDefault(sa => sa.Id == savedAccountId); } else { // If there is a schedule and no indicated saved account to use, try to use payment info associated with the schedule if (_financialScheduledTransaction != null) { _financialPersonSavedAccount = // sa.ReferenceNumber == fst.TransactionCode savedAccounts.FirstOrDefault(sa => !string.IsNullOrEmpty(sa.ReferenceNumber) && sa.ReferenceNumber == _financialScheduledTransaction.TransactionCode) ?? // sa.GatewayPersonIdentifier == fst.TransactionCode savedAccounts.FirstOrDefault(sa => !string.IsNullOrEmpty(sa.GatewayPersonIdentifier) && sa.GatewayPersonIdentifier == _financialScheduledTransaction.TransactionCode) ?? // sa.FinancialPaymentDetailId == fst.FinancialPaymentDetailId savedAccounts.FirstOrDefault(sa => sa.FinancialPaymentDetailId.HasValue && sa.FinancialPaymentDetailId == _financialScheduledTransaction.FinancialPaymentDetailId) ?? // sa.TransactionCode == fst.TransactionCode savedAccounts.FirstOrDefault(sa => !string.IsNullOrEmpty(sa.TransactionCode) && sa.TransactionCode == _financialScheduledTransaction.TransactionCode); } if (_financialPersonSavedAccount == null) { // Use the default or first if no default _financialPersonSavedAccount = savedAccounts.FirstOrDefault(sa => sa.IsDefault) ?? savedAccounts.FirstOrDefault(); } } } if (_financialPersonSavedAccount != null && _referencePaymentInfo == null) { _referencePaymentInfo = _financialPersonSavedAccount.GetReferencePayment(); } if (_transactionType == null) { _transactionType = DefinedValueCache.Get(_automatedPaymentArgs.TransactionTypeGuid ?? SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid()); } if (_financialSource == null) { _financialSource = DefinedValueCache.Get(_automatedPaymentArgs.FinancialSourceGuid ?? SystemGuid.DefinedValue.FINANCIAL_SOURCE_TYPE_WEBSITE.AsGuid()); } }
/// <summary> /// Gets a value indicating whether the gateway requires the name on card for CC processing /// </summary> /// <value> /// <c>true</c> if [name on card required]; otherwise, <c>false</c>. /// </value> public override bool PromptForNameOnCard(FinancialGateway financialGateway) { return(GetAttributeValue(financialGateway, "PromptForName").AsBoolean()); }
/// <summary> /// Gets a value indicating whether [address required]. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <returns></returns> /// <value> /// <c>true</c> if [address required]; otherwise, <c>false</c>. /// </value> public override bool PromptForBillingAddress(FinancialGateway financialGateway) { return(false); }
/// <summary> /// Process the data read from the card reader and generate the transaction. /// </summary> /// <param name="swipeData">The data read from the card.</param> private void ProcessSwipe(string swipeData) { try { using (var rockContext = new RockContext()) { // create swipe object SwipePaymentInfo swipeInfo = new SwipePaymentInfo(swipeData); swipeInfo.Amount = tbAmount.Text.AsDecimal(); // add comment to the transation swipeInfo.Comment1 = PageParameter("Memo"); // get gateway FinancialGateway financialGateway = null; GatewayComponent gateway = null; Guid? gatewayGuid = GetAttributeValue("CreditCardGateway").AsGuidOrNull(); if (gatewayGuid.HasValue) { financialGateway = new FinancialGatewayService(rockContext).Get(gatewayGuid.Value); if (financialGateway != null) { financialGateway.LoadAttributes(rockContext); } gateway = financialGateway.GetGatewayComponent(); } if (gateway == null) { lSwipeErrors.Text = "<div class='alert alert-danger'>Invalid gateway provided. Please provide a gateway. Transaction not processed.</div>"; return; } // // Process the transaction. // string errorMessage = string.Empty; var transaction = gateway.Charge(financialGateway, swipeInfo, out errorMessage); if (transaction == null) { lSwipeErrors.Text = String.Format("<div class='alert alert-danger'>An error occurred while process this transaction. Message: {0}</div>", errorMessage); return; } _transactionCode = transaction.TransactionCode; // // Set some common information about the transaction. // transaction.AuthorizedPersonAliasId = new PersonService(rockContext).Get(SelectedPersonGuid).PrimaryAliasId; transaction.TransactionDateTime = RockDateTime.Now; transaction.FinancialGatewayId = financialGateway.Id; transaction.TransactionTypeValueId = DefinedValueCache.Read(GetAttributeValue("TransactionType")).Id; transaction.SourceTypeValueId = DefinedValueCache.Read(GetAttributeValue("Source")).Id; transaction.Summary = swipeInfo.Comment1; // // Ensure we have payment details. // if (transaction.FinancialPaymentDetail == null) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } transaction.FinancialPaymentDetail.SetFromPaymentInfo(swipeInfo, gateway, rockContext); var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = swipeInfo.Amount; transactionDetail.AccountId = new FinancialAccountService(rockContext).Get(GetAttributeValue("Account").AsGuid()).Id; transaction.TransactionDetails.Add(transactionDetail); var batchService = new FinancialBatchService(rockContext); // Get the batch var batch = batchService.Get( GetAttributeValue("BatchNamePrefix"), swipeInfo.CurrencyTypeValue, swipeInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, financialGateway.GetBatchTimeOffset()); var batchChanges = new List <string>(); if (batch.Id == 0) { batchChanges.Add("Generated the batch"); History.EvaluateChange(batchChanges, "Batch Name", string.Empty, batch.Name); History.EvaluateChange(batchChanges, "Status", null, batch.Status); History.EvaluateChange(batchChanges, "Start Date/Time", null, batch.BatchStartDateTime); History.EvaluateChange(batchChanges, "End Date/Time", null, batch.BatchEndDateTime); } decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange(batchChanges, "Control Amount", batch.ControlAmount.FormatAsCurrency(), newControlAmount.FormatAsCurrency()); batch.ControlAmount = newControlAmount; transaction.BatchId = batch.Id; batch.Transactions.Add(transaction); rockContext.WrapTransaction(() => { rockContext.SaveChanges(); HistoryService.SaveChanges( rockContext, typeof(FinancialBatch), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, batchChanges ); }); ShowReceiptPanel(); } } catch (Exception ex) { lSwipeErrors.Text = String.Format("<div class='alert alert-danger'>An error occurred while process this transaction. Message: {0}</div>", ex.Message); } }
/// <summary> /// Gets the payments that have been processed for any scheduled transactions /// </summary> /// <param name="financialGateway"></param> /// <param name="startDate">The start date.</param> /// <param name="endDate">The end date.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override List <Payment> GetPayments(FinancialGateway financialGateway, DateTime startDate, DateTime endDate, out string errorMessage) { var reportingApi = new Reporting.Api( GetAttributeValue(financialGateway, "User"), GetAttributeValue(financialGateway, "Vendor"), GetAttributeValue(financialGateway, "Partner"), GetAttributeValue(financialGateway, "Password"), GetAttributeValue(financialGateway, "Mode").Equals("Test", StringComparison.CurrentCultureIgnoreCase)); // Query the PayFlowPro Recurring Billing Report for transactions that were processed during data range var recurringBillingParams = new Dictionary <string, string>(); recurringBillingParams.Add("start_date", startDate.ToString("yyyy-MM-dd HH:mm:ss")); recurringBillingParams.Add("end_date", endDate.ToString("yyyy-MM-dd HH:mm:ss")); recurringBillingParams.Add("include_declines", "false"); DataTable recurringBillingTable = reportingApi.GetReport("RecurringBillingReport", recurringBillingParams, out errorMessage); if (recurringBillingTable != null) { // The Recurring Billing Report items does not include the amounts for each transaction, so need // to run a custom report to try and get the amount/tender type for each transaction var transactionCodes = new Dictionary <string, int>(); var customParams = new Dictionary <string, string>(); customParams.Add("start_date", startDate.ToString("yyyy-MM-dd HH:mm:ss")); customParams.Add("end_date", endDate.ToString("yyyy-MM-dd HH:mm:ss")); customParams.Add("maximum_amount", "1000000"); customParams.Add("results", "Approvals Only"); customParams.Add("recurring_only", "true"); customParams.Add("show_order_id", "false"); customParams.Add("show_transaction_id", "true"); customParams.Add("show_time", "false"); customParams.Add("show_type", "false"); customParams.Add("show_tender_type", "true"); customParams.Add("show_account_number", "false"); customParams.Add("show_expires", "false"); customParams.Add("show_aba_routing_number", "false"); customParams.Add("show_amount", "true"); customParams.Add("show_result_code", "true"); customParams.Add("show_response_msg", "false"); customParams.Add("show_comment1", "false"); customParams.Add("show_comment2", "false"); customParams.Add("show_tax_amount", "false"); customParams.Add("show_purchase_order", "false"); customParams.Add("show_original_transaction_id", "false"); customParams.Add("show_avs_street_match", "false"); customParams.Add("show_avs_zip_match", "false"); customParams.Add("show_invoice_number", "false"); customParams.Add("show_authcode", "false"); customParams.Add("show_batch_id", "false"); customParams.Add("show_csc_match", "false"); customParams.Add("show_billing_first_name", "false"); customParams.Add("show_billing_last_name", "false"); customParams.Add("show_billing_company_name", "false"); customParams.Add("show_billing_address", "false"); customParams.Add("show_billing_city", "false"); customParams.Add("show_billing_state", "false"); customParams.Add("show_billing_zip", "false"); customParams.Add("show_billing_email", "false"); customParams.Add("show_billing_country", "false"); customParams.Add("show_shipping_first_name", "false"); customParams.Add("show_shipping_last_name", "false"); customParams.Add("show_shipping_address", "false"); customParams.Add("show_shipping_city", "false"); customParams.Add("show_shipping_state", "false"); customParams.Add("show_shipping_zip", "false"); customParams.Add("show_shipping_country", "false"); customParams.Add("show_customer_code", "false"); customParams.Add("show_freight_amount", "false"); customParams.Add("show_duty_amount", "false"); DataTable customTable = reportingApi.GetReport("CustomReport", customParams, out errorMessage); if (customTable != null) { for (int i = 0; i < customTable.Rows.Count; i++) { transactionCodes.Add(customTable.Rows[i]["Transaction Id"].ToString().Trim(), i); } } var txns = new List <Payment>(); var transactionIdParams = new Dictionary <string, string>(); transactionIdParams.Add("transaction_id", string.Empty); var creditCardTypes = DefinedTypeCache.Get(Rock.SystemGuid.DefinedType.FINANCIAL_CREDIT_CARD_TYPE.AsGuid()).DefinedValues; foreach (DataRow recurringBillingRow in recurringBillingTable.Rows) { bool foundTxn = false; string transactionId = recurringBillingRow["Transaction ID"].ToString().Trim(); decimal amount = decimal.MinValue; string tenderType = string.Empty; if (transactionCodes.ContainsKey(transactionId)) { int rowNumber = transactionCodes[transactionId]; amount = decimal.TryParse(customTable.Rows[rowNumber]["Amount"].ToString(), out amount) ? (amount / 100) : 0.0M; tenderType = customTable.Rows[rowNumber]["Tender Type"].ToString(); foundTxn = true; } else { // If the custom report did not include the transaction, run a transactionIDSearch report to get the amount and tender type transactionIdParams["transaction_id"] = transactionId; DataTable transactionIdTable = reportingApi.GetSearch("TransactionIDSearch", transactionIdParams, out errorMessage); if (transactionIdTable != null && transactionIdTable.Rows.Count == 1) { amount = decimal.TryParse(transactionIdTable.Rows[0]["Amount"].ToString(), out amount) ? (amount / 100) : 0.0M; tenderType = transactionIdTable.Rows[0]["Tender Type"].ToString(); foundTxn = true; } } if (foundTxn) { var payment = new Payment(); payment.Amount = amount; payment.TransactionDateTime = recurringBillingRow["Time"].ToString().AsDateTime() ?? DateTime.MinValue; payment.TransactionCode = recurringBillingRow["Transaction ID"].ToString().Trim(); payment.GatewayScheduleId = recurringBillingRow["Profile ID"].ToString(); payment.ScheduleActive = recurringBillingRow["Status"].ToString() == "Active"; payment.CreditCardTypeValue = creditCardTypes.Where(t => t.Value == tenderType).FirstOrDefault(); txns.Add(payment); } else { errorMessage = "The TransactionIDSearch report did not return a value for transaction: " + recurringBillingRow["Transaction ID"].ToString(); return(null); } } return(txns); } errorMessage = "The RecurringBillingReport report did not return any data"; return(null); }
private void SaveTransaction(FinancialGateway financialGateway, GatewayComponent gateway, Person person, PaymentInfo paymentInfo, FinancialTransaction transaction, RockContext rockContext) { transaction.AuthorizedPersonAliasId = person.PrimaryAliasId; if (RockTransactionEntry != null) { RockCheckBox cbGiveAnonymouslyControl = (( RockCheckBox )(RockTransactionEntry.FindControl("cbGiveAnonymously"))); if (cbGiveAnonymouslyControl != null) { transaction.ShowAsAnonymous = cbGiveAnonymouslyControl.Checked; } } transaction.TransactionDateTime = RockDateTime.Now; transaction.FinancialGatewayId = financialGateway.Id; var txnType = DefinedValueCache.Get(this.GetAttributeValue("TransactionType").AsGuidOrNull() ?? Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid()); transaction.TransactionTypeValueId = txnType.Id; transaction.Summary = paymentInfo.Comment1; if (transaction.FinancialPaymentDetail == null) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } transaction.FinancialPaymentDetail.SetFromPaymentInfo(paymentInfo, gateway, rockContext); Guid sourceGuid = Guid.Empty; if (Guid.TryParse(GetAttributeValue("Source"), out sourceGuid)) { var source = DefinedValueCache.Get(sourceGuid); if (source != null) { transaction.SourceTypeValueId = source.Id; } } var transactionEntity = this.GetTransactionEntity(); foreach (var account in GetSelectedAccounts().Where(a => a.Amount > 0)) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = account.Amount; transactionDetail.AccountId = account.Id; if (transactionEntity != null) { transactionDetail.EntityTypeId = transactionEntity.TypeId; transactionDetail.EntityId = transactionEntity.Id; } transaction.TransactionDetails.Add(transactionDetail); } var batchService = new FinancialBatchService(rockContext); // Get the batch var batch = batchService.Get( GetAttributeValue("BatchNamePrefix"), paymentInfo.CurrencyTypeValue, paymentInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, financialGateway.GetBatchTimeOffset()); var batchChanges = new History.HistoryChangeList(); if (batch.Id == 0) { batchChanges.AddCustom("Add", "Record", "Generated the batch"); History.EvaluateChange(batchChanges, "Batch Name", string.Empty, batch.Name); History.EvaluateChange(batchChanges, "Status", null, batch.Status); History.EvaluateChange(batchChanges, "Start Date/Time", null, batch.BatchStartDateTime); History.EvaluateChange(batchChanges, "End Date/Time", null, batch.BatchEndDateTime); } decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange(batchChanges, "Control Amount", batch.ControlAmount.FormatAsCurrency(), newControlAmount.FormatAsCurrency()); batch.ControlAmount = newControlAmount; transaction.BatchId = batch.Id; transaction.LoadAttributes(rockContext); var allowedTransactionAttributes = GetAttributeValue("AllowedTransactionAttributesFromURL").Split(',').AsGuidList().Select(x => AttributeCache.Get(x).Key); foreach (KeyValuePair <string, AttributeValueCache> attr in transaction.AttributeValues) { if (PageParameters().ContainsKey("Attribute_" + attr.Key) && allowedTransactionAttributes.Contains(attr.Key)) { attr.Value.Value = Server.UrlDecode(PageParameter("Attribute_" + attr.Key)); } } batch.Transactions.Add(transaction); rockContext.SaveChanges(); transaction.SaveAttributeValues(); HistoryService.SaveChanges( rockContext, typeof(FinancialBatch), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, batchChanges ); SendReceipt(transaction.Id); TransactionCode = transaction.TransactionCode; }
/// <summary> /// Shows the readonly details. /// </summary> /// <param name="gateway">The gateway.</param> private void ShowReadonlyDetails( FinancialGateway gateway ) { SetEditMode( false ); lActionTitle.Text = gateway.Name.FormatAsHtmlTitle(); hlInactive.Visible = !gateway.IsActive; lGatewayDescription.Text = gateway.Description; DescriptionList descriptionList = new DescriptionList(); if ( gateway.EntityType != null ) { descriptionList.Add( "Gateway Type", gateway.EntityType.Name ); } var timeSpan = gateway.GetBatchTimeOffset(); if ( timeSpan.Ticks > 0 ) { descriptionList.Add( "Batch Time Offset", timeSpan.ToString() ); } lblMainDetails.Text = descriptionList.Html; }
/// <summary> /// Adds the scheduled payment. /// </summary> /// <param name="financialGateway"></param> /// <param name="schedule">The schedule.</param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialScheduledTransaction AddScheduledPayment(FinancialGateway financialGateway, PaymentSchedule schedule, PaymentInfo paymentInfo, out string errorMessage) { errorMessage = "This gateway does not support adding scheduled transactions. Transactions should be created through the Reach interface."; return(null); }
/// <summary> /// Processes the payments. /// </summary> /// <param name="gateway">The gateway.</param> /// <param name="batchNamePrefix">The batch name prefix.</param> /// <param name="payments">The payments.</param> /// <param name="batchUrlFormat">The batch URL format.</param> /// <returns></returns> public static string ProcessPayments( FinancialGateway gateway, string batchNamePrefix, List<Payment> payments, string batchUrlFormat = "" ) { int totalPayments = 0; int totalAlreadyDownloaded = 0; int totalNoScheduledTransaction = 0; int totalAdded = 0; var batches = new List<FinancialBatch>(); var batchSummary = new Dictionary<Guid, List<Payment>>(); var initialControlAmounts = new Dictionary<Guid, decimal>(); var allBatchChanges = new Dictionary<Guid, List<string>>(); var allTxnChanges = new Dictionary<Guid, List<string>>(); var txnPersonNames = new Dictionary<Guid, string>(); using ( var rockContext = new RockContext() ) { var accountService = new FinancialAccountService( rockContext ); var txnService = new FinancialTransactionService( rockContext ); var batchService = new FinancialBatchService( rockContext ); var scheduledTxnService = new FinancialScheduledTransactionService( rockContext ); var contributionTxnType = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid() ); var defaultAccount = accountService.Queryable() .Where( a => a.IsActive && !a.ParentAccountId.HasValue && ( !a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now ) && ( !a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now ) ) .OrderBy( a => a.Order ) .FirstOrDefault(); var batchTxnChanges = new Dictionary<Guid, List<string>>(); var batchBatchChanges = new Dictionary<Guid, List<string>>(); foreach ( var payment in payments.Where( p => p.Amount > 0.0M ) ) { totalPayments++; // Only consider transactions that have not already been added if ( txnService.GetByTransactionCode( payment.TransactionCode ) == null ) { var scheduledTransaction = scheduledTxnService.GetByScheduleId( payment.GatewayScheduleId ); if ( scheduledTransaction != null ) { scheduledTransaction.IsActive = payment.ScheduleActive; var txnChanges = new List<string>(); var transaction = new FinancialTransaction(); transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); transaction.Guid = Guid.NewGuid(); allTxnChanges.Add( transaction.Guid, txnChanges ); txnChanges.Add( "Created Transaction (Downloaded from Gateway)" ); transaction.TransactionCode = payment.TransactionCode; History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.TransactionCode ); transaction.TransactionDateTime = payment.TransactionDateTime; History.EvaluateChange( txnChanges, "Date/Time", null, transaction.TransactionDateTime ); transaction.ScheduledTransactionId = scheduledTransaction.Id; transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId; History.EvaluateChange( txnChanges, "Person", string.Empty, scheduledTransaction.AuthorizedPersonAlias.Person.FullName ); txnPersonNames.Add( transaction.Guid, scheduledTransaction.AuthorizedPersonAlias.Person.FullName ); transaction.FinancialGatewayId = gateway.Id; History.EvaluateChange( txnChanges, "Gateway", string.Empty, gateway.Name ); transaction.TransactionTypeValueId = contributionTxnType.Id; History.EvaluateChange( txnChanges, "Type", string.Empty, contributionTxnType.Value ); var currencyTypeValue = payment.CurrencyTypeValue; var creditCardTypevalue = payment.CreditCardTypeValue; if ( scheduledTransaction.FinancialPaymentDetail != null ) { if ( currencyTypeValue == null && scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.HasValue ) { currencyTypeValue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.Value ); } if ( creditCardTypevalue == null && scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.HasValue ) { creditCardTypevalue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.Value ); } transaction.FinancialPaymentDetail.AccountNumberMasked = scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked; transaction.FinancialPaymentDetail.NameOnCardEncrypted = scheduledTransaction.FinancialPaymentDetail.NameOnCardEncrypted; transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted; transaction.FinancialPaymentDetail.ExpirationYearEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationYearEncrypted; } if ( currencyTypeValue != null ) { transaction.FinancialPaymentDetail.CurrencyTypeValueId = currencyTypeValue.Id; History.EvaluateChange( txnChanges, "Currency Type", string.Empty, currencyTypeValue.Value ); } if ( creditCardTypevalue != null ) { transaction.FinancialPaymentDetail.CreditCardTypeValueId = creditCardTypevalue.Id; History.EvaluateChange( txnChanges, "Credit Card Type", string.Empty, creditCardTypevalue.Value ); } //transaction.SourceTypeValueId = DefinedValueCache.Read( sourceGuid ).Id; // Try to allocate the amount of the transaction based on the current scheduled transaction accounts decimal remainingAmount = payment.Amount; foreach ( var detail in scheduledTransaction.ScheduledTransactionDetails.Where( d => d.Amount != 0.0M ) ) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.AccountId = detail.AccountId; if ( detail.Amount <= remainingAmount ) { // If the configured amount for this account is less than or equal to the remaining // amount, allocate the configured amount transactionDetail.Amount = detail.Amount; remainingAmount -= detail.Amount; } else { // If the configured amount is greater than the remaining amount, only allocate // the remaining amount transaction.Summary = "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction."; detail.Amount = remainingAmount; detail.Summary = "Note: The downloaded amount was not enough to apply the configured amount to this account."; remainingAmount = 0.0M; } transaction.TransactionDetails.Add( transactionDetail ); History.EvaluateChange( txnChanges, detail.Account.Name, 0.0M.ToString( "C2" ), transactionDetail.Amount.ToString( "C2" ) ); History.EvaluateChange( txnChanges, "Summary", string.Empty, transactionDetail.Summary ); if ( remainingAmount <= 0.0M ) { // If there's no amount left, break out of details break; } } // If there's still amount left after allocating based on current config, add the remainder // to the account that was configured for the most amount if ( remainingAmount > 0.0M ) { transaction.Summary = "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction."; var transactionDetail = transaction.TransactionDetails .OrderByDescending( d => d.Amount ) .First(); if ( transactionDetail == null && defaultAccount != null ) { transactionDetail = new FinancialTransactionDetail(); transactionDetail.AccountId = defaultAccount.Id; } if ( transactionDetail != null ) { transactionDetail.Amount += remainingAmount; transactionDetail.Summary = "Note: Extra amount was applied to this account."; } History.EvaluateChange( txnChanges, defaultAccount.Name, 0.0M.ToString( "C2" ), transactionDetail.Amount.ToString( "C2" ) ); History.EvaluateChange( txnChanges, "Summary", string.Empty, transactionDetail.Summary ); } // Get the batch var batch = batchService.Get( batchNamePrefix, currencyTypeValue, creditCardTypevalue, transaction.TransactionDateTime.Value, gateway.GetBatchTimeOffset(), batches ); var batchChanges = new List<string>(); if ( batch.Id != 0 ) { initialControlAmounts.AddOrIgnore( batch.Guid, batch.ControlAmount ); } batch.ControlAmount += transaction.TotalAmount; batch.Transactions.Add( transaction ); // Add summary if ( !batchSummary.ContainsKey( batch.Guid ) ) { batchSummary.Add( batch.Guid, new List<Payment>() ); } batchSummary[batch.Guid].Add( payment ); totalAdded++; } else { totalNoScheduledTransaction++; } } else { totalAlreadyDownloaded++; } } foreach ( var batch in batches ) { var batchChanges = new List<string>(); allBatchChanges.Add( batch.Guid, batchChanges ); if ( batch.Id == 0 ) { batchChanges.Add( "Generated the batch" ); History.EvaluateChange( batchChanges, "Batch Name", string.Empty, batch.Name ); History.EvaluateChange( batchChanges, "Status", null, batch.Status ); History.EvaluateChange( batchChanges, "Start Date/Time", null, batch.BatchStartDateTime ); History.EvaluateChange( batchChanges, "End Date/Time", null, batch.BatchEndDateTime ); } if ( initialControlAmounts.ContainsKey( batch.Guid ) ) { History.EvaluateChange( batchChanges, "Control Amount", initialControlAmounts[batch.Guid].ToString( "C2" ), batch.ControlAmount.ToString( "C2" ) ); } } rockContext.WrapTransaction( () => { rockContext.SaveChanges(); foreach ( var batch in batches ) { HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, allBatchChanges[batch.Guid] ); foreach ( var transaction in batch.Transactions ) { if ( allTxnChanges.ContainsKey( transaction.Guid ) ) { HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), batch.Id, allTxnChanges[transaction.Guid], txnPersonNames[transaction.Guid], typeof( FinancialTransaction ), transaction.Id ); } } } rockContext.SaveChanges(); } ); } StringBuilder sb = new StringBuilder(); sb.AppendFormat( "<li>{0} {1} downloaded.</li>", totalPayments.ToString( "N0" ), ( totalPayments == 1 ? "payment" : "payments" ) ); if ( totalAlreadyDownloaded > 0 ) { sb.AppendFormat( "<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString( "N0" ), ( totalAlreadyDownloaded == 1 ? "payment was" : "payments were" ), ( totalAlreadyDownloaded == 1 ? "has" : "have" ) ); } if ( totalNoScheduledTransaction > 0 ) { sb.AppendFormat( "<li>{0} {1} could not be matched to an existing scheduled payment profile.</li>", totalNoScheduledTransaction.ToString( "N0" ), ( totalNoScheduledTransaction == 1 ? "payment" : "payments" ) ); } sb.AppendFormat( "<li>{0} {1} successfully added.</li>", totalAdded.ToString( "N0" ), ( totalAdded == 1 ? "payment was" : "payments were" ) ); foreach ( var batchItem in batchSummary ) { int items = batchItem.Value.Count; if (items > 0) { var batch = batches .Where( b => b.Guid.Equals( batchItem.Key ) ) .FirstOrDefault(); string batchName = string.Format("'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString("d")); if ( !string.IsNullOrWhiteSpace( batchUrlFormat ) ) { batchName = string.Format( "<a href='{0}'>{1}</a>", string.Format( batchUrlFormat, batch.Id ), batchName ); } decimal sum = batchItem.Value.Select( p => p.Amount ).Sum(); string summaryformat = items == 1 ? "<li>{0} transaction of {1} was added to the {2} batch.</li>" : "<li>{0} transactions totaling {1} were added to the {2} batch</li>"; sb.AppendFormat( summaryformat, items.ToString( "N0" ), sum.ToString( "C2" ), batchName ); } } return sb.ToString(); }
/// <summary> /// Performs the first step of a three-step charge /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="paymentInfo">The payment information.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public abstract string ChargeStep1(FinancialGateway financialGateway, PaymentInfo paymentInfo, out string errorMessage);
private void SetGatewayOptions( RockContext rockContext ) { _ccGateway = GetGateway( rockContext, "CCGateway" ); _ccGatewayComponent = GetGatewayComponent( rockContext, _ccGateway ); bool ccEnabled = _ccGatewayComponent != null; _achGateway = GetGateway( rockContext, "ACHGateway" ); _achGatewayComponent = GetGatewayComponent( rockContext, _achGateway ); bool achEnabled = _achGatewayComponent != null; if ( _using3StepGateway && _ccGateway != null && _achGateway != null && _ccGateway.Id != _achGateway.Id ) { _gatewaysIncompatible = true; } _ccSavedAccountFreqSupported = GetSavedAcccountFreqSupported( _ccGatewayComponent ); _achSavedAccountFreqSupported = GetSavedAcccountFreqSupported( _achGatewayComponent ); bool allowScheduled = GetAttributeValue( "AllowScheduled" ).AsBoolean(); if ( allowScheduled && ( ccEnabled || achEnabled ) ) { var supportedFrequencies = ccEnabled ? _ccGatewayComponent.SupportedPaymentSchedules : _achGatewayComponent.SupportedPaymentSchedules; // If CC and ACH gateways are both enabled, but different, only allow frequencies supported by both payment gateways (if different) if ( ccEnabled && achEnabled && _ccGatewayComponent.TypeId != _achGatewayComponent.TypeId ) { supportedFrequencies = _ccGatewayComponent.SupportedPaymentSchedules .Where( c => _achGatewayComponent.SupportedPaymentSchedules .Select( a => a.Id ) .Contains( c.Id ) ) .ToList(); } if ( supportedFrequencies.Any() ) { btnFrequency.DataSource = supportedFrequencies; btnFrequency.DataBind(); // If gateway didn't specifically support one-time, add it anyway for immediate gifts var oneTimeFrequency = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_FREQUENCY_ONE_TIME ); if ( !supportedFrequencies.Where( f => f.Id == oneTimeFrequency.Id ).Any() ) { btnFrequency.Items.Insert( 0, new ListItem( oneTimeFrequency.Value, oneTimeFrequency.Id.ToString() ) ); } btnFrequency.SelectedValue = oneTimeFrequency.Id.ToString(); dtpStartDate.SelectedDate = RockDateTime.Today; } } }
/// <summary> /// Performs the final step of a three-step charge. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="resultQueryString">The result query string from step 2.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public abstract FinancialTransaction ChargeStep3(FinancialGateway financialGateway, string resultQueryString, out string errorMessage);
/// <summary> /// Gets a value indicating whether the gateway requires the name on card for CC processing /// </summary> /// <value> /// <c>true</c> if [name on card required]; otherwise, <c>false</c>. /// </value> public override bool PromptForNameOnCard( FinancialGateway financialGateway ) { return GetAttributeValue( financialGateway, "PromptForName" ).AsBoolean(); }
/// <summary> /// Performs the first step of adding a new payment schedule /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="schedule">The schedule.</param> /// <param name="paymentInfo">The payment information.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public abstract string AddScheduledPaymentStep1(FinancialGateway financialGateway, PaymentSchedule schedule, PaymentInfo paymentInfo, out string errorMessage);
/// <summary> /// Gets the payment information. /// </summary> /// <returns></returns> private RequestMessage GetPaymentInfo( FinancialGateway financialGateway, PaymentInfo paymentInfo ) { RequestMessage request = GetMerchantInfo( financialGateway ); if ( paymentInfo is CreditCardPaymentInfo ) { var cc = paymentInfo as CreditCardPaymentInfo; request.card = GetCard( cc ); } else if ( paymentInfo is ACHPaymentInfo ) { var ach = paymentInfo as ACHPaymentInfo; request.check = GetCheck( ach ); } else if ( paymentInfo is ReferencePaymentInfo ) { var reference = paymentInfo as ReferencePaymentInfo; request.recurringSubscriptionInfo = new RecurringSubscriptionInfo(); request.recurringSubscriptionInfo.subscriptionID = reference.ReferenceNumber; } else { return null; } return request; }
/// <summary> /// Performs the third step of adding a new payment schedule /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="resultQueryString">The result query string from step 2.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public abstract FinancialScheduledTransaction AddScheduledPaymentStep3(FinancialGateway financialGateway, string resultQueryString, out string errorMessage);
/// <summary> /// Handles the Swipe event of the csPayWithCard control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="SwipeEventArgs"/> instance containing the event data.</param> protected void csPayWithCard_Swipe(object sender, SwipeEventArgs e) { using (var rockContext = new RockContext()) { try { var swipeInfo = e.PaymentInfo; var person = Customer; if (person == null) { person = new PersonAliasService(rockContext).GetPerson(GetAttributeValue("GuestCustomer").AsGuid()); if (person == null) { nbSwipeErrors.Text = "No guest customer configured. Transaction not processed."; return; } } // // Get the gateway to use. // FinancialGateway financialGateway = null; GatewayComponent gateway = null; Guid? gatewayGuid = GetAttributeValue("CreditCardGateway").AsGuidOrNull(); if (gatewayGuid.HasValue) { financialGateway = new FinancialGatewayService(rockContext).Get(gatewayGuid.Value); if (financialGateway != null) { financialGateway.LoadAttributes(rockContext); } gateway = financialGateway.GetGatewayComponent(); } if (gateway == null) { nbSwipeErrors.Text = "Invalid gateway provided. Please provide a gateway. Transaction not processed."; return; } swipeInfo.Amount = Cart.Total; // // Process the transaction. // string errorMessage = string.Empty; var transaction = gateway.Charge(financialGateway, swipeInfo, out errorMessage); if (transaction == null) { nbSwipeErrors.Text = String.Format("An error occurred while process this transaction. Message: {0}", errorMessage); return; } // // Set some common information about the transaction. // transaction.AuthorizedPersonAliasId = person.PrimaryAliasId; transaction.TransactionDateTime = RockDateTime.Now; transaction.FinancialGatewayId = financialGateway.Id; transaction.TransactionTypeValueId = DefinedValueCache.Get(GetAttributeValue("TransactionType")).Id; transaction.SourceTypeValueId = DefinedValueCache.Get(GetAttributeValue("Source")).Id; transaction.Summary = swipeInfo.Comment1; // // Ensure we have payment details. // if (transaction.FinancialPaymentDetail == null) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } transaction.FinancialPaymentDetail.SetFromPaymentInfo(swipeInfo, gateway, rockContext); // // Setup the transaction details to credit the correct account. // GetTransactionDetails(rockContext).ForEach(d => transaction.TransactionDetails.Add(d)); var batchService = new FinancialBatchService(rockContext); // // Get the batch // var batch = batchService.Get( GetAttributeValue("BatchNamePrefix"), swipeInfo.CurrencyTypeValue, swipeInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, financialGateway.GetBatchTimeOffset()); var batchChanges = new History.HistoryChangeList(); if (batch.Id == 0) { batchChanges.AddChange(History.HistoryVerb.Add, History.HistoryChangeType.Record, "Batch"); History.EvaluateChange(batchChanges, "Batch Name", string.Empty, batch.Name); History.EvaluateChange(batchChanges, "Status", null, batch.Status); History.EvaluateChange(batchChanges, "Start Date/Time", null, batch.BatchStartDateTime); History.EvaluateChange(batchChanges, "End Date/Time", null, batch.BatchEndDateTime); } // // Update the control amount. // decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange(batchChanges, "Control Amount", batch.ControlAmount.FormatAsCurrency(), newControlAmount.FormatAsCurrency()); batch.ControlAmount = newControlAmount; // // Add the transaction to the batch. // transaction.BatchId = batch.Id; batch.Transactions.Add(transaction); // // Generate the receipt. // int receiptId; using (var rockContext2 = new RockContext()) { var receipt = GenerateReceipt(rockContext2); receiptId = receipt.Id; } // // Update each transaction detail to reference the receipt. // foreach (var transactionDetail in transaction.TransactionDetails) { transactionDetail.EntityTypeId = EntityTypeCache.Get(typeof(InteractionComponent)).Id; transactionDetail.EntityId = receiptId; } rockContext.WrapTransaction(() => { rockContext.SaveChanges(); HistoryService.SaveChanges( rockContext, typeof(FinancialBatch), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, batchChanges ); }); ShowReceiptPanel(); } catch (Exception ex) { nbSwipeErrors.Text = String.Format("An error occurred while process this transaction. Message: {0}", ex.Message); } } }
/// <summary> /// Performs the third step of adding a new payment schedule /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="resultQueryString">The result query string from step 2.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public abstract FinancialScheduledTransaction AddScheduledPaymentStep3( FinancialGateway financialGateway, string resultQueryString, out string errorMessage );
// // Swipe Panel Events // private void ProcessSwipe(string swipeData) { try { using (var rockContext = new RockContext()) { // create swipe object SwipePaymentInfo swipeInfo = new SwipePaymentInfo(swipeData); swipeInfo.Amount = this.Amounts.Sum(a => a.Value); var txnType = DefinedValueCache.Get(new Guid(Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION)); swipeInfo.TransactionTypeValueId = txnType.Id; // if not anonymous then add contact info to the gateway transaction if (this.AnonymousGiverPersonAliasId != this.SelectedGivingUnit.PersonAliasId) { var giver = new PersonAliasService(rockContext).Queryable("Person, Person.PhoneNumbers").Where(p => p.Id == this.SelectedGivingUnit.PersonAliasId).FirstOrDefault(); swipeInfo.FirstName = giver.Person.NickName; swipeInfo.LastName = giver.Person.LastName; if (giver.Person.PhoneNumbers != null) { Guid homePhoneValueGuid = new Guid(Rock.SystemGuid.DefinedValue.PERSON_PHONE_TYPE_HOME); var homephone = giver.Person.PhoneNumbers.Where(p => p.NumberTypeValue.Guid == homePhoneValueGuid).FirstOrDefault(); if (homephone != null) { swipeInfo.Phone = homephone.NumberFormatted; } } var homeLocation = giver.Person.GetHomeLocation(); if (homeLocation != null) { swipeInfo.Street1 = homeLocation.Street1; if (!string.IsNullOrWhiteSpace(homeLocation.Street2)) { swipeInfo.Street2 = homeLocation.Street2; } swipeInfo.City = homeLocation.City; swipeInfo.State = homeLocation.State; swipeInfo.PostalCode = homeLocation.PostalCode; } } // add comment to the transaction swipeInfo.Comment1 = GetAttributeValue("PaymentComment"); // get gateway FinancialGateway financialGateway = null; GatewayComponent gateway = null; Guid? gatewayGuid = GetAttributeValue("CreditCardGateway").AsGuidOrNull(); if (gatewayGuid.HasValue) { financialGateway = new FinancialGatewayService(rockContext).Get(gatewayGuid.Value); if (financialGateway != null) { financialGateway.LoadAttributes(rockContext); } gateway = financialGateway.GetGatewayComponent(); } if (gateway != null) { string errorMessage = string.Empty; var transaction = gateway.Charge(financialGateway, swipeInfo, out errorMessage); if (transaction != null) { _transactionCode = transaction.TransactionCode; var personName = new PersonAliasService(rockContext) .Queryable().AsNoTracking() .Where(a => a.Id == this.SelectedGivingUnit.PersonAliasId) .Select(a => a.Person.NickName + " " + a.Person.LastName) .FirstOrDefault(); transaction.AuthorizedPersonAliasId = this.SelectedGivingUnit.PersonAliasId; transaction.TransactionDateTime = RockDateTime.Now; transaction.FinancialGatewayId = financialGateway.Id; transaction.TransactionTypeValueId = txnType.Id; transaction.Summary = swipeInfo.Comment1; if (transaction.FinancialPaymentDetail == null) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } transaction.FinancialPaymentDetail.SetFromPaymentInfo(swipeInfo, gateway, rockContext); Guid sourceGuid = Guid.Empty; if (Guid.TryParse(GetAttributeValue("Source"), out sourceGuid)) { var source = DefinedValueCache.Get(sourceGuid); if (source != null) { transaction.SourceTypeValueId = source.Id; } } foreach (var accountAmount in this.Amounts.Where(a => a.Value > 0)) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = accountAmount.Value; transactionDetail.AccountId = accountAmount.Key; transaction.TransactionDetails.Add(transactionDetail); var account = new FinancialAccountService(rockContext).Get(accountAmount.Key); } var batchService = new FinancialBatchService(rockContext); // Get the batch var batch = batchService.Get( GetAttributeValue("BatchNamePrefix"), swipeInfo.CurrencyTypeValue, swipeInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, financialGateway.GetBatchTimeOffset()); var batchChanges = new History.HistoryChangeList(); if (batch.Id == 0) { batchChanges.AddChange(History.HistoryVerb.Add, History.HistoryChangeType.Record, "Batch"); History.EvaluateChange(batchChanges, "Batch Name", string.Empty, batch.Name); History.EvaluateChange(batchChanges, "Status", null, batch.Status); History.EvaluateChange(batchChanges, "Start Date/Time", null, batch.BatchStartDateTime); History.EvaluateChange(batchChanges, "End Date/Time", null, batch.BatchEndDateTime); } decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange(batchChanges, "Control Amount", batch.ControlAmount.FormatAsCurrency(), newControlAmount.FormatAsCurrency()); batch.ControlAmount = newControlAmount; transaction.BatchId = batch.Id; batch.Transactions.Add(transaction); rockContext.WrapTransaction(() => { rockContext.SaveChanges(); HistoryService.SaveChanges( rockContext, typeof(FinancialBatch), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, batchChanges ); }); // send receipt in one is configured and not giving anonymously if (!string.IsNullOrWhiteSpace(GetAttributeValue("ReceiptEmail")) && (this.AnonymousGiverPersonAliasId != this.SelectedGivingUnit.PersonAliasId)) { _receiptSent = true; SendReceipt(); } HidePanels(); ShowReceiptPanel(); } else { lSwipeErrors.Text = String.Format("<div class='alert alert-danger'>An error occurred while process this transaction. Message: {0}</div>", errorMessage); } } else { lSwipeErrors.Text = "<div class='alert alert-danger'>Invalid gateway provided. Please provide a gateway. Transaction not processed.</div>"; } } } catch (Exception ex) { lSwipeErrors.Text = String.Format("<div class='alert alert-danger'>An error occurred while process this transaction. Message: {0}</div>", ex.Message); } }
/// <summary> /// Performs the first step of a three-step charge /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="paymentInfo">The payment information.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public abstract string ChargeStep1( FinancialGateway financialGateway, PaymentInfo paymentInfo, out string errorMessage );
/// <summary> /// Gets a value indicating whether the gateway requires the name on card for CC processing /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <returns></returns> /// <value> /// <c>true</c> if [name on card required]; otherwise, <c>false</c>. /// </value> public override bool PromptForNameOnCard(FinancialGateway financialGateway) { return(false); }
/// <summary> /// Performs the final step of a three-step charge. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="resultQueryString">The result query string from step 2.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public abstract FinancialTransaction ChargeStep3( FinancialGateway financialGateway, string resultQueryString, out string errorMessage );
/// <summary> /// Authorizes the specified payment info. /// </summary> /// <param name="financialGateway"></param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialTransaction Authorize(FinancialGateway financialGateway, PaymentInfo paymentInfo, out string errorMessage) { errorMessage = string.Empty; Response ppResponse = null; var invoice = GetInvoice(paymentInfo); BrowserInfo browser = new BrowserInfo(); browser.ButtonSource = "SparkDevelopmentNetwork_SP"; invoice.BrowserInfo = browser; var tender = GetTender(paymentInfo); if (tender != null) { if (paymentInfo is ReferencePaymentInfo) { var reference = paymentInfo as ReferencePaymentInfo; var ppTransaction = new ReferenceTransaction("Authorization", reference.TransactionCode, GetUserInfo(financialGateway), GetConnection(financialGateway), invoice, tender, PayflowUtility.RequestId); ppResponse = ppTransaction.SubmitTransaction(); } else { var ppTransaction = new AuthorizationTransaction(GetUserInfo(financialGateway), GetConnection(financialGateway), invoice, tender, PayflowUtility.RequestId); ppResponse = ppTransaction.SubmitTransaction(); } } else { errorMessage = "Could not create tender from PaymentInfo"; } if (ppResponse != null) { TransactionResponse txnResponse = ppResponse.TransactionResponse; if (txnResponse != null) { if (txnResponse.Result == 0) // Success { var transaction = new FinancialTransaction(); transaction.TransactionCode = txnResponse.Pnref; return(transaction); } else { errorMessage = string.Format("[{0}] {1}", txnResponse.Result, txnResponse.RespMsg); } } else { errorMessage = "Invalid transaction response from the financial gateway"; } } else { errorMessage = "Invalid response from the financial gateway."; } return(null); }
/// <summary> /// Handles the SelectedIndexChanged event of the cpGatewayType 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 cpGatewayType_SelectedIndexChanged( object sender, EventArgs e ) { var gateway = new FinancialGateway { Id = GatewayId, EntityTypeId = cpGatewayType.SelectedEntityTypeId }; BuildDynamicControls( gateway, true ); }
/// <summary> /// Adds the scheduled payment. /// </summary> /// <param name="financialGateway"></param> /// <param name="schedule">The schedule.</param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialScheduledTransaction AddScheduledPayment(FinancialGateway financialGateway, PaymentSchedule schedule, PaymentInfo paymentInfo, out string errorMessage) { errorMessage = string.Empty; var recurring = GetRecurring(schedule); if (paymentInfo is CreditCardPaymentInfo) { recurring.OptionalTrx = "A"; } var ppTransaction = new RecurringAddTransaction(GetUserInfo(financialGateway), GetConnection(financialGateway), GetInvoice(paymentInfo), GetTender(paymentInfo), recurring, PayflowUtility.RequestId); if (paymentInfo is ReferencePaymentInfo) { var reference = paymentInfo as ReferencePaymentInfo; ppTransaction.OrigId = reference.TransactionCode; } var ppResponse = ppTransaction.SubmitTransaction(); if (ppResponse != null) { TransactionResponse txnResponse = ppResponse.TransactionResponse; if (txnResponse != null) { if (txnResponse.Result == 0) // Success { RecurringResponse recurringResponse = ppResponse.RecurringResponse; if (recurringResponse != null) { var scheduledTransaction = new FinancialScheduledTransaction(); scheduledTransaction.TransactionCode = recurringResponse.TrxPNRef; scheduledTransaction.GatewayScheduleId = recurringResponse.ProfileId; scheduledTransaction.FinancialGatewayId = financialGateway.Id; GetScheduledPaymentStatus(scheduledTransaction, out errorMessage); return(scheduledTransaction); } else { errorMessage = "Invalid recurring response from the financial gateway"; } } else { errorMessage = string.Format("[{0}] {1}", txnResponse.Result, txnResponse.RespMsg); } } else { errorMessage = "Invalid transaction response from the financial gateway"; } } else { errorMessage = "Invalid response from the financial gateway."; } return(null); }
private void BuildDynamicControls( FinancialGateway gateway, bool SetValues ) { GatewayEntityTypeId = gateway.EntityTypeId; if ( gateway.EntityTypeId.HasValue ) { var GatewayComponentEntityType = EntityTypeCache.Read( gateway.EntityTypeId.Value ); var GatewayEntityType = EntityTypeCache.Read( "Rock.Model.FinancialGateway " ); if ( GatewayComponentEntityType != null && GatewayEntityType != null ) { using ( var rockContext = new RockContext() ) { Rock.Attribute.Helper.UpdateAttributes( GatewayComponentEntityType.GetEntityType(), GatewayEntityType.Id, "EntityTypeId", GatewayComponentEntityType.Id.ToString(), rockContext ); gateway.LoadAttributes( rockContext ); } } } phAttributes.Controls.Clear(); Rock.Attribute.Helper.AddEditControls( gateway, phAttributes, SetValues, BlockValidationGroup, new List<string> { "Active", "Order" } ); }
/// <summary> /// Gets the payments that have been processed for any scheduled transactions /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <param name="startDate">The start date.</param> /// <param name="endDate">The end date.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override List <Payment> GetPayments(FinancialGateway financialGateway, DateTime startDate, DateTime endDate, out string errorMessage) { errorMessage = string.Empty; return(new List <Payment>()); }
/// <summary> /// Gets the payments that have been processed for any scheduled transactions /// </summary> /// <param name="gateway">The gateway.</param> /// <param name="startDate">The start date.</param> /// <param name="endDate">The end date.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override List <Payment> GetPayments(FinancialGateway gateway, DateTime startDate, DateTime endDate, out string errorMessage) { var today = RockDateTime.Now; var lookupContext = new RockContext(); var accountLookup = new FinancialAccountService(lookupContext); var transactionLookup = new FinancialTransactionService(lookupContext); var donationUrl = GetBaseUrl(gateway, "donations", out errorMessage); var supporterUrl = GetBaseUrl(gateway, "sponsorship_supporters", out errorMessage); var categoryUrl = GetBaseUrl(gateway, "donation_categories", out errorMessage); var projectsUrl = GetBaseUrl(gateway, "projects", out errorMessage); if (donationUrl.IsNullOrWhiteSpace() || supporterUrl.IsNullOrWhiteSpace()) { // errorMessage already set return(null); } var authenticator = GetAuthenticator(gateway, out errorMessage); if (authenticator == null) { // errorMessage already set return(null); } var reachAccountMaps = DefinedTypeCache.Get(GetAttributeValue(gateway, "AccountMap")).DefinedValues; var connectionStatus = DefinedValueCache.Get(GetAttributeValue(gateway, "PersonStatus")); var reachSourceType = DefinedValueCache.Get(GetAttributeValue(gateway, "SourceType")); var defaultAccount = accountLookup.Get(GetAttributeValue(gateway, "DefaultAccount").AsGuid()); var updatePrimaryEmail = GetAttributeValue(gateway, "UpdatePrimaryEmail").AsBoolean(); if (connectionStatus == null || reachAccountMaps == null || reachSourceType == null || defaultAccount == null) { errorMessage = "The Reach Account Map, Person Status, Source Type, or Default Account is not configured correctly in gateway settings."; return(null); } var currentPage = 1; var queryHasResults = true; var skippedTransactionCount = 0; var errorMessages = new List <string>(); var newTransactions = new List <FinancialTransaction>(); var categoryResult = Api.PostRequest(categoryUrl, authenticator, null, out errorMessage); var categories = JsonConvert.DeserializeObject <List <Reporting.Category> >(categoryResult.ToStringSafe()); var projectResult = Api.PostRequest(projectsUrl, authenticator, null, out errorMessage); var projects = JsonConvert.DeserializeObject <List <Reporting.Project> >(projectResult.ToStringSafe()); if (categories == null) { // errorMessage already set return(null); } while (queryHasResults) { var parameters = new Dictionary <string, string> { { "from_date", startDate.ToString("yyyy-MM-dd") }, { "to_date", endDate.ToString("yyyy-MM-dd") }, { "per_page", "50" }, { "page", currentPage.ToString() } }; // to_date doesn't have a timestamp, so it includes transactions posted after the cutoff var donationResult = Api.PostRequest(donationUrl, authenticator, parameters, out errorMessage); var donations = JsonConvert.DeserializeObject <List <Donation> >(donationResult.ToStringSafe()); if (donations != null && donations.Any() && errorMessage.IsNullOrWhiteSpace()) { // only process completed transactions with confirmation codes and within the date range foreach (var donation in donations.Where(d => d.updated_at >= startDate && d.updated_at < endDate && d.status.Equals("complete") && d.confirmation.IsNotNullOrWhiteSpace())) { var transaction = transactionLookup.Queryable() .FirstOrDefault(t => t.FinancialGatewayId.HasValue && t.FinancialGatewayId.Value == gateway.Id && t.TransactionCode == donation.confirmation); if (transaction == null) { // find or create this person asynchronously for performance var personAlias = Api.FindPersonAsync(lookupContext, donation, connectionStatus.Id, updatePrimaryEmail); var reachAccountName = string.Empty; var donationItem = donation.line_items.FirstOrDefault(); if (donationItem != null && donationItem.referral_type.Equals("DonationOption", StringComparison.InvariantCultureIgnoreCase)) { // one-time gift, should match a known category var category = categories.FirstOrDefault(c => c.id == donationItem.referral_id); if (category != null) { reachAccountName = category.title.Trim(); } } else if (donationItem != null && donationItem.referral_type.Equals("Project", StringComparison.InvariantCultureIgnoreCase)) { // one-time gift, should match a known project var project = projects.FirstOrDefault(c => c.id == donationItem.referral_id); if (project != null) { reachAccountName = string.Format("PROJECT {0}", project.title.Trim()); } } else { // recurring gift, lookup the sponsor info var referralId = donation.referral_id ?? donationItem.referral_id; var supporterResults = Api.PostRequest(string.Format("{0}/{1}", supporterUrl, referralId), authenticator, null, out errorMessage); var supporter = JsonConvert.DeserializeObject <Supporter>(supporterResults.ToStringSafe()); if (supporter != null) { var place = supporter.sponsorship?.place?.title; var sponsorshipType = supporter.sponsorship?.sponsorship_type_title; var shareType = supporter.share_type_id; string shareTypeName; switch (shareType) { case "668": shareTypeName = "Primary"; break; case "669": shareTypeName = "Secondary"; break; default: shareTypeName = string.Empty; break; } reachAccountName = string.Format("{0} {1} {2}", place, sponsorshipType, shareTypeName).Trim(); } } int?rockAccountId = defaultAccount.Id; var accountMapping = reachAccountMaps.FirstOrDefault(v => v.Value.Equals(reachAccountName, StringComparison.CurrentCultureIgnoreCase)); if (accountMapping != null) { var accountGuid = accountMapping.GetAttributeValue("RockAccount").AsGuidOrNull(); if (accountGuid.HasValue) { rockAccountId = accountLookup.Get(( Guid )accountGuid).Id; } } // verify person alias was found or created personAlias.Wait(); if (!personAlias.Result.HasValue) { var infoMessage = string.Format("{0} Reach import skipped {1} {2}'s donation {3} for {4} because their record could not be found or created", endDate.ToString("d"), donation.first_name, donation.last_name, donation.confirmation, reachAccountName); ExceptionLogService.LogException(new Exception(infoMessage), null); continue; } // create the transaction var summary = string.Format("Reach Donation for {0} from {1} using {2} on {3} ({4})", reachAccountName, donation.name, donation.payment_method, donation.updated_at, donation.token); transaction = new FinancialTransaction { TransactionDateTime = donation.updated_at, ProcessedDateTime = donation.updated_at, TransactionCode = donation.confirmation, Summary = summary, SourceTypeValueId = reachSourceType.Id, TransactionTypeValueId = contributionTypeId, Guid = Guid.NewGuid(), CreatedDateTime = today, ModifiedDateTime = today, AuthorizedPersonAliasId = personAlias.Result.Value, FinancialGatewayId = gateway.Id, ForeignId = donation.id, FinancialPaymentDetail = new FinancialPaymentDetail(), TransactionDetails = new List <FinancialTransactionDetail> { new FinancialTransactionDetail { AccountId = (int)rockAccountId, Amount = (decimal)donation.amount, Summary = summary, Guid = Guid.NewGuid(), CreatedDateTime = today, ModifiedDateTime = today } } }; newTransactions.Add(transaction); } else if (transaction != null) { skippedTransactionCount++; } } } else { queryHasResults = false; } currentPage++; } if (skippedTransactionCount > 0) { ExceptionLogService.LogException(new Exception(string.Format("{0} Reach import skipped downloading {1} transactions because they already exist", endDate.ToString("d"), skippedTransactionCount)), null); } if (newTransactions.Any()) { using (var rockContext = new RockContext()) { // create batch and add transactions var batchPrefix = GetAttributeValue(gateway, "BatchPrefix"); var batchDate = newTransactions.GroupBy(t => t.TransactionDateTime.Value.Date).OrderByDescending(t => t.Count()) .Select(g => g.Key).FirstOrDefault(); var batch = new FinancialBatchService(rockContext).GetByNameAndDate(string.Format("{0} {1}", batchPrefix, batchDate.ToString("d")), endDate, gateway.GetBatchTimeOffset()); batch.BatchStartDateTime = batchDate; batch.BatchEndDateTime = endDate; batch.Note = string.Format("{0} transactions downloaded starting {1} to {2}", batchPrefix, startDate, endDate); batch.ControlAmount += newTransactions.Select(t => t.TotalAmount).Sum(); var currentChanges = 0; foreach (var transaction in newTransactions) { // save in large batches so it doesn't overload context batch.Transactions.Add(transaction); if (currentChanges++ > 100) { rockContext.SaveChanges(disablePrePostProcessing: true); currentChanges = 0; } } // by default Rock associates with the current person rockContext.SaveChanges(disablePrePostProcessing: true); } } if (errorMessages.Any()) { errorMessage = string.Join("<br>", errorMessages); } return(new List <Payment>()); }
protected override void LoadViewState( object savedState ) { base.LoadViewState( savedState ); var gateway = new FinancialGateway { Id = GatewayId, EntityTypeId = GatewayEntityTypeId }; BuildDynamicControls( gateway, false ); }
/// <summary> /// Submits the transaction. /// </summary> /// <param name="request">The request.</param> /// <returns></returns> private ReplyMessage SubmitTransaction( FinancialGateway financialGateway, RequestMessage request, out string errorMessage ) { ReplyMessage reply = new ReplyMessage(); TransactionProcessorClient client = GetProxyClient( financialGateway, out errorMessage ); // Error message already set, return if ( client == null ) { return null; } try { reply = client.runTransaction( request ); return reply; } catch ( TimeoutException e ) { reply.reasonCode = "151"; reply.additionalData = e.ToString(); return reply; } catch ( FaultException e ) { reply.reasonCode = "150"; reply.additionalData = e.ToString(); return reply; } catch ( Exception e ) { reply.reasonCode = ""; reply.additionalData = e.ToString(); return reply; } }
/// <summary> /// Shows the edit details. /// </summary> /// <param name="gateway">The gateway.</param> private void ShowEditDetails( FinancialGateway gateway ) { if ( gateway.Id == 0 ) { lActionTitle.Text = ActionTitle.Add( FinancialGateway.FriendlyTypeName ).FormatAsHtmlTitle(); } else { lActionTitle.Text = gateway.Name.FormatAsHtmlTitle(); } hlInactive.Visible = !gateway.IsActive; SetEditMode( true ); tbName.Text = gateway.Name; cbIsActive.Checked = gateway.IsActive; tbDescription.Text = gateway.Description; cpGatewayType.SetValue( gateway.EntityType != null ? gateway.EntityType.Guid.ToString().ToUpper() : string.Empty ); tpBatchTimeOffset.SelectedTime = gateway.GetBatchTimeOffset(); BuildDynamicControls( gateway, true ); }
/// <summary> /// Adds the scheduled payment. /// </summary> /// <param name="schedule">The schedule.</param> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialScheduledTransaction AddScheduledPayment( FinancialGateway financialGateway, PaymentSchedule schedule, PaymentInfo paymentInfo, out string errorMessage ) { errorMessage = string.Empty; RequestMessage request = GetPaymentInfo( financialGateway, paymentInfo ); if ( request == null ) { errorMessage = "Payment type not implemented"; return null; } if ( request.recurringSubscriptionInfo == null ) { request.recurringSubscriptionInfo = new RecurringSubscriptionInfo(); } request.recurringSubscriptionInfo.startDate = GetStartDate( schedule ); request.recurringSubscriptionInfo.frequency = GetFrequency( schedule ); request.recurringSubscriptionInfo.amount = paymentInfo.Amount.ToString(); request.paySubscriptionCreateService = new PaySubscriptionCreateService(); request.paySubscriptionCreateService.run = "true"; request.purchaseTotals = GetTotals(); request.billTo = GetBillTo( paymentInfo ); request.item = GetItems( paymentInfo ); request.subscription = new Subscription(); if ( !paymentInfo.CurrencyTypeValue.Guid.Equals( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD ) ) ) { request.subscription.paymentMethod = "check"; } if ( paymentInfo is ReferencePaymentInfo ) { request.paySubscriptionCreateService.paymentRequestID = ( (ReferencePaymentInfo)paymentInfo ).TransactionCode; } // Schedule the payment ReplyMessage reply = SubmitTransaction( financialGateway, request, out errorMessage ); if ( reply != null && reply.reasonCode.Equals( GATEWAY_RESPONSE_SUCCESS ) ) { var transactionGuid = new Guid( reply.merchantReferenceCode ); var scheduledTransaction = new FinancialScheduledTransaction { Guid = transactionGuid }; scheduledTransaction.TransactionCode = reply.paySubscriptionCreateReply.subscriptionID; scheduledTransaction.GatewayScheduleId = reply.paySubscriptionCreateReply.subscriptionID; scheduledTransaction.FinancialGateway = financialGateway; scheduledTransaction.FinancialGatewayId = financialGateway.Id; GetScheduledPaymentStatus( scheduledTransaction, out errorMessage ); return scheduledTransaction; } else if ( string.IsNullOrEmpty( errorMessage ) ) { errorMessage = string.Format( "Your order was not approved.{0}", ProcessError( reply ) ); } return null; }
/// <summary> /// Authorizes/tokenizes the specified payment info. /// </summary> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialTransaction Authorize( FinancialGateway financialGateway, PaymentInfo paymentInfo, out string errorMessage ) { errorMessage = string.Empty; RequestMessage request = GetPaymentInfo( financialGateway, paymentInfo ); if ( request == null ) { errorMessage = "Payment type not implemented"; return null; } if ( request.recurringSubscriptionInfo == null ) { request.recurringSubscriptionInfo = new RecurringSubscriptionInfo(); } request.recurringSubscriptionInfo.frequency = "ON-DEMAND"; request.recurringSubscriptionInfo.amount = paymentInfo.Amount.ToString(); request.paySubscriptionCreateService = new PaySubscriptionCreateService(); request.paySubscriptionCreateService.run = "true"; request.purchaseTotals = GetTotals(); request.billTo = GetBillTo( paymentInfo ); request.item = GetItems( paymentInfo ); request.subscription = new Subscription(); if ( !paymentInfo.CurrencyTypeValue.Guid.Equals( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD ) ) ) { request.subscription.paymentMethod = "check"; } if ( paymentInfo is ReferencePaymentInfo ) { request.paySubscriptionCreateService.paymentRequestID = ( (ReferencePaymentInfo)paymentInfo ).TransactionCode; } // Authorize the transaction ReplyMessage reply = SubmitTransaction( financialGateway, request, out errorMessage ); if ( reply != null && reply.reasonCode.Equals( GATEWAY_RESPONSE_SUCCESS ) ) { var transactionGuid = new Guid( reply.merchantReferenceCode ); var transaction = new FinancialTransaction { Guid = transactionGuid }; transaction.TransactionCode = reply.requestID; return transaction; } else if ( string.IsNullOrEmpty( errorMessage ) ) { errorMessage = string.Format( "Unable to authorize this transaction.{0}", ProcessError( reply ) ); } return null; }
/// <summary> /// Charges the specified payment info. /// </summary> /// <param name="paymentInfo">The payment info.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override FinancialTransaction Charge( FinancialGateway financialGateway, PaymentInfo paymentInfo, out string errorMessage ) { errorMessage = string.Empty; RequestMessage request = GetPaymentInfo( financialGateway, paymentInfo ); if ( request == null ) { errorMessage = "Payment type not implemented"; return null; } request.purchaseTotals = GetTotals(); request.billTo = GetBillTo( paymentInfo ); request.item = GetItems( paymentInfo ); if ( !paymentInfo.CurrencyTypeValue.Guid.Equals( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD ) ) ) { request.ecDebitService = new ECDebitService(); request.ecDebitService.commerceIndicator = "internet"; request.ecDebitService.run = "true"; } else { request.ccAuthService = new CCAuthService(); request.ccAuthService.commerceIndicator = "internet"; request.ccAuthService.run = "true"; request.ccCaptureService = new CCCaptureService(); request.ccCaptureService.run = "true"; } // Charge the transaction ReplyMessage reply = SubmitTransaction( financialGateway, request, out errorMessage ); if ( reply != null && reply.reasonCode.Equals( GATEWAY_RESPONSE_SUCCESS ) ) { var transactionGuid = new Guid( reply.merchantReferenceCode ); var transaction = new FinancialTransaction { Guid = transactionGuid }; transaction.TransactionCode = reply.requestID; return transaction; } else if ( string.IsNullOrEmpty( errorMessage ) ) { errorMessage = string.Format( "Unable to process this order.{0}", ProcessError( reply ) ); } return null; }
/// <summary> /// Gets the batch. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="financialGateway">The financial gateway.</param> /// <param name="suffix">The suffix.</param> /// <returns></returns> private FinancialBatch GetBatch( RockContext rockContext, FinancialGateway financialGateway, string suffix ) { if ( suffix == null ) { suffix = string.Empty; } if ( suffix.Length > 1 ) { suffix = char.ToUpper( suffix[0] ) + suffix.Substring( 1 ); } var batchService = new FinancialBatchService( rockContext ); var batchName = string.Format( "{0}: {1}", BATCH_PREFIX, suffix ); return batchService.GetByNameAndDate( batchName, RockDateTime.Now, financialGateway.GetBatchTimeOffset() ); }
/// <summary> /// Gets the payments that have been processed for any scheduled transactions /// </summary> /// <param name="startDate">The start date.</param> /// <param name="endDate">The end date.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public override List<Payment> GetPayments( FinancialGateway financialGateway, DateTime startDate, DateTime endDate, out string errorMessage ) { errorMessage = string.Empty; List<Payment> paymentList = new List<Payment>(); var reportParams = new Dictionary<string, string>(); var reportingApi = new Reporting.Api( GetAttributeValue( financialGateway, "MerchantID" ), GetAttributeValue( financialGateway, "TransactionKey" ), GetAttributeValue( financialGateway, "ReportUser" ), GetAttributeValue( financialGateway, "ReportPassword" ), GetAttributeValue( financialGateway, "Mode" ).Equals( "Live", StringComparison.CurrentCultureIgnoreCase ) ); TimeSpan timeDifference = endDate - startDate; for ( int offset = 0; offset <= timeDifference.TotalDays; offset++ ) { DateTime offsetDate = startDate.AddDays( offset ) < endDate ? startDate.AddDays( offset ) : endDate; reportParams.Add( "date", offsetDate.ToString( "yyyy/MM/dd" ) ); DataTable dt = reportingApi.GetReport( "SubscriptionDetailReport", reportParams, out errorMessage ); if ( dt != null && dt.Rows.Count > 0 ) { foreach ( DataRow row in dt.Rows ) { var payment = new Payment(); decimal amount = decimal.MinValue; payment.Amount = decimal.TryParse( row["Amount"].ToString(), out amount ) ? amount : 0.0M; var time = DateTime.MinValue; payment.TransactionDateTime = DateTime.TryParse( row["Time"].ToString(), out time ) ? time : DateTime.MinValue; payment.TransactionCode = row["Code"].ToString(); payment.GatewayScheduleId = row["Schedule"].ToString(); payment.ScheduleActive = row["Status"].ToString() == "CURRENT"; paymentList.Add( payment ); } } reportParams.Clear(); } if ( paymentList.Any() ) { return paymentList; } else { errorMessage = "The subscription detail report did not return any data for the timeframe"; return null; } }
private GatewayComponent GetGatewayComponent( RockContext rockContext, FinancialGateway gateway ) { if ( gateway != null ) { gateway.LoadAttributes( rockContext ); var gatewayComponent = gateway.GetGatewayComponent(); if ( gatewayComponent != null ) { var threeStepGateway = gatewayComponent as ThreeStepGatewayComponent; if ( threeStepGateway != null ) { _using3StepGateway = true; Step2IFrameUrl = ResolveRockUrl( threeStepGateway.Step2FormUrl ); } } return gatewayComponent; } return null; }
/// <summary> /// Prompts the name of for bank account. /// </summary> /// <param name="financialGateway">The financial gateway.</param> /// <returns></returns> public override bool PromptForBankAccountName( FinancialGateway financialGateway ) { return GetAttributeValue( financialGateway, "PromptForBankAccountName" ).AsBoolean(); }
private void SaveTransaction( FinancialGateway financialGateway, GatewayComponent gateway, Person person, PaymentInfo paymentInfo, FinancialTransaction transaction, RockContext rockContext ) { var txnChanges = new List<string>(); txnChanges.Add( "Created Transaction" ); History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.TransactionCode ); transaction.AuthorizedPersonAliasId = person.PrimaryAliasId; History.EvaluateChange( txnChanges, "Person", string.Empty, person.FullName ); transaction.TransactionDateTime = RockDateTime.Now; History.EvaluateChange( txnChanges, "Date/Time", null, transaction.TransactionDateTime ); transaction.FinancialGatewayId = financialGateway.Id; History.EvaluateChange( txnChanges, "Gateway", string.Empty, financialGateway.Name ); var txnType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION ) ); transaction.TransactionTypeValueId = txnType.Id; History.EvaluateChange( txnChanges, "Type", string.Empty, txnType.Value ); transaction.Summary = paymentInfo.Comment1; History.EvaluateChange( txnChanges, "Summary", string.Empty, transaction.Summary ); if ( transaction.FinancialPaymentDetail == null ) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } transaction.FinancialPaymentDetail.SetFromPaymentInfo( paymentInfo, gateway, rockContext, txnChanges ); Guid sourceGuid = Guid.Empty; if ( Guid.TryParse( GetAttributeValue( "Source" ), out sourceGuid ) ) { var source = DefinedValueCache.Read( sourceGuid ); if ( source != null ) { transaction.SourceTypeValueId = source.Id; History.EvaluateChange( txnChanges, "Source", string.Empty, source.Value ); } } foreach ( var account in SelectedAccounts.Where( a => a.Amount > 0 ) ) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = account.Amount; transactionDetail.AccountId = account.Id; transaction.TransactionDetails.Add( transactionDetail ); History.EvaluateChange( txnChanges, account.Name, 0.0M.FormatAsCurrency(), transactionDetail.Amount.FormatAsCurrency() ); } var batchService = new FinancialBatchService( rockContext ); // Get the batch var batch = batchService.Get( GetAttributeValue( "BatchNamePrefix" ), paymentInfo.CurrencyTypeValue, paymentInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, financialGateway.GetBatchTimeOffset() ); var batchChanges = new List<string>(); if ( batch.Id == 0 ) { batchChanges.Add( "Generated the batch" ); History.EvaluateChange( batchChanges, "Batch Name", string.Empty, batch.Name ); History.EvaluateChange( batchChanges, "Status", null, batch.Status ); History.EvaluateChange( batchChanges, "Start Date/Time", null, batch.BatchStartDateTime ); History.EvaluateChange( batchChanges, "End Date/Time", null, batch.BatchEndDateTime ); } decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange( batchChanges, "Control Amount", batch.ControlAmount.FormatAsCurrency(), newControlAmount.FormatAsCurrency() ); batch.ControlAmount = newControlAmount; transaction.BatchId = batch.Id; batch.Transactions.Add( transaction ); rockContext.SaveChanges(); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, batchChanges ); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), batch.Id, txnChanges, person.FullName, typeof( FinancialTransaction ), transaction.Id ); SendReceipt( transaction.Id ); TransactionCode = transaction.TransactionCode; }
/// <summary> /// Gets a value indicating whether [address required]. /// </summary> /// <value> /// <c>true</c> if [address required]; otherwise, <c>false</c>. /// </value> public override bool PromptForBillingAddress( FinancialGateway financialGateway ) { return GetAttributeValue( financialGateway, "PromptForAddress" ).AsBoolean(); }
/// <summary> /// Processes the payments. /// </summary> /// <param name="gateway">The gateway.</param> /// <param name="batchNamePrefix">The batch name prefix.</param> /// <param name="payments">The payments.</param> /// <param name="batchUrlFormat">The batch URL format.</param> /// <param name="receiptEmail">The receipt email.</param> /// <returns></returns> public static string ProcessPayments( FinancialGateway gateway, string batchNamePrefix, List<Payment> payments, string batchUrlFormat = "", Guid? receiptEmail = null ) { int totalPayments = 0; int totalAlreadyDownloaded = 0; int totalNoScheduledTransaction = 0; int totalAdded = 0; int totalReversals = 0; int totalStatusChanges = 0; var batches = new List<FinancialBatch>(); var batchSummary = new Dictionary<Guid, List<Decimal>>(); var initialControlAmounts = new Dictionary<Guid, decimal>(); var txnPersonNames = new Dictionary<Guid, string>(); var gatewayComponent = gateway.GetGatewayComponent(); var newTransactions = new List<FinancialTransaction>(); using ( var rockContext = new RockContext() ) { var accountService = new FinancialAccountService( rockContext ); var txnService = new FinancialTransactionService( rockContext ); var batchService = new FinancialBatchService( rockContext ); var scheduledTxnService = new FinancialScheduledTransactionService( rockContext ); var contributionTxnType = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid() ); var defaultAccount = accountService.Queryable() .Where( a => a.IsActive && !a.ParentAccountId.HasValue && ( !a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now ) && ( !a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now ) ) .OrderBy( a => a.Order ) .FirstOrDefault(); var batchTxnChanges = new Dictionary<Guid, List<string>>(); var batchBatchChanges = new Dictionary<Guid, List<string>>(); var scheduledTransactionIds = new List<int>(); foreach ( var payment in payments.Where( p => p.Amount > 0.0M ) ) { totalPayments++; var scheduledTransaction = scheduledTxnService.GetByScheduleId( payment.GatewayScheduleId ); if ( scheduledTransaction != null ) { // Find existing payments with same transaction code var txns = txnService .Queryable( "TransactionDetails" ) .Where( t => t.TransactionCode == payment.TransactionCode ) .ToList(); // Calculate whether a transaction needs to be added var txnAmount = CalculateTransactionAmount( payment, txns ); if ( txnAmount != 0.0M ) { scheduledTransactionIds.Add( scheduledTransaction.Id ); if ( payment.ScheduleActive.HasValue ) { scheduledTransaction.IsActive = payment.ScheduleActive.Value; } var transaction = new FinancialTransaction(); transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); transaction.Guid = Guid.NewGuid(); transaction.TransactionCode = payment.TransactionCode; transaction.TransactionDateTime = payment.TransactionDateTime; transaction.Status = payment.Status; transaction.StatusMessage = payment.StatusMessage; transaction.ScheduledTransactionId = scheduledTransaction.Id; transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId; transaction.SourceTypeValueId = scheduledTransaction.SourceTypeValueId; txnPersonNames.Add( transaction.Guid, scheduledTransaction.AuthorizedPersonAlias.Person.FullName ); transaction.FinancialGatewayId = gateway.Id; transaction.TransactionTypeValueId = contributionTxnType.Id; if ( txnAmount < 0.0M ) { transaction.Summary = "Reversal for previous transaction that failed during processing." + Environment.NewLine; } var currencyTypeValue = payment.CurrencyTypeValue; var creditCardTypevalue = payment.CreditCardTypeValue; if ( scheduledTransaction.FinancialPaymentDetail != null ) { if ( currencyTypeValue == null && scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.HasValue ) { currencyTypeValue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.Value ); } if ( creditCardTypevalue == null && scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.HasValue ) { creditCardTypevalue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.Value ); } transaction.FinancialPaymentDetail.AccountNumberMasked = scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked; transaction.FinancialPaymentDetail.NameOnCardEncrypted = scheduledTransaction.FinancialPaymentDetail.NameOnCardEncrypted; transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted; transaction.FinancialPaymentDetail.ExpirationYearEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationYearEncrypted; transaction.FinancialPaymentDetail.BillingLocationId = scheduledTransaction.FinancialPaymentDetail.BillingLocationId; } if ( currencyTypeValue != null ) { transaction.FinancialPaymentDetail.CurrencyTypeValueId = currencyTypeValue.Id; } if ( creditCardTypevalue != null ) { transaction.FinancialPaymentDetail.CreditCardTypeValueId = creditCardTypevalue.Id; } // Try to allocate the amount of the transaction based on the current scheduled transaction accounts decimal remainingAmount = Math.Abs( txnAmount ); foreach ( var detail in scheduledTransaction.ScheduledTransactionDetails.Where( d => d.Amount != 0.0M ) ) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.AccountId = detail.AccountId; if ( detail.Amount <= remainingAmount ) { // If the configured amount for this account is less than or equal to the remaining // amount, allocate the configured amount transactionDetail.Amount = detail.Amount; remainingAmount -= detail.Amount; } else { // If the configured amount is greater than the remaining amount, only allocate // the remaining amount transaction.Summary += "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction."; transactionDetail.Amount = remainingAmount; transactionDetail.Summary = "Note: The downloaded amount was not enough to apply the configured amount to this account."; remainingAmount = 0.0M; } transaction.TransactionDetails.Add( transactionDetail ); if ( remainingAmount <= 0.0M ) { // If there's no amount left, break out of details break; } } // If there's still amount left after allocating based on current config, add the remainder // to the account that was configured for the most amount if ( remainingAmount > 0.0M ) { transaction.Summary += "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction."; var transactionDetail = transaction.TransactionDetails .OrderByDescending( d => d.Amount ) .FirstOrDefault(); if ( transactionDetail == null && defaultAccount != null ) { transactionDetail = new FinancialTransactionDetail(); transactionDetail.AccountId = defaultAccount.Id; transaction.TransactionDetails.Add( transactionDetail ); } if ( transactionDetail != null ) { transactionDetail.Amount += remainingAmount; transactionDetail.Summary = "Note: Extra amount was applied to this account."; } } // If the amount to apply was negative, update all details to be negative (absolute value was used when allocating to accounts) if ( txnAmount < 0.0M ) { foreach ( var txnDetail in transaction.TransactionDetails ) { txnDetail.Amount = 0 - txnDetail.Amount; } } // Get the batch var batch = batchService.Get( batchNamePrefix, currencyTypeValue, creditCardTypevalue, transaction.TransactionDateTime.Value, gateway.GetBatchTimeOffset(), batches ); var batchChanges = new List<string>(); if ( batch.Id != 0 ) { initialControlAmounts.AddOrIgnore( batch.Guid, batch.ControlAmount ); } batch.ControlAmount += transaction.TotalAmount; batch.Transactions.Add( transaction ); if ( txnAmount > 0.0M && receiptEmail.HasValue ) { newTransactions.Add( transaction ); } // Add summary if ( !batchSummary.ContainsKey( batch.Guid ) ) { batchSummary.Add( batch.Guid, new List<Decimal>() ); } batchSummary[batch.Guid].Add( txnAmount ); if ( txnAmount > 0.0M ) { totalAdded++; } else { totalReversals++; } } else { totalAlreadyDownloaded++; foreach ( var txn in txns.Where( t => t.Status != payment.Status || t.StatusMessage != payment.StatusMessage ) ) { txn.Status = payment.Status; txn.StatusMessage = payment.StatusMessage; totalStatusChanges++; } } } else { totalNoScheduledTransaction++; } } rockContext.SaveChanges(); // Queue a transaction to update the status of all affected scheduled transactions var updatePaymentStatusTxn = new Rock.Transactions.UpdatePaymentStatusTransaction( gateway.Id, scheduledTransactionIds ); Rock.Transactions.RockQueue.TransactionQueue.Enqueue( updatePaymentStatusTxn ); if ( receiptEmail.HasValue ) { // Queue a transaction to send receipts var newTransactionIds = newTransactions.Select( t => t.Id ).ToList(); var sendPaymentReceiptsTxn = new Rock.Transactions.SendPaymentReceipts( receiptEmail.Value, newTransactionIds ); Rock.Transactions.RockQueue.TransactionQueue.Enqueue( sendPaymentReceiptsTxn ); } } StringBuilder sb = new StringBuilder(); sb.AppendFormat( "<li>{0} {1} downloaded.</li>", totalPayments.ToString( "N0" ), ( totalPayments == 1 ? "payment" : "payments" ) ); if ( totalAlreadyDownloaded > 0 ) { sb.AppendFormat( "<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString( "N0" ), ( totalAlreadyDownloaded == 1 ? "payment was" : "payments were" ), ( totalAlreadyDownloaded == 1 ? "has" : "have" ) ); } if ( totalStatusChanges > 0 ) { sb.AppendFormat( "<li>{0} {1} previously downloaded but had a change of status.</li>", totalStatusChanges.ToString( "N0" ), ( totalStatusChanges == 1 ? "payment was" : "payments were" ) ); } if ( totalNoScheduledTransaction > 0 ) { sb.AppendFormat( "<li>{0} {1} could not be matched to an existing scheduled payment profile.</li>", totalNoScheduledTransaction.ToString( "N0" ), ( totalNoScheduledTransaction == 1 ? "payment" : "payments" ) ); } sb.AppendFormat( "<li>{0} {1} successfully added.</li>", totalAdded.ToString( "N0" ), ( totalAdded == 1 ? "payment was" : "payments were" ) ); if ( totalReversals > 0 ) { sb.AppendFormat( "<li>{0} {1} added as a reversal to a previous transaction.</li>", totalReversals.ToString( "N0" ), ( totalReversals == 1 ? "payment was" : "payments were" ) ); } if ( totalStatusChanges > 0 ) { sb.AppendFormat( "<li>{0} {1} previously downloaded but had a change of status.</li>", totalStatusChanges.ToString( "N0" ), ( totalStatusChanges == 1 ? "payment was" : "payments were" ) ); } foreach ( var batchItem in batchSummary ) { int items = batchItem.Value.Count; if ( items > 0 ) { var batch = batches .Where( b => b.Guid.Equals( batchItem.Key ) ) .FirstOrDefault(); string batchName = string.Format( "'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString( "d" ) ); if ( !string.IsNullOrWhiteSpace( batchUrlFormat ) ) { batchName = string.Format( "<a href='{0}'>{1}</a>", string.Format( batchUrlFormat, batch.Id ), batchName ); } decimal sum = batchItem.Value.Sum(); string summaryformat = items == 1 ? "<li>{0} transaction of {1} was added to the {2} batch.</li>" : "<li>{0} transactions totaling {1} were added to the {2} batch</li>"; sb.AppendFormat( summaryformat, items.ToString( "N0" ), sum.FormatAsCurrency(), batchName ); } } return sb.ToString(); }
public abstract void PreRedirect(FinancialGateway financialGateway, PaymentInfo paymentInfo, List <GatewayAccountItem> SelectedAccounts, out string errorMessage);