Tutorial: Integrating the Authorize.Net AIM API with PHP
Working with the Authorize.Net
Fortunately I have taken the time to abstract the AIM API into my own class that not only simplifies the code we will need to work with that API but also makes it modular and easy to port from application to application. To begin you will need to download the AuthnetAIM class. You can find it here.
This code is written in PHP5. PHP4 has been officially retired by PHP and all of the major open source projects have committed to PHP5. I do have an older version of this code written for PHP4 compatibility but I have no intention of writing a tutorial for its use.
The next thing you will need to do is configure your class to work with your Authorize.Net account. This is done in the Authorize.Net class at the very top of the page. You will see these four lines:
1 2 3 4 | const LOGIN = 'yourlogin'; const TRANSKEY = 'yourtransactionkey'; const TEST = false; const GODADDY = false; |
LOGIN is the API login for the account. TRANSKEY is the transaction key generated in the Authorize.Net control panel. If you have a Authorize.Net developer account you can use it with this script by setting TEST to be true. If this script is being used on a Godaddy.com server you will need to set GODADDY to true to have the script use their proxy server. The script will fail otherwise.
Once you have configured your class you will include it into any page you wish to use it with the require() function:
1 | require('AuthnetAIM.class.php'); |
Authorize.Net allows for a lot of information to be passed through their API. Only a few of these are actually required to process a transaction. The following snippet is the minimum amount of code required to successfully process a transaction:
1 2 3 | $payment = new AuthnetAIM(); $payment->transaction($creditcard, $expiration, $total); $payment->process(); |
First we create our Authorize.Net object and store it in a variable called $payment. We then called the transaction() method to set the three required pieces of information for processing a transaction: credit card number, credit card expiration date, and the total amount to be processed. If any of these pieces of information are omitted an exception will be thrown and the transaction will not be processed. Lastly we call the process() method which utilizes the Authorize.Net API to process the transaction and receive the response.
This basic code does not do AVS or CVV verification which, at the very least, means higher rates will be charged for the merchant but it also means they will be missing two important tools for reducing their exposure to fraud. To perform CVV verification we need only to add a fourth parameter to the transaction() method. To perform AVS we only need to provide the street address and zip code to Authorize.Net. We can do that using the setParameter() method which allows us to set predetermined parameters to be sent to the Authorize.Net API along with the transaction information (you can find all of the possible parameters in the integration guide):
1 2 3 4 5 | $payment = new AuthnetAIM(); $payment->transaction($creditcard, $expiration, $total, $cvv); $payment->setParameter("x_address", $business_address); $payment->setParameter("x_zip", $business_zipcode); $payment->process(); |
We can take it a step further and take advantage of Authorize.Net’s logging of transaction information and send over quite a bit of information through their API. Typically the complete billing address and shipping address, if necessary, are provided. Application specific user ids are also good to provide as they make matching a transaction to a user account that much easier to do.
There are other pieces of information that can be configured or provided to further customize a transaction. Two pieces of information that are a good idea to provide with each transaction is the total amount of tax charged to a sale and any invoice number associated with it. These pieces of information are required to processing business cards and since we cannot tell if a card being processed is in fact a business card it is simply a good idea to provide them with every transaction.
A parameter that can be configured programmatically is the window in which Authorize.Net will prevent a duplicate transaction from being processed. Basically this prevents a user from hitting the refresh button in their browser and processing their sale again. The x_duplicate_window parameter controls how long this window is open and is set in seconds.
Here is a sample transaction that sets the duplicate transaction window to three minutes, records the customer’s IP address, turns off the automatic email generated by Authorize.Net, provides an invoice number and tax amount, sends over complete billing and shipping addresses, and records the user id for this customer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | $payment = new AuthnetAIM(); $payment->setTransaction($creditcard, $expiration, $total, $cvv, $invoice, $tax); $payment->setParameter("x_duplicate_window", 180); $payment->setParameter("x_cust_id", $user_id); $payment->setParameter("x_customer_ip", $_SERVER['REMOTE_ADDR']); $payment->setParameter("x_email", $email); $payment->setParameter("x_email_customer", FALSE); $payment->setParameter("x_first_name", $billing_firstname); $payment->setParameter("x_last_name", $billing_lastname); $payment->setParameter("x_address", $billing_address); $payment->setParameter("x_city", $billing_city); $payment->setParameter("x_state", $billing_state); $payment->setParameter("x_zip", $billing_zipcode); $payment->setParameter("x_phone", $billing_telephone); $payment->setParameter("x_ship_to_first_name", $shipping_firstname); $payment->setParameter("x_ship_to_last_name", $shipping_lastname); $payment->setParameter("x_ship_to_address", $shipping_address); $payment->setParameter("x_ship_to_city", $shipping_city); $payment->setParameter("x_ship_to_state", $shipping_state); $payment->setParameter("x_ship_to_zip", $shipping_zipcode); $payment->setParameter("x_description", $product); $payment->process(); |
The Authorize.Net API is not limited to processing sales only. All types of transactions can be performed including refunds and authorizations. To do an authorization without charging the credit card we can use the setTransactionType() method to change the transaction type to AUTH_ONLY. setTransactionType() accepts one of six possible values which are AUTH_CAPTURE, AUTH_ONLY, PRIOR_AUTH_CAPTURE, CREDIT, CAPTURE_ONLY, and VOID. AUTH_CAPTURE is the default for sale and is set automatically when you create the $payment object. Below is a sample AUTH_ONLY transaction:
1 2 3 4 | $payment = new AuthnetAIM(); $payment->transaction($creditcard, $expiration, $total, $cvv); $payment->setTransactionType("AUTH_ONLY"); $payment->process(); |
Now that we know how to process a transaction, let’s see how we handle the results. Our AuthnetAIM class includes three methods for determining the status of the transaction. isApproved() will return true if the transaction was successful. isDeclined() will return true if the sale was declined. isError() will return true if there was some sort of error processing the transaction.
Using these three methods we cn create a flow control structure to handle the response appropriately:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | if ($payment->isApproved()) { // Get the approval code $approval_code = $payment->getAuthCode(); // Get the results of AVS $avs_result = $payment->getAVSResponse(); // Get the Authorize.Net transaction ID $transaction_id = $payment->getTransactionID(); // Do stuff with this. Most likely store it in a database. } else if ($payment->isDeclined()) { // Get reason for the decline from the bank. This always says, // "This credit card has been declined". Not very useful. $reason = $payment->getResponseText(); // Politely tell the customer their card was declined // and to try a different form of payment } else if ($payment->isError()) { // Get the error number so we can reference the Authnet // documentation and get an error description $error_number = $payment->getResponseSubcode(); $error_message = $payment->getResponseText(); // Report the error to someone who can investigate it // and hopefully fix it // Notify the user of the error and request they contact // us for further assistance } |
For debugging purposes the AuthnetAIM class overloads the __toString() to display the current state of the $payment object in a clear and easy to read table. If you run the code below you will get two tables displayed on your screen. The first will show the state of the object before the transaction is processed and the other will show it after.
1 2 3 4 5 | $payment = new AuthnetAIM(); $payment->transaction($creditcard, $expiration, $total, $cvv); echo $payment; $payment->process(); echo $payment; |
The AuthnetAIM class takes advantage of exceptions which are new in PHP5. These are only generated when something isn’t configured properly. This can be anything from the API login and transaction key to setting any parameters. Errors generated by Authorize.Net do not throw exceptions. You can use the new try/catch flow control structure to handle any exceptions generated by the class:
1 2 3 4 5 6 7 8 9 10 11 | try { $payment = new AuthnetAIM(); $payment->transaction($bad_information); $payment->process(); } catch (AuthnetAIMException $e) { echo 'There was an error processing the transaction. Here is the error message: '; echo $e->__toString(); } |
Now that we know how to process a sale, handle its response, and handle potential errors, let’s put it all together in one block so we can see it working all at once:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | require('AuthnetAIM.class.php'); try { $payment = new AuthnetAIM(); $payment->setTransaction($creditcard, $expiration, $total, $cvv, $invoice, $tax); $payment->setParameter("x_duplicate_window", 180); $payment->setParameter("x_cust_id", $user_id); $payment->setParameter("x_customer_ip", $_SERVER['REMOTE_ADDR']); $payment->setParameter("x_email", $email); $payment->setParameter("x_email_customer", FALSE); $payment->setParameter("x_first_name", $business_firstname); $payment->setParameter("x_last_name", $business_lastname); $payment->setParameter("x_address", $business_address); $payment->setParameter("x_city", $business_city); $payment->setParameter("x_state", $business_state); $payment->setParameter("x_zip", $business_zipcode); $payment->setParameter("x_phone", $business_telephone); $payment->setParameter("x_ship_to_first_name", $shipping_firstname); $payment->setParameter("x_ship_to_last_name", $shipping_lastname); $payment->setParameter("x_ship_to_address", $shipping_address); $payment->setParameter("x_ship_to_city", $shipping_city); $payment->setParameter("x_ship_to_state", $shipping_state); $payment->setParameter("x_ship_to_zip", $shipping_zipcode); $payment->setParameter("x_description", $product); $payment->process(); if ($payment->isApproved()) { // Get info from Authnet to store in the database $approval_code = $payment->getAuthCode(); $avs_result = $payment->getAVSResponse(); $transaction_id = $payment->getTransactionID(); // Do stuff with this. Most likely store it in a database. } else if ($payment->isDeclined()) { // Get reason for the decline from the bank. This always says, // "This credit card has been declined". Not very useful. $reason = $payment->getResponseText(); // Politely tell the customer their card was declined // and to try a different form of payment } else if ($payment->isError()) { // Get the error number so we can reference the Authnet // documentation and get an error description $error_number = $payment->getResponseSubcode(); $error_message = $payment->getResponseText(); // Report the error to someone who can investigate it // and hopefully fix it // Notify the user of the error and request they contact // us for further assistance } } catch (AuthnetAIMException $e) { echo 'There was an error processing the transaction. Here is the error message: '; echo $e->__toString(); } |
If you would like to learn more about how this class was written, be sure to read Integrate the Authorize.Net Payment Gateway with PHP. That article covers how to create a basic class that does everything we cover in this article and is a good primer for abstracting APIs with your own classes.
Tags: Authorize.Net, PHP
August 11th, 2008 at 8:00 am | Posted in Programming
September 2nd, 2008 at 3:45 am
Thanks for this descriptive solution. I have one problem I need to create the subscription based on the transaction id which send back by AIM method.
is this possible
Thanks
September 2nd, 2008 at 8:55 pm
Pravin, to do that you will need to use the ARB API. I’ll write a tutorial for that one shortly. Fortunately that’s not hard to do. In the meantime you can read an article I wrote about it: Integrate the Authorize.Net Recurring Billing API with PHP. It should explain enough for you to do what you need.
September 4th, 2008 at 1:50 am
This is perfect, thank you. Two questions… 1) how is this different than your article on merchant-account-services.org? and 2) what information do I have to provide when using this on my own e-commerce site?
September 4th, 2008 at 8:46 am
The article actually shows someone how to write the class and explains Authnet’s API in detail. This is different as this blog post is a tutorial in how to use that class the article is about. It’s aimed at users who aren’t either interested or capable of learning advanced APIs or programming and just want a solution that works.
To use this with your own ecommerce site all you have to do is have your own Authorize.Net payment gateway. That will give you an API login and transaction key. All you need to do is then put them into the class (you can see where at the top of the file) and the script should work for you.
September 9th, 2008 at 3:47 am
HI. thanks for your Tutorial ..I am just finding this kind of article…..I need intergrade Authorize in my e-commence website…..And this my first time to intergade Authorize….it is very detailed…I will have a try later…
September 10th, 2008 at 2:13 am
hi.. thanks a lot for your information.. i want one help from you. actually i php4, so can you please mail me PHP4 compatibility code.
September 15th, 2008 at 11:23 am
hi, can any one help me getting done with SIM integration. I have used the sample code for integrating, but it gives me error..saying “(99) This transaction cannot be accepted.”. Please help me in getting this done .Thanks in advance..
Neethi
September 22nd, 2008 at 11:00 am
So in order to actually process a transaction you would just need fields and a post for the variables here?
September 22nd, 2008 at 12:44 pm
Not exactly sure what you mean. But to use this code you need:
1) a form to capture the payment information (credit card number, expiration date, etc.)
2) to validate all of the data and make sure it is correct before you use this code to communicate with Authorize.Net
3) an Authorize.Net account to handle the payment processing (a test account will work fine)
October 1st, 2008 at 8:12 am
I have it all working fine on one site. I want to launch a similar but slightly different site, same charges, account info, bank account, etc. Can I use the same API setup I have on anther site?
October 1st, 2008 at 8:12 pm
George,
You should be able to. As long as all of the configurables are the same you should be fine.
October 2nd, 2008 at 6:07 pm
Hey John,
It looks like there are a few inconsistencies in the (otherwise good) code:
1. You use AuthnetException in your code, but AuthnetAIMException is what you define at the top of your code.
2. Line 156, if (empty($this->params['x_amount'])) is invalid if x_amount is 0.00 (if you’re trying to force an error for testing) - you may want to use isset or change the error message to explain a little better.
3. This is just a feature request (which I’m going to implement myself if I end up using this), but it’d be nice if you could pass the credentials into the object during __construct rather than defining them within the library.
Hope this helps!
October 2nd, 2008 at 9:23 pm
Chris,
Thanks for taking the time to review my code!
1) Thanks for pointing that out. I’ve updated the class to reflect that.
2) Basically what does is catch an invalid amount before the transaction is attempted. A zero dollar amount is not a valid amount top charge a card so the transaction will automatically. By catching it here at this point we save the merchant money as no transaction is processed so no transaction fee is charged by Authnet.
3) That is definitely an option to take. Especially if you’re in a situation where you may want to use multiple Authnet accounts for one service (I’ve seen it happen). It basically boils down to a tomato, tomáto situation.
October 6th, 2008 at 6:00 pm
it seems $payment->isError is always called with response codes null. when testing this code with authorize.net, is ssl required?
if not, what else could be the possible problem? i’ve tried with valid cc numbers and always get the same results.
rob
October 6th, 2008 at 8:48 pm
If you’re getting a null return value then it should definitely be flagged by $payment->isError as null is not an acceptable response. SSL is always required even when using an Authnet test account. This error should go away once you switch to a secure connection.
October 7th, 2008 at 12:53 am
hi john,
are you sure you need ssl for testing? after doing further research, forum posts suggest that ssl isn’t required for authorize.net gateway in “test mode”.
nonetheless, i setup ssl to troubleshooot, it seems the null response is still present. is there anyway to debug further to see why this is happening?
October 7th, 2008 at 1:43 pm
Hey John,
My pleasure - thanks for providing this library to the community!
One more thing…
It looks like the prepareParameters function can be removed - instead of using it and the rtrim() of ‘&’, you can just use the http_build_query function to do the same thing.
So POSTFIELDS in process() would look like:
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($this->params));
and then you can get rid of prepareParameters() and $this->fields alltogether!
Just a little optimization.
Overall this code is very helpful though - thanks!
October 7th, 2008 at 6:38 pm
update: you can test against authorize.net without having an SSL certificate. you need to specify curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); (which you have, but this is for when you use godaddy’s proxies only)
hopefully that helps others
October 7th, 2008 at 10:28 pm
Chris,
I gotta tell ya, PHP is just full of goodies! Looks like this function was added in PHP 5 and it looks darn useful. Based on what I saw in the PHP manual you are correct and that function would do exactly what prepareParameters() and $this->fields accomplish. Great catch!
Rob,
Thanks for pointing out that you can test over an insecure connection. Hopefully that will help others when they’re integrating this script into their sites. Although I should point out test mode and an Authorize.Net test account are two different things. All Authorize.Net accounts can operate in test mode but a test account must be applied for at Authorize.Net and requires a separate login and password. Their test account does require an SSL account to function normally AFAIK.
October 13th, 2008 at 12:19 am
“echo $e->__toString();” should be just “echo $e;”
php will execute the internal method __toString().
I think calling any method starting with “_” is considered a bad OOP if it is outside of the class itself.
Slavi
October 14th, 2008 at 2:36 pm
How can I use this information to capture and invoice #, amount, and name on a php form?
October 23rd, 2008 at 9:41 am
Just wanted to drop a quick line and say thanks for sharing this code! It’s been a big help!