An Amazon Checkout class

Amazon Web Services provides developers with another option for payment processing. Their Flexible Payments Service (FPS) is slightly more complex than the Google Checkout API, but also offers some additional robustness. It uses a significantly different technical approach, however, and will require some adjustments to our payment processor designs.

FPS allows developers to create more advanced payment mechanisms, as well. For example, you can use FPS to set up recurring payments, such as subscriptions, as well as single-click payments whose parameters will be remembered over time. Google Checkout API offers similar advanced functionality. These features of both services will not be implemented here, but are well documented on the Web. Later in this book we will discuss using Amazon's payment services for digital goods, such as music and software.

The FPS uses a similar interaction flow to Google Checkout. We present our customer with the full contents of their shopping cart, show them a Pay with Amazon checkout button, which when clicked directs them to a co-branded Amazon payments page. After their payment is complete, they are redirected to our e-commerce website and we are given a payment token. This payment token can be used to charge the customer right away or saved for later, after an order is fulfilled, for example.

The payment token portion is the main feature that differs between Checkout and FPS. The Google Checkout API does not charge the customer right away, either. But instead of passing back a token, it creates a record in the vendor's checkout order dashboard. It may also notify an e-mail address. The vendor is required to log in to Checkout and charge the order.

With Amazon, there is no dashboard. This means we have to provide a callback URL to the Amazon service, which handles the return of the customer and stores their token for later processing. A token just means the customer has been authorized for payment, but has not yet been charged. We submit the token separately, when we're ready to charge the order.

As a result of these API differences, processing FPS payments requires additional steps. We will build a CheckoutView subclass as before, but customize it for the Amazon service. We will also need a view to handle the callback that FPS makes after authorizing the customer's payment. This callback will extract the token, which the FPS service adds as part of URI parameters. We will need to save this token and associate it with the specific order we're handling in order to charge the customer.

Let's tackle the CheckoutView subclass first. The AmazonCheckoutView class will include a convert_shopping_cart method as we saw earlier, but instead of converting to an XML format, it constructs a standard Internet URI. We will use this URI as the action attribute on an HTML form that is rendered to the customer. This form will act as the final checkout button, just like the Google Checkout API created. Our Amazon checkout button will use the Amazon branded Pay with Amazon image as a submit button.

When the user submits this form, the URI we generate will be submitted as an HTTP POST. This, kicks off the Amazon authorization step; the user can sign in or create an Amazon account and pay with the usual payment methods that Amazon supports. When payment authorization completes, Amazon will redirect the user to the callback URL mentioned earlier. This page will do two things: first, it presents a thank you message or other confirmation and encourages the customer to continue browsing our e-commerce site.

Second, when our Django backend handles the request, it will extract the token information Amazon adds to our URL, and saves it to the database in a model that associates this customer's order with the token. Later on, when we fulfill the order, for example, this token will be submitted to Amazon to actually charge the customer's account. This view could also attempt to charge the customer right away. In the next chapter we will discuss how to optimize this process using an order pipeline and build some tools to simplify the procedure.

Let's examine the convert_shopping_cart and related methods of our AmazonCheckoutView class:

Class AmazonCheckoutView:
    default_aws_params = {'currencyCode': 'USD',
                          'paymentMethod': 'ABT,ACH,CC',
                          'version': '2009-01-09',
                          'pipelineName': 'SingleUse'}

    def make_signature(self, params):
        path = u'%s?' % self.endpoint
        keys = params.keys()
        keys.sort()

        sign_string = path
        for key in keys:
            sign_string += '%s=%s&' % (urlquote(k), urlquote(params[k]))
        sign_string = sign_string[:-1]
        sig = hmac.new(self.aws_secret_key, sign_string, sha).digest()
        return base64.b64encode(sig)

    …

    def convert_shopping_cart(self):
        cart = self.get_shopping_cart()
        site = Site.objects.get_current()
        params = dict(self.default_aws_params)
        caller_reference = self.create_reference()
        callback_path = reverse('amazon_callback',
                                kwargs={'reference': callerReference})
        callback_url = 'http://%s%s' % (site.domain, callback_path)
        params.update({'callerKey': self.aws_access_key,
                       'callerReference': caller_reference,
                       'paymentReason': 'Purchase from %s' % site.name,
                       'returnURL': callback_url,
                       'transactionAmount': cart.total()})
        signature = self.make_signature(params)
        params.update({'signature': signature,
                       'signatureMethod': 'HmacSHA256',
                       'signatureVersion': '2'})
        urlstring = urllib.urlencode(params) 
        aws_request = '%s?%s' % (self.endpoint, urlstring)   
        self.extra_context.update({'requestUrl': aws_request})

We begin by retrieving the shopping cart for this request. We also need information about our site, specifically about the domain we're running on. This information is retrieved via a call to Django's sites framework as Site.objects.get_current(). With this information, we are able to construct the callback URL where we will have Amazon return the customer after they've authorized payment. This is stored in the callback_url variable.

Next we build the list of parameters to pass to Amazon FPS. This includes other information such as our Amazon Web Services access key, a caller_reference variable, a description of the purchase, and a total cost of the payment.

The caller_reference variable is simply a reference number for the order associated with this purchase. We can use this reference number to look up the corresponding Order object from our database. Notice how this avoids passing any information about the products the customer has ordered. Instead we pass this reference to our Order object, which contains all of the products in the order. Our callback URL can use this reference number to obtain the correct order and associate it with the returned Amazon token.

Once we've constructed this basic set of parameters, we need to generate a base-64 encoded digital signature, very similar to the signature we generated for Google Checkout. After computing this signature, we add it to the FPS parameters and urlencode the whole set of data. We now have full FPS URI and we add it to the view context so that our template can render the appropriate HTML form (the Pay with Amazon button).

One final note regarding the caller_reference parameter. In our simple design, we will use the primary key of the Order object for this request. This gives us a simple way to look up the Order on the callback. Other applications may have different requirements, depending on how you store order data. As a result, we've broken the retrieval of this reference number into a create_reference method. In our case it's very simple:

def create_reference(self):
    if self.order:
        return self.order.id
    raise AttributeError("No order exists for this view.")

It just returns the ID of the Order object in the view. If our needs changed later, we would only need to update this method to return the appropriate value. Remember, though, that we must be able to use this reference number to find our customer's order in the callback routine. The simplest approach is to use the primary key.

The Amazon Callback view

Now that we can generate a checkout button that contains the necessary information and send the customer to Amazon to authorize payment, we next need to implement the callback view. When we defined the callback_path in our convert_shopping_cart method, we included the reference number directly on the URL. The URL pattern was designed this way for simplicity, but the reference number could also be retrieved from GET parameters. Our URL pattern looks like this:

url(r'^amazon/(?P<reference>d+)/$', 'amazon_callback',
     name='amazon_callback'))

The view code is implemented as a regular Django functional view, not a class-based view. The reuse potential of this view is low, so for simplicity a standard Django view seems appropriate. The view expects one argument, the reference value, from the URL. Inside the view, we look up the information Amazon has added to the request as HTTP GET parameters. These parameters include copies of the parameters we constructed in our convert_shopping_cart method. This is useful for debugging.

Two new values are also present in these parameters, both of which Amazon has generated. These are tokenId and status. The tokenId is the key piece, which we will save with our Order object so that we can charge the customer. Without a tokenId, we cannot get Amazon to send us payment, so it's a very important piece of data. The status simply reflects the status of the authorization request, whether it was denied or not. We will save this value as well. The full view code for this function is as shown:

def amazon_callback(request, reference,
                    template_name='payments/amazon_complete.html'): 
    order = get_order_by_reference(reference)
    token = request.GET.get('tokenId') 
    status = request.GET.get('status')
    
    # save the Amazon FPS token for this order to the database
    save_amazon_transaction(order, token, status)
    return render_to_response(template_name, {},
                             context_instance=RequestContext(request))

There are two helper methods, get_order_by_reference and save_amazon_transaction. The code for these functions is as follows:

def get_order_by_reference(reference):
     return Order.objects.get(id=reference)

def save_amazon_transaction(order, token, status):
    try:
        transaction = AmazonTransacion.objects.get(order=order) 
    except AmazonTransaction.DoesNotExist:
        transaction = AmazonTransaction.objects.create(order=order)
    transaction.token = token
    transaction.status = status
    transaction.save()

These are simply using Django's ORM to retrieve and store the information we need. Breaking the view up with these helper functions simplifies the view code and improves readability.

Finally, the view renders a template whose default name is payments/amazon_complete.html. This is where we thank the customer for their order and encourage them to continue browsing. At this point we've successfully taken their order, recorded it in the database, and authorized the payment. The next step is to fulfill the order and actually charge the customer. We will discuss this "order pipeline" in the next chapter.

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

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