/// <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); } } }
/// <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); }
/// <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); }
/// <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); } }
/// <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); } } }
/// <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); } }
/// <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); } }
/// <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); } } } }