# Monday, November 29, 2010

In May of 2010, Google released a new 2.5 version of the Google Checkout API.  The most compelling feature of this version of the API is that it no longer requires https/SSL to retrieve notifications.  They didn't update the .NET sample code to show how to use the new API though (it's now November, so it's been six months).

Below I have not only included the missing sample code for notification handling with GCO 2.5, but I did it using ASP.NET MVC instead of ASP.NET Classic WebForms.  I believe this is the first published Google Checkout ASP.NET MVC sample.

You will want to check the Integration Console in the Google Checkout Sandbox website constantly while testing.  Some errors will only show up there.  You will also want to catch and log errors somewhere like a database, which the below sample does not do.  You may also want to send yourself an e-mail when an order comes through.  You'll have to code that feature yourself, it's not too difficult.


First, Download the Google Checkout .NET DLLs (at least version 2.5.0.3, published 10-16-2010)
Unzip the Google Checkout .NET DLLs and put them somewhere you can find them later


Next, Start Visual Studio 2008

File -> New -> Project
 Visual C# -> Web -> ASP.NET MVC Web Application
  GcoNotifHandlerForSandbox
  OK
 No, do not create a unit test project
  OK

In Solution Explorer -> Right Click References -> Add Reference...
 Select the "GCheckout.dll" you downloaded and unzipped above
 OK

Open web.config for editing:
 Replace "<appSettings/>" with:

<appSettings>
  <
add key="GoogleMerchantID" value="YourMerchantID" />
  <
add key="GoogleMerchantKey" value="YourMerchantKey" />
  <
add key="GoogleEnvironment" value="Sandbox" />
</
appSettings>

Be sure to substitute in your proper sandbox merchant ID and key.

In Solution Explorer -> Right Click Controllers -> Add -> Controller...
 Controller Name: GcoNotifHandlerController
 Add

Open Global.asax.cs for editing:
 Add a new route before the default route:

routes.MapRoute(
  "GcoNotifHandler",
  "{controller}/{action}/{id}",
  new { controller = "GcoNotifHandler", action = "Index", id = "" }
);

In Solution Explorer -> Right Click Models -> Add -> Class...
 Name: GoogleCheckoutHelper.cs
 Add

Add the following new static methods to GoogleCheckoutHelper:

public static string GetGoogleOrderNumber(string serialNumber)
{
  return serialNumber.Substring(0, serialNumber.IndexOf('-'));
}

private static void HandleAuthorizationAmountNotification(GCheckout.AutoGen.AuthorizationAmountNotification inputAuthorizationAmountNotification)
{
  // TODO: Add custom processing for this notification type
}

private static void HandleChargeAmountNotification(GCheckout.AutoGen.ChargeAmountNotification inputChargeAmountNotification)
{
  // TODO: Add custom processing for this notification type
}

private static void HandleNewOrderNotification(GCheckout.AutoGen.NewOrderNotification inputNewOrderNotification)
{
  // Retrieve data from MerchantPrivateData
  GCheckout.AutoGen.anyMultiple oneAnyMultiple = inputNewOrderNotification.shoppingcart.merchantprivatedata;
  System.Xml.
XmlNode[] oneXmlNodeArray = oneAnyMultiple.Any;
  string hiddenMerchantPrivateData = oneXmlNodeArray[0].InnerText;
  // TODO: Process the MerchantPrivateData if provided

  foreach (GCheckout.AutoGen.Item oneItem in inputNewOrderNotification.shoppingcart.items)
  {
    // TODO: Get MerchantItemId from shopping cart item (oneItem.merchantitemid) and process it
  }

  // TODO: Add custom processing for this notification type
}

private static void HandleOrderStateChangeNotification(GCheckout.AutoGen.OrderStateChangeNotification inputOrderStateChangeNotification)
{
  // Charge Order If Chargeable
  if ((inputOrderStateChangeNotification.previousfinancialorderstate.ToString().Equals("REVIEWING")) && (inputOrderStateChangeNotification.newfinancialorderstate.ToString().Equals("CHARGEABLE")))
  {
    GCheckout.OrderProcessing.
ChargeOrderRequest oneChargeOrderRequest = new GCheckout.OrderProcessing.ChargeOrderRequest(inputOrderStateChangeNotification.googleordernumber);
    GCheckout.Util.
GCheckoutResponse oneGCheckoutResponse = oneChargeOrderRequest.Send();
  }

  // Update License If Charged
  if ((inputOrderStateChangeNotification.previousfinancialorderstate.ToString().Equals("CHARGING")) && (inputOrderStateChangeNotification.newfinancialorderstate.ToString().Equals("CHARGED")))
  {
    // TODO: For each shopping cart item received in the NewOrderNotification, authorize the license
  }

  // TODO: Add custom processing for this notification type
}

private static void HandleRiskInformationNotification(GCheckout.AutoGen.RiskInformationNotification inputRiskInformationNotification)
{
  // TODO: Add custom processing for this notification type
}

public static void ProcessNotification(string serialNumber)
{
  string googleOrderNumber = GetGoogleOrderNumber(serialNumber);

  List<string> listOfGoogleOrderNumbers = new List<string> { googleOrderNumber };

  GCheckout.OrderProcessing.NotificationHistoryRequest oneNotificationHistoryRequest = new GCheckout.OrderProcessing.NotificationHistoryRequest(listOfGoogleOrderNumbers);

  GCheckout.OrderProcessing.NotificationHistoryResponse oneNotificationHistoryResponse = (GCheckout.OrderProcessing.NotificationHistoryResponse)oneNotificationHistoryRequest.Send();

  // oneNotificationHistoryResponse.ResponseXml contains the complete response

  // Iterate through the notification history for this order looking for the notification that exactly matches the given serial number
  foreach (object oneNotification in oneNotificationHistoryResponse.NotificationResponses)
  {
    if (oneNotification.GetType().Equals(typeof(GCheckout.AutoGen.NewOrderNotification)))
    {
      GCheckout.AutoGen.
NewOrderNotification oneNewOrderNotification = (GCheckout.AutoGen.NewOrderNotification)oneNotification;
      if (oneNewOrderNotification.serialnumber.Equals(serialNumber))
      {
        HandleNewOrderNotification(oneNewOrderNotification);
      }
    }
    else if (oneNotification.GetType().Equals(typeof(GCheckout.AutoGen.OrderStateChangeNotification)))
    {
      GCheckout.AutoGen.
OrderStateChangeNotification oneOrderStateChangeNotification = (GCheckout.AutoGen.OrderStateChangeNotification)oneNotification;
      if (oneOrderStateChangeNotification.serialnumber.Equals(serialNumber))
      {
        HandleOrderStateChangeNotification(oneOrderStateChangeNotification);
      }
    }
    else if (oneNotification.GetType().Equals(typeof(GCheckout.AutoGen.RiskInformationNotification)))
    {
      GCheckout.AutoGen.
RiskInformationNotification oneRiskInformationNotification = (GCheckout.AutoGen.RiskInformationNotification)oneNotification;
      if (oneRiskInformationNotification.serialnumber.Equals(serialNumber))
      {
        HandleRiskInformationNotification(oneRiskInformationNotification);
      }
    }
    else if (oneNotification.GetType().Equals(typeof(GCheckout.AutoGen.AuthorizationAmountNotification)))
    {
      GCheckout.AutoGen.
AuthorizationAmountNotification oneAuthorizationAmountNotification = (GCheckout.AutoGen.AuthorizationAmountNotification)oneNotification;
      if (oneAuthorizationAmountNotification.serialnumber.Equals(serialNumber))
      {
        HandleAuthorizationAmountNotification(oneAuthorizationAmountNotification);
      }
    }
    else if (oneNotification.GetType().Equals(typeof(GCheckout.AutoGen.ChargeAmountNotification)))
    {
      GCheckout.AutoGen.
ChargeAmountNotification oneChargeAmountNotification = (GCheckout.AutoGen.ChargeAmountNotification)oneNotification;
      if (oneChargeAmountNotification.serialnumber.Equals(serialNumber))
      {
        HandleChargeAmountNotification(oneChargeAmountNotification);
      }
    }
    else
    {
      throw new ArgumentOutOfRangeException("Unhandled Type [" + oneNotification.GetType().ToString() + "]!; serialNumber=[" + serialNumber + "];");
    }
  }
}


Open Controllers\GcoNotifHandlerController.cs for editing:
 Replace the Index method with:

public ActionResult Index()
{
  string serialNumber = null;

  // Receive Request
  System.IO.Stream requestInputStream = Request.InputStream;
  string requestStreamAsString = null;
  using (System.IO.StreamReader oneStreamReader = new System.IO.StreamReader(requestInputStream))
  {
    requestStreamAsString = oneStreamReader.ReadToEnd();
  }

  // Parse Request to retreive serial number
  string[] requestStreamAsParts = requestStreamAsString.Split(new char[] { '=' });
  if (requestStreamAsParts.Length >= 2)
  {
    serialNumber = requestStreamAsParts[1];
  }

  // Call NotificationHistory Google Checkout API to retrieve the notification for the given serial number and process the notification
  GcoNotifHandlerForSandbox.Models.GoogleCheckoutHelper.ProcessNotification(serialNumber);

  string notifAckBegin = "<notification-acknowledgment xmlns=\"http://checkout.google.com/schema/2\"";
  string notifAckSerialNumAttrBegin = " serial-number=\"";
  string notifAckSerialNumAttrEnd = "\"";
  string notifAckEnd = " />";
  return this.Content(notifAckBegin + notifAckSerialNumAttrBegin + serialNumber + notifAckSerialNumAttrEnd + notifAckEnd);
}


That's all there is for building the basic notification handling web service.

You need to publish your notification handling web service to a public website and configure your Google Checkout sandbox account (at https://sandbox.google.com/checkout/sell/ -> Settings -> Integration -> API Callback URL) to use the URL of your web service (http://www.<yourdomain>.com/GcoNotifHandlerForSandbox/).

The Callback contents should be set to "Notification Serial Number" and the API Version should be set to "Version 2.5".


To test your notification web service, you'll need a website with a Google Checkout button.  Instructions for building that are available here:

http://code.google.com/apis/checkout/samples/Google_Checkout_Sample_Code_NET.html


(Note: I have successfully tested this with both ASP.NET MVC 1 & 2 for Visual Studio 2008.)


Good luck!