The care, feeding, and breeding of PayPal on your web site

PayPal is notorious for being hard to interface with. People install eStore packages just because they don’t want to deal with it! But I finally found a way to access the data fairly reliably.

If you are trying to use PayPal for your web site payments and want as simple an interface as possible, I highly recommend THIS tutorial. All the others I tried didn’t work. This one was written in 2010 and uses cURL, and remarkably, it actually works.

Step 3: Catch the return

The URL they are returned to could for example look like this (split for easier reading):

http://business.example.com/order/123/?tx=6BC88318RN685282M
&st=Completed
&amt=20.00
&cc=USD
&cm=
&item_number=BEAR05

As you can see we get some GET parameters to work with here, but the only one you need is the Transaction ID, tx:

if(isset($_GET[‘tx’]))
{
$tx = $_GET[‘tx’];
// Further processing
}

The reason we don’t care about the rest is that they’re not trustworthy at all. Since they are GET parameters we can easily tamper with them in the address bar of our browser, and we just can’t have any of that, can we?

To get good and trustworthy data we need to send this transaction id back to PayPal together with our PDT Identity Token. If the id and token are valid, we will get real and actual transaction details back in return.

Step 4: Do a POST request back to PayPal

Sending the Transaction ID and our PDT Identity Token back to PayPal can be done in various ways from PHP. I used cURL which I found to be pretty clean and easy to use.

// Init cURL
$request = curl_init();// Set request options
curl_setopt_array($request, array
(
CURLOPT_URL => ‘https://www.sandbox.paypal.com/cgi-bin/webscr’,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => http_build_query(array
(
‘cmd’ => ‘_notify-synch’,
‘tx’ => $tx,
‘at’ => $your_pdt_identity_token,
)),
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => FALSE,
// CURLOPT_SSL_VERIFYPEER => TRUE,
// CURLOPT_CAINFO => ‘cacert.pem’,
));

// Execute request and get response and status code
$response = curl_exec($request);
$status   = curl_getinfo($request, CURLINFO_HTTP_CODE);

// Close connection
curl_close($request);

The cURL options we set means roughly: Use this URL, do a POST, send these variables, return the transfer as a string when I call curl_exec and do not include the HTTP headers in that string. The reason behind the last two are in this post. I’ve commented them out, and if it works without them, just leave them out. For me it worked fine on one server, while on another I had to add them.

Note: For security reasons, PayPal will only allow you to use the transaction id to get a response up to 5 times. After that it will be invalidated and you will get an error back instead of the transaction data. Fair enough for regular use, but found it a bit annoying while developing since it took some tries to get the result I wanted :P

Step 5: Check the response

The response will be a string that looks something like this:

SUCCESS
mc_gross=20.00
invoice=AF000001
protection_eligibility=Eligible
address_status=confirmed
payer_id=ADSZV7LHTCJM4
tax=0.00
address_street=Candy+Road+77
payment_date=12%3A04%3A18+Oct+19%2C+2010+PDT
payment_status=Completed
charset=windows-1252
address_zip=99501
first_name=Alice
mc_fee=0.88
address_country_code=US
address_name=Alice+Foobar
custom=
payer_status=unverified
business=paypal%40business.example.com
address_country=United+States
address_city=Anchorage
quantity=1
payer_email=alice.foobar%40example.com
txn_id=6BC88318RN685282M
payment_type=instant
last_name=Foobar
address_state=AK
receiver_email=paypal%40business.example.com
payment_fee=0.88
receiver_id=JNNUMM42GJB9U
txn_type=web_accept
item_name=Stuffed+bear
mc_currency=USD
item_number=BEAR05
residence_country=US
receipt_id=3354-8100-5749-2926
handling_amount=0.00
transaction_subject=Stuffed+bear
payment_gross=20.00
shipping=0.00

To make sure everything is well we must check that the status code is 200 and that the first line says SUCCESS. On failure it would say FAIL, or something like that. We can do that like this:

if($status == 200 AND strpos($response, ‘SUCCESS’) === 0)
{
// Further processing
}
else
{
// Log the error, ignore it, whatever 
}

That response is not very useful right now though. Even if it is valid, the string is just, well, a long string. Not to mention it’s URL encoded and possibly in the wrong character encoding. We want to clean it up and turn it into a much more handy associative array.

Step 6: Clean up the response

We want to remove the first line, URL decode the string, turn it into an associative array and convert the encoding if needed. This is how you might do that:

// Remove SUCCESS part (7 characters long)
$response = substr($response, 7);// URL decode
$response = urldecode($response);

// Turn into associative array
preg_match_all(‘/^([^=\s]++)=(.*+)/m’, $response, $m, PREG_PATTERN_ORDER);
$response = array_combine($m[1], $m[2]);

// Fix character encoding if different from UTF-8 (in my case)
if(isset($response[‘charset’]) AND strtoupper($response[‘charset’]) !== ‘UTF-8’)
{
foreach($response as $key => &$value)
{
$value = mb_convert_encoding($value, ‘UTF-8’, $response[‘charset’]);
}
$response[‘charset_original’] = $response[‘charset’];
$response[‘charset’] = ‘UTF-8’;
}

// Sort on keys for readability (handy when debugging)
ksort($response);

The $response should now contain a nice and sorted associative array with the correct encoding and it’s ready to be used for whatever purpose you need.

Comments are closed