/// <summary>
        /// Processes the PTS file.
        /// </summary>
        public async Task Process()
        {
            // Build contents of the filtering file from the list of unfiltered MasterCard cards.
            IEnumerable <string> unfilteredMasterCards = WorkerActions.RetrieveUnfilteredMasterCards(CardOperations, Context);

            if (unfilteredMasterCards.Count() > 0)
            {
                Collection <FilteringRecord> filteringRecords = new Collection <FilteringRecord>();
                foreach (string unfilteredMasterCard in unfilteredMasterCards)
                {
                    filteringRecords.Add(new FilteringRecord
                    {
                        AuthorizationSetId = CommerceWorkerConfig.Instance.MasterCardAuthorizationSetId,
                        BankCustomerNumber = unfilteredMasterCard,
                        ClearingSetId      = CommerceWorkerConfig.Instance.MasterCardClearingSetId,
                        EffectiveDate      = DateTime.UtcNow.AddDays(-CommerceWorkerConfig.Instance.MasterCardExpectedFilteringDaysDelta),
                        Threshold          = (decimal)CommerceWorkerConfig.Instance.MasterCardTransactionNotificationThreshold
                    });
                }

                // Upload the file to the blob store and to MasterCard, and then mark the cards with the filter added date.
                if (UploadFilteringFile != null)
                {
                    await UploadFilteringFile(FilteringBuilder.Build(filteringRecords, DateTime.UtcNow)).ConfigureAwait(false);

                    AddFilteredMasterCards(unfilteredMasterCards);
                }
            }
        }
示例#2
0
 /// <summary>
 /// Updates the pending redeemed deals in the merchant record list to the credit status specified, if the credit status
 /// is valid.
 /// </summary>
 /// <param name="record">
 /// Merchant record whose redeemed deal to update.
 /// </param>
 /// <param name="creditStatus">
 /// The credit status to which to set the redeemed deals.
 /// </param>
 /// <exception cref="InvalidOperationException">
 /// Parameter creditStatus contains an invalid CreditStatus for this operation.
 /// </exception>
 internal void UpdatePendingRedeemedDeals(OutstandingRedeemedDealInfo record, CreditStatus creditStatus)
 {
     // Update the credit status for the specified list of merchant records.
     WorkerActions.UpdatePendingRedeemedDeals(new Collection <OutstandingRedeemedDealInfo> {
         record
     }, creditStatus, RedeemedDealOperations, Context);
 }
示例#3
0
        /// <summary>
        /// Updates the outstanding referred redemption rewards in the record list to the reward payout status specified.
        /// </summary>
        /// <param name="records">
        /// The list of records whose status to update.
        /// </param>
        /// <param name="rewardPayoutStatus">
        /// The reward payout status to which to set the records.
        /// </param>
        private void UpdateOutstandingReferredRedemptionRewards(Collection <OutstandingReferredRedemptionReward> records,
                                                                RewardPayoutStatus rewardPayoutStatus)
        {
            Collection <int> trackedRedemptionRewardsIds = new Collection <int>();

            foreach (OutstandingReferredRedemptionReward record in records)
            {
                trackedRedemptionRewardsIds.Add(record.TrackedRedemptionRewardsId);
            }
            WorkerActions.UpdateOutstandingReferredRedemptionRewards(trackedRedemptionRewardsIds, rewardPayoutStatus, RewardOperations, Context);
        }
        /// <summary>
        /// Updates the pending redeemed deals in the merchant record list to the credit status specified, if the credit status
        /// is valid.
        /// </summary>
        /// <param name="records">
        /// The list of merchant records whose redeemed deals to update.
        /// </param>
        /// <param name="creditStatus">
        /// The credit status to which to set the redeemed deals.
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// Parameter creditStatus contains an invalid CreditStatus for this operation.
        /// </exception>
//TODO: Make internal once Worker role is up and running.
        public void UpdatePendingRedeemedDeals(Collection <OutstandingRedeemedDealInfo> records,
                                               CreditStatus creditStatus)
        {
            // Ensure specified credit status is valid for this operation.
            if (creditStatus != CreditStatus.StatementCreditRequested && creditStatus != CreditStatus.CreditGranted)
            {
                throw new InvalidOperationException("Parameter creditStatus contains an invalid CreditStatus for this operation.");
            }

            // Update the credit status for the specified list of merchant records.
            WorkerActions.UpdatePendingRedeemedDeals(records, creditStatus, RedeemedDealOperations, Context);
        }
示例#5
0
        /// <summary>
        /// Processes Visa clearing transactions.
        /// </summary>
        /// <returns></returns>
        public async Task Process()
        {
            var outstandingRedeemedDeals = WorkerActions.RetrieveOutstandingPartnerRedeemedDealRecords(Partner.Visa, RedeemedDealOperations, Context);

            if (outstandingRedeemedDeals != null && outstandingRedeemedDeals.Any())
            {
                foreach (var outstandingDeal in outstandingRedeemedDeals)
                {
                    try
                    {
                        var partnerUserId = WorkerActions.RetrieveUserId(outstandingDeal.GlobalUserId, Partner.Visa, UserOperations, Context);

                        Context[Key.CardToken]                 = outstandingDeal.Token;
                        Context[Key.DealDiscountAmount]        = outstandingDeal.DiscountAmount;
                        Context[Key.Transaction]               = outstandingDeal.PartnerRedeemedDealScopeId;
                        Context[Key.TransactionSettlementDate] = outstandingDeal.TransactionDate;
                        Context[Key.PartnerUserId]             = partnerUserId;

                        UpdatePendingRedeemedDeals(outstandingDeal, CreditStatus.SendingStatementCreditRequest);

                        ResultCode resultCode;
                        try
                        {
                            IVisaClient client = new CardLink.Visa(Context);
                            resultCode = await client.SaveStatementCreditAsync();
                        }
                        catch (Exception ex)
                        {
                            Context.Log.Error("Error submitting statement of credit to Visa for outstanding redeemed dealId {0}.", ex, outstandingDeal.DealId);
                            UpdatePendingRedeemedDeals(outstandingDeal, CreditStatus.SendingStatementCreditRequestFailed);
                            continue;
                        }

                        if (resultCode == ResultCode.Created)
                        {
                            UpdatePendingRedeemedDeals(outstandingDeal, CreditStatus.StatementCreditRequested);
                        }
                        else
                        {
                            UpdatePendingRedeemedDeals(outstandingDeal, CreditStatus.RejectedByPartner);
                        }
                    }
                    catch (Exception ex)
                    {
                        Context.Log.Error("Error processing outstanding redeemed dealId {0}.", ex, outstandingDeal.DealId);
                        UpdatePendingRedeemedDeals(outstandingDeal, CreditStatus.GeneratingStatementCreditRequestFailed);
                    }
                }

                //outstandingRedeemedDeals = WorkerActions.RetrieveOutstandingPartnerRedeemedDealRecords(Partner.Visa, RedeemedDealOperations, Context);
            }
        }
        /// <summary>
        /// Adds a redemption reward for the transaction in the context.
        /// </summary>
        /// <param name="redeemedDealId">
        /// The ID of the redeemed deal for which redemption rewards are granted.
        /// </param>
        /// <returns>
        /// A Task to perform the operation.
        /// </returns>
        private async Task AddRedemptionRewards(Guid redeemedDealId)
        {
            RedeemedDealInfo redeemedDealInfo = (RedeemedDealInfo)Context[Key.RedeemedDealInfo];

            if (EnableRedemptionRewards == true && (ReimbursementTender)redeemedDealInfo.ReimbursementTenderId == ReimbursementTender.MicrosoftEarn)
            {
                ConcurrentDictionary <string, string> payload = new ConcurrentDictionary <string, string>();
                IScheduler scheduler = PartnerFactory.Scheduler(CommerceWorkerConfig.Instance.SchedulerQueueName,
                                                                CommerceWorkerConfig.Instance.SchedulerTableName,
                                                                CommerceWorkerConfig.Instance);
                if (WorkerActions.RewardRedemption(RewardOperations, Context) == ResultCode.Success)
                {
                    // Add a job to potentially reward user for their first Earn.
                    payload[Key.RewardPayoutId.ToString()]        = ((Guid)Context[Key.RewardPayoutId]).ToString();
                    payload[Key.PartnerCardId.ToString()]         = (string)Context[Key.PartnerCardId];
                    payload[Key.PartnerRedeemedDealId.ToString()] = redeemedDealInfo.PartnerRedeemedDealId;
                    payload[Key.RewardId.ToString()] = Context.Config.FirstEarnRewardId.ToString();
                    ScheduledJobDetails scheduledJobDetails = new ScheduledJobDetails
                    {
                        JobId          = Guid.NewGuid(),
                        JobType        = ScheduledJobType.ApplyRedemptionReward,
                        JobDescription = redeemedDealInfo.GlobalUserId.ToString(),
                        Orchestrated   = true,
                        Payload        = payload
                    };
                    await scheduler.ScheduleJobAsync(scheduledJobDetails).ConfigureAwait(false);
                }

                // Add a job to potentially reward the person who referred this user for this user's first Earn.
                Context[Key.RedeemedDealId] = ((RedeemedDeal)Context[Key.RedeemedDeal]).Id;
                Guid globalUserId = ((RedeemedDealInfo)Context[Key.RedeemedDealInfo]).GlobalUserId;
                Context[Key.GlobalUserId] = globalUserId;
                string userId = globalUserId.ToString();
                if (WorkerActions.RewardReferredRedemption(RewardOperations, Context) == ResultCode.Success)
                {
                    payload[Key.GlobalUserId.ToString()]  = userId;
                    payload[Key.ReferralEvent.ToString()] = ReferralEvent.Signup.ToString();
                    ScheduledJobDetails scheduledJobDetails = new ScheduledJobDetails
                    {
                        JobId          = Guid.NewGuid(),
                        JobType        = ScheduledJobType.ApplyReferralReward,
                        JobDescription = userId,
                        Orchestrated   = true,
                        StartTime      = DateTime.UtcNow,
                        Payload        = payload
                    };
                    await scheduler.ScheduleJobAsync(scheduledJobDetails).ConfigureAwait(false);
                }
            }
        }
        /// <summary>
        /// Processes the PTS file.
        /// </summary>
        public async Task Process()
        {
            // Get the list of MasterCard redemptions marked ReadyForSettlement.
            Collection <OutstandingRedeemedDealInfo> redeemedDealRecords = WorkerActions.RetrieveOutstandingPartnerRedeemedDealRecords(Partner.MasterCard,
                                                                                                                                       RedeemedDealOperations, Context);

            // Build contents of the rebate file from the list of ReadyForSettlement redemptions.
            string rebateFileContents = RebateBuilder.Build(MasterCard.MarshalOutstandingRedeemedDeals(redeemedDealRecords), DateTime.UtcNow);

            // Upload the file to the blob store and to MasterCard, and then mark the redemptions as SettledAsRedeemed.
            // NOTE: Status is immediately marked SettledAsRedeemed, because, unlike other partners, MasterCard only tells us which rebates it rejects.
            if (UploadRebateFile != null)
            {
                await UploadRebateFile(rebateFileContents).ConfigureAwait(false);

                WorkerActions.MarkSettledAsRedeemed(redeemedDealRecords, RedeemedDealOperations, Context);
            }
        }
示例#8
0
        /// <summary>
        /// Processes an extract file.
        /// </summary>
        internal async Task ProcessExtract()
        {
            // Deserialize extract file into an Extract object.
            ExtractParser extractParser = new ExtractParser(Context.Log);
            Extract       extract       = extractParser.Parse(ExtractFileName, ExtractFileStream);

            if (extract != null)
            {
                // Process each settlement detail record in turn, keeping track of the total amount represented for settlement
                // detail records in the extract.
                decimal totalSettlementTransactionAmount = 0;
                foreach (SettlementDetail settlementDetail in extract.SettlementDetailRecords)
                {
                    if (settlementDetail != null)
                    {
                        ResultCode recordResult = WorkerActions.ProcessPartnerRedeemedDealRecord(Partner.FirstData,
                                                                                                 settlementDetail,
                                                                                                 RedeemedDealOperations,
                                                                                                 Context);
                        if (recordResult == ResultCode.Success || recordResult == ResultCode.CreditStatusTooAdvanced)
                        {
//TODO: How are reversals handled here? May need a real-world reversal example to be sure.
                            totalSettlementTransactionAmount += settlementDetail.TotalTransactionAmount;
                            // If the record was processed successfully, add any applicable rewards tied to the redemption event.
                            if (recordResult == ResultCode.Success)
                            {
                                await ProcessRewardPayoutAsync(settlementDetail);
                            }
                        }
                    }
                }

                // Evaluate the integrity of the Extract file.
                if (extract.Footer != null)
                {
                    WorkerActions.EvaluateRedeemedDealMetadata(extract.SettlementDetailRecords.Count,
                                                               extract.Footer.NumberOfSettlementRecords,
                                                               totalSettlementTransactionAmount,
                                                               extract.Footer.TotalSettlementRecordAmount,
                                                               Context);
                }
            }
        }
示例#9
0
        /// <summary>
        /// Build the string representation of the file.
        /// </summary>
        /// <returns>
        /// String representation of the file or null
        /// </returns>
        public async Task Build(Func <string, Task> onStatementCreditFileBuildFunc)
        {
            Collection <OutstandingRedeemedDealInfo> redeemedDealRecords = WorkerActions.RetrieveOutstandingPartnerRedeemedDealRecords(Partner.Amex,
                                                                                                                                       RedeemedDealOperations, Context);

            if (onStatementCreditFileBuildFunc != null)
            {
                // this will ftp the data and store it in blob
                StatementCreditFile file = BuildCreditFile(redeemedDealRecords);
                string result            = ConvertFileToString(file);
                await onStatementCreditFileBuildFunc(result).ConfigureAwait(false);

                // Commenting the earlier implementation of marking the transactions as StatementCreditRequested
                //WorkerActions.UpdatePendingRedeemedDeals(redeemedDealRecords, CreditStatus.StatementCreditRequested, RedeemedDealOperations, Context);

                // follow the master card pattern to mark as settled unless we get an error from amex
                WorkerActions.MarkSettledAsRedeemed(redeemedDealRecords, RedeemedDealOperations, Context);
            }
        }
示例#10
0
        /// <summary>
        /// Processes the PTS file.
        /// </summary>
        public virtual async Task Process()
        {
            Collection <OutstandingReferredRedemptionReward> result = WorkerActions.RetrieveOutstandingReferredRedemptionRewardRecords(RewardOperations, Context);

            if (OnRewardsPtsBuild != null)
            {
                // Marshal OutstandingReferredRedemptionReward records into the OutstandingRedeemedDealInfo records expected by the PTS builder.
                Collection <OutstandingRedeemedDealInfo> outstandingRedeemedDealInfoList = new Collection <OutstandingRedeemedDealInfo>();
                foreach (OutstandingReferredRedemptionReward outstandingReward in result)
                {
                    outstandingRedeemedDealInfoList.Add(new OutstandingRedeemedDealInfo
                    {
                        RedeemedDealId          = outstandingReward.RedeemedDealId,
                        PartnerMerchantId       = "221042079998",
                        MerchantName            = "REFER FRIEND",
                        OfferId                 = "qqqqqqqqqq",
                        AcquirerReferenceNumber = "24601",
                        Token            = outstandingReward.PartnerCardId,
                        DiscountAmount   = Int32.Parse(outstandingReward.Reward),
                        TransactionDate  = outstandingReward.PayoutScheduledDate,
                        ReferenceNumber  = outstandingReward.TrackedRedemptionRewardsId,
                        SettlementAmount = 0,
                        DiscountId       = String.Empty,
                        DealId           = String.Empty,
                        PartnerData      = null
                    });
                }

                // FDC requires EST.
                TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
                DateTime     estNow      = TimeZoneInfo.ConvertTime(DateTime.UtcNow, easternZone);

                // this will ftp the data and store it in blob
                // Always construct file as sequence number of 3, because PTS job runs once a day and this will be after Visa dump
                string ptsFileContents = PtsBuilder.Build(outstandingRedeemedDealInfoList, estNow, 3, true);
                await OnRewardsPtsBuild(ptsFileContents).ConfigureAwait(false);

                // now update db
                UpdateOutstandingReferredRedemptionRewards(result, RewardPayoutStatus.Pending);
            }
        }
示例#11
0
        /// <summary>
        /// Process the rewards if applicable
        /// </summary>
        /// <param name="settlementDetail">
        /// Settlement Details
        /// </param>
        /// <returns>
        /// Async Task Wrapper
        /// </returns>
        internal async Task ProcessRewardPayoutAsync(SettlementDetail settlementDetail)
        {
            if (settlementDetail.TransactionType == TransactionType.SettlementRedemption)
            {
                // First add a redemption reward to the redeeming user if they're enabled.
                if (EnableRedemptionRewards == true && WorkerActions.RewardRedemption(RewardOperations, Context) == ResultCode.Success)
                {
                    // Add job to process the reward payout. Note that this job will be scheduled 30 minutes in the
                    // future to guard against applying a reward for a transaction that was reversed in a later
                    // record.
                    ConcurrentDictionary <string, string> payload = new ConcurrentDictionary <string, string>();
                    payload[Key.RewardPayoutId.ToString()]        = ((Guid)Context[Key.RewardPayoutId]).ToString();
                    payload[Key.PartnerCardId.ToString()]         = (string)Context[Key.PartnerCardId];
                    payload[Key.PartnerRedeemedDealId.ToString()] = settlementDetail.TransactionId;

                    IScheduler scheduler = PartnerFactory.Scheduler(CommerceWorkerConfig.Instance.SchedulerQueueName,
                                                                    CommerceWorkerConfig.Instance.SchedulerTableName,
                                                                    CommerceWorkerConfig.Instance);
                    ScheduledJobDetails scheduledJobDetails = new ScheduledJobDetails
                    {
                        JobId          = Guid.NewGuid(),
                        JobType        = ScheduledJobType.ApplyRedemptionReward,
                        JobDescription = settlementDetail.ConsumerId,
                        Orchestrated   = true,
                        StartTime      = DateTime.UtcNow.AddMinutes(30),
                        Payload        = payload
                    };

                    await scheduler.ScheduleJobAsync(scheduledJobDetails).ConfigureAwait(false);
                }

                // Then add a referred redemption reward to the user who referred the redeeming user.
                WorkerActions.RewardReferredRedemption(RewardOperations, Context);
            }
            else
            {
                Context.Log.Verbose("No Bing Reward can be given for a reversed transaction.");
            }
        }
        /// <summary>
        /// Processes the PTS file.
        /// </summary>
        public async Task Process()
        {
            Collection <OutstandingRedeemedDealInfo> result = WorkerActions.RetrieveOutstandingPartnerRedeemedDealRecords(Partner.FirstData,
                                                                                                                          RedeemedDealOperations, Context);

            if (OnPtsBuild != null)
            {
                // FDC requires EST.
                TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
                DateTime     estNow      = TimeZoneInfo.ConvertTime(DateTime.UtcNow, easternZone);

                // this will ftp the data and store it in blob
                // Always construct file as sequence number of 1, because FDC PTS job runs first each day.
                string ptsFileContents = PtsBuilder.Build(result, estNow, 1);
                await OnPtsBuild(ptsFileContents).ConfigureAwait(false);

                // now update db
                UpdatePendingRedeemedDeals(result, CreditStatus.StatementCreditRequested);

                if (TransactionPublisher != null)
                {
                    foreach (OutstandingRedeemedDealInfo outstandingRedeemedDealInfo in result)
                    {
                        TransactionDetail transactionDetail = new TransactionDetail()
                        {
                            TransactionDate  = outstandingRedeemedDealInfo.TransactionDate.ToString("yyMMdd-HHmmss"),
                            DealId           = outstandingRedeemedDealInfo.DealId,
                            DiscountId       = outstandingRedeemedDealInfo.DiscountId,
                            DiscountAmount   = outstandingRedeemedDealInfo.DiscountAmount,
                            SettlementAmount = outstandingRedeemedDealInfo.SettlementAmount
                        };

                        await TransactionPublisher.PublishTransactionAsync(transactionDetail).ConfigureAwait(false);
                    }
                }
            }
        }
 /// <summary>
 /// Adds to the data store the filtering date for the MasterCards cards in the specified list.
 /// </summary>
 /// <param name="filteredMasterCards">
 /// The list of MasterCard cards whose filtering data to add to the data store.
 /// </param>
 public void AddFilteredMasterCards(IEnumerable <string> filteredMasterCards)
 {
     WorkerActions.AddFilteredMasterCards(filteredMasterCards, CardOperations, Context);
 }
        private void ProcessAcknowledgement()
        {
            // Deserialize ack file into an ack object.
            AcknowledgmentParser acknowledgmentParser = new AcknowledgmentParser(Context.Log);
            Acknowledgment       acknowledgment       = acknowledgmentParser.Parse(AcknowledgmentFileName, AcknowledgmentFileStream);

            Collection <int> successfullyRedeemedReferenceNumbers = new Collection <int>();
            Collection <int> rejectedByPartnerReferenceNumbers    = new Collection <int>();
            Collection <int> successfullyGrantedRewards           = new Collection <int>();
            Collection <int> rejectedRewards = new Collection <int>();

            if (acknowledgment != null)
            {
                // if we have general ack records to process, get the first one
                // We should ideally have only one B record, and we will log warning in parsing code otherwise.
                if (acknowledgment.GeneralAcknowledgments.Count > 0)
                {
                    GeneralAcknowledgment generalAcknowledgment = acknowledgment.GeneralAcknowledgments[0];
                    if (generalAcknowledgment.AcknowledgementCode != AcknowledgmentConstants.SuccessfulSubmissionAckCode)
                    {
                        // log failure and return
                        Context.Log.Warning(
                            "The PTS file submission was rejected. Details: \r\n:" +
                            "Record Seq Number : {0} \r\n" +
                            "Received Acknowledgment Code : {1} \r\n" +
                            "Sumission Id : {2} \r\n",
                            (int)ResultCode.SubmissionRejected,
                            generalAcknowledgment.RecordSequenceNumber,
                            generalAcknowledgment.AcknowledgementCode,
                            generalAcknowledgment.SubmissionId);

                        return;
                    }
                }


                // process each Detail Ack Record.
                foreach (DetailAcknowledgment detailAcknowledgment in acknowledgment.DetailAcknowledgments)
                {
                    if (detailAcknowledgment != null)
                    {
                        // No overflow because reference number will never be more than int range.
                        int referenceNumber = Convert.ToInt32(detailAcknowledgment.ReferenceNumber);

                        // add all the successfull
                        if (detailAcknowledgment.AcknowledgementCode == AcknowledgmentConstants.SuccessfulRedemptionAckCode)
                        {
                            if (String.Equals(detailAcknowledgment.MerchantDescriptor,
                                              ReferredRedemptionRewardsMerchantDescriptor, StringComparison.OrdinalIgnoreCase) == false)
                            {
                                successfullyRedeemedReferenceNumbers.Add(referenceNumber);
                            }
                            else
                            {
                                successfullyGrantedRewards.Add(referenceNumber);
                            }
                        }
                        else
                        {
                            Context.Log.Warning(
                                "Deal with reference number {0} was not accepted by the partner. " +
                                "We expected AcknowledgmentCode of {1} but we got {2}",
                                (int)ResultCode.RedeemedDealRejectedByPartner,
                                referenceNumber,
                                AcknowledgmentConstants.SuccessfulRedemptionAckCode,
                                detailAcknowledgment.AcknowledgementCode);

                            if (String.Equals(detailAcknowledgment.MerchantDescriptor,
                                              ReferredRedemptionRewardsMerchantDescriptor, StringComparison.OrdinalIgnoreCase) == false)
                            {
                                rejectedByPartnerReferenceNumbers.Add(referenceNumber);
                            }
                            else
                            {
                                rejectedRewards.Add(referenceNumber);
                            }
                        }
                    }
                }
            }

            if (successfullyRedeemedReferenceNumbers.Count > 0)
            {
                UpdateRedeemedDeals(successfullyRedeemedReferenceNumbers, CreditStatus.CreditGranted);
                WorkerActions.UpdateOutstandingReferredRedemptionRewards(successfullyGrantedRewards, RewardPayoutStatus.Paid, RewardOperations, Context);
            }

            if (rejectedByPartnerReferenceNumbers.Count > 0)
            {
                UpdateRedeemedDeals(rejectedByPartnerReferenceNumbers, CreditStatus.RejectedByPartner);
                WorkerActions.UpdateOutstandingReferredRedemptionRewards(rejectedRewards, RewardPayoutStatus.Rescinded, RewardOperations, Context);
            }
        }
        /// <summary>
        /// Process the transaction log file
        /// </summary>
        /// <returns>
        /// Async Task Wrapper
        /// </returns>
        public async Task Process()
        {
            TransactionLogParser transactionLogParser = new TransactionLogParser(Context.Log);
            TransactionLogFile   transactionLogFile   = transactionLogParser.Parse(TransactionLogFileName, TransactionLogFileStream);

            if (transactionLogFile != null)
            {
                foreach (TransactionLogDetail detail in transactionLogFile.TransactionLogRecords)
                {
                    // block the reversed transactions
                    if (TransactionIdSet.Contains(detail.TransactionId) || detail.TransactionAmount <= 0)
                    {
                        continue;
                    }

                    TransactionIdSet.Add(detail.TransactionId);

                    // 1. process the detail record here -> Insert as redeemed deal
                    RedeemedDeal redeemedDeal = new RedeemedDeal()
                    {
                        AnalyticsEventId = Guid.NewGuid()
                    };
                    Context[Key.RedeemedDeal] = redeemedDeal;
                    MarshalRedeemDeal(detail);
                    ResultCode result = RedeemedDealOperations.AddRedeemedDeal();

                    //2. If the record was processed successfully, attempt to add a redemption reward if applicable and analytics
                    if (result == ResultCode.Created)
                    {
                        RedeemedDealInfo redemptionInfo = (RedeemedDealInfo)Context[Key.RedeemedDealInfo];
                        // First add a redemption reward to the redeeming user if they're enabled.
                        if (EnableRedemptionRewards)
                        {
                            if (WorkerActions.RewardRedemption(RewardOperations, Context) == ResultCode.Success)
                            {
                                // Add job to process the reward payout.
                                ConcurrentDictionary <string, string> payload = new ConcurrentDictionary <string, string>();
                                payload[Key.RewardPayoutId.ToString()]        = ((Guid)Context[Key.RewardPayoutId]).ToString();
                                payload[Key.PartnerCardId.ToString()]         = (string)Context[Key.PartnerCardId];
                                payload[Key.PartnerRedeemedDealId.ToString()] = redemptionInfo.PartnerRedeemedDealId;

                                IScheduler scheduler = PartnerFactory.Scheduler(CommerceWorkerConfig.Instance.SchedulerQueueName,
                                                                                CommerceWorkerConfig.Instance.SchedulerTableName,
                                                                                CommerceWorkerConfig.Instance);
                                ScheduledJobDetails scheduledJobDetails = new ScheduledJobDetails
                                {
                                    JobId          = Guid.NewGuid(),
                                    JobType        = ScheduledJobType.ApplyRedemptionReward,
                                    JobDescription = redemptionInfo.GlobalUserId.ToString(),
                                    Orchestrated   = true,
                                    Payload        = payload
                                };

                                await scheduler.ScheduleJobAsync(scheduledJobDetails).ConfigureAwait(false);
                            }
                        }

                        // Then add a referred redemption reward to the user who referred the redeeming user.
                        Context[Key.RedeemedDealId] = ((RedeemedDeal)Context[Key.RedeemedDeal]).Id;
                        Context[Key.GlobalUserId]   = ((RedeemedDealInfo)Context[Key.RedeemedDealInfo]).GlobalUserId;
                        WorkerActions.RewardReferredRedemption(RewardOperations, Context);

                        // Update analytics.
                        // For FDC this happens at AUTH time
                        // Butfor Amex flow, we put analytics at the time of Transaction File Processing
                        SharedUserLogic sharedUserLogic = new SharedUserLogic(Context,
                                                                              CommerceOperationsFactory.UserOperations(Context));
                        Context[Key.GlobalUserId] = redemptionInfo.GlobalUserId;
                        User user = sharedUserLogic.RetrieveUser();
                        Analytics.AddRedemptionEvent(redemptionInfo.GlobalUserId, redeemedDeal.AnalyticsEventId, user.AnalyticsEventId,
                                                     redemptionInfo.ParentDealId, redemptionInfo.Currency,
                                                     redeemedDeal.AuthorizationAmount, redemptionInfo.DiscountAmount,
                                                     redemptionInfo.GlobalId, (string)Context[Key.PartnerMerchantId],
                                                     CommerceWorkerConfig.Instance);
                    }
                }
            }
        }