Commit fb785fd1 authored by Olli Erinko's avatar Olli Erinko
Browse files

Issue #1458386 by operinko: Extended payment status checking. Fixed Undefined index warning.

parent 69c48f27
Loading
Loading
Loading
Loading
+5 −4
Changes for uc_checkoutfi.info: 5 added lines, 4 removed lines.
Original line number Diff line number Diff line
name = "Checkout.fi"
description = "Enable to process payments using checkout.fi"
package = Ubercart - payment
core = 7.x
php = 5.0

dependencies[] = uc_payment
dependencies[] = uc_store
dependencies[] = uc_cart
dependencies[] = uc_product

package = Ubercart - payment
core = 7.x
php = 5.0
 No newline at end of file
dependencies[] = job_scheduler
 No newline at end of file
+166 −28
Changes for uc_checkoutfi.module: 166 added lines, 28 removed lines.
Original line number Diff line number Diff line
@@ -37,13 +37,99 @@ function uc_checkoutfi_cancel() {
  return $output;
}

/**
 * Declare job scheduling holding items that need to be run periodically.
 */
function uc_checkoutfi_cron_job_scheduler_info() {
  $schedulers = array();
  $schedulers['uc_checkoutfi_check_payments'] = array(
    'worker callback' => 'uc_checkoutfi_check_payment',
  );
  return $schedulers;
}

/**
 * Job scheduler worker function to check status for each uncomplete order.
 */
function uc_checkoutfi_check_payment($job) {
  // Load the order tied to the job and get our password.
  $order = uc_order_load($job['id']);
  $data = $order->data;
  $private_key = variable_get('uc_checkoutfi_passphrase', 'SAIPPUAKAUPPIAS');
  // Prepare a request.
  $request = array(
    'VERSION' => '0001',
    'STAMP' => $data['STAMP'],
    'REFERENCE' => $data['REFERENCE'],
    'MERCHANT' => variable_get('uc_checkoutfi_merchant_id', 375917),
    'AMOUNT' => ($order->order_total * 100),
    'CURRENCY' => 'EUR',
    'FORMAT' => 1,
    'ALGORITHM' => 1,
  );

  // Generate a hash checksum for all the fields, with our password as "salt".
  $request['MAC'] = drupal_strtoupper(md5(
    $request['VERSION'] . '+' .
    $request['STAMP'] . '+' .
    $request['REFERENCE'] . '+' .
    $request['MERCHANT'] . '+' .
    $request['AMOUNT'] . '+' .
    $request['CURRENCY'] . '+' .
    $request['FORMAT'] . '+' .
    $request['ALGORITHM'] . '+' .
    $private_key));

  // Let's create a post-data string out of the prepared values.
  foreach ($request as $key => $name) {
    $data_string .= $key . "=" . $name . "&";
  }

  // Update job scheduling.
  // Check status once an hour from this on. CHANGE TO 3600 WHEN DONE TESTING!
  $job['period'] = 5;
  JobScheduler::get('uc_checkoutfi_check_payments')->set($job);

  // Build the http request and execute.
  $host = 'https://rpcapi.checkout.fi/poll';
  $http_options = array(
    'method' => 'POST',
    'data' => $data_string,
    'headers' => array('Content-Type' => 'application/x-www-form-urlencoded'),
  );
  $response = drupal_http_request($host, $http_options);

  // Check if we got an error or not.
  if ($response->data != 'error') {
    // Let's convert the response we got into an xml object.
    $response_xml = new SimpleXMLElement(drupal_convert_to_utf8($response->data, 'ISO-8859-1'));
    $status = uc_checkoutfi_check_status($response_xml->status);
    if($status['state'] == 'success'
       OR $status['state'] == 'failure') {
      // Remove the job as unnecessary.
      JobScheduler::get('uc_checkoutfi_check_payments')->remove($job);
    }
    // Save transaction and order if status changed.
    if ($order->order_status != $status['stat']) {
      $stat = $status['stat'];
      uc_order_update_status($order->order_id, $stat);
      uc_order_comment_save($order->order_id, 0, $status['msg'], 'admin');
      if ($status['state'] == 'success') {
        uc_payment_enter($order->order_id, 'uc_checkoutfi', $order->order_total, 0, NULL, $status['msg']);
      }
      // Log status change.
      watchdog('uc_checkoutfi', 'Order status updated in Checkout.fi. Old status: <pre>@existing_transaction</pre> Updated status: <pre>@transaction</pre>', array('@existing_transaction' => print_r($old_status, 1), '@transaction' => print_r($status, 1)), WATCHDOG_INFO);
    }
  }
}

/**
 * Implementation of hook_ucga_display().
 */
function uc_checkoutfi_ucga_display() {
  // Tell UC Google Analytics to display the e-commerce JS on the custom
  // order completion page for this module.
  if (arg(0) == 'cart' && arg(1) == 'checkoutfi' && arg(2) == 'complete') {
  if (arg(0) == 'cart' && arg(1) == 'uc_checkoutfi' && arg(2) == 'complete') {
    return TRUE;
  }
}
@@ -54,7 +140,7 @@ function uc_checkoutfi_ucga_display() {
function uc_checkoutfi_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'uc_cart_checkout_review_form' && ($order_id = intval($_SESSION['cart_order'])) > 0 && empty($form_state['post'])) {
    $order = uc_order_load($order_id);
    if ($order->payment_method == 'checkoutfi') {
    if ($order->payment_method == 'uc_checkoutfi') {
      unset($form['submit']);
      $form['#prefix'] = '<table style="display: inline; padding-top: 1em;"><tr><td>';
      $form['#suffix'] = '</td><td>'. drupal_render(drupal_get_form('uc_checkoutfi_form', $order)) .'</td></tr></table>';
@@ -70,7 +156,7 @@ function uc_checkoutfi_uc_payment_method() {
  $title = variable_get('uc_checkoutfi_method_title', t('Online payment (Finnish banks)'));

  $methods[] = array(
    'id' => 'checkoutfi',
    'id' => 'uc_checkoutfi',
    'name' => t('Checkout.fi'),
    'title' => $title,
    'desc' => t('Redirect to checkout.fi to pay by online.'),
@@ -117,7 +203,7 @@ function uc_checkoutfi_payment_method($op, &$arg1) {
  }
}

// Form to build the submission to checkout.fi
// Form to build the submission to checkout.fi.
function uc_checkoutfi_form() {
  $order = uc_order_load($_SESSION['cart_order']);

@@ -199,30 +285,65 @@ function uc_checkoutfi_complete() {
  if($_GET['STAMP'] = check_plain($_GET['STAMP'])) {
    $order_id = substr($_GET['STAMP'], strpos($_GET['STAMP'], 'A')+1);
  }
  watchdog('checkoutfi', 'Receiving new order notification for order @order_id.', array('@order_id' => $order_id));
  watchdog('uc_checkoutfi', 'Receiving new order notification for order @order_id.', array('@order_id' => $order_id));
  $payment = uc_checkoutfi_complete_check_vars($_GET);
  $order = uc_order_load($order_id);
  $order->data = $payment;

  if ($order === FALSE || uc_order_status_data($order->order_status, 'state') != 'in_checkout') {
    drupal_set_message(t('An error has occurred during payment. Please contact us to ensure your order has submitted.'), 'error');
    drupal_goto('cart/');
    exit();
  }
  $payment_status = array('message' => $payment['msg'], 'status' => $payment['stat']);
  
  // Save changes to order without it's completion (it will be on finalization step)
  uc_order_save($order);

  if ($payment_status['status'] == 'success' OR $payment_status['status'] == 'pending') {
    $comment = t('Paid by Checkout.fi, order #@order. Checkout status: @status', array('@order' => $order_id, '@status' => $payment_status['message']));
    uc_payment_enter($order->order_id, 'checkoutfi', $order->order_total, 0, NULL, $comment);
  if ($payment['state'] == 'success') {
    $comment = t('Paid by Checkout.fi, order #@order. Checkout status: @status', array('@order' => $order_id, '@status' => $payment['msg']));
    uc_order_comment_save($order->order_id, 0, $comment, 'admin');
    uc_payment_enter($order->order_id, 'uc_checkoutfi', $order->order_total, 0, NULL, $comment);
  }
  elseif ($payment['state'] == 'pending') {
    $comment = t('Payment pending at Checkout.fi, order #@order. Checkout status: @status', array('@order' => $order_id, '@status' => $payment['msg']));

    uc_order_comment_save($order->order_id, 0, $comment, 'admin');

    // Don't try to handle delayed payment checking if job_scheduler is missing.
    if(module_exists('job_scheduler')) {
      // Create a scheduled job for each order.
      // It'll be used to check the payment status from Checkout.fi
      // Use period => 5 to check the status as soon as next cron is run.
      $job = array(
        'id' => $order->order_id,
        'type' => 'order',
        'created' => time(),
        'period' => 5,
        'periodic' => TRUE,
        'data' => $order,
      );

      $schedule = JobScheduler::get('uc_checkoutfi_check_payments');
      // Check that the job doesn't exist already.
      if (!$schedule->check($job)) {
        watchdog('uc_checkoutfi', 'New JobScheduler created');
        $schedule->set($job);
      }
      else {
        watchdog('uc_checkoutfi', "Job already exists, don't create another");
      }
    }
    else {
      // If it's missing, someone needs to know we want it.
      drupal_set_message(t('You need to install job_scheduler module to process pending payments'), 'error');
      uc_order_comment_save($order->order_id, 0, t('Payment is pending approval at Checkout.fi.'), 'admin');
    }
  }
  else {
    drupal_set_message(t('Your order will be processed soon.'));
    uc_order_comment_save($order_id, 0, t('Payment is pending approval at checkout.fi.'), 'admin');
    uc_order_comment_save($order_id, 0, t('Payment is pending approval at Checkout.fi.'), 'admin');
  }

  drupal_goto('cart/checkoutfi/finalize/' . $order->order_id . '/' . $payment_status['status'] . '/' . $payment_status['message']);
  drupal_goto('cart/checkoutfi/finalize/' . $order->order_id . '/' . $payment['state'] . '/' . $payment['msg']);
}

function uc_checkoutfi_complete_check_vars($get) {
@@ -238,7 +359,7 @@ function uc_checkoutfi_complete_check_vars($get) {

  // Validate payment status before proceeding
  $status = uc_checkoutfi_check_status($return_values['STATUS']);
  if ($status['stat'] != 'success' AND $status['stat'] != 'pending') {
  if ($status['state'] != 'success' AND $status['state'] != 'pending') {
    drupal_set_message($status['msg'],'error');
    exit();
  }
@@ -257,101 +378,118 @@ function uc_checkoutfi_check_status($status) {
  switch($status) {
    case -10:
      // Payment refunded to client.
      $st = 'failure';
      $state = 'failure';
      $st = 'canceled';
      $msg = t('Payment refunded to client');
      break;

    case -4:
      // Payment not found.
      $st = 'failure';
      $state = 'failure';
      $st = 'abandoned';
      $msg = t('Payment not found');
      break;

    case -3:
      // Payment timed out.
      $st = 'failure';
      $state = 'failure';
      $st = 'abandoned';
      $msg = t('Payment timed out');
      break;

    case -2:
      // Cancelled by system.
      $st = 'failure';
      $state = 'failure';
      $st = 'canceled';
      $msg = t('Cancelled by system');
      break;

    case -1:
      // Cancelled by client.
      $st = 'failure';
      $state = 'failure';
      $st = 'canceled';
      $msg = t('Cancelled by client');
      break;

    case 1:
      // Payment not finished.
      $state = 'pending';
      $st = 'pending';
      $msg = t('Payment not finished');
      break;

    case 2:
      // Payment accepted.
      $st = 'success';
      $state = 'success';
      $st = 'payment_received';
      $msg = t('Payment accepted');
      break;

    case 3:
      // Payment pending.
      $state = 'pending';
      $st = 'pending';
      $msg = t('Payment pending');
      break;

    case 4:
      // Reserved for future, pending.
      $state = 'pending';
      $st = 'pending';
      $msg = t('Payment pending');
      break;

    case 5:
      // Reserved for future, success.
      $st = 'success';
      $state = 'success';
      $st = 'payment_received';
      $msg = t('Payment accepted');
      break;

    case 6:
      // Payment frozen, accept it.
      $st = 'success';
      $state = 'success';
      $st = 'payment_received';
      $msg = t('Payment frozen, accepted');
      break;

    case 7:
      // Third party accepted, waiting for client confirmation.
      $st = 'success';
      $state = 'pending';
      $st = 'processing';
      $msg = t('Third party accepted, waiting for client confirmation');
      break;

    case 8:
      // Third party accepted and client has been charged.
      $st = 'success';
      $state = 'success';
      $st = 'payment_received';
      $msg = t('Payment accepted');
      break;

    case 9:
      // Reserved for future, accepted.
      $st = 'success';
      $state = 'success';
      $st = 'payment_received';
      $msg = t('Payment accepted');
      break;

    case 10:
      // Order paid to merchant.
      $st = 'success';
      $state = 'success';
      $st = 'payment_received';
      $msg = t('Order paid to merchant.');
      break;

    default:
      // Catch-all failure
      $st = 'failure';
      $state = 'failure';
      $st = 'canceled';
      $msg = t('Unknown status');
      break;
  }
  return array(
    'state' => $state,
    'stat' => $st,
    'msg' => $msg,
  );
@@ -366,11 +504,11 @@ function uc_checkoutfi_finalize($order_id = NULL, $status, $message) {
  }
  if ($status == 'pending') {
    $comment = $message;
    $status = 'payment_pending';
    $status = 'pending';
  }
  if ($status == 'failure') {
    $comment = $message;
    $status = 'payment_failed';
    $status = 'canceled';
  }
  uc_order_comment_save($order->order_id, 0, $comment, 'admin');
  if ($status) {