This page has been designed specifically for the printed screen. It may look different than the page you were viewing on the web.
Please recycle it when you're done reading.

The URI for this page is { http://blog.scrobbld.com }

The PayPal API and PHP Posted on February 15th

UPDATE: Full code listing for PayPal API (NVP Pairs) Demo in PHP5

The PayPal application programming interface (API) has been around from about 2006, and is a more secure way of performing a number of different actions without having to login to PayPal itself. So you can initiate, improve and automate PayPal functions to more suit your applicational needs, as opposed to say, screen scraping. To follow this tutorial, you’ll need a reasonable grasp of PHP, a Business PayPal account (if you have a Premier account, just upgrade: for free!), and some patience to make it work ;)

There are a couple of ways of using the API, either through name value pairs (NVP) or Simple Object Access Protocol (SOAP), which is based upon the XML protocol. I won’t go into too much detail, as this has been covered elsewhere, but the basic premise is that you send an HTTPS request to PayPal, which then sends a file back. If you have worked with the eBay API (or any other web APIs) previously, then this will be familiar, though in my honest opinion, the eBay API is a smoother experience, at least for the end user. For simplicity I’ll focus on the NVP method, which is exactly like sending name-value pairs, like so:

https://api-3t.paypal.com/nvp?METHOD=TransactionSearch&SUBJECT=paypalemail@example.com&VERSION=5

So, you just ping the URL with this query and you would get a response back that is also in NVP. Personally, I never like to mess around with sandboxes, whether PayPal or eBay (especially PayPal) as invariably the sandboxes never seem to reflect reality.

Unfortunately, (unlike eBay) you can’t mix and match with PayPal, so you send NVP, you get NVP back. Splitting this process up into a sending module (Curl), a query module, and a response module, I got the following classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class nvp {
	private $ch = null;	
	public function __construct() { }	
	private function initCurl() {
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, PP_API_ENDPOINT);
		//curl_setopt($ch, CURLOPT_VERBOSE, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, TRUE);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
		curl_setopt($ch, CURLOPT_POST, 1);
		$this->ch = $ch;
	}
	public function send(nvpQuery $query) {
		$this->initCurl();
		curl_setopt($this->ch,CURLOPT_POSTFIELDS,$query->queryString);	
		$response = curl_exec($this->ch);
		if (curl_errno($this->ch)) {
			$r = new nvpResult('', $query);
			$r->setErrorMessage("Curl Error: " . curl_errno($ch) . ": " . curl_error($ch));
			return $r;
		}
		else {
			curl_close($this->ch);				
		}
                //always return a response, regardless of whether there is an error.
		return new nvpResult($response, $query);		
	}	
}

So that’s the ‘main’ sending module, which would be called using:

$nvp = new nvp();

To query the sending module, you need a query class, nvpQuery.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class nvpQuery extends dataContainer {	
	public function __construct() {			
		$this->user = PP_API_USERNAME;
		$this->pwd = PP_API_PASSWORD;
		$this->version = PP_API_VERSION;
		$this->signature = PP_API_SIGNATURE;	
		parent::__construct(func_get_args());
	}	
	private function convertTime($time) {
		 $time = strtotime($time);
		 $date = date('Y-m-d\T00:00:00\Z',  $time);
		 return $date;
	}
	public function __get($var) {	
		if($var == "queryString") {
			return $this->generateQueryString();
		}	
		elseif(eregi("date$", $var)) {
			return $this->convertTime($this->args[$var]);
		}
		else {
			return parent::__get($var);
		}
	}	
	public function merge(nvpQuery $query) {
		if(isset($query->subject)) {
			$this->subject = $query->subject;
		}
	}
	public function generateQueryString() {
		$str = "";
		foreach($this->args as $var => $arg) {
			if(strlen($str) > 0)
				$str .= "&";
			$str .= strtoupper($var) . "="  . urlencode($this->$var);
		}	
		return $str;
	}	
}

I won’t here include the class dataContainer, but suffice to say it’s a base class that accepts name value pairs and will set them in an associative array called $args, which is available to extending classes in $this->args, but can also be called with $this->value (where value is the key in the array $args), using the magic function __get(). If the variable is not set in the declarations (e.g. private $myvar;) and it is set, though not previously declared, it will also go into the $args array, without any PHP warnings.

The constants defined in the constructor need to be defined by yourselves, and are your authentication process to access the PayPal API, which can be found in the PayPal documentation. As mentioned previously, I would recommend trying this out on a live account (not the sandbox). If you do decide to use it on the sandbox, then remember you’ll require different credentials for both the live and sandbox sites.

$this->user = PP_API_USERNAME;
$this->pwd = PP_API_PASSWORD;
$this->version = PP_API_VERSION;
$this->signature = PP_API_SIGNATURE;

So, finally, you require a result object, which decodes the NVP result from PayPal, and lets the developer know if there were any errors in the request or recall from PayPal, which I’ve called, (originally) nvpResult.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class nvpResult extends dataContainer {
	public $rows = array();
	private $fault = false;	
	public $nvpquery = null;
	public function __construct($response, nvpQuery $query) {		
		parent::__construct();
		$this->nvpquery = $query;
		$this->decodeResponse($response);
	}
	public function setErrorMessage($msg) {
		$this->l_longmessage0 = $msg;
	}
	public function isSuccess() {			
		return eregi("^Success", $this->ack) && !$this->fault;		
	}
        private function extractList($class) {
                //at this point, the args is a big list, so we need to split
                //them up into individual objects, say transaction objects.
		foreach($this->args as $key => $value) {
			if(preg_match("/^l_(.*?)([0-9]+)$/", $key, $c)) {
				if(!isset($this->rows[$c[2]])) $this->rows[$c[2]] = new $class($this->nvpquery);					
				$row = &$this->rows[$c[2]];
				$row->{$c[1]} = $value;	
				//get rid of the variable from the the current args list.
				unset($this->{$c[0]});
			}
		}
	}
	private function decodeResponse($nvpstr) {
		if(strlen($nvpstr) == 0) $this->fault = true;
		while(strlen($nvpstr)){
			//postion of Key
			$keypos= strpos($nvpstr,'=');
			//position of value
			$valuepos = strpos($nvpstr,'&') ? strpos($nvpstr,'&'): strlen($nvpstr);
			/*getting the Key and Value values and storing in a Associative Array*/
			$keyval=substr($nvpstr,$init,$keypos);
			$valval=substr($nvpstr,$keypos+1,$valuepos-$keypos-1);
			//decoding the respose
			$val = strtolower(urldecode($keyval));
			$this->$val = urldecode($valval);
			$nvpstr=substr($nvpstr,$valuepos+1,strlen($nvpstr));
	        }	
                if($this->isSuccess()) {
			switch($this->nvpquery->method) {
				case "TransactionSearch":
                                        //put nvpTransaction objects into the $row
                                        //array
					$this->extractList("stdClass");
					break;
			}		
		}			
	}		
}

So, to work through an example, say I want to search for a number of transactions between two different dates. You can also include keywords and so forth, but here it’s just any transactions between two dates.

$nvp = new nvp();
$result = $nvp->send(new nvpQuery("method", "TransactionSearch", "subject", "yourpaypalemail@example.com", "startdate", "10th December 2008", "enddate", "11th December 2008"));
if($result->isSuccess()) {
	print_r($result);
        //success!
}
else { 
	echo print_r($result->nvpquery);
        //not successful.
}

So; that’s about all there is to it. If you want the API to behave a little bit like IPN, e.g. a push methodology as opposed to a pull one, you can query the API whenever you receive an payment notification from PayPal.

Trackback URL

Some Responses to “The PayPal API and PHP” :

  1. I try to follow the above but found out it’s incomplete codes :(

    Commented Boyd on April 9th, 2009.
  2. It is, I will attach the complete code shortly.

    Commented admin on April 17th, 2009.
  3. Can you let us know when please? Nice one, cheers!

    Commented daktau on April 29th, 2009.
  4. Hi there, I’ve now added it, here: http://blog.scrobbld.com/wp-content/uploads/2009/05/paypalapidemophp.txt

    Commented admin on May 1st, 2009.
  5. > $date = date(‘Y-m-d\T00:00:00\Z’, $time);
    is not so good since PP asks for the GMT time while you feed it with your local machine time

    try
    gmdate(“c”);
    instead, it does the work

    Anyway, a helpful post, thanks

    Commented David on April 16th, 2010.
  6. i want to integrate my shopping cart application with pay pal.
    any body help me.

    Commented asif lohar on September 20th, 2010.
  7. Yes, you are very much right, you cannot open a PayPal account for Charity, if your charity is registered in India, same thing happened with me also, I applied a Charity account for my client and after completing all the formalities they just denied for the account.
    I have a suggestion for you, apply for a normal account with some other website and then use it for your Charity website, I have list of websites who are doing the same thing.
    You can also try some other Payment Gateways like CCavenue, but in case you are using Vbulletin script, I don’t think so that it may help you. The first suggestion may work for you.

    Commented ritikaa on September 24th, 2010.
  8. [...] The PayPal API and PHP [...]

    Commented PHP: risorse su PayPal | Gabriele Romanato on August 7th, 2011.
  9. It would have been a lot cleaner and easier to follow, if it weren’t tied into your dataContainer class. This makes it much harder for a newbie to follow and make sense of. :(

    Commented Tsar Bomba on November 4th, 2011.
  10. [...] though and the Payments Pro solution only costs $20 per month plus transaction fees. Good stuff!I've just upgraded a clients payment system to use the Paypal API. One new feature that I like is th…quite a number of things to do to get the certificate installed etc. There are certainly some [...]

    Commented PayPal Payments Pro 4.2 on September 24th, 2012.
  11. I want to receive notification when someone buys something from my website i have tried adding my website address to the ipn forwarding but it does nt do anything so what shall i do and what is the ipn forwarding there for if it does not work

    Commented Joan Green on September 9th, 2013.
Leave your own comments about this post: