22 March 2010
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.
Intermediate
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.
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.
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.
Summing this up, the architectural solution has to comply with three concurrent demands:
To meet these requirements, I suggest the following approach:

The user workflow for the checkout process (see Figure 1) includes the following stages:
Note: To ensure security, all requests must be performed through HTTPS starting from this point in the workflow.






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.
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>
Follow these steps to install the sample files:
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.

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.
The architectural solution has to comply with virtually the same concurrent demands as the Flex application running in Flash Player:
To meet these requirements, I suggest the following approach:
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:
Note: To ensure security, all requests must be performed through HTTPS starting from this point in the workflow.
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.
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>
Follow these steps to install the sample files.
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.
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.

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:
PayPal Technical Documentation
Documentation on the ExternalInterface class
Documentation on the LocalConnection class
This article was developed and authored with feedback and input from PayPal.

This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License