We will explore every aspect of processing a
transaction in detail and write code that will allow us to seamlessly
integrate the Authorize.Net payment gateway into our application.
Introduction
Authorize.Net®
is unquestionably the most popular payment gateway in use today. In
fact, it is synonymous with ecommerce and is often confused as a
payment processor. Its vast popularity with shopping carts, it is
integrated with every major and minor shopping cart available, make it
a natural choice for merchants wishing to begin processing sales
online.
But what if you are
implementing a custom shopping cart solution? You will need to
integrate Authorize.Net's payment gateway into your application
yourself. Fortunately this task is less daunting then it sounds. Below
we will explore every aspect of processing a transaction in detail and
write code that will allow us to seamlessly integrate the Authorize.Net
payment gateway into our application.
Goals
As with any project you undertake, a clear set of goals should be
outlined. The goals we have set forth for our project is to write PHP
code that:
- will integrate with the Authorize.Net API seamlessly
We do not want our customers to leave our website or think they are
leaving our website at any time. We want a clean, seamless transaction
process that presents our business in a professional manner.
- is easy to use
We do not want to spend a lot of time integrating the gateway into our
website. That time would be better spent promoting the website so we
can make money.
- is reusable
Web developers and aggressive entrepeneurs will open more then one
ecommerce store. We definitely do not want to reinvent the wheel when
it comes to integrating a payment gateway so we will want to make sure
the codee we write can be easily transported from integration to
integration.
Getting Started
Before we get to the technical nitty gritty of integrating the
Authorize.Net payment gateway into an ecommerce application, we have to
make sure we have everything we need. Some technical requirements will
need to be met if we wish for the code we will write to function
properly. We should acquire some useful tools and documentation that
will help us understand the inner workings of the Authorize.Net API. We also need to make sure we, as developers, have the technical know-how to handle a task of this magnitude.
Technical Requirements
Before we write a line of code we will need to make sure our server
meets the minimum technical specifications for our code to work. You
can easily check if your server is properly configured by using PHP's phpinfo() function.
- PHP 5
The code that we will be using in this article will be object-oriented.
To ensure it is compatible with future applications it is written in
PHP 5. This also offers much greater support for object-oriented
programming (OOP) then PHP 4 and since we will want our code to be easily reusable, writing it in an object oriented syntax makes good sense.
To download PHP 5 go to the PHP download page. For a detailed overview of what has changed from PHP 4 to PHP 5, see the What's New in PHP 5 from Zend.
- CURL
CURL is a command line tool for transferring files with URL syntax,
supporting FTP, FTPS, TFTP, HTTP, HTTPS, TELNET, DICT, FILE and LDAP.
CURL supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading,
HTTP form based upload, proxies, cookies, user+password authentication
(Basic, Digest, NTLM, Negotiate, kerberos...), file transfer resume,
proxy tunneling and a busload of other useful tricks.
PHP supports
CURL through the libcurl library. The libcurl library is not enabled by
default and also requires the libcurl library to be installed on the
server. Libcurl version 7.10.5 or higher is required to work with PHP
5. You can download libcurl
from the developer's website. When recompiling PHP to work with libcurl
you must be sure to compile PHP '--with-curl'. You can read more about
binding PHP with CURL on the PHP website.
- OpenSSL
The OpenSSL Project is a collaborative effort to develop a robust,
commercial-grade, full-featured, and Open Source toolkit implementing
the Secure Sockets Layer (SSL v2/v3) and Transport Layer Security (TLS
v1) protocols as well as a full-strength general purpose cryptography
library. The project is managed by a worldwide community of volunteers
that use the Internet to communicate, plan, and develop the OpenSSL
toolkit and its related documentation.
OpenSSL version 0.9.6 or higher is required to work with PHP 5. You can download OpenSSL from the developer's website. When recompiling PHP to work with OpenSSL you must be sure to compile PHP '--with-openssl'.
Authorize.Net
Since this article is about integrating the Authorize.Net payment
gateway into an ecommerce application, it stands to reason that we
would have some basic requirements concerning it. Authorize.Net
provides developers a set of tools to assist with the integration of
their software. Utilizing these tools will make our job easier and less
prone to error.
- Request a Test Account
Naturally it is very difficult to develop any application without
having all of the necessary components available for testing. A web
developer who is integrating the Authorize.Net gateway into an existing
merchant account can use their instance of the merchant's gateway and
turn on test mode to test their application. But if you are a web
developer who is developing your own software that will need to
integrate with Authorize.Net gateway, like a shopping cart, or wish to
get into the ecommerce development arena, you will not have an
Authorize.Net account to test your code on.
Fortunately the
developers at Authorize.Net had the foresight to understand that not
every developer will have an Authorize.Net account to test on and will
not want to wait until they have a customer to start writing their
code. (Plus having a testing environment available for developers
allows more applications to be certified thus potentially growing their
marketshare). For these developers Authorize.Net has a system in place
for allowing developers to have fully functional test accounts to test
their integration code.
- Download the Implementation Guide
While you wait for your test account credentials to be provided to you
by Authorize.Net, you should download the documentation which explains
how to communicate with their API. Their documentation is in pdf format and can be downloaded here: Advanced Integration Method Integration Guide . It offers a clear explanation of all of their API calls and should explain what you need to do to communicate successfully.
- Sample Code
If reading boring documentation is not the best way for you learn,
Authorize.Net offers yet another way to learn more about their API. You may request sample code to demonstrate how to connect successfully to their API. They offer sample code for ASP (VBScript), ASP.Net (Using C# or VB.NET), Cold Fusion, Java, Perl, and PHP. The code samples work so writing code to work with their API can be as simple as modifying their code.
Assumptions
The scope of this article, integrating the Authorize.Net payment
gateway, provides us with a narrow topic on which to focus (which is a
good thing). To keep this article focused on the topic at hand we will
not delve into basic programming skills and techniques. Assumptions
that are made in this article include:
- Validated Basic Data
It will be assumed that you have some basic PHP programming skills with
basic to moderate programming experience. A basic part of any
application which receives data from a user is to validate that data.
This means ensuring that all required data is present as well is a
value that is valid for that field. An example would be verifying that
a zip code is actually a five digit number and not just "asdf". To keep
the focus on implementing the Authorize.Net gateway it will be assumed
that your application has already validated all of the user's input and
that it is safe to use. However, we will cover validating credit card
information as it does fall within the scope of this article.
If you are
unfamiliar with validating data there are plenty of tutorials online. A
couple of good ones are offered by HTML Center and Codewalkers.
- Familiar with Object Oriented Development
Object Oriented Development (OOP) offers a developer an opportunity to create a modular application that features reusable code.
If you need to learn more about object oriented programming and design you should read An Introduction to Object-Oriented Design and also checkout WikiPedia's entry for Object-oriented programming.
- Familiar with Regular Expressions
Regular expressions are a common tool used by many programming
languages for matching patterns in strings. Its use in data validation
is essential as it can allow for very precise validation of user input.
There are a few variations of regular express syntax. This article will
use the most common and powerful version: the Perl Compatible Regular
Expressions (PCRE). PCRE is used by the most common web development languages such as Perl, PHP, and Java. For a tutorial see PHP's page on PCRE Syntax and the WikiPedia Entry for regular expressions.
Planning the Transaction
Before we start to tackle processing the credit card payment
programmatically, we should have an understanding of how the customer
data flows from the beginning of the process (they submit the form) to
the end of the process (they are informed of the success of their
transaction). This will not only give us a better understanding of how
an online payment works, but will also allow us to plan for
contingencies like a processing error or declined transaction. Below we
will follow customer data from submission through completion.
- Capture Customer Information
The transaction process begins with the submission of the customer data
including their credit card information. At this point you should
validate it and, if there are any errors, return the user to the
checkout page and ask them to correct any errors. If the information is
correct you will need to prepare it so it will be in the format
Authorize.Net is expecting (you may ask the customer for the
information in one format that is easy for them to understand, but that
may not be compatible with the parameters Authorize.Net is expecting).
What we'll need to do:
- Receive customer submitted data
- Validate all customer submitted data
- Inform the customer of any errors
- Ensure all data is in proper format for submission to the Authorize.Net API
- Send Data to Authorize.Net
Once you have validated the data submitted by the customer and have
ensured it is in the proper format Authorize.Net is expecting you will
need to send it to them via their API.
Authorize.Net then passes the transaction information on to the
processing bank associated with the merchant account linked to this
instance of the gateway. The processing bank will send a response back
to Authorize.Net including whether the transaction was approved and the
results of AVS.
What we'll need to do:
- Connect to the Authorize.Net API
- Send the transaction data
- React to the Response
Upon successful communication with the processing bank Authorize.Net
will return a response to your application. There are three possible
responses that we will need to deal with:
- Approval/Success
The transaction was approved by the processing bank. The merchant will
be credited with their funds in a couple of business days and the
customer will see the charge on their next credit card statement.
- Decline
The transaction was declined by the processing bank. For reasons known
only to the processing bank, meaning you cannot find out why the
transaction was declined, this sale was declined. The merchant will not
get paid and the customer will not be charged.
- Error
Somewhere between your application sending the transaction data to
Authorize.Net and your application receiving the data back an error
occurred. The transaction was not processed and cannot be considered
approved or declined.
Each result will require a different response by your application:
- Approval/Success
At the very least your application should display a message indicating
the transaction was successful. Ideally your application will present
the customer with a printable receipt that they can keep for their
records and a confirmation email will be sent as well.
- Decline
Because the transaction was not successful we essentially can consider
it to be an error. It would be no different then if the customer
entered incorrect information into the order form. The customer should
be informed of the results of the transaction and be offered another
opportunity to pay using a different credit card.
Tip:
You may want to store the credit card information into a session
variable and compare it to the new credit card information submitted by
the customer. If they match you should flag it as an error without ever
sending it to the gateway to be processed. Since the odds of it being
declined twice is very high, almost 100%, you can save money by not
incurring a transaction fee for the new declined transaction.
- Error
There are a variety of reasons an error can occur. Since most of them
are out of our control and beyond the scope of this article, we'll only
concern ourselves with how we might handle them. Basically we have two
options:
- Try Again
Temporary connection errors and other various errors happen from
time-to-time and there is no reason one won't occur while trying to
process a transaction. Just like any other computer error there is no
harm in trying again. And considering a sale is at stake it makes sense
that we will try again. Trying again is as simple as re-sending the
transaction information back to Authorize.Net.
- Inform the customer of the error
Repeated processing errors is an indication that a transaction will not
be successful. This does not mean the sale will necessarily be
declined, but unfortunately something beyond your control is preventing
the transaction from being processed properly. At this point all you
can do is inform the customer of the error and invite them to make
payment in an alternate way (e.g. Paypal, or by telephone).
What we'll need to do:
- Receive the response from Authorize.Net
- React to the response based on the results of the transaction
Building our Class
Now that we have an understanding of how a transaction will flow we can
build our class around it. We should read through the Advanced Integration Method Integration Guide before we begin so we understand what Authorize.Net will be expecting from us.
Naming the Class
Naturally any good class name will indicate what the objects that class
will create represents. For our class we will use the nickname for
Authorize.Net in the credit card processing industry: Authnet.
class Authnet
{
// ...
}
Class Properties
Immediately after declaring our class we will need to declare the
properties we will need for storing our objects properties. All of our
properties will be private as we do not want our scripts to be
accessing any of these values without going through a method. The first
two properties we will declare are our Authorize.Net login and
transaction key:
private $login = "ertdev6345";
private $transkey = "SR2dsf54dEn7vFLQ";
The login is used to login into the merchant's control panel as well as
identifying the merchant to Authorize.Net's API. The transaction key is a random sequence of characters that acts like a password for API
transactions. It is a security measure that prevents the merchant from
putting their true account password into their scripts in an effort to
help keep them safe.
Speaking of safe,
that is the very reason we will hardcode this information into our
class as opposed to making them parameters of the constructor. By
placing this information directly in our class we can keep it out of
our webpage's code. If the server experiences an error and were to
suddenly display our source code, this sensitive information would not
be available to the general public. It also allows us to place this
class outside of our web root so it cannot be displayed by a web
browser.
The Authorize.Net
login will be provided by Authorize.Net upon the accounts creation. You
can get your transaction key by following these steps:
- Login into your account at Authorize.Net
- Click on 'Settings'
- Click on 'Transaction Key'
- Enter the answer to your secret question
- Click on the checkbox to disable your old transaction key
The next three properties will determine if our transaction was
successful (approved), declined, or an error occurred. You'll notice
that $approved and $declined are both set to false while $error is set
to true. This is because it is better to assume there is an error and
either try again or abort the process then to assume a sale was
approved only to find out later it wasn't (and the merchant already
shipped their order).
private $approved = false;
private $declined = false;
private $error = true;
The main function of the Authorize.Net API
is to pass data back and forth to complete a transaction. The next two
properties store the data we will be passing to Authorize.Net and the
data we will be receiving respectively. The $params parameter is an
array that will hold the parameters that we be passing to the
Authorize.Net API. The $results parameter is an array that will hold the parameters that we be receiving from the Authorize.Net API (after a little bit of processing that is).
private $params = array();
private $results = array();
Since we cannot send arrays to the Authorize.Net API
nor can we receive them, we will need a place to store our parameters
in a format that Authorize.Net is expecting as well as a place to
receive their response. The $fields parameter will store our properly
formatted parameters for us (this will be done in a method described
later in this article) . The $response parameter will store the raw
data of the return response from the Authorize.Net API.
private $fields;
private $response;
Finally, this last parameter probably can be done without, but for the
sake of easier testing I have decided to include it in our class. This
member will simply store a boolean value to determine whether we are
currently testing our integration or processing live sales.
Authorize.Net offers a special URL that is to be used for testing an
integration of their API.
Setting this member to TRUE will cause our Authnet object to use that
URL for all transactions until we set it to false. (We will see this in
action in our _construct method).
private $test;
Constructor
New in PHP 5 is the __construct()
function for creating an instance of an class. This function creates
the object and can assign properties to it by assigning values to
properties of the class. Inside our constructor we will be assigning
values to properties that we will not be changing during the course of
using our object. Let's look at what our __construct() function will look like and then break it down:
public function __construct($test = false)
{
$this->test = trim($test);
if ($this->test)
{
$this->url = "https://test.authorize.net/gateway/transact.dll";
}
else
{
$this->url = "https://secure.authorize.net/gateway/transact.dll";
}
$this->params['x_delim_data'] = "TRUE";
$this->params['x_delim_char'] = "|";
$this->params['x_relay_response'] = "FALSE";
$this->params['x_url'] = "FALSE";
$this->params['x_version'] = "3.1";
$this->params['x_method'] = "CC";
$this->params['x_type'] = "AUTH_CAPTURE";
$this->params['x_login'] = $this->login;
$this->params['x_tran_key'] = $this->transkey;
}
Let's start with the constructor declaration:
public function __construct($test = false)
We've made our constructor function public so our application can
access it to create an object. We also have one default parameter. This
parameter is optional as we have provided a default value for it if one
is not supplied during the creation of a new object.
$this->test = trim($test);
if ($this->test == true)
{
$this->url = "https://test.authorize.net/gateway/transact.dll";
}
else
{
$this->url = "https://secure.authorize.net/gateway/transact.dll";
}
You can see the parameter for the constructor is assigned to the
parameter $test. If we pass a value of TRUE as the parameter during the
creation of our object we will tell that object to use the test URL for
the Authorize.Net API. The if/else statement below it chooses the appropriate URL to use for our test transactions.
The next portion of our constructor makes use the $params array we declared earlier in our class. It assigns values to some parameters the Authorize.Net API will be expecting.
$this->params['x_delim_data'] = "TRUE";
$this->params['x_delim_char'] = "|";
$this->params['x_relay_response'] = "FALSE";
$this->params['x_url'] = "FALSE";
$this->params['x_version'] = "3.1";
These five values collectively tell Authorize.Net how we intend to use their API. Since they have little value to our overall understanding of how to use their API
we will avoid going into them in great detail. You can read more about
what each of these parameters do in their implementation guide.
$this->params['x_method'] = "CC";
$this->params['x_type'] = "AUTH_CAPTURE";
The next two parameters tell Authorize.Net what kind of transaction we
are running. The 'x_method' parameter tells Authorize.Net that this
will be a credit card transaction (you can process eCheck transactions
if the gateway has been established with that functionality). The
'x_type' parameter tells Authorize.Net that we wish to authorize and
capture this transaction.
Authorizing but not charging a credit card
When credit cards are processed it actually is a two stage process. The
first stage acquires and authorization number from the card issuing
bank. The funds for that transaction are frozen and associated with
that authorization number. The second stage if the actual capture of
that sale where the funds are deducted from the customer's balance. The
vast majority of Internet transactions perform both stages at once. But
this is not always the case. It is possible that a merchant may want to
freeze the funds on a credit card but not actually charge the customer
until they ship the order. This is called an 'authorization only'
transaction. In these cases the merchant is provided with the
authorization number and uses this authorization number at a future
date to claim those funds.
$this->params['x_login'] = $this->login;
$this->params['x_tran_key'] = $this->transkey;
These last two parameters are our login and transaction key for the API. Obviously without these our access to the API will be denied.
Methods
Now that we have established what the properties for our class will be
we will need to create some methods that will allow us to interact with
them as well as to do the work our class is designed for.
public function transaction($cardnum, $expiration, $amount,
$cvv = "", $invoice = "", $tax = "")
{
$this->params['x_card_num'] = trim($cardnum);
$this->params['x_exp_date'] = trim($expiration);
$this->params['x_amount'] = trim($amount);
$this->params['x_po_num'] = trim($invoice);
$this->params['x_tax'] = trim($tax);
$this->params['x_card_code'] = trim($cvv);
}
The transaction()
method assigns values to some of the properties that we did not have
values for when we first created our object. These properties contain
the necessary information to process the customer's credit card. This
includes the credit card number, expiration date, and amount of the
transaction.
We also have three
optional parameters that are not required to process our transaction
but under certain circumstances will be good to have. Certain types of
credit cards require additional parameters to be sent to the processing
bank during a transaction. For example, business cards require an
invoice number (also called a purchase order number) and the tax amount
for the sale to be submitted with each transaction. If they are not,
that sale will downgrade to a much higher processing rate for the
merchant.
Tip:
There is no way to check to see if a credit card is a business card
without physically viewing the card (even then you cannot always tell).
If keeping merchant accounts fees to a minimum is important to the
merchant, always submit an invoice number and tax amount with every
transaction. It will never hurt the transactions that do not require
this information and will prevent the ones that do from downgrading to
a higher rate.
The CVV2 number is
a security measure designed to help reduce fraud in card not present
transactions. Some credit card issuing banks will decline a transaction
if the CVV2 number is incorrect. Authorize.Net can even be set to
automatically decline any transaction that fails a CVV2 check. Even
though this field is optional in our class, there is no reason not to
send this information with every transaction.
public function process($retries = 3)
{
$this->prepareParameters();
$ch = curl_init($this->url);
$count = 0;
while ($count < $retries)
{
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, rtrim($this->fields, "& "));
$this->response = curl_exec($ch);
$this->parseResults();
if ($this->getResultResponseFull() == "Approved")
{
$this->approved = true;
$this->declined = false;
$this->error = false;
break;
}
else if ($this->getResultResponseFull() == "Declined")
{
$this->approved = false;
$this->declined = true;
$this->error = false;
break;
}
$count++;
}
curl_close($ch);
}
This method is the meat and potatoes of our class. It sends the
transaction information and receives the response from the
Authorize.Net API. It takes one parameter, $retries
which has a default value of 3. This is the number of attempts that we
will make to contact Authorize.Net and process a transaction. Earlier
we had mentioned that this was one possible solution to dealing with
errors when communicating with the Authorize.Net API.
Here we have decided that three attempts are enough. If it does not
connect after three attempts, it probably isn't going to work in a
reasonable amount of time and we should fall back to another solution
(like inviting the customer to call in their order).
Our next line of
code: $this->prepareParameters() calls a private method that as the
name would suggest prepares our parameters to be sent to the
Authorize.Net API. We'll cover this method shortly.
To connect to the Authorize.Net API we have chosen to the built in CURL library. It makes interacting with remote servers easy. This line $ch = curl_init($this->url) initializes a new session and return a CURL handle for use with the other CURL functions.
Before we contact the Authorize.Net API we need to keep track of how many attempts we make before we give up. We do this by with this line: $count = 0.
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, rtrim($this->fields, "& "));
$this->response = curl_exec($ch);
These four lines of code do the hard work for communicating with the Authorize.Net API.
The first line tells CURL to include header information in its output.
The second line tells CURL to store the response as a string instead of
outputting it to the browser. The third line provides CURL with the
string to send to the Authorize.Net API which for us is our parameter string created by $this->prepareParameters(). The fourth line sends the request to the API and receives and stores the response.
To know whether our transaction was successful or not, and more, we
need to parse the results. Our private method parseResults() does this for us. We will cover this method below.
if ($this->getResultResponseFull() == "Approved")
{
$this->approved = true;
$this->declined = false;
$this->error = false;
break;
}
else if ($this->getResultResponseFull() == "Declined")
{
$this->approved = false;
$this->declined = true;
$this->error = false;
break;
}
$count++;
Now that we have our results we need to determine whether our
transaction was successful, declined, or experienced an error. Using
the getResultResponseFull()
method (covered later) we can find the status of our transaction. If
the transaction is "Approved" or "Declined" we set our status properies
approriately. We will use the break statement to end our while loop
since we do not need to connect to the API
any longer. If the transaction is neither "Approved" nor "Declined" we
can assume an error occurred. In this case we will increment our
counter and try again.
private function prepareParameters()
{
foreach($this->params as $key => $value)
{
$this->fields .= "$key=" . urlencode($value) . "&";
}
}
Before we can send our transaction information to Authorize.Net we need
to make sure that information is in a format they are expecting.
prepareParameters() takes the values in this array and separates each
key/value pair with a pipe. It then spearates each set of parameters
with an ampersand (&). We store the results in $this->fields to be sent when we process the transaction.
private function parseResults()
{
$this->results = explode("|", $this->response);
}
When we receive the response from the Authorize.Net API
we will need to convert it into a format that we find easy to use (just
like we had to format our parameters to their specifications).
Fortunately this is as easy as splitting a pipe-delimited string. In
our constructor we told the Authorize.Net API that we will be using a pipe ("|") as our delimiting character ($this->params['x_delim_char'] = "|") so we know we need to split the values by looking for it. We store the results in the $this->results array for later accessing.
public function setParameter($param, $value)
{
$param = trim($param);
$value = trim($value);
$this->params[$param] = $value;
}
The Authorize.Net API
can accept over 80 parameters when submitting a transaction. Only a few
are required but many are handy to use for various reasons ranging from
enhanced security to better record keeping. Our array $this->params stores these fields for us. We can add fields to this array using the setParameter() method. The first parameter of this function is the name of the parameter you wish to send. You can refer to the Advanced Integration Method Integration Guide to see what parameters are available for use. The second parameter to our method is the value you wish to set.
public function getResultResponse()
{
return $this->results[0];
}
public function getResultResponseFull()
{
$response = array("", "Approved", "Declined", "Error");
return $response[$this->results[0]];
}
When a transaction is processed and Authorize.Net returns the results
of that transaction, it sends 73 fields back containing various pieces
of information about the transaction. These 73 fields are all contained
in the $this->results array created by the parseResults() method. The first field contains a numerical indicator of the results of our transaction. Our method getResultResponse() returns that number directly to our script. If you choose to use this method you will receive one of the following numbers:
- 1 - Indicates an Approval
- 2 - Indicates a Decline
- 3 - Indicates an Error
To make displaying this easier, and also making our script possibly easier to read, we can use getResultResponseFull()
to output a human-readable result of our transaction. We simply put the
human-readable results into an array and use the response code as our
array key.
public function isApproved()
{
return $this->approved;
}
public function isDeclined()
{
return $this->declined;
}
public function isError()
{
return $this->error;
}
These three methods are just accessor methods that allow our scripts to
access private properties of our class. In these three cases they allow
a script do determine the status of a transaction. The names of the
methods will make it very clear in your script what you are trying to
accomplish (checking the status of the transaction) and will promote
readability which makes maintaining your script much easier.
public function getResponseText()
{
return $this->results[3];
}
public function getAuthCode()
{
return $this->results[4];
}
public function getAVSResponse()
{
return $this->results[5];
}
public function getTransactionID()
{
return $this->results[6];
}
Just like we have created accessor methods for the results of the
transaction, we can create accessor methods to access the other 72
fields returned by the Authorize.Net API. The four chosen for this article are important enough to be listed here are:
- getResponseText()
If an error occurs or a transaction is declined, this field will
contain text explaining what happened. If it is approved it will simply
say, "This transaction has been approved.".
- getAuthCode()
This is the six-digit approval code provided by the processing bak for
the transaction. This is good to have for any transaction and vital for
AUTH_ONLY transactions.
- getAVSResponse()
This is a one character response indicating the results of AVS. The results of AVS can be very useful in determining whether a transaction may be fraudulant or not. Here are the important AVS codes to know:
- X or Y - Both the numeric address and the Zip code match the card issuing bank’s database.
- A - Address matches but the Zip code does not.
- W or Z - Zip code matches but numeric address does not.
- N - Neither the zip code or street address matches.
- U - The issuing bank doesn’t support AVS.
- G - An international credit card. AVS is not supported.
- getTransactionID()
This is the transaction ID assigned to this transaction by
Authorize.Net. When researching this transaction on their server this
will be handy to have.
Before We Process the Transaction
Although at the beginning of this article we stated that we will assume
that you know how to validate data submitted by the customer. This much
is still true. However, you may not necessarily know how to validate
credit card specific information as you have never encountered it
before. Below we provide techniques for validating the essential
elements of a credit card.
Credit Card Number
Credit card numbers are never longer then 16 digits. The INPUT form
field used to collect this data should have a MAXLENGTH of 16
(maxlength='16') set to prevent users from entering more digits then is
allowed for any credit card issuer.
- Visa
Visa credit cards are always 16 digits long and start with a four (4).
Users may enter this in any of the following formats:
- XXXX-XXXX-XXXX-XXXX
- XXXX XXXX XXXX XXXX
- XXXXXXXXXXXXXXXX
Here is our code to validate a Visa credit card:
if (preg_match('/^4\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/i', $visa_number))
{
// Visa number is correct
}
else
{
// Visa number is incorrect
}
- MasterCard
MasterCard credit cards are always 16 digits long and start with a five
(5). Users may enter this in any of the following formats:
- XXXX-XXXX-XXXX-XXXX
- XXXX XXXX XXXX XXXX
- XXXXXXXXXXXXXXXX
Here is our code to validate a MasterCard credit card:
if (preg_match('/^5\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/i', $mastercard_number))
{
// MasterCard number is correct
}
else
{
// MasterCard number is incorrect
}
- American Express
American Express credit cards are always 15 digits long and start with
three (3). Users may enter this in any of the following formats:
- XXXX-XXXXXX-XXXXX
- XXXX XXXXXX XXXXX
- XXXXXXXXXXXXXXX
Here is our code to validate a American Express credit card:
if (preg_match('/^3\d{3}[ \-]?\d{6}[ \-]?\d{5}$/i', $amex_number))
{
// American Express number is correct
}
else
{
// American Express number is incorrect
}
- Discover Card
Discover Card credit cards are always 16 digits long and start with
6011. Users may enter this in any of the following formats:
- XXXX-XXXX-XXXX-XXXX
- XXXX XXXX XXXX XXXX
- XXXXXXXXXXXXXXXX
Here is our code to validate a Discover Card credit card:
if (preg_match('/^6011[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/i', $discover_cc_number))
{
// Discover Card number is correct
}
else
{
// Discover Card number is incorrect
}
So how do we wrap this all together into something coherent that can be
practically applied to a website? We will take the credit card number
and look at the first number of the credit card number. We will then
validate it based on whether it is a 3, 4, 5, or 6. If it isn’t any of
the four we’ll just tell the user to try again.
$cc_number = trim($_POST['cc_number']);
$first_number = substr($cc_number, 0, 1);
switch ($first_number)
{
case 3:
if (preg_match('/^3\d{3}[ \-]?\d{6}[ \-]?\d{5}$/', $cc_number))
{
// American Express number is correct.
Process the credit card.
}
else
{
// error
}
break;
case 4:
if (preg_match('/^4\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $cc_number))
{
// Visa number is correct. Process the credit card.
}
else
{
// error
}
break;
case 5:
if (preg_match('/^5\d{3}[ \-]?\d{4}[ \-]?\d{4}[ \-]?\d{4}$/', $cc_number))
{
// MasterCard number is correct. Process the credit card.
}
else
{
// error
}
break;
case 6:
if (preg_match('/^6011[ \-]?\d{4}[
\-]?\d{4}[ \-]?\d{4}$/', $cc_number))
{
// Discover Card number is correct. Process the credit card.
}
else
{
// error
}
break;
default:
// error
}
Verifying that a credit card has the correct number of digits and
starts with the correct number is a good start to verifying a credit
card number. But it is still fairly easy to work around. Any 16-digit
number starting with a 4, 5, or 6 will pass this check. To help
eliminate erroneous credit card numbers from being submitted there is
one more check that can be performed. All of the major credit card
institutions use a checksum to validate their credit cards. Each digit
of the credit card is multiplied by 1 or 2. The last digit of the
multiplication is added for each number in the credit card. If the
resulting number is divisible by 10 it is a valid credit card number.
Here is the PHP code:
// The credit card number
$cc_number = "4000123498762345";
// Our starting checksum
$checksum = 0;
// Alternating value of 1 or 2
$j = 1;
// Process each digit one by one starting at the right
for ($i = strlen($cc_number) - 1; $i >= 0; $i--)
{
// Extract the next digit and multiply by 1 or 2 on alternative digits.
$calc = substr($cc_number, $i, 1) * $j;
//
// If the result is in two digits add 1 to the checksum total
if ($calc > 9)
{
$checksum = $checksum + 1;
$calc = $calc - 10;
}
// Add the units element to the checksum total
$checksum += $calc;
//
// Switch the value of j
if ($j == 1)
{
$j = 2;
}
else
{
$j = 1;
}
}
// If checksum is divisible by 10 the credit card number is valid
if ($checksum % 10 == 0)
{
// It's a valid credit card number
}
else
{
// It's not valid
}
How can you tell if a credit card is legitimate?
After reading how to validate a credit card number you may think if a
credit card passes validation that it is a legitimate credit card. This
is not the case. Obviously a credit card in an improper format is not
legitimate. But a credit card number may be in proper format and still
be illegitimate.
So, how do
you verify a credit card number is legitimate? The only way is to
verify the legitimacy of a credit card is to authorize a sale. This
requires using the payment gateway to send a transaction to the
processing bank. The best way to do this is to do an AUTH ONLY
transaction. This is similar to a sale transaction but does not charge
the customer. It only freezes the funds on the customer’s credit card
for a short period of time (no more then 30 days) and they are
completely unaware of it as it does not appear on their statement. This
makes it ideal for verifying their credit card is legitimate.
So, to
verify a credit card is legitimate without actually charging, do an
AUTH ONLY transaction for $1.00 and see if it is approved. If it is,
the credit card is legitimate.
Expiration Date
The expiration date is not very difficult to validate compared to the
credit card number and CVV2 number (see below). You really only have
two issues to be concerned about:
- The expiration date is a four digit number
Authorize.Net expects you to submit a four digit number as the
expiration date for the credit card. The format they are expecting is
MMYY. If the month is a single digit number (e.g. 1-9 for January
through September) you must prefix it with a zero. The year is the last
two numbers of the year. So, if the credit card expires in the year
2010 then you would drop the '20' and keep the '10'. So, a credit card
that expires in August 2010 would have an expiration date of '0810'.
An easy way to do this is to use two SELECT menus in your HTML.
One for the month, one for the year the card is set to expire. That way
you can have the form send you the date in the format you prefer and
all you need to do is concatenate the two values:
<select name="expire_month">
<option value="01">January</option>
<option value="02">February</option>
<option value="03">March</option>
<option value="04">April</option>
<option value="05">May</option>
<option value="06">June</option>
<option value="07">July</option>
<option value="08">August</option>
<option value="09">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<select name="expire_year">
<?php
// Set the year to be the current year up to ten years from now
for ($i = date("Y"); $i < date("Y") + 10; $i++)
{
echo "<option value=\"" . date("y") . "\">" . date("Y") . "</option>";
}
?>
</select>
$expiration_date = $_POST['expire_month'] . $_POST['expire_year'];
- The expiration date is not a date in the past
Naturally an expiration date should be a future date. Any date from the
past should automatically flag the credit card as being invalid. The
current month is considered a future date as a date is not considered
expired until the full month is over. That means if a credit card has
an expiration date of September 2010, its implied expiration date is
September 30, 2010 at 23:59:59. We will compare the current year with
the expiration date's year. If it is sooner then this year, the date is
invalid. If the year is the same as this year we will check the month
to see if it has passed as well. If it hasn't, we have a valid
expiration date.
$current_month = date("m");
$current_year = date("y");
if ($_POST['expire_year'] < $current_year)
{
// Invalid date
}
else
{
// Check if the same year,
// if so, make sure month is current or later
if ($_POST['expire_year'] == $current_year)
{
if ($_POST['expire_month'] < $current_month)
{
// Invalid date
}
else
{
// Valid date
}
}
}
CVV2
CVV2 is a credit card security measure aimed at reducing fraud for card
not present transactions. It is a three or four digit number that is
only present on the credit card. Theoretically, this is used to verify
that the credit card being used in a purchase in the in the possession
of the purchaser at the time of the transaction making the sale more
secure.
This code can be found in
varying places on a credit card and is called different names by each
of the major credit card companies:
- Visa
Visa places their three digit code, which they call “Card Validation Code” (CVC2) on the back of the credit card in the signature panel. Usually at the very top-right most corner.
- MasterCard
MasterCard places their three digit code, which they call “Card Verification Value” (CVV2) on the back of the credit card in the signature panel. Usually at the very top-right most corner.
- American Express
American Express places their four digit number, which they call “Card Identification Number” (CID) on the front of the credit card, usually toward the end of the credit card number.
- Discover Card
Discover Card places their three digit number, called “Cardmember ID”,
on the back of the credit card in the signature panel. Usually at the
very top-right most corner.
When accepting credit cards through a website, collecting the CVV2
number is essential. In the ecommerce world it is an important
indicator as to the potential for fraud for a transaction. But as we've
seen above, a CVV2 value may be three or four digits depending on which
credit card a customer is using. So how do we validate it? By checking
to see which credit card the customer is using and then looking for the
correct amount of digits. We’ll find out which credit card the customer
is using by looking at the first number of the credit card. We’ll
specifically look for American Express cards as they are the odd balls
and have a four digits CVV2 number. Visa, MasterCard, and Discover Card
each have a three digit CVV2 number.
We’ll take the
first digit of the credit card number and then check to see if it is a
three. If it is a three, check to see if the CVV2 code is four digits
long. Otherwise, check to see if the CVV2 code is three digits long.
Here's some simple PHP code that does this:
// The credit card number
$cc_number = $_POST['cc_number'];
// The CVV2 number
$cvv2 = $_POST['cc_cvv'];
// Extract the first number of the credit card
$first_number = substr($cc_number, 0, 1);
// If the first number is a '3 it's an American Express Card
// And we need to verify the CVV2 number is four digits long
// otherwise check to see if it is three digits long
if ($first_number == 3)
{
if (!preg_match("/^\d{4}$/", $cvv2))
{
// It's an American Express card but its
// CVV2 code is not four digits long.
}
}
else
{
if (!preg_match("/^\d{3}$/", $cvv2))
{
// It's not three digits long.
}
}
Using our Class
Now that we have a clear understanding of how the Authorize.Net API works, how we can use it, and how a transaction occurs, we can tie it all together into a cohesive unit.
Processing the Transaction
Thanks to our class handling almost all of the work integrating it into
a script is very simple to do. Let's look at a basic framework that
uses our class and then break it down:
$payment = new Authnet(true);
$payment->transaction($creditcard, $expiration, $total);
$payment->process();
if ($payment -> isApproved())
{
// Display a printable receipt
}
else if ($payment -> isDeclined())
{
$reason = $payment -> getResponseText();
// As for another form of payment
}
else
{
// Ask the merchant to call us
}
The first line of code $payment = new Authnet(true) creates an instance our Authnet object and initializes all of the properties in the constructor. $payment->transaction($creditcard, $expiration, $total) assigns the credit card number, expiration date, and amount of the transaction we wish to process. $payment->process()
sends the transaction to Authorize.Net for processing and returns the
results to our object. All we need to do from here is find out if the
transaction was approved, declined, or resulted in an error. A simple
if/else statement handles this nicely.
You'll notice that if our transaction is declined we use the getResponseText() method to find out why. This will be handy to let our customer know why their card was declined.
And that's it! Believe it or not that's all it takes to process a
credit card transaction through Authorize.Net's API. But there are a couple of shortcomings to our code:
- We do not do AVS
- We do not verify the CVV2 number
Fortunately, ensuring that both of these are done is very easy to do with our class:
$payment = new Authnet(true);
$payment->transaction($creditcard, $expiration, $total, $cvv);
$payment->setParameter("x_address", $business_address);
$payment->setParameter("x_zip", $business_zipcode);
$payment->process();
To make sure that we verify the CVV2 number we simply as it as the fourth parameter to our transaction() method. Performing AVS is slightly more involved but still very simple to do. We use the setParameter()
method to add the street address and zip code to our list of parameters
we will send to Authorize.Net. Now we have helped to reduce our risk of
fraud and we only added two lines to our code!
Inducing a Declined Transaction
The test account offered by Authorize.Net will automatically approve
all transactions by default. While this is important for testing your
application's response to approved transaction, it doesn't help us test
our application's handling of declined transactions or errors.
Fortunately forcing Authorize.Net to decline a transaction is easy to.
Authorize.Net allows a merchant to have transactions be declined even
if they were originally approved by the processing bank if AVS
does not meet the merchant's criteria for an acceptable response. We
can take advantage of this to force our transactions to decline by
creating a situation where AVS will fail to meet our own criteria.
To change our test account's AVS settings follow these steps:
- Log into your test account
- Click on "Settings" (under "Account")
- Click on "Address Verification Service" (under "Basic Fraud Settings")
- Check the checkbox that says, "Reject if... Address information is not provided for AVS check (B)"
- Press the "Submit" button
Now to have a transaction declined we need only to submit a transaction
without the street address and zip code (as we did in our first example
of using our class). Without these two pieces of information our test
account will automatically decline the transaction.
Inducing an Error
Just as we need to create the circumstances for our test transactions
to be declined, we will need to do that same to produce an error.
Fortunately this is just as easy to accomplish and we can do it by
munging our own code. Simply changing the URL we use for contacting the
Authorize.Net API we can cause our transaction to fail. In the example below we simply change the test URL to be oops.Authorize.Net:
if ($this->test == true)
{
$this->url = "https://oops.authorize.net/gateway/transact.dll";
}
Conclusion
We have covered a lot of ground in this article. We started by
analyzing the transaction process from start to finish and identifying
all possible branches to the transaction flow. From there we built upon
the knowledge we gained from reading the Authorize.Net Advanced Integration Method Integration Guide to construct a class to encapsulate our interaction with the Authorize.Net API.
After validating unique transaction data we used our new class to
easily process a transaction utilizing the Authorize.Net gateway.
|