Integrating DoDirectPayment and Tweet Relevance

Like every other chapter in this book, let’s take the Tweet Relevance sample code from Appendix A and use the PayPal product at hand, DoDirectPayment in this case, to implement a payment experience. Although an integration with DoDirectPayment as part of a Website Payments Pro integration normally requires additional integration with Express Checkout according to PayPal’s terms of service, we’ll focus solely on integrating DoDirectPayment in this chapter to maintain maximal focus. (Recall that Express Checkout is discussed at length in Chapters 2 and 3.) A recommended exercise for the seriously interested reader, as presented in the final section of this chapter, is to integrate Express Checkout into this chapter’s sample project.

Note

It may be helpful to review Implementing a Checkout Experience for Tweet Relevance to better understand some of the various payment mechanisms that could be viable for a service like Tweet Relevance if you have not done so already. The remainder of this chapter assumes familiarity with the options as presented in that section and implements the “subscription model,” which was covered in detail in Chapter 2 and used again in Chapter 4.

The first step to integrating DoDirectPayment or any other payment mechanism is to map a URL into the main application as a means of handling a payment experience. Since integrating DoDirectPayment requires only a single call to PayPal, the addition of a /do_direct_payment URL that’s serviced by a PaymentHandler class is the only API-level addition to the application that is necessary. A basic template that we’ll add to collect payment information from the user once their free trial of the service expires will pass the information to /do_direct_payment, which is the entry point for the payment process. The following list itemizes the key changes to the project for integrating DoDirectPayment in a file-by-file fashion.

main.py

Example 5-3 illustrates the entry point into the web application. Note that there’s only one PaymentHandler URL.

handlers/PaymentHandler.py

A standard form that collects payment information, as shown in Figure 5-4, passes this information through to /do_direct_payment as a POST request, which is serviced by PaymentHandler, as shown in Example 5-5. In a nutshell, PaymentHandler validates the payment information, passes it through to DoDirectPayment, and credits the user’s account if the payment action was successful. Otherwise, it displays an error message. The trivial Product class that’s referenced by PaymentHandler is shown in Example 5-4.

Example 5-5. handlers/PaymentHandler.py

# PaymentHandler provides logic for interacting wtih PayPal's Website Payments Pro product

import os
import cgi

from google.appengine.ext import webapp
from google.appengine.api import memcache
from google.appengine.ext.webapp import template
import logging

from paypal.products import DirectPayment as DP
from Product import Product
from handlers.AppHandler import AppHandler


class PaymentHandler(webapp.RequestHandler):

  def post(self, mode=""):

    if mode == "do_direct_payment":

     # To be on the safe side, filter through a pre-defined list of fields
     # to pass through to DoDirectPayment. i.e. prevent the client from
     # potentially overriding IPADDRESS, AMT, etc.

      valid_fields = [
          'FIRSTNAME',
          'LASTNAME',
          'STREET',
          'CITY',
          'STATE',
          'ZIP',
          'COUNTRYCODE',
          'CREDITCARDTYPE',
          'ACCT',
          'EXPDATE',
          'CVV2',
      ]
      
      product = Product.getProduct()

      nvp_params = {'AMT' : str(product['price']), 'IPADDRESS' : self.request.remote_addr}

      for field in valid_fields:
        nvp_params[field] = self.request.get(field)

      response = DP.do_direct_payment(nvp_params)

      if response.status_code != 200:
        logging.error("Failure for DoDirectPayment")

        template_values = {
          'title' : 'Error',
          'operation' : 'DoDirectPayment'
        }
        
        path = os.path.join(os.path.dirname(__file__), '..', 'templates', 'unknown_error.html')
        return self.response.out.write(template.render(path, template_values))

      # Ensure that the payment was successful

      parsed_qs = cgi.parse_qs(response.content)

      if parsed_qs['ACK'][0] != 'Success':
        logging.error("Unsuccessful DoDirectPayment")

        template_values = {
          'title' : 'Error',
          'details' : parsed_qs['L_LONGMESSAGE0'][0]
        }
        
        path = os.path.join(os.path.dirname(__file__), '..', 'templates', 'unsuccessful_payment.html')
        return self.response.out.write(template.render(path, template_values))


      # Credit the user's account

      user_info = memcache.get(self.request.get("sid"))
      twitter_username = user_info['username']
      product = Product.getProduct()

      AppHandler.creditUserAccount(twitter_username, product['quantity'])

      template_values = {
        'title' : 'Successful Payment',
        'quantity' : product['quantity'],
        'units' : product['units']
      }
        
      path = os.path.join(os.path.dirname(__file__), '..', 'templates', 'successful_payment.html')
      self.response.out.write(template.render(path, template_values))

    else:
      logging.error("Unknown mode for POST request!")
products/paypal.py

The final piece of substance to completing the discussion on a DoDirectPayment integration involves the DirectPayment class that’s referenced in PaymentHandler. Basically, this class is just a thin abstraction around the DoDirectPayment API call and follows along with the same pattern used for the paypal package in earlier chapters. In short, it combines the 3-Token credentials with any other keyword parameters passed into the do_direct_payment method and executes a DoDirectPayment API call.

Prior chapters, which covered products explicitly involving PayPal as an intermediating party in the user experience of the application, required more than a single API call to PayPal due to very the nature of setting up a payment, redirecting the customer to PayPal (to avoid having you directly handle sensitive account information), and ensuring the payment was processed before crediting a user’s account. Because DoDirectPayment gives you the ability handle payment account information directly, some of the implementation details are a bit simpler because of the streamlined user experience. Just remember that with the power to handle sensitive account information directly comes great responsibility and (potentially) great liability as it relates to maintaining PCI compliance and safeguarding financial records.