Stay out of trouble! Backup your files with RoboCopy

robocopyApologies for making a reference from the social-satire/sci-fi film that is RoboCop (1987) in my post title. It just had to be done when talking about some tool called RoboCopy. For those who aren’t aware of what RoboCopy is, where have you been? In all honesty, I myself never heard of it until a few days ago.

RoboCopy is a command-line run tool that allows you to copy files from one directory to another. One of its most popular uses for RoboCopy is it’s ability to copy large volumes of files quicker than carrying out a manual copy and paste through a GUI, making it ideal for backup jobs. So you could easily write a backup script to run via a Schedule Task on a daily basis.

I managed to backup around 80Gb of files in less than an hour. What’s even more impressive is that I could run numerous RoboCopy scripts at the same time. Currently, I have only run two scripts simultaneously just to be on the safe side.

Prior to RoboCopy, I was using another command-line tool: XCopy. For my backup purposes XCopy did exactly what I wanted it to do until I came across a misleading error message: “Insufficient memory”. You would think this message would mean the destination directory to where your files are copying to is full or not enough memory resources. In matter of fact this error only happens when the fully qualified file path is longer than 254 characters. Unfortunately, I couldn’t get around this error due to the nature of how my directories are structured. Luckily, RoboCopy doesn’t have this limitation.

One of the major strength’s of RoboCopy is the number of useful options you have at your disposal. A few example’s are:

  • Moving files.
  • Exclude certain files and file types.
  • Detailed logging that tells you new the files that have been copied or over-written.
  • Parameterised scripting.

Example Script

@ECHO OFF

ECHO PROCESSING BACKUP ...

robocopy \\work\Projects\ F:\Projects\Backup\ /mir /sl
/log:"F:\Logs\Projects-%date:/=%.log"

ECHO BACKUP COMPLETED!

The script I have provided is what I use to backup files through a Schedule Task that runs at the end of every day. This script mirrors the source drive exactly. So any files that have been deleted, updated or created will have the same effect on the backup drive. In addition, a log file is created when RoboCopy is running.

More Information

Time for a new chapter in my online presence

Moving blogAfter blogging under the “computing-studio.com” domain name for around 4 years, I think its time for a new chapter in my online presence. Last Friday I decided to buy a new domain name called http://surinder.me. At the time “computing-studio.com” domain seemed like a great idea where me and my fellow techy University friends would contribute. Unfortunately, things didn’t work out and decided to go it alone.

From looking at the number of blog posts I have written (95 at the time of writing), you would be forgiven to make the assumption that I am not the most persistent blogger. I believe the domain has a part to play. After all, “computing-studio.com” somewhat limits what I can write and doesn’t really give me the freedom to talk about things outside my technical field.

Even though I am a techy at heart (I guess being a web developer doesn’t help), I talk about other things non-code related through my Google+ and Twitter posts. I see having a new domain name is just the start. I am hoping to collate all my contributions from sites under the surinder.me address. So everything is about…well…me!

All exciting stuff! I am not looking forward to implementing all the redirects and having to work my way up Google’s page rank again. But its something that has to be done.

Watch this space!

Resolving AddThis Problems When Using Selectivizr

I found there is an issue when implementing AddThis to a site that uses Selectivizr. Selectivizr (for those who don't know), is a JavaScript utility that emulates CSS3 pseudo-classes and attribute selectors for Internet Explorer 6-8.

I noticed that my AddThis widget was not functioning correctly in Internet Explorer versions 6-8. So whenever Selectivizr was required, I encountered two issues:

  • Social bookmark buttons were not displaying. AddThis Issue No Icons
  • The AddThis popup to select more social bookmarking sites did not render correctly. It outputs all the popup contents to the bottom of the page. AddThis Issue Popup

Just as I have experienced two problems with AddThis, there are two ways to resolve:

1) Use custom buttons with AddThis

Using custom buttons will get AddThis to display properly in your page. However, you will still experience viewing AddThis popup. So if you are not too fussed with viewing additional social bookmarking sites, then this will suffice.

<!-- AddThis Button BEGIN -->
<div class="addthis_toolbox addthis_default_style ">
<a class="addthis_button_facebook"><img src="/images/facebook.png" height="16" width="16" /></a>
<a class="addthis_button_twitter"><img src="/images/twitter.png" height="16" width="16" /></a>
<a class="addthis_button_email"><img src="/images/email.png" height="16" width="16" /></a>
<a class="addthis_button_reddit"><img src="/images/reddit.png" height="16" width="16" /></a>
</div>
<script type="text/javascript" src="http://s7.addthis.com/js/250/addthis_widget.js#pubid=xa-4f0b42500b47b860"></script>
<!-- AddThis Button END -->

2) Modify Script tag in AddThis code snippet

To get AddThis to work 100% alongside Selectivizr will require you to modify the script tag and add bit of jQuery. A user on Google Groups suggested this fix and resolved my issues.

<!-- AddThis Button BEGIN -->
<div class="addthis_toolbox addthis_default_style ">
<a class="addthis_button_preferred_1"></a>
<a class="addthis_button_preferred_2"></a>
<a class="addthis_button_preferred_3"></a>
<a class="addthis_button_preferred_4"></a>
<a class="addthis_button_compact"></a>
<a class="addthis_counter addthis_bubble_style"></a>
</div>

<script type="text/javascript">
$(function()
{
    $('head').append('<script type="text/javascript" src="http://s7.addthis.com/js/250/addthis_widget.js#pubid=xa-4f0b42500b47b860"><\/script>');
}); 
</script>
<!-- AddThis Button END -->

Solving Flash Issues in Isotope jQuery Plugin

I’m using a jQuery plugin called “Isotope” to nicely output a mixture of news articles and advertising banners to a page.

I came across a small issue when using advertising banner’s in Flash format. For some reason, Flash content displayed randomly and mouse-clicks were not registered. This issue only seemed to only occur in Firefox. I couldn’t replicate this issue on other browsers.

Thankfully, only one additional line of code  needed to be added when when initially setting the Isotope plugin options:

$('#wall').isotope({
    itemSelector: '.box',
    animationEngine: 'css,
    layoutMode: 'masonry',
    transformsEnabled: false //Disable transformations
});

The following example helped me further in resolving my issue: http://isotope.metafizzy.co/tests/flash.html

Android + Phone Manufacturers = Bad Combination

On Saturday 6th August 2011, I was waiting impatiently for my new phone to be delivered. It’s not just any phone. It’s the phone that will technologically enhance my productivity and will define what an Android operating system really has to offer in a small, slim and neat package. It was the Samsung Galaxy S2!

I had been looking for a new phone for quite sometime to replace my “slow-mo” HTC Hero and the Samsung Galaxy S2 seemed to fit my requirements exactly. One of the things that attracted me to buying this phone was the fact that compared to other phones on the market, the Samsung was a performance powerhouse packing in a dual core processor and a lot of RAM that will future-proof my purchase for some years to come. So spending around £400 didn’t phase me.

During this time, there was talk on the internet on how the 2012 release of Android 4.0 will not just be another Android release, it will be a release to remember. We all know Android has been fraught with fragmentation issues throughout its life due to phone manufacturer’s adding their own customisations thus adding further complexity in future Android upgrades. By starting over in a major new release, consumers will see more similarities between low and high spec handsets.

Knowing I have a high-spec handset I was sure I will be getting Android 4.0.

How wrong could I be…

I was really annoyed to hear just before Christmas that Samsung will not be releasing Android 4.0 to its original Galaxy S phones and tabs. Even though Samsung Nexus S has similar hardware specification to my Samsung Galaxy S2, there is not enough memory available on the device to install Ice Cream Sandwich along with TouchWiz. Who actually wants TouchWiz? It’s an ugly interface. Android is a perfectly acceptable out-of-the-box. Long gone are the days when Android was a bit of an eyesore.

The problem with Android is that its allows too much freedom allowing hardware manufacturers to do what they want. Having all this freedom in the initial stages is great, but there are bound to be complications as future upgrade will require a lot of time and money to get right. No hardware manufacturer would want to do that.

So when will phone manufacturer’s STOP bastardising Android with their own customisations? No good can come of it. This is what really peeves me off about being an Android user. Its phone manufacturer’s like Samsung that are playing with customer loyalty. I think Samsung is not realising their business incentive to do these updates.

Up until now, I’ve been a happy Android user and was proud to be different to the iPhone drones that surrounded me. Now I’m uncertain. Just as uncertain (I believe) the future of Android. I really like my Samsung Galaxy and it does everything I require. I can even make phone calls! But it’s a real kick in the teeth knowing I spent so much on a phone that is not upgradeable especially when its less than 2 years old.

Google should really take more of an initiative. The following come to mind:

  1. Lock down Android to stop unnecessary modifications.
  2. Google take the Apple route and manufacture their own hardware.
  3. Make the latest OS available online for consumers to download and install on their phone.
  4. Allow manufacturer’s to make modifications through a separate driver layer. So consumer can ditch manufacturer customisations with ease.

Smartphone Operating System Market Nov 2011

According to Gartner, Android has now taken over 52.5% of the global smartphone market and stealing share from smartphone operating systems such as Symbian and Blackberry. With this in mind Google and its handset makers should be rewarding its customers and not penalising them. As John Gruber (http://daringfireball.net/) states in one of his posts:

Why bother with software updates? We’ve got their money. Let them buy a new device if they want the latest software.

I am afraid to say that this is the mentality of some Android handset makers where customer satisfaction is a low priority. I hate to say this is where Apple wins. They clearly have more respect for its customers and the longevity of their products.

I wonder what 2012 will have in store for Android. No doubt the Android market momentum will continue to rise at the cost of consumers. I will definitely think twice before purchasing another Android handset.

Workaround To Setting A “cmi.interaction.n.student_response” Reference

One the drawbacks of using SCORM 1.2 is the inability of being able to read a “cmi.interaction.n.student_response” reference. In my mind this is very strange. Why allow a value to be written to but not read? Being able to read a users response to a question is an important feature. If anyone knows the answer to why this is the case, then please leave a comment.

I guess under normal circumstances having a student_response reference that is write only would suffice. Unfortunately, my SCORM content required user’s to review all questions along with their submitted answers.

Even though SCORM 2004 has corrected its previous error of misjudgement, what are developers who are forced into using SCORM 1.2 (like me) to do? Thankfully, there is a really useful reference called “cmi.suspend_data” that allows us to store any string value we want (up to 4096 characters). This is what I will use to store all users responses. I created a “semi-colon delimited string” to parse into the suspend_data reference. For example:

q1=a;q2=1,4,7;q3=d;q4=air;q5=c

The following code can be used to add/update values within the suspend_data:

/*******************************************************************************
**
** Functions to Add/Update CMI.suspend_data field
**
*******************************************************************************/
function EditSuspendData(suspendId, suspendValue) {
    var suspendData = doGetValue("cmi.suspend_data");

    if (!SuspendDataExists(suspendId)) {
        AddSuspendData(suspendId, suspendValue, suspendData);
    }
    else {
        UpdateSuspendData(suspendId, suspendValue, suspendData);
    }
}

function AddSuspendData(suspendId, suspendValue, sdList) {
    if (sdList == null || sdList.length == 0) {
        sdList = suspendId + "=" + suspendValue;
    }
    else {
        sdList += ";" + suspendId + "=" + suspendValue;
    }

    doSetValue("cmi.suspend_data", sdList);
}

function UpdateSuspendData(suspendId, suspendValue, sdList) {
    var sdArr = sdList.split(';');

    for (i = 0; i < sdArr.length; i++) {
        pieces = sdArr[i].split('=');
        if (pieces[0] == suspendId) {
            pieces[1] = suspendValue;
            sdArr[i] = pieces[0] + '=' + pieces[1];
            break;
        }
    }

    //put the string back together;
    var sdList = '';

    for (i = 0; i < sdArr.length; i++) {
        marker = (i == 0) ? '' : ';';
        sdList += marker + sdArr[i];
    }

    doSetValue("cmi.suspend_data", sdList);
}

function GetSuspendDataValue(suspendId) {
    var sdArr = doGetValue("cmi.suspend_data").split(';');

    var answer = "";

    if (sdArr != "") {
        for (i = 0; i < sdArr.length; i++) {
            var qPieces = sdArr[i].split('=');

            if (qPieces[0] == suspendId) {
                answer = qPieces[1];

                NavButtonInactive("submit-button", false);
                break;
            }
        }
    }

    return answer;
}

function SuspendDataExists(suspendId) {
    var sdArr = doGetValue("cmi.suspend_data").split(';');

    var sdFound = false;

    for (i = 0; i < sdArr.length; i++) {
        var qPieces = sdArr[i].split('=');

        if (qPieces[0] == suspendId) {
            sdFound = true;
            break;
        }
    }

    return sdFound;
}

You can now easily add/update/get your question values or any other values you store in your suspend_data. The functions you will need to use are:

  • GetSuspendDataValue() – to retrieve a value.
  • EditSuspendData() – to add/update a value.

SCORM - Point of View From A Novice

Being a developer, I am always open to learning new platforms (to a point) and coding languages. I am currently involved in a project with one key requirement: build a web-based e-learning system. Sounds simple enough. First thing that came to my mind was to build this online learning platform in ASP.NET. However, to keep in line with the clients current method of learning, the build will have to be carried out using a SCORM platform.

What Is SCORM?

There are many articles on the web that discusses what is SCORM. Since being my first post on the subject, it seems like a good idea to take a high-level view of this platform. Wikipedia defines it as:

“A collection of standards and specifications for web-based e-learning. It defines communications between client side content and a host system called the run-time environment, which is commonly supported by a learning management system (LMS).”

I interpret SCORM as being a compliant way to build reusable e-learning objects, re-assuring organisations that new courses will run with existing courses already developed on their system. SCORM is important because there are so many people developing e-learning and deploying it on different systems that ensuring compatibility between these systems and courseware is vital. This is SCORM!

All custom built SCORM content sits in a piece of software called an Learning Management System (LMS) that administrates, documents, tracks and reports all training programmes. Once a course is built, user’s will log into the LMS and take it.

Where Do I Start?

As the title of this post suggests,  I’m a newcomer to the world of SCORM and building my first course wasn’t short of a few obstacles from choosing a development tool to the know-how on creating the content. One of the most difficult aspects of building custom SCORM content is the lack of online information.

There seems to be many people asking questions but not many SCORM experts to answer them. Your best luck in getting the basic know-how is to search various sites and forums. One of the best sites I found is http://www.scorm.com. The guys on this site really know what they are doing and are the best SCORM experts out there. Dare I say they probably know more about SCORM than the original creators.

Building Your First SCORM Package

  1. Look through all examples to get a technical understanding on the basic principles of SCORM. You may not 100% understand what is going on. But these examples are an invaluable source for any SCORM novice. You can even re-use these examples to build your own custom SCORM content.
  2. Use a SCORM player to run packaged content you create. The best one I’ve used is SCORM Cloud. It provides an ideal environment to ensure all your custom content works correctly. What’s even better, the service is free for basic usage. Click here to signup and read more on what SCORM Cloud has to offer.
  3. Download a SCORM package template containing manifest and schema definition files. Without these, your content will not work.
  4. Get an understanding of SCORM Runtime references.
  5. Development tool – Even though there are e-learning development tools available online, you probably won’t need them. Personally, I found the development tools more of a hindrance than an asset. If you do not plan on learning how to build SCORM content from scratch or not too fussed on the layout, then there are many WYSIWYG tools at your disposal. Be warned, it’s more than likely these WYSIWYG tools render your content in Flash or dirty HTML. I use Visual Studio or Programmers Notepad, giving me the added benefit of having total control over how I want my SCORM content to look and feel.

Count Message board Messages for a Document in Kentico

I’ve been using Message Boards for some page templates within my Kentico site. I needed to count the number of messages for each document to be displayed on the homepage. I couldn’t find a way to do this using Kentico’s API.

The only way I could retrieve the message count for a document is to query the database directly. The SQL query is as follows.

SELECT COUNT(*)
FROM Board_Board
JOIN Board_Message ON Board_board.BoardID = Board_Message.MessageBoardID
WHERE 
Board_Message.MessageApproved = 1 AND Board_Board.BoardDocumentID = @DocumentID

 

If anyone knows how to achieve the exact same thing through the API, please leave a comment.

CSS Link Hover Issue in Internet Explorer 7 & 8

I came across a really strange issue yesterday whilst testing a site build in Internet Explorer 6, 7 and 8. For some reason, my anchor link text was not accepting a hover state colour change even though I set the required styles within my style sheet. All other browser accepted the hover styling without any issue.

I decided to create another site in Visual Studio and added the same styling to my links and it worked. After comparing the difference between the original and new style sheet, the only difference was:

/* Original Stylesheet */
body
{
    font-family: Arial;
}
/* New Stylesheet */
body
{
    font-family: Arial, sans-serif;
}

By simply adding “sans-serif” to the font family allowed the hover styles to work correctly in all browsers. I have no idea why making this change resolved my issue.

Google Checkout - Callback Notifications Example

I stated in my last post that when I got better knowledge of using Google Checkout, I will show a full example on how to implement Google’s payment provider.

What I wanted to achieve within my own Google Checkout implementation was the ability to ensure I was retrieving sufficient information from the payment provider. So the transaction data stored within my own database would match exactly what is happening within Google. For example, if a payment was accepted/declined some internal processes need to be carried out. The best way of achieving this was by using a call-back page to process notifications from Google synchronously.

Before we get into the call-back code, we need to login to our Google Checkout account as a merchant and carry out some basic configuration setup. My example’s will be based on a sandbox environment.

Google Account Setup

Integration (Settings > Integration)

The Integration configuration is the most important page since it contains the basis to how we would like Google Checkout to work.

Google Checkout - Integration

As you can see, we have specified a call-back URL that will point to a page in our site. The call-back page is what Google uses to let our site know what’s happening with our transactions and if required, we can create our own internal actions to deal with events that are thrown to us. I have set the notifications to be sent in XML format.

Next, we want to ensure the correct API version is used. Issues are bound to occur is the incorrect version is selected. In this case, I am using the most recent API version: 2.5.

I have unselected Notification Filtering because we want to receive all types of notifications. Google Checkout document states:

Google Checkout responds with a <charge-amount-notification> synchronously after a charge-order request. If you have not selected the option to only receive new order notifications, authorization amount notifications and order state change notifications, Google Checkout will also send you a charge-amount-notification anytime an order has been charged.”

We want to receive a “charge amount notification” to allow us to know if the transaction was charged successfully. This is an actual confirmation to state we have received the money. We shouldn’t assume anything until this is received.

Preferences (Settings > Preferences)

Google Checkout - Preferences

I have setup our order processing to automatically authorise the buyers card for an order. You might be thinking: “This is a bit of manual process…” and you’d be right. If we selected the second option to automatically authorise and charge the buyers card, this would leave a big hole in setting up our internal website processes. By authorising a buyers card gives us more flexibility on how to handle the transaction.

It’s not compulsory for the email option to be selected.

Other Setup

You should be good to go on starting to mess around with implementing your site with Google Checkout. What I haven’t covered is the financial settings such as adding Banking information. This should be a straight-forward process. My main aim is to make sure the basics of Google Merchant account setup is complete.

One really useful page that will help you with investigating problematic transactions is the “Integration Console” (Tools > Integration Console). Generally, this should be empty but any issues will be logged here. This has helped me over the course of integrating Google Checkout in my site.

Call-back Page

Ok. We have Google Merchant Account setup. Now we need to concentrate on our call-back page. My call-back page is based on a customer purchasing a News subscription. We are going to capture all the data Google sends us (excluding refunds) and store them in our own database.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using GCheckout;
using GCheckout.AutoGen;
using GCheckout.Util;
using GCheckout.OrderProcessing;
using System.Xml;

public partial class Pay_GCheckout : System.Web.UI.Page
{
    public string SerialNumber = "";

    void Page_Load(Object sender, EventArgs e)
    {
        // Extract the XML from the request.
        Stream RequestStream = Request.InputStream;
        StreamReader RequestStreamReader = new StreamReader(RequestStream);
        string RequestXml = RequestStreamReader.ReadToEnd();
        
        RequestStream.Close();

        // Act on the XML.
        switch (EncodeHelper.GetTopElement(RequestXml))
        {
            case "new-order-notification":
                NewOrderNotification N1 = (NewOrderNotification)EncodeHelper.Deserialize(RequestXml, typeof(NewOrderNotification));
                SerialNumber = N1.serialnumber;
                string OrderNumber1 = N1.googleordernumber;
                string ShipToName = N1.buyershippingaddress.contactname;
                string ShipToAddress1 = N1.buyershippingaddress.address1;
                string ShipToAddress2 = N1.buyershippingaddress.address2;
                string ShipToCity = N1.buyershippingaddress.city;
                string ShipToState = N1.buyershippingaddress.region;
                string ShipToZip = N1.buyershippingaddress.postalcode;

                CustomerSubscription cs = new CustomerSubscription();
                cs.TransactionNumber = OrderNumber1;
                cs.Address1 = ShipToAddress1;
                cs.Address2 = ShipToAddress2;
                cs.Address3 = ShipToCity;
                cs.Region = ShipToState;
                cs.PostCode = ShipToZip;
                cs.TransactionType = CustomerSubscription.TransTypeOnline;
                cs.FinancialOrderState = N1.ordersummary.financialorderstate.ToString();

                foreach (Item ThisItem in N1.shoppingcart.items)
                {
                    string Name = ThisItem.itemname;
                    int Quantity = ThisItem.quantity;
                    decimal Price = ThisItem.unitprice.Value;
                }
                
                if (N1.shoppingcart.merchantprivatedata != null && N1.shoppingcart.merchantprivatedata.Any != null && N1.shoppingcart.merchantprivatedata.Any.Length > 0)
                {
                    foreach (XmlNode node in N1.shoppingcart.merchantprivatedata.Any) 
                    { 
                        if (node.LocalName.Trim() == "customer-id")
                        { 
                            cs.CustomerID = int.Parse(node.InnerText);
                        }

                        if (node.LocalName.Trim() == "subscription-id")
                        {
                            cs.SubscriptionID = int.Parse(node.InnerText);
                        }
                    }
                }

                CustomerSubscription.AddOnlineSubscription(cs);

                break;
            case "risk-information-notification":
                RiskInformationNotification N2 = (RiskInformationNotification)EncodeHelper.Deserialize(RequestXml, typeof(RiskInformationNotification));
                // This notification tells us that Google has authorized the order and it has passed the fraud check.
                // Use the data below to determine if you want to accept the order, then start processing it.
                SerialNumber = N2.serialnumber;
                string OrderNumber2 = N2.googleordernumber;
                string AVS = N2.riskinformation.avsresponse;
                string CVN = N2.riskinformation.cvnresponse;
                bool SellerProtection = N2.riskinformation.eligibleforprotection;
                break;
            case "order-state-change-notification":
                OrderStateChangeNotification N3 = (OrderStateChangeNotification)EncodeHelper.Deserialize(RequestXml, typeof(OrderStateChangeNotification));
                // The order has changed either financial or fulfillment state in Google's system.
                SerialNumber = N3.serialnumber;
                string OrderNumber3 = N3.googleordernumber;
                string NewFinanceState = N3.newfinancialorderstate.ToString();
                string NewFulfillmentState = N3.newfulfillmentorderstate.ToString();
                string PrevFinanceState = N3.previousfinancialorderstate.ToString();
                string PrevFulfillmentState = N3.previousfulfillmentorderstate.ToString();
                break;
            case "charge-amount-notification":
                ChargeAmountNotification N4 = (ChargeAmountNotification)EncodeHelper.Deserialize(RequestXml, typeof(ChargeAmountNotification));
                // Google has successfully charged the customer's credit card.
                SerialNumber = N4.serialnumber;
                string OrderNumber4 = N4.googleordernumber;
                decimal ChargedAmount = N4.latestchargeamount.Value;

                int customerID = 0;

                if (N4.ordersummary.shoppingcart.merchantprivatedata != null && N4.ordersummary.shoppingcart.merchantprivatedata.Any != null && N4.ordersummary.shoppingcart.merchantprivatedata.Any.Length > 0)
                {
                    foreach (XmlNode node in N4.ordersummary.shoppingcart.merchantprivatedata.Any) 
                    { 
                        if (node.LocalName.Trim() == "customer-id")
                        { 
                            customerID = int.Parse(node.InnerText);
                        }
                    }
                }

                if (N4.ordersummary.financialorderstate == FinancialOrderState.CHARGED)
                {
                    CustomerSubscription.UpdateFinancialOrderState(OrderNumber4, N4.ordersummary.financialorderstate.ToString());

                    //Make user paid member
                    CustomerHelper.UpgradeCustomerToPaid(customerID);
                }

                break;
            case "refund-amount-notification":
                RefundAmountNotification N5 = (RefundAmountNotification)EncodeHelper.Deserialize(RequestXml, typeof(RefundAmountNotification));
                // Google has successfully refunded the customer's credit card.
                SerialNumber = N5.serialnumber;
                string OrderNumber5 = N5.googleordernumber;
                decimal RefundedAmount = N5.latestrefundamount.Value;
                break;
            case "chargeback-amount-notification":
                ChargebackAmountNotification N6 = (ChargebackAmountNotification)EncodeHelper.Deserialize(RequestXml, typeof(ChargebackAmountNotification));
                // A customer initiated a chargeback with his credit card company to get her money back.
                SerialNumber = N6.serialnumber;
                string OrderNumber6 = N6.googleordernumber;
                decimal ChargebackAmount = N6.latestchargebackamount.Value;

                break;
            case "authorization-amount-notification":
                AuthorizationAmountNotification N7 = (AuthorizationAmountNotification)EncodeHelper.Deserialize(RequestXml, typeof(AuthorizationAmountNotification));
                SerialNumber = N7.serialnumber;
                string OrderNumber7 = N7.googleordernumber;
                
                // Charge Order If Chargeable
                if (N7.ordersummary.financialorderstate == FinancialOrderState.CHARGEABLE)
                {
                    GCheckout.OrderProcessing.ChargeOrderRequest chargeReq = new GCheckout.OrderProcessing.ChargeOrderRequest(OrderNumber7);

                    GCheckoutResponse oneGCheckoutResponse = chargeReq.Send();

                }

                CustomerSubscription.UpdateFinancialOrderState(OrderNumber7, N7.ordersummary.financialorderstate.ToString());

                break;
            default:                
                break;
        }
    }
}

N.B: The code snippet (above) is shown purely as a simple example for you to build on. It is based on the “notification_handshake.aspx” sample code that is downloadable here.

So what’s going on here? Well the events are processed in the following order:

  1. “new-order-notification” - A new order has been received. Details of the News Subscription along with the customer who ordered it will be stored in our database.
  2. “authorization-amount-notification” - Google tells us that the amount has successfully be authorised. If the order state is “chargeable”, we can go ahead charge the order and update the order state. The customer order state will consist of the following statuses: Cancelled, Chargeable, Charged, Charging, Payment Declined and Reviewing. I think it’s a good to store this information because it will allow a site administrator to view statuses of all orders and act on them, if needed.
  3. “charge-amount-notification” – Google verifies the order was successful. If the order state is marked as charged, the customer will be upgraded to a paid News Subscriber.

Outcome

You have successfully setup Google Checkout payment with basic notifications. If you already have Google Checkout code in place where a user can add one or more items to the shopping cart this code can stay. All you need to do is carry out the changes described in this post.