Chapter 5. From Payment to Porch: An Order Pipeline

So far we've built a variety of useful e-commerce tools. These include a product catalog, a customer information model, an order and shopping cart interface, and payment processors. These apps cover the customer interaction portion of the e-commerce selling process. In this chapter we will build a simple set of tools for handling the steps that come after we've received a customer's payment. This includes:

  • Updating/assigning status information to our orders
  • Using Google Checkout API's automatic payments system
  • Calculating shipping and handling charges
  • A simple CRM tool to allow customer feedback on orders

The process that happens after an order is completely submitted and paid for by the customer tends to be very specific to a company and the products they're selling. However, the outline above reflects typical areas of need in almost all e-commerce operations. As in the rest of this book, we will build a very simple set of tools with emphasis on highlighting particular Django techniques that can simplify or enhance the development process.

Adding status information to orders

When we built the Order model in Chapter 3, Handling Customers and Their Orders, we included a field and related model to manage the status of particular orders. This status was designed to be simple and lightweight, but capable of describing a variety of circumstances. Some simple example status messages could be:

  • Awaiting Payment
  • Payment Received
  • Shipped
  • Closed

When we initially constructed the simple checkout view in Chapter 3, we did not integrate our Order model. This was added in the last chapter as part of our more general purpose checkout and payment processors. We wrote the following method into our payment processor base class:

def save_order(self):
    cart = self.get_shopping_cart()        
    self.order = make_order_from_cart(order, customer=self.request.user)

Here we convert the customer's shopping cart into an Order object. This happens prior to completing the checkout process with the payments provider (Google Checkout or Amazon). As a result, we will inevitably accumulate Order objects for shopping carts that have been abandoned. This happens when the customer adds items to their carts, proceeds to our checkout page, but does not take the final step to checkout with the payment processor.

The make_order_from_cart function was a module-level function added to our cart module. It is listed below along with the helper function get_status_code:

def make_order_from_cart(cart, customer=None):
    '''
    Takes a shopping cart object and generates a new order object for the provided customer. If no customer is provided, an "anonymous" order is created.
    '''
    order = Order.objects.create(customer=customer,
    status_code=get_status_code('NEW')) for item in cart:
    total_price = item.product.get_price() * item.quantity
    ordered_item = ProductInOrder(order=order,
                                  product=item.product, 
                                  unit_price=item.product.get_price(), 
                                  total_price=total_price, 
                                  quantity=item.quantity)
    ordered_item.save()
    return order

def get_status_code(name):
    code, created = StatusCode.objects.get_or_create(short_name=name)
    return code

These two functions create a new Order object from our shopping cart class and, if necessary, constructs a new StatusCode object to represent the status of the order. In the case of make_order_from_cart we created a StatusCode with the short description of 'NEW'. The NEW status code is designed to mark orders that have just come in and have not yet completed the payment portion of the checkout process.

Some system of clean-up would be necessary, either a simple cron job that runs periodically or a clean-up/delete script that could be run manually, to delete Orders whose status is 'NEW' but that have been abandoned. Abandoned orders could be defined as those with a 'NEW' status created more than two weeks ago. You would have a query like the following:

>>> twoweeks = datetime.today() - datetime.timedelta(days=14)
>>> old_orders = Order.objects.filter(status_code__short_name='NEW',
                                      date_placed__lte=twoweeks).delete()

Using the system we've built in this book, it is very safe to delete NEWOrders after even a short period of time because every time a customer proceeded to checkout we would create a unique, new Order. This was a design decision to simplify the order taking process and prevent the need for cookies or sessions to manage a customer's order and shopping cart combination. Instead of attempting to update the same Order object when a customer makes changes, we simply create a new one.

This is the entry point for all orders in our system. When a customer does not abandon their order and proceeds to the payment service to enter their payment information, two things should happen. First, the payment service will notify us, the e-commerce vendor, which payment succeeded. This notification must include our systems unique Orderid, which we can provide as extra information that will be returned to us when we render the checkout request for the customer. Second, upon receipt of notice from the payment service, we should locate the Order that was paid for in our system and update its status. This will be a new status code of our choosing to reflect the fact that payment has been received and the order should enter our fulfillment process.

Depending on the payment service we're using, the notification of payment could arrive in several ways. For example, Google Checkout by default sends an e-mail notification to the administrative address we entered when we created our API account. This shows the order details including the extra information we include, like our own Orderid.

With this information, a manual solution for low-volume sites could be as simple as logging into the Django admin, locating the Order, and updating its status. We can automate this process, however; using Google Checkout's order processing interface we can build a sophisticated, automatic integration with our Django system.

The order processing aspect of the Checkout API uses a secure SSL callback, which is hosted on our e-commerce site. Google Checkout will notify this callback when an order payment is completed. This requires an SSL certificate and secure Apache installation; see the Apache documentation for more information on configuring this for your site.

This SSL channel delivers order notifications to our system in a secure, timely manner. We can respond, after charging an order, for example, through another set of SSL-secured endpoints on the Checkout API side. This in effect performs all of the operations that are available through the Checkout dashboard, but in a custom way that is integrated with our Django project. The Amazon Flexible Payment Service offers a similar callback approach, as do many other payment services.

Even relatively simple systems with low order volume could benefit from integrating order processing. Despite the extra work involved in developing a Django interface and configuring a secure web server, having all the order management happen in a single tool may be a productivity boost to staff managing orders. The extra work involved in implementation, however, could be significant and requires a skilled developer.

SSL and security considerations

We've already briefly mentioned SSL, but we should stop and emphasize its importance in securing e-commerce applications. Secure Sockets Layer (SSL) is the standard approach to encrypting web communications. Google Checkout requires the secure endpoints we discussed in the previous section because they will be transmitting potentially sensitive order information. Without using SSL, this information would be sent in clear text and could be intercepted or read by anyone positioned between Google and our servers.

With the growing use of public wireless access points, mobile devices, and other "un-trusted" connections to the Internet, securing communications between browser and server has become essential. But similar risks are involved with server-to-server communications and these connections also require protection. Fortunately, SSL is capable of handling both.

A certificate is required to implement SSL on your server. This certificate must be purchased and issued from a trusted authority. There are numerous companies selling these authenticated certificates on the Web; prices and packages vary widely amongst vendors. A purchased certificate will typically require renewal on an annual basis.

Once in possession of a certificate, implementation of a secure server in Apache requires enabling mod_ssl and creating additional configuration sections that use port 443 (instead of 80) and include the SSLEngine on directive. The certificate file should be installed in a secure location (usually readable only by the root account) and specified in the SSLCertificateFile and SSLCertificateKeyFile directives.

After these changes, our Django application can be configured as normal within the SSL configuration section. More sophisticated approaches exist to limit the secured views to a subset of all our site's views. For example, we could write a WSGI script that loads a special, secure settings file and points to a different ROOT_URLCONF that contains only our secure URL patterns and views.

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

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