PayPal IPN https changes 2016 - paypal

Received information today from PayPal:
IPN Verification Postback to HTTPS
If you are using PayPal’s Instant Payment Notification (IPN) service, you will >need to ensure that HTTPS is used when posting the message back to PayPal for >verification. After Sept 30, 2016 HTTP postbacks will no longer be supported.
I am using IPN and the live site is working but our DEV IPN listener which is using the sandbox at: https://www.sandbox.paypal.com/cgi-bin/webscr is broken.
I am confused about what I need to do to fix it. I added this code and the listener page loads without error again.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
But when I try to test a transaction the listener never receives anything from PayPal. Is this because the server of the listener now has to be "https"?
Does PP sandbox now refuse to notify a non SSL address?
I got my c# code originally from a PayPal example but it is no longer on their site.
var useSandbox = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePayPalSandboxYn"]);
var server = useSandbox ? "https://www.sandbox.paypal.com/cgi-bin/webscr" : "https://www.paypal.com/cgi-bin/webscr";
var req = (HttpWebRequest)WebRequest.Create(server);
// set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
//added today
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
byte[] param = Request.BinaryRead(HttpContext.Current.Request.ContentLength);
var strRequest = Encoding.ASCII.GetString(param);
strRequest += "&cmd=_notify-validate";
req.ContentLength = strRequest.Length;
// send the request to PayPal and get the response
var streamOut = new StreamWriter(req.GetRequestStream(), Encoding.ASCII);
streamOut.Write(strRequest);
streamOut.Close();
var streamIn = new StreamReader(req.GetResponse().GetResponseStream());
string strResponse = streamIn.ReadToEnd();
streamIn.Close();
switch (strResponse)
{
case "VERIFIED":
{
I do my debugging with a static IP address and a home router set up as a web server. It's going to be even harder if I have to set up ssl.
Can anyone point me in the right direction?

The only thing you need to do is make sure you're sending your verification POST back to PayPal to https:// instead of http://. You don't have to have an SSL installed on your site for your IPN listener to run on.

I just want to share my code that is working... hope that it can help you to make a little improvements on your code:
private void VerifyTask(HttpRequestBase ipnRequest, bool useLiveAccount = true)
{
string verificationResponse = string.Empty;
var request = (HttpWebRequest)WebRequest.Create(useLiveAccount
? WebConfigurationManager.AppSettings["PaypalURL"]
: WebConfigurationManager.AppSettings["SandboxURL"]);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
var param = ipnRequest.BinaryRead(ipnRequest.ContentLength);
var strRequest = Encoding.ASCII.GetString(param);
strRequest += "&cmd=_notify-validate";
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
using (var writer = new StreamWriter(request.GetRequestStream(), Encoding.ASCII))
{
writer.Write(strRequest);
writer.Close();
}
using (var reader = new StreamReader(request.GetResponse().GetResponseStream()))
{
verificationResponse = reader.ReadToEnd();
reader.Close();
}
if (verificationResponse.Equals("VERIFIED"))
{
//Make the validations here
}
}
Edit:
WebConfigurationManager.AppSettings["PaypalURL"] = "https://www.paypal.com/cgi-bin/webscr"
WebConfigurationManager.AppSettings["SandboxURL"] = "https://www.sandbox.paypal.com/cgi-bin/webscr"

Related

Identity server stuck in redirect loop

I'm using identityserver4 for single sign-on. For most of the time application function smoothly but intermittently we face a redirect loop issue which becomes a show stopper for us until we restart's our app service. The page goes on loading continuously before finally showing a 'Bad request - Request Too Long' page with message: HTTP Error 400. The size of the request headers is too long. If we check the network tab, we can see that the application is looping between the identity server and client application redirect sign in pages. The application insight tells us that the client app gives a 401 on his home/index page and then a 302 on the signin-oidc url, then goes to the identity server connect/token, then connect/userinfo endpoints to get claims and comes back to the client home/index page to again get a 401. The loop continues (Identity server says user is authenticated while client says it is not). We are unable to find a fix for this since long. Any help is appreciated. Attaching the client side configuration for reference.
Findings
Our client app is an mvc app & we have used Session's & TempData in few area's. This areas are the triggering point of the redirect issue. What we have observed is, when the client initially login the authentication cookie is created (Cookie Name: AudDiscoveryAuth) and I could see it being passed in header for each request made to the controller actions. But once the user visit's any such area where we have used Session/TempData and Log out or any other user tries to login, Identity server successfully authenticates the user also the userendpoint to retrieve the details is being invoked however the cookie itself is not being created and is missing in every request to the Index/Home action method hence the redirect loop. Wondering what could be hampering in issuing cookie when using session variable elsewhere in the application or is their a setting missing.
Also in every redirect the occurrence of OpenIdConnect.nonce.XXX cookie is incremented. Once the count of OpenIdConnect.nonce.XXX reaches more then a certain level we get the bad request error page
public void Configuration(IAppBuilder app)
{
string baseClientAddress = ConfigurationManager.AppSettings["ApplicationUrl"];
int slidingExpiryHrs = Convert.ToInt32(ConfigurationManager.AppSettings["SlidingExpiryHrs"]);
int slidingExpiryMins = Convert.ToInt32(ConfigurationManager.AppSettings["SlidingExpiryMins"]);
TimeSpan expireTimeSpan = new TimeSpan(slidingExpiryHrs, slidingExpiryMins, 0);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationType,
CookieName = "AudDiscoveryAuth",
ExpireTimeSpan = expireTimeSpan,
SlidingExpiration = true
});
app.UseOpenIdConnectAuthenticationPatched(new OpenIdConnectAuthenticationOptions
{
ClientId = "ratingspro.web",
Authority = IdsvrConstants.BaseAddress,
RedirectUri = baseClientAddress + "signin-oidc/",
PostLogoutRedirectUri = baseClientAddress + "signout-callback-oidc/",
ResponseType = "code id_token",
Scope = "openid api1 ratingspro.webapi offline_access",
UseTokenLifetime = false,
SignInAsAuthenticationType = DefaultAuthenticationType,
RequireHttpsMetadata = true,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var client = HttpClientFactory.Create();
var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
{
Address = IdsvrConstants.TokenEndpoint,
ClientId = "ratingspro.web",
ClientSecret = "secret",
Code = n.Code,
RedirectUri = n.RedirectUri,
});
if (tokenResponse.IsError)
{
LogHelper.LogMessage("RatingsproApp: Startup => tokenResponseError: " + tokenResponse.Error);
throw new AuthenticationException(tokenResponse.Error);
}
var userInfoResponse = await client.GetUserInfoAsync(new UserInfoRequest
{
Address = IdsvrConstants.UserInfoEndpoint,
Token = tokenResponse.AccessToken
});
if (userInfoResponse.IsError)
{
throw new AuthenticationException(userInfoResponse.Error);
}
var claims = userInfoResponse.Claims;
if (claims.Any(c => c.Type == "ApplicationAccessDenied"))
{
throw new AuthenticationException(claims.FirstOrDefault(c => c.Type == "ApplicationAccessDenied").Value);
}
// create new identity
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(claims);
id.AddClaim(new Claim("AccessToken", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString(CultureInfo.InvariantCulture)));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
client.Dispose();
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
return Task.FromResult(0);
},
AuthenticationFailed = x =>
{
x.ProtocolMessage.RedirectUri = "/";
return Task.CompletedTask;
}
}
});
}
}

Dynamics 365 Plugin call External REST API - SSL/TSL could not create channel issue

I have a task to call the external REST API to get data from third party application.
In that, i have created a C# Console application for tried this and it is working fine and i can get the data from thirty party application via REST API.
The same code used to tried in Dynamics Custom workflow\Plugin, i have got a error below. Please give your valuable suggestion on this.
"System.Net.WebException: The request was aborted: Could not create SSL/TLS secure channel."
Note: The below options are tried but no luck.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(AlwaysGoodCertificate);
ServicePointManager.Expect100Continue = true;
Thanks,
Vasanth
It is not clear how you are calling external API from D365 Plugin.
// Call external API
static async Task<bool> CallExternalAPI(Guid beziehungId)
{
bool status = false;
HttpClient apiClient = new HttpClient();
apiClient.DefaultRequestHeaders.Accept.Clear();
apiClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string url = #"https://reqres.in/api/users?page=2";
HttpResponseMessage response = await apiClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}
return status;
}
If your problem with SSL/TLS secure channel you can use WebClient.
// Could not create SSL/TLS secure channel.
// Use (SecurityProtocolType)3072
using (WebClient client = new WebClient())
{
client.Headers.Add(HttpRequestHeader.UserAgent, "AvoidError");
ServicePointManager.Expect100Continue = true;
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
htmlCode = client.DownloadString("MY LINK");
//passing the URL again to function from which to extract the content and compare from above content.
strNewCompanyCode = client.DownloadString("MY LINK");
}

Facebook API: error 400 when posting with test user, but works with administrator

I have facebook integrated on my login page, and now I'm trying to call a build-in action: books.rates using a Test App.
My app gets a valid access_token, and creates new feed items without problems. But when I'm tryng to make a books.rates API call, only works if the logged user is a real person (in my tests is me, also the App administrator), and fails allways with Error 400 when I try to rate a book with a Test User.
In both cases, the code is the same (only access_token and userid changes) and has "publish_actions" premission enabled. I think I'm missing something on Test App configuration, but I'm really lost right now.
Thanks!
Update 1
This is the code that makes the action. It's a test code so its very basic
Dictionary<string, string> postInfo = new Dictionary<string, string>();
postInfo["book"] = "http://www.whakoom.com/comics/6jMl7/52/4";
postInfo["rating:value"] = "4";
postInfo["rating:scale"] = "5";
postInfo["fb:explicitly_shared"] = "true";
string graphUrl = string.Format("https://graph.facebook.com/v2.1/{0}/books.rates?access_token={1}", FbUserID, FbAccessToken);
string fbResp = PostPageContent(graphUrl, postInfo);
private static string PostPageContent(string url, Dictionary<string,string> postData)
{
string postInfo = string.Empty;
foreach(string key in postData.Keys)
{
if (postInfo.Length > 0)
postInfo += "&";
postInfo += string.Format("{0}={1}", HttpContext.Current.Server.UrlEncode(key), HttpContext.Current.Server.UrlEncode(postData[key]));
}
var request = WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postInfo.Length;
StreamWriter streamOut = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);
streamOut.Write(postInfo);
streamOut.Close();
string retValue = string.Empty;
WebResponse response = request.GetResponse();
var reader = new StreamReader(response.GetResponseStream());
retValue = reader.ReadToEnd();
reader.Close();
return retValue;
It seems that Test App with Test Users is unable to make books.rates calls until the main App is approved. The same api calls targeting a real App with a Test User works without problems.

Payflow gateway and Payment Advanced

I want to test PayPal Payment Advanced on my site an use this:https://ppmts.custhelp.com/app/answers/detail/a_id/929/. Now I finished intergrating it with my site. But now I can pay with PayPal Account, I failed to get payment with a Credit Card, I can't get any wrong response from PayPal when I use this API. This is my Code:
public NameValueCollection PayAdvanced(VM_BasketOrder basketOrder)
{
string baseUrl = Request.Url.Scheme + "://" + Request.Url.Authority;
string ReturnURL = baseUrl + Url.Action("ReturnPayAdvanced", "ShoppingCart");
string CancelURL = baseUrl + Url.Action("CancelPay", "ShoppingCart");
StoredAddress billAddress = basketOrder.billto_sAddress;
StoredAddress shipAddress = basketOrder.shipto_sAddress;
NameValueCollection requestArray = new NameValueCollection()
{
{"PARTNER", "PayPal"},
{"VENDOR", "s04124226"},
{"USER", "g041234226"},
{"PWD", "k_123452346789"},
{"CREATESECURETOKEN", "Y"},
{"SECURETOKENID", genId()},
{"TRXTYPE", "S"},//A
{"SHIPAMOUNT", basketOrder.ShippingAmount.ToString()},
{"AMT", basketOrder.Total.ToString()},
{"TAX", basketOrder.SalesTax.ToString()},
// {"ACCT","4111111111111111"},
// {"EXPDATE","1215"},
{"CURRENCY", "USD"},
//{"SILENTTRAN","TRUE"},
{"RETURNURL", ReturnURL},
{"CANCELURL", CancelURL},
{"ERRORURL", CancelURL},
{"BILLTOFIRSTNAME",billAddress.Name.AddressNameSplit()[0]},
{"BILLTOLASTNAME", billAddress.Name.AddressNameSplit()[1]},
{"BILLTOSTREET", billAddress.Street1},
{"BILLTOCITY",billAddress.City},
{"BILLTOSTATE",billAddress.State},
{"BILLTOZIP",billAddress.Zip},
{"BILLTOCOUNTRY",billAddress.Country},
{"BILLTOPHONE","55555"},
{"SHIPTOFIRSTNAME", shipAddress.Name.AddressNameSplit()[0]},
{"SHIPTOLASTNAME", shipAddress.Name.AddressNameSplit()[1]},
{"SHIPTOSTREET", shipAddress.Street1},
{"SHIPTOCITY", shipAddress.City},
{"SHIPTOSTATE", shipAddress.State},
{"SHIPTOZIP",shipAddress.Zip},
{"SHIPTOCOUNTRY",shipAddress.Country},
{"SHIPTOPHONE","55555"},
};
NameValueCollection resp = run_payflow_call(requestArray);
return resp;
}
protected NameValueCollection run_payflow_call(NameValueCollection requestArray)
{
#region send request to Payflow
String nvpstring = "";
foreach (string key in requestArray)
{
//format: "PARAMETERNAME[lengthofvalue]=VALUE&". Never URL encode.
var val = requestArray[key];
nvpstring += key + "[ " + val.Length + "]=" + val + "&";
}
string urlEndpoint = "https://pilot-payflowpro.paypal.com/";
HttpWebRequest payReq = (HttpWebRequest)WebRequest.Create(urlEndpoint);
payReq.Method = "POST";
payReq.ContentLength = nvpstring.Length;
payReq.ContentType = "application/x-www-form-urlencoded";
StreamWriter sw = new StreamWriter(payReq.GetRequestStream());
sw.Write(nvpstring);
sw.Close();
#endregion
#region get Payflow response
HttpWebResponse payResp = (HttpWebResponse)payReq.GetResponse();
StreamReader sr = new StreamReader(payResp.GetResponseStream());
string response = sr.ReadToEnd();
sr.Close();
#endregion
#region parse string into array and return
NameValueCollection dict = new NameValueCollection();
foreach (string nvp in response.Split('&'))
{
string[] keys = nvp.Split('=');
dict.Add(keys[0], keys[1]);
}
return dict;
#endregion
}
protected string genId()
{
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var random = new Random();
var result = new string(
Enumerable.Repeat(chars, 16)
.Select(s => s[random.Next(s.Length)])
.ToArray());
return "MySecTokenID-" + result; //add a prefix to avoid confusion with the "SECURETOKEN"
}
I use this code, and I get passed a tokenid and token to next code.
<iframe src="#("https://payflowlink.paypal.com?MODE=TEST&SECURETOKENID="+ViewBag.SECURETOKENID+"&SECURETOKEN="+ViewBag.SECURETOKEN)" name="test_iframe" scrolling="no" style=" width:480px;height:490px;border:0px;"></iframe>
I get the PayPal iframe on my site, click the PayPal button, I can pay successfully, but when I enter a credit card number and click "pay now", I can't get money in my test account. Now I don't know how to debug my code, because it is executiin in PayPal, so I can't find where the mistake is in my code. So I pasted my code here, and hope some can help me.
today I find, when I use credit card pay, PNREF=V72A4DR7AE07, I can't get PPREF. when I use PayPal Account to pay, I can get PNREF and PPREF all. And PPREF is the PayPal transactionId.

Set Express Checkout method returning an failure in the ack

I want to do the express checkout process in the paypal for the customers without redirecting the browser. I have written the code like
string sAPIUser = apiuser;
string sAPIPassword = password;
string sAPISignature = "signature";
string sAPIEndpoint = "https://api-3t.sandbox.paypal.com/nvp";
string sAppID = "APP-80W284485P519543T";
StringBuilder sRequest = new StringBuilder();
ASCIIEncoding encoding = new ASCIIEncoding();
string postData = ("&METHOD=SetExpressCheckout");
postData += ("&VERSION=63.0");
postData += ("&PAYMENTREQUEST_0_AMT=10.00");
postData += ("&PAYMENTREQUEST_0_CURRENCYCODE=USD");
postData += ("&PAYMENTREQUEST_0_PAYMENTACTION=Sale");
postData += ("CANCELURL=http://www.google.com");
postData += ("RETURNURL=http://www.google.com");
byte[] data = encoding.GetBytes(postData);
// Prepare web request...
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(sAPIEndpoint);
myRequest.Method = "POST";
myRequest.Headers.Add("X-PAYPAL-SECURITY-USERID", sAPIUser);
myRequest.Headers.Add("X-PAYPAL-SECURITY-PASSWORD", sAPIPassword);
myRequest.Headers.Add("X-PAYPAL-SECURITY-SIGNATURE", sAPISignature);
myRequest.Headers.Add("X-PAYPAL-SERVICE-VERSION", "1.3.0");
myRequest.Headers.Add("X-PAYPAL-REQUEST-DATA-FORMAT", "NV");
myRequest.Headers.Add("X-PAYPAL-RESPONSE-DATA-FORMAT", "NV");
myRequest.Headers.Add("X-PAYPAL-APPLICATION-ID", sAppID);
myRequest.ContentType = "application/x-www-form-urlencoded";
myRequest.ContentLength = data.Length;
// Send the request, read the response
Stream newStream = myRequest.GetRequestStream();
newStream.Write(data, 0, data.Length);
newStream.Close();
HttpWebResponse response = (HttpWebResponse)myRequest.GetResponse();
Stream responseStream = response.GetResponseStream();
Encoding encoding2 = Encoding.GetEncoding("utf-8");
StreamReader reader = new StreamReader(responseStream, encoding2);
string theResponse = reader.ReadToEnd();
theResponse = HttpUtility.HtmlDecode(theResponse);
But i am getting the failure message in the theResponse variable. What error i am doing. The error is like
TIMESTAMP=2013%2d03%2d05T05%3a55%3a38Z&CORRELATIONID=5c10035aca937&ACK=Failure&VERSION=63%2e0&BUILD=5331358&L_ERRORCODE0=10002&L_SHORTMESSAGE0=Authentication%2fAuthorization%20Failed&L_LONGMESSAGE0=You%20do%20not%20have%20permissions%20to%20make%20this%20API%20call&L_SEVERITYCODE0=Error
How can i rectify this.
Make sure that the credentials you are using are for your sandbox account and not your live account. Your code looks like it is pointing to the sandbox, so you would need to use your sandbox credentials. Also, if this is not all of your code, make sure you are not passing across a variable called "SUBJECT" and populating it with an email address.