i am using the following code for my paypal configuration here's the code
///Paypal Array///
$data=array(
//merchant email for live
//'merchant_email'=>'sumofish#yahoo.com',
//merchant email for test
'merchant_email'=>'uneebmir321-facilitator#yahoo.com',
'product_name'=>$bundleplan." Bundle Plan",
's_amount'=>$bundle_came_price, // Second` Amount
's_cycle'=>'M', //Second Period M=montrh,Y=year ,D=Days, W='week'
's_period'=>$period, // Second Cycle
//see small_price fucntionality again
'small_price'=>$bundle_came_price,
////see small_price fucntionality again
'currency_code'=>'USD',
'thanks_page'=>"https://".$_SERVER['HTTP_HOST'].'/puppy/puppy/thanks222.php',
'notify_url'=>"https://puppybundle.com/beta/ipn.php",
'cancel_url'=>"https://puppybundle.com/beta/index.php",
//true for sandbox false for live
'paypal_mode'=>true,
//true for sandbox false for live
'currency_symbole'=>'$'
);
///Paypal Array///
and here's the ipn class
<?php
session_start();
$unique_id=$_SESSION['unique_id'];
include("db.php");
file_put_contents("newfile.txt",var_export($_POST,true));
$status="not_completed";
$status2="paid";
$status3="remaining";
$zero=0;
$currency="CAD";
$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)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
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";
}
// STEP 2: Post IPN data back to paypal to validate
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr'); // change to [...]sandbox.paypal[...] when using sandbox to test
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_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// In wamp like environments that do not come bundled with root authority certificates,
// please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path
// of the certificate as shown below.
// curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
if( !($res = curl_exec($ch)) ) {
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
// STEP 3: Inspect IPN validation result and act accordingly
if (strcmp (trim($res), "VERIFIED") == 0) {
// check whether the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
// assign posted variables to local variables
$price = $_POST['mc_gross'];
$currency = $_POST['mc_currency'];
$payer_email = $_POST['payer_email'];
$txn_id=$_POST['txn_id'];
$item_name = $_POST['item_name'];
if($item_name=="Small Bundle Plan"){
$item_name="small";
}
parse_str($_POST['custom'],$_MYVAR);
$custom =$_MYVAR['id'];
$unique_id =$_MYVAR['unique_id'];
trim($custom);
$txt =$custom;
$currency_code= $_POST['currency_code'];
$fulldate = gmdate('Y-m-d H:i:s');
if($txn_id){
$query="UPDATE `puppy_pending_transaction` SET `status`=? WHERE unique_id=?";
$stmt = $db->prepare($query);
if($stmt){
$stmt->bind_param("ss", $status2,$unique_id);
$stmt->execute();
$stmt->close();
}
$query="INSERT INTO `puppy_transaction_confirmed`(`transaction_id`,`unique_id`, `user_id`, `payer_email`, `transaction_time`, `package`, `amount`, `currency`,`status_delivery`) VALUES (?,?,?,?,?,?,?,?,?)";
$stmt = $db->prepare($query);
if($stmt)
{
$check=$stmt->bind_param("sssssssss",$txn_id,$unique_id,$custom,$payer_email,$fulldate,$item_name,$price,$currency,$status);
$stmt->execute();
$stmt->close();
}
$query="INSERT INTO `puppy_paid_transaction_record`(`unique_id`, `month_delivered`, `total`,`status`) VALUES (?,?,?,?)";
$stmt = $db->prepare($query);
if($stmt){
$stmt->bind_param("ssss", $unique_id,$zero,$item_name,$status3);
$stmt->execute();
$stmt->close();
}
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
}
?>
the problem is this code is working 100% fine for sandbox now what i did for live version is to change the merchant email to client email and paypal_mode to false for live testing and one other thing i changed the
`$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
in the ipn class to
`$ch = curl_init('https://www.paypal.com/cgi-bin/webscr');`
for live mode i have used ipn simulator to validate my file and it is ok more over i am sure about the ipn path i set in paypal for merchant that is also ok am sure of it, i dont know whats wrong! can anybody point me on the right direction?
As per downloading the newer PHP IPN code from here. You save the IPN class file to whereever you need it (exampled below in the same folder as your IPN file.
You will also need to setup a Exception catching routine as Paypal don't implement one by default (that's what the try{ ...} catch{} block does).
<?php
define("LOG_FILE", "paypal_ipn.log");
////edit
error_log("Log File Started:\n",3,LOG_FILE);
require('PaypalIPN.php'); //check path is correct.
$ipn = new PayPalIPN();
try {
// Use the sandbox endpoint during testing.
$ipn->useSandbox(); //comment this line out to use live version.
$verified = $ipn->verifyIPN(); //returns true or false.
if ($verified) {
/*****
* Process IPN
* A list of variables is available here:
* https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNandPDTVariables/
*
* Here is where you add your data from your current setup, your own custom data to take the values from Paypal and process them.
****/
// check whether the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
// assign posted variables to local variables
$price = $_POST['mc_gross'];
$currency = $_POST['mc_currency'];
$payer_email = $_POST['payer_email'];
$txn_id = $_POST['txn_id'];
$item_name = $_POST['item_name'];
if ($item_name == "Small Bundle Plan") {
$item_name = "small";
}
parse_str($_POST['custom'], $_MYVAR);
$custom = $_MYVAR['id'];
$unique_id = $_MYVAR['unique_id'];
trim($custom);
$txt = $custom;
$currency_code = $_POST['currency_code'];
$fulldate = gmdate('Y-m-d H:i:s');
if ($txn_id) {
$query = "UPDATE `puppy_pending_transaction` SET `status`=? WHERE unique_id=?";
$stmt = $db->prepare($query);
if ($stmt) {
$stmt->bind_param("ss", $status2, $unique_id);
$stmt->execute();
$stmt->close();
}
$query = "INSERT INTO `puppy_transaction_confirmed`(`transaction_id`,`unique_id`, `user_id`, `payer_email`, `transaction_time`, `package`, `amount`, `currency`,`status_delivery`) VALUES (?,?,?,?,?,?,?,?,?)";
$stmt = $db->prepare($query);
if ($stmt) {
$check = $stmt->bind_param("sssssssss", $txn_id, $unique_id, $custom, $payer_email, $fulldate, $item_name, $price, $currency, $status);
$stmt->execute();
$stmt->close();
}
$query = "INSERT INTO `puppy_paid_transaction_record`(`unique_id`, `month_delivered`, `total`,`status`) VALUES (?,?,?,?)";
$stmt = $db->prepare($query);
if ($stmt) {
$stmt->bind_param("ssss", $unique_id, $zero, $item_name, $status3);
$stmt->execute();
$stmt->close();
}
/***
* End OP code
***/
}
// Reply with an empty 200 response to indicate to paypal the IPN was received correctly.
header("HTTP/1.1 200 OK");
}
}
catch (Exception $e) {
error_log("There was a problem: ".$e->getMessage(),3,LOG_FILE);
}
I also highly recommend you download the associated .pem file and upload it to your server and adjust the reference to it on line 106 of the IPN class file. This pem file has a key for your server to communicate with the Paypal secure server and solves a large batch of historic problems relating to this.
You may need to tweak some of your own code for editing the incoming data but this script works for me much better than the older procedural Paypal code did.
.pem file clarification:
I may be easiest for at least testing perspective to simply have the Paypal Pem file in the same folder as you keep your paypal IPN class.
so line 106 of the class:
if ($this->use_local_certs) {
curl_setopt($ch, CURLOPT_CAINFO, "cacert.pem");
}
and ensure that $this->use_local_certs = true;.
Related
I have been at this for 3 days now and still can't get it to work.
What I want to do is to get PayPal response from the IPN listener so that I can modify my database accordingly, but no matter what I do, it just won't work. I have already done the following in my PayPal Sandbox account:
Enabled Auto Return
Set Auto Return URL ('paypal/success')
Enabled Payment Data Transfer (PDT)
Enabled IPN message reception
Set IPN URL ('paypal/ipn')
The redirect to Auto Return URL works fine and I receive the payment data in success page, but the IPN won't process for reasons beyond me. A quick look at the IPN history on my PayPal profile shows that the messages are being sent, but I don't receive them at my end.
Here is my current IPN listener: Paypal/ipn
public function ipn() {
//Build the data to post back to Paypal
$postback = 'cmd=_notify-validate';
// go through each of the posted vars and add them to the postback variable
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$postback .= "&$key=$value";
}
// build the header string to post back to PayPal system to validate
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= "Host: www.sandbox.paypal.com\r\n";//or www.sandbox.paypal.com
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($postback) . "\r\n\r\n";
// Send to paypal or the sandbox depending on whether you're live or developing
// comment out one of the following lines
$fp = fsockopen ('www.sandbox.paypal.com', 443, $errno, $errstr, 30);//open the connection
//$fp = fsockopen ('www.paypal.com', 80, $errno, $errstr, 30);
// or use port 443 for an SSL connection
//$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);
if ( ! $fp ) {
// HTTP ERROR Failed to connect
$message = 'HTTP ERROR Failed to connect!';
$this->email_me($message);
} else { // if we've connected OK
fputs ($fp, $header . $postback); //post the data back
while ( ! feof($fp) ) {
$response = fgets ($fp, 1024);
if (strcmp (trim($response), "VERIFIED") == 0) { //It's verified
//read the payment details and the account holder
$payment_status = $_POST['payment_status'];
$receiver_email = urldecode($_POST['receiver_email']);
// further checks
if( ($payment_status == 'Completed') && ($receiver_email == $this->business_email) ) {
$message = 'IPN verified successfully!';
$this->email_me($message);
// Insert the transaction data in the database
$this->product_model->insert_transaction_details($_POST);
} else {
$message = 'Payment could not be verified!';
$this->email_me($message);
}
} else {
$message = 'IPN invalid!';
$this->email_me($message);
}
}
}
}
Can someone point me in the right direction please?
Also, is there anyway I can check the IPN response ("VERIFIED" or "INVALID") on chrome debugger or in my PayPal Sandbox dashboard? I can see delivery status in my dashboard but it doesn't say 'Verified' or 'Invalid' anywhere.
I found the solution! I wrote the IPN handler inside a controller that allows access to users who are logged in as admin. Apparently, the IPN method was denying access to PayPal to verify the transaction. I figured this out and wrote the IPN method in a different controller and everything worked perfectly.
I also changed my IPN handler to this code (although the original might still work... i didn't try it):
class Paypal_ipn extends MY_Controller {
public function __construct() {
parent::__construct();
$this->load->model('product_model');
$this->sandbox = $this->config->item('sandbox');
$this->paypal_host = $this->config->item('paypal_host');
$this->paypal_url = $this->config->item('paypal_url');
$this->business_email = $this->config->item('business');
}
public function ipn() {
// STEP 1: Read POST data
// reading posted data from directly from $_POST causes serialization
// issues with array data in POST
// reading raw POST data from input stream instead.
$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)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
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";
}
// STEP 2: Post IPN data back to paypal to validate
$ch = curl_init($this->paypal_url);
$headers = array(
'POST /cgi-bin/webscr HTTP/1.1',
'Host: ' . $this->paypal_host,
'Content-Type: application/x-www-form-urlencoded; charset=utf-8',
'Content-Length: ' . strlen($req),
'User-Agent: PayPal-IPN-VerificationScript',
'Connection: Close'
);
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_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
if( !($res = curl_exec($ch)) ) {
curl_close($ch);
exit;
}
curl_close($ch);
// STEP 3: Inspect IPN validation result and act accordingly
if (strcmp ($res, "VERIFIED") == 0) {
// check whether the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
// 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 = urldecode($_POST['receiver_email']);
$payer_email = $_POST['payer_email'];
$school_id = $_POST['custom'];
// further checks
if($payment_status == 'Completed') {
$message = 'IPN verified successfully!';
$this->email_developer($message);
// Insert the transaction data in the database
$this->product_model->insert_transaction_details($_POST);
} else {
$message = 'Payment could not be verified!';
$this->email_developer($message);
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
$message = 'IPN Invalid!';
$this->email_developer($message);
}
}
}
For those that might experience my predicament, ensure you also do the following:
If you enabled Cross Site Request Forgery (CSRF), ensure the IPN listener/handler is whitelisted, else IPN message will fail (Error 403 in PayPal IPN history).
To be sure your IPN listener is working well, run it as a URL and see the response. If there is any error, it won't work. For response, trying echoing "Verified" or "Invalid".
Use the PayPal IPN Simulator to test the process. Include a procedure that will submit information to the database upon success.
I hope it helps someone.
use php://input instead of $_POST
reson described here in details : PHP "php://input" vs $_POST
also paypal has documentation for implementing IPN Listener and its in php as well
Paypal tutorial
My PayPal IPN code receives the post data sent back from PayPal upon purchase completion but doesn't insert the data into the database. I had the code send me an email if it fails to insert the data into the database. If I take that post data and paste it into the address bar after ipn.php?, it works perfectly. What am I doing wrong? Do I have to post the data back to PayPal?
My IPN Listener
<?php
include 'includes/class.user.php';
$user = new USER();
$emailtext = "";
$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)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
foreach ($myPost as $key => $value) {
$value = urlencode($value);
$req .= "&$key=$value";
}
$paypalURL = "https://ipnpb.paypal.com/cgi-bin/webscr";
$ch = curl_init($paypalURL);
if ($ch == FALSE) {
return FALSE;
}
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);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close', 'User-Agent: Company name removed for security, LLC.'));
$res = curl_exec($ch);
$tokens = explode("\r\n\r\n", trim($res));
$res = trim(end($tokens));
//Payment data
$txn_id = $_GET['txn_id'];
$payment_gross = $_GET['mc_gross'];
$currency_code = $_GET['mc_currency'];
$pay_status = $_GET['payment_status'];
$payer_email = $_GET['payer_email'];
$date = date("m-d-y");
$invNum = md5($date);
$orderNum = md5($txn_id);
$fullname = $_GET['address_name'];
$address = $_GET['address_street'];
$city = $_GET['address_city'];
$state = $_GET['address_state'];
$zip = $_GET['address_zip'];
if($user->verify_txnid($txn_id)){
exit();
}else{
$insertPayment = $user->insert_purchase($fullname,$address,$city,$state,$zip,"0000000000",$payer_email,$orderNum,$date,$txn_id,$invNum,$pay_status);
if($insertPayment === TRUE){
$num_cart_items = $_GET['num_cart_items'];
for ($i = 0; $i <= $num_cart_items; $i++) {
$order_item_name = $_GET['item_name' . $i];
$order_item_quantity = $_GET['quantity' . $i];
$order_item_gross_amount = $_GET['mc_gross_' . $i];
$order_item_custom = $_GET['option_selection1_' . $i];
$user->insert_order($txn_id, $order_item_name, $order_item_quantity, $order_item_gross_amount, $order_item_custom);
}
}else{
foreach ($_GET as $key => $value)
{
$emailtext .= $key . " = " .$value ."\n\n";
}
mail('myemail#gmail.com', 'Insert Payment FAILED', $emailtext."\r\n".$req."\r\n".$res);
}
}
header("HTTP/1.1 200 OK");
I have confirmed that the post from PayPal matches the post back to PayPal but I still get an INVALID response and none of the data gets inserted into the database. If I take the raw post data string from PayPal's initial POST and paste it into the address bar, it inserts into the database just fine. I can't figure out for the life of me why it's not inserting the data into the database on its own. My IPN history on PayPal show an html response of 500 and that its retrying.
If IPN message you POST back does not exactly match the one PayPal sent to you, INVALID status will be occurred. You can double check if there have mess code in the IPN message you POST back, such as non-English characters. But if you still cannot find out the root cause, you have to contact PayPal technical support via https://www.paypal-techsupport.com/app/ask/ and provide PayPal transaction ID for the further checking.
Had the exactly same problem on sandbox.
Got it working by switching off "Negative Testing" on the account profile settings and removing the date from the ipn simulator.
That simple code made it work
$ipn_post_data = $_POST;
// Choose url
if (array_key_exists('test_ipn', $ipn_post_data) && 1 === (int) $ipn_post_data['test_ipn']){
$url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
}else{
$url = 'https://www.paypal.com/cgi-bin/webscr';
}
// Set up request to PayPal
$request = curl_init();
curl_setopt_array($request, array
(
CURLOPT_URL => $url,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => http_build_query(array('cmd' => '_notify-validate') + $ipn_post_data),
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_HEADER => FALSE,
));
// Execute request and get response and status code
$response = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
// Close connection
curl_close($request);
if ($status == 200 && $response == 'VERIFIED') {
$today = date("Y_m_d");
$file = fopen("LogIPN.$today.txt", "ab");
$hour = date("H:i:s T");
fwrite($file, "Verified\r\n");
} else {
$today = date("Y_m_d");
$file = fopen("LogIPN.$today.txt", "ab");
$hour = date("H:i:s T");
fwrite($file, $response."\r\n");
fwrite($file, $status."\r\n");
}
I would never use that code in production for many reasons.
Hope this can make things easy for u.
i took the example implementation of the IPN handler from paypals official website
now this worked very well in the beginning. But the answers got slower and slower. For a while they worked fine with 20 seconds delay. Now I am getting no traffic at all for minutes. What is going on? maybe is the sandbox ipn handler of paypal themselves close to be down? they have a bad setup?
I want to mention this post (it kinda shed some light)
note i get not even an initial log. Paypal seems to not call my side at all any longer.
The IPN is callable, i can call it from, say an incognito window, and it will write to the logfile.
for completeness, here my file:
<?php
use ...;
// CONFIG: Enable debug mode. This means we'll log requests into 'ipn.log' in the same directory.
// Especially useful if you encounter network errors or other intermittent problems with IPN (validation).
// Set this to 0 once you go live or don't require logging.
define("DEBUG", 1);
// Set to 0 once you're ready to go live
define("USE_SANDBOX", 1);
define("LOG_FILE", "./ipn.log");
require_once(__DIR__ . "/../../bootstrap.php");
//define("LOG_FILE", __DIR__ . "/../../../../../japi/logs/ipn.log");
error_log(date('[Y-m-d H:i e] '). "Lukas: Initial call to log". PHP_EOL, 3, LOG_FILE);
// Read POST data
// reading posted data directly from $_POST causes serialization
// issues with array data in POST. Reading raw POST data from input stream instead.
$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)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
//$mmessage ="hi lukas <br/>";
//
// include_once JCR_KINT_CLASS;
//$mmessage .= #\Kint::dump($myPost);
//mail("lukas.meier#gmail.com", "IPN from paypal test", $mmessage);
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
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 IPN data back to PayPal to validate the IPN data is genuine
// Without this step anyone can fake IPN data
if(USE_SANDBOX == true) {
$paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr";
} else {
$paypal_url = "https://www.paypal.com/cgi-bin/webscr";
}
$ch = curl_init($paypal_url);
if ($ch == FALSE) {
return FALSE;
}
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_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
if(DEBUG == true) {
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
}
// CONFIG: Optional proxy configuration
//curl_setopt($ch, CURLOPT_PROXY, $proxy);
//curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
// Set TCP timeout to 30 seconds
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// CONFIG: Please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path
// of the certificate as shown below. Ensure the file is readable by the webserver.
// This is mandatory for some environments.
//$cert = __DIR__ . "./cacert.pem";
//curl_setopt($ch, CURLOPT_CAINFO, $cert);
$res = curl_exec($ch);
if (curl_errno($ch) != 0) // cURL error
{
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Can't connect to PayPal to validate IPN message: " . curl_error($ch) . PHP_EOL, 3, LOG_FILE);
}
curl_close($ch);
exit;
} else {
// Log the entire HTTP response if debug is switched on.
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "HTTP request of validation request:". curl_getinfo($ch, CURLINFO_HEADER_OUT) ." for IPN payload: $req" . PHP_EOL, 3, LOG_FILE);
error_log(date('[Y-m-d H:i e] '). "HTTP response of validation request: $res" . PHP_EOL, 3, LOG_FILE);
}
curl_close($ch);
}
// Inspect IPN validation result and act accordingly
// Split response headers and payload, a better way for strcmp
$tokens = explode("\r\n\r\n", trim($res));
$res = trim(end($tokens));
if (strcmp ($res, "VERIFIED") == 0) {
// check whether the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your PayPal email
// check that payment_amount/payment_currency are correct
// process payment and mark item as paid.
// 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'];
$jppa = new JcrPayPalAnswer();
$jppa->createFromPayPalIpnResponseArray($myPost);
if($jppa->is_completed){
//content of file: paykey => array($project_id, serialize($fd), $statisticsId);
$r = parse_ini_file(JCR_PAYKEY_INIFILE);
if($r){
$pkArr = $r[$jppa->pay_key];
$project_id = $pkArr[0];
/** #var FundingDetails $fd */
$fd = unserialize(base64_decode($pkArr[1]));
$statisticsId = $pkArr[2];
$jcrp = new JewcerProject($project_id);
$jewcerFee = $jcrppa->amount_fee_account;
$fundingAmount = $jcrppa->amount_funding_account_brutto;
$x = null;
$js = new JcrStatistic($x, $statisticsId);
//fna [$amount, $paypalfee, $jewcerFeeAmount]
$fnA = JcrPayPalService::getFeesAndAmount($fd->amount, $fd->coverfee, $jcrp->getFundingFee());
$amount = $fnA[0];
$paypalfee = $fnA[3];
$jewcerFeeAmount = $fnA[2];
$fd->wepayFee = $paypalfee;
$fd->jcrFee = $jewcerFeeAmount;
$amount_with_fee = $amount;
if ($fd->coverfee) {
$fd->amount_without_fees = $amount - $paypalfee - $jewcerFeeAmount;
} else {
$fd->amount_without_fees = $fd->amount;
}
$fd->amount = $amount_with_fee;
$jcrf = new JcrFunder($project_id);
$jcrf->setBasicFunderValues($fd);
$jcrf->save();
$js->add_stats_from_fundingdetails($fd, "jfp3");
EmailService::sendDonationSuccessEmails($jcrp, $fd);
unset($r[$jppa->pay_key]);
UtilityService::write_ini_file($r, JCR_PAYKEY_INIFILE);
UtilityService::write_ini_file(array('ok', $jcrf->id ), JCR_PAYPAL_STATUS_FOLDER . $jppa->pay_key);
}else{
error_log(date('[Y-m-d H:i e] '). "JEWCER ERROR 3200: couldn't find entry for paykey in inifile, inifile val: " . var_export($r, true). PHP_EOL, 3, LOG_FILE);
}
// mail("lukas.meier#gmail.com", "IPN COMPLETED", serialize($jppa));
// JcrPayPalKeyPool::$keys[$jppa->pay_key] = $jppa; //eventually verify emails
// JcrPayPalKeyPool::$keys[$jppa->pay_key] = $jppa; //eventually verify emails
}
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Verified IPN: $req ". PHP_EOL, 3, LOG_FILE);
}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
// Add business logic here which deals with invalid IPN messages
if(DEBUG == true) {
error_log(date('[Y-m-d H:i e] '). "Invalid IPN: $req" . PHP_EOL, 3, LOG_FILE);
}
}
I have been using IPN for many years with a wide variety of projects. In my experience, if everything is configured correctly and your IPN script has no problems then it will work very close to real-time without many hiccups.
What you can run into, though, is if your IPN script is failing for any reason, or simply sending PayPal a response code other than 200 when it gets hit, it will re-try, but each time it re-tries it delays the time in which it sends. So it might send one instantly, if that doesn't provide a 200 response it'll send another one in 5 seconds, 10 seconds, 20 seconds, 40 seconds, etc. (that's not the exact increment it uses but it's an example of what it does.)
If your script is not returning a 200 response at some point, or if this is happening a lot with the IPNs that are getting sent to your script, PayPal's system will move your IPNs to a slower queue than the others that are working well.
Eventually, if it still isn't fixed, they'll just disable it altogether on your account.
Check the IPN history in your PayPal account to see what response you're getting back there on the IPNs that are getting sent. Of course, this will allow you to verify that they are indeed getting sent as well.
You'll also want to check the PHP error logs on your server to see if anything is going on when the IPN script gets hit that causes it to fail for any reason. This could be happening with only certain IPN types or when particular characters are included in the data, for example.
For some reason im having more trouble with this then I should... I have a IPN listner for Paypal and the IPN Simulator says successful each time and with different methods, but I can not get it to then manipulate the database based on a successful response.
Any ideas anyone?
<?php
//INCLUDE CONNECTION STRING
include('connect.php');
// STEP 1: read POST data
// Reading POSTed data directly from $_POST causes serialization issues with array data in the POST.
// Instead, read raw POST data from the input stream.
$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)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
$req = 'cmd=_notify-validate';
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";
}
// STEP 2: POST IPN data back to PayPal to validate
$ch = curl_init('https://www.paypal.com/cgi-bin/webscr');
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_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
if( !($res = curl_exec($ch)) ) {
error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
// STEP 3: Inspect IPN validation result and act accordingly
if (strcmp ($res, "VERIFIED") == 0) {
// 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'];
if($payment_status=="Completed"){
$selectuser = mssql_query("select statsmemberid from statsmembers where email='$payer_email'");
if(mssql_num_rows($selectuser) != 0){
$row = mssql_fetch_row($selectuser);
$statsmemberid = $row[0];
$getCredits = mssql_query("select creditsbought from statsmuplayers where statsmemberid='$statsmemberid'");
$row = mssql_fetch_row($getCredits);
$totalCredits = $row[0]+11;
$updatemu = mssql_query("update statsmuplayers set creditsbought='$totalCredits' where statsmemberid='$statsmemberid'");
echo "Credits Applyed";
}else{
echo "Invalid Email";
}
}
} else if (strcmp ($res, "INVALID") == 0) {
// IPN invalid, log for manual investigation
echo "The response from IPN was: <b>" .$res ."</b>";
}
?>
I'd suggest you encapsulate all of this code into a few objects. That will really help you figure out where things are going wrong.
You want an object that handles the IPN communication with paypal (you don't need to write that from scratch here's the first PHP implementation I found in a google search https://github.com/dodev34/paypal-ipn-response-client)
Then you want a base object that handles your database connections. And finally you want a statsmembers object that extends your database connection object and enforces your business logic. You might just lift some PHP ORM code like what you see here http://www.phpactiverecord.org/projects/main/wiki/Quick_Start
That would allow you to test the update functionality separately from the actual paypal IPN communication. The good news is at that point you don't have to rely on writing to system files for debugging like someone suggested in a comment above.
I don't see the specific error in your code just from a glance sorry, are you sure your passing the payer_email through paypal correctly?
I have a working IPN script. It is updating the data correctly in database according to the logged in user. So, Once the payment is successfully made user account will be upgraded as a paid member. And since i am using a paypal subscribe button. So, from the next month billing process will occur automatically.
So, here what i think(I am not sure), Paypal will not interact with my IPN script stored in my server.
So, My question is :-
If my assumption about IPN script is correct then how could i track which user has made a payment for the next billing cycle? (I don't want to be involved with manual work like tracking user payment information from my Paypal merchant account. I just want to do it through a script. So, once the subscription amount has been deducted from user Paypal account his account on my website will be upgraded as a paid member.)
For the reference what exactly i wanted to update through my ipn script. Below is my IPN script.
<?php
// STEP 1: Read POST data
// reading posted data from directly from $_POST causes serialization
// issues with array data in POST
// reading raw POST data from input stream instead.
$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)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the post from PayPal system and add 'cmd'
$req = 'cmd=_notify-validate';
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";
}
// STEP 2: Post IPN data back to paypal to validate
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
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_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// In wamp like environments that do not come bundled with root authority certificates,
// please download 'cacert.pem' from "http://curl.haxx.se/docs/caextract.html" and set the directory path
// of the certificate as shown below.
// curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
if( !($res = curl_exec($ch)) ) {
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
// STEP 3: Inspect IPN validation result and act accordingly
if (strcmp ($res, "VERIFIED") == 0) {
// check whether the payment_status is Completed
// check that txn_id has not been previously processed
// check that receiver_email is your Primary PayPal email
// check that payment_amount/payment_currency are correct
// process payment
// assign posted variables to local variables
$item_name = $_POST['item_name'];
$item_name=strip_tags($item_name);
$item_number = strip_tags($_POST['item_number']);
$payment_status = strip_tags($_POST['payment_status']);
$payment_amount = strip_tags($_POST['mc_gross']);
$payment_currency = strip_tags($_POST['mc_currency']);
$txn_id = strip_tags($_POST['txn_id']);
$user_id=strip_tags($_POST['custom']);
$receiver_email = strip_tags($_POST['receiver_email']);
$payer_email = strip_tags($_POST['payer_email']);
//if(strcmp($receiver_email, "h_1356964205_per#gmail.com") == 0)
//{
/*if($payment_status != "Completed")
{
$msg="Transaction with id ".$txn_id." status is not completed..";
mail("support#example.com","Transaction with the same id already exists in database.",$msg,"From:admin#leadstool.net");
exit();
}*/
include_once('connection.php');
//$user_id=getfield('id');
// Query to check the duplicate transaction id.
$query="SELECT `User_id` FROM `transaction` WHERE `Transaction_id`='".mysql_real_escape_string($txn_id)."'";
if($query_run=mysql_query($query))
{
$num=mysql_num_rows($query_run);
if($num == 0)
{
// Query to check the number of times for subscription.
$query="SELECT `Transaction_id` FROM `transaction` WHERE `User_id`='".mysql_real_escape_string($user_id)."'";
if($query_run=mysql_query($query))
{
$num=mysql_num_rows($query_run);
if($num>=1)
{
$type_of_subscription=2;// This 2 will denote the user is rnewing his account
} else {
$type_of_subscription=1;// Here 1 is denoting that user has subscribed for the 1st time.
}
$query="SELECT `B_ad_no_paid_user`,`T_ad_no_paid_user` FROM `WebsiteContent` WHERE `Creator_id`='1' ORDER BY `Date_of_update` DESC LIMIT 1";
if($query_run=mysql_query($query))
{
while($rows=mysql_fetch_array($query_run))
{
$banner_ad_limit=$rows['B_ad_no_paid_user'];
$text_ad_limit=$rows['T_ad_no_paid_user'];
}
}
}// Query to check the number of times for subscription ends here.
//Query to insert the transaction details in database.
$query="INSERT INTO `transaction` VALUES('".$txn_id."','".$user_id."','".$payment_amount."','".$type_of_subscription."','".$payment_status."','1','".$payer_email."',now())";
if($query_run=mysql_query($query))
{
$query="UPDATE `user` SET `User_type`='1', `Banner_ad_limit`='".$banner_ad_limit."', `Text_ad_limit`='".$text_ad_limit."' WHERE `id`='".mysql_real_escape_string($user_id)."'";
if($query_run=mysql_query($query))
{
$msg="Thank you for subscribing to our service. Your Transaction Id is $txn_id.";
mail("$payer_email","Subscription confirmation mail",$msg,"From:admin#example.com");
} else {
$msg="Thank you! Your transaction is successful with transaction id:- $txn_id. But we are unable to upgrade your profile right now. Please contact admin to resolve the problem.";
mail("$payer_email","Subscription confirmation mail",$msg,"From:admin#example.com");
}
} else {
$msg="For Transaction with id ".$txn_id." failed to update in database.";
mail("support#example.com","Unable to update the details in database.",$msg,"From:admin#example.com");
exit();
}
// Query to insert data in database ends here.
} else {
$msg="Transaction with id $txn_id already exists in database. Admin please verify the details manually and contact the user. Email id of user is: $payer_email";
mail("support#example.com","Transaction with the same id already exists in database.",$msg,"From:admin#example.com");
exit();
}// Query to check the duplicate transaction id ends here.
}
//} else {
//$msg="Investigate the reason why the registered email id with paypal does not matched with this id $receiver_email";
//mail("support#example.com","Receiver email address do not matched",$msg,"From:admin#example.com");
//exit();
//}
} else if (strcmp ($res, "INVALID") == 0) {
// log for manual investigation
$msg="Dear administrator please verify the reason why the transaction failure occures. The details is:- $res";
mail("support#example.com","IPN interaction was not verified.",$msg,"From:admin#example.com");
exit();
}
?>
This is possible using the IPN and notify_url.
Here is a good tutorial:
http://www.techrepublic.com/article/handling-recurring-payments-with-paypal-subscriptions-and-ipn/5331883