PHP PayPal IPN Script
To use this script, you need to create the database table it'll write to. You can cut & paste this definition into your MySQL front-end. It's designed to work with the PayPal structure, but could be used as a starting point for other systems too:
/*------------------------------------*/
/* Sales - Order received via Paypal */
/*------------------------------------*/
CREATE TABLE sales (
invoice INT UNSIGNED AUTO_INCREMENT,
receiver_email VARCHAR(60),
item_name VARCHAR(100),
item_number VARCHAR(10),
quantity VARCHAR(6),
payment_status VARCHAR(10),
pending_reason VARCHAR(10),
payment_date VARCHAR(20),
mc_gross VARCHAR(20),
mc_fee VARCHAR(20),
tax VARCHAR(20),
mc_currency VARCHAR(3),
txn_id VARCHAR(20),
txn_type VARCHAR(10),
first_name VARCHAR(30),
last_name VARCHAR(40),
address_street VARCHAR(50),
address_city VARCHAR(30),
address_state VARCHAR(30),
address_zip VARCHAR(20),
address_country VARCHAR(30),
address_status VARCHAR(10),
payer_email VARCHAR(60),
payer_status VARCHAR(10),
payment_type VARCHAR(10),
notify_version VARCHAR(10),
verify_sign VARCHAR(10),
referrer_id VARCHAR(10),
PRIMARY KEY (invoice)
);
Line By Line: ipn.php
This program works as described in the chapter on Automation. It is important to get it working perfectly - you don't want to lose orders. Having said that, it's not rocket science, as you'll see most of it's quite straightforward...
Note, the program comments are included as a major part of the explanation!
- Program Start.
The construct: 'gmstrftime(time())' formats the system time into something readable.
<?php
//------------------------------------------------------------------
// Open log file (in append mode) and write the current time into it.
// Open the DB Connection. Open the actual database.
//-------------------------------------------------------------------
$log = fopen("ipn.log", "a");
fwrite($log, "\n\nipn - " . gmstrftime ("%b %d %Y %H:%M:%S", time()) . "\n");
$db = mysql_connect("localhost", "yourname", "yourpassword");
mysql_select_db("yourdb",$db);
- Create a reply to validate the PayPal post. (Standard PayPal Code)
This bit of code first creates a new array ($postvars) containing all of the values posted from PayPal.
Then it begins a reply message ($req), with 'cmd=_notify-validate'
Using a 'for' loop, it adds a list of posted vars in the format 'variable=value .
//------------------------------------------------
// Read post from PayPal system and create reply
// starting with: 'cmd=_notify-validate'...
// then repeating all values sent - VALIDATION.
//------------------------------------------------
$postvars = array();
while (list ($key, $value) = each ($HTTP_POST_VARS)) {
$postvars[] = $key;
}
$req = 'cmd=_notify-validate';
for ($var = 0; $var < count ($postvars); $var++) {
$postvar_key = $postvars[$var];
$postvar_value = $$postvars[$var];
$req .= "&" . $postvar_key . "=" . urlencode ($postvar_value);
}
INFO: Note the values are urlencoded - this is the format in which data is transmitted in a post from a web page.
The function 'urlencode':
"Returns a string in which all non-alphanumeric characters except -_. have been replaced with a percent (%) sign followed by two hex digits and spaces encoded as plus (+) signs" (from the PHP manual).
This is basically a method of transmitting an 8-bit character set in just 7-bits. |
- Create an HTTP header for the reply message, open a connection...
//--------------------------------------------
// Create message to post back to PayPal...
// Open a socket to the PayPal server...
//--------------------------------------------
$header .= "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen ($req) . "\r\n\r\n";
$fp = fsockopen ("www.paypal.com", 80, $errno, $errstr, 30);
- Write the transaction details to the log file.
The log file is useful for debugging problems. You'll ideally never lose any details, and if the database is down for any reason, you need a backup..
//---------------------------------------------
fwrite($log, "Vals: ". $invoice." ". $receiver_email." ". $item_name." ". $item_
number." ". $quantity." ". $payment_status." ". $pending_reason." ".$payment_dat
e." ". $payment_gross." ". $payment_fee." ". $txn_id." ". $txn_type." ". $first_
name." ". $last_name." ". $address_street." ". $address_city." ". $address_state
. " ".$address_zip." ". $address_country." ". $address_status." ". $payer_email.
" ". $payer_status." ". $payment_type." ". $notify_version." ". $verify_sign. "\
n");
- Check Connection...
The variables $errstr and $errno are system variables which report details on the last error.
//----------------------------------------------------------------------
// Check HTTP connection made to PayPal OK, If not, print an error msg
//----------------------------------------------------------------------
if (!$fp) {
echo "$errstr ($errno)";
fwrite($log, "Failed to open HTTP connection!");
$res = "FAILED";
}
- Final Verification
At last, the string of posted variables ($req) is sent to PayPal's server. When it receives it - the server gives a 'VERIFIED' response if the transaction was real and successful.
//--------------------------------------------------------
// If connected OK, write the posted values back, then...
//--------------------------------------------------------
else {
fputs ($fp, $header . $req);
//-------------------------------------------
// ...read the results of the verification...
// If VERIFIED = continue to process the TX...
//-------------------------------------------
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {
- If payment is complete, get the password from the database.
If the payment status is 'Completed'... The payment wasn't by e-check. The transaction's complete.
Using the 'while' construct to get the data into a local variable is just a convenience... We know that only 1 row will come back from the query.
//----------------------------------------------------------------------
// If the payment_status=Completed... Get the password for the product
// from the DB and email it to the customer.
//----------------------------------------------------------------------
if (strcmp ($payment_status, "Completed") == 0) {
$qry = "SELECT password FROM products WHERE pid = \"$item_number\" ";
$result = mysql_query($qry,$db);
while ($myrow = mysql_fetch_row($result)) { $passwd = $myrow[0]; }
- Create message. Send the email
The message is created in the variable '$message' then sent using the PHP 'mail' function. Note the sender and reply-to info goes at the end.
$message .= "Dear Customer,\n Thankyou for your order.\n\nThe password f
or the item you ordered is: $row[0]\n\nIf you have any problems, please contact
us: \n\nsales\@yourdomain.com";
mail($payer_email, "Your Book Password...", $message, "From: ipn@yourdomain.com\nReply-To: sales@yourdomain.com");
}
- Handle Incomplete Transactions
Inform the customer, and also inform yourself that something needs doing.
//----------------------------------------------------------------------
// If the payment_status is NOT Completed... You'll have to send the
// password later, by hand, when the funds clear...
//----------------------------------------------------------------------
else {
$message .= "Dear Customer,\n Thankyou for your order.\n\nThe password for the item you ordered will be sent to you when the funds have cleared.\n\nThankyou \n\nsales\@yourdomain.com";
mail($payer_email, "Your Book Password...", $message, "From: ipn@yourdomain.com\nReply-To: sales@yourdomain.com");
mail($receiver_email, "Incomplete PayPal TX...", "An incomplete transaction requires your attention.");
}
- Deal with 'Unverified' transactions
If, for example, the transaction you just processed didn't originate with PayPal, but was an attempted hack - it would cause this error to occur. An invalid transaction needs human intervention.
//----------------------------------------------------------------
// ..If UNVerified - It's 'Suspicious' and needs investigating!
// Send an email to yourself so you investigate it.
//----------------------------------------------------------------
else {
mail($payer_email, "An Error Occurred...", "Dear Customer,\n an error occurred while PayPal was processing your order. It will be investigated by a human at the earliest opportunity.\n\nWe apologise for any inconvenience.", "From: ipn@yourdomain.com\nReply-To: sales@yourdomain.com");
mail($receiver_email, "Invalid PayPal TX...", "An invalid transaction requires your attention.");
}
}
}
- Insert Details Into DB
The '\"' is placed around each of the variable to ensure that they appear in double quotes in the query string. This ensures that whatever they contain, they'll get inserted.
//--------------------------------------
// Insert Transaction details into DB.
//--------------------------------------
$qry = "INSERT into psales (
invoice, receiver_email, item_name, item_number, quantity, payment_status, pendi
ng_reason, payment_date, payment_gross, payment_fee, txn_id, txn_type, first_nam
e, last_name, address_street, address_city, address_state, address_zip, address_
country, address_status, payer_email, payer_status, payment_type, notify_version
, verify_sign )
VALUES
( \"$invoice\", \"$receiver_email\", \"$item_name\", \"$item_number\", \"$quanti
ty\", \"$payment_status\", \"$pending_reason\", \"$payment_date\", \"$payment_gr
oss\", \"$payment_fee\", \"$txn_id\", \"$txn_type\", \"$first_name\", \"$last_na
me\", \"$address_street\", \"$address_city\", \"$address_state\", \"$address_zip
\", \"$address_country\", \"$address_status\", \"$payer_email\", \"$payer_status
\", \"$payment_type\", \"$notify_version\", \"$verify_sign\" ) ";
$result = mysql_query($qry,$db);
- Close up.
//-------------------------------------------
// Close PayPal Connection, Log File and DB.
//-------------------------------------------
fclose ($fp);
fclose ($log);
mysql_close($db);
?>
|
|