I have applications registered in Azure AD Tenant and these applications have clientid and secret.
I have a requirement to get the azure application credentials expiry date. I am using azure sdk for java in my application.
How can we get the client secret expiry date using java ?
I have googled for it but didn't find any useful links. Can anyone help me on this.
If you want to get secret expiry date in your Java application, you can call the Microsoft Graph API to get the application. Then the application's property passwordCredentials has the information. For example
Register a new application using the Azure portal
Sign in to the Azure portal using either a work or school account or
a personal Microsoft account.
If your account gives you access to more than one tenant, select your account in the top right corner, and set your portal session to the Azure AD tenant that you want.
In the left-hand navigation pane, select the Azure Active Directory service, and then select App registrations > New registration.
Configure Microsoft Graph permissions you need for your application
Code
//install ADAL4J get accesss token
String clientId = "your application id";
String appKey = "your client secret";
String tenantId = "your tenant id";
String authority =String.format("https://login.microsoftonline.com/",getTenantContextId())
String resourceUrl = "https://graph.microsoft.com"
ExecutorService service = Executors.newFixedThreadPool(1);
AuthenticationContext context = ew AuthenticationContext(authority, false, service);
ClientCredential clientCred = new ClientCredential(
clientId, appKey);
Future<AuthenticationResult> future = context.acquireToken(resourceUrl, clientCred, null);
AuthenticationResult result = future.get();
//Call Microsoft graph api
String stringUrl ="https://graph.microsoft.com/beta/applications?$filter=appId eq '{ApplicationId}'";
URL url = new URL(stringUrl.replaceAll(" ","%20"));
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "Bearer " + result.getAccessToken());
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Content-Type", "application/json");
int httpResponseCode = conn.getResponseCode();
if (httpResponseCode == 200 ) {
BufferedReader in = null;
StringBuilder response;
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String inputLine;
response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
JSONObject jb = new JSONObject(response.toString());
For more details, please refer to the document
Update
Please use the following code to get access token
String authority = "https://login.microsoftonline.com/" + tenant;
ExecutorService service = Executors.newFixedThreadPool(1);
AuthenticationContext context = new AuthenticationContext(authority, true, service);
ClientCredential cred = new ClientCredential(clientId, clientSecret);
String resourceId ="https://graph.microsoft.com";
Future<AuthenticationResult> future = context.acquireToken(resourceId, cred, null);
AuthenticationResult result = future.get();
String accesstoken = result.getAccessToken();
Add an auth file(optional but suggested, you can directly use client id and secret in your code)
Use Azure Management Libraries for Java
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure</artifactId>
<version>1.24.2</version>
</dependency>
Code sample
public static void main(String[] args) throws IOException {
File credFile = new File(ClassLoader.getSystemResource("./others/xh.auth").getPath());
ApplicationTokenCredentials credentials = ApplicationTokenCredentials.fromFile(credFile);
Azure.Authenticated authenticated = Azure.configure().authenticate(credentials);
String appObjectId = "b48bc188-ff55-4655-a1d0-b8590c179a99";
ActiveDirectoryApplication application = authenticated.activeDirectoryApplications().getById(appObjectId);
Map<String, PasswordCredential> map = application.passwordCredentials();
for ( Map.Entry<String,PasswordCredential> entry: map.entrySet()) {
String key = entry.getKey();
PasswordCredential value = entry.getValue();
System.out.println("Name -> " + key + " ; End date -> " + value.endDate().toString());
}
}
Output
Name -> t o m ; End date -> 2299-12-30T16:00:00.000Z
Name-> 3ba9bb7b-5251-4bbb-a373-658e346eb44d ; End date -> 2299-12-30T16:00:00.000Z
Name-> p o s t m a n ; End date -> 2299-12-30T16:00:00.000Z
Update:
You can get the application object id from portal:
Update2:
There is a getByName method:
ActiveDirectoryApplication byName = authenticated.activeDirectoryApplications().getByName("");
But there is a known problem. As the applications registered in Azure AD could have the same name. This method will not get a correct
ActiveDirectoryApplication instance as you expected. You will always get the first one in the list. (Actually, with REST API and filter, you will get a list too)
But, if you have created all the applications with different names, then you can use the getByName method.
Related
I have an Azure webapp (App Service) running with Tomcat. I'd deployed 2 war applications. WAR-1 provides web service call which return a json files using Springboot. WAR-2 is a web application which call this web services in WAR-1. This webapp has system assigned managed identity (or MSI). In addition, this webapp has authentication on with AAD, using Express configuration.
I can access static pages in WAR-2, after authentication through AAD. Now I need to fetch data from WAR-1. I have a servlet which contains code like this:
String subscriptionId = "xxxx";
String testURL = "https://yyy.azurewebsites.net/war1/person/100";
String resourceId = "https://management.azure.com/";
AppServiceMSICredentials credentials = new AppServiceMSICredentials(AzureEnvironment.AZURE);
Azure azure = Azure.configure()
.withLogLevel(LogLevel.BODY_AND_HEADERS)
.authenticate(credentials)
.withSubscription(subscriptionId);
String token = credentials.getToken(resourceId);
HttpURLConnection conn = (HttpURLConnection) new URL(testURL).openConnection();
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "Bearer " + token);
int responseCode = conn.getResponseCode();
OutputStream os = conn.getOutputStream();
....
I do able to get a token, but the response code is 500 when I make the GET call.
So my question is ... is this the correct way to do this call ? I did found an articlehttps://dotnetdevlife.wordpress.com/2018/10/22/call-azure-ad-protected-website-using-managed-service-identity-msi/ similar to this situation but it uses .Net. I cannot find any Java equivalent of this.
I tested at my side, and here are my steps:
1. Two apps in one Azure web app.
App1: https://jackdemoapp1.azurewebsites.net/app1/
App2: https://jackdemoapp1.azurewebsites.net/app2/
2. Configure Authentication/Authorization on Azure portal.
And you can get the client ID by clicking into the details, note it down and we will use it in app2:
3. Configure managed identity on Azure portal
To simplify the test, the app1 will just return a "Hello" string.
4. Code in app2
#ResponseBody
#RequestMapping("/")
public String index() {
JSONObject json = new JSONObject();
try {
AppServiceMSICredentials credential = new AppServiceMSICredentials(AzureEnvironment.AZURE);
// As we want to get token for accessing the aad-protected app, change the
// resource to the client ID you get in step 2
String token = credential.getToken("ac07d701-6f7d-462e-8b67-5dffa1df955f");
json.put("token", token);
// The URL for app1 API
String app1 = "https://jackdemoapp1.azurewebsites.net/app1/";
HttpURLConnection conn = (HttpURLConnection) new URL(app1).openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Authorization", "Bearer " + token);
conn.setDoOutput(true);
conn.setDoInput(true);
// Open the connection
conn.connect();
int code = conn.getResponseCode();
if (code >= 200 && code <= 300) {
try (InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
StringBuilder stringBuilder = new StringBuilder();
String line = "";
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
String response = stringBuilder.toString();
json.put("response", response);
}
} else {
json.put("Error", "Response Code" + conn.getResponseCode());
}
conn.disconnect();
} catch (Exception e) {
json.put("Exception", e.getStackTrace());
}
return json.toString();
}
Result
I want to check whether the file exists in the container of the Azure Blob Storage. If the file exists then I download the file.
Due to there not being any REST API or SDK API to check a blob whether exists, you can not directly check it. As I known, the only way to check the existence of a blob is to check the error information when getting a blob. Please refer to Common REST API Error Codes, as below.
Here are my steps and sample code for checking the existence of a blob using Microsoft Azure Storage SDK v10 for Java.
The maven dependency of Azure Storage SDK v10 for Java as below.
<!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure-storage-blob -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>10.4.0</version>
</dependency>
Generate a blob url with SAS signature.
String accountName = "<your storage account name>";
String accountKey = "<your storage account key>";
public String generateUrlWithSAS(String containerName, String blobName) throws InvalidKeyException {
SharedKeyCredentials credentials = new SharedKeyCredentials(accountName, accountKey);
ServiceSASSignatureValues values = new ServiceSASSignatureValues()
.withProtocol(SASProtocol.HTTPS_ONLY) // Users MUST use HTTPS (not HTTP).
.withExpiryTime(OffsetDateTime.now().plusDays(2)) // 2 days before expiration.
.withContainerName(containerName)
.withBlobName(blobName);
BlobSASPermission permission = new BlobSASPermission()
.withRead(true)
.withAdd(true)
.withWrite(true);
values.withPermissions(permission.toString());
SASQueryParameters serviceParams = values.generateSASQueryParameters(credentials);
String sasSign = serviceParams.encode();
return String.format(Locale.ROOT, "https://%s.blob.core.windows.net/%s/%s%s", accountName, containerName, blobName, sasSign);
}
Make a HTTP HEAD request for the URL with SAS signature to check the response status code
public static boolean exists(String urlWithSAS) throws MalformedURLException, IOException {
HttpURLConnection conn = (HttpURLConnection) new URL(urlWithSAS).openConnection();
conn.setRequestMethod("HEAD");
return conn.getResponseCode() == 200;
}
Also, you can directly check the existence via catching the related exception while downloading a blob.
Easier way (and less confusing to me) in case you are the blob owner, inspired by #Peter Pan's answer.
boolean blobExists(String containerName, String blobName) {
ServiceURL serviceUrl = getServiceUrl();
HttpURLConnection httpUrlConnection = (HttpURLConnection)
serviceUrl.createContainerURL(containerName)
.createBlockBlobURL(blobName)
.toURL().openConnection();
httpUrlConnection.setRequestMethod("HEAD");
return httpUrlConnection.getResponseCode() / 100 == 2;
}
ServiceURL getServiceUrl() {
SharedKeyCredentials credentials = new SharedKeyCredentials(accountName, accountKey);
return new ServiceURL(new URL(azureUrl), StorageURL.createPipeline(credentials, new PipelineOptions()));
}
Tested with library com.microsoft.azure:azure-storage-blob:10.5.0.
Microsoft states that getting a Blob is just a normal http get https://myaccount.blob.core.windows.net/mycontainer/myblob. But how do I format the string when I have an account + shared key?
I know there is an Azure SDK, but i'm creating an "add-on" to an existing java ee system, and cannot run in Azure, so I'm using REST Api. This is what i've tried so far:
String account = "myaccount";
String key = "243fedfsdf23f4f";
String protocol = "http";
String storageConnectionString = String.format("DefaultEndpointsProtocol=%s;AccountName=%s;AccountKey=%s", protocol, account, key);
System.out.println(storageConnectionString);
URL url = new URL("https://mysite.azureweb.com/myfile.txt");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn.getResponseCode() != 200) {
throw new IOException(conn.getResponseMessage());
}
// Buffer the result into a string
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = rd.readLine()) != null) {
sb.append(line);
}
rd.close();
conn.disconnect();
The string probably needs some Base64 encoding?
Update
The Http request looks like GET https://myAccount.blob.core.windows.net/myDir/myfile.txt HTTP/1.1
x-ms-date: Thu, 01 Oct 2015 12:56:11 GMT
x-ms-version: 2015-02-21
Authorization: SharedKey myAccount:asdfkjsladjfsdf827fhwf298f924f92723dfh23f273f2h7h4f
Host: myAccount.blob.core.windows.net
I "just" need to package this into a request to get a file in /mydir/myfile.txt
There are two access types for Azure Storage. One via Shared Keys and other other one via Shared Access Signatures.
Shared Keys give access to the whole storage account. Per storage account you have two shared keys and they are both equal. Usually you never give your shared keys away. Typically you only use them on the server side not in apps on the client side.
You only want to give someone access to a single file. Therefor using shared keys would be the wrong solution.
Shared Access Signatures give you the possibility to create a (REST) request, that is limited to certain files or containers. You can choose the privileges like write, read, delete etc. And you define a timeframe where the access is valid. For Shared Access Signatures you have two options: a) ad-hoc and b) policy-based. Ad-hoc Shared Access Signatures cannot be easily revoked (you could delete the file or invalidate the shared key which you used to create the Shared Access Signature). Policy-based Shared Access Signatures can easily be revoked by deleting the policy.
If you do not want to use the Azure SDK, you can create your own Shared Access Signatures. How to construct them is explained in the following link:
Constructing a Service SAS
There are also samples.
Service SAS Examples
Your file is stored in a BLOB. So you have to use the service BLOB. On the samples page you find the following BLOB sample.
signedstart=2013-08-16
signedexpiry=2013-08-17
signedresource=c
signedpermissions=r
signature=dD80ihBh5jfNpymO5Hg1IdiJIEvHcJpCMiCMnN/RnbI=
signedidentifier=YWJjZGVmZw==
signedversion=2013-08-15
responsecontent-disposition=file; attachment
responsecontent-type=binary
StringToSign = r + \n
2013-08-16 + \n
2013-08-17 + \n
/myaccount/pictures + \n
YWJjZGVmZw== + \n
2013-08-15 + \n
+ \n
file; attachment + \n
+ \n
+ \n
binary
HMAC-SHA256(URL.Decode(UTF8.Encode(StringToSign))) = a39+YozJhGp6miujGymjRpN8tsrQfLo9Z3i8IRyIpnQ=
Finally you get a URL for your REST request.
GET https://myaccount.blob.core.windows.net/pictures/profile.jpg?sv=2013-08-15&st=2013-08-16&se=2013-08-17&sr=c&sp=r&rscd=file;%20attachment&rsct=binary &sig=YWJjZGVmZw%3d%3d&sig=a39%2BYozJhGp6miujGymjRpN8tsrQfLo9Z3i8IRyIpnQ%3d HTTP/1.1
Have a look on the two pages for the full explanation.
There is a simple way to generate the SAS for getting files in the private container by using the Azure Storage SDK.
Following the sample code below to generat the SAS key and format the URL:
String accountName = "<your_account_name>";
String accountKey = "<your_account_key>";
String containerName = "<your_private_container_name>";
String blobFileName = "<your_blob_file_name>";
String storageConnectionString = String.format("DefaultEndpointsProtocol=%s;AccountName=%s;AccountKey=%s", "https", accountName, accountKey);
CloudStorageAccount account = CloudStorageAccount.parse(storageConnectionString);
CloudBlobClient blobClient = account.createCloudBlobClient();
CloudBlobContainer container = blobClient.getContainerReference(containerName);
CloudBlockBlob blob = container.getBlockBlobReference(blobFileName);
SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy();
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
calendar.setTime(new Date());
policy.setSharedAccessStartTime(calendar.getTime());
calendar.add(Calendar.HOUR, 1);
policy.setSharedAccessExpiryTime(calendar.getTime());
policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ));
String sas = blob.generateSharedAccessSignature(policy, null);
System.out.println(sas)
String urlstr = String.format("https://%s.blob.core.windows.net/%s/%s?%s", accountName, containerName, blobFileName, sas);
System.out.println(urlstr);
For details, you can refer to the doc https://msdn.microsoft.com/en-us/library/hh875756.aspx.
At my site a user is allowed to sign in with facebook. When doing that I ask for permission
to post to the users feed. This works like a charm.
When signed in, a user is allowed to write a review and when saving the review the user is asked if the user wants to post the review to the users feed on facebook. Since the post to facebook should be done after the review is saved in my local db, I understand that I need to perform an authentication serverside and then when I have a token I'm able to POST to eg.
http://graph.facebook.com/10XXXX40308/feed
with
message : "This works"
I have been trying to implement the facebook web login as described here:
The steps are:
Perform a request against
https://graph.facebook.com/oauth/authorize?client_id=MY_API_KEY&
redirect_uri=http://www.facebook.com/connect/login_success.html&
scope=publish_stream
Facebook will redirect you to
http://www.facebook.com/connect/login_success.html?
code=MY_VERIFICATION_CODE
Request
https://graph.facebook.com/oauth/access_token?client_id=MY_API_KEY&
redirect_uri=http://www.facebook.com/connect/login_success.html&
client_secret=MY_APP_SECRET&code=MY_VERIFICATION_CODE Facebook will
respond with access_token=MY_ACCESS_TOKEN
When doing 1. in a browser the application behaves accordingly. I get a redirect back from facebook with the MY_VERIFICATION_CODE:
So I try to do it in code like this:
String url = "https://graph.facebook.com/oauth/authorize?client_id="+clientId+"&scope=publish_stream&redirect_uri=http://www.facebook.com/connect/login_success.html";
URL obj = new URL(url);
conn = (HttpURLConnection) obj.openConnection();
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
System.out.println("Request URL ... " + url);
boolean redirect = false;
// normally, 3xx is redirect
int status = conn.getResponseCode();
if (status != HttpURLConnection.HTTP_OK) {
if (status == HttpURLConnection.HTTP_MOVED_TEMP
|| status == HttpURLConnection.HTTP_MOVED_PERM
|| status == HttpURLConnection.HTTP_SEE_OTHER)
redirect = true;
}
System.out.println("Response Code ... " + status);
if (redirect) {
// get redirect url from "location" header field
String newUrl = conn.getHeaderField("Location");
// get the cookie if need, for login
String cookies = conn.getHeaderField("Set-Cookie");
// open the new connnection again
conn = (HttpURLConnection) new URL(newUrl).openConnection();
conn.setRequestProperty("Cookie", cookies);
System.out.println("Redirect to URL : " + newUrl);
}
BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer html = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
html.append(inputLine);
}
in.close();
System.out.println("URL Content... \n" + html.toString());
System.out.println("Done");
But what happens is that instead of getting the 302 back I get a 200 back and the login
page in code:
It seems that I have missed a step or do not understand the flow.
What I'm trying to accomplish is to implement a similar call like to janrain's:
https://rpxnow.com/api/v2/facebook/stream.publish
where you are allowed to do this.
Thank you!
I guess rtfm is in place here. The user need to authenticate, so what I'm really trying to do here is to bypass the authentication process. This is of course not allowed. So
how do you solve this?
When the user authenticates I need to save the access token and the expire time so that
I can add that to the request later on.
I think that is the only way...Correct me if i'm wrong.
So in the authentication process i create a regexp:
Pattern pattern = Pattern.compile("access_token=([a-zA-Z0-9]+)&expires=([0-9]+)");
Then in the callback from facebook I extract the token with the regexp:
String accessToken = "";
String expires = "";
Matcher matcher = pattern.matcher(token.trim());
if(matcher.matches()) {
accessToken = matcher.group(1);
expires = matcher.group(2);
} else {
return new JSONObject()
.put("error", "OathBean: accessToken is null");
}
I then call facebook to get the values for the user and return all the values so that I can work with them:
return new JSONObject()
.put("facebookId", facebookId)
.put("firstName", firstName)
.put("lastName", lastName)
.put("email", email)
.put("photo", photo)
.put("accessToken", accessToken)
.put("expires", expires);
Later on when the user wants post a review to facebook. I populate the request and post the review.
Map<String, String> requestData = new HashMap<String, String>();
requestData.put("link",url(newReview));
requestData.put("description", "reviewText");
requestData.put("access_token", credential.getPassword());
String query = createQuery(requestData);
JSONObject result = null;
try {
URL url = new URL("https://graph.facebook.com/"+identifier+"/feed");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.connect();
OutputStreamWriter osw = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
osw.write(query);
osw.close();
result = new JSONObject(IOUtils.toString(conn.getInputStream()));
}
catch (Exception e) {
log.error("Could not call graph feed to publish for id: "+identifier, e);
}
if(result != null) {
boolean success = StringUtils.isNotBlank(result.getString("id"));
entityManager.persist(new FBPublishEvent(currentUser, newReview, success, result.toString()));
}
If you have a better solution, please share =)
I have been trying to implement the paypal IPN system in our company website. When I test my script in the IPN sandbox tool, it is validated and everything goes well, however when I move it live, the IPN returned as INVALID but the payment has been completed well.
When I check the IPN history in my account, I can see this IPN message with the HTTP Response 200:
mc_gross=0.01&invoice=40&protection_eligibility=Ineligible&item_number1=&payer_id=mypayerId&tax=0.00&payment_date=08:06:52 Sep 03, 2013 PDT&payment_status=Completed&charset=windows-1252&mc_shipping=0.00&mc_handling=0.00&first_name=myName&mc_fee=0.01¬ify_version=3.7&custom=18528&payer_status=verified&business=bussiness_mail&num_cart_items=1&mc_handling1=0.00&verify_sign=AJ.HL1f2A9aoBiFQCLn.3J-QkKQGAF.RVW8er5rbGJ6SsQFWBbStuRtD&payer_email=myMail&mc_shipping1=0.00&tax1=0.00&txn_id=61052338B4613440H&payment_type=instant&last_name=MySurname&item_name1=Paquete Lite&receiver_email=mybussiness_mail&payment_fee=&quantity1=1&receiver_id=SVRXVCZYE2AYC&txn_type=cart&mc_gross_1=0.01&mc_currency=EUR&residence_country=ES&transaction_subject=18528&payment_gross=&ipn_track_id=38c492cbe6257
Paypal live credentials are OK.
URL is OK (https://www.paypal.com/cgi-bin/webscr)
My IPNHandler is a Java Servlet. The doPost Action is:
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Java JSP
log.error("IPN doPost " + new Date().getHours() + ":" + new Date().getMinutes() + ":" + new Date().getSeconds());
// read post from PayPal system and add 'cmd'
Enumeration en = request.getParameterNames();
String str = "cmd=_notify-validate";
while (en.hasMoreElements()) {
String paramName = (String) en.nextElement();
String paramValue = request.getParameter(paramName);
paramValue = new String(paramValue.getBytes("iso-8859-1"), "utf-8");
str = str + "&" + paramName + "=" + URLEncoder.encode(paramValue);
}
boolean isSandbox = "true".equals(PropertiesManager.getProperty("signbox", "PaypalSandbox"));
// post back to PayPal system to validate
// NOTE: change http: to https: in the following URL to verify using SSL (for increased security).
// using HTTPS requires either Java 1.4 or greater, or Java Secure Socket Extension (JSSE)
// and configured for older versions.
String url = null;
if (isSandbox){
url = "https://www.sandbox.paypal.com/cgi-bin/webscr";
}else{
url = "https://www.paypal.com/cgi-bin/webscr";
}
log.error("La url de a la que redirigimos a Paypal es " + url);
URL u = new URL(url);
URLConnection uc = u.openConnection();
uc.setDoOutput(true);
uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
PrintWriter pw = new PrintWriter(uc.getOutputStream());
pw.println(str);
pw.close();
BufferedReader in = new BufferedReader(
new InputStreamReader(uc.getInputStream()));
String res = in.readLine();
in.close();
log.error("Tras abrir la conexión, res es = " + res);
// assign posted variables to local variables
String idUser = request.getParameter("custom");
String idCompra = request.getParameter("invoice");
String paymentStatus = request.getParameter("payment_status");
String paymentAmount = request.getParameter("mc_gross");
String fee = request.getParameter("mc_fee");
String paymentCurrency = request.getParameter("mc_currency");
String txnId = request.getParameter("txn_id");
String receiptId = request.getParameter("receipt_id");
String receiverEmail = request.getParameter("receiver_email");
String payerEmail = request.getParameter("payer_email");
String paymentType = request.getParameter("payment_type");
String txnType = request.getParameter("txn_type");
if (!"instant".equals(paymentType) || !"cart".equals(txnType)) {
log.debug("NO ES UN CART CHECKOUT. Detalles:");
log.debug("idCompra=" + idCompra);
log.debug("status=" + paymentStatus);
log.debug("amount=" + paymentAmount);
log.debug("currency=" + paymentCurrency);
log.debug("transactionId=" + txnId);
log.debug("receiptId=" + receiptId);
log.debug("receiverEmail=" + receiverEmail);
log.debug("payerEmail=" + payerEmail);
log.debug("paymentType=" + paymentType);
log.debug("txnType=" + txnType);
return;
}
**//HERE THE RESPONSE IS INVALID IN LIVE MODE**
if (res != null && res.equals("VERIFIED")) { //res = "VERIFIED" res = "INVALID"
// more code not important for this issue....
Any idea? As I said, payments are completed but the IPN is sended INVALID.
I know this is old but it might help someone else, I think you need to POST the data for validation.
According to Paypal IPN Docs:
//After receiving an IPN message from PayPal,
//you must respond to PayPal with a POST message
//that begins with "cmd=_notify-validate".
//Append to your message a duplicate of the
//notification received (the same IPN fields
//and values in the exact order you received them)
...
URL u = new URL(url);
HttpURLConnection uc = (HttpURLConnection) u.openConnection();
uc.setRequestMethod("POST");
...
To receive IPN message data from PayPal, your listener must follow this request-response flow:
1.Your listener listens for the HTTPS POST IPN messages that PayPal sends with each event.
2.After receiving the IPN message from PayPal, your listener returns an empty HTTP 200 response to PayPal. Otherwise, PayPal resends the IPN message.
3.Your listener sends the complete message back to PayPal using HTTPS POST.
Prefix the returned message with the cmd=_notify-validate variable, but do not change the message fields, the order of the fields, or the character encoding from the original message.
Send response messages back to PayPal:
https://ipnpb.sandbox.paypal.com/cgi-bin/webscr (for Sandbox IPNs)
https:/ipnpb.paypal.com/cgi-bin/webscr (for live IPNs)
for more info see also:
https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNImplementation/#specs