You are here

function uc_paypal_ipn in Ubercart 7.3

Same name and namespace in other branches
  1. 5 payment/uc_paypal/uc_paypal.module \uc_paypal_ipn()
  2. 6.2 payment/uc_paypal/uc_paypal.pages.inc \uc_paypal_ipn()

Processes Instant Payment Notifiations from PayPal.

1 string reference to 'uc_paypal_ipn'
uc_paypal_menu in payment/uc_paypal/uc_paypal.module
Implements hook_menu().

File

payment/uc_paypal/uc_paypal.pages.inc, line 11
Paypal administration menu items.

Code

function uc_paypal_ipn() {
  if (!isset($_POST['invoice'])) {
    watchdog('uc_paypal', 'IPN attempted with invalid order ID.', array(), WATCHDOG_ERROR);
    return;
  }
  if (strpos($_POST['invoice'], '-') > 0) {
    list($order_id, $cart_id) = explode('-', $_POST['invoice']);

    // Sanitize order ID and cart ID
    $order_id = intval($order_id);
    $cart_id = check_plain($cart_id);
    if (!empty($cart_id)) {

      // Needed later by uc_complete_sale to empty the correct cart
      $_SESSION['uc_cart_id'] = $cart_id;
    }
  }
  else {
    $order_id = intval($_POST['invoice']);
  }
  watchdog('uc_paypal', 'Receiving IPN at URL for order @order_id. <pre>@debug</pre>', array(
    '@order_id' => $order_id,
    '@debug' => variable_get('uc_paypal_wps_debug_ipn', FALSE) ? print_r($_POST, TRUE) : '',
  ));
  $order = uc_order_load($order_id);
  if ($order == FALSE) {
    watchdog('uc_paypal', 'IPN attempted for non-existent order @order_id.', array(
      '@order_id' => $order_id,
    ), WATCHDOG_ERROR);
    return;
  }

  // Assign posted variables to local variables
  $payment_status = check_plain($_POST['payment_status']);
  $payment_amount = check_plain($_POST['mc_gross']);
  $payment_currency = check_plain($_POST['mc_currency']);
  $receiver_email = check_plain($_POST['business']);
  if ($receiver_email == '') {
    $receiver_email = check_plain($_POST['receiver_email']);
  }
  $txn_id = check_plain($_POST['txn_id']);
  $txn_type = check_plain($_POST['txn_type']);
  $payer_email = check_plain($_POST['payer_email']);

  // Express Checkout IPNs may not have the WPS email stored. But if it is,
  // make sure that the right account is being paid.
  $uc_paypal_wps_email = trim(variable_get('uc_paypal_wps_email', ''));
  if (!empty($uc_paypal_wps_email) && drupal_strtolower($receiver_email) != drupal_strtolower($uc_paypal_wps_email)) {
    watchdog('uc_paypal', 'IPN for a different PayPal account attempted.', array(), WATCHDOG_ERROR);
    return;
  }
  foreach ($_POST as $key => $value) {
    $post_fields[] = $key . '=' . urlencode(stripslashes($value));
  }
  $post_fields[] = 'cmd=_notify-validate';
  if (variable_get('uc_paypal_wpp_server', '') == 'https://api-3t.paypal.com/nvp') {
    $host = 'https://ipnpb.paypal.com/cgi-bin/webscr';
  }
  else {
    $host = variable_get('uc_paypal_wps_server', 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr');
  }

  // Setup the cURL request.
  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $host);
  curl_setopt($ch, CURLOPT_VERBOSE, 0);
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_POSTFIELDS, implode('&', $post_fields));
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
  curl_setopt($ch, CURLOPT_NOPROGRESS, 1);
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
  $response = curl_exec($ch);

  // Log any errors to the watchdog.
  if ($error = curl_error($ch)) {
    watchdog('uc_paypal', 'IPN failed with cURL error: @error', array(
      '@error' => $error,
    ), WATCHDOG_ERROR);
    return;
  }
  curl_close($ch);
  if (strcmp($response, 'VERIFIED') == 0) {
    watchdog('uc_paypal', 'IPN transaction verified.');
    $duplicate = (bool) db_query_range('SELECT 1 FROM {uc_payment_paypal_ipn} WHERE txn_id = :id AND status <> :status', 0, 1, array(
      ':id' => $txn_id,
      ':status' => 'Pending',
    ))
      ->fetchField();
    if ($duplicate) {
      if ($order->payment_method != 'credit') {
        watchdog('uc_paypal', 'IPN transaction ID has been processed before.', array(), WATCHDOG_NOTICE);
      }
      return;
    }
    db_insert('uc_payment_paypal_ipn')
      ->fields(array(
      'order_id' => $order_id,
      'txn_id' => $txn_id,
      'txn_type' => $txn_type,
      'mc_gross' => $payment_amount,
      'status' => $payment_status,
      'receiver_email' => $receiver_email,
      'payer_email' => $payer_email,
      'received' => REQUEST_TIME,
    ))
      ->execute();
    switch ($payment_status) {
      case 'Canceled_Reversal':
        uc_order_comment_save($order_id, 0, t('PayPal has canceled the reversal and returned !amount !currency to your account.', array(
          '!amount' => uc_currency_format($payment_amount, FALSE),
          '!currency' => $payment_currency,
        )), 'admin');
        break;
      case 'Completed':
        if (abs($payment_amount - $order->order_total) > 0.01) {
          watchdog('uc_paypal', 'Payment @txn_id for order @order_id did not equal the order total.', array(
            '@txn_id' => $txn_id,
            '@order_id' => $order->order_id,
          ), WATCHDOG_WARNING, l(t('view'), 'admin/store/orders/' . $order->order_id));
        }
        $comment = t('PayPal transaction ID: @txn_id', array(
          '@txn_id' => $txn_id,
        ));
        uc_payment_enter($order_id, 'paypal_wps', $payment_amount, $order->uid, NULL, $comment);
        uc_cart_complete_sale($order);
        uc_order_comment_save($order_id, 0, t('PayPal IPN reported a payment of @amount @currency.', array(
          '@amount' => uc_currency_format($payment_amount, FALSE),
          '@currency' => $payment_currency,
        )));
        break;
      case 'Denied':
        uc_order_comment_save($order_id, 0, t("You have denied the customer's payment."), 'admin');
        break;
      case 'Expired':
        uc_order_comment_save($order_id, 0, t('The authorization has failed and cannot be captured.'), 'admin');
        break;
      case 'Failed':
        uc_order_comment_save($order_id, 0, t("The customer's attempted payment from a bank account failed."), 'admin');
        break;
      case 'Pending':
        uc_order_update_status($order_id, 'paypal_pending');
        uc_order_comment_save($order_id, 0, t('Payment is pending at PayPal: @reason', array(
          '@reason' => _uc_paypal_pending_message(check_plain($_POST['pending_reason'])),
        )), 'admin');
        break;

      // You, the merchant, refunded the payment.
      case 'Refunded':
        $comment = t('PayPal transaction ID: @txn_id', array(
          '@txn_id' => $txn_id,
        ));
        uc_payment_enter($order_id, 'paypal_wps', $payment_amount, $order->uid, NULL, $comment);
        break;
      case 'Reversed':
        watchdog('uc_paypal', 'PayPal has reversed a payment!', array(), WATCHDOG_ERROR);
        uc_order_comment_save($order_id, 0, t('Payment has been reversed by PayPal: @reason', array(
          '@reason' => _uc_paypal_reversal_message(check_plain($_POST['reason_code'])),
        )), 'admin');
        break;
      case 'Processed':
        uc_order_comment_save($order_id, 0, t('A payment has been accepted.'), 'admin');
        break;
      case 'Voided':
        uc_order_comment_save($order_id, 0, t('The authorization has been voided.'), 'admin');
        break;
    }
  }
  elseif (strcmp($response, 'INVALID') == 0) {
    watchdog('uc_paypal', 'IPN transaction failed verification.', array(), WATCHDOG_ERROR);
    uc_order_comment_save($order_id, 0, t('An IPN transaction failed verification for this order.'), 'admin');
  }
}