Creating Your Own Payment Gateway For Magento – Part 3

Well, after nearly a year, here comes the third part for the Magento payment gateway. If you have already forgotten about the previous two parts, go check here and here. What I will discuss here is how you can save your custom fields to the database and show them on the admin order page. Still I won’t get into the details of the payment as it’s pretty domain specific.

I have worked on Magento 1.4.0.1 for this and you can download the latest version here. As always you will be able to download the codes from my github, but give some time for me to commit them (need time to clean the code from private stuff etc).

What we had done this far was that we had created our payment gateway and everything was fine with it. We had taken our specific information from $_POST which was not the best way. So what we should do is to find a way, to integrate this thing with Magento. What we will use is the observers and events!

Before moving to the events and observers, we should mention where and how these new attributes of the order will be saved. I don’t know if you have ever opened the phpMyAdmin for Magento but it’s very easy to get lost between the tables, so it’s not very easy to create two more fields to the order table. Because order table is split on 5 or 6 different tables, each having different properties. So we should find and automated way to this for us. We should first edit our MySQL installation script.

startSetup();

// Get ID of the entity model 'sales/order'.
$sql = 'SELECT entity_type_id FROM '.$this->getTable('eav_entity_type').' WHERE entity_type_code="order"';
$row = Mage::getSingleton('core/resource')
         ->getConnection('core_read')
	     ->fetchRow($sql);

// Create EAV-attribute for the order, bank id and installment
//These two fields are new for our setup script.
$c = array (
  'entity_type_id'  => $row['entity_type_id'],
  'attribute_code'  => 'pos_bank_id',
  'backend_type'    => 'decimal',     // MySQL-Datatype
  'frontend_input'  => 'text', // Type of the HTML form element
  'is_global'       => '1',
  'is_visible'      => '1',
  'is_required'     => '0',
  'is_user_defined' => '0',
  'frontend_label'  => 'Bank Id',
);
$attribute = new Mage_Eav_Model_Entity_Attribute();
$attribute->loadByCode($c['entity_type_id'], $c['attribute_code'])
          ->setStoreId(0)
          ->addData($c);
$attribute->save();

$c = array (
  'entity_type_id'  => $row['entity_type_id'],
  'attribute_code'  => 'pos_installment',
  'backend_type'    => 'decimal',     // MySQL-Datatype
  'frontend_input'  => 'text', // Type of the HTML form element
  'is_global'       => '1',
  'is_visible'      => '1',
  'is_required'     => '0',
  'is_user_defined' => '0',
  'frontend_label'  => 'Installment',
);
$attribute = new Mage_Eav_Model_Entity_Attribute();
$attribute->loadByCode($c['entity_type_id'], $c['attribute_code'])
          ->setStoreId(0)
          ->addData($c);
$attribute->save();

//Now create the required table...
//Remember these code block had already been created in the first part.
$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;

");

$installer->endSetup();

What have we done with this script apart from creating our own table)? We have created custom fields for our custom order information. Notice that there is no “ALTER TABLE” command. There are only inserts. I guess we should mention how the magento keeps its table and their fields. There are like 5-6 tables for one table only. For different MySQL variable types (int, date, varchar) a table is created and values related to the specific table is inserted their. Each table has a fields table. Fields of an enitity are rows and not columns. This gives us a flexibility to add/remove new columns. Giving each module a chance to attach its own fields to the appropriate entity they want. And we want to attach these fields to the order model. So we get its entity_type_id and assign our fields to it.

Beware that if the changes haven’t taken any effect, you might want to reinstall the module. For this, find the table with name core_resources and delete the our module’s entry there. Once you run the page again, installer script will be executed too.

So as we have understood the basics of Magento’s table architecture. we can go on with the actual subject of observers and events. What are the observers and events? Deeply: Check Observer Pattern. Simply: During the flow of code, many events occur. Some of the events, notify it’s watchers and triggers these watcher’s code blocks. You can write your custom codes to these watchers also known as observers. An another name to events can be hooks if you are familiar with WordPress development. What we will do is this: We will hook up to the save order event and save our custom data to the order only when the event is triggered. Don’t worry it’s not that difficult.

As always open up your root/app/code/local/Kartaca/Pos/etc/config.xml and add these lines to their appropriate place:



    
    
        
          
            
              
                model
                Kartaca_Pos_Helper_Data
                setPosInformations
              
            
          
        
    
    

As you see I have defined a class and a method to occur for a specific event. Now of course we have to create this class in the root/app/code/local/Kartaca/Pos/Helper/Data.php:


_getRequest()->getPost("pos_bank_id"); //Name of the form element in the previous parts might have been different, the same. that they are the same.
        $observer->getEvent()->getOrder()->setPosBankId($accId);
        
        $installment = $this->_getRequest()->getPost("pos_installment");
        $observer->getEvent()->getOrder()->setPosInstallment($installment);
    }
}

Still there is nothing complicated. We have taken the observer, fetched the getOrder() and set the attributes we had attached during the mysql installation script. What you should notice that how I found out the getPosBankId() syntax. That’s easy too. Every “_” (underscore) character will be deleted and be followed with an uppercase character. So pos_bank_id will be renamed too posBankId and as according to the camelCase coding standard, after the get command the first character must be uppercase too. So finally we get getPosBankId. The same goes for set method and it will be: setPosBankId.

So we have finally added these fields to the order. How we will be able to reach them on the payment part then? Let’s get to our root/app/code/local/Kartaca/Model/Payment.php and add modify some of the lines as:


//Some many codes here...

public function authorize(Varien_Object $payment, $amount)
{
        $orderId = $payment->getOrder()->getIncrementId();
        try {
            $paymentValues = array("cardType" => $payment->getCcCid(),
                                   "expiresMonth" => $payment->getCcExpMonth(),
                                   "expiresYear" => $payment->getCcExpYear(),
                                   "cardHolderName" => $payment->getCcOwner(),
                                   "cardNumber" => $payment->getCcNumber(),
                                   "amount" => $amount,
                                   "orderId" => $orderId,
                                   "bankId" => $payment->getOrder()->getPosBankId(), //Notice how I use the get methods?
                                   "installment" => $payment->getOrder()->getPosInstallment(),
                                   "username" => "my_bank_username", //FIXME: We must change these hard coded lines..
                                   "password" => "my_secret_password_generally_not_that_secret",
                                   "clientid" => "my_clientid_given_to_me_by_the_bank",
                                  );
       //Some more and more code will come here...
       if (!$isPaymentAccepted) {//Always throw an error otherwise your payment will be accepted even if it's declined...
           $payment->setStatus(self::STATUS_ERROR);
           Mage::throwException(new Exception($result->message));
       }
       return $this;
}

Everything seems clear, we have only added the parts related to the new fields. Well so is everything done? Not yet! Actually you are done with saving everything to the database once the order is approved (when you see the payment accepted page) however, you should be able to see it on the administration screen right? So let’s create the admin html. Create a file as root/app/code/local/Kartaca/design/adminhtml/default/default/template/pos/payment/info/pos.phtml and add these lines in it:


__('Name on the Card: %s', $this->htmlEscape($this->getInfo()->getCcOwner())) ?>
__('Credit Card Type: %s', $this->htmlEscape($this->getCcTypeName())) ?>
getIsSecureMode()):?> __('Credit Card Number: xxxx-%s', $this->htmlEscape($this->getInfo()->getCcLast4())) ?>
__('Credit Card Number: %s', $this->htmlEscape($this->getInfo()->getCcNumber())) ?>
__('Expiration Date: %s/%s', $this->htmlEscape($this->getCcExpMonth()), $this->htmlEscape($this->getInfo()->getCcExpYear())) ?>
getInfo()->getOrder()->getPosBankId(); $_bank = Mage::getModel("pos/pos")->load($_bankId); //Easy as a pie right? ?> __('Bank Name: %s', $_bank->getDisplayName() . " (" . $_bank->getId() . ")") ?>
__('Installment: %d', $this->getInfo()->getOrder()->getPosInstallment()) ?>

And now you should be good to go. Sometimes, adminhtml files in your code pool won’t work. You might have to (I had to) copy and paste the contents of the file to the root/app/design/adminhtml/default/default/template/pos/payment/info/pos.html.

You will notice that your custom info will be together with the credit card info etc. You can change its place if you want but you should also change where the file stands in the folder structure.

So I guess this is everything. You might notice that there are some hard coded parts in the Payment model, we must have some configuration for our module so we should be able to define these somewhere in the admin panel. Of course we could have defined these values in the config.xml and then use them from there, however this will not give us the flexibility to change it from admin page. I will explain this in another blog but it won’t be the 4th part :)

Update: I have uploaded the code to the github. You might check it out from there.