Monday, November 7, 2011

Moving Paypal API based Web Store in Java to the Google App Engine

A member of the development team nicknamed Zeo, living in south California sent me an email asking help to solve an issue. In his spare time he was migrating a web store application in Java/Spring 3.1 to the Google App Engine. One of the requirements was to integrate  Express Checkout API to streamline the checkout process for buyers of digital goods. PayPal X delivers an open source java toolkit for Google App Engine available on Google Code named paypalx-gae-toolkit, which provides support for the Adaptive Payments APIs.  Unfortunatly  using the Adaptive Payments API, PayPal redirect page is not designed for mobile, so it creates a bad user experience.  Googling the internet Zeo found an entry in  the x.commerce forum which talk about this issue.
Currently PayPal don't have a mobile version of Adaptive Checkout, we only provide a full web flow or the embedded checkout flow (lightbox). At the moment the only mobile web solution PayPal offers is Mobile Express Checkout. One alternative is to have the customers sign up to a preApproval, once they set that up you can charge their PayPal account without the need for a redirect to PayPal for each payment. The main issue again with  this is that there is no mobile friendly PreApproval flow.
In addition, after reviewing the source code developed by Zeo I realized that most of the application use the Paypal NVP HTTP API to interface the Express Checkout  and to adopt  the Paypalx GAE toolkit would require major changes to the application and would provide limited support for mobile devices.
The first issue Zeo faced, was an exception raised during the application start up.The problem was related to the xerces.jar included with the Paypal SDK. A solution was to remove the dependency changing the original code :
  1. protected static void setupHandler(final String message, DefaultHandler handler)
  2.             throws SAXException, IOException {
  3.    ClassLoader cl = APICallerBase.class.getClassLoader();
  4.    URL url = cl.getResource(message);
  5.    SAXParserFactory factory = SAXParserFactory.newInstance();
  6.    try {
  7.        SAXParser saxParser = factory.newSAXParser();
  8.        XMLReader xr = saxParser.getXMLReader();
  9.        xr.setContentHandler(handler);
  10.        xr.parse(new InputSource(url.openStream()));
  11.    } catch (ParserConfigurationException e) {
  12.        e.printStackTrace();
  13.    }
  14. }
  1. public void startElement(String uri, String xname, String name, Attributes atts) {
  2.     /* Do not change the original code */
  3. }
  4. public void endElement(String uri, String xname, String name) {
  5.     /* Do not change the original code */
  6. }

Basically it replaces the xerces parser with the SAX Parser included with the Java SDK, which removes the dependency from the library delivered with the Paypal SDK.  One less library that needs to be  included in the application WEB-INF/lib.
The second issue related to the  Google app engine  “Black List” , in other words, Java APIs that did not work on GAE. In this case the issue was related to the Apache Http Client dependency, because of the NVPClientSocket class. .
Also in this case, a solution was to remove the dependency from the Apache Http Library (what a shame) and use the Google App Engine's URL fetch service.
Below is the code:
  1. public final String call(String payload) throws PayPalException {
  2.    String nvpResponse = "";
  3.    URLFetchService fetcher = URLFetchServiceFactory.getURLFetchService();
  4.    try {
  5.       HTTPRequest request = new HTTPRequest(url, HTTPMethod.POST);
  6.       StringBuffer payloadData = new         StringBuffer(payload).append("&").append(header);
  7.    request.setPayload( payloadData.toString().getBytes() );
  8.    HTTPResponse response = fetcher.fetch(request);
  9.    byte[] content = response.getContent();
  10.    // 200, 404, 500, etc
  11.    int responseCode = response.getResponseCode();
  12.    if ( responseCode == HttpURLConnection.HTTP_OK) {
  13.        nvpResponse = new String(content);
  14.    } else {
  15.        throw new FatalException("HTTP Error code " + responseCode + " received, transaction not submitted");
  16.    }
  17.    } catch (IOException e) {
  18.        e.printStackTrace();
  19.    }
  20.    return nvpResponse;
  21. }

Another change, was to remove the and all the references to the org.apache.commons.httpclient.*  in the code. 
I packed the changes in a new paypal-base-gae.jar and sent to Zeo to test on the web store: it worked. I explained to Zeo this solution is a workaround, and does not pretend to be "the solution" to run the Paypal API on GAE. He promised me next time I will be in South California we will grab a burger at one of my favorite places Hodads.

Cheers !