Breaking down the PayPal API

Binpress PHP Web development April 23rd, 2011 by Eran Galperin

PayPal is the most popular platform for receiving online payments. The relative ease of opening a PayPal account and receiving payments compared to opening a merchant account for a traditional payment gateway is the number one reason, and another is the comprehensive API they provide for their payment services.

Foreclosure: The PayPal API is amongst the worst I've ever had to deal with - inconsistencies, sometimes poor and conflicting documentation, unpredictable failures and account changes and major differences between the live and sandbox versions all conspire to make working with the PayPal API quite a pain in the ass.

Unfortunately, there doesn't seem to be any better alternatives currently, so hopefully this guide will help ease the pain for some of you out there taking your lumps working the API into your applications.

The different payment options

PayPal offers a variety of payment options, which might be confusing at first:

  • Express Checkout - The premier PayPal service. Express checkout allows you to receive payments without having a merchant account or meeting special requirements other than having your account verified (either via a bank account or a credit card).Previously you could receive Express Checkout payments from PayPal users only, but PayPal has since added a credit-card option for non-PayPal users making this service accessible to practically anyone with a major credit-card.Note that the Express Checkout process occurs on the PayPal platform and thus can never be fully integrated into your site experience completely.
  • Direct Payment - The Direct Payment method allows you to receive credit-card payments directly through an API call. This allows you to host the payment process on your site in full, which might make for a more complete shopping experience for shoppers.The Direct Payment method has several variations that allow you to authorize a payment and complete it at a later date - the appropriately named methods Authorization and Capture. The direct payments methods are a part of the Website Payments Pro API, which is only available U.S accounts.
  • Recurring Payments - allow you to set up a recurring transaction - i.e, a subscription payment.
  • Mass Payments - allow you to transfer money to multiple accounts at once.

This is a comprehensive list, but it covers the main payment options (see the docs for more).

Making API requests

PayPal supports two main formats over HTTP - NVP and SOAP. NVP is short for Node-Value-Pairs and SOAP stands for Simple Object Access Protocol. I will cover the NVP approach which I prefer to SOAP's comparatively verbose and general complex syntax.

Each of the API methods have different parameters but they all share some basic parameters which are used to identify the API account and sign the transaction. Those include:

  • USER - Your PayPal API Username.
  • PWD - Your PayPal API Password.
  • VERSION - Version number of the NVP API service, such as 56.0.
  • SIGNATURE - Your PayPal API signature string. This parameter is optional if you use a certificate to authenticate

The last required parameter is METHOD which declares which API method we are calling.

Requests are made over HTTPS. We'll use cURL to build up our basic request, and encapsulate the process in a function:

function paypalApiRequest($method,$params = array()) {
	if( empty($method) ) { //Check if API method is not empty
		return false;
	}
 
	//Our request parameters
	$requestParams = array(
		'METHOD' => $method,
		'VERSION' => '65.1',
		'PWD' => 'ourAPIpassword',
		'USER' => 'ourAPIusername',
		'SIGNATURE' => 'ourAPIsignature'
	);
	//Building our NVP string
	$request = http_build_query($requestParams + $params);
 
	//cURL settings
	$curlOptions = array (
		CURLOPT_URL => $this -> getOption('endpoint'),
		CURLOPT_VERBOSE => 1,
		CURLOPT_SSL_VERIFYPEER => true,
		CURLOPT_SSL_VERIFYHOST => 2,
		CURLOPT_CAINFO => dirname(__FILE__) . '/cacert.pem', //CA cert file
		CURLOPT_RETURNTRANSFER => 1,
		CURLOPT_POST => 1,
		CURLOPT_POSTFIELDS => $request
	);
 
	$ch = curl_init();
	curl_setopt_array($ch,$curlOptions);
 
	//Sending our request - $response will hold the API response
	$response = curl_exec($ch);
 
	//Checking for cURL errors
	if (curl_errno($ch)) {
		//Handle errors
	} else  {
		curl_close($ch);
		$responseArray = array();
		parse_str($response,$responseArray); // Break the NVP string to an array
		return $responseArray;
	}
}

Note that I use a CA certificate file for SSL certificate validation. You can obtain the file from the cURL website or any trusted source. Update the file to the certificate file according to where you placed it.

The response returned would be in NVP format as well. A parameter named ACK signifies the status of the request - Success or SuccessWithWarning on a successful request and Error or Warning values when the request failed. There are many reasons that a request could fail, different ones for each API method, and those are covered in detail in the manual. Keep in mind that the parameter values are case-sensitive so you should code against it accordingly.

Express Checkout

In this post I'll focus on the Express Checkout process and use it to illustrate how you can work with the API:

The Express Checkout process works as follows:

  • We request a checkout token from PayPal using the order details
  • If successful, we redirect the user to the PayPal endpoint using the received token
  • User completes / cancels payment on the PayPal platform and is redirected back to our site
  • We complete the payment either when the user is redirected back or via IPN

1. Getting the checkout token - SetExpressCheckout

We initiate the Express Checkout process by passing the order details to the PayPal API and receive a token string that identifies it. This token would be used in the next step to redirect to PayPal.

Required parameters:

  • METHOD - 'SetExpressCheckout' (the API method we're using)
  • RETURNURL - The URL the user will be redirected to after payment process is complete
  • CANCELURL - The URL the user will be redirected to after cancelling the payment process
  • PAYMENTREQUEST_0_AMT - The transaction total amount.
  • PAYMENTREQUEST_0_ITEMAMT - The cost total of the items in the order, excluding shipping, taxes and other costs. If there are no extra costs, it should be the same value as PAYMENTREQUEST_0_AMT.

There are additional parameters we can pass to add more information about the order, some of which have default values:

  • PAYMENTREQUEST_0_CURRENCYCODE - Payment currency. A three-letter code, default is USD.
  • PAYMENTREQUEST_0_SHIPPINGAMT - Total shipping costs for this order
  • PAYMENTREQUEST_0_TAXAMT - Total tax amount for this order (required if per item tax is specified, see below)
  • PAYMENTREQUEST_0_DESC - Order description

We can also add details about individual items in the order:

  • L_PAYMENTREQUEST_0_NAMEm - Item name.
  • L_PAYMENTREQUEST_0_DESCm - Item description.
  • L_PAYMENTREQUEST_0_AMTm - Item cost
  • L_PAYMENTREQUEST_0_QTYm - Item quantity

'm' is a variable index identifying the item (use the same number for all details of the same item).

There are many other optional options (read more in the API docs).

We'll use the function we wrote above for building the SetExpressCheckout request:

//Our request parameters
$requestParams = array(
	'RETURNURL' => 'http://www.yourdomain.com/payment/success',
	'CANCELURL' => 'http://www.yourdomain.com/payment/cancelled'
);
 
$orderParams = array(
	'PAYMENTREQUEST_0_AMT' => '500',
	'PAYMENTREQUEST_0_SHIPPINGAMT' => '4'
	'PAYMENTREQUEST_0_CURRENCYCODE' => 'GBP'
);
 
$item = array(
	'L_PAYMENTREQUEST_0_NAME0' => 'iPhone',
	'L_PAYMENTREQUEST_0_DESC0' => 'White iPhone, 16GB',
	'L_PAYMENTREQUEST_0_AMT0' => '500',
	'L_PAYMENTREQUEST_0_QTY0' => '1'
);
$response = paypalApiRequest('SetExpressCheckout',$requestParams + $orderParams + $item);

2. Redirecting to PayPal using the Checkout Express token

If the request was successful, we'll receive a checkout token in the 'TOKEN' parameter of the response.

if(is_array($response) && $response['ACK'] == 'Success') { //Request successful
      $token = $response['TOKEN'];
      header('Location: https://www.paypal.com/webscr?cmd=_express-checkout&token=' . urlencode($token));
}

The user will now go through the purchase process on the PayPal site. If he confirms or cancels it he will return to one of the URLs we specified in the request.

3. Completing the transaction

Assuming the user confirmed the transaction, he will be redirected to our site by PayPal. At this point we have two relevant API methods we should use - 'DoExpressCheckoutPayment' will complete the transaction, but before that we might want to get additional information on the buyer using 'GetExpressCheckoutDetails'.

PayPal will redirect the user back from the purchase with the checkout token, which we will use to call those methods. The token will be available in the URL query parameters via the 'token' parameter. We will check for its existence in the confirmation URL, and send our API requests if we find it.

The 'GetExpressCheckoutDetails' method requires only the checkout token. 'DoExpressCheckoutPayment' requires a couple of additional parameters:

  • PAYMENTREQUEST_0_PAYMENTACTION - Payment action. Should be set to 'Sale' unless we specified a different action in the 'SetExpressCheckout' method (possible values include 'Authorization' and 'Capture')
  • PAYERID - Unique PayPal account identification. This is returned in the URL query parameters as well, in the 'PayerID' parameter, and can also be retrieved from the details returned by 'GetExpressCheckoutDetails'.
if( isset($_GET['token']) && !empty($_GET['token']) ) { // Token parameter exists
	// Get checkout details, including buyer information.
	// We can save it for future reference or cross check with the data we have
	$checkoutDetails = paypalApiRequest('GetExpressCheckoutDetails', array('TOKEN' => $_GET['token']));
 
	// Complete the checkout transaction
	$requestParams = array(
		'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
		'PAYERID' => $_GET['PayerID']
	);
 
	$response = paypalApiRequest('DoExpressCheckoutPayment',$requestParams);
	if( is_array($response) && $response['ACK'] == 'Success') { // Payment successful
		// We'll fetch the transaction ID for internal bookkeeping
		$transactionId = $response['PAYMENTINFO_0_TRANSACTIONID'];
	}
}

Just the tip of the iceberg

In this post I showed a very simple way of working with the PayPal API. I didn't discuss error handling, more complex methods such as DirectPayment (for direct credit-card payments) and more. There is enough to do there that I could fill 10 more articles like this and not cover everything.

If you don't want to deal with the API yourself, you can check out my PayPal class over at Binpress. It covers additional APIs including handling Direct Payments (credit-card payments), Mass payments (paying multiple recipients at the same time) and IPN (instant payment notifications).

Tags: , , ,

If you liked this article you should follow me on Twitter and/or share below:
  • http://twitter.com/benwallis Ben Wallis

    Is this API the same as the Paypal Adaptive Payments API? Or is that something completely different?

    Thanks.

  • http://www.binpress.com Eran Galperin

    The Paypal adaptive payments API is unfortunately completely different for some reason. I’m working on integrating it into our service, so I might run a post about that in the near future.

  • http://digitalmantra.org Ben Klang

    Would it be possible to amend the example to include SSL certificate validation? Or at least some notes on what is required to perform validation? Financial transactions are among the most sensitive of any interface we will write, and running cURL without certificate validation enabled feels a bit like running with scissors.

  • http://www.binpress.com Eran Galperin

    I modified the code to use a CA cert file for validation.

    It’s interesting to note though that this is the code PayPal is using in it’s SDK (SSL verification off).

  • Anonymous

    I would like to second your comments, only for the XMLPay method of accessing PayFlow Gateway — insufficient documentation, and even inaccuracies in places (in the XML schema at the end of the document, even!) Dunno why such a huge company would provide such weak support.

  • Vinitha Rajesh

    Thanks for this good post .I want to point out that please change $nvpreq
    to $request in $curlOptions array ie CURLOPT_POSTFIELDS => $nvpreq when you are referring this .

  • Ricard Clau

    I agree that PayPal API is far from perfect, bust trust me, Spanish Sermepa Payment API (which is where all VISA, MasterCard and AMEX direct payments go here) is so worse!

  • Pingback: WordPress Shops and PayPal | Gary Eckstein

  • Pingback: Elsewhere, on September 21st - Once a nomad, always a nomad