Creating Your Own Payment Gateway For Magento – Part 1

Yet another complex job explanation for people out the who are trying to figure out how they can connect their own countries’ bank payment gateways to their Magento installations. I love you guys, I understand you pain! This is a long post, so make sure you have enough time to read it and apply it. First part describes the admin panel and things like models and other things. Second part includes the definition of our own payment gateway. You might skip to the part 2 if you are familiar with these things.

Now let’s get to the point. First of all, you should install the latest version Magento. Which you can find here.

Second go to here and download Module Creator plugin. You will have to install it via Magento Connect in which you just get the hashcode from, Magento extensions page, you just enter the code, in the Magento Connect page on the admin panel. Why we need this? If you remember from my previous posts, you should know that, it’s really a pain in the ass to create all the required files and folders to get started on a module in Magento. This extension will help us create all the required files and folders, even creates a simple admin tab and you are ready to go for the development.

So, once done you should get to the http://localhost/magento/moduleCreator link and fill the form like this:

How To FIll the Module Creator Box

Always loyal to my company, I have created a namespace as Kartaca and named my module as Pos. I haven’t filled anything about layouts or themes, because I’m using the “default” layout for magento.

Once you have clicked on the “create” button, you will notice some files that have been created. Don’t bother to find out what they are, I will show you, what they are. I’m gonna split it to three parts. First the controllers and module files, second template files and third configuration files. First one:
Module Layout

Second:
adminhtml layout
frontedn-layouttemplate for pos module

And finally third:

Configuration File View

Yes, now we know what files are created. A lots of files isn’t it? I don’t think I can ever remember what to create without missing anything. So the great part is Module Creator has created both frontend and backend views and controllers for our module. We won’t have anything related to the frontend so I will not mention anything about it but while looking for the backend, you will notice that you will be able to handle frontend the same way we handle the backend.

So you should remember that, Magento is based on Zend Framework which is an MVC framework. So you have models and views and controllers and where could be these controllers? Under “controllers” folder of course. There is an IndexController.php and a adminhtml/PosController.php. As you can guess, adminhtml is for the backend and IndexController is for the frontend. When we open the file PosController.php we notice that it’s not an empty file and it already includes a “News Module” where you can add and show news.

So we already have a grid view and an edit view for our needs. Now lets define what we will gonna need for setting up a payment gateway. I will keep this short and won’t include a lot of fields, however you may need additional ones, so don’t forget to include them. What I need is this: A url, a username, a password, a bank identifier, a currency and finally something which will define if it’s enabled or not.

Pretty simplistic. So where to add these values? Check the files and folders created for the module and you will notice that, there is an sql folder in it which creates a table with the name of your module. As our module creator had created a “News Module” for us, you will notice that there are things related to the news. So let’s change the table definition to something which will fill our needs:

$installer->run("

-- DROP TABLE IF EXISTS {$this->getTable('pos')};
CREATE TABLE {$this->getTable('pos')} (
  `pos_id` int(11) unsigned NOT NULL auto_increment,
  `title` varchar(255) NOT NULL,
  `bank_url` varchar(255) NOT NULL,
  `username` varchar(100) NOT NULL,
  `password` varchar(100) NOT NULL,
  `bank_id` int(11) NOT NULL,
  `currency` VARCHAR(5) NOT NULL,
  `status` smallint(6) NOT NULL default '0',
  `created_time` datetime NULL,
  `update_time` datetime NULL,
  PRIMARY KEY (`pos_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    ");

I have changed the sql accordingly. However for these changes to take effect you must reinstall your module (or just run the script we have just written as we have not yet created a “full module”). Notice that we haven’t defined the name of the table and the name comes from somewhere else! Your guess is true, from config.xml file. When You open the file, you will find these lines:



        
            
                Kartaca_Pos_Model
                pos_mysql4
            
            
                Kartaca_Pos_Model_Mysql4
                
                    
                        pos

As you can easily understand, the name of the table defined here. You can also define additional table names like this:

            
                Kartaca_Pos_Model_Mysql4
                
                    
                        krtc_pos_drivers
krtc_pos_banks

By adding “krtc_pos” as prefix we differentiate the tables with the ones of clean magento installation. It helps us to find out what we are looking for easily on the phpMyAdmin (well, at least I’m not increasing the complexity). Of course to call the “krtc_pos_banks”, I need to make a call like: $this->getTable(“banks”).

So now we have defined our database table! However we need to have some input from the user to save something. So we need to change the form that we see when we click on the edit button. Where is it? Form is created on Kartaca/Pos/Block/Adminhtml/Edit/Form.php. However the form elements are created on Kartaca/Pos/Block/Adminhtml/Edit/Tab/Form.php. And if you are looking for how they are called see here: Kartaca/Pos/Block/Adminhtml/Edit/Tabs.php:19. If you are curious how the parsing works for: “pos/adminhtml_pos_edit_tab_form”. The part before “/” is the namespace or the module name (Kartaca_Pos for here). The part later defines the classname for the block. When we add the namespace to the name: Kartaca_Pos_Block_Adminhtml_Pos_Edit_Tab_Form (isn’t this ridicolously long:) where you can find on app/code/local/Kartaca/Pos/Block/Adminhtml/Pos/Edit/Tab/Form.php! You generally miss the Block part :)

We modify the form like this:

class Kartaca_Pos_Block_Adminhtml_Pos_Edit_Tab_Form extends Mage_Adminhtml_Block_Widget_Form
{
    protected function _prepareForm()
    {
        $form = new Varien_Data_Form();
        $this->setForm($form);
        $fieldset = $form->addFieldset('pos_form', array('legend'=>Mage::helper('pos')->__('Item information')));

        $fieldset->addField('title', 'text', array(
            'label'     => Mage::helper('pos')->__('Title'),
            'class'     => 'required-entry',
            'required'  => true,
            'name'      => 'title',
        ));

        $fieldset->addField('bank_url', 'text', array(
            'label'     => Mage::helper('pos')->__('Url'),
            'required'  => true,
            'name'      => 'bank_url',
        ));

        $fieldset->addField('username', 'text', array(
            'label'     => Mage::helper('pos')->__('Username'),
            'required'  => true,
            'name'      => 'username',
        ));

        $fieldset->addField('password', 'text', array(
            'label'     => Mage::helper('pos')->__('Password'),
            'required'  => true,
            'name'      => 'password',
        ));

        $fieldset->addField('bank_id', 'select', array(
            'label'     => Mage::helper('pos')->__('Bank Identifier'),
            'required'  => true,
            'name'      => 'bank_id',
            'values'    => Mage::getSingleton('pos/bank')->getOptionArray(),
        ));

        $fieldset->addField('currency', 'select', array(
            'label'     => Mage::helper('pos')->__('Currency'),
            'required'  => true,
            'name'      => 'currency',
            'values'    => Mage::getSingleton("pos/currency")->getOptionArray(),
        ));

        $fieldset->addField('status', 'select', array(
            'label'     => Mage::helper('pos')->__('Status'),
            'name'      => 'status',
            'values'    => Mage::getSingleton("pos/status")->getOptionArray(),
        ));

        if ( Mage::getSingleton('adminhtml/session')->getPosData() )
        {
            $form->setValues(Mage::getSingleton('adminhtml/session')->getPosData());
            Mage::getSingleton('adminhtml/session')->setPosData(null);
        } elseif ( Mage::registry('pos_data') ) {
            $form->setValues(Mage::registry('pos_data')->getData());
        }
        return parent::_prepareForm();
    }
}

You might notice two things: Currency and Bank models. We have not created any tables for them so how are we using them? Currency is defined somewhere in Magento and I want to show you how this is used. Also banks are generally something static and they don’t need to be changed via a content manager and changing them generally requires some development, so I use them as predefined values and wrap them with a model. So first the easy one, bank model:

class Kartaca_Pos_Model_Bank extends Varien_Object
{
    private $_bankList = array(
        101 => "Garanti",
        201 => "YapiKredi",
        202 => "Akbank",
        203 => "HSBC",
        //Add more static banks here...
    );

    public function getCollection()
    {
        return $this->_bankList;
    }

    public function getOptionArray()
    {
        $arr = array();
        foreach ($this->_bankList as $key => $val) {
            $arr[] = array(
                "value" => $key,
                "label" => Mage::helper("pos")->__($val),
            );
        }
        return $arr;
    }
}

What I did is pretty easy. I have defined variables and later called them using Mage::getSingleton(). I might have used Mage::getModel(). However singleton pattern is good for this one. Easy. Now let’s get to the currencies. Honestly to find out how the currencies were listed was a real pain. Another problem was to get them localized. So here is what I found. Magento treats currencies as a part of the localization process and it does not contain a list of currencies in the database. Magento has a model for the locale. Things that you need to be localized and you get the currencies that way. So our code looks something like this:

class Kartaca_Pos_Model_Currency extends Varien_Object
{
    public function getCollection()
    {
        return $this->_getTranslatedCurrencies();
    }

    public function getOptionArray()
    {
        return Mage::getSingleton("core/locale")->getOptionCurrencies();
    }

    protected function _getTranslatedCurrencies()
    {
        //Shame on you Varien, why do you ever use text for an array key???!!!!
        $badCurrencies = Mage::getSingleton("core/locale")->getTranslationList('currencytoname');
        $allowedCurrencies = Mage::getSingleton("core/locale")->getAllowCurrencies();
        $goodCurrencies = array();
        foreach ($badCurrencies as $name => $code) {
            if (in_array($code, $allowedCurrencies)) {
                $goodCurrencies[$code] = $name;
            }
        }
        return $goodCurrencies;
    }
}

So as you see there is actually no need to use this model because Magento gives us a great option for using currencies.However, as I noted in my code, why do you use currencies localized texts as the key for the array I will not understand.

As you see we have two code classes having two methods, getOptionArray() and getCollection(). I tried to stick to the naming convention on Magento. So the getOptionArray() returns an array ready to be used in select form’s value part. getCollection() returns a list of values. That’s it! I will need the getCollection() later on this how-to.

So seems like we have reached to the “save” part. As you will notice the save part is already created for us by the module creator. Great! When I click on save, everything went smoothly and I returned to the grid view. I want to see the bank names and urls and currency for the driver on the grid view. What to do now? Open up Kartaca/Pos/Block/Adminhtml/Pos/Grid.php and add these lines in the _prepareColumns() method anywhere before the return statement:

        $this->addColumn('bank_id', array(
            'header'    => Mage::helper('pos')->__('Bank Identifier'),
            'align'     => 'left',
            'width'     => '80px',
            'index'     => 'bank_id',
            'type'      => 'options',
            'options'   => Mage::getSingleton("pos/bank")->getCollection(),
        ));

        $this->addColumn('currency', array(
            'header'    => Mage::helper('pos')->__('Currency'),
            'align'     => 'left',
            'width'     => '80px',
            'index'     => 'currency',
            'type'      => 'options',
            'options'   => Mage::getSingleton("pos/currency")->getCollection(),

The order of the method calls will define the positions of the columns so add them where ever you want to put the columns.

On the grid view, you can also make some mass actions. Our module creator has already given us the mass status changing code example. So let’s go and mass change the currency info for all the drivers. So on the same file, we add something like this below the massStatus add block on _prepareMassaction():

$this->getMassactionBlock()->addItem('currency', array(
            'label'=> Mage::helper('pos')->__('Change Currency'),
            'url'  => $this->getUrl('*/*/massCurrency', array('_current'=>true)),
            'additional' => array(
            'visibility' => array(
            'name' => 'currency',
            'type' => 'select',
            'class' => 'required-entry',
            'label' => Mage::helper('pos')->__('Currency'),
            'values' => Mage::getSingleton("pos/currency")->getOptionArray(),
            )
            )
        ));

Now that we have finally created the select button we must also do something on the controller. Now open the controllers/Adminhtml/PosController.php and add this method:

    public function massCurrencyAction()
    {
        $posIds = $this->getRequest()->getParam('pos');
        if(!is_array($posIds)) {
            Mage::getSingleton('adminhtml/session')->addError($this->__('Please select item(s)'));
        } else {
            try {
                foreach ($posIds as $posId) {
                    $pos = Mage::getSingleton('pos/pos')
                        ->load($posId)
                        ->setCurrency($this->getRequest()->getParam('currency'))
                        ->setIsMassupdate(true)
                        ->save();
                }
                $this->_getSession()->addSuccess(
                    $this->__('Total of %d record(s) were successfully updated', count($posIds))
                );
            } catch (Exception $e) {
                $this->_getSession()->addError($e->getMessage());
            }
        }
        $this->_redirect('*/*/index');
    }

This, as you can guess, is copy-paste from the massStatusAction(). I just changed the name and getModel() part.

So It seems like we have finished the admin part. Now let’s get to the real part! What should we do to overwrite or add our own payment gateway? Follow me on the second part!

If there is anything misunderstood or unclear or wrong code example, please point that. I might have missed something.

Update: I have uploaded the code for this module to my github account here. You can download the full code there.