<?php
/**
* ipn_main_handler.php callback handler for paypal IPN payment method
*
* @package paymentMethod
* @copyright Copyright 2003-2007 Zen Cart Development Team
* @copyright Portions Copyright 2003 osCommerce
* @license http://www.zen-cart.com/license/2_0.txt GNU Public License V2.0
* @version $Id: ipn_main_handler.php 7497 2007-11-27 06:20:27Z drbyte $
*/
/**
* handle Express Checkout processing:
*/
if (isset($_GET['type']) && $_GET['type'] == 'ec') {
// this is an EC handler request
require('includes/application_top.php');
require(DIR_WS_CLASSES . 'payment.php');
// See if we were sent a request to clear the session for PayPal.
if (isset($_GET['clearSess']) || isset($_GET['ec_cancel'])) {
// Unset the PayPal EC information.
unset($_SESSION['paypal_ec_temp']);
unset($_SESSION['paypal_ec_token']);
unset($_SESSION['paypal_ec_payer_id']);
unset($_SESSION['paypal_ec_payer_info']);
}
// See if the paypalwpp module is enabled.
if (defined('MODULE_PAYMENT_PAYPALWPP_STATUS') && MODULE_PAYMENT_PAYPALWPP_STATUS == 'True') {
$paypalwpp_module = 'paypalwpp';
// init the payment object
$payment_modules = new payment($paypalwpp_module);
// set the payment, if they're hitting us here then we know
// the payment method selected right now.
$_SESSION['payment'] = $paypalwpp_module;
// check to see if we have a token sent back from PayPal.
if (!isset($_SESSION['paypal_ec_token']) || empty($_SESSION['paypal_ec_token'])) {
// We have not gone to PayPal's website yet in order to grab
// a token at this time. This will send the user over to PayPal's
// website to login and return a token
$$paypalwpp_module->ec_step1();
} else {
// This will push on the second step of the paypalwpp payment
// module, as we already have a PayPal express checkout token
// at this point.
$$paypalwpp_module->ec_step2();
}
}
?>
<html>
Processing...
</html>
<?php
/**
* If we got here, we are an IPN transaction:
*/
} else {
$isECtransaction = ($_POST['txn_type']=='express_checkout' /*|| $_POST['txn_type']=='cart'*/);
/**
* Include custom application_top.php
*/
$show_all_errors = false;
$current_page_base = 'paypalipn';
$loaderPrefix = 'paypal_ipn';
require('includes/application_top.php');
$extraDebug = (defined('IPN_EXTRA_DEBUG_DETAILS') && IPN_EXTRA_DEBUG_DETAILS == 'All');
if ( (defined('MODULE_PAYMENT_PAYPALWPP_DEBUGGING') && strstr(MODULE_PAYMENT_PAYPALWPP_DEBUGGING, 'Log')) ||
(defined('MODULE_PAYMENT_PAYPAL_IPN_DEBUG') && strstr(MODULE_PAYMENT_PAYPAL_IPN_DEBUG, 'Log')) ||
($_POST['ppdebug'] == 'on' && strstr(EXCLUDE_ADMIN_IP_FOR_MAINTENANCE, $_SERVER['REMOTE_ADDR'])) ) {
@ini_set('display_errors', E_ALL ^ E_NOTICE);
error_reporting(E_ALL ^ E_NOTICE);
$show_all_errors = true;
}
/**
* do confirmation post-back to PayPal and extract the results for subsequent use
*/
$ipnData = ipn_postback();
$postdata = $ipnData['postdata'];
$info = $ipnData['info'];
$new_status = 1;
ipn_debug_email('Breakpoint: 1 - Collected data from PayPal notification');
/**
* validate transaction -- email address, matching txn record, etc
*/
if (!ipn_validate_transaction($info, $_POST) === true) {
if (!$isECtransaction && $_POST['txn_type'] != '') {
ipn_debug_email('IPN FATAL ERROR :: Transaction did not validate');
die();
}
}
ipn_debug_email('Breakpoint: 2 - Validated transaction components');
/**
* is this a sandbox transaction?
*/
if (isset($_POST['test_ipn']) && $_POST['test_ipn'] == 1) {
ipn_debug_email('IPN NOTICE :: Processing SANDBOX transaction.');
}
if (isset($_POST['test_internal']) && $_POST['test_internal'] == 1) {
ipn_debug_email('IPN NOTICE :: Processing INTERNAL TESTING transaction.');
}
if (isset($_POST['pending_reason']) && $_POST['pending_reason'] == 'unilateral') {
ipn_debug_email('*** NOTE: TRANSACTION IS IN *unilateral* STATUS pending creation of a PayPal account for this receiver_email address.' . "\n" . 'Please create the account, or make sure the PayPal account is *Verified*.');
}
ipn_debug_email('Breakpoint: 3 - Communication method verified');
/**
* Lookup transaction history information in preparation for matching and relevant updates
*/
$lookupData = ipn_lookup_transaction($_POST);
$ordersID = $lookupData['order_id'];
$paypalipnID = $lookupData['paypal_ipn_id'];
$txn_type = $lookupData['txn_type'];
ipn_debug_email('Breakpoint: 4 - ' . 'Details: txn_type=' . $txn_type . ' ordersID = '. $ordersID . ' IPN_id=' . $paypalipnID . "\n\n" . ' Relevant data from POST:' . "\n " . 'txn_type = ' . $txn_type . "\n " . 'parent_txn_id = ' . ($_POST['parent_txn_id'] =='' ? 'None' : $_POST['parent_txn_id']) . "\n " . 'txn_id = ' . $_POST['txn_id']);
// this is used to determine whether a record needs insertion. ie: original echeck notice failed, but now we have cleared, so need parent record established:
$new_record_needed = ($txn_type == 'unique' ? true : false);
/**
* evaluate what type of transaction we're processing
*/
$txn_type = ipn_determine_txn_type($_POST, $txn_type);
ipn_debug_email('Breakpoint: 5 - Transaction type (txn_type) = ' . $txn_type);
/**
* take action based on transaction type and corresponding requirements
*/
switch ($txn_type) {
case ($_POST['txn_type'] == 'send_money'):
case ($_POST['txn_type'] == 'merch_payment'):
case ($_POST['txn_type'] == 'new_case'):
// these types are irrelevant to ZC transactions
ipn_debug_email('IPN NOTICE :: Transaction txn_type not relevant to Zen Cart processing. IPN handler aborted.');
die();
break;
case (substr($_POST['txn_type'],0,7) == 'subscr_'):
// For now we filter out subscription payments
ipn_debug_email('IPN NOTICE :: Subscription payment - Not currently supported by Zen Cart. IPN handler aborted.');
die();
break;
case 'pending-unilateral':
// cannot process this order because the merchant's PayPal account isn't valid yet
ipn_debug_email('IPN NOTICE :: Please create a valid PayPal account and follow the steps to *Verify* it. IPN handler aborted.');
die();
break;
case 'pending-address':
case 'pending-intl':
case 'pending-multicurrency':
case 'pending-verify':
if (!isECtransaction) {
ipn_debug_email('IPN NOTICE :: '.$txn_type.' transaction -- inserting initial record for reference purposes');
$sql_data_array = ipn_create_order_array($ordersID, $txn_type);
zen_db_perform(TABLE_PAYPAL, $sql_data_array);
$sql_data_array = ipn_create_order_history_array($paypalipnID);
zen_db_perform(TABLE_PAYPAL_PAYMENT_STATUS_HISTORY, $sql_data_array);
die();
break;
}
// case 'cart':
case 'express_checkout':
// This is an express-checkout transaction -- IPN serves no needed purpose
if ($_POST['payment_status'] == 'Completed') {
if (isset($_POST['auth_status']) && $_POST['auth_status'] == 'Completed') {
ipn_debug_email('IPN NOTICE :: Auth-Capture notice on completed order -- IPN Ignored');
} else {
ipn_debug_email('IPN NOTICE :: Express Checkout payment notice on completed order -- IPN Ignored');
}
die();
}
if ($_POST['payment_type'] == 'instant' && isset($_POST['auth_status']) && $_POST['auth_status'] == 'Pending') {
ipn_debug_email('IPN NOTICE :: Direct Payment notice on pre-auth order -- IPN Ignored');
die();
}
if (!(substr($txn_type,0,8) == 'pending-' && (int)$ordersID <= 0) && !($new_record_needed && $txn_type == 'echeck-cleared') && $txn_type != 'unique') break;
case ($txn_type == 'cart'):
ipn_debug_email('IPN NOTICE :: This is a detailed-cart transaction');
case ($txn_type == 'cart' && !isECtransaction):
ipn_debug_email('IPN NOTICE :: This is a detailed-cart transaction (i)');
case (substr($txn_type,0,8) == 'pending-' && (int)$ordersID <= 0):
case ($new_record_needed && $txn_type == 'echeck-cleared'):
case 'unique':
/**
* require shipping class
*/
require(DIR_WS_CLASSES . 'shipping.php');
/**
* require payment class
*/
require(DIR_WS_CLASSES . 'payment.php');
$payment_modules = new payment($_SESSION['payment']);
$shipping_modules = new shipping($_SESSION['shipping']);
/**
* require order class
*/
require(DIR_WS_CLASSES . 'order.php');
$order = new order();
/**
* require order_total class
*/
require(DIR_WS_CLASSES . 'order_total.php');
$order_total_modules = new order_total();
$order_totals = $order_total_modules->process();
if (valid_payment($order->info['total'], $_SESSION['currency']) === false) {
die();
}
if ($ipnFoundSession === false) {
ipn_debug_email('IPN NOTICE :: Unique but no session - Assumed to be a personal payment, rather than an IPN transaction. Ignoring.');
die();
}
$insert_id = $order->create($order_totals);
if ($extraDebug) ipn_debug_email('Breakpoint: 5a - built order -- OID:' . $insert_id);
$sql_data_array = ipn_create_order_array($insert_id, $txn_type);
if ($extraDebug) ipn_debug_email('Breakpoint: 5b - PP table OID:' . print_r($sql_data_array, true));
zen_db_perform(TABLE_PAYPAL, $sql_data_array);
if ($extraDebug) ipn_debug_email('Breakpoint: 5c - PP table OID saved');
$pp_hist_id = $db->Insert_ID();
if ($extraDebug) ipn_debug_email('Breakpoint: 5d - PP hist ID:' . $pp_hist_id);
$sql_data_array = ipn_create_order_history_array($pp_hist_id);
if ($extraDebug) ipn_debug_email('Breakpoint: 5e - PP hist_data:' . print_r($sql_data_array, true));
zen_db_perform(TABLE_PAYPAL_PAYMENT_STATUS_HISTORY, $sql_data_array);
if ($extraDebug) ipn_debug_email('Breakpoint: 5f - PP hist saved');
$new_status = MODULE_PAYMENT_PAYPAL_ORDER_STATUS_ID;
if ($extraDebug) ipn_debug_email('Breakpoint: 5g - new status code' . $new_status);
if ($_POST['payment_status'] =='Pending') {
$new_status = MODULE_PAYMENT_PAYPAL_PROCESSING_STATUS_ID;
if ($extraDebug) ipn_debug_email('Breakpoint: 5h - newer status code' . $new_status);
$sql = "UPDATE " . TABLE_ORDERS . "
SET orders_status = " . MODULE_PAYMENT_PAYPAL_PROCESSING_STATUS_ID . "
WHERE orders_id = '" . $insert_id . "'";
$db->Execute($sql);
if ($extraDebug) ipn_debug_email('Breakpoint: 5i - order table updated');
}
$sql_data_array = array('orders_id' => $insert_id,
'orders_status_id' => $new_status,
'date_added' => 'now()',
'comments' => 'PayPal status: ' . $_POST['payment_status'] . ' ' . $_POST['pending_reason']. ' @ '.$_POST['payment_date'] . (($_POST['parent_txn_id'] !='') ? "\n" . ' Parent Trans ID:' . $_POST['parent_txn_id'] : '') . "\n" . ' Trans ID:' . $_POST['txn_id'] . "\n" . ' Amount: ' . $_POST['mc_gross'] . ' ' . $_POST['mc_currency'],
'customer_notified' => false
);
if ($extraDebug) ipn_debug_email('Breakpoint: 5j - order stat hist update:' . print_r($sql_data_array, true));
zen_db_perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array);
if ($extraDebug) ipn_debug_email('Breakpoint: 5k - OSH update done');
$order->create_add_products($insert_id, 2);
if ($extraDebug) ipn_debug_email('Breakpoint: 5L - adding products');
$order->send_order_email($insert_id, 2);
if ($extraDebug) ipn_debug_email('Breakpoint: 5m - emailing customer');
$_SESSION['cart']->reset(true);
if ($extraDebug) ipn_debug_email('Breakpoint: 5n - emptying cart');
$ordersID = $insert_id;
$paypalipnID = $pp_hist_id;
ipn_debug_email('Breakpoint: 6 - Completed IPN order add.' . ' ordersID = '. $ordersID . ' IPN_id =' . $paypalipnID);
if (!($new_record_needed && $txn_type == 'echeck-cleared')) break;
case 'parent':
case 'cleared-address':
case 'cleared-multicurrency':
case 'cleared-echeck':
case 'cleared-authorization':
case 'cleared-verify':
case 'cleared-intl':
case 'echeck-denied':
case 'echeck-cleared':
case 'denied-address':
case 'denied-multicurrency':
case 'denied-echeck':
case 'failed-echeck':
case 'denied-intl':
case 'denied':
case 'voided':
case 'express-checkout-cleared':
if ($txn_type == 'parent') {
$sql_data_array = ipn_create_order_array($ordersID, $txn_type);
zen_db_perform(TABLE_PAYPAL, $sql_data_array);
} else {
$sql_data_array = ipn_create_order_update_array($txn_type);
zen_db_perform(TABLE_PAYPAL, $sql_data_array, 'update', "txn_id='" . $_POST['txn_id'] . "'");
}
$sql_data_array = ipn_create_order_history_array($paypalipnID);
zen_db_perform(TABLE_PAYPAL_PAYMENT_STATUS_HISTORY, $sql_data_array);
ipn_debug_email('IPN NOTICE :: Updating PP table record status for order #' . $ordersID . ' txn_id: ' . $_POST['txn_id'] . ' PP IPN ID: ' . $paypalipnID);
switch ($txn_type) {
case 'voided':
case ($_POST['payment_status'] == 'Refunded' || $_POST['payment_status'] == 'Reversed' || $_POST['payment_status'] == 'Voided'):
//payment_status=Refunded or payment_status=Voided
$new_status = MODULE_PAYMENT_PAYPALWPP_REFUNDED_STATUS_ID;
if (defined('MODULE_PAYMENT_PAYPAL_REFUND_ORDER_STATUS_ID') && (int)MODULE_PAYMENT_PAYPAL_REFUND_ORDER_STATUS_ID > 0 && !$isECtransaction) $new_status = MODULE_PAYMENT_PAYPAL_REFUND_ORDER_STATUS_ID;
break;
case 'echeck-denied':
case 'denied-echeck':
case 'failed-echeck':
//payment_status=Denied or failed
$new_status = ($isECtransaction ? MODULE_PAYMENT_PAYPALWPP_REFUNDED_STATUS_ID : MODULE_PAYMENT_PAYPAL_REFUND_ORDER_STATUS_ID);
break;
case 'echeck-cleared':
//echeck-cleared
$new_status = MODULE_PAYMENT_PAYPAL_ORDER_STATUS_ID;
break;
case ($txn_type=='express-checkout-cleared' || substr($txn_type,0,8) == 'cleared-'):
//express-checkout-cleared
$new_status = ($isECtransaction && defined('MODULE_PAYMENT_PAYPALWPP_ORDER_STATUS_ID') ? MODULE_PAYMENT_PAYPALWPP_ORDER_STATUS_ID : MODULE_PAYMENT_PAYPAL_ORDER_STATUS_ID);
if ((int)$new_status == 0) $new_status = 2;
break;
case 'pending-auth':
// pending authorization
$new_status = ($isECtransaction ? MODULE_PAYMENT_PAYPALWPP_REFUNDED_STATUS_ID : MODULE_PAYMENT_PAYPAL_REFUND_ORDER_STATUS_ID);
break;
case (substr($txn_type,0,7) == 'denied-'):
// denied for any other reason - treat as pending for now
case (substr($txn_type,0,8) == 'pending-'):
// pending anything
$new_status = ($isECtransaction ? MODULE_PAYMENT_PAYPALWPP_ORDER_PENDING_STATUS_ID : MODULE_PAYMENT_PAYPAL_PROCESSING_STATUS_ID);
break;
}
// update order status history with new information
ipn_debug_email('IPN NOTICE :: Set new status ' . $new_status . " for order ID = " . $ordersID . ($_POST['pending_reason'] != '' ? '. Reason_code = ' . $_POST['pending_reason'] : '') );
if ((int)$new_status == 0) $new_status = 1;
if (in_array($_POST['payment_status'], array('Refunded', 'Reversed', 'Denied', 'Failed'))
|| substr($txn_type,0,8) == 'cleared-' || $txn_type=='echeck-cleared' || $txn_type == 'express-checkout-cleared') {
ipn_update_orders_status_and_history($ordersID, $new_status, $txn_type);
}
break;
case ($txn_type == 'pending-echeck' && (int)$ordersID > 0):
ipn_debug_email('IPN NOTICE :: Pending echeck transaction for existing order. No action required. Waiting for echeck to clear.');
break;
case ($txn_type == 'pending-multicurrency' && (int)$ordersID > 0):
ipn_debug_email('IPN NOTICE :: Pending multicurrency transaction for existing order. No action required. Waiting for merchant to "accept" the order via PayPal account console.');
break;
case ($txn_type == 'pending-address' && (int)$ordersID > 0):
ipn_debug_email('IPN NOTICE :: "Pending address" transaction for existing order. No action required. Waiting for address approval by store owner via PayPal account console.');
break;
default:
// can't understand result found. Thus, logging and aborting.
ipn_debug_email('IPN WARNING :: Could not process for txn type: ' . $txn_type . "\n" . ' postdata=' . str_replace('&', " \n&", urldecode($postdata)));
}
}
?>