Home > air, flex, payment gateways, tutorials > Integrating PayPal Express Checkout with AIR and Flex

Integrating PayPal Express Checkout with AIR and Flex

July 30th, 2009

This article continues the series about integrating Flex and AIR with payment gateway services. Parts of this article are very similar with the other posts about this subject:
Integrating PayPal Express Checkout with Flex(which I recommend reading before this article),Using Amazon Flexible Payment System with Flex and Using Amazon Flexible Payment System with Flex & AIR.

I will focus mainly on how to take the PayPal + Flex workflow (described here) out of the browser and into an AIR application.

Although 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 a result, we can conclude that because AIR is a client technology hardcoding sensitive information into an AIR app is highly insecure (even though the code is compiled into bytecode). This means that any credential related info (like API_USERNAME, API_PASSWORD and API_SIGNATURE) should NOT be stored in the AIR application.

Architectural Approach and UI solutions

Summing this up, our architectural solution has to comply with some concurrent demands:

  • an AIR front-end that is stateful and is built following the single page app paradigm
  • the need for security that requires that the AIR app should NOT deal with PayPal API credentials
  • The payment workflow has a part that is hosted on the PayPal servers and that is a standard request-response web application

In order to address these I propose the following approach:

  • all PayPal API calls should be done on the server side so that API credentials will be protected
  • the access to the PayPal web application should be done in a browser page. IMPORTANT SECURITY NOTE: Although technically it might be possible to use the HTML container of the AIR runtime this is an unsecure practice because the end user cannot visually verify that he is entering his credentials on the PayPal site. In the browser he can check the URL and the security certificate.

So the buyer workflow becomes:

  1. (Step 1) Chooses to checkout using PayPal in the AIR application (Security Note: Starting from this point it is mandatory that all requests are done through https)
  2. (Step 2) Sees a browser window open and logs into PayPal to authenticate his/her identity
  3. (Step 3) Reviews the transaction on PayPal
  4. (Step 4) Confirms the order and pays from your site
  5. (Step 5) Reviews the order confirmation on your site
  6. (Step 6) Returns to the AIR application


Calling the PayPal part of the checkout process

So the PayPal workflow should be open in a browser window. Here is how to do that in the AIR app:

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");

Everything else is pretty similar with how you call the PayPal workflow from a Flex app.

Returning from PayPal and notifying the AIR application

Here again we follow an approach similar with how you return from PayPal to a Flex application (Step 4 and Step 5).

The only thing that differs is how we get back to the AIR application (Step 6). ExternalInterface is not suited for this job but we can use instead the LocalConnection to make the browser application communicate with the AIR one. LocalConnection, while being more secure than ExternalIterface, can still be exploited using techniques like DNS rewriting so we can’t pass sensitive information through it. Furthermore, since the AIR app is independent from the browser this means that our browser app has a different server session than the AIR App. So a simple notification is not enough: we 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.

Let’s take a look on what we need to do right after we call the PAY operation in our return page. In the transaction detail page we will link to a little Flex application that will only communicate with the AIR application. We can use this little application to also forward the error and cancel messages. We will call it using an anchor parameter:

<a class="home" id="CallsLink" href="payPalAIRReturn.html#method=doStatus">Return to AIR</a>

This little application will only contain some code that will get the cookie string from the browser (remember we need this to make the AIR application connect to the same server session) and send a message through LocalConnection to the AIR application:

private var outbound:LocalConnection = new LocalConnection();
protected function application1_applicationCompleteHandler(event:FlexEvent):void
	var browserManager:IBrowserManager = BrowserManager.getInstance();
	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');

Please notice that when launching from Flex Builder the AIR Application has no Publisher ID so the connection name is “app#payPalAIR:paymentSample”. After packaging and installation the AIR app will get a Publisher ID so the connection name becomes something like this: app#payPalAIR.F0B3F68E1857B8A07069FED1D0638CAF200F76EB.1:paymentSample

You can get the publisher ID of the installed AIR app by looking at the META-INF/AIR/publisherid file within the application install directory.

Back in the AIR application we need to expose the functions through local connection:

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.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;



All methods (doStatus, do Error and doCancel) will receive the cookie string as a parameter. In do Status we need to check the transaction status on the server:

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 {

		if (event.result.status.type == 'SUCCESS') {
			currentState = 'Succes';
		} else {
			currentState = 'Fail';
	srv.addEventListener(FaultEvent.FAULT,function (event:FaultEvent):void {
		currentState = 'Fail';

where getPaymentStatus.php is just a simple page that gets the status from the session and serialize it in a simple XML format:

	<type><?php echo strtoupper ($_SESSION ['reshash'] ["ACK"] ) ?></type>

Installing the sample files

  1. In Flash Builder click File->Import .. and choose Flash Builder Project
  2. Choose payPalAIR.fxp
  3. Unzip payPalAIR.zip into your Web Root folder (For example: /work/www).
  4. Edit payPalAIR/ppNVP/constants.php and replace
  5. define('API_USERNAME', 'sdk-three_api1.sdk.com');
    define('API_SIGNATURE', 'A.d9eRKfd1yVkRrtmMfCFLTqa6M9AyodL0SJkhYztxUi8W9pCXF6.4NI');


  6. In Flash Builder click File->Import … and choose Flash Builder Project.
  7. Choose payPalAIRReturn.fxp
  8. Fill the Output Folder Location (this one should point to where you have unzipped payPalAIR.zip, in your web root), Web Root and Root URL with your values. It should look something like this:

Categories: air, flex, payment gateways, tutorials Tags:
  1. Spiros Kabasakalis
    November 21st, 2009 at 11:27 | #1

    Great tutorial.Could it be possible to integrate flex with the Website Payments Standard solution using a “bridge” php file that posts the nv pairs with curl?

    (instead of


    Thans in advance

Comments are closed.