Create Custom Payment Method – API Based

img
In this tutorial, we will see how to create payment method in magento which is api based. API based means, we will directly integrate authorize, capture, refund etc with the payment gateway using the gateway api’s.

Before going through this tutorial, please read the basics of setting up a payment method here. I will add-on to the previous post itself and not cover the basics of payment method setup. Also, the name of module used for the payment method is Excellence_Pay and the payment method code is ‘pay’.
Attached is the source code of the module
Module Name: Payment Method API Based

Direct Credit Card

First we will see the direct payment method, i.e when credit card information is directly collected on the website and there is no redirection to payment gateway website.
To setup this, we will have to show a credit card form in the checkout page, and save all credit card information to order. Then we will to authorize or capture payment depending on the gateway api’s.

Credit Card Form
First, step is show a credit card form where customer will enter the credit card information on the checkout page. For this, magento already has an inbuilt block and template files which we can use directly in our payment method. So to include a block in payment method, we need to put in code our payment method model file i.e Excellence_Pay_Model_Pay

protected $_formBlockType = 'pay/form_pay'; 

here ‘pay/form_pay’ is path to the block class. The code written inside block class is

<?php
class Excellence_Pay_Block_Form_Pay extends Mage_Payment_Block_Form_Ccsave
{
}

This simple extends the default block class of magento which has code to display the credit card form.

Checkout Payment Step - Credit Card Info

Checkout Payment Step - Credit Card Info


This block which we extended is also integrated with system -> configuration settings automatically. The credit card types to show and cvv fields are configurable from admin settings, and can be configured from admin. All you need to do is add this xml code in your existing system.xml file

<cctypes translate="label">
    <label>Credit Card Types</label>
    <frontend_type>multiselect</frontend_type>
    <source_model>adminhtml/system_config_source_payment_cctype</source_model>
    <sort_order>4</sort_order>
    <show_in_default>1</show_in_default>
    <show_in_website>1</show_in_website>
    <show_in_store>0</show_in_store>
    <can_be_empty>1</can_be_empty>
</cctypes>
<useccv translate="label">
    <label>Request Card Security Code</label>
    <frontend_type>select</frontend_type>
    <source_model>adminhtml/system_config_source_yesno</source_model>
    <sort_order>5</sort_order>
    <show_in_default>1</show_in_default>
    <show_in_website>1</show_in_website>
    <show_in_store>0</show_in_store>
</useccv>

Adding this code to system.xml will add the fields as shown below

Credit Card Block System Configuration

Credit Card Block System Configuration


So you can select from here in admin, which credit card types to display and also weather to show cvv number or not.
Now the display of the form has been setup, next we need to save these form fields to database so that we can get the credit information etc when required. Doing this is also very simple. All you need to do is that you payment method module file should extend Mage_Payment_Model_Method_Cc i.e

class Excellence_Pay_Model_Pay extends Mage_Payment_Model_Method_Cc
{

All function to start the credit fields to database are already defined in the class Mage_Payment_Model_Method_Cc so you don’t need to worry about them. If you want to see how its done, simple open the class Mage_Payment_Model_Method_Cc, and look at the assignData(), prepareSave() and validate() functions.
There is just one important thing to know here, there is variable $_canSaveCc, which we can define in our payment method class. If the value of $_canSaveCc = false, then credit card information is not stored in database at all, but if $_canSaveCc = true then the credit card number is stored in ‘sales_quote_flat_payment’ table in encoded form. It’s best not to store the credit card information in database as it might be a security risk, so $_canSaveCc should be false unless required.
Not saving credit card information in db doesn’t mean you want be able to access it in your authorize and capture api’s. So you dont’ need to worry about that, magento is still able to give you the credit card number in these api calls. We will see this later how its done.
Next let us see how to implement the authorization in magento.
Payment Authorization
Authorize is the step which is there in all payment gateway api and this method is use to authorize a transaction through the credit card. You can read more details here on how a payment gateway work and what authorization actually means. In magento authorization flow works like this

Magento Authorization Flow

Magento Authorization Flow


So here is code you need to enable authorization, in your system.xml file add this code inside your payment method group

 <payment_action translate="label">
    <label>Payment Action</label>
    <frontend_type>select</frontend_type>
    <source_model>paygate/authorizenet_source_paymentAction</source_model>
    <sort_order>15</sort_order>
    <show_in_default>1</show_in_default>
    <show_in_website>1</show_in_website>
</payment_action>

After adding this you see a drop in System -> Configuration to select the payment action, select Authorize from there. Attached is screenshot of how it will look

Payment Action

Payment Action


Next in the payment method model file (Excellence_Pay_Model_Pay) need to add variable

protected $_canAuthorize              = true;

Then define the authorize function in our payment method class

public function authorize(Varien_Object $payment, $amount)
	{
		$order = $payment->getOrder();
		$result = $this->callApi($payment,$amount,'authorize');
		if($result === false) {
			$errorCode = 'Invalid Data';
			$errorMsg = $this->_getHelper()->__('Error Processing the request');
		} else {
			Mage::log($result, null, $this->getCode().'.log');
			//process result here to check status etc as per payment gateway.
			// if invalid status throw exception

			if($result['status'] == 1){
				$payment->setTransactionId($result['transaction_id']);
				$payment->setIsTransactionClosed(0);
				$payment->setTransactionAdditionalInfo(Mage_Sales_Model_Order_Payment_Transaction::RAW_DETAILS,array('key1'=>'value1','key2'=>'value2')); //use this in case you want to add some extra information
			}else{
				Mage::throwException($errorMsg);
			}

			// Add the comment and save the order
		}
		if($errorMsg){
			Mage::throwException($errorMsg);
		}

		return $this;
	}

//right now this function has sample code only, you need put code here as per your api.
private function callApi(Varien_Object $payment, $amount,$type){

		//call your authorize api here, incase of error throw exception.
		//only example code written below to show flow of code

		/*
		 $order = $payment->getOrder();
		$types = Mage::getSingleton('payment/config')->getCcTypes();
		if (isset($types[$payment->getCcType()])) {
		$type = $types[$payment->getCcType()];
		}
		$billingaddress = $order->getBillingAddress();
		$totals = number_format($amount, 2, '.', '');
		$orderId = $order->getIncrementId();
		$currencyDesc = $order->getBaseCurrencyCode();

		$url = $this->getConfigData('gateway_url');
		$fields = array(
				'api_username'=> $this->getConfigData('api_username'),
				'api_password'=> $this->getConfigData('api_password'),
				'customer_firstname'=> $billingaddress->getData('firstname'),
				'customer_lastname'=> $billingaddress->getData('lastname'),
				'customer_phone'=> $billingaddress->getData('telephone'),
				'customer_email'=> $billingaddress->getData('email'),
				'customer_ipaddress'=> $_SERVER['REMOTE_ADDR'],
				'bill_firstname'=> $billingaddress->getData('firstname'),
				'bill_lastname'=> $billingaddress->getData('lastname'),
				'Bill_address1'=> $billingaddress->getData('street'),
				'bill_city'=> $billingaddress->getData('city'),
				'bill_country'=> $billingaddress->getData('country_id'),
				'bill_state'=> $billingaddress->getData('region'),
				'bill_zip'=> $billingaddress->getData('postcode'),
				'customer_cc_expmo'=> $payment->getCcExpMonth(),
				'customer_cc_expyr'=> $payment->getCcExpYear(),
				'customer_cc_number'=> $payment->getCcNumber(),
				'customer_cc_type'=> strtoupper($type),
				'customer_cc_cvc'=> $payment->getCcCid(),
				'merchant_ref_number'=> $order->getIncrementId(),
				'currencydesc'=>$currencyDesc,
				'amount'=>$totals
		);

		$fields_string="";
		foreach($fields as $key=>$value) {
		$fields_string .= $key.'='.$value.'&';
		}
		$fields_string = substr($fields_string,0,-1);
		//open connection
		$ch = curl_init($url);
		//set the url, number of POST vars, POST data
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		curl_setopt($ch, CURLOPT_POST,1);
		curl_setopt($ch, CURLOPT_POSTFIELDS,$fields_string);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION ,1);
		curl_setopt($ch, CURLOPT_HEADER ,0); // DO NOT RETURN HTTP HEADERS
		curl_setopt($ch, CURLOPT_RETURNTRANSFER ,1); // RETURN THE CONTENTS OF THE CALL
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120); // Timeout on connect (2 minutes)
		//execute post
		$result = curl_exec($ch);
		curl_close($ch);
		*/

		return array('status'=>1,'transaction_id' => time() , 'fraud' => rand(0,1));
	}

As you can see above, what happens in the authorize function is
1. call the authorize api from payment gateway
2. check if transaction was successful or rejected
3. if transaction is rejected, throw exception
4. if transaction is successful, create order and save transaction id.

Calling the authorize function creates an order in magento with status = Processing. Plus transaction is registered into magento with the transaction id. Order comment is added automatically for authorization. No invoice is created.

Payment Capture
Some payment gateways, offer direct authorize and capture itself. So to implement that in magento we need to make the following changes. First from System -> Configuration, in the settings for the payment method you need to select the ‘Authorize and Capture’ option.
Next in your payment gateway model file you need to add this code

protected $_canCapture              = true;

this tell magento that the payment gateway can do capture. Next we need to add the capture() function in the payment gateway where we can call the capture api.

/** For capture **/
	public function capture(Varien_Object $payment, $amount)
	{
		$order = $payment->getOrder();
		$result = $this->callApi($payment,$amount,'authorize');
		if($result === false) {
			$errorCode = 'Invalid Data';
			$errorMsg = $this->_getHelper()->__('Error Processing the request');
		} else {
			Mage::log($result, null, $this->getCode().'.log');
			//process result here to check status etc as per payment gateway.
			// if invalid status throw exception

			if($result['status'] == 1){
				$payment->setTransactionId($result['transaction_id']);
				$payment->setIsTransactionClosed(1);
				$payment->setTransactionAdditionalInfo(Mage_Sales_Model_Order_Payment_Transaction::RAW_DETAILS,array('key1'=>'value1','key2'=>'value2'));
			}else{
				Mage::throwException($errorMsg);
			}

			// Add the comment and save the order
		}
		if($errorMsg){
			Mage::throwException($errorMsg);
		}

		return $this;
	}

Calling the capture function creates an order in magento with status = Processing. Plus transaction is registered into magento with the transaction id. Order comment is added automatically for authorization. Invoice is also created with status = Paid.

Refund
If you payment gateway supports refunds, you can integrate online refunds through magento. Online refund is possible through magento if invoice has been paid through magento itself, which means in technical terms: Transaction object exists for that invoice with a transaction id. You can do refunds only on invoices in magento, to do refund open an invoice and the click on Credit Memo button. You can do both partial refund and full refund in magento, but for now lets see how to do full refund.

First to enable refunds in your payment method, you need to add this code

protected $_canRefund               = true;

There are two types of refunds in magento, Online Refund and Refund. You need to do online refund for doing refund from payment gateway. When refund is done from admin 3 functions are called in your payment method model class in order

public function processBeforeRefund($invoice, $payment){} //before refund
public function refund(Varien_Object $payment, $amount){} //refund api
public function processCreditmemo($creditmemo, $payment){} //after refund

In the refund() you need to call your payment gateways refund api, here is a sample code.

public function refund(Varien_Object $payment, $amount){
		$order = $payment->getOrder();
		$result = $this->callApi($payment,$amount,'refund');
		if($result === false) {
			$errorCode = 'Invalid Data';
			$errorMsg = $this->_getHelper()->__('Error Processing the request');
			Mage::throwException($errorMsg);
		} 
		return $this;

	}

Redirect Based Method

This is another way in which payment gateway’s offer integration, i.e when user click on checkout he is redirected to they payment gateway website where he completes the payment. Usually in such method what happens is that, payment gateway give a form code which needs to be submitted to a payment gateway url. To accomplish this what needs to be done is in the payment method model file you need to add the function

public function getOrderPlaceRedirectUrl()
{
		if((int)$this->_getAmount() > 0){
			return Mage::getUrl('pay/index/index', array('_secure' => true));
		}else{
			return false;
		}
}

what this does is return’s the url to the controller pay/index/index. When the checkout process is a sucess, magento will automatically redirect the page pay/index/index. In this page you need to load a phtml file with the form code as given by you payment gateway and prefil it using the order details. This prefilled form needs to be automatically submitted using javascript. When the form is submitted it will automatically redirect to the payment gateway and there payment can be completed. After payment is completed or reject, it comes back to sucess or error page where we update the order status appropriately.

These are all the basics related to payment gateway integration in magento. I have attached module will full source code, in the module attached an example of CC-Avenue gateway has been taken for the Redirect Based Method (It’s only a sample code).

Using in Admin for Creating Order or Reorder

To use your payment gateway in admin for creating new orders or reorder, you can do this in magento as setting up $_canUseInternal to true in your model.

protected $_canUseInternal = true;

If you are using a different template for your payment gateway then you need to put the same template file in adminhtml of your design directory which may reside at app/design/adminhtml/base/default/pay/form.phtml.

Magento Transactions

While viewing the order details in admin you will see a tab Transactions which are meant for reoccurring billing profiles and they don’t have a general role in your flow and are only important if you need to implement partial payments or reoccurring payments.
It is required when we need to void/cancel a transaction online.

So here in our code:

if($result['status'] == 1){
    $payment->setTransactionId($result['transaction_id']);
    $payment->setIsTransactionClosed(1);
    $payment->setTransactionAdditionalInfo(Mage_Sales_Model_Order_Payment_Transaction::RAW_DETAILS, array('key1'=>'value1','key2'=>'value2'));
}

there are three things to note

  1. setTransactionId – setting transaction id returned from gateway
  2. isTransactionClosed – if transaction is complete then close it.
  3. setTransactionAdditionalInfo – is most useful for read-only attributes attributes attributes

The above code should be written in authorize(), capture() and refund() on success of transaction to make an entry in sales_payment_transaction table.

  • Tariq Rahman

    Excellent tutorial – thanks for sharing this!

    • satish choudhari

      Hi Manish,
      The page is not redirecting to the gateway url….can you pls ..help..

  • sireesha Chilakamarri

     Is it possible to add a credit card to a customer account using SOAP API?

  • http://www.facebook.com/ryanflores9 Ryan Flores

    This is a great tutorial.  However your code and descriptions for Authorize are not accurate.  They reflect information regarding Capture.  Also, the Order Status can be controlled in config.xml.  So it won’t necessarily be “Processing”. Please correct these to resolve confusions that your visitors may encounter.

    • http://www.facebook.com/people/Eric-Hileman/100003087726335 Eric Hileman

      Ryan’s right. Those auth / cap methods are all kinds of screwy. Otherwise it’s a good tut!

  • http://www.facebook.com/pdillu Dhilip Kumar

    Hello Prakash

    Thanks for Nice tutorials in support for Magento Developers.
    Tutorials are excellent to use and improve the skills.

    I have a requirement but no where I found the solution, so I’m posting it here, Please support me…

    My requirement, I want to allow customers with duplicate E-mail Id’s.
    Means same email id can be used by more customers. I’m using a custom module for login with user name.

    Its a very urgent requirement and I’m a very new bee to zend and magento as well, I believe I can expect this article soon.

  • Magento Themes

    would it be less trouble some than using any onilne service…

  • http://twitter.com/thomasvs thomasvs

    Can you rename your first ‘capture’ method to ‘authorize’ and set _canAuthorize to true instead? Right now it’s confusing to people.

  • D. Rafique Black

    Fatal error: Call to a member function getStreet() on a non-object in <— I'm getting this error!?!?

  • http://twitter.com/daniel_rafique Daniel Rafique

    Thankyou for sharing this tutorial. I have a problem with this module not showing in the checkout page? Do I need to edit any paths to use it for redirect?

  • http://twitter.com/daniel_rafique Daniel Rafique

    Got it showing but the wrong form is showing: default/template/payment/form/ccsave.phtml

  • Manish Prakash

    Hello Neetesh,
    You need to login via facebook for downloading this file. If then also you are having problem with download revert me back.

  • suki

    How can I download the source????????

    • Manish Prakash

      Just login with Facebook.

  • http://twitter.com/ThatzYourJob ☁ALBERT NINYEH Jr☁™

    Great tutorial there Mr. Manish Prakash. I have a real API which i would like to send to you so you could demonstrate it to other who want to learn as well. Please let me know if you’re interested. Thanks

    • Manish Prakash

      Hi Albert,

      Ok Please send me .

      Thanks

      • Er Suresh Sanghvi

        Hello Manish ,

        Can you please send me the real api as Albert send to you ?
        Please send me on below id : sanghvi9891@gmail.com

        Hope you give me response asap .

        Thanks .

  • Antonio Madrigal

    Nice tuto!

    I need some help, im using the authorize function but i dont know how to show the transaction message!

    What i mean its to show to the user, if success -> the transaction id and some message!

    if not the error message, right now its only showing the normal order message!

    Thank you for your purchase!
    Your order # is: 100000024.
    You will receive an order confirmation email with details of your order and a link to track its progress.

    can anyone help me, thanks

    • Manish Prakash

      Hi Antonio,

      On basis of status message for payment gateway, set variable in registry to get status of payment and on basis of that show error or success message.

      Or also you can set seperate url of error .

      Thanks

  • Antonio Madrigal

    How can i redirect to “checkout/onepage/failure” instead sending a message to the user? (throw exception), i have been trying with the code

    $this->_redirect(“checkout/onepage/failure”);

    but isnt working! please help!

  • vivek

    Hello Manish,

    I am new on magneto, my requirement is that, i want
    to payment method and shipping method for each vendor separately in one-page checkout.

  • ajinkya shidhore

    Great tutorial! But it is missing “function authorize”. And “function capture” is defined twice in this article. Can you please include “function authorize”? Thanks!

  • Manuel

    At the Redirect Based Method, is there any reason for calling the url ‘pay/index/index’, or is just arbitrary? Is this the same to call it, let’s say ‘pay/payment/redirect’ ? Or are there any platform benefit, of some kind, for using those names for the controller and action?

    Thanks

  • Abishiek

    In which table Merchant Id and salt value will be stored in Magento Database

  • Marcio

    How to Clean the form of credit card when error returned?

  • Marcio

    When you place your order status is “pending”, poque when I run the following script to store the information on historic status is changed to “processing”?

    $history = $order->addStatusHistoryComment($comment, $historyStatus)->setIsVisibleOnFront($isVisibleOnFront)->setIsCustomerNotified($isCustomerNotified);
    $order->save();