Creating Your Own Payment Gateway For Magento – Part 2

So in my previous blog, I had mentioned about an admin panel and basic configuration for creating a simple module. This is the second part and I will try to explain you how you can implement your own payment gateway and getting the values we have set on the database. If you have not read the first part, go take a look.

So we have three things to do now: First we should create something which will handle the payment, define the “thing” we have done in the configuration and finally an html file which will hold specific forms for us. However the third one is optional.

So first let’s get to the configuration file. As you know remember our moduleCreator had already created an etc/config.xml file for us. So we open it up and add these lines under the config node:

    
        
            
                1 
                pos/payment
                1
                Kartaca Payment Gateway
                
                VI,MC
                authorize 
                0
            
        
    

And also add these lines under global/blocks node (we will need this one later to customize the from seen):

            
                Kartaca_Pos_Block
                Mage_Payment_Block
            

There are two classes because if you only define one, you won’t be able to reach the other namespace!

This part is for the curious ones who wants to learn how I have found out these, for others, just get to the next paragraph. Well, open up your file browser, and go to the /app/code/core/Mage. Make your best bet on finding the module related to the “payment”!. As all the modules created by the Varien itself are modules too, like all 3rd party modules including this one, they also must have configuration files. Open up /app/code/core/Mage/Payment/etc/config.xml. You will notice first the blocks/payment part defined. This is where the second part of the config modification is coming. I should define my own blocks to the payment blocks! This was what I did. Secondly, you will notice the default part with exactly the same things we have done. Defining some default values. For more curios people; “ccsave”, “checkmo” and others like this are equivalent to the “pos” and of course, $_code variable for the class below. You should also notice that the payment method is defined in a “model” node. That’s interesting. When I said, “pos/payment” I have pointed to a class named Kartaca_Pos_Model_Payment. “pos” was the namespace defining Kartaca_Pos, Model came from the “model” node and of course, Payment came from the “payment”.

Magento approches the payment methods as “models” too. So we should define our own payment method as a model. So we create a file in Kartaca/Pos/Model/Payment.php:

/**
 * Notice we are not creating a totally new payment type, but extend the original Credit card payment
 * To write something totally new, you should extend from the Abstract one!
 */
class Kartaca_Pos_Model_Payment extends Mage_Payment_Model_Method_Cc
{
    protected $_isGateway = true;
    protected $_canAuthorize = true;
    protected $_canUseCheckout = true;

    protected $_code = "pos";
    protected $_formBlockType = 'pos/payment_form_pos';

    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,
                                  );
            //FIXME: Find a way to define this part in the $payment object which is Magento_Sales_Info or something like that.                      
            $bankid = $_POST['bank_id']; //FIXME: Do not forget to sanitise this...
            if ($bankid == 12) { //Different banks...
                $paymentValues['username'] = "my_bank_username";
                $paymentValues['password'] = "my_secret_password_generally_not_that_secret";
                $paymentValues['clientid'] = "my_clientid_given_to_me_by_the_bank";
            } else if ($bankid == 14) { //... can require different values to be sent to them
                $paymentValues['username'] = "my_second_bank_username";
                $paymentValues['password'] = "my_secret_password_generally_not_that_secret";
                $paymentValues['clientid'] = "my_clientid_given_to_me_by_the_bank";
                $paymentValues['additionalSecondBankField'] = "additional_info";
            } else {
                throw new Exception("Invalid bankid: $bankid");
            }
            //Define the url where I'm making the request...                      
            $urlToPost = "https://my.bank.com/pos/service/address/";
            //Now Create the request which I will send via Post Method...
            //Create a string like: cardType=VI&expiresMonth=12&expiresYear=2011&amount=100.50
            $postData = "";
            foreach ($paymentValues as $key => $val) {
                $posData .= "{$key}=" . urlencode($val) . "&";
            }
            //Let's create a curl request and send the values above to the bank...
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $urlToPost);
            curl_setopt($ch, CURLOPT_TIMEOUT, 180);
            curl_setopt($ch, CURLOPT_HEADER, false);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); //Put the created string here in use...
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            $data = curl_exec($ch); //This value is the string returned from the bank...
            if (!$data) {
                throw new Exception(curl_error($ch));
            }
            $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if ($httpcode && substr($httpcode, 0, 2) != "20") { //Unsuccessful post request...
                throw new Exception("Returned HTTP CODE: " . $httpcode . " for this URL: " . $urlToPost);
            }
            curl_close($ch);
        } catch (Exception $e) {
            $payment->setStatus(self::STATUS_ERROR);
            $payment->setAmount($amount);
            $payment->setLastTransId($orderId);
            $this->setStore($payment->getOrder()->getStoreId());
            Mage::throwException($e->getMessage());
        }
        /*
         * Data outputted from the curl request
         *  is generally an xml string.
         * Assume that it is something like:
         *
         * 
         *   1
         *   1234233241
         * 
         *
         * However no bank response is never this simple by the way...
         * But this one gives you a general view of the thing.
         */
        $xmlResponse = new SimpleXmlElement($data); //Simple way to parse xml, Magento might have an equivalent class
        $isPaymentAccepted = $xmlResponse->isPaymentAccepted == 1;
        if ($isPaymentAccepted) {
            $this->setStore($payment->getOrder()->getStoreId());
            $payment->setStatus(self::STATUS_APPROVED);
            $payment->setAmount($amount);
            $payment->setLastTransId($orderId);
        } else {
            $this->setStore($payment->getOrder()->getStoreId());
            $payment->setStatus(self::STATUS_DECLINED);
            $payment->setAmount($amount);
            $payment->setLastTransId($orderId);
        }
        return $this;
    }
}

So this code block is modified from the parent. By doing these things, you have already created your own payment gateway! When user selects this payment type (disable the rest of them and only enable this one, so they can only select this!), authorize method will be called and you can change the whatever way you like it! I’m not getting on the details on using of curl or Zend_Soap.

However, don’t forget that we also had a bank_id thing. We also want to send this info or save this info somewhere in the magento. What then? When I select the Kartaca Payment Gateway, I do not see the bank information selector. Ww should change the form and add some files and lines of code of course! First let’s create a file in /app/design/frontend/default/default/template/pos/payment/form/pos.phtml (some directories should already be created by the module creator):


getMethodCode() ?>

This part is copy-paste from /app/design/frontend/default/default/template/payment/form/cc.phtml. However I have removed the card holder name which I did not require and also added a bank_id select box. Ah did you notice the method getBankAvailableTypes()? Well what now? First let’s get back to our payment class and define some required info. Open it up and add an attribute like this:

class Kartaca_Pos_Model_Payment extends Mage_Payment_Model_Method_Cc
{
    protected $_isGateway = true;
    protected $_canAuthorize = true;
    protected $_canUseCheckout = true;
    protected $_code = "pos";
    //The lines above are already defined...

    protected $_formBlockType = 'pos/payment_form_pos'; //Another file? Yes, sadly!
//more code...

So if you have already read my blog about customizing magento cms pages with custom php code or really understood the part 1, a bright light bulb should be seen around your head. If not, let me explain you. We have changed the “block” called by the method and now our “block” will be called by our payment class and the value “pos/payment_form_pos” defines the class: Kartaca_Pos_Block_Payment_Form_Pos!

We create it like this:

class Kartaca_Pos_Block_Payment_Form_Pos extends Mage_Payment_Block_Form_Cc
{
    protected function _construct()
    {
        parent::_construct();
        //Where do you think the association with phtml and classes were made?
        $this->setTemplate('pos/payment/form/pos.phtml'); //This time "/" does not define the namespace but file system!
    }
    //So we found the missing method right!
    public function getBankAvailableTypes()
    {
        return Mage::getSingleton("pos/bank")->getCollection(); //Alien? Read the part 1!
    }
}

Finally you will be able to see the bank selector there too! Notice that you won’t be able to call it via $payment->getBankId(). You should either change the Order model used (Mage_Sales_Order_Payment) by extending it and adding it to the config file. Or just use $_POST. I might get into the details on a third part. However, this is all the time I can spare right now (I have used my whole two days to write these articles and develop the module:)

Hope that helps. If you need some help about Magento or payment gateways, feel free to contact me or post a comment. If you require support and want to pay for it (I doubt you want to pay however:) please send an email to me or post a comment and someone from my company will get in touch with you.

Update: I have uploaded the code for this module to my github account here. You can download the full code there.
Update: I have updated the code for the payment part. I have added a part with a simple bank request. I have updated the code on the github to. There can be some errors on the code so feel free to point them as I may have missed something.