• Status: Open
  • Priority: Low
  • Security: Public
  • Views: 54
  • Last Modified:

PDO connection doesn't work on Paypal IPN listener

Hi everybody, I'm not able to finalize development of PayPal IPN Listener.
I'm integrating Paypal into a php/Mysql based website. IPN fails when I try to add a new record to Mysql table of orders.

I make one step back to explain the issue.
I succeded in creating (in the Sandbox environment) merchant express checkout button and fully configured IPN notifications.
Uploaded code to my php page once clicked the 'buy now' button, connection passes smoothly to Paypal Sandbox asking buyer to log in.
Once buyer is logged in it's possible to interact with Paypal and buy.
IPN listener successfully gets purchase parameters and report on a file (just for debugging) parameters and sql string to be used in my PDO.
Everything works just fine and on the backend of the merchant profile I see the purchase notification appearing in the IPN history and parameters are clearly showing that payment has been succesfully made.

Problem is given at PDO call.
PDO call to open connection and execute sql to add record fails.
PDO parameters are ok (same throughout the whole site), sql works fine (run directly to mysql web interface it does its dirty work).
Try-catch doesn't capture any exception and script to create log file (during PDO) doesn't run.

Any advice? Many thanks !

  • $payment_status="Completed"
  • test.txt produced
  • exception.txt and complete.txt not produced
IPN Listener.php
<?php namespace Listener;
require('PaypalIPN.php');
use PaypalIPN;

$ipn = new PaypalIPN();

$ipn->useSandbox();
$verified = $ipn->verifyIPN();
if ($verified) {

    $fh = fopen("test.txt", 'w');	  

	foreach ($_POST as $parm => $var) 
	{
	$var = urlencode(stripslashes($var));
	$resp .= "&$parm=$var";
	fwrite ($fh, $parm."-->".$var."\n");	
	}

	
	//for the sake of clarity only one parameter is considered
    $txn_id = filter_var($_POST['txn_id'], FILTER_SANITIZE_STRING);
	$sql = "INSERT INTO orders (id_transaction) VALUES ('$txn_id')";
	
        fwrite ($fh, $sql);

	fclose ($fh);


	 if ($payment_status = "Completed"){
		try {

		$db = new PDO ('mysql:host=x.y.w.z;dbname=dbname', "account", "password", array(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true));
		$add_record = $db->prepare ($sql);
		$add_record->execute();		

                   $fq = fopen("complete2.txt", 'w');	  
	  	   fwrite ($fq, "ok, record added");
	  	   fclose ($fq);

		} catch (PDOException $e) {
		   $fh1 = fopen("exception.txt", 'w');	  
	  	   fwrite ($fh1, $e->getMessage());
	  	   fclose ($fh1);		   
		   }
	  		 
	  }
}

header("HTTP/1.1 200 OK");
?>

Open in new window


PaypalIPN.php
<?php

class PaypalIPN
{

    /**
     * @var bool $use_sandbox     Indicates if the sandbox endpoint is used.
     */
    private $use_sandbox = true;
    /**
     * @var bool $use_local_certs Indicates if the local certificates are used.
     */
    private $use_local_certs = true;

    /** Production Postback URL */
    const VERIFY_URI = 'https://www.paypal.com/cgi-bin/webscr';
    /** Sandbox Postback URL */
    const SANDBOX_VERIFY_URI = 'https://www.sandbox.paypal.com/cgi-bin/webscr';


    /** Response from PayPal indicating validation was successful */
    const VALID = 'VERIFIED';
    /** Response from PayPal indicating validation failed */
    const INVALID = 'INVALID';


    /**
     * Sets the IPN verification to sandbox mode (for use when testing,
     * should not be enabled in production).
     * @return void
     */
    public function useSandbox()
    {
        $this->use_sandbox = true;
    }

    /**
     * Sets curl to use php curl's built in certs (may be required in some
     * environments).
     * @return void
     */
    public function usePHPCerts()
    {
        $this->use_local_certs = false;
    }


    /**
     * Determine endpoint to post the verification data to.
     * @return string
     */
    public function getPaypalUri()
    {
        if ($this->use_sandbox) {
            return self::SANDBOX_VERIFY_URI;
        } else {
            return self::VERIFY_URI;
        }
    }


    /**
     * Verification Function
     * Sends the incoming post data back to PayPal using the cURL library.
     *
     * @return bool
     * @throws Exception
     */
    public function verifyIPN()
    {
        if ( ! count($_POST)) {
            throw new Exception("Missing POST Data");
        }

        $raw_post_data = file_get_contents('php://input');
        $raw_post_array = explode('&', $raw_post_data);
        $myPost = array();
        foreach ($raw_post_array as $keyval) {
            $keyval = explode('=', $keyval);
            if (count($keyval) == 2) {
                // Since we do not want the plus in the datetime string to be encoded to a space, we manually encode it.
                if ($keyval[0] === 'payment_date') {
                    if (substr_count($keyval[1], '+') === 1) {
                        $keyval[1] = str_replace('+', '%2B', $keyval[1]);
                    }
                }
                $myPost[$keyval[0]] = urldecode($keyval[1]);
            }
        }

        // Build the body of the verification post request, adding the _notify-validate command.
        $req = 'cmd=_notify-validate';
        $get_magic_quotes_exists = false;
        if (function_exists('get_magic_quotes_gpc')) {
            $get_magic_quotes_exists = true;
        }
        foreach ($myPost as $key => $value) {
            if ($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
                $value = urlencode(stripslashes($value));
            } else {
                $value = urlencode($value);
            }
            $req .= "&$key=$value";
        }

        // Post the data back to PayPal, using curl. Throw exceptions if errors occur.
        $ch = curl_init($this->getPaypalUri());
        curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
        curl_setopt($ch, CURLOPT_SSLVERSION, 6);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

        // This is often required if the server is missing a global cert bundle, or is using an outdated one.
        if ($this->use_local_certs) {
            curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . "/cert/cacert.pem");
        }
        curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
        $res = curl_exec($ch);
        if ( ! ($res)) {
            $errno = curl_errno($ch);
            $errstr = curl_error($ch);
            curl_close($ch);
            throw new Exception("cURL error: [$errno] $errstr");
        }

        $info = curl_getinfo($ch);
        $http_code = $info['http_code'];
        if ($http_code != 200) {
            throw new Exception("PayPal responded with http code $http_code");
        }

        curl_close($ch);

        // Check if PayPal verifies the IPN data, and if so, return true.
        if ($res == self::VALID) {
            return true;
        } else {
            return false;
        }
    }
}

Open in new window

0
Paolo Farnesi
Asked:
Paolo Farnesi
0 Comments

Join & Write a Comment

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now