public async Task <IActionResult> ChargebackOrder(OrderViewModel viewModel, ChargebackStatus chargebackStatus) { return(await ModifyOrderView("ChargebackDone", viewModel, async order => { #region Fraud Protection Service var chargeback = new Chargeback { ChargebackId = Guid.NewGuid().ToString(), Amount = order.Total, Currency = order.RiskPurchase?.Currency, BankEventTimestamp = DateTimeOffset.Now, Reason = viewModel.ReturnOrChargebackReason, Status = chargebackStatus.ToString(), PurchaseId = order.RiskPurchase.PurchaseId, UserId = order.RiskPurchase.User.UserId, }; var correlationId = _fraudProtectionService.NewCorrelationId; var response = await _fraudProtectionService.PostChargeback(chargeback, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, chargeback, response, "Chargeback"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); #endregion order.ReturnOrChargebackReason = viewModel.ReturnOrChargebackReason; order.Status = OrderStatus.ChargeBack; order.RiskChargeback = chargeback; order.RiskChargebackResponse = response; })); }
public async Task <IActionResult> ReturnOrder(OrderViewModel viewModel, RefundStatus refundStatus, RefundReason refundReason) { return(await ModifyOrderView("ReturnDone", viewModel, async order => { #region Fraud Protection Service var refund = new Refund { RefundId = Guid.NewGuid().ToString(), Amount = order.Total, Currency = order.RiskPurchase?.Currency, BankEventTimestamp = DateTimeOffset.Now, PurchaseId = order.RiskPurchase.PurchaseId, Reason = refundReason.ToString(), Status = refundStatus.ToString(), UserId = order.RiskPurchase.User.UserId, }; var correlationId = _fraudProtectionService.NewCorrelationId; var response = await _fraudProtectionService.PostRefund(refund, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, refund, response, "Refund"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); #endregion order.ReturnOrChargebackReason = refundReason.ToString(); order.Status = refundStatus == RefundStatus.Approved ? OrderStatus.ReturnCompleted : OrderStatus.ReturnRejected; order.RiskRefund = refund; order.RiskRefundResponse = response; })); }
private async Task<IActionResult> CallCustomAssessmentApi(CustomAssessmentViewModel model, string returnUrl) { #region Fraud Protection Service var correlationId = _fraudProtectionService.NewCorrelationId; var assessment = new CustomAssessment { ApiName = model.ApiName, Payload = model.Payload }; var response = await _fraudProtectionService.PostCustomAssessment(assessment, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, model.Payload, response, "Custom Assessment", true); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); #endregion return View("CustomAssessment", model); }
public async Task <IActionResult> CheckoutDetails(CheckoutDetailsViewModel checkoutDetails) { var basketViewModel = await GetBasketViewModelAsync(); var address = new Address( string.Join(' ', checkoutDetails.ShippingAddress.Address1, checkoutDetails.ShippingAddress.Address2), checkoutDetails.ShippingAddress.City, checkoutDetails.ShippingAddress.State, checkoutDetails.ShippingAddress.CountryRegion, checkoutDetails.ShippingAddress.ZipCode); var paymentInfo = new PaymentInfo( string.Join(' ', checkoutDetails.User.FirstName, checkoutDetails.User.LastName), checkoutDetails.CreditCard.CardNumber, checkoutDetails.CreditCard.CardType, checkoutDetails.CreditCard.CVV, string.Join('/', checkoutDetails.CreditCard.ExpirationMonth, checkoutDetails.CreditCard.ExpirationYear)); #region Fraud Protection Service //Call Fraud Protection to get the risk score for this purchase. //We get a correlation ID that we pass to all Fraud Protection calls (up to 4) that we make in the purchase workflow. //This is optional, but can help when troubleshooting bugs or performance issues. var purchase = SetupPurchase(checkoutDetails, basketViewModel); var correlationId = _fraudProtectionService.NewCorrelationId; var result = await _fraudProtectionService.PostPurchase(purchase, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, purchase, result, "Purchase"); //Check the risk score that was returned and possibly complete the purchase. var status = await ApproveOrRejectPurchase( result.ResultDetails.MerchantRuleDecision, checkoutDetails.CreditCard.UnformattedCardNumber, purchase.PurchaseId, correlationId, fraudProtectionIO); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); #endregion await _orderService.CreateOrderAsync(basketViewModel.Id, address, paymentInfo, status, purchase, result); await _basketService.DeleteBasketAsync(basketViewModel.Id); ViewData["OrderResult"] = status; return(View("Checkout")); }
private async Task <IActionResult> RegisterUser(RegisterViewModel model, string returnUrl, bool useAP) { //Create the user object and validate it before calling Fraud Protection var user = new ApplicationUser { UserName = model.User.Email, Email = model.User.Email, FirstName = model.User.FirstName, LastName = model.User.LastName, PhoneNumber = model.User.Phone, Address1 = model.Address.Address1, Address2 = model.Address.Address2, City = model.Address.City, State = model.Address.State, ZipCode = model.Address.ZipCode, CountryRegion = model.Address.CountryRegion }; foreach (var v in _userManager.UserValidators) { var validationResult = await v.ValidateAsync(_userManager, user); if (!validationResult.Succeeded) { AddErrors(validationResult); } } ; foreach (var v in _userManager.PasswordValidators) { var validationResult = await v.ValidateAsync(_userManager, user, model.Password); if (!validationResult.Succeeded) { AddErrors(validationResult); } } ; if (ModelState.ErrorCount > 0) { return(View("Register", model)); } #region Fraud Protection Service var correlationId = _fraudProtectionService.NewCorrelationId; // Ask Fraud Protection to assess this signup/registration before registering the user in our database, etc. if (useAP) { AccountProtection.SignUp signupEvent = CreateSignupAPEvent(model); var signupAssessment = await _fraudProtectionService.PostSignupAP(signupEvent, correlationId); //Track Fraud Protection request/response for display only var fraudProtectionIO = new FraudProtectionIOModel(correlationId, signupEvent, signupAssessment, "Signup"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); bool rejectSignup = false; if (signupAssessment is AccountProtection.ResponseSuccess signupResponse) { rejectSignup = signupResponse.ResultDetails.FirstOrDefault()?.Decision != AccountProtection.DecisionName.Approve; } if (rejectSignup) { ModelState.AddModelError("", "Signup rejected by Fraud Protection. You can try again as it has a random likelihood of happening in this sample site."); return(View("Register", model)); } } else { SignUp signupEvent = CreateSignupEvent(model); var signupAssessment = await _fraudProtectionService.PostSignup(signupEvent, correlationId); //Track Fraud Protection request/response for display only var fraudProtectionIO = new FraudProtectionIOModel(correlationId, signupEvent, signupAssessment, "Signup"); //2 out of 3 signups will succeed on average. Adjust if you want more or less signups blocked for tesing purposes. var rejectSignup = new Random().Next(0, 3) != 0; var signupStatusType = rejectSignup ? SignupStatusType.Rejected.ToString() : SignupStatusType.Approved.ToString(); var signupStatus = new SignupStatusEvent { SignUpId = signupEvent.SignUpId, StatusType = signupStatusType, StatusDate = DateTimeOffset.Now, Reason = "User is " + signupStatusType }; if (!rejectSignup) { signupStatus.User = new SignupStatusUser { UserId = model.User.Email }; } var signupStatusResponse = await _fraudProtectionService.PostSignupStatus(signupStatus, correlationId); fraudProtectionIO.Add(signupStatus, signupStatusResponse, "Signup Status"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); if (rejectSignup) { ModelState.AddModelError("", "Signup rejected by Fraud Protection. You can try again as it has a random likelihood of happening in this sample site."); return(View("Register", model)); } } #endregion var result = await _userManager.CreateAsync(user, model.Password); if (!result.Succeeded) { AddErrors(result); return(View("Register", model)); } await _signInManager.SignInAsync(user, isPersistent : false); await TransferBasketToEmailAsync(user.Email); return(RedirectToLocal(returnUrl)); }
private async Task <IActionResult> SignInUser(LoginViewModel model, string returnUrl, bool useAP) { var applicationUser = new ApplicationUser { UserName = model.Email }; string hashedPassword = _userManager.PasswordHasher.HashPassword(applicationUser, model.Password); bool rejectSignIn = false; if (useAP) { var user = new AccountProtection.User() { UserType = AccountProtection.UserType.Consumer, Username = model.Email, UserId = model.Email }; var device = new AccountProtection.DeviceContext() { DeviceContextId = model.DeviceFingerPrinting.SessionId, IpAddress = _contextAccessor.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(), Provider = DeviceContextProvider.DFPFingerPrinting.ToString() }; var metadata = new AccountProtection.EventMetadataAccountLogin() { TrackingId = Guid.NewGuid().ToString(), LoginId = Guid.NewGuid().ToString(), CustomerLocalDate = DateTime.Now, MerchantTimeStamp = DateTime.Now }; var signIn = new AccountProtection.SignIn() { Name = "AP.AccountLogin", Version = "0.5", Device = device, User = user, Metadata = metadata }; var correlationId = _fraudProtectionService.NewCorrelationId; var signInResponse = await _fraudProtectionService.PostSignInAP(signIn, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, signIn, signInResponse, "SignIn"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); if (signInResponse is AccountProtection.ResponseSuccess response) { rejectSignIn = response.ResultDetails.FirstOrDefault()?.Decision != AccountProtection.DecisionName.Approve; } } else { var signIn = new SignIn { SignInId = Guid.NewGuid().ToString(), PasswordHash = hashedPassword, MerchantLocalDate = DateTimeOffset.Now, CustomerLocalDate = model.DeviceFingerPrinting.ClientDate, UserId = model.Email, DeviceContextId = model.DeviceFingerPrinting.SessionId, AssessmentType = AssessmentType.Protect.ToString(), CurrentIpAddress = _contextAccessor.HttpContext.Connection.RemoteIpAddress.ToString() }; var correlationId = _fraudProtectionService.NewCorrelationId; var signInResponse = await _fraudProtectionService.PostSignIn(signIn, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, signIn, signInResponse, "SignIn"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); //2 out of 3 signIn will be successful rejectSignIn = new Random().Next(0, 3) != 0; } if (!rejectSignIn) { var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure : false); if (!result.Succeeded) { ModelState.AddModelError(string.Empty, "Invalid login attempt."); return(View("SignIn", model)); } // redirect if signIn is not rejected and password sign-in is success await TransferBasketToEmailAsync(model.Email); return(RedirectToLocal(returnUrl)); } else { ModelState.AddModelError("", "Signin rejected by Fraud Protection. You can try again as it has a random likelihood of happening in this sample site."); return(View("SignIn", model)); } }
public async Task <IActionResult> LabelOrder( OrderViewModel viewModel, LabelSource labelSource, LabelObjectType labelObjectType, LabelState labelStatus, LabelReasonCodes labelReasonCode) { var order = await GetOrder(viewModel.OrderNumber); if (order == null) { return(BadRequest("No such order found for this user.")); } #region Fraud Protection Service string labelObjectId; switch (labelObjectType) { case LabelObjectType.Purchase: labelObjectId = order.RiskPurchase.PurchaseId; break; case LabelObjectType.Account: labelObjectId = order.RiskPurchase.User.UserId; break; case LabelObjectType.Email: labelObjectId = order.RiskPurchase.User.Email; break; case LabelObjectType.PI: labelObjectId = order.RiskPurchase.PaymentInstrumentList[0].MerchantPaymentInstrumentId; break; default: throw new InvalidOperationException("Label object type not supported: " + labelObjectType); } var label = new Label { LabelObjectType = labelObjectType.ToString(), LabelObjectId = labelObjectId, LabelSource = labelSource.ToString(), LabelReasonCodes = labelReasonCode.ToString(), LabelState = labelStatus.ToString(), EventTimeStamp = DateTimeOffset.Now, Processor = "Fraud Protection sample site", }; if (labelObjectType == LabelObjectType.Purchase) { label.Amount = order.Total; label.Currency = order.RiskPurchase?.Currency; } var correlationId = _fraudProtectionService.NewCorrelationId; var response = await _fraudProtectionService.PostLabel(label, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, label, response, "Label"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); #endregion return(View("LabelDone")); }
/// <summary> /// Checks the purchase's risk score. /// If the purchase is rejected, submit a REJECTED purchase status. /// If the purchase is approved, submit the bank AUTH, bank CHARGE, and purchase status (approved if the bank also approves the auth and charge, or rejected otherwise). /// If the purchase is in review, submit unknown bank AUTH and unknown purchase status (so that case management case is still created) /// </summary> private async Task <OrderStatus> ApproveOrRejectPurchase(string merchantRuleDecision, string cardNumber, string purchaseId, string correlationId, FraudProtectionIOModel fraudProtectionIO) { var status = OrderStatus.Received; BankEvent auth = null; BankEvent charge = null; PurchaseStatusEvent purchaseStatus; if (!FakeCreditCardBankResponses.CreditCardResponses.TryGetValue(cardNumber, out FakeCreditCardBankResponses creditCardBankResponse)) { //default response creditCardBankResponse = new FakeCreditCardBankResponses { IgnoreFraudRiskRecommendation = false, IsAuthApproved = true, IsChargeApproved = true }; } var isApproved = merchantRuleDecision.StartsWith("APPROVE", StringComparison.OrdinalIgnoreCase); var inReview = merchantRuleDecision.StartsWith("REVIEW", StringComparison.OrdinalIgnoreCase); if (isApproved || inReview || creditCardBankResponse.IgnoreFraudRiskRecommendation) { if (!creditCardBankResponse.IsAuthApproved) { //Auth - Rejected auth = SetupBankEvent(BankEventType.Auth, DateTimeOffset.Now, purchaseId, BankEventStatus.Declined); //Purchase status - Rejected purchaseStatus = SetupPurchaseStatus(purchaseId, PurchaseStatusType.Rejected); status = OrderStatus.Rejected; } else { //Auth - Approved/Unknown var authStatus = inReview ? BankEventStatus.Unknown : BankEventStatus.Approved; auth = SetupBankEvent(BankEventType.Auth, DateTimeOffset.Now, purchaseId, authStatus); //Charge var chargeStatus = creditCardBankResponse.IsChargeApproved ? BankEventStatus.Approved : BankEventStatus.Declined; if (!inReview) { charge = SetupBankEvent(BankEventType.Charge, DateTimeOffset.Now, purchaseId, chargeStatus); } if (inReview) { //Purchase status - Unknown purchaseStatus = SetupPurchaseStatus(purchaseId, PurchaseStatusType.Unknown); status = OrderStatus.InReview; } else if (creditCardBankResponse.IsChargeApproved) { //Purchase status - Approved purchaseStatus = SetupPurchaseStatus(purchaseId, PurchaseStatusType.Approved); } else { //Purchase status - Rejected purchaseStatus = SetupPurchaseStatus(purchaseId, PurchaseStatusType.Rejected); status = OrderStatus.Rejected; } } } else { purchaseStatus = SetupPurchaseStatus(purchaseId, PurchaseStatusType.Rejected); status = OrderStatus.Rejected; } if (auth != null) { var response = await _fraudProtectionService.PostBankEvent(auth, correlationId); fraudProtectionIO.Add(auth, response, "BankEvent Auth"); } if (charge != null) { var response = await _fraudProtectionService.PostBankEvent(charge, correlationId); fraudProtectionIO.Add(charge, response, "BankEvent Charge"); } if (purchaseStatus != null) { var response = await _fraudProtectionService.PostPurchaseStatus(purchaseStatus, correlationId); fraudProtectionIO.Add(purchaseStatus, response, "PurchaseStatus"); } return(status); }
public async Task <IActionResult> Index(IndexViewModel model) { if (!ModelState.IsValid) { return(View(model)); } var user = await _userManager.GetUserAsync(User); if (user == null) { throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } // Update the Application User user.FirstName = model.User.FirstName; user.LastName = model.User.LastName; user.Email = model.User.Email; user.UserName = model.Username; user.PhoneNumber = model.User.Phone; user.Address1 = model.Address.Address1; user.Address2 = model.Address.Address2; user.City = model.Address.City; user.State = model.Address.State; user.ZipCode = model.Address.ZipCode; user.CountryRegion = model.Address.CountryRegion; var result = await _userManager.UpdateAsync(user); #region Fraud Protection Service // If storing the user locally succeeds, update Fraud Protection if (result.Succeeded) { var billingAddress = new UserAddress { Type = UserAddressType.Billing.ToString(), FirstName = user.FirstName, LastName = user.LastName, Street1 = user.Address1, Street2 = user.Address2, City = user.City, State = user.State, ZipCode = user.ZipCode, Country = user.CountryRegion }; var shippingAddress = new UserAddress { Type = UserAddressType.Shipping.ToString(), FirstName = user.FirstName, LastName = user.LastName, Street1 = user.Address1, Street2 = user.Address2, City = user.City, State = user.State, ZipCode = user.ZipCode, Country = user.CountryRegion }; var fraudProtectionUser = new User { UserId = user.Email, UpdateDate = DateTimeOffset.Now, Email = user.Email, FirstName = user.FirstName, LastName = user.LastName, PhoneNumber = user.PhoneNumber, AddressList = new List <UserAddress> { billingAddress, shippingAddress }, ZipCode = user.ZipCode, Country = user.CountryRegion, TimeZone = new TimeSpan(0, 0, -model.DeviceFingerPrinting.ClientTimeZone, 0).ToString(), DeviceContext = new DeviceContext { DeviceContextId = _contextAccessor.GetSessionId(), IPAddress = _contextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(), Provider = DeviceContextProvider.DFPFingerPrinting.ToString() } }; var correlationId = _fraudProtectionService.NewCorrelationId; var response = await _fraudProtectionService.PostUser(fraudProtectionUser, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, fraudProtectionUser, response, "UpdateAccount"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); } #endregion StatusMessage = "Your profile has been updated"; return(RedirectToAction(nameof(Index))); }
public async Task <IActionResult> ManagePaymentInstrument(ManagePaymentInstrumentViewModel model) { var user = await _userManager.GetUserAsync(User); if (user == null) { throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); } // Set card info user.DefaultCardType = model.CreditCard.CardType; user.DefaultCardNumber = model.CreditCard.CardNumber; user.DefaultCardName = model.CreditCard.CardName; user.DefaultCVV = model.CreditCard.CVV; user.DefaultExpirationMonth = model.CreditCard.ExpirationMonth; user.DefaultExpirationYear = model.CreditCard.ExpirationYear; // Set billing address info user.BillingAddress1 = model.BillingAddress.Address1; user.BillingAddress2 = model.BillingAddress.Address2; user.BillingCity = model.BillingAddress.City; user.BillingState = model.BillingAddress.State; user.BillingZipCode = model.BillingAddress.ZipCode; user.BillingCountryRegion = model.BillingAddress.CountryRegion; var updateResult = await _userManager.UpdateAsync(user); #region Fraud Protection Service // If storing the user's payment information succeeds, update Fraud Protection. if (updateResult.Succeeded) { var billingAddress = new AddressDetails { FirstName = user.FirstName, LastName = user.LastName, Street1 = user.BillingAddress1, Street2 = user.BillingAddress2, City = user.BillingCity, State = user.BillingState, ZipCode = user.BillingZipCode, Country = user.BillingCountryRegion }; var userId = user.Email; var fraudProtectionUser = new User { UserId = userId, PaymentInstrumentList = new List <PaymentInstrument> { new PaymentInstrument { MerchantPaymentInstrumentId = $"{userId}-CreditCard", Type = PaymentInstrumentType.CreditCard.ToString(), CardType = model.CreditCard.CardType, HolderName = model.CreditCard.CardName, BIN = model.CreditCard.BIN, ExpirationDate = model.CreditCard.ExpirationDate, LastFourDigits = model.CreditCard.LastFourDigits, BillingAddress = billingAddress, CreationDate = DateTimeOffset.Now, State = PaymentInstrumentState.Active.ToString(), } }, DeviceContext = new DeviceContext { DeviceContextId = _contextAccessor.GetSessionId(), IPAddress = _contextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(), Provider = DeviceContextProvider.DFPFingerPrinting.ToString() } }; var correlationId = _fraudProtectionService.NewCorrelationId; var response = await _fraudProtectionService.PostUser(fraudProtectionUser, correlationId); var fraudProtectionIO = new FraudProtectionIOModel(correlationId, fraudProtectionUser, response, "UpdateAccount"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); } #endregion StatusMessage = "Your payment information has been updated"; return(RedirectToAction(nameof(ManagePaymentInstrument))); }
private async Task <IActionResult> RegisterUser(RegisterViewModel model, string returnUrl) { //Create the user object and validate it before calling Fraud Protection var user = new ApplicationUser { UserName = model.User.Email, Email = model.User.Email, FirstName = model.User.FirstName, LastName = model.User.LastName, PhoneNumber = model.User.Phone, Address1 = model.Address.Address1, Address2 = model.Address.Address2, City = model.Address.City, State = model.Address.State, ZipCode = model.Address.ZipCode, CountryRegion = model.Address.CountryRegion }; foreach (var v in _userManager.UserValidators) { var validationResult = await v.ValidateAsync(_userManager, user); if (!validationResult.Succeeded) { AddErrors(validationResult); } } ; foreach (var v in _userManager.PasswordValidators) { var validationResult = await v.ValidateAsync(_userManager, user, model.Password); if (!validationResult.Succeeded) { AddErrors(validationResult); } } ; if (ModelState.ErrorCount > 0) { return(View("Register", model)); } #region Fraud Protection Service var correlationId = _fraudProtectionService.NewCorrelationId; // Ask Fraud Protection to assess this signup/registration before registering the user in our database, etc. AccountProtection.SignUp signupEvent = CreateSignupAPEvent(model); var signupAssessment = await _fraudProtectionService.PostSignup(signupEvent, correlationId, HttpContext.Session.GetString("envId")); //Track Fraud Protection request/response for display only var fraudProtectionIO = new FraudProtectionIOModel(correlationId, signupEvent, signupAssessment, "Signup"); TempData.Put(FraudProtectionIOModel.TempDataKey, fraudProtectionIO); bool rejectSignup = false; if (signupAssessment is ResponseSuccess signupResponse) { rejectSignup = signupResponse.ResultDetails.FirstOrDefault()?.Decision != DecisionName.Approve; } if (rejectSignup) { ModelState.AddModelError("", "Signup rejected by Fraud Protection. You can try again as it has a random likelihood of happening in this sample site."); return(View("Register", model)); } #endregion var result = await _userManager.CreateAsync(user, model.Password); if (!result.Succeeded) { AddErrors(result); return(View("Register", model)); } await _signInManager.SignInAsync(user, isPersistent : false); await TransferBasketToEmailAsync(user.Email); return(RedirectToLocal(returnUrl)); }