Tips for PHP Developers on the PayPal API

This entry was posted Monday, 13 June, 2011 at 8:35 am by Mario Lurig

I recently had a large amount of frustration and work trying to get an integration going with the PayPal Express Checkout for Digital Goods API. The steps to just get an account were tricky, but then weeding through all of the API documentation was hard. Mind you, I had just come off of building integrations with both SendGrid and Twilio, which have fantastic RESTful APIs. In all, the entire process was 8 hours. After that much trouble, I wanted to put online a few of the tips, tricks, code, and documentation links to help others out.

Documentation

PayPal does offer documentation, and in many ways it is really robust. However, it doesn’t do a good job of stepping you through the basics of connecting, then the integration, and doesn’t offer clear examples of how the responses will be returned. However, here is where I found all the relevant details.

 

Testing

PayPal does do a great job of offering a SandBox account, so you can test every scenario before turning on the real credentials. They also do a great job of including a wizard to auto-generate the appropriate accounts for testing. Really, this was the best part of the whole experience. You can access the PayPal Sandbox at anytime. You get to use API credentials for the sandbox, and then just switch them out when you are ready with your real account’s API credentials.

Sending Requests to the API

The basic principle is to send an HTTP POST with all the important values over to the API. To do so, below is some helpful code. First and foremost, a function for sending over a cURL request:

function CurlMePost($url,$post){ 
	// $post is a URL encoded string of variable-value pairs separated by &
	$ch = curl_init();
	curl_setopt ($ch, CURLOPT_URL, $url);
	curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt ($ch, CURLOPT_POST, 1);
	curl_setopt ($ch, CURLOPT_POSTFIELDS, $post); 
	curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 3); // 3 seconds to connect
	curl_setopt ($ch, CURLOPT_TIMEOUT, 10); // 10 seconds to complete
	$output = curl_exec($ch);
	curl_close($ch);
	return $output;
}

Before I pass along the code for your initial communication, it’s important to mention how PayPal’s API responds to requests. It actually outputs directly to the page a url string much like you pass to the function above. Thus, you’ll notice that in the below code, I use the function parse_str() to break it into an array that I can reference and work with it easily. With the initial communication, there are two response variables: ACK and TOKEN. TOKEN is only returned if ACK is equal to ‘Success’ or ‘SuccessWithWarning’ (this is the status message from PayPal).

/************************************************
Note: $cost variable must be provided by you.
In this example, I am sending over only 1 item, 
thus item cost and total cost are the same.
************************************************/
//$baseurl = 'https://api-3t.sandbox.paypal.com/nvp'; //sandbox
$baseurl = 'https://api-3t.paypal.com/nvp'; //live
$username = urlencode('yourApiUsernameFromPaypal');
$password = urlencode('yourApiPasswordFromPaypal');
$signature = urlencode('yourSignatureFromPaypal');
$returnurl = urlencode('http://yourURL'); // where the user is sent upon successful completion
$cancelurl = urlencode('http://yourURL'); // where the user is sent upon canceling the transaction
$post[] = "USER=$username";
$post[] = "PWD=$password";
$post[] = "SIGNATURE=$signature";
$post[] = "VERSION=65.1"; // very important!
$post[] = "PAYMENTREQUEST_0_CURRENCYCODE=USD"; 
$post[] = "PAYMENTREQUEST_0_AMT=$cost";
$post[] = "PAYMENTREQUEST_0_ITEMAMT=$cost";
$post[] = "PAYMENTREQUEST_0_PAYMENTACTION=Sale"; // do not alter
$post[] = "L_PAYMENTREQUEST_0_NAME0=NameOfDigitalGood"; // use %20 for spaces
$post[] = "L_PAYMENTREQUEST_0_ITEMCATEGORY0=Digital"; // do not alter
$post[] = "L_PAYMENTREQUEST_0_QTY0=1";
$post[] = "L_PAYMENTREQUEST_0_AMT0=$cost";
$post['returnurl'] = "RETURNURL=$returnurl"; // do not alter
$post['cancelurl'] = "CANCELURL=$cancelurl"; // do not alter
$post['method'] = "METHOD=SetExpressCheckout"; // do not alter
 
$post_str = implode('&',$post);
$output_str = CurlMePost($baseurl,$post_str);
parse_str($output_str,$output_array);
$ack = $output_array['ACK'];
$token = (!empty($output_array['TOKEN'])) ? $output_array['TOKEN'] : '';

If you get back a successful response, and thus a TOKEN, then you will be reusing that TOKEN to redirect the user to PayPal per the documentation (make sure you urlencode it). While I didn’t make use of the javascript overlay they provide (it just made it more and more complicated), the URLs you can use are noted below:

//$redirecturl = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=$token"; //sandbox
$redirecturl = "https://www.paypal.com/incontext?token=$token"; //live

After the Transaction

This is where the PayPal documentation and behaviour gets weird. See, your return and cancel urls (which I used the same URL and smart code) will be where the respondent is sent, and PayPal will always send over as a query string ($_GET) the token. Of course, in this situation, it is $_GET[‘token’] (notice it is lowercase now). So if they cancel, you will get just a token back. If it is successful, you will also get PayerID: $_GET[‘PayerID’]. If you get that back, you have to do a final confirmation with PayPal, passing back almost everything all over again, and also including the PayerID and token.

$payerid = urlencode($_GET['PayerID']);
$token = urlencode($_GET['token']);
 
//$baseurl = 'https://api-3t.sandbox.paypal.com/nvp'; //sandbox
$baseurl = 'https://api-3t.paypal.com/nvp'; //live
$username = urlencode('yourApiUsernameFromPaypal');
$password = urlencode('yourApiPasswordFromPaypal');
$signature = urlencode('yourSignatureFromPaypal');
$post[] = "USER=$username";
$post[] = "PWD=$password";
$post[] = "SIGNATURE=$signature";
$post[] = "VERSION=65.1";
$post[] = "PAYMENTREQUEST_0_CURRENCYCODE=USD";
$post[] = "PAYMENTREQUEST_0_AMT=$cost";
$post[] = "PAYMENTREQUEST_0_ITEMAMT=$cost";
$post[] = "PAYMENTREQUEST_0_PAYMENTACTION=Sale";
$post[] = "L_PAYMENTREQUEST_0_NAME0=NameOfDigitalGood"; // use %20 for spaces
$post[] = "L_PAYMENTREQUEST_0_ITEMCATEGORY0=Digital";
$post[] = "L_PAYMENTREQUEST_0_QTY0=1";
$post[] = "L_PAYMENTREQUEST_0_AMT0=$cost";
$post['method'] = "METHOD=DoExpressCheckoutPayment";
$post['token'] = "TOKEN=$token";
$post['payerid'] = "PayerID=$payerid";
$post_str = implode('&',$post);
$output_str = CurlMePost($baseurl,$post_str);
parse_str($output_str,$output_array);

This last call returns just like the first one (where you got the token), and includes a lot more data. The most important of course is still ACK, which should return ‘Success’. It returns some really useful information, including fees charged, and unfortunately I lost my notes on the fields returned for digital goods. However, the one I chose to store, the most important one in my opinion, is the Transaction ID:

$transactionid = $output_array['PAYMENTINFO_0_TRANSACTIONID'];

There you have it, a quick guide on working with the PayPal API for Digital Goods. Do make sure if the payments are less than $12, that you set your PayPal account to micropayments for the $0.05 + 5% fee rate, which makes a huge difference on small amounts, compared to the standard rate of $0.30 + 2.9%.

Update: June 14, 2011

A friend pointed out that for security, I really should use a cURL with SSL verification enabled. For those interested in taking this route, please see the following well-written guide on downloading the certificate and adjusting the cURL call: UnitStep.net. Here is also a new function using your downloaded certificate (renamed it by adding .crt to the end of the default name):

function CurlMePaypal($url,$post){
	$ch = curl_init();
	curl_setopt ($ch, CURLOPT_URL, $url);
	curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, true);
	curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 2);
	curl_setopt ($ch, CURLOPT_CAINFO, getcwd() . "/api-3t.paypal.com.crt");
	curl_setopt ($ch, CURLOPT_POST, 1);
	curl_setopt ($ch, CURLOPT_POSTFIELDS, $post); 
	curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 3);
	curl_setopt ($ch, CURLOPT_TIMEOUT, 10);
	$output = curl_exec($ch);
	curl_close($ch);
	return $output;
}
Share and Enjoy:
  • Facebook
  • Twitter
  • Reddit
  • StumbleUpon
  • Digg
  • del.icio.us
  • Tumblr
  • PDF

11 Comments to Tips for PHP Developers on the PayPal API

  1. Sasha says:

    June 26th, 2011 at 7:52 am

    Thanks for the article, Mario. I struggled with many of the same issues, and in my quest to find answers I ran across a nice digital goods script from Firesage Solutions. I was literally up and running in 10 minutes, without having to worry about all the Paypal communication, tokens, and parameter parsing. It even simplified the product details, as well as the variables for both post and get. I only wish I had found it before I wasted two days of my life working from the Paypal docs. Hopefully this post saves someone the same frustration.

    http://firesage.com/digitalgoods.php

  2. Brent says:

    July 10th, 2011 at 10:46 pm

    Thanks for publishing Mario. The more community documentation we can get out there for Digital Goods the better.

    After I deciphered the PayPal documentation, I abstracted the API with an easy-to-use PHP class. I’ve released the class free & open-source on GitHub. You can find it here: https://github.com/thenbrent/paypal-digital-goods

    Despite already having battled (and beaten) the PayPal Doc Demon, you may still find the class useful for interacting with the API.

  3. Erik says:

    May 4th, 2012 at 12:49 pm

    Thank you very much for sharing this. Exactly what I was looking for (basically in order to avoid spending too much time in PayPal documentation hell, crappy PHP4 libraries, examples and infos you never can be sure if it applies).

    Kudos! Much appreciated.

  4. Tongny says:

    September 20th, 2012 at 9:40 pm

    Thanks very much. It help me alot!

    Thanks!
    Tongny

  5. vinawap.mobi says:

    May 6th, 2013 at 9:13 pm

    Thanks for the article, Mario

  6. Janine says:

    May 13th, 2013 at 1:50 pm

    all the time i used to read smaller content that
    as well clear their motive, and that is also happening with
    this article which I am reading here.

  7. butowap.mobi says:

    July 12th, 2013 at 4:21 am

    very useful! Thanks for your source

  8. Deepak Vishwakarma says:

    January 16th, 2014 at 9:40 pm

    Thank you so much, I was using code provided by Paypal Integration Wizard but it kept failing due to some reason or the other, I used your code and it worked like charm the very first time. What a relief!! Thank you!!

  9. Muhammad Azeem says:

    April 28th, 2014 at 5:17 am

    Thanks dude…

    Its really helpful. Paypal’s extensive documentation stuck my mind. Nice article.

  10. Gabriel Villalobos says:

    August 3rd, 2014 at 8:22 pm

    Thanks a lot, this solutions works amazing for all those needs to send request through ajax, this php code helps a lot to avoid cross domain issues. I’m very glad bro.

  11. vgat says:

    December 16th, 2014 at 11:46 pm

    Is old! Pls update source

Leave a comment