/// <summary> /// Logs the NewCardInfo member of the specified NewCardPayload object with obfuscated fields where required. /// </summary> /// <param name="newCardInfo"> /// The NewCardInfo object to obfuscate and log. /// </param> /// <param name="context"> /// The context of the current request. /// </param> internal static void LogObfuscatedNewCardInfo(NewCardInfo newCardInfo, CommerceContext context) { string number = newCardInfo.Number; if (String.IsNullOrWhiteSpace(number) == false && number.Length >= 4) { number = number.Substring(number.Length - 4, 4); } string logInfo = string.Empty; if (newCardInfo != null) { NewCardInfo obfuscatedNewCardInfo = new NewCardInfo() { CardBrand = newCardInfo.CardBrand, Expiration = newCardInfo.Expiration, NameOnCard = newCardInfo.NameOnCard, Number = number }; logInfo = General.SerializeJson(obfuscatedNewCardInfo); } context.Log.Verbose("Obfuscated NewCardInfo:\r\n{0}", logInfo); }
/// <summary> /// Populates the User and Card object within the current Context object. /// </summary> private void PopulateContextUserAndCard() { // Populate the Context InitialUser from the discovered User. User user = (User)Context[Key.User]; Context[Key.InitialUser] = new User(user); RewardPrograms rewardPrograms = RewardPrograms.CardLinkOffers; if (Context.ContainsKey(Key.RewardProgramType) == true) { rewardPrograms = (RewardPrograms)Context[Key.RewardProgramType]; } // Build a Card from the discovered User and specified NewCardInfo. NewCardInfo newCardInfo = (NewCardInfo)Context[Key.NewCardInfo]; int year = newCardInfo.Expiration.Year; int month = newCardInfo.Expiration.Month; Context[Key.Card] = new Card(user.GlobalId) { NameOnCard = newCardInfo.NameOnCard, LastFourDigits = newCardInfo.Number.Substring(newCardInfo.Number.Length - 4, 4), Expiration = new DateTime(year, month, DateTime.DaysInMonth(year, month)), CardBrand = ParseCardBrand(newCardInfo.CardBrand), RewardPrograms = rewardPrograms }; }
/// <summary> /// Generates a legacy NewCardInfo object for the specified credit card number. /// </summary> /// <param name="newCardNumber"> /// The object containing the number for which to generate a legacy NewCardInfo object. /// </param> /// <returns> /// The generated NewCardInfo object. /// </returns> /// <remarks> /// When the V1 AddCard and GetCard APIs are removed, legacy compatibility can be removed from all layers. /// </remarks> internal static NewCardInfo GenerateLegacyCardInfo(NewCardNumber newCardNumber) { string number = newCardNumber.Number; NewCardInfo result = new NewCardInfo { Expiration = DateTime.MaxValue, Number = number, FlightId = newCardNumber.FlightId }; if (String.IsNullOrWhiteSpace(number) == false) { switch (number[0]) { case '4': result.CardBrand = "Visa"; break; case '5': result.CardBrand = "MasterCard"; break; case '3': if (number[1] == '4' || number[1] == '7') { result.CardBrand = "AmericanExpress"; } break; } } return(result); }
/// <summary> /// Checks whether Card number matches the rejection list. /// </summary> /// <returns> /// * True if the card matches rejection list. /// * Else returns false. /// </returns> /// <remarks> /// This method takes configurable rejection list from Configuration and checks the card number against it. /// </remarks> internal bool CardProviderRejected() { bool result = false; if (ListCardProviderRejectionMask == null) { string rejectMask = CommerceServiceConfig.Instance.CardProviderRejectionMask; ListCardProviderRejectionMask = rejectMask.Split(';'); } NewCardInfo newCardInfo = (NewCardInfo)Context[Key.NewCardInfo]; string cardNumber = newCardInfo.Number; foreach (string regEx in ListCardProviderRejectionMask) { if (regEx.Length > 0) { if (Regex.IsMatch(cardNumber, regEx) == true) { result = true; break; } } } return(result); }
public Task <HttpResponseMessage> Add(NewCardNumber newCardNumber) { Stopwatch callTimer = Stopwatch.StartNew(); Task <HttpResponseMessage> result; // Build a context object to pass down the pipeline. CommerceContext context = CommerceContext.BuildAsynchronousRestContext("Add card and queue claiming already claimed deals", Request, new AddCardResponse(), callTimer); try { if (newCardNumber == null) { throw new ArgumentNullException("newCardNumber", "Parameter newCardNumber cannot be null."); } context.Log.Information("Processing {0} call.", context.ApiCallDescription); // Generate a legacy NewCardInfo object from the specified number. NewCardInfo newCardInfo = ControllerUtilities.GenerateLegacyCardInfo(newCardNumber); ControllerUtilities.LogObfuscatedNewCardInfo(newCardInfo, context); // Add parameters of the call to the context. context[Key.QueueJob] = true; context[Key.NewCardInfo] = newCardInfo; context[Key.GlobalUserId] = CommerceContext.PopulateUserId(context); context[Key.ReferrerId] = newCardNumber.Referrer; context[Key.RewardProgramType] = ControllerUtilities.GetRewardProgramAssociatedWithRequest(this.Request); // Create an executor object to execute the API invocation. AddCardExecutor executor = new AddCardExecutor(context); Task.Factory.StartNew(async() => await executor.Execute()); result = ((TaskCompletionSource <HttpResponseMessage>)context[Key.TaskCompletionSource]).Task; } catch (Exception ex) { result = RestResponder.CreateExceptionTask(context, ex); } return(result); }
public Task <HttpResponseMessage> Add(AddCardPayload payload) { Stopwatch callTimer = Stopwatch.StartNew(); if (payload == null) { throw new ArgumentNullException("payload", "Parameter payload cannot be null."); } // Build a context object to pass down the pipeline. CommerceContext context = CommerceContext.BuildAsynchronousRestContext("Create account for unauthenticated user, add card, and queue claiming deals", Request, new UnauthenticatedAddCardResponse(), callTimer); context.Log.Information("Processing {0} call.", context.ApiCallDescription); // Generate a legacy NewCardInfo object from the specified number. NewCardInfo newCardInfo = ControllerUtilities.GenerateLegacyCardInfo(new NewCardNumber { Number = payload.Number }); ControllerUtilities.LogObfuscatedNewCardInfo(newCardInfo, context); // Add parameters of the call to the context. context[Key.CreateUnauthenticatedAccount] = true; context[Key.EmailAddress] = payload.Email; context[Key.ReferrerId] = payload.Referrer; context[Key.UserLocation] = payload.UserLocation; context[Key.ReferralEvent] = ReferralEvent.Signup; context[Key.QueueJob] = true; context[Key.NewCardInfo] = newCardInfo; context[Key.RewardProgramType] = ControllerUtilities.GetRewardProgramAssociatedWithRequest(this.Request); // Create an executor object to execute the API invocation. AddCardExecutor executor = new AddCardExecutor(context); Task.Factory.StartNew(async() => await executor.Execute()); return(((TaskCompletionSource <HttpResponseMessage>)context[Key.TaskCompletionSource]).Task); }
public NewCardInfo GetNewCardInfo() { NewCardInfo cardinfo = new NewCardInfo(); return cardinfo; }
/// <summary> /// Performs preliminary checks to determine whether the card may be valid. /// </summary> /// <returns> /// * True if the card may be valid. /// * Else returns false. /// </returns> /// <remarks> /// This method does not determine whether the card actually _is_ valid, i.e. whether it can be used to make /// purchases, belongs to the specified person, etc.. Instead, it determines whether the card _may be_ valid, by /// ensuring the card brand matches the card number, that the stated expiration date is not in the past, and that the /// card number is valid according to Luhn's modulus 10 algorithm. /// </remarks> internal bool CardMayBeValid() { bool result = false; Context.Log.Verbose("Determining if NewCardInfo object may be valid."); // Ensure the card number matches the card brand. NewCardInfo newCardInfo = (NewCardInfo)Context[Key.NewCardInfo]; if (String.IsNullOrWhiteSpace(newCardInfo.Number) == false && (newCardInfo.Number[0] - '0') == (int)ParseCardBrand(newCardInfo.CardBrand)) { // Get baseline DateTime for the last day of the current month. DateTime currentDate = DateTime.UtcNow; int year = currentDate.Year; int month = currentDate.Month; int lastDayOfMonth = DateTime.DaysInMonth(year, month); DateTime endOfCurrentMonth = new DateTime(year, month, lastDayOfMonth); // Get baseline DateTime for the last day of the month in which the card expires. year = newCardInfo.Expiration.Year; month = newCardInfo.Expiration.Month; lastDayOfMonth = DateTime.DaysInMonth(year, month); DateTime endOfExpirationMonth = new DateTime(year, month, lastDayOfMonth); // If the card has not expired, continue validation. if (endOfExpirationMonth >= endOfCurrentMonth) { // Validate card number using Luhn's algorithm. // Luhn's algorithm works by starting from the last (check) digit in the credit card number and then, for the // check digit and every second previous digit, adding those digits into a total, and, for all other // digits, multiplying those digits by 2, adding the resulting digits together, and adding those sums into // the total. Those modified values are determinant, and can therefore be represented in an array for fast // runtime lookups. int checksum = 0; bool modifyDigits = false; bool nonDigitFound = false; for (int count = newCardInfo.Number.Length - 1; count >= 0; count--) { int digit = newCardInfo.Number[count] - '0'; if (digit < 0 || digit > 9) { nonDigitFound = true; break; } if (modifyDigits == false) { checksum += digit; } else { checksum += LuhnModifiedValues[digit]; } modifyDigits = !modifyDigits; } if (nonDigitFound == false) { result = checksum % 10 == 0; } } } if (result == true) { Context.Log.Verbose("NewCardInfo may be valid."); } else { Context.Log.Verbose("NewCardInfo cannot be valid."); } return(result); }
/// <summary> /// Invokes AddCard with each currently registered partner. /// </summary> public async Task Invoke() { try { ResultCode resultCode = ResultCode.Created; NewCardInfo newCardInfo = (NewCardInfo)Context[Key.NewCardInfo]; Card card = (Card)Context[Key.Card]; List <ICommercePartner> partners = new List <ICommercePartner>(); //partners.Add(new FirstData(Context)); CardBrand cardBrand = (CardBrand)Enum.Parse(typeof(CardBrand), newCardInfo.CardBrand); switch (cardBrand) { case CardBrand.AmericanExpress: partners.Add(new Amex(Context)); break; case CardBrand.MasterCard: partners.Add(new MasterCard(Context)); break; case CardBrand.Visa: partners.Add(new Visa(Context)); break; } foreach (ICommercePartner partner in partners) { string partnerName = partner.GetType().Name; Context.Log.Verbose("Adding card to partner {0}.", partnerName); ResultCode partnerResult = ResultCode.None; try { partnerResult = await partner.AddCardAsync(); } catch (Exception ex) { Context.Log.Error("Add card call to partner {0} ended with an error.", ex, partnerName); } // Update overall result from result of call to partner. switch (partnerResult) { case ResultCode.Created: // At this time, FirstData PartnerCardId is used as the PanToken for the card. if (partner is FirstData) { PartnerCardInfo firstDataInfo = card.PartnerCardInfoList.SingleOrDefault(info => info.PartnerId == Partner.FirstData); if (firstDataInfo != null) { card.PanToken = firstDataInfo.PartnerCardId; } } break; case ResultCode.InvalidCard: resultCode = ResultCode.InvalidCard; break; case ResultCode.UnknownError: resultCode = ResultCode.UnknownError; break; case ResultCode.InvalidCardNumber: resultCode = ResultCode.InvalidCardNumber; break; case ResultCode.CorporateOrPrepaidCardError: resultCode = ResultCode.CorporateOrPrepaidCardError; break; case ResultCode.MaximumEnrolledCardsLimitReached: resultCode = ResultCode.MaximumEnrolledCardsLimitReached; break; case ResultCode.CardRegisteredToDifferentUser: resultCode = ResultCode.CardRegisteredToDifferentUser; break; case ResultCode.CardExpired: resultCode = ResultCode.CardExpired; break; default: throw new InvalidOperationException("Call to partner invoker returned ResultCode.None."); } } AddCardConcluder addCardConcluder = new AddCardConcluder(Context); addCardConcluder.Conclude(resultCode); } catch (Exception ex) { RestResponder.BuildAsynchronousResponse(Context, ex); } }