Google Answers Logo
View Question
 
Q: Paypal IPN php error for a Ecom site ( No Answer,   1 Comment )
Question  
Subject: Paypal IPN php error for a Ecom site
Category: Computers
Asked by: arun_p-ga
List Price: $25.00
Posted: 06 Oct 2006 02:18 PDT
Expires: 05 Nov 2006 01:18 PST
Question ID: 771226
Hi,

We are deploying a shopping site for our software products, but for
our trial software activation into a paid version we need to provide
three inputs: "Name" "Company name" & "CD-Key" after the purchase is
confirmed.

Now,Help needed for customizing php paypal ipn script that can grab
product key from our database and send to the buyer e-mail if they
purchase the download version after getting verified payment. If user
purchase the cd-version then the product-key will be sent by e-mail to
our sales department to include the product key with cd shipping. That
means there will be no product key duplication can occur. All e-mail
sent out have to include buyer details (shipping address / invoice of
purchase). So, we need you to tell us where is the error generating
from and how to sort it out.

Find below the complete script: 

#1 = Live on PayPal Network 
#2 = Testing with www.BelaHost.com/pp
#3 = Testing with the PayPal Sandbox
$verifymode = "2"; # be sure to change value for testing/live!
# Send notifications to here
$send_mail_to = "sysadmin@celframe.com";
# subject of messages
$sysname = "Paypal IPN Transaction";
# Your primary PayPal e-mail address
$paypal_email = "sysadmin@celframe.com";
# Your sendmail path
$mailpath = "/usr/sbin/sendmail -t";
#the name you wish to see the messages from
$from_name = "Celframe IT";
#the emails will be coming from
$from_email = "sales@celframe.com";

# MySQL Database Information set by Default to insert into paypal_ipn table.
# This can be changed after the IPN is VERIFIED.
$MyUser = "deleted";
$MyPass = "deleted";
$MyHost = "deleted";
$MyDatabase = "deleted";



# Convert Super globals For backward compatibility
if(phpversion() <= "4.0.6")  { $_POST = ($HTTP_POST_VARS);  }

# Check for IPN post if non then return 404 error.
if (!$_POST['txn_type']) {header("Status: 404 Not Found");exit; } else {
header("Status: 200 OK");    }

# Now we Read the Posted IPN
$postvars = array();



            foreach ($_POST as $ipnvars => $ipnvalue) $postvars[] = $ipnvars;
                            # Now we ADD "cmd=_notify-validate" for
Post back Validation
                   $postipn = 'cmd=_notify-validate'; 
                   $orgipn = '<b>Posted IPN variables in order
received:</b><br><br>';
                                # Prepare for validation
            for ($x=0; $x < count($postvars); $x++) { $y=$x+1; $postkey =
$postvars[$x]; $postval = $$postvars[$x]; $postipn.= "&" . $postkey .
"=" . urlencode($postval);  $orgipn.= "<b>#" . $y . "</b> Key: " .
$postkey . " <b>=</b> " . $postval . "<br>";  }

## Verify Mode 1: This will post the IPN variables to the Paypal
Network for Validation
            if     ($verifymode == 1)
            {
$port = fsockopen ("errpaypal.com", 80, $errno, $errstr, 30);
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n".
"Host: www.paypal.com\r\n".
"Content-Type: application/x-www-form-urlencoded\r\n".
"Content-Length: " . strlen($postipn) . "\r\n\r\n";
            }
## Verify Mode 2: This will post the IPN variables to Belahost Test Script for
validation
## Located at www.belahost.com/pp/index.php
            elseif ($verifymode == 2)
            {
$port = fsockopen ("www.belahost.com", 80, $errno, $errstr, 30);
$header = "POST /pp/ HTTP/1.0\r\n".
"Host: www.belahost.com\r\n".
"Content-Type: application/x-www-form-urlencoded\r\n".
"Content-Length: " . strlen($postipn) . "\r\n\r\n";        
            } 
            elseif ($verifymode == 3)
            {
$port = fsockopen ("www.sandbox.paypal.com", 80, $errno, $errstr, 30);
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n".
"Host: www.sandbox.paypal.com\r\n".
"Content-Type: application/x-www-form-urlencoded\r\n".
"Content-Length: " . strlen($postipn) . "\r\n\r\n";
             }
            else 
                    { 
                    $error=1; echo "CheckMode: " . $verifymode . " is invalid!";  
                       exit; 
                    }


# Error at this point: If at this point you need to check your
Firewall or your Port
restrictions?
            if (!$port && !$error)
            {
                        echo "Problem: Error Number: " . $errno . "
Error String: " . $errstr;
#Here is a small email notification so you know if your system ever
fails
            send_mail("$send_mail_to", "$sysname", "\nYour Paypal System failed due
to $errno and string $errstr \n");                        
                        exit;
            }


# If No Errors to this point then we proceed with the processing.
# Open port to paypal or test site and post Varibles.
            else
         {
                        fputs ($port, $header . $postipn);
            while (!feof($port))
                                    {
                   $reply = fgets ($port, 1024);
                   $reply = trim ($reply);
                    }

# Prepare a Debug Report
$ipnreport = $orgipn . "<br><b>" . "IPN Reply: " . $reply . "</b>";


# Buyer Information
                   $address_city = $_POST['address_city'];
                   $address_country = $_POST['address_country'];
                   $address_country_code =
$_POST['address_country_code'];
        
                   $address_name = $_POST ['address_name'];
                   $address_state = $_POST['address_state'];
                   $address_status = $_POST['address_status'];
                   $address_street = $_POST['address_street'];
                   $address_zip = $_POST['address_zip'];
                   $first_name = $_POST['first_name'];
                   $last_name = $_POST['last_name'];
                   $payer_business_name = $_POST['payer_business_name'];
                   $payer_email = $_POST['payer_email'];
                   $payer_id = $_POST['payer_id'];
                   $payer_status = $_POST['payer_status'];
                   $residence_country = $_POST['residence_country'];
# Below Instant BASIC Payment Notifiction Variables
                   $business = $_POST['business'];
                   $item_name = $_POST['item_name'];
                   $item_number = $_POST['item_number'];
                   $quantity = $_POST['quantity'];
                   $receiver_email = $_POST['receiver_email'];
                   $receiver_id = $_POST['receiver_id'];
#Advanced and Customer information
                   $custom = $_POST['custom'];
                   $invoice = $_POST['invoice'];
                   $memo = $_POST['memo'];
                   $option_name1 = $_POST['option_name1'];
                   $option_name2 = $_POST['option_name2'];
                   $option_selection1 = $_POST['option_selection1'];
                   $option_selection2 = $_POST['option_selection2'];
                   $tax = $_POST['tax'];
#Website Payment Pro and Other IPN Variables
                   $auth_id = $_POST['auth_id'];
                   $auth_exp = $_POST['auth_exp'];
                   $auth_amount = $_POST['auth_amount'];
                   $auth_status = $_POST['auth_status'];
# Shopping Cart Information
                   $mc_gross = $_POST['mc_gross'];
                   $mc_handling = $_POST['mc_handling'];
                   $mc_shipping = $_POST['mc_shipping'];
                   $num_cart_items = $_POST['num_cart_items'];
# Other Transaction Information
                   $parent_txn_id = $_POST['parent_txn_id'];
                   $payment_date = $_POST['payment_date'];
                   $payment_status = $_POST['payment_status'];
                   $payment_type = $_POST['payment_type'];
                   $pending_reason = $_POST['pending_reason'];
                   $reason_code = $_POST['reason_code'];
                   $remaining_settle = $_POST['remaining_settle'];
                   $transaction_entity = $_POST['transaction_entity'];
                   $txn_id = $_POST['txn_id'];
                   $txn_type = $_POST['txn_type'];
# Currency and Exchange Information                   
                   $exchange_rate = $_POST['exchange_rate'];
                   $mc_currency = $_POST['mc_currency'];
                   $mc_fee = $_POST['mc_fee'];
                   $payment_fee = $_POST['payment_fee'];
                   $payment_gross = $_POST['payment_gross'];
                                   $settle_amount = $_POST['settle_amount'];
                   $settle_currency = $_POST['settle_currency'];
# Auction Information 
                   $for_auction = $_POST['for_auction'];
                   $auction_buyer_id = $_POST['auction_buyer_id'];
                   $auction_closing_date = $_POST['auction_closing_date'];
                   $auction_multi_item = $_POST['auction_multi_item'];
# Below are Subscription - Instant Payment Notifiction Variables
                   $subscr_date = $_POST['subscr_date'];
                   $subscr_effective = $_POST['subscr_effective'];
                   $period1 = $_POST['period1'];
                   $period2 = $_POST['period2'];
                   $period3 = $_POST['period3'];
                   $amount1 = $_POST['amount1'];
                   $amount2 = $_POST['amount2'];
                   $amount3 = $_POST['amount3'];
                   $mc_amount1 = $_POST['mc_amount1'];
                   $mc_amount2 = $_POST['mc_amount2'];
                   $mc_amount3 = $_POST['mc_amount3'];
                   $recurring = $_POST['recurring'];
                   $reattempt = $_POST['reattempt'];
                   $retry_at = $_POST['retry_at'];
                   $recur_times = $_POST['recur_times'];
                   $username = $_POST['username'];
                   $password = $_POST['password'];
                   $subscr_id = $_POST['subscr_id'];
# Complaint Variables Used when paypal logs a complaint                   
                   $case_id = $_POST['case_id'];
                   $case_type = $_POST['case_type'];
                   $case_creation_date = $_POST['case_creation_date'];
#Last but not least
                   $notify_version = $_POST['notify_version'];
                   $verify_sign = $_POST['verify_sign'];

#There are certain variables that we will not store for cart since
they are dynamic
                  
#such as mc_gross_x as they will be forever changing/increasing your script must
check these


# IPN was Confirmed as both Genuine and VERIFIED
            if (!strcmp ($reply, "VERIFIED"))
            {

/* Now that IPN was VERIFIED below are a few things which you may want
to do at this
point.
 1. Check that the "payment_status" variable is: "Completed"
 2. If it is Pending you may want to wait or inform your customer?
 3. You should Check your datebase to ensure this "txn_id" or "subscr_id" is not a
duplicate. txn_id is not sent with subscriptions!
 4. Check "payment_gross" or "mc_gross" matches match your prices!
 5. You definately want to check the "receiver_email" or "business" is yours. 
 6. We have included an insert to database for table paypal_ipn
 */

# Begin the mighty Insert
$conn = mysql_connect($MyHost, $MyUser, $MyPass);
mysql_select_db($MyDatabase);
$mysqlstr = "INSERT INTO paypal_ipn values ('NULL',
                        '$receiver_email',
                        '$receiver_id',
                        '$business',
                        '$item_name',
                        '$item_number',
                        '$quantity',
                        '$invoice',
                        '$custom',
                        '$option_name1',
                        '$option_selection1',
                        '$option_name2',
                        '$option_selection2',
                        '$num_cart_items',
                        '$payment_type',
                        '$payment_status',
                        '$pending_reason',
                        '$reason_code',
                        '$payment_date',
                        '$settle_amount',
                        '$settle_currency',
                        '$exchange_rate',
                        '$payment_gross',
                        '$payment_fee',
                        '$mc_gross',
                        '$mc_fee',
                        '$mc_currency',
                        '$mc_handling',
                        '$mc_shipping',
                        '$tax',
                        '$txn_id',
                        '$txn_type',
                        '$for_auction',
                        '$auction_buyer_id',
                        '$auction_closing_date',
                        '$auction_multi_item',
                        '$first_name',
                        '$last_name',
                        '$address_name',
                        '$address_street',
                        '$address_city',
                        '$address_state',
                        '$address_zip',
                        '$address_country',
                        '$address_country_code',
                        '$address_status',
                        '$payer_business_name',
                        '$payer_email',
                        '$payer_id',
                        '$payer_status',
                        '$residence_country',
                        '$memo',
                        '$subscr_date',
                        '$subscr_effective',
                        '$period1',
                        '$period2',
                        '$period3',
                        '$amount1',
                        '$amount2',
                        '$amount3',
                        '$mc_amount1',
                        '$mc_amount2',
                        '$mc_amount3',
                        '$recurring',
                        '$reattempt',
                        '$retry_at',
                        '$recur_times',
                        '$username',
                        '$password',
                        '$subscr_id',
                        '$auth_id',
                        '$auth_exp',
                        '$auth_amount',
                        '$auth_status',
                        '$transaction_entity',
                        '$remaining_settle',
                        '$parent_txn_id',
                        '$case_id',
                        '$case_type',
                        '$case_creation_date',
                        '$notify_version',
                        '$verify_sign')";
mysql_query($mysqlstr, $conn) or die(mysql_error());
mysql_close($conn);
# End The long insert!


# Remove Echo below when live
echo $ipnreport;
send_mail("$send_mail_to", "$sysname", "\n Verified IPN Transaction\n
\n$ipnreport\n");
                                  }

# IPN was Not Validated as Genuine and is INVALID
            elseif (!strcmp ($reply, "INVALID"))
            {

/* Now that IPN was INVALID below are a few things which you may want to do at this
point.
 1. Check your code for any post back Validation problems!
 2. Investigate the Fact that this Could be an attack on your script IPN!
 3. If updating your DB, Ensure this "txn_id" is Not a Duplicate!
*/

# Remove Echo line below when live
echo $ipnreport;
send_mail("$send_mail_to", "$sysname", "\n IN Valid IPN Transaction\n
\n$ipnreport\n");
                                  }

            else
            {

# If your script reaches this point there is a problem. Communication from your
script to test/paypal pages could be 1 reason.
$ipnreport;
send_mail("$send_mail_to", "$sysname", "\n FATAL ERROR No Reply at all\n
\n$ipnreport\n");
            }


# Terminate the Socket connection and Exit
        fclose ($port); exit;
      }

      
      
      
      
      
      
/* =================================
         Below are functions
   ================================= */
   
# Email function
function send_mail($to, $subj, $body)
{
    global $from_name, $from_email, $mailpath;

# E-mail Configuration
        $announce_subject = "$subj";
        $announce_from_email = "$from_email";
        $announce_from_name = "$from_name";
        $announce_to_email = "$to";
        $MP = "$mailpath"; 
        $spec_envelope = 1;
        # End email config
# Access Sendmail
# Conditionally match envelope address
        if(isset($spec_envelope))
        {
        $MP .= " -f $announce_from_email";
        }
        $fd = popen($MP,"w"); 
        fputs($fd, "To: $announce_to_email\n"); 
        fputs($fd, "From: $announce_from_name <$announce_from_email>\n");
        fputs($fd, "Subject: $announce_subject\n"); 
        fputs($fd, "X-Mailer: MyPayPal_Mailer\n");
        fputs($fd, "Content-Type: text/html\n");
        fputs($fd, $body); # $body will be sent when the function is used
        pclose($fd);
}

?>
Answer  
There is no answer at this time.

Comments  
Subject: Re: Paypal IPN php error for a Ecom site
From: matt__-ga on 26 Oct 2006 14:12 PDT
 
Sure, but changing this script alone isn't enough as the solution
relies upon your online database structure. So, as I'm feeling
generous, here's the basic approach you need to take:
1. Check that the payment hasn't come from the paypal sandbox!! Also
check they PayPal IPN version - there's no guarantee of backwards
compatibility so if it doesn't match then email your support
department.
2. When the user presses the checkout button, don't go directly to
paypal but instead insert an order into your order tracking table. In
this table have a "confirmed" field set to zero; this indicates a
pending order awaiting payment. Generate a unique order id and also
store this in the table. Now post a form to paypal with all of the
order details as normal but also set the custom value to your unique
order id number. You'll also want to store the total charge to the
user, including shipping, tax, etc. in this database.
3. On the return from paypal - and once all standard paypal checks are
completed (i.e. the ones in the script), check to ensure that you have
a pending order ("confirmed" status is zero) where the paypal custom
field matches the table unique order id field. If it doesn't, the
payment is not for any placed order.
4. Check that the received paypal data indicates it is a payment, if
it's not then handle appropriately (basically just flag it to customer
services and bomb out since anything other than a payment is not worth
processing in a script unless you're as big as amazon).
5. Once you've confirmed the order id, now confirm that the balance
received matches the balance from the order - you stored the requested
balance before posting to paypal as you don't want to calculate it
again since that way you're guaranteed to get a different answer by
calculating it incorrectly a second time. Also, by storing the
grand-total we don't care if naughty users try to mess around with the
value-per-goods, shipping, whatever; you only care if we get the right
amount of money.
6. If the gross doesn't match, EITHER:deduct the funds received from a
running total and store this back into the database. Send an Email to
the purchaser confirming partial receipt and that they'd better
cough-up more cash [and bomb-out of the script] OR: notify customer
services of overpayment so that they can offer a discount on future
purchases or a refund of the overpayment (less fees) and continue
processing the script.
7. Set the "confirmed" status to 1 in your order table and also store
the paypal transaction id so that you always have traceability back to
paypal in the future should you need it. It also provides a link to
the paypal_ipn table where you grab the shipping name and address
info. Note, personally I wouldn't store all the IPN data as separate
fields but combine them into a Comma-Separated-Value string and just
store that - it'll be quicker and more resilient to paypal adding more
fields. CSV strings can be opened in most spreadsheets.
8. At this point you have received full payment, logged this into your
database and saved a link to the paypal transaction. You're now safe
to generate the CD key using whatever means - the order database entry
will indicate whether they purchased a download or a physical CD and
therefore determine whether to email this to the user or to your order
fulfilment dept.
9. Email the user thanking them for the payment and indicating how
long until their delivery will arrive.

Always remember, the golden rule of paypal processing is never trust
*anything* that paypal sent you as it is fully open to fraud even if
the data comes from paypal as users can change all sorts of values
during checkout. Ensure that you check *everything* against an order
entry stored locally before you accept payment.

Btw, please tell me that you changed the paypal Email address before
you posted this script!

Important Disclaimer: Answers and comments provided on Google Answers are general information, and are not intended to substitute for informed professional medical, psychiatric, psychological, tax, legal, investment, accounting, or other professional advice. Google does not endorse, and expressly disclaims liability for any product, manufacturer, distributor, service or service provider mentioned or any opinion expressed in answers or comments. Please read carefully the Google Answers Terms of Service.

If you feel that you have found inappropriate content, please let us know by emailing us at answers-support@google.com with the question ID listed above. Thank you.
Search Google Answers for
Google Answers  


Google Home - Answers FAQ - Terms of Service - Privacy Policy