Getting email from oauth authentication (Microsoft) - asp.net-mvc-4

How can I get the email from microsoft account? I'm doing the following:
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//...
string email = null;
if (result.Provider.ToLower() == "google")
{
email = result.ExtraData["email"];
}
else if (result.Provider.ToLower() == "facebook")
{
email = result.ExtraData["username"];
}
else if (result.Provider.ToLower() == "microsoft")
{
email = result.ExtraData["????"];
}
}
For google and facebook I'm able to get the email but I can't with microsoft? What kew should I use?

Solution:
public class MicrosoftScopedClient : IAuthenticationClient
{
private string clientId;
private string clientSecret;
private string scope;
private const string baseUrl = "https://login.live.com/oauth20_authorize.srf";
private const string tokenUrl = "https://login.live.com/oauth20_token.srf";
public MicrosoftScopedClient(string clientId, string clientSecret, string scope)
{
this.clientId = clientId;
this.clientSecret = clientSecret;
this.scope = scope;
}
public string ProviderName
{
get { return "Microsoft"; }
}
public void RequestAuthentication(HttpContextBase context, Uri returnUrl)
{
string url = baseUrl + "?client_id=" + clientId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + HttpUtility.UrlEncode(scope) + "&response_type=code";
context.Response.Redirect(url);
}
public AuthenticationResult VerifyAuthentication(HttpContextBase context)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.ToString();
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(code, rawUrl);
if (userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
string id = userData["id"];
string username = userData["email"];
userData.Remove("id");
userData.Remove("email");
AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
return result;
}
private IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
{
string token = QueryAccessToken(redirectURI, accessCode);
if (token == null || token == "")
{
return null;
}
var userData = GetUserData(token);
return userData;
}
private IDictionary<string, string> GetUserData(string accessToken)
{
ExtendedMicrosoftClientUserData graph;
var request =
WebRequest.Create(
"https://apis.live.net/v5.0/me?access_token=" + EscapeUriDataStringRfc3986(accessToken));
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(responseStream))
{
string data = sr.ReadToEnd();
graph = JsonConvert.DeserializeObject<ExtendedMicrosoftClientUserData>(data);
}
}
}
var userData = new Dictionary<string, string>();
userData.Add("id", graph.Id);
userData.Add("username", graph.Name);
userData.Add("name", graph.Name);
userData.Add("link", graph.Link == null ? null : graph.Link.AbsoluteUri);
userData.Add("gender", graph.Gender);
userData.Add("firstname", graph.FirstName);
userData.Add("lastname", graph.LastName);
userData.Add("email", graph.Emails.Preferred);
return userData;
}
private string QueryAccessToken(string returnUrl, string authorizationCode)
{
var entity =
CreateQueryString(
new Dictionary<string, string> {
{ "client_id", this.clientId },
{ "redirect_uri", returnUrl },
{ "client_secret", this.clientSecret},
{ "code", authorizationCode },
{ "grant_type", "authorization_code" },
});
WebRequest tokenRequest = WebRequest.Create(tokenUrl);
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.ContentLength = entity.Length;
tokenRequest.Method = "POST";
using (Stream requestStream = tokenRequest.GetRequestStream())
{
var writer = new StreamWriter(requestStream);
writer.Write(entity);
writer.Flush();
}
HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();
if (tokenResponse.StatusCode == HttpStatusCode.OK)
{
using (Stream responseStream = tokenResponse.GetResponseStream())
{
using (StreamReader sr = new StreamReader(responseStream))
{
string data = sr.ReadToEnd();
var tokenData = JsonConvert.DeserializeObject<OAuth2AccessTokenData>(data);
if (tokenData != null)
{
return tokenData.AccessToken;
}
}
}
}
return null;
}
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
private static string EscapeUriDataStringRfc3986(string value)
{
StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));
// Upgrade the escaping to RFC 3986, if necessary.
for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
{
escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
}
// Return the fully-RFC3986-escaped string.
return escaped.ToString();
}
private static string CreateQueryString(IEnumerable<KeyValuePair<string, string>> args)
{
if (!args.Any())
{
return string.Empty;
}
StringBuilder sb = new StringBuilder(args.Count() * 10);
foreach (var p in args)
{
sb.Append(EscapeUriDataStringRfc3986(p.Key));
sb.Append('=');
sb.Append(EscapeUriDataStringRfc3986(p.Value));
sb.Append('&');
}
sb.Length--; // remove trailing &
return sb.ToString();
}
protected class ExtendedMicrosoftClientUserData
{
public string FirstName { get; set; }
public string Gender { get; set; }
public string Id { get; set; }
public string LastName { get; set; }
public Uri Link { get; set; }
public string Name { get; set; }
public Emails Emails { get; set; }
}
protected class Emails
{
public string Preferred { get; set; }
public string Account { get; set; }
public string Personal { get; set; }
public string Business { get; set; }
}
}
AuthConfig.cs
public static class AuthConfig
{
public static void RegisterAuth()
{
Dictionary<string, object> MicrosoftsocialData = new Dictionary<string, object>();
MicrosoftsocialData.Add("Icon", "../Content/icons/microsoft.png");
OAuthWebSecurity.RegisterClient(new MicrosoftScopedClient("XXXXXXXX", "YYYYYYYYYYYYY",
"wl.basic wl.emails"), "Microsoft", MicrosoftsocialData);
//......
}
}
Usage:
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//...
string email = null;
if (result.Provider.ToLower() == "google")
{
email = result.ExtraData["email"];
}
else if (result.Provider.ToLower() == "facebook")
{
email = result.ExtraData["username"];
}
else if (result.Provider.ToLower() == "microsoft")
{
email = result.UserName;
}
}
Based on: How OAuthWebSecurity to obtain emails for different oauth clients, but Microsoft Client doesn’t return email, it didn’t include scope “wl.emails”

or even simpler: https://stackoverflow.com/a/22723713/1586498
var mo =
new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions
{
CallbackPath = new Microsoft.Owin.PathString("/Callbacks/External"),//register at oAuth provider
ClientId = "<<yourclientid>>",
ClientSecret = "<<yourclientsecret>>",
Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new Claim(providerKey, context.Identity.AuthenticationType));
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirstValue(ClaimTypes.Name)));
return System.Threading.Tasks.Task.FromResult(0);
}
}
};
mo.Scope.Add("wl.basic");
mo.Scope.Add("wl.emails"); //HERE IS THE GOLD
app.UseMicrosoftAccountAuthentication(mo);
and my way of grabbing them:
var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
externalIdentity.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.Email));

amp's answer really helped me out.
Also want to mention that you have to check the 'Live SDK support' checkbox when you register your application (https://apps.dev.microsoft.com/) - otherwise the OAuth service complains that you don't have a client secret (even if you do).
Just wanted to add how to do this without using the AuthConfig.cs stuff in case anyone is interested (a bit more manual, but it makes it easier to understand if you're not familiar with the framework):
public ActionResult LoginWithMicrosoftAccount(CancellationToken cancellationToken)
{
var client = new MicrosoftScopedClient(appID, appsecret, "wl.basic wl.emails");
var urlNoQueryString = Request.Url.GetLeftPart(UriPartial.Path);
AuthenticationResult result = null;
if(Request.QueryString["error"]!= null)
{//Microsoft service returns error
return View();
}
if (Request.QueryString["code"] != null)
{
result = client.VerifyAuthentication(this.HttpContext);
//at this point, you should get the username from result.UserName
}
if(Request.QueryString["code"]==null || result.UserName == null)
{//will do the redirection
client.RequestAuthentication(this.HttpContext, new Uri(urlNoQueryString));
}
return View();
}

Related

How to configure MailKit without password

In ASP.NET Core 6 Web API, I am using MailKit for Email configuration. I am using the SMTP server of my company which doesn't need a password.
I have this:
public class MailSettings
{
public string SmtpServer { get; set; }
public string SenderName { get; set; }
public string SenderEmail { get; set; }
public int SmtpPort { get; set; }
}
Since I am using my company SMTP configuration that needs no password, I use this method to send mails:
public async Task<bool> SendEmailAsync(MailRequest mailRequest)
{
var email = new MimeMessage { Sender = MailboxAddress.Parse(_mailSettings.SenderEmail) };
email.To.Add(MailboxAddress.Parse(mailRequest.ToEmail));
email.Subject = mailRequest.Subject;
var builder = new BodyBuilder();
if (mailRequest.Attachments != null)
{
foreach (var file in mailRequest.Attachments.Where(file => file.Length > 0))
{
byte[] fileBytes;
await using (var ms = new MemoryStream())
{
file.CopyTo(ms);
fileBytes = ms.ToArray();
}
builder.Attachments.Add((file.FileName + Guid.NewGuid().ToString()), fileBytes, ContentType.Parse(file.ContentType));
}
}
builder.HtmlBody = mailRequest.Body;
email.Body = builder.ToMessageBody();
try
{
using var smtp = new SmtpClient();
smtp.Connect(_mailSettings.SmtpServer, _mailSettings.SmtpPort, SecureSocketOptions.StartTls);
smtp.Authenticate(_mailSettings.SenderEmail);
await smtp.SendAsync(email);
smtp.Disconnect(true);
return true;
}
catch (Exception e)
{
_logger.Error(e, e.Source, e.InnerException, e.Message, e.ToString());
return false;
}
}
I got this error:
Argument 1: cannot convert from 'string' to 'MailKit.Security.SaslMechanism'
and it highlights this line of code:
smtp.Authenticate(_mailSettings.SenderEmail);
Expecting me to do it this way:
smtp.Authenticate(_mailSettings.SenderEmail, _mailSettings.Password);
How do I resolve this without password?
Thanks

Getting " InvalidOperationException: Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2"

I'm trying to implement refresh token in my api. The api is following CQRS+MediatR pattern and is using JWT for authentication.
After adding a Handler to manage refresh call and a refresh token service I am getting the following error:
Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler`2
[UM.Business.Application.Token.Command.Update.UpdateTokenCommand,UM.Business.Application.Common.HandlerResult]
Lifetime: Transient ImplementationType: UM.Business.Application.Token.Command.Update.UpdateTokenHandler':
Unable to resolve service for type 'UM.Infrastructure.Common.Configuration.AuthSettings'
while attempting to activate 'UM.Business.Application.Token.Command.Update.UpdateTokenHandler'.
Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'UM.Infrastructure.Common.Configuration.AuthSettings'
while attempting to activate 'UM.Business.Application.Token.Command.Update.UpdateTokenHandler'.
My service is doing basic crud operations on the Refresh Token class.
First of all I added dependancy injection in a service module class,
service.AddTransient<IRefreshTokenService, RefreshTokenService>();
This is my controller end point,
[Route("refresh")]
[ProducesResponseType(typeof(LoginResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(IEnumerable<string>), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(string), StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(ConfirmEmail), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(AccountBlocked), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Refresh([FromBody] UpdateRefreshTokenVM request, CancellationToken ct)
{
var refreshCommand= _mapper.Map<UpdateTokenCommand>(request);
var authenticationResult = await _mediator.Send(refreshCommand, ct);
if (authenticationResult == null)
return Unauthorized();
if (authenticationResult.IsSuccess)
return Ok(authenticationResult.Result);
}
My UpdateRefreshTokenVM,
public class UpdateRefreshTokenVM
{
public string AccessToken { get; set; }
public Core.Domain.Models.RefreshToken RefreshToken { get; set; } }
And UpdateTokenCommand,
public class UpdateTokenCommand : CommandBase<HandlerResult>
{
public string AccessToken { get; set; }
public Core.Domain.Models.RefreshToken RefreshToken { get; set; }
}
Handler getting called is this one,
public UpdateTokenHandler(IUserService userService, IMapper mapper, IRefreshTokenService refreshTokenService, IRoleService roleService, AuthSettings authSetting, IUserClaimsService userClaimService)
{
_userService = userService;
_refreshTokenService = refreshTokenService;
_userClaimService = userClaimService;
_roleService = roleService;
_authSetting = authSetting;
_mapper = mapper;
}
public async Task<HandlerResult> Handle(UpdateTokenCommand request, CancellationToken cancellationToken)
{
var loginResponse = new HandlerResult();
var userIdentity = await _userService.FindByEmailAsync(request.RefreshToken.User.Email);
string accessToken = request.AccessToken;
var refreshToken = request.RefreshToken;
var principal = TokenGenerator.GetPrincipalFromExpiredToken(accessToken);
var username = principal.Identity.Name; //this is mapped to the Name claim by default
var refreshTokenFromDb =await _refreshTokenService.FindByUserId(refreshToken.User.Id);
if (refreshTokenFromDb.RefreshTokenExpiryTime <= DateTime.Now)
{
return null;
}
if (refreshTokenFromDb == null || refreshTokenFromDb != refreshToken )
{
loginResponse.IsSuccess = false;
loginResponse.ErrorMessage="Invalid client request";
loginResponse.Result = null;
return loginResponse;
}
var userClaims = await _userClaimService.GetClaims(userIdentity);
var userRoles = await _roleService.GetUserRoles(userIdentity);
var resultObject = TokenGenerator.GenerateJsonWebToken(userIdentity, userClaims, _authSetting, userRoles);
//var newRefreshToken = TokenGenerator.GenerateRefreshToken();
loginResponse.Result = resultObject;
loginResponse.ErrorMessage = null;
loginResponse.IsSuccess = true;
//user.RefreshToken = newRefreshToken;
//userContext.SaveChanges();
// now save the token variable in db
await _refreshTokenService.UpdateAsync(resultObject.RefreshToken);
return loginResponse;
}
}
Following is the token generator used in the handler,
internal static LoginResponse GenerateJsonWebToken(UM.Core.Domain.Models.User userInfo,IList<System.Security.Claims.Claim> userClaims, AuthSettings authSetting, IList<string> roleNames)
{
userClaims.Add(new System.Security.Claims.Claim(ClaimTypes.Email, userInfo.Email));
userClaims.Add(new System.Security.Claims.Claim(ClaimTypes.NameIdentifier, userInfo.Id));
userClaims.Add(new System.Security.Claims.Claim(ClaimTypes.Name, userInfo.NormalizedUserName));
foreach(string name in roleNames)
{
userClaims.Add(new System.Security.Claims.Claim(ClaimTypes.Role, name));
}
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(authSetting.Key));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha512Signature);
var tokenDescriptor = new SecurityTokenDescriptor()
{
Issuer = authSetting.Issuer,
Audience = authSetting.Audience,
Subject = new ClaimsIdentity(userClaims),
Expires = DateTime.UtcNow.AddMinutes(authSetting.ExpirationTimeInMin),
SigningCredentials = credentials
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
// Now generate refresh token
var refreshToken = new RefreshToken
{
User = userInfo,
//UserId = int.Parse(userInfo.Id),
RefreshTokenString = GenerateRefreshToken(),
RefreshTokenExpiryTime = DateTime.Now.AddDays(14) // will change this later
};
var authenticationResponse = new LoginResponse
{
AccessToken = tokenHandler.WriteToken(token),
RefreshToken = refreshToken,
IsValid = true
};
return authenticationResponse;
}
public static string GenerateRefreshToken()
{
var randomNumber = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomNumber);
return Convert.ToBase64String(randomNumber);
}
}
public static ClaimsPrincipal GetPrincipalFromExpiredToken(string token)
{
var tokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false, //you might want to validate the audience and issuer depending on your use case
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superSecretKey#345")),
ValidateLifetime = false //here we are saying that we don't care about the token's expiration date
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken securityToken;
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out securityToken);
var jwtSecurityToken = securityToken as JwtSecurityToken;
if (jwtSecurityToken == null || !jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase))
throw new SecurityTokenException("Invalid token");
return principal;
}
Auth settings(which I didn't touch for this implementation)
public class AuthSettings
{
public string Key { get; set; }
public string Issuer { get; set; }
public string Audience { get; set; }
public int ExpirationTimeInMin { get; set; }
}
And my login Response class,
public class LoginResponse
{
public string AccessToken { get; set; }
public RefreshToken RefreshToken { get; set; }
public bool IsValid { get; set; }
public bool VerificationRequired { get; set; }
public bool TwoFactorRequired { get; set; }
public LoginResponse()
{
RefreshToken = new RefreshToken();
}
}

How to keep user logged in after browser is closed

Every time I close the browser I need to log in again into this app. It is developed in .NET Core 2.0. I'm trying to let it logged in, like every other regular site.
I checked this post that may be useful, but since the code is quite different from this application I decided to create this post.
This is my security code:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
namespace Petito.Common
{
public interface IActivityContext
{
string ActivityID { get; }
IPrincipal User { get; }
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityIdentity : IIdentity
{
private string _name = null;
[JsonIgnore()]
private bool _isAuthenticated = false;
[JsonIgnore()]
private string _authenticationType = "";
public ActivityIdentity()
{
}
public ActivityIdentity(string name) : this(name, false, "")
{
}
internal ActivityIdentity(string name, bool isAuthenticated, string authenticationType)
{
this._name = name;
this._isAuthenticated = isAuthenticated;
this._authenticationType = authenticationType;
}
public string Name { get => _name; }
public bool IsAuthenticated { get => _isAuthenticated; }
public string AuthenticationType { get => _authenticationType; }
public static ActivityIdentity Unathenticated => new ActivityIdentity();
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityPrincipal : IPrincipal
{
private ActivityIdentity _activityIdentity = null;
private string[] _roles = null;
public ActivityPrincipal() : this(ActivityIdentity.Unathenticated, null)
{
}
public ActivityPrincipal(ActivityIdentity activityIdentity, params string[] roles)
{
_activityIdentity = activityIdentity;
_roles = roles;
}
public ActivityIdentity Identity => _activityIdentity;
IIdentity IPrincipal.Identity => _activityIdentity;
public bool IsInRole(string role)
{
if (_roles != null && _roles.Length > 0)
{
return _roles.Contains(role);
}
return false;
}
}
[JsonObject(MemberSerialization = MemberSerialization.Fields)]
public class ActivityContext : IDisposable, IActivityContext
{
private string _activityID = Guid.NewGuid().ToString();
private DateTime _startDate = DateTime.UtcNow;
private DateTime? _endDate = null;
private ActivityPrincipal _activityPrincipal = null;
public ActivityContext() : this(null)
{
}
public ActivityContext(IPrincipal principal)
{
_activityPrincipal = Convert(principal);
}
private ActivityPrincipal Convert(IPrincipal principal)
{
if (principal == null)
{
return new ActivityPrincipal();
}
var activityPrincipal = principal as ActivityPrincipal;
if (activityPrincipal != null)
{
return activityPrincipal;
}
var claimsPrincipal = principal as ClaimsPrincipal;
if (claimsPrincipal != null)
{
var roles = claimsPrincipal.Claims.Select(x => x.Value);
var p = new ActivityPrincipal(
new ActivityIdentity(claimsPrincipal.Identity.Name, claimsPrincipal.Identity.IsAuthenticated, claimsPrincipal.Identity.AuthenticationType)
, roles.ToArray()
);
return p;
}
throw new NotSupportedException($"Converting {principal.GetType()} not supported");
}
public void Dispose()
{
if (!_endDate.HasValue)
{
_endDate = DateTime.UtcNow;
}
}
public string ActivityID { get => _activityID; }
public DateTime StartDate { get => _startDate; }
public DateTime? EndDate { get => _endDate; }
public IPrincipal User
{
get
{
return _activityPrincipal;
}
}
}
}
Of course, I'll still try to figure out what's wrong with the code. Please if you need another part of the code other from that I posted let me know.
Thanks!

ASP.NET Core Paypal Implementation

I try to implement a PayPal cart payment in ASP.NET Core. I have a working example in ASP.NET MVC 5 and I try to convert it to ASP.NET Core but I had no success. The point that I can not resolve is how to get the values that I have to get the transactionID, amount paid and Order ID. In ASP.NET MVC 5 the IPN action is as follows:
public ActionResult IPN()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var formVals = new Dictionary<string, string>();
formVals.Add("cmd", "_notify-validate");
string response = GetPayPalResponse(formVals, true);
if (response == "VERIFIED")
{
string transactionID = Request["txn_id"];
string sAmountPaid = Request["mc_gross"];
string orderID = Request["custom"];
:
:
In my ASP.NET Core application the IPN action is executed by PayPal and I have a VERIFIED response but I can not get the next three values. I have tried various ways to get these values without success.
My initial approach was the following:
string transactionID = Request.Query["txn_id"];
string sAmountPaid = Request.Query["mc_gross"];
string orderID = Request.Query["custom"];
Can someone suggest me a way to get these values?
I found a solution to my problem and I will post it just in case someone wants to do something similar.
[Route("PayPal/IPN")]
[HttpPost]
public ActionResult IPN()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
PayPalRespond response = GetPayPalResponse();
if (response.RespondType == RespondTypeEnum.Verified)
{
System.IO.File.AppendAllText(_env.WebRootPath + Path.DirectorySeparatorChar.ToString() + "data.txt", $"{DateTime.Now.ToString()} {response.JsonData}." + Environment.NewLine);
Order order = GetOrder(154);
//check the amount paid
if (order.Total <= response.AmountPaid)
{
// IPN Order successfully transacted. Save changes to database
return Ok();
}
else
{
// Amount Paid is incorrect
}
}
else
{
// Not verified
}
return Content("");
}
PayPalRespond GetPayPalResponse()
{
PayPalRespond output = new PayPalRespond();
var formVals = new Dictionary<string, string>();
formVals.Add("cmd", "_notify-validate");
string paypalUrl = UseSandbox ? "https://www.sandbox.paypal.com/cgi-bin/webscr" : "https://www.paypal.com/cgi-bin/webscr";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(paypalUrl);
// Set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] param;
using (var ms = new MemoryStream(2048))
{
Request.Body.CopyTo(ms);
param = ms.ToArray();
}
string strRequest = Encoding.ASCII.GetString(param);
var QueryValues = System.Web.HttpUtility.ParseQueryString(strRequest);
output.Data = new List<QueryValue>();
foreach (var item in QueryValues.AllKeys)
{
if (item.Equals("txn_id"))
output.TransactionID = QueryValues[item];
else if (item.Equals("mc_gross"))
{
CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
NumberStyles style = NumberStyles.Number;
Decimal amountPaid = 0;
Decimal.TryParse(QueryValues[item], style, culture, out amountPaid);
output.AmountPaid = amountPaid;
}
else if (item.Equals("custom"))
output.OrderID = QueryValues[item];
output.Data.Add(new QueryValue { Name = item, Value = QueryValues[item] });
}
output.JsonData = Newtonsoft.Json.JsonConvert.SerializeObject(output.Data);
StringBuilder sb = new StringBuilder();
sb.Append(strRequest);
foreach (string key in formVals.Keys)
{
sb.AppendFormat("&{0}={1}", key, formVals[key]);
}
strRequest += sb.ToString();
req.ContentLength = strRequest.Length;
//Send the request to PayPal and get the response
string response = "";
using (StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII))
{
streamOut.Write(strRequest);
streamOut.Close();
using (StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream()))
{
response = streamIn.ReadToEnd();
}
}
output.RespondType = response.Equals("VERIFIED") ? RespondTypeEnum.Verified : RespondTypeEnum.Invalid;
return output;
}
The enumerator and the classes that you will need are the following:
public enum RespondTypeEnum { Verified, Invalid }
public class PayPalRespond
{
public RespondTypeEnum RespondType { get; set; }
public List<QueryValue> Data { get; set; }
public string JsonData { get; set; }
public string TransactionID { get; set; }
public string OrderID { get; set; }
public Decimal AmountPaid { get; set; }
}
public class QueryValue
{
public string Name { get; set; }
public string Value { get; set; }
}

RavenDB lazy search against Index returns uninitialized statistiscs

I am trying to run lazy queries against raven db and get the counts on total matching results. I am finding when I query against a static index, a lazy search does not initialize the statistics when the query is materialized, but otherwise it comes back all right.
Below is the test to prove this behaviour.
[TestFixture]
public class CanSearchLazily
{
private const int ServerPort = 8085;
private readonly string _serverAddress = #"http://localhost:{0}".For(ServerPort);
[Test]
public void CanGetTotalResultsFromStatisticsOnLazySearchAgainstDynamicIndex()
{
CanGetTotalResultsFromStatisticsOnLazySearchAgainstAnIndex();
}
[Test]
public void CanGetTotalResultsFromStatisticsOnLazySearchAgainstStaticIndex()
{
CanGetTotalResultsFromStatisticsOnLazySearchAgainstAnIndex("UserByFirstName");
}
private void CanGetTotalResultsFromStatisticsOnLazySearchAgainstAnIndex(string indexName = "")
{
BuilderSetup.DisablePropertyNamingFor<User, string>(x => x.Id);
var users = Builder<User>.CreateListOfSize(2000).All()
.With(x => x.FirstName = GetRandom.FirstName())
.With(x => x.LastName = GetRandom.LastName())
.Build();
using (GetNewServer())
using (var store = new DocumentStore { Url = _serverAddress }.Initialize())
{
using (var session = store.OpenSession())
{
users.ForEach(session.Store);
session.SaveChanges();
IndexCreation.CreateIndexes(typeof(UserByFirstName).Assembly, store);
session.Query<User, UserByFirstName>().Customize(x => x.WaitForNonStaleResults()).ToList();
}
using (var session = store.OpenSession())
{
var names = session.Query<User>().Select(u => u.FirstName).Distinct().Take(15).ToList();
RavenQueryStatistics stats;
var query = string.IsNullOrEmpty(indexName)
? session.Query<User>().Statistics(out stats).Where(x => x.FirstName.In(names))
: session.Query<User>(indexName).Statistics(out stats).Where(x => x.FirstName.In(names));
var results = query.Take(8).Lazily();
Assert.AreEqual(8, results.Value.ToList().Count);
Assert.AreEqual(DateTime.Now.Year, stats.IndexTimestamp.Year, "the index should have the current year on its timestamp");
Assert.IsTrue(stats.TotalResults > 0, "The stats should return total results");
}
}
}
protected RavenDbServer GetNewServer(bool initializeDocumentsByEntitiyName = true)
{
var ravenConfiguration = new RavenConfiguration
{
Port = ServerPort,
RunInMemory = true,
DataDirectory = "Data",
AnonymousUserAccessMode = AnonymousUserAccessMode.All
};
if (ravenConfiguration.RunInMemory == false)
IOExtensions.DeleteDirectory(ravenConfiguration.DataDirectory);
var ravenDbServer = new RavenDbServer(ravenConfiguration);
if (initializeDocumentsByEntitiyName)
{
using (var documentStore = new DocumentStore
{
Url = _serverAddress
}.Initialize())
{
new RavenDocumentsByEntityName().Execute(documentStore);
}
}
return ravenDbServer;
}
}
[Serializable]
public class User
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserByFirstName : AbstractIndexCreationTask<User>
{
public UserByFirstName()
{
Map = users => from user in users
select new {user.FirstName};
}
}