Home > Dreamtech Books, Manning Publication > Collecting payment through PayPal

Collecting payment through PayPal

Free Excerpt from the Book – Lift in Action by Timothi Perrett

Enabling users to send funds for their orders, one of Lift’s modules provides out-of-the-box support for the online payment service provider PayPal. The integration supports the two most common forms of electronic payment used by PayPal: Payment Data Transfer (PDT)1 and Instant Payment Notification (IPN)2. In order to get access to the integration, simply add the following dependency to your SBT project definition, run the update command and away you go:

val paypal = “net.liftweb” % “lift-paypal” % liftVersion % “compile”

The PayPal integration negates your having to set up your own dispatch functions and other elements necessary for handling the responses from PayPal and parsing their post-back to your application. Typically, all you need to do is create a handler singleton (object) that implements methods required by the PaypalPDT and PaypalIPN traits and hook up the dispatchers supplied by those traits in your application boot.

Environment setup

Before we cover the implementation, however, there are several things you must know about PayPal and their developer process. You will need to register yourself on developer.paypal.com. The first thing you should do before going any further would be to do some background reading on the process of PDT and IPN. The online documentation is very comprehensive and I won’t repeat it here other than to touch on some high-level configuration instructions. With your account created and at developer.paypal.com, follow the steps in table 1—I have placed links to the online PayPal documentation where applicable.

Now that your environment is set up, we need to actually wire up the Lift side of things; otherwise, the communications from PayPal will disappear. As we mentioned earlier, two main traits in the PayPal package take responsibility for the two supported transaction systems—PaypalIPN and PaypalPDT. In this example, let’s create a handler that implements these traits called PaypalHandler with an implementation as shown in listing 1.

Listing 1 The PaypalHandler implementation

import net.liftweb.common.{Loggable,Full,Box,Empty,Failure}
import net.liftweb.paypal.{PaypalIPN,PaypalPDT,
PaypalTransactionStatus,PayPalInfo}
import net.liftweb.http.DoRedirectResponse
import net.liftweb.mapper.By
import example.travel.model.{Order,OrderStatus}
object PaypalHandler extends PaypalIPN with PaypalPDT with Loggable {
val paypalAuthToken = “yourtokengoeshere” 1
def pdtResponse = {
case (info, resp) => info.paymentStatus match {
case Full(PaypalTransactionStatus.CompletedPayment) => 2
DoRedirectResponse.apply(“/paypal/success”) 2
case _ => DoRedirectResponse.apply(“/paypal/failure”) 3
}
}
def actions = {
case (PaypalTransactionStatus.CompletedPayment,info,_) => 4

updateOrder(info,OrderStatus.Complete) 4
case (PaypalTransactionStatus.FailedPayment,info,_) => 4
updateOrder(info,OrderStatus.Failed) 4
case (status, info, resp) => 4
} 4
private def updateOrder(info: PayPalInfo, status: OrderStatus.Value){ 5
Order.find(By(Order.reference, 5
info.itemNumber.map(_.toLong).openOr(0L))) match { 5
case Full(order) => order.status(status).save 5
case _ => 5
} 5
} 5
}
1 PDT token key
2 Successful PDT action
3 Failure PDT action
4 IPN response handlers
5 Find and update order by ref

So, there is a fair bit of code here. In short, the two PayPal traits have simply been composed together and the required methods have been implemented to handle the various responses PayPal might provide. It does, however, go without saying that this is for example only. In a live production system you would account for a lot more responses and intelligently handle them; this, in comparison, is a mere stub to illustrate the process and ease of implementing ecommerce within Lift. (#1) When you enabled PDT within the PayPal sandbox, you would have been assigned a security token to use within the PDT request; enter it here, so PayPal will know that it’s your application calling back to it for information on the transaction. In this implementation, PDT serves only to display the correct response screen back to the user; #2 and #3 dictate the URL to which the user should be redirected based on the result of their transaction. #4 is somewhat more complex since, when the PayPal servers make the IPN call back to the application, it contains much more data and is generally considered the best way to then update the order information with the transaction data and so on. Here, we have a helper method at #5 that will look up an order by its random order reference number that was generated when the order was created; then, depending upon the status received from the IPN data, the order has its status set appropriately.

While this code is functional, it is currently not wired into the application boot cycle, so it won’t ever be called by anything. With two lines of code in your Boot class you can include the functionality:

import net.liftweb.paypal.PaypalRules
import example.travel.lib.PaypalHandler
class Boot extends Loggable {
def boot {

PaypalRules.init
PaypalHandler.dispatch.foreach(LiftRules.dispatch.append(_))

}
}

These two lines of code first initialize the PaypalRules object that contains configuration information used to determine a range of factors about the transaction. For example, it contains a function configuration that lets you dynamically determine which currency should be used when communicating with PayPal. In this instance, it uses the defaults, so it will just select a currency based on the locale of the JVM running the example. Secondly, it takes the dispatch functions in the PaypalHandler object (inherited, of course, from the two Lift PayPal traits), which respond to the PDT and IPN callbacks, and maps them into the application using Lift’s dispatching mechanism.

The Buy Now button

With the backend all wired up and ready to go, we need to supply the user with a simple one-click icon to instantiate the transaction process in the familiar PayPal way: a bright orange button! Within Lift’s PayPal support there is a mechanism for automatically generating these buttons; you simply need to implement the BuyNowSnippet trait into one of your snippet classes, and populate the required methods so it knows the value and several other aspects of metadata that you wish to send to PayPal.

Listing 2 details the OrderSummary snippet that computes the overall worth of an order and presents the user with a rundown of what they were going to purchase.

Listing 2 The OrderSummary

import scala.xml.{NodeSeq,Text}
import net.liftweb.util.Helpers._
import example.travel.model.Customer
class OrderSummary {
val order = Customer.currentUser.flatMap(_.order)
val amount = order.map(_.totalValue).openOr(0D)
val reference = order.map(_.reference.is.toString).openOr(“n/a”)
def value(xhtml: NodeSeq): NodeSeq = Text(amount.toString) 1
def shipping(xhtml: NodeSeq): NodeSeq = 2
Customer.currentUser.flatMap(_.order.map(order => 2
bind(“s”,xhtml, 2
“address_one” -> order.shippingAddressOne.is, 2
“address_two” -> order.shippingAddressTwo.is, 2
“city” -> order.shippingAddressCity.is, 2
“postcode” -> order.shippingAddressPostalCode.is 2
))).openOr(Text(“Unable to obtain shipping information”)) 2
}

1 Get total order value for auction
2 Bind shipping info from order

There are two key methods here: #1 is obtaining the overall value of the order using the totalValue helper method that was added to the Order model. The second method here (#2) simply reads the values that were entered in the Checkout screen and binds them for display in the usual manner. Had we been using a full Wizard, we would not need to reload the values in the same way but, for the sake of simplicity and as a reiteration of what has already been covered so far, it makes sense to do it this way rather than complicate the example with yet more new content.

Since this already has the data we need for the button, the simplest approach is to just compose the BuyNowSnippet with the existing class. Listing 3 shows the changes made to the class.

Listing 3 Implementing the BuyNowSnippet trait

import net.liftweb.paypal.snippet.BuyNowSnippet
class OrderSummary extends BuyNowSnippet { 1
override def dispatch = { 2
case “paynow” => buynow _ 2

}
override val values = Map( 3
“business” -> “me@business.com”, 3
“item_number” -> reference, 3
“item_name” -> (“Auction Order: ” + reference)) 3
}
1 Compose the trait
2 Setup the dispatch
3 Configure the PayPal fields

As you can see in the listing, very few lines of code are required to actually implement the button; the main code is overriding the Map[String,String] of key-value pairs that need to be included in the form submission to PayPal. Finally, ensure you add the PayPal response pages to the SiteMap within the Boot class:

Menu(“Transaction Complete”) / “paypal” / “success”
>> LocGroup(“public”) >> Hidden,
Menu(“Transaction Failure”) / “paypal” / “failure”
>> LocGroup(“public”) >> Hidden,

For simplicity sake, I have just populated these two files with a friendly message so the user is aware of the transaction outcome. You might even want to customize the pages with customer information about their order or such; you have everything at your disposal within the PDT and IPN responses supplied by PayPal.

Advertisement
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.