PayPal NVP/SOAP API TransactionSearchReq Call - java

I am currently trying to implement a class in Java that lists all PayPal transactions of a specific time frame. I am using the NVP/SOAP API Merchant SDK (https://github.com/paypal/merchant-sdk-java) as this seems to be the only possibility to list 'all' transactions. According to other questions on stackoverflow the REST SDK only lists transactions that were made by REST calls, which is not suitable in my case.
Unfortunately the sample code for a TransactionSearchReq call on github shown in the README file is not complete and there is also no other sample implementation of that call available.
So my questions are:
1) Can anybody help with a sample code?
2) Will the merchant NVP/API SDK fulfill the requirements for the new security updates of PayPal (https://www.paypal-engineering.com/2016/05/12/upcoming-security-changes-notice/ & https://www.paypal.com/au/webapps/mpp/tls-http-upgrade) taking place by June 2018?

Answer to question 1):
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import urn.ebay.api.PayPalAPI.*;
import urn.ebay.apis.eBLBaseComponents.PaymentTransactionSearchResultType;
public class PaymentManager {
public static void main(String[] args) {
Map<String,String> configMap = new HashMap<String,String>();
configMap.put("mode", "live");
// Account Credential
configMap.put("acct1.UserName", "...");
configMap.put("acct1.Password", "...");
configMap.put("acct1.Signature", "...-...");
// Subject is optional, only required in case of third party permission
//configMap.put("acct1.Subject", "");
// Sample Certificate credential
// configMap.put("acct2.UserName", "certuser_biz_api1.paypal.com");
// configMap.put("acct2.Password", "D6JNKKULHN3G5B8A");
// configMap.put("acct2.CertKey", "password");
// configMap.put("acct2.CertPath", "resource/sdk-cert.p12");
// configMap.put("acct2.AppId", "APP-80W284485P519543T");
TransactionSearchReq txnreq = new TransactionSearchReq();
TransactionSearchRequestType requestType = new TransactionSearchRequestType();
requestType.setStartDate("2018-04-01T00:00:00.000Z");
requestType.setEndDate("2018-04-05T23:59:59.000Z");
requestType.setVersion("95.0");
requestType.setTransactionID("");
txnreq.setTransactionSearchRequest(requestType);
PayPalAPIInterfaceServiceService service = new PayPalAPIInterfaceServiceService(configMap);
try {
TransactionSearchResponseType txnresponse = service.transactionSearch(txnreq, configMap.get("acct1.UserName"));
List<PaymentTransactionSearchResultType> transactions = txnresponse.getPaymentTransactions();
for (int i = 0; i < transactions.size(); i++) {
System.out.println(transactions.get(i).getPayer());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

Related

When using service account impersonation, when calling export on Google Docs using v3 API, viewedByMeTime timestamp is updated

I am using a service account to access google doc files of users in my enterprise google account using impersonation.
See:
https://developers.google.com/drive/api/v3/about-auth#OAuth2Authorizing
So far so good.
Then, I need to download contents of Google Docs.
When calling Google Drive API to download the contents of a Google Doc, the documentation says to run the following:
https://developers.google.com/drive/api/v3/manage-downloads
Here is a java program that should reproduce the problem:
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.SecurityUtils;
import com.google.api.services.drive.Drive;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;
public class FetchGoogleDocContentsWithServiceAccount {
static int readTimeout = 60000;
static int connectTimeout = 60000;
static String serviceAccountId = "";
static String serviceAccountEmail = "";
static String serviceAccountPrivateKeyFile = "";
static String serviceAccountPrivateKeyFilePassword = "";
static String fileId = "";
static JacksonFactory jacksonFactory = new JacksonFactory();
static NetHttpTransport httpTransport = new NetHttpTransport();
static List<String> googleScopeList = Arrays.asList("https://www.googleapis.com/auth/drive.readonly",
"https://www.googleapis.com/auth/admin.directory.group.readonly",
"https://www.googleapis.com/auth/admin.directory.user.alias.readonly",
"https://www.googleapis.com/auth/admin.directory.group", "https://www.googleapis.com/auth/admin.directory.user",
"https://www.googleapis.com/auth/drive");
public static void main(String[] args) throws Exception {
Drive drive = (new Drive.Builder(httpTransport,
jacksonFactory,
getRequestInitializer(getGoogleCredentials())))
.setApplicationName("Sample app").build();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
drive.files().export(fileId, "application/vnd.google-apps.document")
.executeMediaAndDownloadTo(baos);
System.out.println(baos.toString("UTF-8"));
}
public static HttpRequestInitializer getRequestInitializer(final GoogleCredential requestInitializer) {
return httpRequest -> {
requestInitializer.initialize(httpRequest);
httpRequest.setConnectTimeout(readTimeout);
httpRequest.setReadTimeout(connectTimeout);
};
}
public static GoogleCredential getGoogleCredentials() {
GoogleCredential credential;
try {
GoogleCredential.Builder b = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(jacksonFactory).setServiceAccountId(serviceAccountId)
.setServiceAccountPrivateKey(SecurityUtils.loadPrivateKeyFromKeyStore(SecurityUtils.getPkcs12KeyStore(),
new FileInputStream(new File(serviceAccountPrivateKeyFile)), serviceAccountPrivateKeyFilePassword,
"privatekey", serviceAccountPrivateKeyFilePassword))
.setServiceAccountScopes(googleScopeList);
if (serviceAccountEmail != null) {
b = b.setServiceAccountUser(serviceAccountEmail);
}
credential = b.build();
} catch (IOException | GeneralSecurityException e1) {
throw new RuntimeException("Could not build client secrets", e1);
}
return credential;
}
}
When I have performed this operation, we are seeing that the viewedByMeTime field is actually being updated as the impersonated user.
This is not good, because now people think someone might have stolen access to their account. They are going to open tickets with the security team.
Is this expected? How can I make this stop? Is there another method in the API I can call to download the google docs without updating this timestamp?
Also opened a ticket on the github for the google drive java sdk: https://github.com/googleapis/google-api-java-client-services/issues/3160
Updating the viewedByMeTime field upon calling the endpoint is indeed intended behaviour. Any action performed through the API is considered the same way as if the user did that action manually (i.e. that field would be updated too when the user visits the document through the UI).
By using domain-wise delegation (or "user impersonation"), you have no way to avoid this issue.
The only workaround would be to give the service account access to this file, and let it export the file without domain-wide delegation. The viewedByMeTime field will be updated only for the service account itself, but not for the original owner of that file (or any other user having access to it).

Google Sheets use API key instead of client_secret.json

In the QuickStart.java example on Java Quickstart they use OAuth client ID to identify the application, and this pops up a windows asking for Google credentials to use the application. You have to download a client_secret.json to modify a Google Sheet.
My question is: Can you evade the popping up window asking for Google credentials using an API Key or something else? And, if it's possible, how do you change the Java code in order to do that?
An API key could only work when accessing the resources owned by the project that created the key.
For resources like spreadsheets, you're typically accessing resources owned by a user. It would be pretty awful if you got access to my private sheets simply by having an API key.
So no, I wouldn't expect there to be any way to avoid getting authorization to work with a user's documents. However, you should be able to use the Java OAuth library to retain the auth token so you can avoid needing to ask for it more than once. (Unless the user revokes access, of course.)
As DalmTo says, you can use service account credentials if you're trying to access resources owned by the project (or which the project can be granted access to). Note that if you're running on AppEngine, Google Kubernetes Engine or Google Compute Engine, the service account credentials for that environment should be available automatically.
The popup window you are seeing is the Oauth2 consent screen. In order to access private user data you need to have consent of the user in order to access their data.
There is another option its called a service account. If the sheet you are trying to access is one that you as the developer have control of then you can create service account credeitals take the service account email address and grant the service account access to the sheet.
The best example for service account access with java that i am aware of is the one for Google Analytics you will have to alter it for Google sheets i may be able to help with that if you have any issues. hello analytics service account.
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.analytics.Analytics;
import com.google.api.services.analytics.AnalyticsScopes;
import com.google.api.services.analytics.model.Accounts;
import com.google.api.services.analytics.model.GaData;
import com.google.api.services.analytics.model.Profiles;
import com.google.api.services.analytics.model.Webproperties;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.io.IOException;
/**
* A simple example of how to access the Google Analytics API using a service
* account.
*/
public class HelloAnalytics {
private static final String APPLICATION_NAME = "Hello Analytics";
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
private static final String KEY_FILE_LOCATION = "<REPLACE_WITH_JSON_FILE>";
public static void main(String[] args) {
try {
Analytics analytics = initializeAnalytics();
String profile = getFirstProfileId(analytics);
System.out.println("First Profile Id: "+ profile);
printResults(getResults(analytics, profile));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Initializes an Analytics service object.
*
* #return An authorized Analytics service object.
* #throws IOException
* #throws GeneralSecurityException
*/
private static AnalyticsReporting initializeAnalytic() throws GeneralSecurityException, IOException {
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
GoogleCredential credential = GoogleCredential
.fromStream(new FileInputStream(KEY_FILE_LOCATION))
.createScoped(AnalyticsScopes.all());
// Construct the Analytics service object.
return new Analytics.Builder(httpTransport, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME).build();
}
private static String getFirstProfileId(Analytics analytics) throws IOException {
// Get the first view (profile) ID for the authorized user.
String profileId = null;
// Query for the list of all accounts associated with the service account.
Accounts accounts = analytics.management().accounts().list().execute();
if (accounts.getItems().isEmpty()) {
System.err.println("No accounts found");
} else {
String firstAccountId = accounts.getItems().get(0).getId();
// Query for the list of properties associated with the first account.
Webproperties properties = analytics.management().webproperties()
.list(firstAccountId).execute();
if (properties.getItems().isEmpty()) {
System.err.println("No Webproperties found");
} else {
String firstWebpropertyId = properties.getItems().get(0).getId();
// Query for the list views (profiles) associated with the property.
Profiles profiles = analytics.management().profiles()
.list(firstAccountId, firstWebpropertyId).execute();
if (profiles.getItems().isEmpty()) {
System.err.println("No views (profiles) found");
} else {
// Return the first (view) profile associated with the property.
profileId = profiles.getItems().get(0).getId();
}
}
}
return profileId;
}
private static GaData getResults(Analytics analytics, String profileId) throws IOException {
// Query the Core Reporting API for the number of sessions
// in the past seven days.
return analytics.data().ga()
.get("ga:" + profileId, "7daysAgo", "today", "ga:sessions")
.execute();
}
private static void printResults(GaData results) {
// Parse the response from the Core Reporting API for
// the profile name and number of sessions.
if (results != null && !results.getRows().isEmpty()) {
System.out.println("View (Profile) Name: "
+ results.getProfileInfo().getProfileName());
System.out.println("Total Sessions: " + results.getRows().get(0).get(0));
} else {
System.out.println("No results found");
}
}
}

validate webhook using java Event.validateReceivedEvent always fails signature validation

I prepared a servlet in my web site to be notified from PayPal webhook. The development version of the servlet logs the http headers and the body. Here is a screen capture with one example:
I've created a "self contained test application" that shows the problem.
package com.rsws.renew;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Map;
import com.paypal.api.payments.Event;
import com.paypal.base.Constants;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.PayPalRESTException;
import com.paypal.base.rest.PayPalResource;
/**
* #author Ignacio
*
*/
public class TestWebHook {
public static void main(String[] argv) {
try {
InputStream is = InvoicePaid.class
.getResourceAsStream("/sdk_config.properties");
try {
PayPalResource.initConfig(is);
} catch (PayPalRESTException e) {
e.printStackTrace();
}
APIContext apiContext = new APIContext();
Map<String, String> map = new HashMap<>(PayPalResource.getConfigurations());
apiContext.setConfigurationMap(map);
Map<String,String> headers = new HashMap<String,String>();
// this is the data provided by PayPal sandbox
map.put(Constants.PAYPAL_WEBHOOK_ID, "3W2725225F637605K");
String payload = "{\"id\":\"WH-0T490472X6099635W-7LJ29748BW389372K\",\"create_time\":\"2015-09-25T23:14:14Z\",\"resource_type\":\"invoices\",\"event_type\":\"INVOICING.INVOICE.PAID\",\"summary\":\"An invoice was created\",\"resource\":{\"id\":\"INV2-8FSD-3HT6-BRHR-UHYV\",\"number\":\"MM00063\",\"status\":\"PAID\",\"merchant_info\":{\"email\":\"example#outlook.com\",\"first_name\":\"Dennis\",\"last_name\":\"Doctor\",\"business_name\":\"Medical Professional LLC\",\"address\":{\"line1\":\"1234 Main St\",\"line2\":\"Apt 302\",\"city\":\"Portland\",\"state\":\"OR\",\"postal_code\":\"97217\",\"country_code\":\"US\"}},\"billing_info\":[{\"email\":\"example#example.com\",\"business_name\":\"Medical Professionals LLC\",\"language\":\"en_US\"}],\"items\":[{\"name\":\"Sample Item\",\"quantity\":1,\"unit_price\":{\"currency\":\"USD\",\"value\":\"1.00\"},\"unit_of_measure\":\"QUANTITY\"}],\"invoice_date\":\"2015-09-28 PDT\",\"payment_term\":{\"term_type\":\"DUE_ON_RECEIPT\",\"due_date\":\"2015-09-28 PDT\"},\"tax_calculated_after_discount\":true,\"tax_inclusive\":false,\"total_amount\":{\"currency\":\"USD\",\"value\":\"1.00\"},\"payments\":[{\"type\":\"PAYPAL\",\"transaction_id\":\"22592127VV907111U\",\"transaction_type\":\"SALE\",\"method\":\"PAYPAL\",\"date\":\"2015-09-28 14:37:13 PDT\"}],\"metadata\":{\"created_date\":\"2015-09-28 14:35:46 PDT\",\"last_updated_date\":\"2015-09-28 14:37:13 PDT\",\"first_sent_date\":\"2015-09-28 14:35:47 PDT\",\"last_sent_date\":\"2015-09-28 14:35:47 PDT\"},\"paid_amount\":{\"paypal\":{\"currency\":\"USD\",\"value\":\"1.00\"}},\"links\":[{\"rel\":\"self\",\"href\":\"https://api.paypal.com/v1/invoicing/invoices/INV2-8FSD-3HT6-BRHR-UHYV\",\"method\":\"GET\"}]},\"links\":[{\"href\":\"https://api.paypal.com/v1/notifications/webhooks-events/WH-0T490472X6099635W-7LJ29748BW389372K\",\"rel\":\"self\",\"method\":\"GET\"},{\"href\":\"https://api.paypal.com/v1/notifications/webhooks-events/WH-0T490472X6099635W-7LJ29748BW389372K/resend\",\"rel\":\"resend\",\"method\":\"POST\"}]}";
headers.put("PAYPAL-CERT-URL", "https://api.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-df8cd2d5");
headers.put("PAYPAL-TRANSMISSION-ID", "464163d0-e0ae-11e5-af72-51ae350aaff1");
headers.put("PAYPAL-TRANSMISSION-TIME", "2016-03-02T19:38:01Z");
headers.put("PAYPAL-AUTH-ALGO", "SHA256withRSA");
headers.put("PAYPAL-TRANSMISSION-SIG", "S3AjY87GLp1MP/UsGAWPoEes+laa7xbV4X7pMi9PdC0QR7MoNC/L/O2UThAh1IBzDZ5DGXvkEDvXK9fF0IfoS2QtLJUBm5+UFoo1jJMlH+QCiJUEHSuio2UrFGbxoqaIPcA1PN0tmd5FwikDRPCnpht6pvMvCZV1FEQbBMr9ld3d3XoWBKeWQG+oxAWSTNYJiKQIrM6l/8+hKVQ1LZID8dtR3c7y6eFxNFsDQ3WgwChZZ15vpyhDWQ4t08m3PsWFyjvsQmNRyXQyUeAC8xw96sBwGmHsgwKJwbAamVrWicQqQ/tXuUcqx9Y0pg3P4LuGNPFKzktq9L3ZImTEJxpRLA==");
// this shows invalid
System.out.println(Event.validateReceivedEvent(apiContext, headers, payload) ? "valid" : "invalid");
// this is the data provided in the sdk examples https://github.com/paypal/PayPal-Java-SDK/blob/master/rest-api-sdk/src/test/java/com/paypal/base/ValidateCertTest.java
map.put(Constants.PAYPAL_WEBHOOK_ID, "3RN13029J36659323");
payload = "{\"id\":\"WH-2W7266712B616591M-36507203HX6402335\",\"create_time\":\"2015-05-12T18:14:14Z\",\"resource_type\":\"sale\",\"event_type\":\"PAYMENT.SALE.COMPLETED\",\"summary\":\"Payment completed for $ 20.0 USD\",\"resource\":{\"id\":\"7DW85331GX749735N\",\"create_time\":\"2015-05-12T18:13:18Z\",\"update_time\":\"2015-05-12T18:13:36Z\",\"amount\":{\"total\":\"20.00\",\"currency\":\"USD\"},\"payment_mode\":\"INSTANT_TRANSFER\",\"state\":\"completed\",\"protection_eligibility\":\"ELIGIBLE\",\"protection_eligibility_type\":\"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE\",\"parent_payment\":\"PAY-1A142943SV880364LKVJEFPQ\",\"transaction_fee\":{\"value\":\"0.88\",\"currency\":\"USD\"},\"links\":[{\"href\":\"https://api.sandbox.paypal.com/v1/payments/sale/7DW85331GX749735N\",\"rel\":\"self\",\"method\":\"GET\"},{\"href\":\"https://api.sandbox.paypal.com/v1/payments/sale/7DW85331GX749735N/refund\",\"rel\":\"refund\",\"method\":\"POST\"},{\"href\":\"https://api.sandbox.paypal.com/v1/payments/payment/PAY-1A142943SV880364LKVJEFPQ\",\"rel\":\"parent_payment\",\"method\":\"GET\"}]},\"links\":[{\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2W7266712B616591M-36507203HX6402335\",\"rel\":\"self\",\"method\":\"GET\"},{\"href\":\"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2W7266712B616591M-36507203HX6402335/resend\",\"rel\":\"resend\",\"method\":\"POST\"}]}";
headers.put("PAYPAL-CERT-URL", "https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-a5cafa77");
headers.put("PAYPAL-TRANSMISSION-ID", "b2384410-f8d2-11e4-8bf3-77339302725b");
headers.put("PAYPAL-TRANSMISSION-TIME", "2015-05-12T18:14:14Z");
headers.put("PAYPAL-AUTH-ALGO", "SHA256withRSA");
headers.put("PAYPAL-TRANSMISSION-SIG", "vSOIQFIZQHv8G2vpbOpD/4fSC4/MYhdHyv+AmgJyeJQq6q5avWyHIe/zL6qO5hle192HSqKbYveLoFXGJun2od2zXN3Q45VBXwdX3woXYGaNq532flAtiYin+tQ/0pNwRDsVIufCxa3a8HskaXy+YEfXNnwCSL287esD3HgOHmuAs0mYKQdbR4e8Evk8XOOQaZzGeV7GNXXz19gzzvyHbsbHmDz5VoRl9so5OoHqvnc5RtgjZfG8KA9lXh2MTPSbtdTLQb9ikKYnOGM+FasFMxk5stJisgmxaefpO9Q1qm3rCjaJ29aAOyDNr3Q7WkeN3w4bSXtFMwyRBOF28pJg9g==");
// this shows valid
System.out.println(Event.validateReceivedEvent(apiContext, headers, payload) ? "valid" : "invalid");
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (PayPalRESTException e) {
e.printStackTrace();
}
}
}
The code shows valid when the data has been taken from examples and invalid when the data comes from paypal web site.
I wonder why this cannot be validated. Any help is welcome.
You may want to test the validation with actual sandbox transactions and webhook events. Simulator mock data may not be updated with the sandbox algorithm, and is recommended for testing URL accessibility of your script.

Run Java program inside PHP code [duplicate]

This question already has answers here:
How to run java code (.class) using php and display on the same web page
(2 answers)
Closed 7 years ago.
I am trying to make a simple recommender system, and I found that with mahout it is pretty easy to make one. I have the following code (I am running it on eclipse and everything works great:
package com.predictionmarketing.RecommenderApp;
import java.io.File;
import java.io.IOException;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.ThresholdUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.UserBasedRecommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
/**
* Java's application, user based recommender system
*
*/
public class App
{
public static void main( String[] args )
{
// Modelo
DataModel model = null;
// Inicializar similaridad
UserSimilarity similarity = null;
// Leer .cv userID, itemID, value
try {
model = new FileDataModel(new File("data/dataset.csv"));
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// Encontrar matriz de similaridad
try {
similarity = new PearsonCorrelationSimilarity(model);
} catch (TasteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
UserNeighborhood neighborhood = new ThresholdUserNeighborhood(0.1, similarity, model);
UserBasedRecommender recommender = new GenericUserBasedRecommender(model, neighborhood, similarity);
java.util.List<RecommendedItem> recommendations = null;
try {
recommendations = recommender.recommend(2, 3);
} catch (TasteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Mostrar Recomendaciones
for (RecommendedItem recommendation : recommendations) {
System.out.println(recommendation.getItemID());
}
}
}
However, I need to run this code online because I am making the application on PHP and that is where my problem arises. Is there a way to run this code on PHP, so I can use the "recommendation" variable?
You can run this java code (compiled first) from php code with shell_exec.
But is a better solution build a REST service (or another) to do it language agnostic.
There is no simple solution for this. To make it work and communicate with PHP you have to create some interface for it. For example create java servlet, and put it on Servlet container (Java web server). This is simplest I see now.
Other solution you could consider also REST or SOAP service, to exchange data between this Java code and your PHP application. This also will need JavaEE container.

Amazon Product Advertising API through Java/SOAP

I have been playing with Amazon's Product Advertising API, and I cannot get a request to go through and give me data. I have been working off of this: http://docs.amazonwebservices.com/AWSECommerceService/2011-08-01/GSG/ and this: Amazon Product Advertising API signed request with Java
Here is my code. I generated the SOAP bindings using this: http://docs.amazonwebservices.com/AWSECommerceService/2011-08-01/GSG/YourDevelopmentEnvironment.html#Java
On the Classpath, I only have: commons-codec.1.5.jar
import com.ECS.client.jax.AWSECommerceService;
import com.ECS.client.jax.AWSECommerceServicePortType;
import com.ECS.client.jax.Item;
import com.ECS.client.jax.ItemLookup;
import com.ECS.client.jax.ItemLookupRequest;
import com.ECS.client.jax.ItemLookupResponse;
import com.ECS.client.jax.ItemSearchResponse;
import com.ECS.client.jax.Items;
public class Client {
public static void main(String[] args) {
String secretKey = <my-secret-key>;
String awsKey = <my-aws-key>;
System.out.println("API Test started");
AWSECommerceService service = new AWSECommerceService();
service.setHandlerResolver(new AwsHandlerResolver(
secretKey)); // important
AWSECommerceServicePortType port = service.getAWSECommerceServicePort();
// Get the operation object:
com.ECS.client.jax.ItemSearchRequest itemRequest = new com.ECS.client.jax.ItemSearchRequest();
// Fill in the request object:
itemRequest.setSearchIndex("Books");
itemRequest.setKeywords("Star Wars");
// itemRequest.setVersion("2011-08-01");
com.ECS.client.jax.ItemSearch ItemElement = new com.ECS.client.jax.ItemSearch();
ItemElement.setAWSAccessKeyId(awsKey);
ItemElement.getRequest().add(itemRequest);
// Call the Web service operation and store the response
// in the response object:
com.ECS.client.jax.ItemSearchResponse response = port
.itemSearch(ItemElement);
String r = response.toString();
System.out.println("response: " + r);
for (Items itemList : response.getItems()) {
System.out.println(itemList);
for (Item item : itemList.getItem()) {
System.out.println(item);
}
}
System.out.println("API Test stopped");
}
}
Here is what I get back.. I was hoping to see some Star Wars books available on Amazon dumped out to my console :-/:
API Test started
response: com.ECS.client.jax.ItemSearchResponse#7a6769ea
com.ECS.client.jax.Items#1b5ac06e
API Test stopped
What am I doing wrong (Note that no "item" in the second for loop is being printed out, because its empty)? How can I troubleshoot this or get relevant error information?
I don't use the SOAP API but your Bounty requirements didn't state that it had to use SOAP only that you wanted to call Amazon and get results. So, I'll post this working example using the REST API which will at least fulfill your stated requirements:
I would like some working example code that hits the amazon server and returns results
You'll need to download the following to fulfill the signature requirements:
http://associates-amazon.s3.amazonaws.com/signed-requests/samples/amazon-product-advt-api-sample-java-query.zip
Unzip it and grab the com.amazon.advertising.api.sample.SignedRequestsHelper.java file and put it directly into your project. This code is used to sign the request.
You'll also need to download Apache Commons Codec 1.3 from the following and add it to your classpath i.e. add it to your project's library. Note that this is the only version of Codec that will work with the above class (SignedRequestsHelper)
http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.3.zip
Now you can copy and paste the following making sure to replace your.pkg.here with the proper package name and replace the SECRET and the KEY properties:
package your.pkg.here;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class Main {
private static final String SECRET_KEY = "<YOUR_SECRET_KEY>";
private static final String AWS_KEY = "<YOUR_KEY>";
public static void main(String[] args) {
SignedRequestsHelper helper = SignedRequestsHelper.getInstance("ecs.amazonaws.com", AWS_KEY, SECRET_KEY);
Map<String, String> params = new HashMap<String, String>();
params.put("Service", "AWSECommerceService");
params.put("Version", "2009-03-31");
params.put("Operation", "ItemLookup");
params.put("ItemId", "1451648537");
params.put("ResponseGroup", "Large");
String url = helper.sign(params);
try {
Document response = getResponse(url);
printResponse(response);
} catch (Exception ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static Document getResponse(String url) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(url);
return doc;
}
private static void printResponse(Document doc) throws TransformerException, FileNotFoundException {
Transformer trans = TransformerFactory.newInstance().newTransformer();
Properties props = new Properties();
props.put(OutputKeys.INDENT, "yes");
trans.setOutputProperties(props);
StreamResult res = new StreamResult(new StringWriter());
DOMSource src = new DOMSource(doc);
trans.transform(src, res);
String toString = res.getWriter().toString();
System.out.println(toString);
}
}
As you can see this is much simpler to setup and use than the SOAP API. If you don't have a specific requirement for using the SOAP API then I would highly recommend that you use the REST API instead.
One of the drawbacks of using the REST API is that the results aren't unmarshaled into objects for you. This could be remedied by creating the required classes based on the wsdl.
This ended up working (I had to add my associateTag to the request):
public class Client {
public static void main(String[] args) {
String secretKey = "<MY_SECRET_KEY>";
String awsKey = "<MY AWS KEY>";
System.out.println("API Test started");
AWSECommerceService service = new AWSECommerceService();
service.setHandlerResolver(new AwsHandlerResolver(secretKey)); // important
AWSECommerceServicePortType port = service.getAWSECommerceServicePort();
// Get the operation object:
com.ECS.client.jax.ItemSearchRequest itemRequest = new com.ECS.client.jax.ItemSearchRequest();
// Fill in the request object:
itemRequest.setSearchIndex("Books");
itemRequest.setKeywords("Star Wars");
itemRequest.getResponseGroup().add("Large");
// itemRequest.getResponseGroup().add("Images");
// itemRequest.setVersion("2011-08-01");
com.ECS.client.jax.ItemSearch ItemElement = new com.ECS.client.jax.ItemSearch();
ItemElement.setAWSAccessKeyId(awsKey);
ItemElement.setAssociateTag("th0426-20");
ItemElement.getRequest().add(itemRequest);
// Call the Web service operation and store the response
// in the response object:
com.ECS.client.jax.ItemSearchResponse response = port
.itemSearch(ItemElement);
String r = response.toString();
System.out.println("response: " + r);
for (Items itemList : response.getItems()) {
System.out.println(itemList);
for (Item itemObj : itemList.getItem()) {
System.out.println(itemObj.getItemAttributes().getTitle()); // Title
System.out.println(itemObj.getDetailPageURL()); // Amazon URL
}
}
System.out.println("API Test stopped");
}
}
It looks like the response object does not override toString(), so if it contains some sort of error response, simply printing it will not tell you what the error response is. You'll need to look at the api for what fields are returned in the response object and individually print those. Either you'll get an obvious error message or you'll have to go back to their documentation to try to figure out what is wrong.
You need to call the get methods on the Item object to retrieve its details, e.g.:
for (Item item : itemList.getItem()) {
System.out.println(item.getItemAttributes().getTitle()); //Title of item
System.out.println(item.getDetailPageURL()); // Amazon URL
//etc
}
If there are any errors you can get them by calling getErrors()
if (response.getOperationRequest().getErrors() != null) {
System.out.println(response.getOperationRequest().getErrors().getError().get(0).getMessage());
}

Categories

Resources