First Commit
This commit is contained in:
1 parent
5eb9dd037d
commit
2e62c0e60b
11877 files changed
+2035312
No files matched your search
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
// Buffer anything db.php prints so a later HTTP 500 still reaches PayPal.
|
||||
ob_start();
|
||||
$noSession = 1;
|
||||
require("db.php");
|
||||
|
||||
date_default_timezone_set("Canada/Pacific");
|
||||
|
||||
if (!is_array($_POST) || count($_POST) == 0) {
|
||||
$contents = file_get_contents("/m/data1/app.hotlunches.net/ppPaymentHotLunch.log");
|
||||
echo(nl2br(htmlentities($contents)));
|
||||
exit();
|
||||
}
|
||||
|
||||
function saveOutput($contents) {
|
||||
$f = fopen("/m/data1/app.hotlunches.net/ppPaymentHotLunch.log", "w");
|
||||
fwrite($f, $contents);
|
||||
fclose($f);
|
||||
|
||||
$f = fopen("/m/data1/app.hotlunches.net/ppPaymentHotLunch.trx.log", "a");
|
||||
fwrite($f, $contents);
|
||||
fclose($f);
|
||||
|
||||
return nl2br(htmlentities($contents));
|
||||
}
|
||||
|
||||
function hotlunches_paypal_extract_status($body) {
|
||||
$body = trim($body);
|
||||
if ($body === 'VERIFIED' || $body === 'INVALID') return $body;
|
||||
if (preg_match('/(?:^|\r\n|\n)(VERIFIED|INVALID)\s*$/', $body, $m)) return $m[1];
|
||||
return $body;
|
||||
}
|
||||
|
||||
function hotlunches_paypal_postback_curl($url, $req) {
|
||||
if (!function_exists('curl_init')) {
|
||||
return array('ok' => false, 'status' => '', 'error' => 'cURL is not available');
|
||||
}
|
||||
|
||||
$ch = curl_init($url);
|
||||
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); // TLS 1.2
|
||||
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, 3);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 6);
|
||||
if (defined('CURL_IPRESOLVE_V4')) {
|
||||
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||
'User-Agent: PHP-IPN-VerificationScript',
|
||||
'Connection: Close'
|
||||
));
|
||||
$body = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
$errstr = curl_error($ch);
|
||||
$http_code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($body === false || $errno) {
|
||||
return array('ok' => false, 'status' => '', 'error' => $errno . ' - ' . $errstr);
|
||||
}
|
||||
if ($http_code !== 200) {
|
||||
return array('ok' => false, 'status' => hotlunches_paypal_extract_status($body), 'error' => 'PayPal HTTP ' . $http_code);
|
||||
}
|
||||
return array('ok' => true, 'status' => hotlunches_paypal_extract_status($body), 'error' => '');
|
||||
}
|
||||
|
||||
function hotlunches_paypal_postback_socket($url, $req) {
|
||||
$url_parts = parse_url($url);
|
||||
$host = isset($url_parts['host']) ? $url_parts['host'] : '';
|
||||
$path = isset($url_parts['path']) && $url_parts['path'] !== '' ? $url_parts['path'] : '/cgi-bin/webscr';
|
||||
if ($host === '') {
|
||||
return array('ok' => false, 'status' => '', 'error' => 'Invalid PayPal URL');
|
||||
}
|
||||
|
||||
$ssl = array(
|
||||
'verify_peer' => true,
|
||||
'verify_peer_name' => true,
|
||||
'peer_name' => $host
|
||||
);
|
||||
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
|
||||
$ssl['crypto_method'] = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
|
||||
}
|
||||
$ctx = stream_context_create(array('ssl' => $ssl));
|
||||
$fp = @stream_socket_client('ssl://' . $host . ':443', $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $ctx);
|
||||
if ($fp === false) {
|
||||
return array('ok' => false, 'status' => '', 'error' => $errno . ' - ' . $errstr);
|
||||
}
|
||||
|
||||
// HTTP/1.0 avoids chunked encoding so the body is exactly VERIFIED/INVALID
|
||||
$header = "POST " . $path . " HTTP/1.0\r\n";
|
||||
$header .= "Host: " . $host . "\r\n";
|
||||
$header .= "User-Agent: PHP-IPN-VerificationScript\r\n";
|
||||
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
|
||||
$header .= "Content-Length: " . strlen($req) . "\r\n";
|
||||
$header .= "Connection: close\r\n\r\n";
|
||||
fwrite($fp, $header . $req);
|
||||
$response = '';
|
||||
while (!feof($fp)) {
|
||||
$response .= fgets($fp, 1024);
|
||||
}
|
||||
fclose($fp);
|
||||
|
||||
$header_body = preg_split("/\r\n\r\n|\n\n/", $response, 2);
|
||||
$status = isset($header_body[1]) ? hotlunches_paypal_extract_status($header_body[1]) : '';
|
||||
if ($status === 'VERIFIED' || $status === 'INVALID') {
|
||||
return array('ok' => true, 'status' => $status, 'error' => '');
|
||||
}
|
||||
return array('ok' => false, 'status' => $status, 'error' => 'Unexpected socket response from PayPal');
|
||||
}
|
||||
|
||||
// Rebuild the original IPN payload. PayPal requires original field order/encoding.
|
||||
$raw_post_data = file_get_contents('php://input');
|
||||
if ($raw_post_data === false || $raw_post_data === '') {
|
||||
$req = 'cmd=_notify-validate';
|
||||
foreach ($_POST as $key => $value) {
|
||||
$req .= '&' . $key . '=' . urlencode(stripslashes($value));
|
||||
}
|
||||
} else {
|
||||
$raw_post_array = explode('&', $raw_post_data);
|
||||
$myPost = array();
|
||||
foreach ($raw_post_array as $keyval) {
|
||||
$keyval = explode('=', $keyval, 2);
|
||||
if (count($keyval) == 2) {
|
||||
if ($keyval[0] === 'payment_date' && substr_count($keyval[1], '+') === 1) {
|
||||
$keyval[1] = str_replace('+', '%2B', $keyval[1]);
|
||||
}
|
||||
$myPost[$keyval[0]] = urldecode($keyval[1]);
|
||||
}
|
||||
}
|
||||
$req = 'cmd=_notify-validate';
|
||||
foreach ($myPost as $key => $value) {
|
||||
$req .= '&' . $key . '=' . urlencode($value);
|
||||
}
|
||||
}
|
||||
|
||||
$use_sandbox = array_key_exists('test_ipn', $_POST);
|
||||
$paypal_urls = $use_sandbox
|
||||
? array(
|
||||
'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr',
|
||||
'https://www.sandbox.paypal.com/cgi-bin/webscr'
|
||||
)
|
||||
: array(
|
||||
'https://ipnpb.paypal.com/cgi-bin/webscr',
|
||||
'https://www.paypal.com/cgi-bin/webscr'
|
||||
);
|
||||
|
||||
$errors = array();
|
||||
$unexpectedErrors = 0;
|
||||
$paypal_status = '';
|
||||
$paypal_error = '';
|
||||
$verified = false;
|
||||
|
||||
foreach ($paypal_urls as $url_index => $paypal_url) {
|
||||
if ($verified || $paypal_status === 'INVALID') break;
|
||||
|
||||
// Primary endpoint is retried once. The backup endpoint is tried once.
|
||||
// Timeouts stay inside PayPal's IPN response window.
|
||||
$max_attempts = ($url_index === 0) ? 2 : 1;
|
||||
for ($attempt = 1; $attempt <= $max_attempts; $attempt++) {
|
||||
$result = hotlunches_paypal_postback_curl($paypal_url, $req);
|
||||
$paypal_status = $result['status'];
|
||||
$paypal_error = $result['error'];
|
||||
if ($result['ok'] && ($paypal_status === 'VERIFIED' || $paypal_status === 'INVALID')) {
|
||||
$verified = ($paypal_status === 'VERIFIED');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Socket fallback only when cURL never got a final VERIFIED/INVALID answer.
|
||||
if ($verified || $paypal_status === 'INVALID' || $url_index !== 0) continue;
|
||||
|
||||
$result = hotlunches_paypal_postback_socket($paypal_url, $req);
|
||||
$paypal_status = $result['status'];
|
||||
$paypal_error = $result['error'];
|
||||
if ($result['ok'] && $paypal_status === 'VERIFIED') {
|
||||
$verified = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Transport failure must be HTTP 500 so PayPal resends the IPN.
|
||||
// INVALID stays HTTP 200 so a rejected notice is not retried.
|
||||
if (!$verified && $paypal_status !== 'INVALID') {
|
||||
if (function_exists('http_response_code')) {
|
||||
http_response_code(500);
|
||||
} else {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
}
|
||||
|
||||
ob_start("saveOutput");
|
||||
|
||||
echo(date('r') . "\n\n");
|
||||
$x = $_POST;
|
||||
if (isset($x['callbackPW'])) unset($x['callbackPW']);
|
||||
print_r($x);
|
||||
echo("\n\n");
|
||||
|
||||
if ($verified) {
|
||||
echo("VERIFIED");
|
||||
} elseif ($paypal_status === 'INVALID') {
|
||||
echo("INVALID");
|
||||
$errors[] = "The PayPal server could not validate the IPN.";
|
||||
$unexpectedErrors = 1;
|
||||
} else {
|
||||
echo("ERROR\n" . $paypal_error);
|
||||
$errors[] = "Error connecting to PayPal server to validate IPN.";
|
||||
$unexpectedErrors = 1;
|
||||
}
|
||||
echo("\n\n");
|
||||
|
||||
if ($unexpectedErrors == 1) exit();
|
||||
|
||||
|
||||
extract($_POST,EXTR_PREFIX_ALL,'P');
|
||||
$school_num = isset($P_custom) ? intval($P_custom) : 0;
|
||||
|
||||
// Load user information from the database
|
||||
$query = "SELECT * FROM schools WHERE record_num = $school_num";
|
||||
$r = db_query("SELECT * FROM schools WHERE record_num = {$school_num}");
|
||||
if (db_num_rows($r) != 1) {
|
||||
echo("Invalid school number: $school_num.\n");
|
||||
echo("$query");
|
||||
exit();
|
||||
} else {
|
||||
extract(db_fetch_assoc($r), EXTR_PREFIX_ALL, 's');
|
||||
}
|
||||
|
||||
// Check that the payment was made in Canadian funds
|
||||
if (!$P_mc_currency || $P_mc_currency != 'CAD') {
|
||||
echo("Unexpected currency - payment should be made in $s_paypal_currency");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Get the invoice number(s)
|
||||
$r = db_query("SELECT * FROM invoice_header WHERE status = 'Open' AND school_num = $school_num");
|
||||
if (db_num_rows($r) == 0) {
|
||||
echo("Error: no open invoices found.\n");
|
||||
echo("$query");
|
||||
exit();
|
||||
}
|
||||
|
||||
$invoices = "";
|
||||
while ($row = db_fetch_array($r)) {
|
||||
if (strlen($invoices)) $invoices .= ",";
|
||||
$invoices .= $row['record_num'];
|
||||
}
|
||||
|
||||
// Get current outstanding balance
|
||||
$current_balance_owing = db_getVal("select sum(total - amount_paid) from invoice_header where school_num = $school_num and record_num in ($invoices)");
|
||||
|
||||
// Check the order total
|
||||
if (!isset($P_mc_gross) || $P_mc_gross != $current_balance_owing) {
|
||||
$x = number_format($current_balance_owing, 2);
|
||||
echo("Invalid payment amount - expected {$x}");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Check the payment was sent to the correct account
|
||||
if ($P_receiver_email != 'mark@hotlunches.net' && $P_receiver_email != 'paypal@hotlunches.net') {
|
||||
echo("Payment was sent to an unexpected account.\n");
|
||||
echo("$P_receiver_email => 'mark@hotlunches.net'\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
// check for non-completed orders (ie. pending for some reason)
|
||||
if ($P_payment_status != 'Completed') {
|
||||
echo("Payment was not completed.");
|
||||
exit();
|
||||
}
|
||||
|
||||
echo("Payment validated ok - updating database\n\n");
|
||||
|
||||
// Update invoice header
|
||||
$today = date("Y-m-d",time());
|
||||
$query = "UPDATE invoice_header " .
|
||||
"SET amount_paid = total, status = 'Paid', reference = '{$P_txn_id}', payment_date = '$today', payment_method = 'PayPal' " .
|
||||
"WHERE school_num = $school_num " .
|
||||
"AND record_num in ($invoices)";
|
||||
db_query($query);
|
||||
echo("{$query}\n");
|
||||
|
||||
// Update expiry date
|
||||
$renewal_date = db_getVal("SELECT max(renewal_date) FROM invoice_header WHERE school_num = $school_num");
|
||||
$edate = date("Y-m-d",strtotime($renewal_date) - 60 * 60 * 12);
|
||||
$query = "UPDATE schools SET expiry_date = '$edate' WHERE record_num = $school_num";
|
||||
db_query($query);
|
||||
echo("{$query}\n");
|
||||
|
||||
|
||||
echo("Payment processed ok\n\n\n");
|
||||
?>
|
||||
Reference in new issue
Block a user