Add AVS and CVN to Magento’s Admin Screen in Just 637 Easy Steps

by Nicholas Piasecki on October 3rd, 2009
AVS Information In Magento

AVS Information In Magento

We’ve recently set up a new e-commerce store at Men’s Underwear Discounters that represents a division of the company that I work for, Skiviez. We decided to save time for this discount store by using Magento, the current darling child in the open source e-commerce platform market segment.

A brief interlude on consulting, documentation, and open source

Now, to be clear, it doesn’t take much to exceed expectations in the “open source e-commerce platform” market because prior to Magento, your options were pretty much limited to osCommerce, which is quite possibly the most reckless, idiotic excuse of an e-commerce platform that I have seen (and I don’t like to insult code), and its slightly-less-retarded sibling Zen Cart. (I will save the osCommerce rants for another post.)

Magento seems to be a dream come true: thousands of man-hours of development into an e-commerce platform for free. So what’s the catch? While Varien (“the Magento company”) has done a great service to the world and the open source community by creating Magento, they’re still in it to make a buck–and that’s not a bad thing–but you might not realize how they make those bucks until you’ve started playing around with your own Magento store. They make money on support, consulting, and customization because Magento is huge and poorly documented.

Actually, “poorly documented” would imply that there was at least some documentation, and while there truly is some documentation available in the source code and on Varien’s Magento Web site, in reality, that amount of documentation is a statistical rounding error. And when you actually view your Magento installation in an FTP program, you will be absolutely astounded at the number of directories and files. You can try inspecting the source files to try to put it all together, but a lot of it is “magic”: a framework erected at runtime by XML configuration files and PHP’s idiotic __call mechanism for magic functions that appear at runtime out of thin air. You can see references to a function called getOrder(), for example, grep the entire installation for “getOrder()”, and not get any results because the function doesn’t actually exist at compile time. Personally, I’ve always considered magic methods to be an extremely unwise idea, a “too clever” approach to programming. But for people new to PHP looking to do a little customization to their Magento store, it makes the codebase all the more impenetrable. That’s more consulting dollars.

You could go to the Magento forums, but you won’t find too many helpful answers because Magento obviously doesn’t want to give help for free, since giving help is central to their business model. So if you need help, your best bet is to generally (a) pay for it or (b) hope that you stumble across somebody’s blog who took the time to post a solution to your exact problem.

Why do I need to do this, anyway?

Magento has a lot of stuff built in. But we need to remember that Magento was built by software developers, and software developers generally don’t understand the problem domain of running an e-commerce business very well. Sure, every software developer has built the pet shop–products, categories, inventory, orders, how hard could it be?–but they stop or fail to understand that there’s a whole class of operations that need to happen after the order has been placed.

One of those operations is fraud analysis. The Address Verification System (AVS) and the Card Verification Number (CVN) system for credit cards enable merchants to help determine if a credit card has been fraudulently used on an order. AVS will tell the merchant whether or not the billing address specified by the customer matches or partially matches the billing address on the credit card’s billing statement. And the CVN number represents the number that (supposedly) exists in only two places in the world: on the physical credit card and in the issuing bank’s files. If a CVN doesn’t match and the AVS doesn’t match, then there’s a good bet that there is something wrong with the order.

Because software developers are nerds, this information doesn’t currently display by default in Magento’s administration section. So let’s add it.

Can’t I just edit the template?

I’m talking about adding this AVS display functionality in the context of the CyberSource extension for Magento. After spelunking through the myriad files and directories, I discovered that the Varien-provided CyberSource extension is saving the AVS (setCcAvsStatus()) and CVN (setCcCidStatus()) information when it authorizes the card, but it doesn’t display in the information block in the administration section.

While you could just modify app/design/adminhtml/default/default/template/cybersource/info.phtml directly (yes, that is the actual path to the file that displays the little “Payment” block), your changes would be lost and overwritten in the future if you ever upgraded the CyberSource extension via Magento Connect. That’s obviously less than ideal.

A better way would be to use Magento’s “rewrite” mechanism. We’ll create our own Magento module and instruct Magento to replace the CyberSource extension’s information block with our own.

Creating our own module

First, we’ll create a new empty directory to hold our files. Let’s call it avsdisplay. Then we’ll create code, etc, and design subdirectories. These directories mirror the subdirectories in the app folder of your Magento installation.

In the etc directory, we need to create an XML configuration file. Magento scans this directory at runtime for XML files and reads them to know which modules to load. I’ve named my configuration file Mud_Avsdisplay.xml, and the naming is very important. (Magento uses a mixture of convention and configuration.) The part of the filename before the underscore is your “namespace,” since PHP doesn’t support namespaces. And the part of the filename after the underscore is the name of your module. Mine is “Avsdisplay.” Don’t try to use something like “AvsDisplay,” however, because that would break Magento’s convention as we’ll see in a moment.

Inside this file, we tell Magento to load our module in the local pool and to actually run it (by setting “active” to “true”). (The other pools are core, for those modules provided by Varien, and community, for those modules that you can download via Magento Connect.)

<?xml version="1.0"?>
<config>
	<modules>
		<Mud_Avsdisplay>
			<active>true</active>
			<codePool>local</codePool>
		</Mud_Avsdisplay>
	</modules>
</config>

Now it’s time to create the meat of the module. In the code folder, create a local directory. Within that, create a directory named after your chosen namespace (Mud in my example) and within that yet another subdirectory named after your module (Avsdisplay in my example). (This is why naming was important. If I had called it “Mud_AvsDisplay,” with a capital ‘D’, then Magento would have looked in app/code/local/Mud/Avs/Display for my module instead of app/code/local/Mud/Avsdisplay.)

Within your module directory, you’ll need to create three more subdirectories: Block, etc, and Helper. When it’s all said and done, you’ll end up with a hierarchy that looks like this (ignore the “design” directory, we’ll get to that later):

Directory Structure

Directory Structure

Creating the block

Next, we need to create the “block” that we’ll be replacing. After searching through the codebase, I figured out that I wanted to replace the Mage_Cybersource_Block_Info block (for reference, by Magento’s convention, that file lives in app/core/Mage/Cybersource/Block/Info.php — see the convention?). But I really only want to add stuff to it, not replace it outright, so I’ll create a new file in my Block folder called Info.php that extends that class. Something like:

<?php
 
class Mud_AvsDisplay_Block_Info extends Mage_Cybersource_Block_Info
{
	// Yes, these really have to be protected, not private, due to more Magento
	// "magic"
	protected $avsDescriptions = array(
		// Snipped for brevity; download file at end of blog post
	);
 
	protected $ccCidDescriptions = array(
		// Snipped for brevity; download file at end of blog post
	);
 
	protected function _construct()
	{
		parent::_construct();
		$this->setTemplate('avsdisplay/info.phtml');
	}
 
	public function getAvsStatusDescription()
	{
		$info = $this->getInfo();
		$avsCode = $info->getCcAvsStatus();
		$avsDescription = 'Unrecognized response code.';
 
		if (array_key_exists($avsCode, $this->avsDescriptions))
		{
			$avsDescription = $this->avsDescriptions[$avsCode];
		}
 
		return $avsDescription;
	}
 
	public function getCcCidDescription()
	{
		$info = $this->getInfo();
		$ccCidCode = $info->getCcCidStatus();
		$ccCidDescription = 'Unrecognized response code.';
 
		if (array_key_exists($ccCidCode, $this->ccCidDescriptions))
		{
			$ccCidDescription = $this->ccCidDescriptions[$ccCidCode];
		}
 
		return $ccCidDescription;
	}
}

Okay, this makes sense. I’m doing everything the old CyberSource info block did, except I added two new functions that give me human-friendly descriptions of the AVS and CVN codes, and I’m setting the template to something called avsdisplay/info.phtml instead of cybersouce/info.phtml. How this comes into play will make sense in a minute.

Creating the helper

In my Helper directory, I need to add a Data.php file that looks like this:

<?php
 
class Mud_Avsdisplay_Helper_Data extends Mage_Core_Helper_Abstract
{
}

That’s right, it does nothing except inherit from a Magento-provided abstract class. What is a helper? I have no idea, but you have to do it–Magento looks for this file.

Telling Magento to do the swap

In my etc directory, I need to add a config.xml file that will tell Magento some information about my module and, most importantly, that I want to be swapped with the CyberSource extension’s info block:

<?xml version="1.0"?>
<config>
	<modules>
		<Mud_Avsdisplay>
			<version>0.0.1</version>
		</Mud_Avsdisplay>
	</modules>
	<global>
		<helpers>
			<avsdisplay>
				<class>Mud_Avsdisplay_Helper_Data</class>
			</avsdisplay>
		</helpers>
		<blocks>
			<cybersource>
				<rewrite>
					<info>Mud_Avsdisplay_Block_Info</info>
				</rewrite>
			</cybersource>
		</blocks>
	</global>
</config>

Under <modules>, I’m just telling Magento the version number of my module. Under <global>, I say that I want to “rewrite” the CyberSource info block with my own class named Mud_Avsdisplay_Block_Info, which, by convention, Magento will look for in [module-root]/Mud/Avsdisplay/Block/Info.php. That is the file that I just created. Great.

Finally, writing the template that displays the information

But we haven’t actually changed any HTML at this point! Remember in my Info.php file where I set the template to something called info.phtml? Well, I need to create that file. At the top of my module folder–the one with the code and etc subdirectories–I’ll need to create some more subdirectories. A lot of them. In fact, I’ll need to create a bunch of empty directories that look like this:

Design Directory Structure

In the bottom-most subdirectory, I’ll create the info.phtml file. This is a PHP file, except that when I use the $this pseudo-variable in this file, $this will be pointing to an instance of my Mud_Avsdisplay_Block_Info class. So I’ll be able to call my getAvsStatusDescription() method here among other things:

<?php
	if ($info = $this->getInfo())
	{
		?>
			<strong><?php echo $this->getMethod()->getTitle(); ?></strong><br />
			<strong>Type:</strong> <?php echo $this->__($this->getCcTypeName()); ?><br />
			<strong>Number:</strong> xxxx-<?php echo $this->__($info->getCcLast4()); ?><br />
			<strong>Expiry:</strong> <?php echo $this->__('%s/%s', $this->getCcExpMonth(), $info->getCcExpYear()); ?><br />
			<strong>AVS:</strong> (<?php echo $this->__($info->getCcAvsStatus()); ?>) <?php echo $this->__($this->getAvsStatusDescription()); ?><br />
			<strong>CCID:</strong> (<?php echo $this->__($info->getCcCidStatus()); ?>) <?php echo $this->__($this->getCcCidDescription()); ?><br />
		<?php
	}
?>

Conclusions and delusions

And that’s it. Upload this entire directory structure to your Magento installation’s app directory, merging the contents as necessary, and you should be able to see AVS information for your CyberSource orders in the administration section.

How did I figure out how to learn all of this? It was mostly by lots of Googling, picking apart existing modules to see how they were organized, and piecing together lots of blog posts. I hope this helps someone out there. Good luck!

Download the code used in this article.

6 Comments
  1. Great post – I really appreciate seeing posts by someone working in a small/medium sized retail environment, some of what you are talking about is pretty familiar…

    CM

  2. Chris Russell permalink

    Thanks Nicholas. This is probably the most informative ‘how to’ for creating a magento module I’ve seen…certainly better than on their site. Keep up the good work.

  3. Jeff permalink

    Any way to get this to work with Authorize.net? I’ve been trying for the past couple hours and it’s just not working. Both $info->getCcAvsStatus() and $info->getCcCidStatus() return nothing. I’ve compared the Cybersource and Authorize.net (Paygate) extensions, and I can’t figure out what the differences are that would allow one to work and not the other.

  4. Jeff permalink

    Just an update to me previous comment. The code does indeed work for Authorize.net. The only real changes were just to overwrite the Paygate module instead of the CyberSource module. The reason I couldn’t get anything to display is because the information is not being stored in the database. Paygate uses the exact same setCcAvsStatus() and setCcCidStatus() functions, so I’m not sure where the problem is. I am using a developer Authorize.net account, but it should be returning the same info. In fact, there is a debug table for Paygate in the database. When debug is enabled, I can see the response that Authorize.net returns, and I can see the “Y” code in there for an AVS match. When I add this “Y” code (or any other valid code) to the appropriate table in the database manually, then the AVS information is displayed properly on the order screen. I can do the same for CVN, by adding an “M” or any valid code to the database. I did notice in the debug table that Authorize.net is not returning any codes for CVN. I believe this is because test accounts do not verify card codes at all. The CVN information doesn’t show in my Authorize.net account either or in the merchant email receipts. So I guess my whole point is, your mod works perfectly, I just need to figure out why the info isn’t showing up in the database.

  5. Has a solution been found for the Paygate module? That is what we have as well (vs Cybersource) but there is no Block folder in the core/Mage/Paygate file path. We are also using authorize.net and I cannot get the AVS results to show in the order admin either.

Trackbacks & Pingbacks

  1. Creating sane plain text sales transactional e-mails in Magento | Simply Does Not Work

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS