Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

PayPal IPN PHP Question 1

Status
Not open for further replies.

Seecke

IS-IT--Management
Aug 23, 2003
68
US
Actually I think this is more of just a n IPN question but I hope I can get an answer here.

I found this code for IPN

Code:
   <?php
//Lets do MySql connection stuff
$mysql_host='localhost'; //Leave at localhost
$mysql_user='blah'; //DB User
$mysql_pass='blah'; //DB Pass
$mysql_db='blah'; //DB Name

//------------------------------------------------------------------
// 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($mysql_host, $mysql_user, $mysql_pass);
mysql_select_db($mysql_db,$db);
//------------------------------------------------
// 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);
}
//--------------------------------------------
// 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-[URL unfurl="true"]www-form-urlencoded\r\n";[/URL]
$header .= "Content-Length: " . strlen ($req) . "\r\n\r\n";
$fp = fsockopen ("[URL unfurl="true"]www.paypal.com",[/URL] 80, $errno, $errstr, 30);

//---------------------------------------------
fwrite($log, "Vals: ". $invoice." ". $receiver_email." ". $item_name." ". $item_ number." ". $quantity." ". $payment_status." ". $pending_reason." ".$payment_date." ". $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"); 

// assign posted variables to local variables
$item_name = $_POST['item_name'];
$item_number = $_POST['item_number'];
$payment_status = $_POST['payment_status'];
$payment_amount = $_POST['mc_gross'];
$payment_currency = $_POST['mc_currency'];
$txn_id = $_POST['txn_id'];
$receiver_email = $_POST['receiver_email'];
$payer_email = $_POST['payer_email'];

//----------------------------------------------------------------------
// 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";
}//--------------------------------------------------------
// 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) {

//--------------------------------------
// Insert Transaction details into DB.
//--------------------------------------
$qry = "INSERT into sales (
invoice, receiver_email, item_name, item_number, quantity, payment_status, pending_reason, payment_date, 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 )
VALUES
( \"$invoice\", \"$receiver_email\", \"$item_name\", \"$item_number\", \"$quantity\", \"$payment_status\", \"$pending_reason\", \"$payment_date\", \"$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\" ) ";

$result = mysql_query($qry,$db);
}
}
}
//-------------------------------------------
// Close PayPal Connection, Log File and DB.
//-------------------------------------------
fclose ($fp);
fclose ($log);
mysql_close($db);
?>


My question is this:

Where do all of the items that are inserted into my database come from? Is there a Form that I create that has some of the variables above and the others come from PayPal or are the variables coming from PayPal completely?

Thanks for your help! I may need a little more guidance depending on the answer.

Steve
 
The data is coming from the POST variables, which would be from whatever page / form calls this function, presumably on a previous page.

Off hand, while the code looks functional, it doesn't appear to be very secure. POST variables can be manipulated and it looks like the code just iterates through the list of them, assigns values according to the pair's key, and then writes these values to YOUR database.

 
OK, Learning here so please excuse me.

The information is, at current, coming from the paypal IPN test page in paypal's sandbox. I have verified that the IPN write back is functioning correctly and all of the fields that have data are getting thru to the ipn.log file.

I have used the fwrite() function to follow each step of the code in my original post.

After a few 'syntax' modifications I find that he ONLY thing not happening is the placing of the data into the database.

Any ideas?
 
that all seems terribly complex.

assuming you are trying to log the IPN information to a file I suggest the following

Code:
$filename = 'myfilename.txt';
$fh = fopen($filename, 'w+b');
fputcsv($fh,array_merge(array(time()), $_POST));
fclose($fh);


if you are trying to store ipn message data in a database try this

Code:
//profile the data schema and map against the IPN fields
$fields = array(

'dbField' => 'IPN Field',
//repeat as necessary
);
$_field = $_value = array();
foreach ($fields as $dbField=>$IPNField):
  if (isset($_POST[$IPNField])):
    $_field[] = $dbField;
    $_value[] = "'" . mysql_real_escape_string($IPNField) . "'";
  else: 
    $_field[] = $dbField;
    $_value[] = '';
  endif;
endforeach;

$sql = "insert into myIPNTable (".implode (',', $_field) . ") values (".implode(',', $_value) .")";
mysql_query($sql) or die (mysql_error());

and if you are trying to chat with paypal about the IPN message (after you have verified it against your transaction database;

Code:
function ipnValidate(){
$ch = curl_init ('paypal url');
curl_setopt_array($ch,
array(
 CURLOPT_POST            => 1,
 CURLOPT_POSTFIELDS     => $_POST,
 CURLOPT_RETURNTRANSFER=>TRUE));
$v = curl_exec($ch);
return strtoupper($v) == 'VERIFIED' ? true : false;
}
 
JPadie,

Thank you for your reply.

The second option is what I am looking at doing as a portion of giving a user 'membership access' AFTER paying for the membership.

I was hoping to utilize the IPN data as a way to capture the user's information and use portions of that data for their membership.

I am so new to this side of things that a lot of what you have posted is a bit 'greek' to me. I am, however, able to understand bits and pieces after a bit of personal research on the terms used in the code.

In the code: 'dbField' => 'IPNField', I assume that you are saying that each dbfield is to be a duplicated into the IPN Field. i.e. 'invoice' => 'invoice' for as many db fields as in have in the db to create the $fields array, naming them $dbField and $IPNField as it steps through the fields.

SNIPPET
Code:
$fields = array(

'invoice' => 'invoice', 'receiver_email' => 'receiver_email', 'item_name' => 'item_name',
//repeat until all fields are accounted for
);

Please correct me if I am wrong.

{NOTE: My database table has all of the fields mentioned in $qry that I posted in the original post.}

Then the code cycles through the array ultimately creating one big array of the data from all of the fields. Once complete it then enters the large array into the query string.

The implode function, I believe, joins array elements with a string so instead of writing out the entire database structure.

In your $sql statement, you used 'implode' to again cycle through the array... dynamically building the insert into statement.

Then with the mysql_query statement, the data is placed into the database.

Thanks again and I hope to learn quickly!!!
 
that looks right.

however the point of the mapping is so that you do not _have_ to call you db fields the same as the ipn fields.
 
GREAT!!! This will quite possibly make things a bit easier for me in the final code.

OK, stepping back a bit... can you tell me why the data isn't entered into the database with the code the way it is right now?

When I use the fwrite function to show me where the code gets to in my IPN.log... I can see that it reaches the end of the file without error but I still have no data in the database.

Here is what my ipn log reports:
ipn.log said:
ipn - Nov 22 2010 18:50:45

after connect to paypal

Values: '. .' '. seller@paypalsandbox.com.' '. something.' '. number.' '. 1.' '. Completed.' '. .' '.16:50:45 Nov 22, 2010 PST.' '. .' '. .' '. 451123050.' '. web_accept.' '. name.' '. Smith.' '. 123, any street.' '. San Jose.' '. CA.' '.95131.' '. United States.' '. confirmed.' '. buyer@paypalsandbox.com.' '. verified.' '. instant.' '. 2.1.' '. Axce1vcDSOHrRgYEgj7lCzDYRnzVA.qZLcgTGQR71RNaq1BUqk7wQrfR.'

assigned posted variables to local variables completed
Succeeded in opening an HTTP connection!
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<
before database injection >>>END<<<

(Cleaned up for a bit of easier reading)

The ">>>END<<<" is located just before the code that closes all of the connections. It has a while loop to cycle through all of the fields then the close is executed.

So, with that being said I see that the entire page is working... I just don't have the data in the database when I go and look at it.

Thanks!

Steve
 
there are a bundle of things wrong with the code (not least that it uses functionality that has been switched off for a number of years).

happy to help debug but we need some more data first

for starters, please post the schema of your database. in particular I would like to see the schemas for:

1. the orders table
2. the order items table
3. the ipn response storage table

separately, I see a paypal interaction as follows:

1. punter browses your site, adding things to a shopping basket.
2. the shopping basket is stored in a session or a table
3. at checkout the punter transforms the shopping basket into an order, at which point the line items in the basket are irrevocably committed to a table
4. at payment time, the items in the order are taken from the table and transmitted to paypal
5. paypal requests payment
6. when payment is authorised paypal sends an IPN message to you
7. you compare the information in that message against the data in your orders table (to make sure that the message has not been tampered with)
8. if the information is correct, you ask IPN to validate the incoming message
9. paypal validates or declines the message
10. if the message is verified, and it is a payment message
11. query paypal to test that the payment is 'Completed'
12. verify that the transactionID is not already processed
13 do fraud checks
14 take appropriate action

My concern from reading your posts is that you are not storing your own transactions in a table BEFORE submitting them to paypal. If this is the case then, imo, the solution is flawed.

imo you should have tables that capture the following

1. the order data (in fact this is two tables, one for the order itself and one for the order items)
2. the requests to paypal
3. the responses to paypal
4. order fulfillment

assuming we are not being asked to assist with item 4, the first three are, imo, essential parts of any e-com system. you might dispense with item 2, or roll items 2 and 3 together. dispensing with item 2 would be a bad idea, imo, as you then lose the ability to audit your system

of course, the order process must also interact with a stock control system, assuming that you are selling physical goods.
 
Thanks for the reply... let me clarify a bit here... what I posted above was found on the net as an example of what I would like to do... I was using it to learn how PHP and mySQL work together and for the syntax used to complete the process. I figured if I could understand something more complex and make it function... then what I wanted to do with my stuff would be a breeze.

My scenario seems real basic to me. A one year subscription (renewable) to my site that allows access to specific areas and tools. (So there isn't a cart or a number of products to sell.)

IF the user wants to become a member, they enter their personal information into the form, press subscribe, go to paypal and pay, return and everything gets entered into the database along with paypal's response that says all is complete and valid.

Storing info to a db prior to paypal is preferred but I need to make sure that they don't gain access until they have paid and paypal verifies as such.

I have two main tables ( there are other tables but they are irrelivant to this process)...

the Customer table that holds their information:

TABLE Agents

Company
First_Name
Last_Name
Phone
Email
Address
Address_1
City
State
Zip_Code
User_Id
Password

and the paypal IPN table:

Currently it has ALL of the fields mentioned in the first post, however, when I understand the process and decide what I really need, I will strip it down just those fields

TABLE paypal_ipn

invoice
receiver_email
item_name
item_number
quantity
payment_status
pending_reason
payment_date
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

IPN seems to be the best way for me to validate the payment for my users at this time. If there is something much simpler... I am more than willing to take a look at it. Can't spend much money though as there aint much to go around right now thus the reason for trying to learn and build one myself.

Thanks!

Steve
 
Flash Forward:

My form works entering data into the database!!

The same form, works connecting to paypal and the payment is processing. (I changed the "post" variable to the paypal sandbox URL for testing this section.)

jpadie, you mentioned something about adding the data to the database before sending to paypal.

Can this be done simultaneously? by that I mean, Can I have the form post to a page the inserts the data into the database and then, without user interaction, direct them to the paypal payment page for processing... upon return from PayPal a few more pieces of data is placed into the database to complete the authorization of the membership?
 
thanks... now are there any code samples out there that I can look at to 'see this in action'?
 
there is no magic. just do one thing and then the other in the same script.
 
That's too simple LOL I will give it a shot!
 
Can't quite find the process for getting the form to post to the database then forward the user to paypal for payment with just one click of a subscribe button... Googled quite a bit and am not really understanding the process. On another note.. My IPN works but I have a strange issue.

When the user pays, and returns to my site, the data gets dumped into the database twice. Also, two emails are received. Almost like its looping through the IPN page.

My notify-url is only called once in the form as shown below (only paypal variables here.. other form elements have been removed to save space):
Code:
            <form target="paypal" action="[URL unfurl="true"]https://www.sandbox.paypal.com/cgi-bin/webscr"[/URL] method="post">
             <input type="hidden" name="invoice" id="invoice" value="" />
             <input type="hidden" name="cmd" value="_xclick-subscriptions" />
             <input type="hidden" name="business" id="business" value="sandboxtestemail@mywebsite.com" />
             <input type="hidden" name="item_name" id="item_name" value="12 Month Subscription to mywebsite.com" />
             <input type="hidden" name="currency_code" id="currency_code" value="USD" />
             <input type="hidden" name="notify_url" id="notify_url" value="[URL unfurl="true"]http://www.mywebsite.com/ipn.php"/>[/URL]            
             <input type="hidden" name="cancel_return" value="[URL unfurl="true"]http://www.mywebsite.com/contact.php"[/URL] />
             <input type="hidden" name="return" value="[URL unfurl="true"]http://www.mywebsite.com/index.php"[/URL] />
             <input type="hidden" name="no_shipping" id="no_shipping" value="1" />
             <input type="hidden" name="a3" id="a3" value="69.95" />
             <input type="hidden" name="p3" id="p3" value="1" />
             <input type="hidden" name="t3" id="t3" value="Y" />
             <input type="hidden" name="src" id="src" value="1" />
             <input type="hidden" name="rm" id="rm" value="2" />
             <input type="image" src="[URL unfurl="true"]https://www.paypal.com/en_US/i/btn/btn_subscribeCC_LG.gif"[/URL] border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">

Here is the code for my ipn.php:

Code:
<?php

include 'db.php'; //holds all database connection information

// reading the post from the PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}
// posting back to PayPal system to validate
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Content-Type: application/x-[URL unfurl="true"]www-form-urlencoded\r\n";[/URL]
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";

//$fp = fsockopen ('ssl://[URL unfurl="true"]www.paypal.com',[/URL] 443, $errno, $errstr, 30);
$fp = fsockopen('ssl://[URL unfurl="true"]www.sandbox.paypal.com',443,$err_num,$err_str,30);[/URL] 

if (!$fp) {
// HTTP ERROR
} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);
if (strcmp ($res, "VERIFIED") == 0) {

// PAYMENT VALIDATED & VERIFIED!

$email = "emailfor@mywebsite.com";
$password = mt_rand(1000, 9999);

mysql_query("INSERT INTO paypal_sales (payer_email, password) VALUES('". mysql_escape_string($email) ."', '".md5($password)."' ) ") or die(mysql_error());

$to      = $email;
$subject = 'Membership Login Credentials';
$message = '

Thank you for your purchase

Your account information
-------------------------
Email: '.$email.'
Password: '.$password.'
-------------------------

You can now login at [URL unfurl="true"]http://www.mywebsite.com/login.php';[/URL]
$headers = 'From:email@mywebsite.com' . "\r\n";

mail($to, $subject, $message, $headers);

}

else if (strcmp ($res, "INVALID") == 0) {

// PAYMENT INVALID & INVESTIGATE MANUALY!

}
}
fclose ($fp);
}
?>

Can someone please tell me if they see anything in here that would cause that... I found this doing a Google search and it does work, (save for the double posting in the database and double emails) I am going to add more fields that get entered into the database from paypal's reply and may possibly remove the password feature in this step. Just trying to get a functioning ipn.php page first.


NOTE: The reason I think it is looping is that the second email and the second entry in the database have a different assigned password.

Thanks!
 
my concerns with your script above are:

1. you do not seem to be transmitting a hash to paypal. is that no longer necessary?
2. you are not storing an invoiceID. how then do you associate the payment with an invoice? I suspect this is the reason for the double entry.
3. you are storing the paypal notify url in the form. I can't help feeling that it is more secure to change the IPN listener to something obscure and store it in your user profile
 
hash"? I am unfamiliar with that term.

I realize that I am not sending much to my dB right now. I just wanted to get the post working then add the other fields that I need.
The IPN listener, ipn.php, is also for the test however I see your point and will make the necessary adjustments once it all seems to be working fine. I am assuming that you mean the listener would be a file name that is obscure AND it is stored in the PayPal profile so I don't have to code it into the form. Correct?

Again.. Thanks for your help!
 
Correct.
it is not essential, due to the callback mechanisms built in to paypal, but i think it is advisable. i would do it that way, at least.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top