<?php

/**
 * @file
 * Use Google Checkout to collect payment and process orders.
 */

/******************************************************************************
 * Drupal hooks                                                               *
 ******************************************************************************/

/**
 * Implements hook_help().
 */
function uc_google_checkout_help($page, $args) {
  switch ($page) {
    case 'admin/store/settings/payment/method/google_checkout':
      if (variable_get('uc_google_checkout_mode', 'checkout') == 'checkout') {
        $checkout_url = 'http://checkout.google.com';
      }
      else {
        $checkout_url = 'http://sandbox.google.com/checkout';
      }
      return '<p>' . t('In the <a href="!checkout_url/sell">Google Checkout Merchant Center</a>, enter %url as the callback URL for this site. Also be sure that "Callback contents" is set to "Notification Serial Number" and that the API Version is 2.5.', array('!checkout_url' => $checkout_url, '%url' => url('google_checkout', array('absolute' => TRUE)))) . '</p>';

    case 'admin/store/orders/%/google_checkout':
      return '<p>' . t('Use this terminal to process credit card payments.') . '</p>';
  }
}

/**
 * Implements hook_menu().
 */
function uc_google_checkout_menu() {
  $items = array();

  $items['admin/store/settings/payment/method/google_checkout'] = array(
    'title' => 'Google Checkout settings',
    'description' => 'Set merchant ID and key for Google Checkout.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('uc_google_checkout_settings'),
    'access arguments' => array('administer store'),
    'file' => 'uc_google_checkout.admin.inc',
  );
  $items['admin/store/settings/payment/method/google_checkout/account'] = array(
    'title' => 'Account',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/store/settings/payment/method/google_checkout/shipping'] = array(
    'title' => 'Shipping',
    'description' => 'Calculate shipping charges for orders through Google Checkout.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('uc_google_checkout_shipping_settings'),
    'access arguments' => array('administer store'),
    'type' => MENU_LOCAL_TASK,
    'file' => 'uc_google_checkout.admin.inc',
  );
  $items['admin/store/settings/payment/method/google_checkout/taxes'] = array(
    'title' => 'Taxes',
    'description' => 'Calculate taxes for orders through Google Checkout.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('uc_google_checkout_taxes_settings'),
    'access arguments' => array('administer store'),
    'type' => MENU_LOCAL_TASK,
    'file' => 'uc_google_checkout.admin.inc',
  );
  $items['google_checkout'] = array(
    'page callback' => 'uc_google_checkout_callback',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'file' => 'uc_google_checkout.pages.inc',
  );
  $items['google_checkout/calculations'] = array(
    'page callback' => 'uc_google_checkout_merchant_calculation',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
    'file' => 'uc_google_checkout.pages.inc',
  );
  $items['admin/store/orders/%uc_order/google_checkout'] = array(
    'title callback' => 'uc_google_checkout_terminal_title',
    'title arguments' => array(3),
    'description' => 'Process a credit card payment or refund through Google Checkout.',
    'page callback' => 'uc_google_checkout_terminal',
    'page arguments' => array(3),
    'access arguments' => array('manual payments'),
    'file' => 'uc_google_checkout.admin.inc',
  );

  return $items;
}

/**
 * Menu title callback function.
 */
function uc_google_checkout_terminal_title($order) {
  return t('Google Checkout terminal: Order @order_id', array('@order_id' => $order->order_id));
}

/**
 * Implements hook_init().
 */
function uc_google_checkout_init() {
  // When an order comes in from Google Checkout, we need to unset some
  // $_SESSION data. Since this isn't done under the customer's user, we
  // do it when they next load a page.
  global $user;
  $users = variable_get('uc_google_checkout_order_users', array());
  if (isset($users[$user->uid])) {
    unset($users[$user->uid]);
    if (isset($_SESSION['cart_order'])) {
      unset($_SESSION['uc_checkout'][$_SESSION['cart_order']]['do_complete']);
      unset($_SESSION['cart_order']);
    }
    variable_set('uc_google_checkout_order_users', $users);
  }

  global $conf;
  $conf['i18n_variables'][] = 'uc_google_checkout_order_cancel_reason';

  $id = variable_get('googleanalytics_account', '');
  if (module_exists('googleanalytics') && !empty($id) && _googleanalytics_visibility_pages() && _googleanalytics_visibility_user($user)) {
    $scope = variable_get('googleanalytics_js_scope', 'footer');
    drupal_add_js('document.write(unescape("%3Cscript src=\"//checkout.google.com/files/digital/ga_post.js\" type=\"text/javascript\"%3E%3C/script%3E"));', 'inline', $scope);
  }
}

/**
 * Implements hook_theme().
 */
function uc_google_checkout_theme() {
  return array(
    'uc_google_checkout_shipping_settings' => array(
      'render element' => 'form',
      'file' => 'uc_google_checkout.admin.inc',
    ),
    'uc_google_checkout_taxes_settings' => array(
      'render element' => 'form',
      'file' => 'uc_google_checkout.admin.inc',
    ),
  );
}

/**
 * Implements hook_form_alter().
 */
function uc_google_checkout_form_alter(&$form, &$form_state, $form_id) {
  if (uc_product_is_product_form($form)) {
    $node = $form['#node'];
    if (is_object($node) && $form_id == $node->type . '_node_form' && uc_product_is_product($node->type)) {
      $policy_url = 'https://checkout.google.com/support/sell/bin/answer.py?answer=75724';
      $form['base']['gc_salable'] = array(
        '#type' => 'checkbox',
        '#title' => t('Product conforms to Google Checkout content policies.'),
        '#default_value' => isset($node->gc_salable) ? $node->gc_salable : TRUE,
        '#description' => t('To be a Google Checkout approved merchant, your items must conform to the <a href="@url">Google Checkout content policies</a>.', array('@url' => $policy_url)),
        '#weight' => 40,
      );
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter() for uc_payment_by_order_form().
 */
function uc_google_checkout_form_uc_payment_by_order_form_alter(&$form, &$form_state) {
  unset($form['payments']['new']['method']['#options']['google_checkout']);
}

/**
 * Implements hook_form_FORM_ID_alter() for uc_order_view_update_form().
 */
function uc_google_checkout_form_uc_order_view_update_form_alter(&$form, &$form_state) {
  $form['#submit'][] = 'uc_google_checkout_notify_update';
}

/**
 * Implements hook_form_FORM_ID_alter() for uc_store_format_settings_form().
 */
function uc_google_checkout_form_uc_store_format_settings_form_alter(&$form, &$form_state) {
  $form['currency']['uc_currency_code']['#description'] .= ' ' . t('Google Checkout only accepts the following currencies: @list', array('@list' => implode(', ', _uc_google_checkout_currency_codes())));
}

/**
 * Implements hook_node_insert().
 */
function uc_google_checkout_node_insert($node) {
  uc_google_checkout_node_update($node);
}

/**
 * Implements hook_node_update().
 */
function uc_google_checkout_node_update($node) {
  if (uc_product_is_product($node->type)) {
    if (isset($node->gc_salable)) {
      if (empty($node->revision)) {
        db_delete('uc_gc_products')
          ->condition('vid', $node->vid)
          ->execute();
      }

      db_insert('uc_gc_products')
        ->fields(array(
          'vid' => $node->vid,
          'nid' => $node->nid,
          'gc_salable' => $node->gc_salable,
        ))
        ->execute();
    }
  }
}

/**
 * Implements hook_node_load().
 */
function uc_google_checkout_node_load($nodes, $types) {
  $vids = array();
  $product_types = uc_product_types();

  foreach ($nodes as &$node) {
    if (in_array($node->type, $product_types)) {
      $vids[$node->nid] = $node->vid;

      // Set default value.
      $node->gc_salable = TRUE;
    }
  }

  if ($vids) {
    $result = db_query("SELECT nid, gc_salable FROM {uc_gc_products} WHERE vid IN (:vids)", array(':vids' => $vids));
    foreach ($result as $salable) {
      $nodes[$node->nid]->gc_salable = $salable->gc_salable;
    }
  }
}

/**
 * Implements hook_node_delete().
 */
function uc_google_checkout_node_delete($node) {
  db_delete('uc_gc_products')
    ->condition('nid', $node->nid)
    ->execute();
}

/**
 * Implements hook_node_revision_delete().
 */
function uc_google_checkout_node_revision_delete($node) {
  db_delete('uc_gc_products')
    ->condition('vid', $node->vid)
    ->execute();
}

/******************************************************************************
 * Hook Functions (Ubercart)                                                  *
 ******************************************************************************/

/**
 * Implements hook_uc_order_pane().
 */
function uc_google_checkout_uc_order_pane() {
  $panes['email_allowed'] = array(
    'callback' => 'uc_google_checkout_pane_email_allowed',
    'title' => t('Marketing preferences'),
    'desc' => t("Display the customer's preferences about mass-marketing."),
    'class' => 'pos-left',
    'weight' => 4,
    'show' => array('view', 'customer'),
  );

  return $panes;
}

/**
 * Implements hook_uc_line_item().
 */
function uc_google_checkout_uc_line_item() {
  $items['gc_coupon'] = array(
    'title' => t('Google Checkout Coupon'),
    'stored' => TRUE,
    'calculated' => TRUE,
    'weight' => 2,
  );
  $items['gc_gift_certificate'] = array(
    'title' => t('Google Checkout Gift Certificate'),
    'stored' => TRUE,
    'calculated' => TRUE,
    'weight' => 2,
  );

  return $items;
}

/**
 * Implements hook_uc_order().
 */
function uc_google_checkout_uc_order($op, &$order, $arg2) {
  switch ($op) {
    case 'load':
      $result = db_query("SELECT * FROM {uc_gc_orders} WHERE order_id = :id", array(':id' => $order->order_id));
      if ($gc = $result->fetchObject()) {
        $order->google_order_number = $gc->gc_order_number;
        $order->financial_state = $gc->financial_state;
        $order->fulfillment_state = $gc->fulfillment_state;
        $order->gc_total = $gc->gc_total;
      }
    break;
    case 'can_update':
      if (!isset($order->google_order_number) || isset($_SESSION['google_updates']) && $_SESSION['google_updates']) {
        return TRUE;
      }
      switch ($arg2) {
        case 'canceled':
          if (uc_google_checkout_cancel_order($order)) {
            drupal_set_message(t('Cancel order request sent to Google Checkout. The order will be updated momentarily.'));
          }
          else {
            drupal_set_message(t('Order is not canceled in Google Checkout.'));
          }
        return FALSE;
      }
    break;
  }
}

/**
 * Implements hook_uc_payment_method().
 */
function uc_google_checkout_uc_payment_method() {
  $methods['google_checkout'] = array(
    'name' => t('Google Checkout'),
    'title' => t('Google Checkout'),
    'desc' => t('Express payment with Google Checkout.'),
    'callback' => 'uc_payment_method_google_checkout',
    'weight' => 1,
    'checkout' => FALSE,
    'backend' => FALSE,
    'express' => 'uc_google_checkout_cart_form',
  );
  return $methods;
}

/**
 * Implements hook_uc_shipment().
 */
function uc_google_checkout_uc_shipment($op, $shipment) {
  switch ($op) {
    case 'save':
      $google_order_number = uc_google_checkout_get_google_number($shipment->order_id);
      if ($google_order_number && $shipment->is_new) {
        $xml_data = '';
        foreach ($shipment->packages as $package) {
          if ($package->tracking_number) {
            $tracking_number = $package->tracking_number;
          }
          elseif ($shipment->tracking_number) {
            $tracking_number = $shipment->tracking_number;
          }
          if ($tracking_number) {
            foreach ($package->products as $product) {
              $xml_data .= '<item-shipping-information>';
              $xml_data .= '<item-id>';
              $xml_data .= '<merchant-item-id>' . check_plain($product->nid . '|' . $product->model) . '</merchant-item-id>';
              $xml_data .= '</item-id>';
              $xml_data .= '<tracking-data-list>';
              $xml_data .= '<tracking-data>';
              $xml_data .= '<carrier>' . check_plain($shipment->carrier) . '</carrier>';
              $xml_data .= '<tracking-number>' . check_plain($tracking_number) . '</tracking-number>';
              $xml_data .= '</tracking-data>';
              $xml_data .= '</tracking-data-list>';
              $xml_data .= '</item-shipping-information>';
            }
          }
        }
        if ($xml_data) {
          $request = '<?xml version="1.0" encoding="UTF-8"?>';
          $request .= "\n";
          $request .= '<ship-items xmlns="http://checkout.google.com/schema/2" google-order-number="' . $google_order_number . '">';
          $request .= '<item-shipping-information-list>';
          $request .= $xml_data;
          $request .= '</item-shipping-information-list>';
          $request .= '<send-email>true</send-email>';
          $request .= '</ship-items>';
          $response = uc_google_checkout_send_request('request', $request);
        }
      }
    break;
    case 'delete':
      $google_order_number = uc_google_checkout_get_google_number($shipment->order_id);
      if ($google_order_number) {
        foreach ($shipment->packages as $package) {
          foreach ($package->products as $product) {
            $reset_ids[] = check_plain($product->nid . '|' . $product->model);
          }
        }
        $request = '<?xml version="1.0" encoding="UTF-8"?>';
        $request .= "\n";
        $request .= '<reset-items-shipping-information xmlns="http://checkout.google.com/schema/2" google-order-number="' . $google_order_number . '">';
        $request .= '<item-ids>';
        foreach (array_unique($reset_ids) as $item_id) {
          $request .= '<item-id>';
          $request .= '<merchant-item-id>' . $item_id . '</merchant-item-id>';
          $request .= '</item-id>';
        }
        $request .= '</item-ids>';
        $request .= '<send-email>false</send-email>';
        $request .= '</reset-items-shipping-information>';

        $response = uc_google_checkout_send_request('request', $request);
      }
    break;
  }
}

/******************************************************************************
 * Callback Functions, Forms, and Tables                                      *
 ******************************************************************************/

/**
 * Form builder for uc_google_checkout_cart_form().
 *
 * @see uc_google_checkout_cart_form_submit()
 * @ingroup forms
 */
function uc_google_checkout_cart_form($form, &$form_state) {
  if (variable_get('uc_google_checkout_mode', 'checkout') == 'checkout') {
    $merchant_id = variable_get('uc_google_checkout_merchant_id', '');
    $checkout_url = 'https://checkout.google.com';
  }
  else {
    $merchant_id = variable_get('uc_google_checkout_test_merchant_id', '');
    $checkout_url = 'https://sandbox.google.com/checkout';
  }
  if (!$merchant_id) {
    watchdog('google', 'Google Checkout is enabled, but no Merchant ID found.', array(), WATCHDOG_ERROR, 'admin/store/settings/google_checkout');
    return array();
  }

  global $user;
  $id = variable_get('googleanalytics_account', '');
  if (module_exists('googleanalytics') && !empty($id) && _googleanalytics_visibility_pages() && _googleanalytics_visibility_user($user)) {
    $form['#attributes'] = array('onsubmit' => 'setUrchinInputCode(pageTracker);');
  }

  $disable = FALSE;
  foreach (uc_cart_get_contents() as $item) {
    $product = node_load($item->nid);
    if ($product && !$product->gc_salable) {
      $disable = TRUE;
      break;
    }
  }

  switch (variable_get('uc_google_checkout_button_size', 'large')) {
    case 'large':
      $width = 180;
      $height = 46;
    break;
    case 'medium':
      $width = 168;
      $height = 44;
    break;
    case 'small':
      $width = 160;
      $height = 43;
    break;
  }

  $form['uc_google_checkout'] = array(
    '#type' => 'image_button',
    '#button_type' => 'checkout',
    '#src' => $checkout_url . '/buttons/checkout.gif?merchant_id=' . $merchant_id . '&w=' . $width . '&h=' . $height . '&style=' . variable_get('uc_google_checkout_button_color', 'trans') . '&variant=' . ($disable ? 'disabled' : 'text') . '&loc=en_US',
    '#disabled' => $disable,
    '#attributes' => array(
      'alt' => t('Google Checkout'),
      'title' => t('Fast checkout through Google.'),
      'height' => $height,
      'width' => $width,
    ),
    '#description' => '<a href="javascript:void(window.open(\' http://checkout.google.com/seller/what_is_google_checkout.html\',\'whatischeckout\',\'scrollbars=0,resizable=1,directories=0,height=250,width=400\'));" OnMouseOver="return window.status = \'' . addslashes(t('What is Google Checkout?')) . '\';" OnMouseOut="return window.status = \'\';">' . addslashes(t('What is Google Checkout?')) . '</a>',
    '#submit' => array('uc_cart_view_form_submit', 'uc_google_checkout_cart_form_submit'),
  );

  $form['analyticsdata'] = array(
    '#type' => 'hidden',
    '#default_value' => '',
  );

  return $form;
}

/**
 * Form submission handler for uc_google_checkout_cart_form().
 *
 * @see uc_google_checkout_cart_form()
 */
function uc_google_checkout_cart_form_submit($form, &$form_state) {
  global $user;
  $items = uc_cart_get_contents();

  if (!is_array($items) || count($items) == 0) {
    drupal_set_message(t('You do not have any items in your shopping cart.'));
    return;
  }

  if (empty($_SESSION['cart_order'])) {
    $order = uc_order_new($user->uid);
    $_SESSION['cart_order'] = $order->order_id;
  }
  else {
    $order = new stdClass();
    $order->uid = $user->uid;
    $order->order_id = $_SESSION['cart_order'];
    $order->primary_email = $user->mail;
    $order->order_status = uc_order_state_default('in_checkout');
  }

  $order->products = $items;
  uc_order_save($order);
  uc_order_update_status($order->order_id, 'in_google_checkout');
  $order->order_status = 'in_google_checkout';

  $order->analytics_data = $form_state['values']['analyticsdata'];
  $request = uc_google_checkout_cart_request($order);
  if ($response = uc_google_checkout_send_request('merchantCheckout', $request)) {
    $redirect = (string) $response->{'redirect-url'};
    drupal_goto($redirect);
  }
}

/**
 * Builds the XML request to send shopping cart information to Google Checkout.
 *
 * @param $order
 * The mostly empty order object containing the contents of the shopping cart.
 *
 * @return string
 * The XML request.
 */
function uc_google_checkout_cart_request($order) {
  if (variable_get('uc_google_checkout_mode', 'checkout') == 'checkout') {
    $merchant_id = variable_get('uc_google_checkout_merchant_id', '');
  }
  else {
    $merchant_id = variable_get('uc_google_checkout_test_merchant_id', '');
  }

  $output = '<?xml version="1.0" encoding="UTF-8"?>';
  $output .= "\n";

  if (count($order->products)) {
    $output .= '<checkout-shopping-cart xmlns="http://checkout.google.com/schema/2">';
    $output .= '<shopping-cart>';
    $output .= '<items>';
    foreach ($order->products as $product) {
      $output .= '<item>';
      $output .= '<item-name>' . check_plain($product->model) . '</item-name>';
      $output .= '<item-description>' . check_plain($product->title) . '</item-description>';
      $output .= '<unit-price currency="' . variable_get('uc_currency_code', 'USD') . '">' . $product->price . '</unit-price>';
      $output .= '<item-weight unit="LB" value="' . $product->weight * uc_weight_conversion($product->weight_units, 'lb') . '" />';
      if (!uc_order_product_is_shippable($product)) {
        $output .= '<digital-content>';
          $output .= '<display-disposition>PESSIMISTIC</display-disposition>';
          $output .= '<email-delivery>true</email-delivery>';
        $output .= '</digital-content>';
      }
      $output .= '<quantity>' . $product->qty . '</quantity>';
      $output .= '<merchant-item-id>' . $product->nid . '|' . $product->model . '</merchant-item-id>';
      $output .= '</item>';
    }
    $output .= '</items>';
    $output .= '<merchant-private-data>';
    $output .= '<cart-id>' . uc_cart_get_id() . '</cart-id>';
    $output .= '<order-id>' . $order->order_id . '</order-id>';
    $output .= '</merchant-private-data>';
    $output .= '</shopping-cart>';
    $output .= '<checkout-flow-support>';
    $output .= '<merchant-checkout-flow-support>';

    if (uc_order_is_shippable($order)) {
      $output .= '<shipping-methods>';

      $order->delivery_city = variable_get('uc_google_checkout_delivery_city', '');
      $order->delivery_zone = variable_get('uc_google_checkout_delivery_zone', 0);
      $order->delivery_country = variable_get('uc_google_checkout_delivery_country', 0);
      $order->delivery_postal_code = variable_get('uc_google_checkout_delivery_postal_code', '');

      $methods = module_invoke_all('uc_shipping_method');
      module_load_include('inc', 'uc_quote', 'uc_quote.pages');
      $quote_data = uc_quote_assemble_quotes($order);
      foreach ($quote_data as $method_id => $options) {
        foreach ($options as $accsrl => $data) {
          if (isset($data['rate'])) {
            $output .= '<merchant-calculated-shipping name="' . check_plain($methods[$method_id]['quote']['accessorials'][$accsrl]) . '">';
            $output .= '<price currency="' . variable_get('uc_currency_code', 'USD') . '">' . $data['rate'] . '</price>';
            $output .= '</merchant-calculated-shipping>';
          }
        }
      }

      $output .= '</shipping-methods>';
      $output .= '<merchant-calculations>';
      $output .= '<merchant-calculations-url>' . url('google_checkout/calculations', array('absolute' => TRUE)) . '</merchant-calculations-url>';
      $output .= '</merchant-calculations>';
    }

    $output .= '<analytics-data>' . $order->analytics_data . '</analytics-data>';

    $tax_table = '';
    $result = db_query("SELECT zone, rate, tax_shipping FROM {uc_gc_taxes}");
    foreach ($result as $tax) {
      $tax_table .= '<default-tax-rule>';
      if ($tax->tax_shipping) {
        $tax_table .= '<shipping-taxed>true</shipping-taxed>';
      }
      $tax_table .= '<rate>' . (float)$tax->rate . '</rate>';

      $tax_table .= '<tax-area>';
      $tax_table .= '<us-state-area>';
      $tax_table .= '<state>' . $tax->zone . '</state>';
      $tax_table .= '</us-state-area>';
      $tax_table .= '</tax-area>';

      $tax_table .= '</default-tax-rule>';
    }

    if ($tax_table) {
      $output .= '<tax-tables>';
      $output .= '<default-tax-table>';
      $output .= '<tax-rules>';
      $output .= $tax_table;
      $output .= '</tax-rules>';
      $output .= '</default-tax-table>';
      $output .= '</tax-tables>';
    }

    $output .= '<edit-cart-url>' . url('cart', array('absolute' => TRUE)) . '</edit-cart-url>';
    if (($page = variable_get('uc_continue_shopping_url', '')) != '<none>') {
      $output .= '<continue-shopping-url>' . url($page, array('absolute' => TRUE)) . '</continue-shopping-url>';
    }
    $output .= '<platform-id>218752253180456</platform-id>';
    $output .= '</merchant-checkout-flow-support>';
    $output .= '</checkout-flow-support>';
    $output .= '</checkout-shopping-cart>';
  }

  return $output;
}

/**
 * Helper function to build HTTP headers for requests to Google Checkout.
 *
 * @return array
 *
 */
function uc_google_checkout_headers() {
  if (variable_get('uc_google_checkout_mode', 'checkout') == 'checkout') {
    $merchant_id = variable_get('uc_google_checkout_merchant_id', '');
    $merchant_key = variable_get('uc_google_checkout_merchant_key', '');
  }
  else {
    $merchant_id = variable_get('uc_google_checkout_test_merchant_id', '');
    $merchant_key = variable_get('uc_google_checkout_test_merchant_key', '');
  }
  $headers = array();
  $authorization = $merchant_id . ':' . $merchant_key;
  if ($authorization != ':') {
    $headers['Authorization'] = 'Basic ' . base64_encode($authorization);
  }
  $headers['Content-Type'] = 'application/xml; charset=UTF-8';
  $headers['Accept'] = 'application/xml; charset=UTF-8';
  return $headers;
}

/**
 * Sends the built request to Google Checkout over HTTP.
 *
 * @param string $api
 * The particular API the request should be processed by.
 * @param string $request
 * XML request string.
 *
 * @return SimpleXMLElement
 * The XML response object.
 */
function uc_google_checkout_send_request($api, $request) {
  if (variable_get('uc_google_checkout_mode', 'checkout') == 'checkout') {
    $merchant_id = variable_get('uc_google_checkout_merchant_id', '');
    $checkout_url = 'https://checkout.google.com';
  }
  else {
    $merchant_id = variable_get('uc_google_checkout_test_merchant_id', '');
    $checkout_url = 'https://sandbox.google.com/checkout';
  }

  if (!$merchant_id) {
    return;
  }

  // Google's XML parser doesn't like named entities apparently.
  str_replace(array('&amp;', '&lt;', '&gt;'), array('&#x26;', '&#x3c;', '&#x3e;'), $request);
  $response_obj = drupal_http_request($checkout_url . '/api/checkout/v2/' . $api . '/Merchant/' . $merchant_id, array(
    'headers' => uc_google_checkout_headers(),
    'method' => 'POST',
    'data' => $request,
  ));
  if (isset($response_obj->error)) {
    watchdog('google', '@error', array('@error' => $response_obj->error), WATCHDOG_ERROR);
  }
  $response = new SimpleXMLElement($response_obj->data);
  if ($response->getName() == 'error') {
    $error = (string) $response->{'error-message'};
    drupal_set_message($error, 'error');
    watchdog('google', '@error', array('@error' => $error), WATCHDOG_ERROR);
    return NULL;
  }

  /**
   * Ugly hack to work around PHP bug, details here:
   *   http://bugs.php.net/bug.php?id=23220
   * We strip out errors that look something like:
   *  warning: fread() [function.fread]: SSL fatal protocol error in...
   * Copied from http://drupal.org/node/70915.
   */
  $messages = drupal_set_message();
  $errors = isset($messages['error']) ? $messages['error'] : array();
  foreach ($errors as $i => $error) {
    if (strpos($error, 'SSL: fatal protocol error in')) {
      unset($_SESSION['messages']['error'][$i]);
    }
  }
  if (empty($_SESSION['messages']['error'])) {
    unset($_SESSION['messages']['error']);
  }

  db_delete('watchdog')
    ->condition('type', 'php')
    ->condition('variables', '%SSL: fatal protocol error%', 'LIKE')
    ->execute();
  // End of ugly hack.

  return $response;
}

/**
 * Indicates whether the customer accepts marketing emails.
 *
 * Order pane callback.
 */
function uc_google_checkout_pane_email_allowed($op, $order) {
  switch ($op) {
    case 'customer':
    case 'view':
      if ($order->payment_method == 'google_checkout') {
        if (isset($order->data['email_allowed']) && $order->data['email_allowed']) {
          $build = array('#markup' => t('Customer will accept marketing emails.'));
        }
        else {
          $build = array('#markup' => t('Customer does not want marketing emails.'));
        }

        return $build;
      }
  }
}

/**
 * Adds setting callbacks to the payment settings section.
 */
function uc_payment_method_google_checkout($op, &$order) {
  switch ($op) {
    case 'order-view':
      $build['#markup'] = l(t('Google Checkout terminal'), 'admin/store/orders/' . $order->order_id . '/google_checkout');

      return $build;
    case 'settings':
      module_load_include('inc', 'uc_google_checkout', 'uc_google_checkout.admin');
      $form_state = form_state_defaults();
      return uc_google_checkout_settings(array(), $form_state);;
  }
}

/**
 * Saves an order to the database from a new order notification.
 *
 * @param SimpleXMLElement $new_order
 * "new-order" notification from Google Checkout.
 *
 * @return boolean
 * TRUE if an order was successfully updated from the XML response.
 * FALSE if an order could not be found from the given order ID.
 */
function uc_google_checkout_new_order($new_order) {
  $order_id = $new_order->{'shopping-cart'}->{'merchant-private-data'}->{'order-id'};
  $cart_id = (string) $new_order->{'shopping-cart'}->{'merchant-private-data'}->{'cart-id'};

  $order = uc_order_load($order_id);
  if ($order) {
    $shipping_address = $new_order->{'buyer-shipping-address'};
    $order->delivery_company = (string) $shipping_address->{'company-name'};
    $order->delivery_first_name = (string) $shipping_address->{'structured-name'}->{'first-name'};
    $order->delivery_last_name = (string) $shipping_address->{'structured-name'}->{'last-name'};
    $order->delivery_phone = (string) $shipping_address->phone;
    $order->delivery_street1 = (string) $shipping_address->address1;
    $order->delivery_street2 = (string) $shipping_address->address2;
    $order->delivery_city = (string) $shipping_address->city;
    $zone_id = db_query("SELECT zone_id FROM {uc_zones} WHERE zone_code = :code", array(':code' => $shipping_address->region))->fetchField();
    $order->delivery_zone = $zone_id;
    $countries = uc_get_country_data(array('country_iso_code_2' => $shipping_address->{'country-code'}));
    $order->delivery_country = $countries[0]['country_id'];
    $order->delivery_postal_code = (string) $shipping_address->{'postal-code'};

    $billing_address = $new_order->{'buyer-billing-address'};
    $order->billing_company = (string) $billing_address->{'company-name'};
    $order->billing_first_name = (string) $billing_address->{'structured-name'}->{'first-name'};
    $order->billing_last_name = (string) $billing_address->{'structured-name'}->{'last-name'};
    $order->billing_phone = (string) $billing_address->phone;
    $order->billing_street1 = (string) $billing_address->address1;
    $order->billing_street2 = (string) $billing_address->address2;
    $order->billing_city = (string) $billing_address->city;
    if ($billing_address['region'] != $shipping_address->region) {
      $zone_id = db_query("SELECT zone_id FROM {uc_zones} WHERE zone_code = :code", array(':code' => $billing_address->region))->fetchField();
    }
    $order->billing_zone = $zone_id;
    if ($billing_address->{'country-code'} != $shipping_address->{'country-code'}) {
      $countries = uc_get_country_data(array('country_iso_code_2' => $billing_address->{'country-code'}));
    }
    $order->billing_country = $countries[0]['country_id'];
    $order->billing_postal_code = (string) $billing_address->{'postal-code'};

    if (!$order->primary_email) {
      $order->primary_email = (string) $billing_address->email;
    }

    if ($new_order->{'buyer-marketing-preferences'}->{'email-allowed'} == 'true') {
      $order->data['email_allowed'] = TRUE;
      if (module_exists('simplenews')) {
        simplenews_subscribe_user($order->primary_email, variable_get('uc_google_checkout_simplenews_tid', 0), TRUE);
      }
    }
    else {
      $order->data['email_allowed'] = FALSE;
      if (module_exists('simplenews')) {
        simplenews_unsubscribe_user($order->primary_email, variable_get('uc_google_checkout_simplenews_tid', 0), FALSE);
      }
    }

    $order->payment_method = 'google_checkout';

    $comments = array();

    // Ubercart should already be set up to calculate taxes itself.
    //uc_order_line_item_add($order_id, 'tax', t('Total tax'), $new_order->{'order-adjustment'}->{'total-tax'});
    if (isset($new_order->{'order-adjustment'}->shipping)) {
      $shipping_line = $new_order->{'order-adjustment'}->shipping[0];
      if ($shipping_line->{'shipping-cost'}) {
        $shipping = array(
          'name' => check_plain($shipping_line->{'shipping-name'}),
          'cost' => $shipping_line->{'shipping-cost'},
        );
      }
      elseif ($shipping_line->{'carrier-calculated-shipping-adjustment'}->{'shipping-cost'}) {
        $shipping = array(
          'name' => check_plain($shipping_line->{'carrier-calculated-shipping-adjustment'}->{'shipping-name'}),
          'cost' => $shipping_line->{'carrier-calculated-shipping-adjustment'}->{'shipping-cost'},
        );
      }
      elseif ($shipping_line->{'merchant-calculated-shipping-adjustment'}->{'shipping-cost'}) {
        $shipping = array(
          'name' => check_plain($shipping_line->{'merchant-calculated-shipping-adjustment'}->{'shipping-name'}),
          'cost' => $shipping_line->{'merchant-calculated-shipping-adjustment'}->{'shipping-cost'},
        );
      }
      uc_order_line_item_add($order_id, 'shipping', $shipping['name'], $shipping['cost']);
    }
    if (isset($new_order->{'order-adjustment'}->{'merchant-codes'})) {
      foreach ($new_order->{'order-adjustment'}->{'merchant-codes'}->children() as $adjustment) {
        $type = $adjustment->getName();
        if ($type == 'coupon-adjustment') {
          uc_order_line_item_add($order_id, 'gc_coupon', check_plain((string) $adjustment->code), -((string) $adjustment->{'applied-amount'}));
          $comments[] = check_plain((string) $adjustment->message);
        }
        elseif ($type == 'gift-certificate-adjustment') {
          uc_order_line_item_add($order_id, 'gc_gift_certificate', check_plain((string) $adjustment->code), -((string) $adjustment->{'applied-amount'}));
          $comments[] = check_plain((string) $adjustment->message);
        }
      }
    }

    uc_order_save($order);

    uc_cart_complete_sale($order);
    // uc_cart_complete_sale() empties the current cart (Google Checkout
    // API's cart) so we must empty the customer's manually.
    uc_cart_empty($cart_id);
    // Add a comment to let sales team know this came in through the site.
    if (variable_get('uc_google_checkout_mode', 'checkout') == 'checkout') {
      $checkout_url = 'https://checkout.google.com';
    }
    else {
      $checkout_url = 'https://sandbox.google.com/checkout';
    }
    uc_order_comment_save($order->order_id, 0, t('Order created through Google Checkout (#@gco_order).', array('@gco_order' => l($new_order->{'google-order-number'}, $checkout_url . '/sell/multiOrder', array('query' => array('order' => (string) $new_order->{'google-order-number'}), 'external' => TRUE)))), 'admin');

    // uc_cart_complete_sale() also unsets some $_SESSION variables. These
    // are tied to the user-id, so we record that for when they log in later.
    $users = variable_get('uc_google_checkout_order_users', array());
    $users[$order->uid] = $order->uid;
    variable_set('uc_google_checkout_order_users', $users);

    // Add messages from order adjustments (coupons, gift certificates, etc.).
    foreach ($comments as $comment) {
      $comment = trim($comment);
      if ($comment) {
        uc_order_comment_save($order->order_id, 0, $comment, 'order', $order->order_status, 0);
      }
    }

    db_insert('uc_gc_orders')
      ->fields(array(
        'order_id' => $order_id,
        'gc_order_number' => $new_order->{'google-order-number'},
        'gc_total' => $new_order->{'order-total'},
      ))
      ->execute();

    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Saves AVS and CVN response to the order comments.
 *
 * @param SimpleXMLElement $risk
 * XML response holding risk information associated with fulfilling the order.
 * @return boolean
 * TRUE if an order corresponding to the Google order number was found and
 * updated.
 * FALSE otherwise.
 */
function uc_google_checkout_accept_risk($risk) {
  $order_id = uc_google_checkout_get_order($risk->{'google-order-number'});
  if ($order_id) {
    $risk_info = $risk->{'risk-information'};
    $assessment = t('Risk information notification:') . '<br />';
    $avs_response = $risk_info->{'avs-response'};
    switch ($avs_response) {
      case 'Y':
        $assessment .= t('- Full AVS match (address and postal code)');
      break;
      case 'P':
        $assessment .= t('- Partial AVS match (postal code only)');
      break;
      case 'A':
        $assessment .= t('- Partial AVS match (address only)');
      break;
      case 'N':
        $assessment .= t('- No AVS match');
      break;
      case 'U':
        $assessment .= t('- AVS not supported by issuer');
      break;
      default:
        $assessment .= t('<b>Error:</b> No AVS response.');
      break;
    }
    $assessment .= '<br />';

    $cvn_response = $risk_info->{'cvn-response'};
    switch ($cvn_response) {
      case 'M':
        $assessment .= t('- CVN match');
      break;
      case 'N':
        $assessment .= t('- No CVN match');
      break;
      case 'U':
        $assessment .= t('- CVN not available');
      break;
      case 'E':
        $assessment .= t('- CVN error');
      break;
      default:
        $assessment .= t('<b>Error:</b> No CVN response.');
      break;
    }
    $assessment .= '<br />';
    $assessment .= t('Partial CC number: %s', array('%s' => $risk_info->{'partial-cc-number'}));
    $assessment .= '<br />';
    $assessment .= format_plural($risk_info->{'buyer-account-age'}, 'Google Checkout member for 1 day.', 'Google Checkout member for @count days.');
    $assessment .= '<br />';
    $assessment .= t('Eligible for protection: <strong>@bool</strong>', array('@bool' => drupal_strtoupper($risk_info->{'eligible-for-protection'})));
    uc_order_comment_save($order_id, 0, $assessment, 'admin', 'chargeable');

    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Processes order status changes originating from Google Checkout.
 *
 * @param SimpleXMLElement $change
 * XML containing the status change information.
 * @return boolean
 * TRUE if an order corresponding to the Google order number was found and
 * updated.
 * FALSE otherwise.
 */
function uc_google_checkout_order_state_change($change) {
  $order_id = uc_google_checkout_get_order($change->{'google-order-number'});
  if ($order_id) {
    $new_financial = (string) $change->{'new-financial-order-state'};
    $new_fulfillment = (string) $change->{'new-fulfillment-order-state'};
    $prev_financial = (string) $change->{'previous-financial-order-state'};
    $prev_fulfillment = (string) $change->{'previous-fulfillment-order-state'};

    db_update('uc_gc_orders')
      ->fields(array(
        'financial_state' => $new_financial,
        'fulfillment_state' => $new_fulfillment,
      ))
      ->condition('order_id', $order_id)
      ->condition('financial_state', $prev_financial)
      ->condition('fulfillment_state', $prev_fulfillment)
      ->execute();

    if ($new_financial != $prev_financial) {
      $_SESSION['google_updates'] = TRUE;
      switch ($new_financial) {
        case 'CHARGEABLE':
          uc_order_update_status($order_id, 'chargeable');
        break;
        case 'CHARGING':
          watchdog('google', 'Charging @order_id', array('@order_id' => $order_id));
        break;
        case 'CHARGED':
          watchdog('google', 'Charged @order_id', array('@order_id' => $order_id));
        break;
        case 'PAYMENT_DECLINED':
          watchdog('google', 'Payment declined @order_id', array('@order_id' => $order_id));
        break;
        case 'CANCELLED_BY_GOOGLE':
          $message = t('Order %order canceled by Google: %reason', array('%order' => $order_id, '%reason' => $change->reason));
          uc_order_comment_save($order_id, 0, $message, 'admin', 'canceled');
        case 'CANCELLED':
          uc_order_comment_save($order_id, 0, t('Order canceled.'), 'order', 'canceled');
          uc_order_update_status($order_id, 'canceled');
        break;
        default:

        break;
      }
      unset($_SESSION['google_updates']);
    }
    elseif ($new_fulfillment != $prev_fulfillment) {
      $_SESSION['google_updates'] = TRUE;
      switch ($new_fulfillment) {
        case 'PROCESSING':
          watchdog('google', 'Processing @order_id', array('@order_id' => $order_id));
        break;
        case 'DELIVERED':
          uc_order_update_status($order_id, 'completed');
          watchdog('google', 'Delivered @order_id', array('@order_id' => $order_id));
        break;
        case 'WILL_NOT_DELIVER':
          watchdog('google', 'Will not deliver @order_id', array('@order_id' => $order_id));
        break;
      }
      unset($_SESSION['google_updates']);
    }

    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Responds to payment authorization notification.
 *
 * @param SimpleXMLElement $auth
 * XML notification of payment authorization.
 *
 * @return boolean
 * TRUE if an order corresponding to the Google order number was found and
 * updated.
 * FALSE otherwise.
 */
function uc_google_checkout_authorize_amount($auth) {
  global $user;

  $order_id = uc_google_checkout_get_order($auth->{'google-order-number'});
  if ($order_id) {
    $_SESSION['google_updates'] = TRUE;

    uc_order_update_status($order_id, 'chargeable');

    unset($_SESSION['google_updates']);

    $o_comment = t('<b>Authorization:</b> @amount', array('@amount' => uc_currency_format((string) $auth->{'authorization-amount'})));
    uc_order_comment_save($order_id, $user->uid, $o_comment);

    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Sends charge request to Google Checkout.
 *
 * @param int $order_id
 * The order ID.
 * @param double $amount
 * The total payment amount to be charged.
 *
 * @return string
 * Redirect path for form submission. Points to the order-view page.
 */
function uc_google_checkout_charge($order_id, $amount) {
  $google_order_number = uc_google_checkout_get_google_number($order_id);
  $output = '';

  $output .= '<?xml version="1.0" encoding="UTF-8"?>';
  $output .= "\n";
  $output .= '<charge-and-ship-order xmlns="http://checkout.google.com/schema/2" google-order-number="' . $google_order_number . '">';
  $output .= '<amount currency="' . variable_get('uc_currency_code', 'USD') . '">' . $amount . '</amount>';
  $output .= '</charge-and-ship-order>';

  if ($response = uc_google_checkout_send_request('request', $output)) {
    if (!uc_google_checkout_charge_order($response)) {
      drupal_set_message(t('Charge request sent to Google Checkout. The charge confirmation should appear on this page momentarily.'));
    }
  }
  return 'admin/store/orders/' . $order_id;
}

/**
 * Responds to the charge-order notification.
 *
 * Enters a payment to the order for the amount given.
 *
 * @param type $charge
 * @return boolean
 * TRUE if an order corresponding to the Google order number was found and
 * updated.
 * FALSE otherwise.
 */
function uc_google_checkout_charge_order($charge) {
  $order_id = uc_google_checkout_get_order($charge->{'google-order-number'});
  if ($order_id) {
    $amount = (string) $charge->{'latest-charge-amount'};
    uc_payment_enter($order_id, 'google_checkout', $amount, 0,
                     '', t('Payment received by Google Checkout'));
    uc_order_comment_save($order_id, 0, t('Payment of %amount received by Google Checkout.', array('%amount' => uc_currency_format($amount))), 'admin', 'chargeable');

    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Sends a refund request to Google Checkout.
 *
 * @param int $order_id
 * The order ID.
 * @param double $amount
 * The amount to refund in the site's currency.
 * @param string $reason
 * The reason for the refund.
 * @param string $comment
 * Additional comments displayed to the customer.
 *
 * @return string
 * Redirect path for form submission. Points to the order-view page.
 */
function uc_google_checkout_refund($order_id, $amount, $reason, $comment = '') {
  $google_order_number = uc_google_checkout_get_google_number($order_id);
  $output = '';

  $output .= '<?xml version="1.0" encoding="UTF-8"?>';
  $output .= "\n";
  $output .= '<refund-order xmlns="http://checkout.google.com/schema/2" google-order-number="' . $google_order_number . '">';
  $output .= '<amount currency="' . variable_get('uc_currency_code', 'USD') . '">' . $amount . '</amount>';
  $output .= '<comment>' . check_plain($comment) . '</comment>';
  $output .= '<reason>' . check_plain($reason) . '</reason>';
  $output .= '</refund-order>';

  if ($response = uc_google_checkout_send_request('request', $output)) {
    if (!uc_google_checkout_refund_order($response)) {
      drupal_set_message(t('Refund request sent to Google Checkout. The refund confirmation should appear on this page momentarily.'));
    }
  }
  return 'admin/store/orders/' . $order_id;
}

/**
 * Responds to the refund-order notification.
 *
 * Enters a negative payment to the order for the amount given.
 *
 * @param SimpleXMLElement $refund
 * XML notification
 * @return boolean
 * TRUE if an order corresponding to the Google order number was found and
 * updated.
 * FALSE otherwise.
 */
function uc_google_checkout_refund_order($refund) {
  $order_id = uc_google_checkout_get_order($refund->{'google-order-number'});
  if ($order_id) {
    uc_payment_enter($order_id, 'google_checkout', -((string) $refund->{'latest-refund-amount'}), 0, '', t('Refund received by Google Checkout'));
    uc_order_comment_save($order_id, 0, t('Refund of %amount received by Google Checkout.', array('%amount' => uc_currency_format((string) $refund->{'latest-refund-amount'}))), 'admin', 'processing');

    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Submit callback. Sends order update notifcations through Google Checkout.
 */
function uc_google_checkout_notify_update($form, &$form_state) {
  $order = uc_order_load($form_state['values']['order_id']);
  if ($order !== FALSE && isset($order->google_order_number) && isset($form_state['values']['order_comment']) && drupal_strlen($form_state['values']['order_comment'])) {
    $request = uc_google_checkout_buyer_message_request($form_state['values']['order_id'], $form_state['values']['order_comment']);
    $response = uc_google_checkout_send_request('request', $request);
  }
}

/**
 * Builds the send-buyer-message request.
 *
 * @param int $order_id
 * The order ID.
 * @param string $message
 * The update message.
 *
 * @return string
 * The XML request.
 */
function uc_google_checkout_buyer_message_request($order_id, $message) {
  $google_order_number = uc_google_checkout_get_google_number($order_id);
  $output = '<?xml version="1.0" encoding="UTF-8"?>';
  $output .= "\n";
  $output .= '<send-buyer-message xmlns="http://checkout.google.com/schema/2" google-order-number="' . $google_order_number . '">';
  $output .= '<message>' . check_plain($message) . '</message>';
  $output .= '<send-email>true</send-email>';
  $output .= '</send-buyer-message>';

  return $output;
}

/**
 * Sends the cancel-order request.
 *
 * @param object $order
 * @return boolean
 * TRUE if the request succeeded, FALSE otherwise.
 */
function uc_google_checkout_cancel_order($order) {
  $request = '<?xml version="1.0" encoding="UTF-8"?>';
  $request .= "\n";
  $request .= '<cancel-order xmlns="http://checkout.google.com/schema/2" google-order-number="' . $order->google_order_number . '">';
  $request .= '<reason>' . token_replace(variable_get('uc_google_checkout_order_cancel_reason', t('Order canceled. See order comments at [uc_order:url] for more information.')), array('uc_order' => $order)) . '</reason>';
  $request .= '</cancel-order>';

  if ($response = uc_google_checkout_send_request('request', $request)) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Acknowledges that the notification was received successfully.
 *
 * @param string $serial_number
 * Unique identifier for the notification.
 */
function uc_google_checkout_notification_acknowledgement($serial_number) {
  drupal_add_http_header('Status', '200 OK');
  print '<?xml version="1.0" encoding="UTF-8"?>';
  print "\n";
  print '<notification-acknowledgment xmlns="http://checkout.google.com/schema/2" serial-number="' . $serial_number . '" />';
  exit();
}

/**
 * Reports that the notification was not understood.
 *
 * @param string $message
 * Optional error message.
 */
function uc_google_checkout_notification_error($message = NULL) {
  if (is_null($message)) {
    $message = t('Unknown order id or malformed XML.');
  }
  drupal_add_http_header('Status', '400 Bad Request');
  watchdog('google', '!message', array('!message' => $message), WATCHDOG_ERROR);
  exit();
}

/**
 * Transforms a Google order number to an Ubercart order ID.
 *
 * @param string $google_order_number
 * Google's order number.
 * @return int
 * The order ID, or FALSE if not found.
 */
function uc_google_checkout_get_order($google_order_number) {
  return db_query("SELECT order_id FROM {uc_gc_orders} WHERE gc_order_number = :number", array(':number' => $google_order_number))->fetchField();
}

/**
 * Transforms an Ubercart order ID to a Google order number.
 *
 * @param int $order_id
 * @return string
 * The Google order number, or FALSE if not found.
 */
function uc_google_checkout_get_google_number($order_id) {
  return db_query("SELECT gc_order_number FROM {uc_gc_orders} WHERE order_id = :id", array(':id' => $order_id))->fetchField();
}

/**
 * Helper function to return all valid shipping services in Google Checkout.
 *
 * @return array
 */
function uc_google_checkout_shipping_services() {
  return array_merge(uc_google_checkout_fedex_services(), uc_google_checkout_ups_services(), uc_google_checkout_usps_services());
}

/**
 * Lists the available FedEx shipping services.
 *
 * @return array
 */
function uc_google_checkout_fedex_services() {
  return array(
    'fedex_ground' => 'Ground',
    'fedex_home' => 'Home Delivery',
    'fedex_express' => 'Express Saver',
    'fedex_first' => 'First Overnight',
    'fedex_priority' => 'Priority Overnight',
    'fedex_standard' => 'Standard Overnight',
    'fedex_2day' => '2Day',
  );
}

/**
 * Lists the available UPS shipping services.
 *
 * @return array
 */
function uc_google_checkout_ups_services() {
  return array(
    'ups_next_day' => 'Next Day Air',
    'ups_next_day_am' => 'Next Day Air Early AM',
    'ups_next_day_saver' => 'Next Day Air Saver',
    'ups_2nd_day' => '2nd Day Air',
    'ups_2nd_day_am' => '2nd Day Air AM',
    'ups_3_day' => '3 Day Select',
    'ups_ground' => 'Ground',
  );
}

/**
 * Lists the available USPS shipping services.
 *
 * @return array
 */
function uc_google_checkout_usps_services() {
  return array(
    'usps_express' => 'Express Mail',
    'usps_priority' => 'Priority Mail',
    'usps_parcel' => 'Parcel Post',
    'usps_media' => 'Media Mail',
  );
}

/**
 * Lists the available shipping companies.
 *
 * @return array
 */
function uc_google_checkout_shipping_companies() {
  return array(
    'fedex' => 'FedEx',
    'ups' => 'UPS',
    'usps' => 'USPS',
  );
}

/**
 * Returns an array of options for the currency selection widget.
 */
function _uc_google_checkout_currency_codes() {
  return drupal_map_assoc(array('AUD', 'CAD', 'EUR', 'GBP', 'HKD', 'JPY', 'USD'));
}

/**
 * Maps AVS codes to messages.
 *
 * @param string $code
 * Optional AVS respons code.
 * @return
 * If $code is given, returns the corresponding message. Otherwise, returns the
 * array of all messages.
 */
function uc_google_checkout_avs_code($code = NULL) {
  $codes = array(
    'Y' => t('Full AVS match (address and postal code)'),
    'P' => t('Partial AVS match (postal code only)'),
    'A' => t('Partial AVS match (address only)'),
    'N' => t('No AVS match'),
    'U' => t('AVS not supported by issuer'),
  );

  if ($code) {
    return isset($codes[$code]) ? $codes[$code] : '';
  }
  else {
    return $codes;
  }
}

/**
 * Maps CVN codes to messages.
 *
 * @param string $code
 * Optional CVN respons code.
 * @return
 * If $code is given, returns the corresponding message. Otherwise, returns the
 * array of all messages.
 */
function uc_google_checkout_cvn_code($code = NULL) {
  $codes = array(
    'M' => t('CVN match'),
    'N' => t('No CVN match'),
    'U' => t('CVN not available'),
    'E' => t('CVN error'),
  );

  if ($code) {
    return isset($codes[$code]) ? $codes[$code] : '';
  }
  else {
    return $codes;
  }
}
