Creating an Ubercart Payment Gateway module

Payment Gateway

I recently was tasked with creating a Drupal 6 Ubercart Payment Gateway module. I started the task off looking over various contributed payment gateway modules for Ubercart as well as looking at the authorize.net payment gateway module that comes with the Ubercart module. What I found was that there are many techniques that developers use when building a payment gateway module. I am going to outline the ones that worked best for me. Just a note, I was heavily biased towards following the authorize.net payment gateway module as that is the one I have used the most and it appears to be well written, well tested, and secure.

First, lets start off with a .info file.


; $Id:$
name = MyPaymentGateway
description = Process payments using MyPaymentGatewaw
dependencies[] = uc_payment
dependencies[] = uc_credit
package = Ubercart - payment
core = 6.x
php = 5.0
version = "6.x-1.0"

Now lets create the .install file:

// $Id$
 
/**
 * @file
 * uc_mypaymentgateway.install
 */
 
/**
 * Implementation of hook_requirements().
 *
 * Verifies that PHP CURL library is installed on the server
 */
function uc_mypaymentgateway_requirements($phase) {
  $t = get_t();
 
  $has_curl = function_exists('curl_init');
 
  $requirements['uc_mypaymentgateway_curl'] = array(
    'title' => $t('cURL'),
    'value' => $has_curl ? $t('Enabled') : $t('Not found'),
  );
  if (!$has_curl) {
    $requirements['uc_mypaymentgateway_curl']['severity'] = REQUIREMENT_ERROR;
    $requirements['uc_mypaymentgateway_curl']['description'] = $t("MyPaymentGateway Module requires the PHP <a href='!curl_url'>cURL</a> library.", array('!curl_url' => '<a href="http://php.net/manual/en/curl.setup.php'">http://php.net/manual/en/curl.setup.php'</a>));
  }
 
  return $requirements;
}
 
/**
 * Implements hook_uninstall().
 *
 * Uninstalls all variables set by the uc_mypaymentgateway module.
 */
function uc_mypaymentgateway_uninstall() {
  // Delete related variables all at once.
  db_query("DELETE FROM {variable} WHERE name LIKE 'uc_mypaymentgateway_%%'");
}

You will notice in the above example that it checks to ensure the PHP Curl library has been installed on the server. This is not necessary unless you actually use CURL, but chances are you might need it if you are sending data to an Payment Processor's API.

The next thing to do is to create the .module file.

// $Id$
 
/**
 * @file
 * uc_mypaymentgateway.module
 * Ubercart Payment gateway for MyPaymentGateway
 */
 
//Define the test and production URLs
define('UC_MYPAYMENTGATEWAY_TEST_GATEWAY_URL', '<a href="https://testurl.com'">https://testurl.com'</a>);
define('UC_MYPAYMENTGATEWAY_LIVE_GATEWAY_URL', '<a href="https://liveurl.com'">https://liveurl.com'</a>);
 
/**
 * Implementation of hook_payment_gateway().
 *
 * Exposes MyPaymentGateway to ubercart.
 */
function uc_mypaymentgateway_payment_gateway() {
  $gateways[] = array(
    'id' => 'mypaymentgateway',
    'title' => t('MyPaymentGateway'),
    'description' => t('Process credit card payments using MyPaymentGateway.'),
    'settings' => 'uc_mypaymentgateway_settings_form',
    'credit' => 'uc_mypaymentgateway_charge',
    'credit_txn_types' => array(UC_CREDIT_AUTH_CAPTURE),
  );
 
  return $gateways;
}

The above code just defines some global variables to use for the test and live URLS provided by the payment processor. The hook_payment_gateway() piece of code just exposes the payment gateway to Ubercart. Some important things to note are the functions for settings, and credit. The function defined for settings is the form that will be called by Ubercart that contains the payment gateway settings (api keys, payment gateway urls, etc). The credit function is called when a user checks out and actually tries to make a payment against their credit card.

One more important thing to note is the credit_txn_types. If you are only planning to authorize and capture the payment immediately, you can use my example above. If you plan to use other types of transactions (such as authorize only), you may want to consult the Ubercart documentation for hook_payment_gateway or have a look at the authorize.net payment gateway module that is included when you download the Ubercart module.

Now I will go ahead and create the settings form for the payment gateway module.

/**
 * Callback for payment gateway settings. Admin form to allow setting up of the
 * necessary mypaymentgateway configuration settings.
 */
function uc_mypaymentgateway_settings_form() {
  $form['uc_mypaymentgateway_transaction_mode'] = array(
    '#type' => 'radios',
    '#title' => t('Transaction mode'),
    '#description' => t('Select the transaction mode to use for communication with mypaymentgateway. The URLs for this transaction mode are specified below.'),
    '#options' => array(
      'production' => t('Production'),
      'development' => t('Development'),
    ),
    '#default_value' => variable_get('uc_mypaymentgateway_transaction_mode', 'development'),
  );
  $form['api_id_key'] = array(
    '#type' => 'fieldset',
    '#title' => t('mypaymentgateway API Information'),
    '#description' => t('This information is required for Ubercart to interact with mypaymentgateway'),
  );
  $form['api_id_key']['uc_mypaymentgateway_test_gateway_url'] = array(
    '#type' => 'textfield',
    '#title' => t('mypaymentgateway Test Gateway URL'),
    '#default_value' => variable_get('uc_mypaymentgateway_test_gateway_url', UC_MYPAYMENTGATEWAY_TEST_GATEWAY_URL),
  );
  $form['api_id_key']['uc_mypaymentgateway_live_gateway_url'] = array(
    '#type' => 'textfield',
    '#title' => t('mypaymentgateway Live Gateway URL'),
    '#default_value' => variable_get('uc_mypaymentgateway_live_gateway_url', UC_MYPAYMENTGATEWAY_LIVE_GATEWAY_URL),
  );
 
  //other form items for API keys, etc would go here
 
  return $form;
}

For the most part, everything above is pretty much the boilerplate code you need for starting an Ubercart Payment gateway module. At this point the code begins to become Payment Processor specific depending on the details of the API you are trying to post payments too. You will need to define the function that is called when a transaction is processed:

/**
 * Main handler for processing credit card transactions.
 *
 * @param $order_id
 *   The order id of the order connected to the credit card to be charged
 * @param $amount
 *   The amount to charge the credit card
 * @param $data
 *   Transactional information
 *
 * @return
 *   An array containing a success/failure boolean along with response message
 */
function uc_mypaymentgateway_charge($order_id, $amount, $data) {
  //all your custom payment processor specific code goes here
}

Because the code can be very different depending on the type of API you are hitting, the type of responses returned, the type of transactions you are processing, etc, I will just go over what Ubercart expects to be returned from the above function. Ubercart is expecting the return value to be an array with a few important constructs.

Here is an example of what a very basic failed transaction might look like:

global $user;
return array(
  'success' => FALSE,
  'message' => t('Credit card payment declined'),
  'uid' => $user->uid,
);

Here is an example of what a very basic successful transaction might look like:

global $user;
return array(
  'success' => TRUE,
  'comment' => t('A comment can go here'),
  'message' => t('A message can go here'),
  'data' => array(), //an array of data to store with the payment can go here
  'uid' => $user->uid,
);