Notification API

There are two possible implementations of Google Checkout notifications. The first, called simply the Notification API, is a push-based solution. A data pull-based solution, called Notification History API, is also available. Developers can implement either or both, depending on their needs, but for our examples we will stick with the push-based Notification API.

To implement support for Notification API, we must only use Django views that are secured using SSL. In addition, each request that comes into our application via a secure view must be verified as an authenticated Google Checkout notification. This is accomplished using HTTP Basic Authentication, wherein the Authorization header includes a base64 encoded pairing of our Checkout Merchant ID and Merchant Key.

These ID and key values should remain secret, which is part of the necessity of using SSL for Notification API communications. All notification requests should be verified by our view. This is done by inspecting the authorization header, which we can do using a decorator function. The decorator appears below:

def verify_googlecheckout(view_func):
    '''
    Decode the Authorization header for Google Checkout Notification API requests and verify that it matches MERCHANT_ID:MERCHANT_KEY when base64 decoded.
    '''
    def _verified_view_func(request, *args, **kwargs):
        merchant_key = getattr(settings, 'GOOGLE_MERCHANT_KEY', None)    
        merchant_id = getattr(settings, 'GOOGLE_MERCHANT_ID', None)      
        b64string = request.META.get('Authorization', None)
        If b64string: 
            cleartext = base64.b64decode(b64string) 
            auth_id, auth_key = cleartext.split(":") 
            if auth_id != merchant_id or auth_key != merchant_key:
                raise Http404
            return view_func(request, *args, **kwargs)
        else:
            raise Http404 
    return _verified_view_func(request, *args, **kwargs)

The Notification API is configured in the Google Checkout Merchant Center by editing the Integration settings and entering an API callback URL. This is the primary URL where Notification information will be sent to us from Google Checkout.

When our view receives a notification it is sent using HTTP POST and the message is stored as XML in the request body. The contents of the POST are not the usual key-value combinations we're used to working with in Django views. As a result, the request.POST QueryDict in our view will not be usable for our purposes. Instead we will need to access the raw POST data. This is done through the raw_post_data attribute on the request object. This should contain a valid XML document that represents the Notification API message.

There are four kinds of notifications: new orders, risk information, order state change, and amount notifications. The usefulness of each of these is dependent on the application. For example, an e-commerce site that needs to handle lots of refunds and cancellations will be certain to implement support for amount notifications because they cover that type of information.

Our example in this chapter will use the new order notification data to update the order status in our Django model. We will ignore the other notification types, but the implementation for each of them will follow the same pattern. The specific actions required, however, are determined by the application or business need.

When a new order notification arrives, it includes several key pieces of information. This includes the shopping cart element, as it appeared in our Checkout XML submissions generated by the payment processors in Chapters 3 and Chapter 4, Building Payment Processors. It also includes: the buyer's billing and shipping address, a buyer ID that is unique to Google Checkout, the order total, and order adjustment information that corresponds to the shipping method and whether any coupons or gift certificates were used.

For our purposes, the <shopping-cart> element is important here because we can use it to locate the Order object in our Django application's database. When we built the cart XML template in earlier chapters, we did not include the Order ID in our local database. We should modify our shopping cart XML to include this information in a special tag called <merchant-private-data>.

The <merchant-private-data> tag can contain any information we choose. We should structure this information as valid XML and choose a tag name that makes sense for parsing later on. Here is an example tag to add to the shopping cart XML for our payment processor:

<shopping-cart>
   ...
<merchant-private-data>
<django-order-id>{{ order.id }}</django-order-id>
</merchant-private-data>
</shopping-cart>

The <merchant-private-data> tag must be added as a subtag to the shopping cart in our XML file. It should contain private data for the order as a whole.

We could add merchant data to the shopping cart XML for specific items. This could tie the items a customer is ordering to Product model IDs in our Django database. The tag to use for this is <merchant-private-item-data> and it must be added as a subtag to a specific <item> tag in the shopping cart XML. This is not necessary for our example here, but it would look something like this:

<item>
    ...
<merchant-private-item-data>
<django-product-id>{{ product.id }}</django-product-id>
</merchant-private-item-data>
</item>

With this modification to our payment processor, we gain the ability to tie new order notifications to specific Order objects in our system. Let's examine the code that makes this possible. We'll start with the notification callback view:

@verify_googlecheckout
def notification_callback(request):
    '''
    A simple endpoint for Google Checkout's Notification API. This handles New Order notifications and ignores all other information.
    '''
    if request.method is not 'POST':
        raise Http404 
    dom = parseString(request.raw_post_data) 
    notify_type = get_notification_type(dom) 
    if notify_type is 'new-order-notification':
        order_id = get_order_id(dom) 
        order = Order.objects.get(id=order_id) 
        set_status_code(order, 'NEW_CHKOUT') 
        return HttpResponse('OK')
    raise Http404

Despite the short length of this snippet, there is a lot going on. First, we've wrapped the view in our verification decorator to make sure we have authentic Notification API requests. Next, we use the raw_post_data attribute on the HttpRequest object as input to an XML parser. This is using the parseString function from Python's xml.dom.minidom package. Finally, if this is a new order notification, we process it using a series of additional helper functions.

When we use parseString, we get a Python DOM object. This has a variety of methods for traversing the XML DOM tree. In our case, we'll stick to some very simple techniques. First, to determine the notification type, we simply look at the tag name for the whole XML document we receive. A New Order notification XML will look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<new-order-notification xmlns="http://checkout.google.com/schema/2"
serial-number="85f54628-538a-44fc-8605-ae62364f6c71">
<shopping-cart>
      ...
</shopping-cart>
</new-order-notification>

Whereas a risk information notification XML would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<risk-information-notification xmlns="http://checkout.google.com/schema/2"
serial-number="0b95f758-0332-45d5-aced-5da64c8fc5b9">
<risk-information>
      ...
</risk-information>
</risk-information-notification>

So by examining the root XML tag, we can determine the type of request. This is handled by a helper function that simply extracts the tagName of the root document element:

def get_notification_type(xmldoc):
    return xmldoc.documentElement.tagName

We traverse the DOM in a similar fashion to obtain the order ID information from our merchant-private-data tag.

def get_private_data(xmldoc, private_data_tag='merchant-private-data'):
    docEl = xmldoc.documentElement
    return docEl.getElementsByTagName(private_data_tag)[0]

def get_order_id(xmldoc, order_tag='django-order-id'):
    private_data = get_private_data(xmldoc)
    order_id = private_data.getElementsByTagName(order_tag)[0]
    return order_id.nodeValue

The full set of DOM traversal methods are described in the Python xml.dom module documentation.

Once we obtain the order ID, we simply look it up in our Order model and change its status. You can use whatever status you like here, but the goal is to indicate that the order has been submitted and payment information was entered in the Checkout API system. Checkout is still working on verification for the payment at this point, so we do not want to go any further than this.

At some point the Notification API will let us know that the order payment has passed fraud and verification checks. This means the order is chargeable. We can handle these types of notifications as well by adding conditional clauses to our notification view. The full set of Notification API messages is documented at the following URL:

http://code.google.com/apis/checkout/developer/Google_Checkout_XML_API_Notification_API.html.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset