Friday, July 13, 2012

How to Create a Custom Ubercart Payment Gateway

If you're looking to create your own Ubercart payment gateway for payment processing on a site outside of Drupal (eg. redirect to an external payment site like PayPal or Moneris hosted checkout), then hopefully the following instructions will help you get started...

Step 1: Create your payment gateway module's .info file and start a blank .module file.

Step 2: Define the gateway in your .module file:

define('MY_PAYMENT_GATEWAY_URL', 'https://test.MyMerchantGateway.com');

function my_pay_gateway_uc_payment_gateway() {
  $gateways['my_pay_gateway'] = array(
    'title' => t('My Payment Gateway'),
    'description' => t('Process payments through my custom payment gateway'),
  );
  return $gateways;
}

Step 3: Then you must provide a payment method:

function my_pay_gateway_uc_payment_method() {
  $methods[] = array(
    'id' => 'my_pay_credit',
    'name' => t('My Payment Gateway'),
    'title' => t('My Payment Gateway'),
    'desc' => t('Pay through my payment gateway'),
    'callback' => 'my_payment_method',
    'redirect' => 'my_payment_form',
    'weight' => 1,
    'checkout' => TRUE,
  );
  return $methods;
}


Later, when you enable your payment gateway module, you will be able to find the payment method you defined and get to the settings form in your store's payment configurations:

Your payment gateway method.

Step 4: In the callback method, you should define the settings form for your payment gateway:

function my_payment_method($op, &$order) {
  switch ($op) {
    case 'settings':
      $form['my_payment_gateway_username'] = array(
        '#type' => 'textfield',
        '#title' => t('Username'),
        '#description' => t('Your merchant username'),
        '#default_value' => variable_get('my_payment_gateway_username'),
      );
      $form['my_custom_checkout_label'] = array(
        '#type' => 'textfield',
        '#title' => t('Checkout button label'),
        '#description' => t('Customize the label of the final checkout button when the customer is about to pay.'),
        '#default_value' => variable_get('my_custom_checkout_label'),
        );
      return $form;
  }
}

The code above will render the following settings form:
 
This is the simple form as rendered by the code above.


Step 5: Now, you have to add the form code for the redirect callback method that you specified in Step 3.

function my_payment_form($form, &$form_state, $order) {

  // Collect some information about the order
  $time = time();
  $order_id = $order->order_id;
  $order_total = number_format($order->order_total, 2, '.', '');
  $customer_email = $order->primary_email;
  $cart_id = uc_cart_get_id();

  // Build the data to send to my payment gateway
  $data = array(
    'timestamp' => time(),
    'order_id' => $order->order_id,
    'order_total' => number_format($order->order_total, 2, '.', ''),
    'customer_email' => $order->primary_email,
    'cart_id' => uc_cart_get_id(),
  );

  // This code goes behind the final checkout button of the checkout pane
  foreach ($data as $name => $value) {
    if (!empty($value)) {
      $form[$name] = array('#type' => 'hidden', '#value' => $value);
    }
  }

  $form['#action'] = MY_PAYMENT_GATEWAY_URL;
  $form['actions'] = array('#type' => 'actions');
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => variable_get('my_custom_checkout_label', t('Submit Orders')),
  );
  return $form;
}
 

Code above goes behind this button

If you inspect the button element, you should see the html for the form that you built in your code including all the hidden form elements of your order data.

Step 6: Now you have to create a menu callback where you can receive confirmation from your payment gateway.

function my_pay_gateway_menu() {
  $items['paymentcomplete'] = array(
    'title' => 'Payment Complete',
    'page callback' => 'my_pay_gateway_complete',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

function my_pay_gateway_complete() {

  if (empty($_POST)) {
    watchdog('My Payment Gateway',
             'Received an empty or incomplete response.  Response details: @request_details',
             array('@request_details' =>  print_r($_POST,true)), WATCHDOG_ERROR);

    return 'There was a problem with your payment';
  }

  if ($_POST['status'] == 'SUCCESS') {

    // Insert logic here to make sure payment info can be matched to valid order

    // Assuming all tests passed and payment was successful
    // Complete the order
    uc_payment_enter($order_id, 'my_pay_gateway', $amount, $order->uid, NULL, $orderId);
    uc_cart_complete_sale($order, variable_get('uc_new_customer_login', FALSE));

    return 'Thank you for your purchase';
  }
}

I opted to get the returned values from my payment gateway through $_POST variable but you can also put values in the completion URL path like this:

function my_pay_gateway_menu() {
  $items['paymentcomplete/%/%'] = array(
    'title' => 'Payment Complete',
    'page callback' => 'my_pay_gateway_complete', 
    'page arguments' => array(1, 2),  
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

function my_pay_gateway_complete($orderId, $paymentStatus) {
...
} 

If you require another example, I found it helpful to look at the PayPal payment module code.
 
Also, this post on Stackoverflow is a great example of an alternative way for Step 6.

10 comments:

  1. I have added these code and it works fine,it pays all time payment success without go to secure server and without payment details. can you please help me,

    ReplyDelete
    Replies
    1. You haven't provided enough details for me to help you. However, I'd suggest that you post your question on stackoverflow.com so that others can help you. Feel free to post the link to the question here and I'm happy to check it out.

      Delete
  2. sorry, I was very blind about my payment gateway implements the instructions below to Ubercart, can you help me apply the code below into Ubercart?.

    ------------------------------here sample --------------
    'api_key_merchant', // API Key Merchant / Penjual
    'action' => 'payment',
    'product' => 'Nama Produk',
    'price' => '101000', // Total Harga
    'quantity' => 1,
    'comments' => 'Keterangan Produk', // Optional
    'ureturn' => 'http://websiteanda.com/return.php',
    'unotify' => 'http://websiteanda.com/notify.php',
    'ucancel' => 'http://websiteanda.com/cancel.php',

    /* Parameter untuk pembayaran lain menggunakan PayPal
    * ----------------------------------------------- */
    'invoice_number' => uniqid('INV-'), // Optional
    'paypal_email' => 'email_paypal_merchant',
    'paypal_price' => 1, // Total harga dalam kurs USD
    /* ----------------------------------------------- */

    'format' => 'json' // Format: xml / json. Default: xml
    );

    $params_string = http_build_query($params);

    //open connection
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, count($params));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $params_string);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

    //execute post
    $request = curl_exec($ch);

    if ( $request === false ) {
    echo 'Curl Error: ' . curl_error($ch);
    } else {

    $result = json_decode($request, true);

    if( isset($result['url']) )
    header('location: '. $result['url']);
    else {
    echo "Request Error ". $result['Status'] .": ". $result['Keterangan'];
    }
    }

    //close connection
    curl_close($ch);

    ?>

    ReplyDelete
    Replies
    1. I'd suggest you post the question on http://drupal.stackexchange.com. It will get more exposure there and likely more help, though you may want to translate the parts that aren't in English into English. Also, your code is incomplete. I assume the sample parts you posted are a part of the $param variable?

      Delete
  3. This comment has been removed by the author.

    ReplyDelete
  4. apologize in advance, I really wanted to ask for help on how to make online payment gateway module for the following website www.ipaymu.com in my country, some clues as api.key I already have, but I have absolutely no idea about how to implement it in order to support by Ubercart. thank you

    ReplyDelete
  5. How can I call external API for checkout. In my case, external API is not a payment gateway. I just want to transfer information from my drupal site to that API.
    Thanks in advance.

    ReplyDelete
  6. Ill try to do this for Payeasy payment gateway.

    ReplyDelete
  7. Is this an implementation for Ubercart 3 / Drupal 7?

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete