home | career | drupal | java | mac | mysql | perl | php | scala | uml | unix

Drupal example source code file (uc_google_checkout.module)

This example Drupal source code file (uc_google_checkout.module) is included in the DevDaily.com "Drupal Source Code Warehouse" project. The intent of this project is to help you "Learn Drupal by Example".

PHP - Drupal tags/keywords

array, break, case, check_plain, checkout, function, google, if, order_id, output, php, request, return, true

The uc_google_checkout.module Drupal example source code

<?php
// $Id: uc_google_checkout.module,v 1.1.2.16 2010/01/27 22:23:32 islandusurper Exp $

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

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

function uc_google_checkout_help($page, $args) {
  $output = '';
  switch ($page) {
    case 'admin/store/settings/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';
      }
      $output .= t('In the <a href="!checkout_url/sell">Google Checkout Merchant Center</a>, enter %url as the callback URL for this site. This will allow Ubercart to communicate with Google Checkout.', array('!checkout_url' => $checkout_url, '%url' => url('google_checkout', array('absolute' => TRUE))));
    break;
  }
  return $output;
}

function uc_google_checkout_menu() {
  $items = array();

  $items['admin/store/settings/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'),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'uc_google_checkout.admin.inc',
  );
  $items['admin/store/settings/google_checkout/account'] = array(
    'title' => 'Account',
    'type' => MENU_DEFAULT_LOCAL_TASK,
  );
  $items['admin/store/settings/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/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' => 'uc_google_checkout_callback_access',
    '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'),
    'type' => MENU_CALLBACK,
    'file' => 'uc_google_checkout.admin.inc',
  );

  return $items;
}

function uc_google_checkout_callback_access() {
  return TRUE;
}

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

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($_SESSION['cart_order'], $_SESSION['do_complete'], $_SESSION['new_user'], $users[$user->uid]);
    variable_set('uc_google_checkout_order_users', $users);
  }

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

function uc_google_checkout_theme() {
  return array(
    'uc_google_checkout_shipping_settings' => array(
      'arguments' => array('form' => NULL),
      'file' => 'uc_google_checkout.admin.inc',
    ),
    'uc_google_checkout_taxes_settings' => array(
      'arguments' => array('form' => NULL),
      'file' => 'uc_google_checkout.admin.inc',
    ),
  );
}

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['google_checkout'] = array('#type' => 'fieldset',
        '#title' => t('Google Checkout settings'),
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
      );
      $form['google_checkout']['gc_salable'] = array('#type' => 'checkbox',
        '#title' => t('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 Google Checkout content policies found <a href="!url">here</a>.', array('!url' => $policy_url)),
      );
    }
  }
  elseif ($form_id == 'uc_payment_methods_form') {
    // Make sure no one enables this for Ubercart checkout.
    $form['pmtable']['google_checkout']['uc_payment_method_google_checkout_checkout']['#disabled'] = TRUE;
    $form['pmtable']['google_checkout']['uc_payment_method_google_checkout_checkout']['#value'] = FALSE;
  }
  elseif ($form_id == 'uc_payment_by_order_form') {
    // Don't use Google Checkout for manual payments either.
    unset($form['payments']['new']['method']['#options']['google_checkout']);
  }
  elseif ($form_id == 'uc_order_view_update_form') {
    $form['#submit'][] = 'uc_google_checkout_notify_update';
  }
  elseif ($form_id == 'uc_store_format_settings_form') {
    $form['currency']['uc_currency_code']['#description'] .= ' '. t('Google Checkout only accepts the following currencies: @list', array('@list' => implode(', ', _uc_google_checkout_currency_codes())));
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function uc_google_checkout_nodeapi(&$node, $op) {
  if (uc_product_is_product($node->type)) {
    switch ($op) {
      case 'insert':
      case 'update':
        if (isset($node->gc_salable)) {
          if (!$node->revision) {
            db_query("DELETE FROM {uc_gc_products} WHERE vid = %d", $node->vid);
          }
          db_query("INSERT INTO {uc_gc_products} (vid, nid, gc_salable) VALUES (%d, %d, %d)",
            $node->vid, $node->nid, $node->gc_salable);
        }
      break;
      case 'load':
        $salable = db_result(db_query("SELECT gc_salable FROM {uc_gc_products} WHERE vid = %d", $node->vid));
        if ($salable === FALSE) {
          $salable = TRUE;
        }
        return array('gc_salable' => $salable);
      break;
      case 'delete':
        db_query("DELETE FROM {uc_gc_products} WHERE nid = %d", $node->nid);
      break;
      case 'delete revision':
        db_query("DELETE FROM {uc_gc_products} WHERE vid = %d", $node->vid);
      break;
    }
  }
}

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

function uc_google_checkout_cart_pane() {
  $panes[] = array(
    'id' => 'uc_google_checkout',
    'title' => t('Google Checkout'),
    'enabled' => TRUE,
    'weight' => 1,
    'body' => '<div align="'. variable_get('uc_google_checkout_button_align', 'right') .'">'. drupal_get_form('uc_google_checkout_cart_form') .'</div>',
  );
  return $panes;
}

function uc_google_checkout_order_pane() {
  $panes[] = array(
    'id' => 'email_allowed',
    '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;
}

function uc_google_checkout_line_item() {
  $items[] = array(
    'id' => 'gc_coupon',
    'title' => t('Google Checkout Coupon'),
    'stored' => TRUE,
    'calculated' => TRUE,
    'weight' => 2,
  );
  $items[] = array(
    'id' => 'gc_gift_certificate',
    'title' => t('Google Checkout Gift Certificate'),
    'stored' => TRUE,
    'calculated' => TRUE,
    'weight' => 2,
  );

  return $items;
}

function uc_google_checkout_order($op, &$arg1, $arg2) {
  switch ($op) {
    case 'load':
      $result = db_query("SELECT * FROM {uc_gc_orders} WHERE order_id = %d", $arg1->order_id);
      if ($gc = db_fetch_object($result)) {
        $arg1->google_order_number = $gc->gc_order_number;
        $arg1->financial_state = $gc->financial_state;
        $arg1->fulfillment_state = $gc->fulfillment_state;
        $arg1->gc_total = $gc->gc_total;
      }
    break;
    case 'can_update':
      if (is_null($arg1->google_order_number) || isset($_SESSION['google_updates']) && $_SESSION['google_updates']) {
        return TRUE;
      }
      switch ($arg2) {
        case 'canceled':
          if (uc_google_checkout_cancel_order($arg1)) {
            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;
  }
}

/**
 * Implementation of hook_payment_method().
 */
function uc_google_checkout_payment_method() {
  $methods[] = array(
    'id' => 'google_checkout',
    '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,
  );
  return $methods;
}

function uc_google_checkout_payment_gateway() {
  $gateways[] = array(
    'id' => 'google_checkout',
    'title' => t('Google Checkout'),
    'description' => t('Express payment with Google Checkout.'),
    'google_checkout' => 'uc_google_checkout_charge',
  );
  return $gateways;
}

function uc_google_checkout_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                                      *
 ******************************************************************************/

function uc_google_checkout_cart_form() {
  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;
  }

  // Hack to allow the image button to submit.
  if (isset($_POST['submit_x'])) {
    $form['submit'] = array(
      '#type' => 'submit',
      '#value' => 'submit',
    );
  }

  $disable = FALSE;
  foreach (uc_cart_get_contents() as $item) {
    $product = node_load($item->nid);
    if (!$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['submit_image'] = array(
    '#value' => ($disable ? '<img ' : '<input name="submit" type="image" ') .'alt="'. t('Google Checkout') .'" title="'. t('Fast checkout through Google.') .'" 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" height="'. $height .'" width="'. $width .'" />',
  );
  $form['submit_help'] = array(
    '#value' => '<br /><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 = \''. t('What is Google Checkout?') .'\';" OnMouseOut="return window.status = \'\';">'. t('What is Google Checkout?') .'</a>',
  );

  return $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';

  $request = uc_google_checkout_cart_request($order);
  if ($response = uc_google_checkout_send_request('merchantCheckout', $request)) {
    $redirect = (string)$response->{'redirect-url'};
    drupal_goto($redirect);
  }
}

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_cart_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)) {
      $companies = uc_google_checkout_shipping_companies();
      $services = uc_google_checkout_shipping_services();
      $shipping_options = '';
      foreach ($services as $key => $title) {
        $settings = variable_get('uc_google_checkout_shipping_'. $key, array());
        if ($settings['enable']) {
          $shipping_options .= '<carrier-calculated-shipping-option>';
          $shipping_options .= '<price currency="'. variable_get('uc_currency_code', 'USD') .'">'. $settings['default'] .'</price>';
          $temp = explode('_', $key, 2);
          $shipping_options .= '<shipping-company>'. $companies[$temp[0]] .'</shipping-company>';
          $shipping_options .= '<carrier-pickup>'. $settings['pickup'] .'</carrier-pickup>';
          $shipping_options .= '<shipping-type>'. $services[$key] .'</shipping-type>';
          if ($settings['fixed_charge']) {
            $context = array(
              'revision' => 'formatted-original',
              'type' => 'amount',
            );
            $options = array(
              'sign' => FALSE,
              'thou' => FALSE,
              'dec' => '.',
            );
            $shipping_options .= '<additional-fixed-charge currency="'. variable_get('uc_currency_code', 'USD') .'">'. uc_price($settings['fixed_charge'], $context, $options) .'</additional-fixed-charge>';
          }
          if ($settings['percent_adjustment']) {
            $shipping_options .= '<additional-variable-charge-percent>'. (float)$settings['percent_adjustment'] .'</additional-variable-charge-percent>';
          }
          $shipping_options .= '</carrier-calculated-shipping-option>';
        }
      }
      if ($shipping_options) {
        $output .= '<shipping-methods>';
        $output .= '<carrier-calculated-shipping>';
        $output .= '<carrier-calculated-shipping-options>';
        $output .= $shipping_options;
        $output .= '</carrier-calculated-shipping-options>';

        $address = variable_get('uc_quote_store_default_address', new stdClass());
        if (empty($address)) {
          $address->city = variable_get('uc_store_city', NULL);
          $address->zone = uc_get_zone_code(variable_get('uc_store_zone', NULL));
          $address->postal_code = variable_get('uc_store_postal_code', NULL);
          $address->country = uc_store_default_country();
        }
        $country = uc_get_country_data(array('country_id' => $address->country));
        $address->country_code = $country[0]['country_iso_code_2'];
        $output .= '<shipping-packages>';
        $output .= '<shipping-package>';
        $output .= '<ship-from id="here">';
        $output .= '<city>'. $address->city .'</city>';
        $output .= '<region>'. $address->zone .'</region>';
        $output .= '<country-code>'. $address->country_code .'</country-code>';
        $output .= '<postal-code>'. $address->postal_code .'</postal-code>';
        $output .= '</ship-from>';
        $output .= '</shipping-package>';
        $output .= '</shipping-packages>';

        $output .= '</carrier-calculated-shipping>';
        $output .= '</shipping-methods>';
      }
    }

    $tax_table = '';
    $result = db_query("SELECT zone, rate, tax_shipping FROM {uc_gc_taxes}");
    while ($tax = db_fetch_object($result)) {
      $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;
}

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;
}

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';
  }

  // Google's XML parser doesn't like named entities apparently.
  str_replace(array('&', '<', '>'), array('&', '<', '>'), $request);
  $response_obj = drupal_http_request($checkout_url .'/api/checkout/v2/'. $api .'/Merchant/'. $merchant_id, uc_google_checkout_headers(), 'POST', $request);
  if ($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 = $messages['error'];
  $total = count($errors);
  for ($i = 0; $i <= $total; $i++) {
    if (strpos($errors[$i], 'SSL: fatal protocol error in')) {
      unset($_SESSION['messages']['error'][$i]);
    }
  }
  if (empty($_SESSION['messages']['error'])) {
    unset($_SESSION['messages']['error']);
  }
  db_query("DELETE FROM {watchdog} WHERE type = 'php' AND variables LIKE '%%SSL: fatal protocol error%%'");
  // End of ugly hack.

  return $response;
}

function uc_google_checkout_pane_email_allowed($op, $arg1) {
  switch ($op) {
    case 'customer':
    case 'view':
      if ($arg1->data['email_allowed']) {
        $output = t('Customer will accept marketing emails.');
      }
      else {
        $output = t('Customer does not want marketing emails.');
      }
    return $output;
  }
}

/**
 * Add setting callbacks to the payment settings section
 *  The fulfillment sections need coded still. JS.
 */
function uc_payment_method_google_checkout($op, &$arg1) {
  switch ($op) {
    case 'order-view':
      $output = l(t('Google Checkout terminal'), 'admin/store/orders/'. $arg1->order_id .'/google_checkout');
      return $output;
    case 'settings':
      $form = array();

      $form['link'] = array(
        '#value' => l(t('Click here to go to Google Checkout settings.'), 'admin/store/settings/google_checkout'),
      );

    return $form;
  }
}

function uc_google_checkout_new_order($new_order) {
  $order_id = $new_order->{'shopping-cart'}->{'merchant-private-data'}->{'order-id'};
  $cart_id = $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 = $shipping_address->{'company-name'};
    $order->delivery_first_name = $shipping_address->{'structured-name'}->{'first-name'};
    $order->delivery_last_name = $shipping_address->{'structured-name'}->{'last-name'};
    $order->delivery_phone = $shipping_address->phone;
    $order->delivery_street1 = $shipping_address->address1;
    $order->delivery_street2 = $shipping_address->address2;
    $order->delivery_city = $shipping_address->city;
    $zone_id = db_result(db_query("SELECT zone_id FROM {uc_zones} WHERE zone_code = '%s'", $shipping_address->region));
    $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 = $shipping_address->{'postal-code'};

    $billing_address = $new_order->{'buyer-billing-address'};
    $order->billing_company = $billing_address->{'company-name'};
    $order->billing_first_name = $billing_address->{'structured-name'}->{'first-name'};
    $order->billing_last_name = $billing_address->{'structured-name'}->{'last-name'};
    $order->billing_phone = $billing_address->phone;
    $order->billing_street1 = $billing_address->address1;
    $order->billing_street2 = $billing_address->address2;
    $order->billing_city = $billing_address->city;
    if ($billing_address['region'] != $shipping_address->region) {
      $zone_id = db_result(db_query("SELECT zone_id FROM {uc_zones} WHERE zone_code = '%s'", $billing_address->region));
    }
    $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 = $billing_address->{'postal-code'};

    if (!$order->primary_email) {
      $order->primary_email = $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();

    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];
      uc_order_line_item_add($order_id, 'shipping', check_plain($shipping_line->{'shipping-name'}), $shipping_line->{'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($adjustment->code), -$adjustment->{'applied-amount'});
          $comments[] = check_plain($adjustment->message);
        }
        elseif ($type == 'gift-certificate-adjustment') {
          uc_order_line_item_add($order_id, 'gc_gift_certificate', check_plain($adjustment->code), -($adjustment->{'applied-amount'}));
          $comments[] = check_plain($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.
    uc_order_comment_save($order->order_id, 0, t('Order created through Google Checkout (#@gco_order).', array('@gco_order' => $new_order->{'google-order-number'})), '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_query("INSERT INTO {uc_gc_orders} (order_id, gc_order_number, gc_total) VALUES (%d, '%s', %f)", $order_id, $new_order->{'google-order-number'}, $new_order->{'order-total'});

    uc_google_checkout_notification_acknowledgement($new_order['serial-number']);
  }
  else {
    uc_google_checkout_notification_error();
  }
}

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 @count month.', 'Google Checkout member for @count months.');
    $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');
    uc_google_checkout_notification_acknowledgement($risk['serial-number']);
  }
}

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_query("UPDATE {uc_gc_orders} SET financial_state = '%s', fulfillment_state = '%s' WHERE order_id = %d AND financial_state = '%s' AND fulfillment_state = '%s'", array($new_financial, $new_fulfillment, $order_id, $prev_financial, $prev_fulfillment));
    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']);
    }
    uc_google_checkout_notification_acknowledgement($change['serial-number']);
  }
  else {
    uc_google_checkout_notification_error();
  }
}

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-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-order>';

  if ($response = uc_google_checkout_send_request('request', $output)) {
    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;
}

function uc_google_checkout_charge_order($charge) {
  $order_id = uc_google_checkout_get_order($charge->{'google-order-number'});
  if ($order_id) {
    uc_payment_enter($order_id, 'google_checkout', $charge->{'latest-charge-amount'}, 0,
                     '', t('Payment received by Google Checkout'));
    $context = array(
      'revision' => 'formatted-original',
      'type' => 'amount',
    );
    uc_order_comment_save($order_id, 0, t('Payment of %amount received by Google Checkout.', array('%amount' => uc_price($charge->{'latest-charge-amount'}, $context))), 'admin', 'chargeable');
    uc_google_checkout_notification_acknowledgement($charge['serial-number']);
  }
  else {
    uc_google_checkout_notification_error();
  }
}

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)) {
    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;
}

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', -$refund->{'latest-refund-amount'},
                     0, '', t('Refund received by Google Checkout'));
    $context = array(
      'revision' => 'formatted-original',
      'type' => 'amount',
    );
    uc_order_comment_save($order_id, 0, t('Refund of %amount received by Google Checkout.', array('%amount' => uc_price($refund->{'latest-refund-amount'}, $context))),
                          'admin', 'processing');
    uc_google_checkout_notification_acknowledgement($refund['serial-number']);
  }
  else {
    uc_google_checkout_notification_error();
  }
}

function uc_google_checkout_notify_update($form, &$form_state) {
  $order = uc_order_load($form_state['values']['order_id']);
  if ($order !== FALSE && 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);
  }
}

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;
}

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_multiple(variable_get('uc_google_checkout_order_cancel_reason', t('Order canceled. See order comments at [order-url] for more information.')), array('global' => NULL, 'order' => $order)) .'</reason>';
  $request .= '</cancel-order>';

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

function uc_google_checkout_notification_acknowledgement($serial_number) {
  drupal_set_header('HTTP/1.1 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();
}

function uc_google_checkout_notification_error($message = NULL) {
  if (is_null($message)) {
    $message = t('Unknown order id or malformed XML.');
  }
  drupal_set_header('HTTP/1.1 400 Bad Request');
  watchdog('google', '!message', array('!message' => $message), WATCHDOG_ERROR);
  exit();
}

function uc_google_checkout_get_order($google_order_number) {
  return db_result(db_query("SELECT order_id FROM {uc_gc_orders} WHERE gc_order_number = '%s'", $google_order_number));
}

function uc_google_checkout_get_google_number($order_id) {
  return db_result(db_query("SELECT gc_order_number FROM {uc_gc_orders} WHERE order_id = %d", $order_id));
}

function uc_google_checkout_shipping_services() {
  return array_merge(uc_google_checkout_fedex_services(), uc_google_checkout_ups_services(), uc_google_checkout_usps_services());
}

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',
  );
}

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',
  );
}

function uc_google_checkout_usps_services() {
  return array(
    'usps_express' => 'Express Mail',
    'usps_priority' => 'Priority Mail',
    'usps_parcel' => 'Parcel Post',
    'usps_media' => 'Media Mail',
  );
}

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'));
}

Other Drupal examples (source code examples)

Here is a short list of links related to this Drupal uc_google_checkout.module source code file:

new blog posts

"Drupal" is a registered trademark of Dries Buytaert.

my drupal tutorials and examples  

Copyright 1998-2016 Alvin Alexander, alvinalexander.com
All Rights Reserved.

Beginning in 2016, a portion of the proceeds from pages under the '/drupal-code-examples/' URI will be donated to charity.