Miti Pricope

Created
22 March 2010
Prerequisite knowledge
To follow all the steps in this tutorial you will need general familiarity with Flex and ActionScript 3 as well as PHP. Some knowledge of the PayPal Express Checkout API will also be helpful.
 
User level
Intermediate
Required products
Flash Builder (Download trial)
Sample files
In this article, I will show you how to integrate PayPal Express Checkout with either a Flex application running in Flash Player or an Adobe AIR application. Express Checkout streamlines the checkout process for PayPal buyers, but there are security implications for using it in stateful applications.
 
l will review the security and UI considerations, introduce the architectural approach, and then show you how to implement the whole thing. You can also download working samples to see how everything works together.
 
For server-side scripting, I have used PHP but the techniques outlined here can be extended to any server language.

 
Starting PayPal Express Checkout from a Flex application running in Flash Player

The primary challenge of integrating payment services into RIAs is that currently payment services such as PayPal are designed to work in the request-response paradigm (the standard paradigm for web-based applications), while RIAs are, of course, stateful.
 
Consider the following simple RIA payment service scenario.
 
John visits MitiOnDemand.tv, a new on-demand video site. He decides to watch The Matrix. Just when Neo is asked to choose between the red and blue pill, the movie pauses and John is asked for $1, the fee for watching premium content on MitiOnDemant.tv. John, being already caught up in the action, elects to pay the amount using PayPal. After the successful transaction John happily enters The Matrix.
 
Security considerations
One reason for the success of e-commerce is that the Internet has proven to be a secure medium for transferring money and making payments. PayPal uses several security elements to make sure that all the payments processed through the service are as secure as possible:
 
Usage of HTTPS for communicating with PayPal Adaptive Payments web services ensures that the communication is protected from third-party access.
 
A set of API_USERNAME, API_PASSWORD, and API_SIGNATURE values ensures that the calling party is uniquely identified.
 
A part of the payment approval process is hosted on the PayPal servers. This is a very important anti-phishing mechanism and ensures that users enter their credentials and approve or pre-approve all the amounts only on the PayPal domain.
 
When looking at these security elements of the PayPal APIs, one important observation regarding protecting the PayPal API credentials becomes clear: because Flex is a client technology (and even though the code is compiled into bytecode), hardcoding sensitive information into a Flex application is a highly insecure practice. Put simply, any information related to credentials (including the API_USERNAME, API_PASSWORD and API_SIGNATURE values) should NOT be stored in the Flex application.
 
Architectural approach and UI solutions
Summing this up, the architectural solution has to comply with three concurrent demands:
 
  • It calls for a Flex RIA front end that is stateful and is built following the single page application paradigm.
  • To be secure, the Flex application must not handle PayPal API credentials.
  • The payment workflow will include a part that is hosted on the PayPal servers and that uses a standard request-response web application.
To meet these requirements, I suggest the following approach:
 
  • All PayPal API calls will be made from the server side so that API credentials will be protected.
  • Access to the PayPal web application will be from a new pop-up page, enabling the Flex Application to stay in the single page paradigm and preserve its state.
Figure 1. The workflow incorporates a RIA front end and PayPal Express Checkout.
Figure 1. The workflow incorporates a RIA front end and PayPal Express Checkout.
The user workflow for the checkout process (see Figure 1) includes the following stages:
 

1. The user chooses to checkout using PayPal in the RIA application (see Figure 2).

Note: To ensure security, all requests must be performed through HTTPS starting from this point in the workflow.
 

Figure 2. The user is presented with the opportunity to pay using PayPal.
Figure 2. The user is presented with the opportunity to pay using PayPal.

2. The user sees a new window open and logs into PayPal to authenticate his or her identity (see Figure 3).

Figure 3. The user logs in to his or her account on the PayPal site.
Figure 3. The user logs in to his or her account on the PayPal site.

3. The user reviews the transaction on PayPal (see Figure 4).

Figure 4. On the PayPal site, the user reviews the payment method, address, and contact information.
Figure 4. On the PayPal site, the user reviews the payment method, address, and contact information.

4. The user confirms the order and pays from my site (see Figure 5).

Figure 5. Back on my site, the user approves the purchase.
Figure 5. Back on my site, the user approves the purchase.

5. The user reviews the order confirmation on my site (see Figure 6).

Figure 6. The application reports that the transaction was successful.
Figure 6. The application reports that the transaction was successful.

6. The user closes the pop-up and returns to Flex Application (see Figure 7).

Figure 7. After clicking Return to Flex, the user is now ready to play the movie.
Figure 7. After clicking Return to Flex, the user is now ready to play the movie.
 
Calling the PayPal part of the checkout process from Flex
Because the PayPal API credentials need to stay on the server, the PayPal API should be invoked from the server as well. For this tutorial, I use PHP as a server language, and I also use the PayPal Name-Value Pair (NVP) API sample code. Of course, you can use any server language, the principles and techniques highlighted here remain the same.
 
First, I will open a pop-up window from Flex using the following code:
 
payPalFlex.mxml
 
//Open the Pop-Up Window first. Using the //ExternalInterface call we can control the window appereance ExternalInterface.call('window.open','about:blank', 'payPalWindow','height=500,width=900,toolbar=no,scrollbars=yes');
In the newly opened window, I send a request to a PHP server page and include the user’s purchase choice:
 
payPalFlex.mxml
 
var url:URLRequest = new URLRequest(URL_ROOT + "/payPalFlex/startPaymentFlex.php"); url.data = new URLVariables(); var obj:URLVariables = new URLVariables(); url.data.movieId = '1'; url.data.paymentReason = 'Enter The Matrix'; url.method = "GET"; navigateToURL(url, "payPalWindow");
On the server page, I follow the PayPal NVP samples and generate the URL that will redirect the user to PayPal site.
 
startPaymentFlex.php
 
$serverName = $_SERVER ['SERVER_NAME']; $serverPort = $_SERVER ['SERVER_PORT']; $url = dirname ( 'http://' . $serverName . ':' . $serverPort . $_SERVER ['REQUEST_URI'] ); function getMovieAmount($movieId) { //you can replace this function with a more sophisticated one return 1; } $paymentAmount = getMovieAmount($_GET['movieId']); //$_REQUEST ['paymentAmount']; $currencyCodeType = 'USD'; //$_REQUEST ['currencyCodeType']; $paymentType = 'Sale'; //$_REQUEST ['paymentType']; /* The returnURL is the location where buyers return when a payment has been succesfully authorized. The cancelURL is the location buyers are sent to when they hit the cancel button during authorization of payment during the PayPal flow */ $returnURL = urlencode ( $url . '/GetExpressCheckoutDetails.php?currencyCodeType=' . $currencyCodeType . '&paymentType=' . $paymentType . '&paymentAmount=' . $paymentAmount ); $cancelURL = urlencode ( "$url/cancel.php?paymentType=$paymentType" ); /* Construct the parameter string that describes the PayPal payment; the variables were set in the web form, and the resulting string is stored in $nvpstr */ $nvpstr = "&Amt=" . $paymentAmount . "&PAYMENTACTION=" . $paymentType . "&ReturnUrl=" . $returnURL . "&CANCELURL=" . $cancelURL . "&CURRENCYCODE=" . $currencyCodeType; /* Make the call to PayPal to set the Express Checkout token If the API call succeeded, then redirect the buyer to PayPal to begin to authorize payment. If an error occured, show the resulting errors */ $resArray = hash_call ( "SetExpressCheckout", $nvpstr ); $_SESSION ['reshash'] = $resArray; $ack = strtoupper ( $resArray ["ACK"] ); if ($ack == "SUCCESS") { // Redirect to paypal.com here $token = urldecode ( $resArray ["TOKEN"] ); $payPalURL = PAYPAL_URL . $token; header ( "Location: " . $payPalURL ); } else { //Redirecting to APIError.php to display errors. $location = "APIError.php"; header ( "Location: $location" ); }
As you can see in the code above, I store the result of the API call in a session variable to use later.
 
In a real e-commerce site, I strongly suggest that you also log the application state in a database. This way you will have access to all transaction steps later on, should you need them.
 
Returning from PayPal and notifying the Flex application
After the user completes the workflow on the PayPal site he needs to complete the payment on my site by confirming the transaction (step 4 of the workflow) and then reviewing the order confirmation (step 5).
 
Although I could have the user close the pop-up window at this point and continue the workflow in the Flex application, this might not be a good idea because it would add an extra step between payment review on the PayPal site (step 3) and payment approval on my site (step 4).
 
So I choose to implement the payment approval using standard PHP and HTML and reuse the PHP NVP API samples from PayPal. Specifically, I use the GetExpressCheckoutDetails API (for step 4) and the DoExpressCheckoutPayment API (for step 5). See the sample files for more details on the implementation.
 
What remains to be done now is to close the pop-up window, return to the Flex application, and verify that the transaction succeeded. To communicate with the Flex application I use the Externalnterface API. However, since ExternalInterface is not a secure communication channel I will use it only to notify the Flex application that the pop-up workflow has ended. The Flex application will then retrieve the status from the server. With this approach, a malicious user will not be able to inject a false status into the Flex application.
 
First, after I call the DoExpressCheckoutPayment API, I save the results in a session variable:
 
DoExpressCheckoutPayment.php
 
$resArray=hash_call("DoExpressCheckoutPayment",$nvpstr); $_SESSION ['reshash'] = $resArray;
In Flex, I have a method that checks the status and determines if the transaction succeeded or failed:
 
private function paymentComplete():void { var srv:HTTPService = new HTTPService(); srv.url = URL_ROOT + "/payPalFlex/getPaymentStatus.php"; //srv.resultFormat srv.addEventListener(ResultEvent.RESULT,function (event:ResultEvent):void { Alert.show(event.result.status.type); if (event.result.status.type == 'SUCCESS') { currentState = 'Succes'; } else { currentState = 'Fail'; } }); srv.addEventListener(FaultEvent.FAULT,function (event:FaultEvent):void { currentState = 'Fail'; Alert.show(event.message.toString()); }); srv.send(); }
In the code above, getPaymentStatus.php is a simple PHP page that retrieves the status from the DoExpressCheckoutPayment result previously stored in a session variable.
 
getPaymentStatus.php
 
<status> <type><?php echo strtoupper ($_SESSION ['reshash'] ["ACK"] ) ?></type> </status>
The paymentComplete method must be explicitly exposed through the ExternalInterface API to make it available to JavaScript calls. This can be done when the Flex application initializes; the applicationComplete event is a good candidate for this.
 
payPalFlex.mxml
 
ExternalInterface.addCallback('paymentComplete',paymentComplete);
Finally, the following code closes the pop-up window and notifies the Flex application:
 
DoExpressCheckoutPayment.php
 
<script type="text/javascript"> function gotoflex() { window.opener.window.document.getElementById('payPalFlex').paymentComplete(); window.close(); } </script> <a class="home" id="CallsLink" href="javascript:gotoflex()">Return to Flex</a>

 
Installing the sample files

Follow these steps to install the sample files:
 
  1. Unzip the PayPalFlexFiles.zip archive and put the files in a temporary location; for example, /tmp.
  2. Copy the payPalFlex folder into your web root folder; for example, /work/www.
  3. Edit payPalFlex/ppNVP/constants.php and find the following lines:
define('API_USERNAME', 'sdk-three_api1.sdk.com'); define('API_PASSWORD', 'QFZCWN5HZM8VBG7Q'); define('API_SIGNATURE', 'A.d9eRKfd1yVkRrtmMfCFLTqa6M9AyodL0SJkhYztxUi8W9pCXF6.4NI');
Replace the values with your own PayPal API_USERNAME, API_PASSWORD and API_SIGNATURE.
 
  1. In Flash Builder 4 choose File > Import and select Flash Builder Project.
  2. Navigate to the payPalFlex.fxp file and select it
  3. Specify the Output folder location, Web root, and Root URL with values appropriate for your setup. The settings I used are shown in Figure 8; your settings may be different.
Figure 8. Specify path variables when importing the project.
Figure 8. Specify path variables when importing the project.

 
Starting PayPal Express Checkout from a Flex application running in Adobe AIR

Note: Because the Flash Player and Adobe AIR implementations described in this article both use Flex, parts of the following discussion repeat concepts introduced earlier.
 
Although Adobe AIR applications run on the desktop and the local security constraints are different from those in the browser, from a payment gateway point of view things don’t change at all. As with the Flash Player implementation, AIR is a client technology, which makes hardcoding sensitive information into the AIR application a highly insecure practice (even though the code is compiled into bytecode). This means that any credential related information (like API_USERNAME, API_PASSWORD and API_SIGNATURE) should NOT be stored in the AIR application.
 
Architectural approach and UI solutions
The architectural solution has to comply with virtually the same concurrent demands as the Flex application running in Flash Player:
 
  • It calls for an AIR front end that is stateful and is built following the single page application paradigm
  • To ensure security, the AIR application should NOT deal with PayPal API credentials.
  • The payment workflow will include a part that is hosted on the PayPal servers and that uses a standard request-response web application.
To meet these requirements, I suggest the following approach:
 
  • All PayPal API calls will be made from the server side so that API credentials will be protected.
  • Access to the PayPal web application will be from a browser page.
Security Note: Although technically it might be possible to use the HTML container of the AIR runtime, this is an insecure practice because the end user cannot visually verify that he is entering his credentials on the PayPal site. In the browser, the user can check the URL and the security certificate.
 
In the user workflow for the checkout process, the buyer:
 
  1. Chooses to checkout using PayPal in the AIR application.
    Note: To ensure security, all requests must be performed through HTTPS starting from this point in the workflow.
     
  2. Sees a browser window open and logs into PayPal to authenticate his or her identity.
  3. Reviews the transaction on PayPal.
  4. Confirms the order and pays from my site.
  5. Reviews the order confirmation on my site.
  6. Returns to the AIR application.
 
Calling the PayPal part of the checkout process from AIR
The first step of the PayPal workflow is performed in a browser window. The following code opens a browser window from the AIR application and loads the startPaymentFlex.php page:
 
payPalAIR.mxml
 
var url:URLRequest = new URLRequest(URL_ROOT + "/payPalAIR/startPaymentFlex.php"); url.data = new URLVariables(); var obj:URLVariables = new URLVariables(); url.data.movieId = '1'; url.data.paymentReason = 'Enter The Matrix'; url.method = "GET"; navigateToURL(url, "new"); currentState='Wait';
The remaining details for this step are similar to the Flex implementation discussed earlier. See Calling the PayPal part of the checkout process from Flex for more information.
 
Returning from PayPal and notifying the AIR application
Here again, I follow an approach similar to the one used to return from PayPal to a Flex application. (See Returning from PayPal and notifying the Flex application for more details on steps 4 and 5 of the workflow.)
 
The key difference lies in how to return to the AIR application (step 6). The ExternalInterface API is not suited for this job but the LocalConnection API can be used to communicate between the browser application and the AIR application. LocalConnection, while more secure than ExternalInterface, can still be exploited using techniques like DNS rewriting. As a result, it is inadvisable to pass sensitive information through it. Furthermore, since the AIR application is independent from the browser, it has a different server session. So a simple notification is not enough; I need to also pass the session id. This is not sensitive information, but it will allow the AIR application to retrieve from the server any sensitive information that the browser application has set.
 
Take a look at what needs to be done right after the PAY operation is invoked. In the transaction detail page I link to a little Flex application that only communicates with the AIR application. This little application is also used to forward the error and cancel messages. I call it using an anchor fragment.
 
DoExpressCheckoutPayment.php
 
<a class="home" id="CallsLink" href="payPalAIRReturn.html#method=doStatus">Return to AIR</a>
This little Flex application contains only code to get the cookie string from the browser (remember I need this to enable the AIR application to connect to the same server session) and send a message through LocalConnection to the AIR application:
 
payPalAIRReturn.mxml
 
private var outbound:LocalConnection = new LocalConnection(); protected function application1_applicationCompleteHandler(event:FlexEvent):void { var browserManager:IBrowserManager = BrowserManager.getInstance(); browserManager.init(); var urlObj:Object = URLUtil.stringToObject(browserManager.fragment); if (urlObj.method) { //get the cookie string ExternalInterface.call('eval','window.cookieStr = function () {return document.cookie};') var cookieStr:String = ExternalInterface.call('cookieStr'); outbound.connect("paymentSample"); outbound.send("app#payPalAIR:paymentSample",urlObj.method,cookieStr); outbound.close(); } }
Note that when launching from Flash Builder, the AIR application has no Publisher ID so the connection name is app#payPalAIR:paymentSample. After packaging and installation, the AIR application will get a Publisher ID and the connection name becomes something like this:
 
app#payPalAIR.F0B3F68E1857B8A07069FED1D0638CAF200F76EB.1:paymentSample
You can determine the publisher ID of an installed AIR application by looking at the META-INF/AIR/publisherid file within the application install directory.
 
Back in the AIR application I expose the functions through the LocalConnection API:
 
payPalAIR.mxml
 
private function initApp():void { //only allow connections from localhost //you need to replace "localhost" with the final domain //where your application will be hosted inbound.allowDomain("localhost"); inbound.client = new Object(); //this is the function that will be called by the Browser App inbound.client.doStatus = doStatus; inbound.client.doError = doError; inbound.client.doCancel = doCancel; //inbound.client. inbound.connect("paymentSample"); }
The methods doStatus(), doError(), and doCancel()receive the cookie string as a parameter. In doStatus() I check the transaction status on the server:
 
payPalAIR.mxml
 
private function doStatus(cookieStr:String):void { var srv:HTTPService = new HTTPService(); srv.headers.Cookie = cookieStr; srv.url = URL_ROOT + "/payPalAIR/getPaymentStatus.php"; srv.addEventListener(ResultEvent.RESULT,function (event:ResultEvent):void { Alert.show(event.result.status.type); nativeApplication.activate(); if (event.result.status.type == 'SUCCESS') { currentState = 'Succes'; } else { currentState = 'Fail'; } }); srv.addEventListener(FaultEvent.FAULT,function (event:FaultEvent):void { nativeApplication.activate(); currentState = 'Fail'; Alert.show(event.message.toString()); }) srv.send(); }
In the code above getPaymentStatus.php is just a simple page that gets the status from the session and serializes it in a simple XML format:
 
getPaymentStatus.php
 
<?php session_start(); ?> <status> <type><?php echo strtoupper ($_SESSION ['reshash'] ["ACK"] )?></type> </status>
 
Installing the sample files
Follow these steps to install the sample files.
 
  1. In Flash Builder 4 choose File > Import and select Flash Builder Project
  2. Navigate to payPalAIR.fxp and select it.
  3. Unzip payPalAIR.zip into your web root folder; for example, /work/www.
  4. Edit payPalAIR/ppNVP/constants.php and find the following lines:
define('API_USERNAME', 'sdk-three_api1.sdk.com'); define('API_PASSWORD', 'QFZCWN5HZM8VBG7Q'); define('API_SIGNATURE', 'A.d9eRKfd1yVkRrtmMfCFLTqa6M9AyodL0SJkhYztxUi8W9pCXF6.4NI');
Replace the defined values with your own PayPal API_USERNAME, API_PASSWORD, and API_SIGNATURE.
 
  1. In Flash Builder 4, choose File > Import and select Flash Builder Project.
  2. Navigate to payPalAIRReturn.fxp and select it.
Specify the Output folder location (where you have copied the payPalAIR folder in your web root), Web root, and Root URL with values appropriate for your setup. The settings I used are shown in Figure 9; your settings may be different.
 
Figure 9. Specify the correct path variables for your system when importing the project.
Figure 9. Specify the correct path variables for your system when importing the project.

 
Where to go from here

In this article I have demonstrated how to securely implement a payment workflow using PayPal Express Checkout and Flex, running both in Flash Player and in Adobe AIR.
 
The following resources provide more details on the technologies used in this article:
 
 

Attributions

This article was developed and authored with feedback and input from PayPal.