Tutorial: Integrating the Authorize.Net AIM API with PHP

Working with the Authorize.Net AIM API in and of itself is very easy to do. Between the integration guide and sample code a good programmer can have a working solution in only minutes. But this code is for demonstration purposes only and isn’t suited for live production sites. A reusable concise solution is needed.

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. If you aren’t programming in PHP5 yet, you should be. If your host doesn’t support PHP5 yet, find a new host.

 

To begin you will need to include the class using the require() function:

1
require('AuthnetAIM.class.php');

As with any class/object the first thing we need to do is instantiate it. This can be done as follows:

1
$payment = new AuthnetAIM('myapilogin', 'mYtRaNsaCTiOnKEy');

Up to three parameters are accepted in constructor with the first two being required. myapilogin is the API login for the account. mYtRaNsaCTiOnKEy 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 the third parameters to TRUE to be true.

1
$payment = new AuthnetAIM('myapilogin', 'mYtRaNsaCTiOnKEy', true);

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('myapilogin', 'mYtRaNsaCTiOnKEy');
$payment->setTransaction($creditcard, $expiration, $total);
$payment->process();

First we create our Authorize.Net object and store it in a variable called $payment. We then called the setTransaction() 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 setTransaction() 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('myapilogin', 'mYtRaNsaCTiOnKEy');
$payment->setTransaction($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('myapilogin', 'mYtRaNsaCTiOnKEy');
$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('myapilogin', 'mYtRaNsaCTiOnKEy');
$payment->setTransaction($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 can 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
36
37
38
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();
 
    // Or just echo the message out ourselves
    echo $payment->getResponseMessage();
 
    // 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() method 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('myapilogin', 'mYtRaNsaCTiOnKEy');
$payment->setTransaction($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('myapilogin', 'mYtRaNsaCTiOnKEy');
    $payment->setTransaction($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 with a few extras thrown in to show you most of what this class can do:

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
require('AuthnetAIM.class.php');
 
try
{
    $user_id = 1;
    $email   = 'johnny@example.com';
    $product = 'A test transaction';
    $business_firstname = 'John';
    $business_lastname  = 'Smith';
    $business_address   = '123 Main Street';
    $business_city      = 'Townsville';
    $business_state     = 'NJ';
    $business_zipcode   = '12345';
    $business_telephone = '800-555-1234';
    $shipping_firstname = 'John';
    $shipping_lastname  = 'Smith';
    $shipping_address   = '100 Business Rd';
    $shipping_city      = 'Big City';
    $shipping_state     = 'NY';
    $shipping_zipcode   = '10101';
 
    $creditcard = '4111-1111-1111-1111';
    $expiration = '12-2016';
    $total      = 1.00;
    $cvv        = 123;
    $invoice    = substr(time(), 0, 6);
    $tax        = 0.00;
 
    $payment = new AuthnetAIM('myapilogin', 'mYtRaNsaCTiOnKEy', true);
    $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();
        $cvv_result     = $payment->getCVVResponse();
        $transaction_id = $payment->getTransactionID();
 
        // Do stuff with this. Most likely store it in a database.
        // Direct the user to a receipt or something similiar.
    }
    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();
 
        // OR
 
        // Capture a detailed error message. No need to refer to the manual
        // with this one as it tells you everything the manual does.
        $full_error_message =  $payment->getResponseMessage();
 
        // We can tell what kind of error it is and handle it appropriately.
        if ($payment->isConfigError())
        {
            // We misconfigured something on our end.
        }
        else if ($payment->isTempError())
        {
            // Some kind of temporary error on Authorize.Net's end. 
            // It should work properly "soon".
        }
        else
        {
            // All other errors.
        }
 
        // 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.

UPDATE 2009-09-16: I’ve updated this code a bit and have altered the tutorial to better reflect this. I’ve also, obviously, provided the newer code for download and changed the license to be LGPL from GPL.

UPDATE 2009-12-18: Apparently Godaddy.com got rid of their stupid proxy server so there’s no need to account for it. I’ve removed it from the code. I’ve also optimized handling connection errors and make exceptions thrown more informative.

Related Posts:

96 thoughts on “Tutorial: Integrating the Authorize.Net AIM API with PHP

  1. Is there any API to authenticate API_Login_ID and Transaction_key.

    Thanks in advance
    Pradeep.

  2. Hey Johnny,

    I have a couple quick questions. First, just letting you know I am a total newbie when it comes to php. I know the basics and learn pretty fast though.

    1. Ok so basically I have my (donate.php) and a (donate_confirm.php) and now the (AuthnetAIM.class.php).

    2. So do i use the AuthnetAIM.class.php as the form action that sends from my donate_confirm.php (this is the page that displays the info the customer entered)

    3 Ok and I am lost…I have the AuthnetAimclass but where do I put the rest of the code on the tutorial like……require(‘AuthnetAIM.class.php’);

  3. John,

    Thank you so much. You’ve created an API interface that is absolutely elegant when it comes to extensibility. Its a lifesaver since the API docs, while thorough on the send and receive info, are lacking a bit on “best practices” when it comes to PHP implementation which of course is beyond the scope of that doc.

    Even better is the great example of the PHP5 feature use, which has me running back to the manual to explore the new class and procedural concepts again, as there’s some key tools I’ve overlooked!

  4. Hey there, really beginner PHP user here. But do you have a sample implementation. IE a sample PHP form that uses this code to process it?

    That would be awesome!

  5. Hey John,

    My pleasure – thanks for providing this library to the community!

    One more thing…

    please tell me. How to refund for this?

  6. Hey John,

    Have Send me the Refund code for this. i try this but i’m not sure about this because i have thest account it refund.

    $transid=$_POST['TransctionId'];
    $type=’CREDIT';
    $payment->setTransaction($_POST['cardno'],$expdate,$refundamount,$_POST['cvv']);
    $payment->setParameter(“x_type”, $type);
    $payment->setParameter(“x_trans_id”,$transid);
    $payment->process();

    if ($payment->isApproved())
    {
    // process
    }

    else if ($payment->isError())
    {
    //$msg=” Try Again Later”;
    $error_number = $payment->getResponseSubcode();
    $error_message = $payment->getResponseText();

    // Or just echo the message out ourselves
    $msg= $payment->getResponseMessage();

    }
    else if ($payment->isDeclined())
    {
    $msg = $payment->getResponseText();

    }

    please advise me

    Thanks & Regrads

  7. Hi Johnny,

    As you have replied to George in above comments that we can use the AIM key in two different sites.

    I have 2 sites, for 1 site i am using AIM for a quite a long time and it is running smoothly but yesterday I configure AIM on my other site and used same AIM key on my other site it 1st worked fine. but today i checked again it is not working customers have placed orders but payment details are not shown in AIM. can you please help me?

  8. Hi Johnny

    First, THANKS for the work you did on this…saved me a lot of time! It looks like excellent work!

    Second, I noticed that you are casting the invoice number to an integer. Alot of invoice numbers are not just numeric…some have alpha characters embedded in them. You may want to think about being a little less stringent on the format as the API documentation does not require a specific data type.

  9. Thanks Johnny

    One other thing I added was a getResponseString property because I always like to store the raw return data for later…

  10. Hello Johny.
    Please tell me that where should i place this code and how i will start it working.?
    please help me.
    Thanks in advance.

  11. I was wondering if you had a sample input form that can be used to get the information from the visitor?

    I haven’t done this in so long that I’m way too rusty.

    Thanks

  12. Hey, I just wanted to say thanks for providing this. Its been a huge time saver for me.

    You should consider adding a “Buy me a Coffee” paypal widget to your page.
    I’d certainly buy you one. =)

    Cheers

  13. Absolutely great script. Thanks. Would donate if you had somewhere to do so….

    Maybe you should make your own “Buy me a coffee” widget, with this Authorize.net class of course!

  14. I really loved this class.

    It is really simple to implement for the newbie like me.
    Thanks Jonny.. You made my day.

  15. Wanted to say thanks for this and the CIM class. I do have one suggestion/improvement for this class.

    On line 187 it has:

    unset($this->ch);

    which will actually destroy the private variable and the next time you try to access it will default to the __set() call. This will throw errors if you call process() again (i.e. you are using a single instance for multiple payments). I fixed this with:

    $this->ch = null;

    which clears the variable but doesn’t destroy it.

  16. helo sir,
    First of all it was a gr8 tutorial!!!!!!

    i have a question,
    i am trying to use the basic idea you’ve provided and trying to make a code in python to perform the transaction.

    i made a code and it provides proper outputs and response reason text.
    my question is after getting the “TRANSACTION HAS BEEN APROOVED” message i need to perform the actual fund transferring.
    what future steps must be taken to perform the actual tranfer of funds.

    i have created a test account in authorize.net……what should be next course of action.
    plz help

  17. Hi,

    First of all, awesome stuff. Thanks. That said, I have a problem that’s not so much related to your code as AUTH.NET itself. Basically, I’m getting declined transactions (which is expected) that I can’t VOID. Although they are in the “UNSETTLED REGISTER” when I log in, the error message returned when I try to VOID them is “The transaction cannot be found.”

    Any ideas as to what could be happening?

    Best.

  18. Only transactions that have been approved can be voided. Declined transactions cannot be voided since they essentially don’t exist anymore.

  19. I see. Well, the problem we’re experiencing is customers still have a hold on their credit cards (or bank accounts) for the attempted amount, despite the transaction being declined.

    What is the recommended mechanism for preventing this from happening or purging/releasing these amounts?

    Best.

  20. That’s kind of bizarre. Declined transactions shouldn’t hold any funds on the cardholder’s account since it was never approved. This is especially true when the card is declined for insufficient funds. You may want to speak with your merchant account provider and see if they can explain this as it is not how a merchant account is supposed to function.

  21. It would appear that we’re getting holds on AVS declines which means that all info (except the address) is correct. Apparently, per AUTH.NET, these appear as declined, but a hold still occurs and they’ll settle 3 days later. This makes absolutely no sense.

    We’re getting back on the horn with AUTH.NET. I’ll post back what I hear.

    Thanks again.

  22. First thing, thank you a great deal for this class, it has made integration of authorize.net a breeze.

    Second, I have a quick question: Is the credit card information required for processing a CREDIT transaction? Or is the x_trans_id and amount sufficient?

  23. For a credit transaction you should also need the last four digits of the credit card number. If you don’t have it you can get it by using the new Transaction Detail API. Just send the transaction number over and it will return everything about that transaction including the last four digits of the credit card number.

  24. John,

    I’m running across a new issue. When submitting a credit card on a LIVE account with the credit card number of 4111111111111111, the TRY CATCH isn’t true or false, and just continues on (in my case, creates a user profile) … but once I looked at Authorize.net, I saw “General Error” for unsettled transactions.

    I suppose that in this case, even though there is an error code being generated, your AIM class does not deal with this error? I’m not sure how to handle this.

  25. Thank you!

    I was killing myself with other alternatives, but this is SO easy. Thanks!

    Daniel

  26. Great code example using PHP, You wouldn’t happen to have a C# code tutorial like this one, would you ?

  27. best authorize.net implementation tutorial i’ve seen to date. Nice work.
    Have you confirmed this code still works with current setup of API?
    I’m planning to test out this code with a few nonprofit clients but before I jump into it just wanted to check.

  28. A million thanks. Absolutely what i was looking for. I guess it might take atleast 30 days to build a class like this from api documentation of authorize.net

    You saved me all time.

    Million thanks

  29. Do you always need the card number to issue a refund or cancel and order?

    Isn’t the transaction ID and authorization code enough information?

    Thanks,
    Gary

  30. thanks , your code works fine with test details for AIM but when i entered wrong cvv number still it shows successful transaction , don’t know how to resolve this issue ..

  31. I’ll google this but thought I’d ask here too. What’s the different between VOID and CREDIT? I can only VOID a transaction_id once it seems, and the amount I set seems irrelevant (i can set below or above the original amount). Whereas with Credit, it seems I can endlessly apply credits (on my test account, at least…).

    Basically I want to know what I should do if I have a fraudulent order that has already settled (captured). Should I send a “VOID”? or CREDIT? or something else? I have just been calling the bank in the past and having them do it but I’d like to do it myself if I can. thanks!

  32. Hey,
    this is a great post. I am using authorize.net’s php SDK. Everything works OK with USD currency. But I want to use CAD currency. Is there any way to set the currency for the transaction.

    Thanks in advance.

  33. Hi I have a question, how do you VOID a transaction after the card is authorized using AUTH_ONLY (using your library)? Thanks!

  34. Hey Johnny,
    What if the gateway admin wants to refund some amount. For example the merchant quoted up the price of 1000bucks and only item of 500buck was delivered to customer.. Now refund of 500buck is possible or not?
    If yes then please help me out.

  35. I have just one doubt. I am sending customer ip to aut.net fine i can even see that in main they send. But what my issue is when i open my transaction in aut.net site why under “Authorization Information” Customer IP is still showing blank??

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>