Sol*_*son 4 paypal paypal-buttons paypal-ipn
使用位于GitHub上的代码:https: //github.com/Quixotix/PHP-PayPal-IPN
好吧,所以,我有一个像我这样的php页面,我用作我的ipnlistener作为sa=paypal_verify
变量中url 的一部分,在按钮创建的第3步中如下:
notify_url=http://mydomain.com/index.php?page=paypaltest;sa=paypal_verify
return=http://mydomain.com/index.php?page=paypaltest;sa=thankyou
rm=2
Run Code Online (Sandbox Code Playgroud)
以下是链接到http://mydomain.com/index.php?page=paypaltest的代码
if (!empty($_REQUEST['sa']) && $_REQUEST['sa'] != 'thankyou')
{
// Require file for loading up global variables, etc. Might not be needed, but just in here in case.
require_once('/public_html/Settings.php');
global $smcFunc, $context, $scripturl, $boarddir, $modSettings, $txt;
if ($_REQUEST['sa'] == 'paypal_verify')
{
ini_set('log_errors', true);
ini_set('error_log', '/ipn_errors.log');
// Here is where the actual IpnListener Class is defined
// and uses cURL or fSocket to post back to paypal.
require_once($boarddir . '/ipn/ipnlistener.php');
$listener = new IpnListener();
try {
$listener->requirePostMethod();
$verified = $listener->processIpn();
} catch (Exception $e) {
error_log($e->getMessage());
exit(0);
}
/*
The processIpn() method returned true if the IPN was "VERIFIED" and false if it
was "INVALID".
*/
if ($verified) {
$errmsg = ''; // stores errors from fraud checks
// 1. Make sure the payment status is "Completed"
if ($_POST['payment_status'] != 'Completed') {
// simply ignore any IPN that is not completed
exit(0);
}
// Database Query in here (excluded) that selects sellers_email, product_name, and price from within the database for the actual purchase, based on the $_POST['item_name'] from paypal.
// If return results are 0 from database table than do following:
$errmsg .= "Product Not Found in the database: ";
$errmsg .= $_POST['item_name']."\n";
// manually investigate errors from the fraud checking
$body = "IPN failed fraud checks: \n$errmsg\n\n";
$body .= $listener->getTextReport();
mail('myemail@address.com', 'IPN Fraud Warning', $body);
exit(0);
// Set $sellers_email, $item_name, and $price variables from the database table. And than free the mysql result.
// 2. Make sure seller email matches your primary account email.
if ($_POST['receiver_email'] != $sellers_email) {
$errmsg .= "'receiver_email' does not match: ";
$errmsg .= $_POST['receiver_email']."\n";
}
// 3. Make sure the amount(s) paid match
if ($_POST['mc_gross'] != $price) {
$errmsg .= "'mc_gross' does not match: ";
$errmsg .= $_POST['mc_gross']."\n";
}
// 4. Make sure the currency code matches
if ($_POST['mc_currency'] != 'USD') {
$errmsg .= "'mc_currency' does not match: ";
$errmsg .= $_POST['mc_currency']."\n";
}
// 5. Ensure the transaction is not a duplicate.
// Attempt to grab a transaction id from the table where it goes. The column is id_txn, if it exists
$errmsg .= "'txn_id' has already been processed: ".$_POST['txn_id']."\n";
// free database result.
// Set $txn_id from paypal to the $txn_id variable.
$txn_id = (string) $_POST['txn_id'];
if (!empty($errmsg)) {
// manually investigate errors from the fraud checking
$body = "IPN failed fraud checks: \n$errmsg\n\n";
$body .= $listener->getTextReport();
mail('myemail@address.com', 'IPN Fraud Warning', $body);
} else {
// send email to buyer.
// and just to be sure, send them to the sa=thankyou page!
}
} else {
// send email to self with the errors listed
}
}
}
if (!empty($_REQUEST['sa']) && $_REQUEST['sa'] == 'thankyou')
echo '
<div class="information">Thank you for purchasing this product. We sent you an email with your details and link to download this software.<br />';
echo '
<div class="cat_bar boardframe">
<h3 class="catbg">
PayPal Test Sale
</h3>
</div>
<div class="roundframe blockframe">
This is just a Test Selling Page to be sure that the PayPal electronic downloads actually work!
</div>
<span class="lowerframe"><span><!-- // --></span></span>
<br />
<div class="cat_bar boardframe">
<h3 class="catbg">
Purchase at 0.01 USD
</h3>
</div>
<div class="roundframe blockframe">
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<input type="hidden" name="cmd" value="_s-xclick">
<input type="hidden" name="hosted_button_id" value="FJAGXAC7GCFSY">
<input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_paynow_SM.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" style="border: none; background: none;">
<img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
</form>
</div>
<span class="lowerframe"><span><!-- // --></span></span>
<br class="clear" />';
Run Code Online (Sandbox Code Playgroud)
ipnlistener.php文件中包含以下代码:
class IpnListener {
/**
* If true, the recommended cURL PHP library is used to send the post back
* to PayPal. If flase then fsockopen() is used. Default true.
*
* @var boolean
*/
public $use_curl = true;
/**
* If true, explicitly sets cURL to use SSL version 3. Use this if cURL
* is compiled with GnuTLS SSL.
*
* @var boolean
*/
public $force_ssl_v3 = true;
/**
* If true, cURL will use the CURLOPT_FOLLOWLOCATION to follow any
* "Location: ..." headers in the response.
*
* @var boolean
*/
public $follow_location = false;
/**
* If true, an SSL secure connection (port 443) is used for the post back
* as recommended by PayPal. If false, a standard HTTP (port 80) connection
* is used. Default true.
*
* @var boolean
*/
public $use_ssl = true;
/**
* If true, the paypal sandbox URI www.sandbox.paypal.com is used for the
* post back. If false, the live URI www.paypal.com is used. Default false.
*
* @var boolean
*/
public $use_sandbox = false;
/**
* The amount of time, in seconds, to wait for the PayPal server to respond
* before timing out. Default 30 seconds.
*
* @var int
*/
public $timeout = 30;
private $post_data = array();
private $post_uri = '';
private $response_status = '';
private $response = '';
const PAYPAL_HOST = 'www.paypal.com';
const SANDBOX_HOST = 'www.sandbox.paypal.com';
/**
* Post Back Using cURL
*
* Sends the post back to PayPal using the cURL library. Called by
* the processIpn() method if the use_curl property is true. Throws an
* exception if the post fails. Populates the response, response_status,
* and post_uri properties on success.
*
* @param string The post data as a URL encoded string
*/
protected function curlPost($encoded_data) {
if ($this->use_ssl) {
$uri = 'https://'.$this->getPaypalHost().'/cgi-bin/webscr';
$this->post_uri = $uri;
} else {
$uri = 'http://'.$this->getPaypalHost().'/cgi-bin/webscr';
$this->post_uri = $uri;
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_CAINFO,
dirname(__FILE__)."/cert/api_cert_chain.crt");
curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $encoded_data);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $this->follow_location);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
if ($this->force_ssl_v3) {
curl_setopt($ch, CURLOPT_SSLVERSION, 3);
}
$this->response = curl_exec($ch);
$this->response_status = strval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
if ($this->response === false || $this->response_status == '0') {
$errno = curl_errno($ch);
$errstr = curl_error($ch);
throw new Exception("cURL error: [$errno] $errstr");
}
}
/**
* Post Back Using fsockopen()
*/
protected function fsockPost($encoded_data) {
if ($this->use_ssl) {
$uri = 'ssl://'.$this->getPaypalHost();
$port = '443';
$this->post_uri = $uri.'/cgi-bin/webscr';
} else {
$uri = $this->getPaypalHost(); // no "http://" in call to fsockopen()
$port = '80';
$this->post_uri = 'http://'.$uri.'/cgi-bin/webscr';
}
$fp = fsockopen($uri, $port, $errno, $errstr, $this->timeout);
if (!$fp) {
// fsockopen error
throw new Exception("fsockopen error: [$errno] $errstr");
}
$header = "POST /cgi-bin/webscr HTTP/1.1\r\n";
$header .= "Host: ".$this->getPaypalHost()."\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= "Content-Length: ".strlen($encoded_data)."\r\n";
$header .= "Connection: Close\r\n\r\n";
fputs($fp, $header.$encoded_data."\r\n\r\n");
while(!feof($fp)) {
if (empty($this->response)) {
// extract HTTP status from first line
$this->response .= $status = fgets($fp, 1024);
$this->response_status = trim(substr($status, 9, 4));
} else {
$this->response .= fgets($fp, 1024);
}
}
fclose($fp);
}
private function getPaypalHost() {
if ($this->use_sandbox) return self::SANDBOX_HOST;
else return self::PAYPAL_HOST;
}
/**
* Get POST URI
*/
public function getPostUri() {
return $this->post_uri;
}
/**
* Get Response
*
* Returns the entire response from PayPal as a string including all the
* HTTP headers.
*
* @return string
*/
public function getResponse() {
return $this->response;
}
/**
* Get Response Status 200 if Successful
*/
public function getResponseStatus() {
return $this->response_status;
}
/**
* Get Text Report
*/
public function getTextReport() {
$r = '';
// date and POST url
for ($i=0; $i<80; $i++) { $r .= '-'; }
$r .= "\n[".date('m/d/Y g:i A').'] - '.$this->getPostUri();
if ($this->use_curl) $r .= " (curl)\n";
else $r .= " (fsockopen)\n";
// HTTP Response
for ($i=0; $i<80; $i++) { $r .= '-'; }
$r .= "\n{$this->getResponse()}\n";
// POST vars
for ($i=0; $i<80; $i++) { $r .= '-'; }
$r .= "\n";
foreach ($this->post_data as $key => $value) {
$r .= str_pad($key, 25)."$value\n";
}
$r .= "\n\n";
return $r;
}
/**
* Process IPN
*
* Handles the IPN post back to PayPal and parsing the response. Call this
* method from your IPN listener script. Returns true if the response came
* back as "VERIFIED", false if the response came back "INVALID", and
* throws an exception if there is an error.
*
* @param array
*
* @return boolean
*/
public function processIpn($post_data=null) {
$encoded_data = 'cmd=_notify-validate';
if ($post_data === null) {
if (!empty($_POST)) {
$this->post_data = $_POST;
$encoded_data .= '&'.file_get_contents('php://input');
} else {
throw new Exception("No POST data found.");
}
} else {
$this->post_data = $post_data;
foreach ($this->post_data as $key => $value) {
$encoded_data .= "&$key=".urlencode($value);
}
}
if ($this->use_curl) $this->curlPost($encoded_data);
else $this->fsockPost($encoded_data);
if (strpos($this->response_status, '200') === false) {
throw new Exception("Invalid response status: ".$this->response_status);
}
if (strpos($this->response, "VERIFIED") !== false) {
return true;
} elseif (strpos($this->response, "INVALID") !== false) {
return false;
} else {
throw new Exception("Unexpected response from PayPal.");
}
}
public function requirePostMethod() {
// require POST requests
if ($_SERVER['REQUEST_METHOD'] && $_SERVER['REQUEST_METHOD'] != 'POST') {
header('Allow: POST', true, 405);
throw new Exception("Invalid HTTP request method.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
error_log中没有记录错误(如果发生错误,实际上会创建此文件,因此它甚至不存在).它永远不会在数据库中添加任何内容.我知道item_name
PayPal服务器上的内容是正确的,并且也应该在数据库表中匹配.
似乎ipnlistener存在问题或者它是如何链接到它的?或者是从它返回了什么?
Is there a way I can test the return value exactly from the ipnlistener.php file? So that I can actually see it? Maybe if I could even see what gets sent to the following url (from Paypal): http://mydomain.com/index.php?page=paypaltest;sa=paypal_verify
I'm trying to be able to track this so that I can figure out exactly where it is going wrong at... Any ideas?
Perhaps it has something to do with this line in the cURL:
curl_setopt($ch, CURLOPT_CAINFO,
dirname(__FILE__)."/cert/api_cert_chain.crt");
Run Code Online (Sandbox Code Playgroud)
I never heard tell of attaching an SSL Certificate within the actual file structure of a server.
Also, it might be possible, because it has index.php
within the notify_url
that it is adding additional headers. If so, can I somehow flush everything out of the file before it grabs the encoded data from PayPal and returns it back to paypal for verification? That way it's sure to be exactly the same as when PayPal sent it to the notify_url
.
我不明白为什么要这么复杂!我已经连续7天拔掉头发,几乎没有睡觉!
我知道:我的服务器上启用了cURL.如果我浏览实际notify_url
页面它是BLANK(就像一个完全空的页面),我认为这是正确的.查看通知历史记录时,它显示已发送,HTTP响应代码为200.
这个我不知道:哪里失败了?PayPal通知究竟在哪里失败?为什么它没有触及我的数据库?我在任何地方都没有错误......
我发现问题,OMG,它在认证文件中:
刚刚更改了以下内容:
curl_setopt($ch, CURLOPT_CAINFO,
dirname(__FILE__)."/cert/api_cert_chain.crt");
Run Code Online (Sandbox Code Playgroud)
从/ home /开始到我服务器上的实际文件路径
curl_setopt($ ch,CURLOPT_CAINFO,"FULL FILE PATH/cert/api_cert_chain.crt");
天啊我真傻!现在完美运作!Yayyy!
归档时间: |
|
查看次数: |
3603 次 |
最近记录: |