open cart — how to create a payment module for open cart

To be honestly, this is my first module for open cart. I know about open cart years ago(2009),but never study in deep.The installation of open cart is straightforward.After some minutes of playing around open cart frontend and admin backend, i decided to start work.

To create a payment module for open cart,first,we need to create all files for a open cart payment module.As we know,open cart was developed in MVC pattern,so all modules will include a model class,controller class and file for View,detail as below
[the name of the new payment module is callled moneybrace]
catalog/controller/payment/moneybrace.php (Controller class)
catalog/model/payment/moneybrace.php  (Model class)
catalog/language/english/payment/moneybrace.php (Language file for View)
catalog/view/theme/default/template/payment/moneybrace.tpl (Template file for View)
admin/controller/payment/moneybrace.php
admin/language/english/payment/moneybrace.php
admin/view/template/payment/moneybrace.php

It was obviously,all files located under catalog folder is for frontend use,all the others will be called in the admin backend page.

Keep in mind that you must follow the folder structure as the one above.And you’d better put template file in the default theme folder.You would not like to copy and paste file when you change the theme.

Now let’s do some coding work.Let’s start from implementing the payment’s bakend config page.For the Controller class,it should looks like below:
[admin/controller/payment/moneybrace.php]

Source code    
class ControllerPaymentMoneybrace extends Controller {
	private $error = array();
 
	public function index() {
 
	}
	private function validate() {
	}
}

 

The classname must start with ControllerPayment and should be a subclass of Controller.It's very simple,you only need to implement two method: index() and validate() .open cart will help you deal with all the other details.

Below is the language file which stores all lables for all form elements which were used to config the payment module:
[admin/language/english/payment/moneybrace.php]

Source code    
<?php
// Heading
$_['heading_title']			= 'Moneybrace';
 
// Text
$_['text_payment']			= 'Payment';
$_['text_success']			= 'Success: You have modified Moneybraces!';
$_['text_moneybrace']			= '<a onclick="window.open(\'http://www.moneybrace.com/\');"><img src="http://www.moneybrace.com/templets/default/images/logo.jpg" style="border: 1px solid #EEEEEE;" /></a>';
 
// Entry
$_['entry_merchantacct']		= 'Merchant Account:';
$_['entry_cert']			= 'Merchant Certificate:';
$_['entry_debug']			= 'Debug Mode:<br/><span class="help">Logs additional information to the system log.</span>';
$_['entry_completed_status']      = 'Completed Status:';
$_['entry_pending_status']		= 'Pending Status:';
$_['entry_processed_status']		= 'Processed Status:';
$_['entry_geo_zone']			= 'Geo Zone:';
$_['entry_status']			= 'Status:';
$_['entry_sort_order']			= 'Sort Order:';
 
// Error
$_['error_permission']			= 'Warning: You do not have permission to modify payment Moneybrace!';
$_['error_merchantacct']		= 'Merchant Account required!';
$_['error_cert']			= 'Merchant certificates required!';
?>

 

you need to pay some more attention for the third file,this is where you defined  related configure parameter which will be shown on backend config page. All the configure parameter will be insert into the setting table of the open cart database.The name of the input will be used as config key.You'd better start all key name with same prefix,here i use moneybrace,which is the name of the payment gateway.
[admin/view/template/payment/moneybrace.php]

Source code    
<?php echo $header; ?>
<div id="content">
  <div class="breadcrumb">
    <?php foreach ($breadcrumbs as $breadcrumb) { ?>
    <?php echo $breadcrumb['separator']; ?><a href="<?php echo $breadcrumb['href']; ?>"><?php echo $breadcrumb['text']; ?></a>
    <?php } ?>
  </div>
  <?php if ($error_warning) { ?>
  <div class="warning"><?php echo $error_warning; ?></div>
  <?php } ?>
  <div class="box">
    <div class="heading">
      <h1><img src="view/image/payment.png" alt="" /> <?php echo $heading_title; ?></h1>
      <div class="buttons"><a onclick="$('#form').submit();" class="button"><?php echo $button_save; ?></a><a onclick="location = '<?php echo $cancel; ?>';" class="button"><?php echo $button_cancel; ?></a></div>
    </div>
    <div class="content">
      <form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" id="form">
        <table class="form">
          <tr>
            <td><span class="required">*</span> <?php echo $entry_merchantacct; ?></td>
            <td><input type="text" name="moneybrace_merchantacct" value="<?php echo $moneybrace_merchantacct; ?>" />
              <?php if ($error_merchantacct) { ?>
              <span class="error"><?php echo $error_merchantacct; ?></span>
              <?php } ?></td>
          </tr>
          <tr>
            <td><span class="required">*</span> <?php echo $entry_cert; ?></td>
            <td><input type="text" name="moneybrace_cert" value="<?php echo $moneybrace_cert; ?>" />
              <?php if ($error_cert) { ?>
              <span class="error"><?php echo $error_cert; ?></span>
              <?php } ?></td>
          </tr>
          <tr>
            <td><?php echo $entry_debug; ?></td>
            <td><select name="moneybrace_debug">
                <?php if ($moneybrace_debug) { ?>
                <option value="1" selected="selected"><?php echo $text_enabled; ?></option>
                <option value="0"><?php echo $text_disabled; ?></option>
                <?php } else { ?>
                <option value="1"><?php echo $text_enabled; ?></option>
                <option value="0" selected="selected"><?php echo $text_disabled; ?></option>
                <?php } ?>
              </select></td>
          </tr>zz
          <tr>
            <td><?php echo $entry_pending_status; ?></td>
            <td><select name="moneybrace_pending_status_id">
                <?php foreach ($order_statuses as $order_status) { ?>
                <?php if ($order_status['order_status_id'] == $moneybrace_pending_status_id) { ?>
                <option value="<?php echo $order_status['order_status_id']; ?>" selected="selected"><?php echo $order_status['name']; ?></option>
                <?php } else { ?>
                <option value="<?php echo $order_status['order_status_id']; ?>"><?php echo $order_status['name']; ?></option>
                <?php } ?>
                <?php } ?>
              </select></td>
          </tr>
          <tr>
            <td><?php echo $entry_processed_status; ?></td>
            <td><select name="moneybrace_processed_status_id">
                <?php foreach ($order_statuses as $order_status) { ?>
                <?php if ($order_status['order_status_id'] == $moneybrace_processed_status_id) { ?>
                <option value="<?php echo $order_status['order_status_id']; ?>" selected="selected"><?php echo $order_status['name']; ?></option>
                <?php } else { ?>
                <option value="<?php echo $order_status['order_status_id']; ?>"><?php echo $order_status['name']; ?></option>
                <?php } ?>
                <?php } ?>
              </select></td>
          </tr>
          <tr>
            <td><?php echo $entry_geo_zone; ?></td>
            <td><select name="moneybrace_geo_zone_id">
                <option value="0"><?php echo $text_all_zones; ?></option>
                <?php foreach ($geo_zones as $geo_zone) { ?>
                <?php if ($geo_zone['geo_zone_id'] == $moneybrace_geo_zone_id) { ?>
                <option value="<?php echo $geo_zone['geo_zone_id']; ?>" selected="selected"><?php echo $geo_zone['name']; ?></option>
                <?php } else { ?>
                <option value="<?php echo $geo_zone['geo_zone_id']; ?>"><?php echo $geo_zone['name']; ?></option>
                <?php } ?>
                <?php } ?>
              </select></td>
          </tr>
          <tr>
            <td><?php echo $entry_status; ?></td>
            <td><select name="moneybrace_status">
                <?php if ($moneybrace_status) { ?>
                <option value="1" selected="selected"><?php echo $text_enabled; ?></option>
                <option value="0"><?php echo $text_disabled; ?></option>
                <?php } else { ?>
                <option value="1"><?php echo $text_enabled; ?></option>
                <option value="0" selected="selected"><?php echo $text_disabled; ?></option>
                <?php } ?>
              </select></td>
          </tr>
          <tr>
            <td><?php echo $entry_sort_order; ?></td>
            <td><input type="text" name="moneybrace_sort_order" value="<?php echo $moneybrace_sort_order; ?>" size="1" /></td>
          </tr>
        </table>
      </form>
    </div>
  </div>
</div>
<?php echo $footer; ?>

This file define the following config parameter for the moneybrace open cart payment module:

Source code    
moneybrace_merchantacct
moneybrace_cert
moneybrace_debug
moneybrace_pending_status_id
moneybrace_processed_status_id
moneybrace_geo_zone_id
moneybrace_status
moneybrace_sort_order

By now we had finished all development for the payment backend configure page. let's move forward to develop the main logic the payment module will do -- generating a form with all the payment infomation.
Below is the controller class.Make sure the class name start with ControllerPayment,and it must have  an index method to init all form element value. The class also define a callback() method, which will be used to receive and process payment result. You can name it  whatever you like,but you should told the payment gateway the correct method to call when payment is done.
[catalog/controller/payment/moneybrace.php]

Source code    
<?php
/* Moneybrace online payment
 *
 * @version 1.0
 * @date 11/03/2012
 * @author george zheng <xinhaozheng@gmail.com>
 * @more info available on mzcart.com
 */
class ControllerPaymentMoneybrace extends Controller {
	protected function index() {
		$this->language->load('payment/moneybrace');
 
		$this->data['button_confirm'] = $this->language->get('button_confirm');
 
		$this->data['action'] = 'https://payment.moneybrace.com/payment/paypage.aspx';
 
		$this->load->model('checkout/order');
 
		$order_info = $this->model_checkout_order->getOrder($this->session->data['order_id']);
 
		if ($order_info) {
		    $this->data['merchantid'] = trim($this->config->get('moneybrace_merchantacct'));
			$this->data['encoding'] = 'utf-8';
			$this->data['transtype'] = 'IC';
			$this->data['version'] = '1.0.0';
			$this->data['orderid'] = date('His') . $this->session->data['order_id'];
 
			switch($this->session->data['language']) {
				case 'de':
					 $this->data['language'] = 'de-de';
					 break;
				case 'fr':
					 $this->data['language'] = 'fr-fr';
					 break;
				case 'it':
					 $this->data['language'] = 'it-it';
					 break;
				case 'es':
					 $this->data['language'] = 'es-es';
					 break;
				case 'pt':
					 $this->data['language'] = 'pt-pt';
					 break;
				case 'jp':
					 $this->data['language'] = 'ja-jp';
					 break;
				default:
					 $this->data['language'] = 'en-us';
		    }
			$this->data['callbackurl'] = $this->url->link('payment/moneybrace/callback');
			$this->data['browserbackurl'] = $this->url->link('checkout/success');
			$this->data['accessurl'] = 'https://payment.moneybrace.coms';
			$this->data['orderdate'] = date('YmdHis');
			$this->data['currency'] = $order_info['currency_code'];
            $allowed_cur = array('USD', 'EUR', 'GBP', 'CAD', 'AUD', 'JPY');
			$currency = $_SESSION['currency'];
			if ( !in_array($currency, $allowed_cur)) {
				$currency = 'USD';
			}
 
			$this->data['orderamount'] = $this->currency->format($order_info['total'], $currency , false, false);
 
			$this->data['first_name'] = html_entity_decode($order_info['payment_firstname'], ENT_QUOTES, 'UTF-8');
			$this->data['last_name'] = html_entity_decode($order_info['payment_lastname'], ENT_QUOTES, 'UTF-8');
 
			$this->data['billemail'] = $order_info['email'];
			$this->data['billphone'] = html_entity_decode($order_info['telephone'], ENT_QUOTES, 'UTF-8');
			$this->data['billaddress'] = html_entity_decode($order_info['payment_address_1'], ENT_QUOTES, 'UTF-8');
			$this->data['billcountry'] = html_entity_decode($order_info['payment_iso_code_2'], ENT_QUOTES, 'UTF-8');
			$this->data['billprovince'] = html_entity_decode($order_info['payment_zone'], ENT_QUOTES, 'UTF-8');;
			$this->data['billcity'] = html_entity_decode($order_info['payment_city'], ENT_QUOTES, 'UTF-8');
			$this->data['billpost'] = html_entity_decode($order_info['payment_postcode'], ENT_QUOTES, 'UTF-8');
 
			$this->data['deliveryname'] = html_entity_decode($order_info['shipping_firstname'] . $order_info['shipping_lastname'], ENT_QUOTES, 'UTF-8');
			$this->data['deliveryaddress'] = html_entity_decode($order_info['shipping_address_1'], ENT_QUOTES, 'UTF-8');
			$this->data['deliverycity'] = html_entity_decode($order_info['shipping_city'], ENT_QUOTES, 'UTF-8');
			$this->data['deliverycountry'] = html_entity_decode($order_info['shipping_iso_code_2'], ENT_QUOTES, 'UTF-8');
			$this->data['deliveryprovince'] = html_entity_decode($order_info['shipping_zone'], ENT_QUOTES, 'UTF-8');
			$this->data['deliveryemail'] = $order_info['email'];
			$this->data['deliveryphone'] = html_entity_decode($order_info['telephone'], ENT_QUOTES, 'UTF-8');
			$this->data['deliverypost'] = html_entity_decode($order_info['shipping_postcode'], ENT_QUOTES, 'UTF-8');
 
			$strProducts = '';
			$htmlProducts = '';
			foreach ($this->cart->getProducts() as $product) {
			    $pname = trim(str_replace('"', '', $product['name']));
 
			    if ( $pname == '' && $strProducts == '' && $htmlProducts = '') {
				    $pname = 'Order ' . $this->data['orderid'];
					$psn = $this->data['orderid'];
					$qty = 1;
					$price = $this->currency->format($product['price'], $currency, false, false);
				    $strProducts = 'Order ' . $this->data['orderid'] . '1';
					$htmlProducts = '<input name="productname1" value="" ' . $pname . ' />' .
									'<input name="productsn1" value="" ' . $psn . '/>' .
									'<input name="quantity1" value="" ' . $qty . ' />' .
									'<input name="unit1" value="" ' . $price . '/>' ;
					break;
				}
 
				$psn = $product['model'];
				$qty = $product['quantity'];
				$price = $this->currency->format($product['price'], $currency, false, false);
 
				$strProducts =  $pname . $psn . $qty . $price;
				$htmlProducts = '<input type="hidden" name="productname1" value="' . $pname . '" />' .
								'<input type="hidden" name="productsn1" value="' . $psn . '" />' .
								'<input type="hidden" name="quantity1" value="' . $qty . '" />' .
								'<input type="hidden" name="unit1" value="' . $price . '"/>' ;
 
			}
			$this->data['htmlProducts'] = $htmlProducts;
 
			$cert = $this->config->get('moneybrace_cert');
 
			$strSource =$cert . $this->data['version'] . $this->data['encoding'] . $this->data['language'] . $this->data['merchantid'] .
			$this->data['orderid'] . $this->data['orderdate'] . $this->data['currency'] . $this->data['orderamount'] . $this->data['transtype'] .
			$this->data['callbackurl'] . $this->data['browserbackurl'] . $this->data['accessurl'] .
            $strProducts .
			$this->data['billaddress'] . $this->data['billcountry'] . $this->data['billprovince'] . $this->data['billcity'] .
			$this->data['billemail'] . $this->data['billphone'] . $this->data['billpost'] .
			$this->data['deliveryname'] . $this->data['deliveryaddress'] . $this->data['deliverycountry'] . $this->data['deliveryprovince'] .
			$this->data['deliverycity'] . $this->data['deliveryemail'] . $this->data['deliveryphone'] . $this->data['deliverypost'];
            if ($this->config->get('moneybrace_debug')) {
				$this->log->write('Submit source string:' . $strSource);
			}
			$signature = md5($strSource);
			$this->data['signature'] = $signature;
 
			if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/payment/moneybrace.tpl')) {
				$this->template = $this->config->get('config_template') . '/template/payment/moneybrace.tpl';
			} else {
				$this->template = 'default/template/payment/moneybrace.tpl';
			}
 
			$this->render();
		}
	}
 
	public function callback() {
		if (isset($this->request->post['orderid'])) {
			$order_id = trim(substr(($this->request->post['orderid']), 6));
		} else {
			die('Illegal Access');
		}
 
		$this->load->model('checkout/order');
 
		$order_info = $this->model_checkout_order->getOrder($order_id);
 
		if ($order_info) {
		    $data = array_merge($this->request->post,$this->request->get);
			foreach ($data as $key => $value) {
				${$k} = $value;
			}
 
			$product='';
			for($i=1;$i<=10;$i++) {
				if(!isset($data['productname'.$i]) || $data['productname'.$i] == '') {
					break;
				}
				$product = $product . $data['productname'.$i] . $data['productsn'.$i] . $data['quantity'.$i] . $data['unit'.$i];
			}
 
			$cert = $this->config->get('moneybrace_cert');
			$strSource = $cert . $version . $encoding . $lang . $merchantid . $transtype . $orderid .
            $orderdate . $currency. $orderamount . $paycurrency . $payamount .$remark1 . $remark2 .
            $remark3 .  $product . $shippingfee . $deliveryname . $deliveryaddress . $deliverycountry .$deliveryprovince.
            $deliverycity . $deliveryemail . $deliveryphone . $deliverypost . $transid . $transdate . $status;
 
			if ($this->config->get('moneybrace_debug')) {
				$this->log->write('Return source string:' . $strSource);
			}
 
			$getsignature=md5($strSource);
			if ( $getsignature != $signature) {
			    $order_status_id = $this->config->get('moneybrace_pending_status_id');
			    $this->model_checkout_order->confirm($order_id, $this->config->get('config_order_status_id'));
			    die('Data validate failed');
			}
 
			//payment was made succ
			if ($status == 'Y' || $status == 'y') {
			    $order_status_id = $this->config->get('moneybrace_processed_status_id');
				if (!$order_info['order_status_id'] || $order_info['order_status_id'] != $order_status_id) {
					$this->model_checkout_order->confirm($order_id, $order_status_id);
				} else {
					$this->model_checkout_order->update($order_id, $order_status_id);
				}
			}
		}
	}
}
?>

Let's take a look at the model class of the moneybrace payment module:
[catalog/model/payment/moneybrace.php]

Source code    
<?php
/* Moneybrace online payment
 *
 * @version 1.0
 * @date 11/03/2012
 * @author george zheng <xinhaozheng@gmail.com>
 * @more info available on mzcart.com
 */
class ModelPaymentMoneybrace extends Model {
  	public function getMethod($address, $total) {
		$this->load->language('payment/moneybrace');
 
		$query = $this->db->query("SELECT * FROM " . DB_PREFIX . "zone_to_geo_zone WHERE geo_zone_id = '" . (int)$this->config->get('moneybrace_geo_zone_id') . "' AND country_id = '" . (int)$address['country_id'] . "' AND (zone_id = '" . (int)$address['zone_id'] . "' OR zone_id = '0')");
 
		if ($this->config->get('moneybrace_total') > $total) {
			$status = false;
		} elseif (!$this->config->get('moneybrace_geo_zone_id')) {
			$status = true;
		} elseif ($query->num_rows) {
			$status = true;
		} else {
			$status = false;
		}
 
		$currencies = array(
			'AUD',
			'CAD',
			'EUR',
			'GBP',
			'JPY',
			'USD',
			'NZD',
			'CHF',
			'HKD',
			'SGD',
			'SEK',
			'DKK',
			'PLN',
			'NOK',
			'HUF',
			'CZK',
			'ILS',
			'MXN',
			'MYR',
			'BRL',
			'PHP',
			'TWD',
			'THB',
			'CNY',
			'TRY'
		);
 
		if (!in_array(strtoupper($this->currency->getCode()), $currencies)) {
			$status = false;
		}
 
		$method_data = array();
 
		if ($status) {
      		$method_data = array(
        		'code'       => 'moneybrace',
        		'title'      => $this->language->get('text_title'),
				'sort_order' => $this->config->get('moneybrace_sort_order')
      		);
    	}
 
    	return $method_data;
  	}
}
?>

If you want to limit the payment module only available to some order over a preset amount,or limit the payment module only available to some countries,currency, you can implement the logic here in the getMethod() method of model class.
[catalog/language/english/payment/moneybrace.php]

Source code    
<?php
/* Moneybrace online payment
 *
 * @version 1.0
 * @date 11/03/2012
 * @author george zheng <xinhaozheng@gmail.com>
 * @more info available on mzcart.com
 */
// Text
$_['text_title']    = '<img src="http://moneybrace.com/pic/mblogo.gif" alt="Moneybrace" style="border:none" />';
$_['text_reason'] 	= 'REASON';
$_['text_total']	= 'Shipping, Handling, Discounts & Taxes';
?>

[catalog/view/theme/default/template/payment/moneybrace.tpl]

Source code    
<form action="<?php echo $action; ?>" method="post">
	<input type="hidden" name="merchantid" value="<?php echo $merchantid; ?>" />
	<input type="hidden" name="orderid" value="<?php echo $orderid; ?>" />
	<input type="hidden" name="encoding" value="<?php echo $encoding; ?>" />
	<input type="hidden"  name="transtype" value="<?php echo $transtype; ?>" />
	<input type="hidden" name="language" value="<?php echo $language; ?>" />
	<input type="hidden" name="callbackurl" value="<?php echo $callbackurl; ?>" />
	<input type="hidden" name="browserbackurl" value="<?php echo $browserbackurl; ?>" />
	<input type="hidden" name="accessurl" value="<?php echo $accessurl; ?>" />
	<input type="hidden" name="orderdate" value="<?php echo $orderdate; ?>" />
	<input type="hidden" name="currency" value="<?php echo $currency; ?>" />
	<input type="hidden" name="orderamount" value="<?php echo $orderamount; ?>" />
    <?php echo $htmlProducts; ?>
	<input type="hidden" name="version" value="<?php echo $version; ?>" />
	<input type="hidden" name="billemail" value="<?php echo $billemail; ?>" />
	<input type="hidden" name="billphone" value="<?php echo $billphone; ?>" />
	<input type="hidden" name="billaddress" value="<?php echo $billaddress; ?>" />
	<input type="hidden" name="billcountry" value="<?php echo $billcountry; ?>" />
	<input type="hidden" name="billprovince" value="<?php echo $billprovince; ?>" />
	<input type="hidden" name="billcity" value="<?php echo $billcity; ?>" />
	<input type="hidden" name="billpost" value="<?php echo $billpost; ?>" />
	<input type="hidden" name="deliveryname" value="<?php echo $deliveryname; ?>" />
	<input type="hidden" name="deliveryaddress" value="<?php echo $deliveryaddress; ?>" />
	<input type="hidden" name="deliverycity" value="<?php echo $deliverycity; ?>" />
	<input type="hidden" name="deliverycountry" value="<?php echo $deliverycountry; ?>" />
	<input type="hidden" name="deliveryprovince" value="<?php echo $deliveryprovince; ?>" />
	<input type="hidden" name="deliveryemail" value="<?php echo $deliveryemail; ?>" />
	<input type="hidden" name="deliveryphone" value="<?php echo $deliveryphone; ?>" />
	<input type="hidden" name="deliverypost" value="<?php echo $deliverypost; ?>" />
	<input type="hidden" name="signature" value="<?php echo $signature; ?>" />
 
	<div class="buttons">
	<div class="right">
	    <input type="submit" value="<?php echo $button_confirm; ?>" class="button" />
	</div>
	</div>
</form>

 

 

One thought on “open cart — how to create a payment module for open cart

  1. awesome tutorial. that was quick and simple. thanks. god bless you.

    my client is currently running magento but its too heavy and too complex to run & maintain for small business like theirs. so i am migrating their current messy store to opencart. in the process, i am learning OC on-the-job and it does look easy to use / code / maintain than that messy magento software. this hands-on tutorial was really cool. thanks a tonne.

    i will be writing my own modules to customize shipping / payment / order totals / country detection & redirection / etc etc and once that is complete & live, will release the code on OC extensions directory for free, so that everyone can benefit from it. enjoy !

    thanks once again.

Leave a Reply

Your email address will not be published. Required fields are marked *

*


− 3 = four

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>