Paypal IPN沙盒 - IPN监听器 - 未验证或无效

6

我在开发者模式下设置了测试账户Business和Personal,以尝试沙盒模式。

我在我的网站上创建了一个支付按钮。当我点击按钮并跳转到Paypal时,测试用户的个人账户被用于付款。当我查看Paypal商业账户时,支付会通过并且状态显示为已完成。我的网站重定向也运作良好。

因此,在我看来,我应该有一个“可工作”的系统。

然而,我的IPN监听器没有返回验证或无效状态。

以下是我的监听器:

 <?php
 require("../widgets/init.inc.php");

//Test the script is launced
$log_query = mysql_query("INSERT INTO `log` VALUES (' ','zz','ff','rr', 'rr')");                

// Link to PayPal Codes //
// https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables // 

// read the post from PayPal system and add 'cmd' //
$req = 'cmd=_notify-validate';

foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= "&$key=$value";
}

// post back to PayPal system to validate //
$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";

//For the below line i have tried "www.sandbox.paypal" as well as "www.paypal"
$fp = fsockopen ('ssl://www.paypal.com', 443, $errno, $errstr, 30);

// 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'];
$txnId = $_POST['txn_id'];
$receiverEmail = $_POST['receiver_email'];
$payerEmail = $_POST['payer_email'];
//$id = ($_POST['custom']);

if (!$fp) {

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

if ($payment_status=='Completed') {

//$txnIdCheck = mysql_query("SELECT `txnId` FROM `log` WHERE `txnId`='".$txnId."'");  
//if (mysql_num_rows($txnIdCheck) !=1) {

// enter your email address associated with your paypal account here // 
if ($receiverEmail=='biztester1@mydomain.dk') { 

/* when a new premium member makes payment paypal will update our database. The txn_id from paypal, members email address, date of payment and members id we gave them when they joined our site will be inserted into the log table. At the same time the memberAdmin table for the member will be updated by adding a '1' to premium and the date of payment so the new premium member will have access to premium areas of your site. */

//Check if verified is launced          
$log_query_2 = mysql_query("INSERT INTO `log` VALUES (' ','yes','yes','yes', 'yes')");          

//$log_query = mysql_query("INSERT INTO `log` VALUES (' ','".intval($id)."','".$txnId."','".$payerEmail."', now())");                   

//$update_premium = mysql_query("UPDATE `memberAdmin` SET `premium`=1, `premiumDate`=now() WHERE `id`=".intval($id)."");    

}       
//}
}
}
else if (strcmp ($res, "INVALID") == 0) {         
//Check if invalid is launced
$log_query_2 = mysql_query("INSERT INTO `log` VALUES (' ','no','no','no', 'no'");   
}
}
fclose ($fp);
}
?>

我已在Paypal商业账户网站上激活了IPN。我已经制作了一个来自Paypal的返回信息的txt文件,它看起来像这样:

==== Thu, 18 Jul 2013 01:34:21 +0200 ====
POST: mc_gross = 0.01
POST: protection_eligibility = Ineligible
POST: payer_id = 73WPPK8HKSW7C
POST: tax = 0.00
POST: payment_date = 15:15:20 Jul 17, 2013 PDT
POST: payment_status = Completed
POST: charset = windows-1252
POST: first_name = Niels
POST: mc_fee = 0.01
POST: notify_version = 3.7
POST: payer_status = verified
POST: business = biztester1@mydomain.dk
POST: quantity = 1
POST: verify_sign = A-ddKAwtxju4TpHGJOGq6c3ewj26AyiHsD.XO90coZ.4rVzOT7VyooKO
POST: payer_email = tester1@mydomain.dk
POST: txn_id = 6WH41489RE087103M
POST: payment_type = instant
POST: last_name = Tester
POST: receiver_email = biztester1@mydomain.dk
POST: payment_fee = 0.01
POST: receiver_id = PCX638B2M7WPA
POST: txn_type = web_accept
POST: item_name = 500Credits
POST: mc_currency = USD
POST: item_number = 1
POST: residence_country = DE
POST: test_ipn = 1
POST: handling_amount = 0.00
POST: transaction_subject = 500Credits
POST: payment_gross = 0.01
POST: shipping = 0.00
POST: ipn_track_id = 2dffcbf8767d3
InvoiceID: 
Custom1: 
Custom2: 
Custom3: 

看起来让它工作起来比我想象的更难。

1个回答

13

我找到了问题...让人恼火的是PayPal文档又错了..我不明白为什么文档会说按这个按钮,按那个按钮..但是在网站上根本没有这样的按钮...

针对这个问题 - 文档中说明的方法是错误/缺少所需的完整代码。

问题是:$res返回了以下内容:

HTTP/1.0 400 Bad Request
Server: BigIP
Connection: close
Content-Length: 19
Invalid Host header

需要添加的是:

$header .= "Host: www.sandbox.paypal.com\r\n";

然后上面的代码看起来像这样:

// 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";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);

这是我最终使用的完整代码:

<?php
require("../widgets/init.inc.php");         

// Link to PayPal Codes //
// https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables // 

// read the post from PayPal system and add 'cmd' //
$req = 'cmd=_notify-validate';

foreach ($_POST as $key => $value) {
 $value = urlencode(stripslashes($value));
 $req .= "&$key=$value";
}

// 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";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: " . strlen($req) . "\r\n\r\n";
$fp = fsockopen ('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);

// 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'];
$txnId = $_POST['txn_id'];
$receiverEmail = $_POST['receiver_email'];
$payerEmail = $_POST['payer_email'];
//$id = ($_POST['custom']);


if (!$fp) {

} else {
fputs ($fp, $header . $req);
while (!feof($fp)) {
$res = fgets ($fp, 1024);

$log_query_pre = mysql_query("INSERT INTO `log` VALUES (' ','$receiverEmail','$payment_status','$res', 'yes')");    

  if (strcmp ($res, "VERIFIED") == 0) {

if ($payment_status=='Completed') {

//$txnIdCheck = mysql_query("SELECT `txnId` FROM `log` WHERE `txnId`='".$txnId."'");  
//if (mysql_num_rows($txnIdCheck) !=1) {

// enter your email address associated with your paypal account here // 
if ($receiverEmail=='biztester1@mydomain.dk') { 

/* when a new premium member makes payment paypal will update our database. The txn_id from paypal, members email address, date of payment and members id we gave them when they joined our site will be inserted into the log table. At the same time the memberAdmin table for the member will be updated by adding a '1' to premium and the date of payment so the new premium member will have access to premium areas of your site. */

//Check if verified is launced          
$log_query_2 = mysql_query("INSERT INTO `log` VALUES (' ','yes','yes','yes', 'yes')");      

//$log_query = mysql_query("INSERT INTO `log` VALUES (' ','".intval($id)."','".$txnId."','".$payerEmail."', now())");                   

//$update_premium = mysql_query("UPDATE `memberAdmin` SET `premium`=1, `premiumDate`=now() WHERE `id`=".intval($id)."");    

}       
//}
}
}
else if (strcmp ($res, "INVALID") == 0) {         
//Check if invalid is launced
$log_query_2 = mysql_query("INSERT INTO `log` VALUES (' ','no','no','no', 'no')");  
}
}
fclose ($fp);
}
?>

1
谢谢!我整个下午都遇到了同样的问题。很高兴找到了这个解决方法。令人沮丧的是,这是一个文档错误... - Karl M.W.
@Niels,我按照上面的步骤操作,但是$res返回的是HTTP/1.1 200 OK,我该如何获取VERIFIED或INVALID消息? - avolquez
1
@evolquez - 它从 $res 输出多行,当第一行是 HTTP/1.1 200 时,下一行确定实际的返回消息.. 返回消息是你获得“已验证”消息的地方.. 上述代码仍然适用于 PayPal 集成,因为它在线上我的一个网站上运行.. - Niels
@Niels,我甚至没有得到验证,而是得到了HTTP/1.1 200和其他cookie、日期等。有什么想法吗? - Marium Malik
@MariumMalik 我相信自创建以来,PayPal的事情已经发生了变化。 - Niels
对于任何需要帮助的人:注意,"\r\n\r\n"; 应该只在标头的最后一行中出现。标头中存在过多的换行符会导致响应既不包含 VERIFIED 也不包含 INVALID。这也意味着您应该防御性地编写代码以应对这种可能性:只有在收到 VERIFIED 响应时才继续进行交易,以保护自己不仅免受 INVALID 响应的影响,还免受包含其中一个或都没有的格式不良的响应的影响。 - Fabien Snauwaert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接