<?php
/**
* A PayPal IPN Class to redirect and validate a purchase.
*
* @funcs __construct( $accountEmail, $cancelUrl, $returnUrl, $notifyUrl )
* process( $itemId, $itemName, $itemPrice )
* validate( )
*/
class PayPal_IPN
{
private $accountEmail = '';
private $cancelUrl = '';
private $notifyUrl = '';
private $returnUrl = '';
private $useSandbox = true;
/**
* When an instance of the PayPal_IPN Class has been initialized.
*
* @param string $accountEmail The e-mail address that the PayPal Merchant account belongs to.
* @param string $cancelUrl The URL to redirect the user to if they cancel the payment.
* @param string $returnUrl The URL to redirect the user once their payment has been processed.
* @param string $notifyUrl The IPN URL to notify once the payment has been processed (even if it fails).
* @param bool $useSandbox If we should be placing this payment using the PayPal sandbox (not live).
* @return void
*/
public function __construct( $accountEmail, $cancelUrl, $returnUrl, $notifyUrl, $useSandbox = true )
{
$this->accountEmail = $accountEmail;
$this->cancelUrl = $cancelUrl;
$this->notifyUrl = $notifyUrl;
$this->returnUrl = $returnUrl;
$this->useSandbox = $useSandbox;
}
/**
* Redirect the user to the PayPal IPN pages to pay for their item.
*
* @param mixed $itemId
* @param string $itemName
* @param float $itemPrice
* @return void
* @throws Exception If an error was encountered, an Exception will be thrown.
*/
public function process( $itemId, $itemName, $itemPrice = 0.00 )
{
// An array of keys to avoid adding to the string.
$avoid = array( 'amount', 'business', 'cancel_return', 'item_name', 'item_number', 'notify_url', 'return' );
// Append your PayPal e-mail address to the query string.
$queryString = "?business=" . urlencode( $this->accountEmail ) . "&";
// Append the item name and price to the query string.
$queryString .= "item_name=" . urlencode( $itemName ) . "&";
$queryString .= "item_number=" . urlencode( $itemId ) . "&";
$queryString .= "amount=" . urlencode( $itemPrice ) . "&";
// Append all of the $_POST keys and values to the query string.
foreach( $_POST as $key => $value )
{
// If we shouldn't append this key to the query string.
if ( !in_array( $key, $avoid ) )
{
$queryString .= $key . "=" . urlencode( stripslashes( $value ) ) . "&";
}
}
// Append Pay{al return addresses to the query string.
$queryString .= "return=" . urlencode( stripslashes( $this->returnUrl ) ) . "&";
$queryString .= "cancel_return=" . urlencode( stripslashes( $this->cancelUrl ) ) . "&";
$queryString .= "notify_url=" . urlencode( $this->notifyUrl );
// Define which PayPal URL will be used, live or sandbox mode.
$url = 'https://www.paypal.com/cgi-bin/webscr';
// If we should be placing this purchase in the PayPal sandbox (not live).
if ( $this->useSandbox == true )
{
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
}
// Redirect them to the PayPal IPN.
header( 'Location: ' . $url . $queryString );
// Make sure we exit the script at this point.
exit( );
}
/**
* Validate the user's PayPal IPN purchase.
*
* @return array {custom, item_name, item_number, payer_email, payment_amount, payment_currency, payment_status, receiver_email, txn_id}
* @throws Exception If an error is encountered, an Exception will be thrown.
*/
public function validate( )
{
// Read the post from PayPal system and add 'cmd'
$request = 'cmd=_notify-validate';
// Loop through all $_POST variables and append them to the requirement string.
foreach( $_POST as $key => $value )
{
$request .= "&" . $key . "=" . preg_replace( '/(.*[^%^0^D])(%0A)(.*)/i','${1}%0D%0A${3}', urlencode( stripslashes( $value ) ) );
}
// What host are we using?
$host = 'www.paypal.com';
// If we should be placing this purchase in the PayPal sandbox (not live).
if ( $this->useSandbox == true )
{
$host = 'www.sandbox.paypal.com';
}
// Post back to PayPal system to validate.
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Host: " . $host . "\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen( $request ) . "\r\n\r\n";
// Open a socket connection to PayPal to validate this payment.
$fp = @fsockopen( 'ssl://' . $host, 443, $errno, $errstr, 30 );
// If no socket connection was able to be established.
if ( !$fp )
{
throw new Exception( 'No connection to PayPal could be established.' );
}
// If a socket connection was established.
else
{
// Send the headers and request string to the socket connection.
@fputs( $fp, $header . $request );
// While socket connection is still open.
while( !feof( $fp ) )
{
// Read the next row from the socket.
$res = @fgets( $fp, 1024 );
// If the response was "VERIFIED", the PayPal payment has been processed.
if ( strcmp( $res, "VERIFIED" ) == 0 )
{
// Close this connection.
@fclose( $fp );
// Return information about this payment.
return array(
'custom' => $_POST['custom'],
'item_name' => $_POST['item_name'],
'item_number' => $_POST['item_number'],
'payer_email' => $_POST['payer_email'],
'payment_amount' => $_POST['mc_gross'],
'payment_currency' => $_POST['mc_currency'],
'payment_status' => $_POST['payment_status'],
'receiver_email' => $_POST['receiver_email'],
'txn_id' => $_POST['txn_id']
);
}
// If the response was "INVALID", the PayPal payment failed and should be investigated manually.
else if ( strcmp( $res, "INVALID" ) == 0 )
{
$error = 'This PayPal payment has failed.';
}
}
// Close the socket connection.
@fclose( $fp );
// If no error has been found.
if ( !isset( $error ) )
{
$error = 'An unknown error was encountered.';
}
// Throw an exception because we made it this far, which means the Payment wasn't successful.
throw new Exception( $error );
}
}
}
?>