Skip to content

Instantly share code, notes, and snippets.

@dmzoneill
Last active January 25, 2023 19:44
Show Gist options
  • Save dmzoneill/faaa84782eaed4d2dc28ad55c19119ee to your computer and use it in GitHub Desktop.
Save dmzoneill/faaa84782eaed4d2dc28ad55c19119ee to your computer and use it in GitHub Desktop.
PHP Paypal Instant Payment Notification
<?php
require 'site-config.php';
require 'PaypalIPN.php';
if (class_exists('Transaction') === false) {
class Transaction
{
private $error = array();
public function __construct()
{
global $paypal_logs_dir;
if(isset($_POST['payment_date'])) {
$this->paypal_instant_payment_notification();
}
else {
$this->normal_http_callback();
}
$req_dump = print_r($_REQUEST, true);
$ser_dump = print_r($_SERVER, true);
@file_put_contents($paypal_logs_dir . '/' . date("Y-m-d h:i:s") . '.log', $req_dump . "\n" . $ser_dump);
}
private function normal_http_callback()
{
global $contact_add_name, $transaction_confirmed_image;
$tx = isset($_GET['tx']) ? $_GET['tx'] : "";
$tx = filter_var($tx, FILTER_SANITIZE_STRING);
if (is_user_logged_in() === false) {
header('Location: ' . get_site_url() . '/wp-login.php?action=login');
return;
}
get_header();
print "<div id='primary' class='content-area container'><div class='container' style='text-align:center'><span style='display: inline-block; width:60%; vertical-align: middle;'>";
$this->error["1.1"] = "The transaction from paypal was malformed - missing amt";
$this->error["1.2"] = "The transaction from paypal was malformed - missing cc";
$this->error["1.3"] = "The transaction from paypal was malformed - missing item_name";
$this->error["1.4"] = "The transaction from paypal was malformed - missing item_number";
$this->error["1.5"] = "The transaction from paypal was malformed - missing st";
$this->error["1.6"] = "The transaction from paypal was malformed - missing tx";
$this->error["1.7"] = "The transaction from paypal was malformed - item number unknown";
$this->error["1.8"] = "The transaction from paypal was malformed - paypal sent uncompleted transaction";
$this->error["1.9"] = "This transaction has already been added to your account";
$this->error["1.10"] = "The transaction id has invalid length";
$this->error["2.1"] = "Did not receive okay for transation validation request";
$this->error["2.2"] = "Unable to structure the the payal response, possibly malformed";
$this->error["2.3"] = "Paypal did not return success for the transaction";
$this->error["3.1"] = "Failed to save purchased transaction, not adding classes";
$this->error["3.2"] = "Failed to insert bought tokens for the user";
$valres = $this->validate_transaction_request();
$current_user = wp_get_current_user();
if ($valres[0] === false) {
print "<h2>Transaction Error</h2>";
print "<p>There was an error with your transaction <br><b>" . $valres[1] . "</b><br><br>";
print "Please quote the <b>Paypal</b> transaction id <b>" . $tx . "</b> with <b>Paypal</b> or <b>$contact_add_name</b> appropriately</p>";
$nse = new Student_Emailer();
$nse->transaction_failed($current_user->user_login, $current_user->user_email, $tx, $valres[1], $valres[2]);
} else {
$valres = $this->validate_transation();
if ($valres[0] === false) {
print "<h2>Transaction Error</h2>";
print "<p>There was an error with your transaction <br><b>" . $valres[1] . "</b><br><br>";
print "Please quote the paypal transaction id <b>" . $tx . "</b> with paypal or $contact_add_name appropriately</p>";
$nse = new Student_Emailer();
$nse->transaction_failed($current_user->user_login, $current_user->user_email, $tx, $valres[1], $valres[2]);
} else {
$valres = $this->update_member($valres[1]);
if ($valres[0] === false) {
$nse = new Student_Emailer();
$nse->transaction_failed($current_user->user_login, $current_user->user_email, $tx, $valres[1], $valres[2]);
}
print "<h2>Transaction Confirmed</h2>";
print "<p>Thank you for completing your transaction.<br><br>";
print "Your credits have been added to your account.<br><br>";
print "To gift this to someone or to get a receipt, please visit your ";
print "<a style='text-decoration: underline' href='" . get_site_url() . "/profile/'>profile page.<br><br>";
print "<a style='text-decoration: underline' href='" . get_site_url() . "/classes/'>Take me to the schedule</a></p>";
$nse = new Student_Emailer();
$nse->transaction_confirmed($current_user->user_login, $current_user->user_email, $tx);
}
}
print "</span><span style='display: inline-block; width:30%; vertical-align: middle;'><br>";
print "<img style='width:100%' src='";
if (file_exists(get_template_directory() . rawurldecode($transaction_confirmed_image))) {
print get_template_directory_uri() . $transaction_confirmed_image;
} else {
print $transaction_confirmed_image;
}
print "'></span></div></div>";
get_footer();
}
private function paypal_instant_payment_notification()
{
$ipn = new PaypalIPN();
$ipn->useSandbox();
$verified = $ipn->verifyIPN();
if ($verified) {
/*
* Process IPN
* A list of variables is available here:
* https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNandPDTVariables/
*/
$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]);
}
}
// Reply with an empty 200 response to indicate to paypal the IPN was received correctly.
//header("HTTP/1.1 200 OK");
}
public function validate_transaction_request()
{
global $wpdb, $ashtanga_booking_tables;
$amt = isset($_GET['amt']) ? $_GET['amt'] : false; // 0.50
$amt = filter_var($amt, FILTER_VALIDATE_FLOAT);
$cc = isset($_GET['cc']) ? $_GET['cc'] : false; // EUR
$cc = filter_var($cc, FILTER_SANITIZE_STRING);
$item_name = isset($_GET['item_name']) ? $_GET['item_name'] : false; //TestBuy
$item_name = filter_var($item_name, FILTER_SANITIZE_STRING);
$item_number = isset($_GET['item_number']) ? $_GET['item_number'] : false; // testbuy1
$item_number = filter_var($item_number, FILTER_SANITIZE_STRING);
$st = isset($_GET['st']) ? $_GET['st'] : false; // Completed
$st = filter_var($st, FILTER_SANITIZE_STRING);
$tx = isset($_GET['tx']) ? $_GET['tx'] : false; // 8CY850347S384831W
$tx = filter_var($tx, FILTER_SANITIZE_STRING);
if ($amt === false) {
return array(false, $this->error["1.1"], "1.1");
}
if ($cc === false) {
return array(false, $this->error["1.2"], "1.2");
}
if ($item_name === false) {
return array(false, $this->error["1.3"], "1.3");
}
if ($item_number === false) {
return array(false, $this->error["1.4"], "1.4");
}
if ($st === false) {
return array(false, $this->error["1.5"], "1.5");
}
if ($tx === false) {
return array(false, $this->error["1.6"], "1.6");
}
if (strlen($tx) !== 17) {
return array(false, $this->error["1.10"], "1.10");
}
$query = "SELECT * FROM " . $ashtanga_booking_tables['paypal_payment_buttons'] . " ORDER BY id ASC";
$paypal_payment_buttons = $wpdb->get_results($query);
$pids = array();
foreach ($paypal_payment_buttons as $paypal_payment_button) {
$pids[] = $paypal_payment_button->paypal_button_id;
}
if (!in_array($item_number, $pids)) {
return array(false, $this->error["1.7"], "1.7");
}
if ($st !== "Completed") {
return array(false, $this->error["1.8"], "1.8");
}
$query = "SELECT count(*) FROM " . $ashtanga_booking_tables['student_purchase_history'] . " where txn_id = %s";
$prepared = $wpdb->prepare($query, $tx);
$stale_transaction = $wpdb->get_var($prepared);
if ($stale_transaction != "0") {
return array(false, $this->error["1.9"], "1.9");
}
return array(true, "");
}
public function validate_transation()
{
global $error, $paypal_client_id, $paypal_logs_dir;
$tx = isset($_GET['tx']) ? $_GET['tx'] : false; // 8CY850347S384831W
$txid = filter_var($tx, FILTER_SANITIZE_STRING);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.paypal.com/cgi-bin/webscr");
curl_setopt($ch, CURLOPT_POST, 1);
$queryfields = http_build_query(array(
'cmd' => '_notify-synch',
'tx' => $txid,
'at' => $paypal_client_id,
)
);
curl_setopt($ch, CURLOPT_POSTFIELDS, $queryfields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
@file_put_contents($paypal_logs_dir . "/$txid", $body);
if ($httpcode !== 200) {
return array(false, $this->error["2.1"], "2.1");
}
$lines = explode("\n", $body);
if (is_array($lines) === false) {
return array(false, $this->error["2.2"], "2.2");
}
if (trim($lines[0]) !== "SUCCESS") {
return array(false, $this->error["2.3"], "2.3");
}
return array(true, $lines);
}
public function update_member($lines)
{
global $wpdb, $ashtanga_booking_tables;
$current_wpuser = wp_get_current_user();
$opts = array();
foreach ($lines as $line) {
if (strpos($line, "=") !== false) {
$parts = explode("=", $line);
$opts[trim($parts[0])] = trim($parts[1]);
}
}
$query = "SELECT * from " . $ashtanga_booking_tables['paypal_payment_buttons'] . " order by id asc";
$paypal_payment_buttons = $wpdb->get_results($query);
$numclass = array();
$expiryclass = array();
$monthly = array();
$classrestriction = array();
foreach ($paypal_payment_buttons as $paypal_payment_button) {
$numclass[$paypal_payment_button->paypal_button_id] = intval($paypal_payment_button->credits);
$expiryclass[$paypal_payment_button->paypal_button_id] = date('Y-m-d', strtotime($paypal_payment_button->credit_expiry));
$monthly[$paypal_payment_button->paypal_button_id] = $paypal_payment_button->monthly;
$classrestriction[$paypal_payment_button->paypal_button_id] = $paypal_payment_button->class_type_restriction;
}
$student_user = $wpdb->get_row("SELECT * FROM " . $ashtanga_booking_tables['student'] . " WHERE wp_uid = '" . $current_wpuser->ID . "'");
$vals = array(
"student_id" => $student_user->id,
"purchase_date" => date("Y-m-d"),
"expiry_date" => $expiryclass[$opts['item_number']],
"purchase_type" => $opts['item_number'],
"purchase_value" => $opts['mc_gross'],
"purchase_amount" => $numclass[$opts['item_number']],
"payer_id" => $opts['payer_id'],
"first_name" => $opts['first_name'],
"txn_id" => $opts['txn_id'],
"last_name" => $opts['last_name'],
"payer_email" => $opts['payer_email'],
"receiver_id" => $opts['receiver_id'],
"residence_country" => $opts['residence_country'],
);
// log transaction
$res = $wpdb->insert($ashtanga_booking_tables['student_purchase_history'], $vals);
//print $wpdb->last_query;
$trans_id = $wpdb->insert_id;
if ($trans_id !== false) {
if ($monthly[$opts['item_number']] != "1") {
for ($i = 0; $i < $numclass[$opts['item_number']]; $i++) {
$exp = $expiryclass[$opts['item_number']];
$wpdb->insert($ashtanga_booking_tables['student_tokens'], array(
"student_id" => $student_user->id,
"wp_uid" => $current_wpuser->ID,
"purchase_id" => $trans_id,
"expiry_date" => $exp,
"class_type_restriction" => isset($classrestriction[$opts['item_number']]) ? $classrestriction[$opts['item_number']] : null,
)
);
if ($wpdb->insert_id === false) {
return array(false, $this->error["3.2"], "3.2");
}
}
}
} else {
return array(false, $this->error["3.1"], "3.1");
}
return array(true, "Success");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment