// you’re reading...

Coder()

Interfacing to Google Checkout - Java 1.4, Linux (CentOS) - using the XML, Level 2, Alternative Method (programmatic) API - Part 1

I am posting this thread because I found it difficult to find some of this information and so thought it might eventually be useful to someone else in the Google Checkout trenches.Â

 The main link for information is: http://code.google.com/apis/checkout/

at that link the main page is: http://code.google.com/apis/checkout/developer/index.html

SETTING UP SANDBOX ACCOUNTSÂ

One of the first things ya gotta do is setup three google accounts - one real and one sandbox seller (merchant account) and one sandbox buyer. In order to differentiate the sand box accounts from each other and from your real one you need to use different email addresses when you set them up. Yeah, so that means three email addresses are required, since that is what you use to log in to these accounts. I used real email addresses - and don’t know if fake ones will work just as well. Your page for your real account will have a link on it to your sandbox account(s) login.

 The pertinent paragraph in the docs is:Â

“You need to create two test accounts in the Sandbox. One of these accounts will function as a customer account and the other will function as your merchant account. Please note that Google Checkout does not allow a single account to function as both the customer and the merchant for the same transaction. In addition, the links in the previous sentence point to different signup URLs, and you will be asked for different information to create these two accounts.”

This link to create the sandbox customer account did not work for me, perhaps because I created the sandbox merchant account first [long story, suffice it to say that I incorrectly used my ‘real’ email address to create my first sandbox account]. A link that DID work for me is: http://sandbox.google.com/checkout/buy

SETTING UP A CART and HANDING THE USER OVER TO THEIR GOOGLE ACCOUNT SO THAT THEY CAN MAKE PAYMENT

 Here is the code as it stands now. Seems to me that taking their sample code and modifying it, [i.e. the MerchantConstants file and the event handlers] which is what a lot of people do, is not the best idea. What if google changes things later? Ya want to keep rewriting this stuff? Who wants to spend the time to setup and incorporate the building of this code into their app? Anyway, the following code uses what they call their example code in a minimal fashion, and will probably not use it at all next revision.

I chose to use the Java 1.4 version, since I use Jikes [a very fast compiler]Â and that is stuck at 1.4 [looks like we will have to abandon Jikes unless someone steps in and updates it real soon now]. But the example code, all that I’ve been able to find, is in the jsp fiels in the top level directory of the jdk 1.5 version WAR file that google has on their downloads page: http://code.google.com/apis/checkout/samplecode.html

The other part of this code, the asynchronous callback handler that lets goggle send us messages to let us know how the checkout process went, will be posted at a future date (aka after it works).

We are using resin-pro 3.x and had a little bit of a hard time getting it to see all of the classes this code references in the jaxb libraries that the google checkout sample code uses. One thing that I did to make sure Resin found some of the classes at runtime was to rename xsdlib.jar to axsdlib.jar, so that it would be loaded before libs that reference it. Shades of LD_LIBRARY_PATH…

Part II will be forthcoming after CES sometime… as will more comments here… Strings is our class here that manages lists of class String. EVerything else is a google checkout class provided by their example libraries. So, anyway, here is an example of how to create a cart and send it, and the user, off to Goggle after polling it for an address to send them to.

  Â
import javax.xml.bind.JAXBException;
import com.sun.msv.datatype.xsd.DoubleType;

import com.google.checkout.sample.util.StringUtil;
import com.google.checkout.sample.protocol.CheckoutCartBuilder;

import com.google.checkout.schema._2.impl.AnyTypeImpl;
import com.google.checkout.schema._2.USStateArea;
import com.google.checkout.schema._2.Item;
import com.google.checkout.schema._2.ShoppingCart;
import com.google.checkout.schema._2.ShippingRestrictions;
import com.google.checkout.schema._2.DefaultTaxTable;
import com.google.checkout.schema._2.DefaultTaxRule;
import com.google.checkout.schema._2.MerchantCheckoutFlowSupport;
import com.google.checkout.schema._2.CheckoutRedirect;
import com.google.checkout.sample.crypto.CryptUtil;
//import com.google.checkout.sample.MerchantConstants;

import org.w3c.dom.Document;

import org.xml.sax.InputSource;
import javax.net.ssl.HttpsURLConnection;

/**----------------------------------------------------------------------------------------------
 * This class
 *
 * @author      Michael L. Davis
 *----------------------------------------------------------------------------------------------*/
public class NzGoogleCheckoutOperations implements NziBillingMethodWorkflow
	{
	private static	final String	sandboxMerchantId	= "xxxxxxxxxxxxxxxx";
	private static	final String	sandboxMerchantKey	= "xxxxxxxxxxxxxxxxxxxxxxx";
	private static	final String	sandboxRequestUrl	= "https://sandbox.google.com/cws/v2/Merchant/" + sandboxMerchantId + "/request";
	private static 	final String	sandboxCheckoutButtonUrl= "http://sandbox.google.com/buttons/checkout.gif?merchant_id=" + sandboxMerchantId;		 // "http://sandbox.google.com/checkout/" after 1/3/07
	private static	final String	merchantId		= "xxxxxxxxxxxxxxxxxxxxx";
	private static	final String	merchantKey		= "xxxxxxxxxxxxxxxxxxxxxxxx";
	private static	final String	requestUrl		= "https://checkout.google.com/cws/v2/Merchant/" + merchantId + "/request";
	private static 	final String	checkoutButtonUrl	= "http://checkout.google.com/buttons/checkout.gif?merchant_id=" + merchantId;

	public				NzGoogleCheckoutOperations()
		{
		}

	public static	String		getMerchantID()
		{
		if (Utility.toBoolean(NzConfigurationResources.get(NziConstants.NZ_GOOGLE_CHECKOUT_USING_SANDBOX)))
			{
			return(sandboxMerchantId);
			}
		return(merchantId);
		}
	private static	String		getMerchantKey()
		{
		if (Utility.toBoolean(NzConfigurationResources.get(NziConstants.NZ_GOOGLE_CHECKOUT_USING_SANDBOX)))
			{
			return(sandboxMerchantKey);
			}
		return(merchantKey);
		}
	private static	String		getRequestUrl()
		{
		if (Utility.toBoolean(NzConfigurationResources.get(NziConstants.NZ_GOOGLE_CHECKOUT_USING_SANDBOX)))
			{
			return(sandboxRequestUrl);
			}
		return(requestUrl);
		}
	public static	String		getCheckoutButtonUrl()
		{
		if (Utility.toBoolean(NzConfigurationResources.get(NziConstants.NZ_GOOGLE_CHECKOUT_USING_SANDBOX)))
			{
			return(sandboxCheckoutButtonUrl);
			}
		return(checkoutButtonUrl);
		}
	public static 	String 		getHttpAuth()
		{
		if (Utility.toBoolean(NzConfigurationResources.get(NziConstants.NZ_GOOGLE_CHECKOUT_USING_SANDBOX)))
			{
			String combinedKey = sandboxMerchantId + ":" + sandboxMerchantKey;
			return(CryptUtil.base64Encode(combinedKey));
			}
		String combinedKey = merchantId + ":" + merchantKey;
		return(CryptUtil.base64Encode(combinedKey));
		}

	public 		boolean		billCustomer(
						String accountId,
						String pendindOrderId,
						Strings orderItemIds,
						String cost,
						String taxAmount,
						String discount,
						String totalAmount,
						Strings itemizedCosts,
						Strings itemizedCostsDescriptions,
						Strings itemizedDiscountIds,
						Strings itemizedDiscounts,
						Strings itemizedDiscountsDescriptions,
						String customerReference,
						String generalDescription,
						Strings errors,
						HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException
		{

		// Google
		String[] signature = new String[1];
		String cart = null;

		try	{
			cart = buildEncodedCart(
						accountId,
						pendindOrderId,
						orderItemIds,
						cost,
						taxAmount + "",
						discount,
						totalAmount,
						itemizedCosts,
						itemizedCostsDescriptions,
						itemizedDiscountIds,
						itemizedDiscounts,
						itemizedDiscountsDescriptions,
						customerReference);
			}
		catch (Throwable t)
			{
			NzLog.logException("Unable to build google cart for account: " + accountId, t);
			}

		try	{
			URL url = new URL(getRequestUrl()); 

			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); 

			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setRequestMethod("POST");
			conn.setRequestProperty("Authorization","Basic " + getHttpAuth());
			conn.setRequestProperty("Content-Type","application/xml");
                	conn.setRequestProperty("Accept","application/xml");
			conn.setRequestProperty("Host", conn.getURL().getHost());
			conn.setUseCaches (false);
			conn.setInstanceFollowRedirects(true);
			conn.connect(); 

			OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
			wr.write(cart);
			wr.flush();
			//wr.close(); 

			CheckoutRedirect redirect = null;
			try	{
				String responseCode = "" + conn.getResponseCode();
				String responseMessage = "" + conn.getResponseMessage();

				//Get the response
				InputSource source = new InputSource(conn.getInputStream());
				redirect = (CheckoutRedirect)CheckoutCartBuilder.getInstance().parseToJAXB(source); 

				response.setStatus(302);
				response.sendRedirect(redirect.getRedirectUrl()); 

				return(true);
				}
			catch (Throwable t)
				{
				t.printStackTrace();
				NzLog.logException("Unable to redirect to google cart for account: " + accountId + ", to address: " + (redirect != null ? redirect.getRedirectUrl() : "null"), t);
				}
			}
		catch (Throwable t)
			{
			NzLog.logException("Unable to send request for google cart address message for account: " + accountId, t);
			}
		return(false);
		}

	/**
	 * Does this billing handler handle checkout synchronously, or must we wait for
	 * some kind of asynchronous message indicating the success of failure of the
	 & checkout proocess?
	 **/
	public		boolean		isSynchronous()
		{
		return(false);
		}

	/**
	 * There is a little example code for building carts, but for
	 * alternative 1, in the jsp files found  in the top
	 * diretory of the example War file for java 1.5 on
	 * google's site
	 **/
	private		String		buildEncodedCart(
						String accountId,
						String pendingOrderId,
						Strings orderItemIds,
						String cost,
						String taxAmount,
						String discount,
						String totalAmount,
						Strings itemizedCosts,
						Strings itemizedCostsDescriptions,
						Strings itemizedDiscountIds,
						Strings itemizedDiscounts,
						Strings itemizedDiscountsDescriptions,
						String customerReference) throws JAXBException, Exception
		{
		String mKey = getMerchantKey();
  		String mId = getMerchantID();

		CheckoutCartBuilder pBuilder = null;
		pBuilder = CheckoutCartBuilder.getInstance();

		List itemList = new ArrayList();
		for (int i = 0; i < itemizedCosts.size(); ++i)
			{
			Item item = pBuilder.createShoppingItem(itemizedCostsDescriptions.get(i), NzOrderItemOperations.getOrderItemDescription(orderItemIds.get(i)), 1, Utility.toFloat(itemizedCosts.get(i)), null, null, null);
			itemList.add(item);
			}
		for (int i = 0; i < itemizedDiscounts.size(); ++i)
			{
			Item item = pBuilder.createShoppingItem(itemizedDiscountIds.get(i), itemizedDiscountsDescriptions.get(i), 1, -Utility.toFloat(itemizedDiscounts.get(i)), null, null, null);
			itemList.add(item);
			}

		ShoppingCart cart = pBuilder.createShoppingCart(itemList, null, null);

		// Unfortunately there is no programmatic way to create an empty MerchantCheckoutFlowSupport
		// taxTable = null;
		USStateArea stateArea = pBuilder.createStateArea("CO");
		DefaultTaxRule taxRule = pBuilder.createDefaultTaxRule(stateArea, 0.0F, false);
		List ruleList = new ArrayList();
		ruleList.add(taxRule);
		DefaultTaxTable taxTable = pBuilder.createDefaultTaxTable(ruleList);

		MerchantCheckoutFlowSupport flowSupport = pBuilder.createMerchantSupport(null, taxTable, null, null, null, null);

		flowSupport.setContinueShoppingUrl("http://" + NzConfigurationResources.get(NziConstants.NZ_APPLICATION_DOMAIN_NAME) + "/nav?action=portfolio-statements");

		AnyTypeImpl pendingOrderIdHolder = new AnyTypeImpl();
		pendingOrderIdHolder.setAny(pendingOrderId);
		cart.setMerchantPrivateData(pendingOrderIdHolder);

		Document cartDom = pBuilder.createCheckoutCart(cart, flowSupport);
		String clearXml = pBuilder.unmarshal(cartDom);

		return(clearXml);
		}

	}

Discussion

No comments for “Interfacing to Google Checkout - Java 1.4, Linux (CentOS) - using the XML, Level 2, Alternative Method (programmatic) API - Part 1”

Post a comment

You must be logged in to post a comment.

Categories