Tutorial: Integrate All Authorize.Net XML APIs With One Universal PHP Class (AIM, ARB, CIM, Transaction Details)

Github

If you find this tutorial and code useful, please take a moment to star it on Github. If you want to help develop it futher, fork it.

This class has been replaced by a new package which utilizes the new JSON APIs introduced by Authorize.Net. You can find the new AuthnetJSON Package on Github. Don’t forget to read the tutorial to see how to implement it into your project.

Some of the most popular articles on this website are tutorials for using PHP code that I have written specifically to interface with each of their respective Authorize.Net’s APIs. It started with the Integrate the Authorize.Net AIM API with PHP Tutorial which was followed by the Integrate the Authorize.Net ARB API with PHP Tutorial and the Integrate the Authorize.Net CIM API with PHP Tutorial. The AIM API had a unique name/value pair format which only required changing the transaction type parameter (x_type) to switch between transaction types (AUTH_CAPTURE, CREDIT, VOID, etc). When the ARB and CIM APIs were introduced they differed from the AIM API by using XML for the their API calls and response. These classes offered methods that corresponded directly to its respective API call (createSubscription mapped to ARBCreateSubscriptionRequest, etc).

In August 2009, Authorize.Net released an XML API for their Advanced Integration Method (AIM) API. This means all of their payment APIs now shared a common interface. As a result we had an opportunity to consolidate all of our PHP-based Authorize.Net classes into one universal class. The benefits of this include:

  • Simplifying our code if we need to use more than one API
  • Offer a consistent interface which makes developing Authorize.Net based applications easier to do
  • Increases our flexibility if Authorize.Net offers new XML based APIs (their Transaction Detail API being a perfect example of this)

Let’s take a look and see how this class works.

Getting Started With The AuthnetXML Class

Requirements

  • PHP 5.2 or newer
  • cURL PHP Extension
  • SimpleXML

To follow this tutorial and integrate an Authorize.Net XML API into your PHP website follow these steps:

  1. If you don’t already have one, sign up for an Authorize.Net Developer Account. This will allow you to test your code without having an actual Authorize.Net account or incurring any processing fees.
  2. Download the AuthnetXML Package which includes the AuthnetXML PHP class and sample code.

Included in the AuthnetXML Package is sample code for every API call supported by the class (AIM, ARB, CIM, and Transaction Detail). They all work in the same manner with the only differences being the information required and response returned for that particular API call. So, to demonstrate how an API call is made using the AuthnetXML class, we will make calls to the CIM API to create a customer profile and handle the response. But before we get started, we will look at how the class works as understanding this makes using it very easy regardless of which API you will be using.

PHP’s Magic Methods

PHP5 introduced greatly improved support for object oriented programming. One aspect of this improved support was the addition of “magic methods”. Magic methods act as interceptors when certain conditions are met, usually when our code attempts to call a class method or property that doesn’t exist. The AuthnetXML class uses three magic methods although only two of them are actually used when implementing Authorize.Net’s APIs. It’s these magic methods that make the AuthnetXML class possible. They are:

  • __call()

    This magic method is the heart of our class. The __call() magic method is called when a method is used that doesn’t exist in the calling class. We can take advantage of this to make calls to any of the XML APIs without having to specify which one ahead of time just by using the name of the API call as the name of the method being invoked. We will see an example of how we use this demonstrated below.

  • __get()

    After making an API call we will need to access the specific results of that call to see if it was successful, and, if so, get any possible values that we will need for the business logic in our software. Just as with the __call() magic method, we want to dynamically access these results without resorting to hardcoding them into our class. __get() allows us to do this by allowing us to request the value of a property that we never declared in our class and have PHP automagically retrieve it for us from the response.

  • __toString()

    __toString() is not a fundemental magic method in our class. However, it is a very handy one. By overloading it we can make developing and troubleshooting Authorize.Net applications much easier to do. In the AuthnetXML class I have set it to display the key parameters (login ID, transaction key, cURL handle, Authorize.Net API URL being used, request XML, and response XML) allowing us to quickly and easily see the inner workings of an API call.

Now that we understand how our PHP class will work under the hood, we can see some examples of its use.

Including The AuthnetXML Class

To use the AuthnetXML Class you must include it in the PHP file that will be making our API call. You will use PHP’s require() function to do this:

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

Authentication

All calls to Authorize.Net’s APIs require authentication before an API call is made. If authentication fails the rest of the API call will not be processed. (So if you have errors in your API call you won’t know until your API call passes authentication). The AuthnetXML Class automatically handles authentication for us when the class is first instanciated. We do this by passing two or three varibales to the AuthnetXML constructor. It is defined as such:

public AuthnetXML::__construct() (string $api_login, string $transaction_key
[, bool $production_server = true]);

The first two parameters, $api_login and $transaction_key, are the Authorize.Net API login and Transaction Key, respectively, of the Authorize.Net account we will be using (which will probably be the your developer account at first). Both parameters are required. The third parameter, $production_server, allows us to switch back and forth between the developer server we use for testing and the production server which will process live transactions.

Here’s how it could look in your code:

1
2
3
4
    define('AUTHNET_LOGIN', 'yourloginid');
    define('AUTHNET_TRANSKEY', 'yourtransactionkey');
    $xml = new AuthnetXML(AUTHNET_LOGIN, AUTHNET_TRANSKEY,
        AuthnetXML::USE_DEVELOPMENT_SERVER);

There are two things worth noting in the above code:

  1. I put the API login and transaction key into constants (AUTHNET_LOGIN and AUTHNET_TRANSKEY respectively). These values will never change in our code so there’s no reason for them to be placed in variables. They ultimately should be placed in a configuration file for your application.
  2. I used another fetaure offered by PHP 5, class constants, to set the third parameters of the class constructor to false. By using the built in class constant USE_DEVELOPMENT_SERVER our code becomes more readable and easier to maintain. (At least in this tutorial. There are even better ways to do this in more complex applications).

Assuming we have provided the correct information, the AuthnetXML class will automatically create the XML for the authentication for us and also select the proper URL so we don’t have to.

Making An API Call

Now that we have taken care of authentication we can focus on passing the data required for making an API call. Our first API call will be to create a customer profile and payment profile for a ficticious user. The CIM API call to do this is createCustomerProfileRequest (see page 18 of the CIM guide for more information on this API call). As explained earlier in this tutorial we will call the createCustomerProfileRequest() method which is created for us “magically” by PHP with the magic method __call(). This is done with the following code:

1
2
// Just a stub
$xml->createCustomerProfileRequest(/* API call parameters go here */);

Our next step is to create, and then pass, the associative array that contains the parameters needed by the createCustomerProfileRequest API call. We will use a little bit of PHP recursion magic behind the scenes here to make this happen. Inside of our __call() magic method we call a private method named setParameters() to take that array and convert it into the XML we will need to send to Authorize.Net as part of our API call.

setParameters() takes two parameters: the first is the XML we are constructing to make our API call, the second is the value we are attaching to that XML. setParameters() checks to see if the second parameter it is passed is an array or a scalar variable. If it is a scalar variable it adds it as a child node to the XML. Since we’re using SimpleXML its value is automatically appended for us. If it is an array it adds a child node to the XML and then calls itself recursively passing name of the array as the first parameter of setParameters() and the array itself as the second parameter continuing the recursive cycle. This continues until our originial array of parameters is completely processed.

Let’s see an actual example of how this works. First we will create a customer profile. You can see the example demonstrated here in the AuthnetXML Package under /examples/CIM/createCustomerProfileRequest.php.

Create A Customer Profile

We already know that our AuthnetXML class is powered by the PHP __call() magic method, using it to dynamically create our API call for us behind the scenes. Inside of that magic method we call our own setParameters() private method to take the associative array of paramaters we want to pass to the API and transform it into the XML that will form the body of the XML used in the API call. Let’s take a look at the XML we will need to create so we will know what we will need to construct our associative array:

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
< ?xml version="1.0"?>
<createcustomerprofilerequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
  <merchantauthentication>
    <name>yourloginid</name>
    <transactionkey>yourtransactionkey</transactionkey>
  </merchantauthentication>
  <profile>
    <merchantcustomerid>12345</merchantcustomerid>
    <email>user@example.com</email>
    <paymentprofiles>
      <billto>
        <firstname>John</firstname>
        <lastname>Smith</lastname>
        <address>123 Main Street</address>
        <city>Townsville</city>
        <state>NJ</state>
        <zip>12345</zip>
        <phonenumber>800-555-1234</phonenumber>
      </billto>
      <payment>
        <creditcard>
          <cardnumber>4111111111111111</cardnumber>
          <expirationdate>2016-08</expirationdate>
        </creditcard>
      </payment>
    </paymentprofiles>
    <shiptolist>
      <firstname>John</firstname>
      <lastname>Smith</lastname>
      <address>123 Main Street</address>
      <city>Townsville</city>
      <state>NJ</state>
      <zip>12345</zip>
      <phonenumber>800-555-1234</phonenumber>
    </shiptolist>
  </profile>
  <validationmode>liveMode</validationmode>
</createcustomerprofilerequest>

Here’s that same XML as a nested assocoative array:

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
array(
    'profile' => array(
        'merchantCustomerId' => '12345',
        'email' => 'user@example.com',
        'paymentProfiles' => array(
            'billTo' => array(
                'firstName' => 'John',
                'lastName' => 'Smith',
                'address' => '123 Main Street',
                'city' => 'Townsville',
                'state' => 'NJ',
                'zip' => '12345',
                'phoneNumber' => '800-555-1234'
            ),
            'payment' => array(
                'creditCard' => array(
                'cardNumber' => '4111111111111111',
                'expirationDate' => '2016-08',
                ),
            ),
        ),
        'shipToList' => array(
            'firstName' => 'John',
            'lastName' => 'Smith',
            'address' => '123 Main Street',
            'city' => 'Townsville',
            'state' => 'NJ',
            'zip' => '12345',
            'phoneNumber' => '800-555-1234'
        ),
    ),
    'validationMode' => 'liveMode'
)

You’ll notice the authentication is not included as the AuthnetXML class will add that automatically for us.

For the sake of clarity and completeness, here is how you pass those parameters to the API and make an API call to create a customer payment profile:

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
    $xml->createCustomerProfileRequest(array(
        'profile' => array(
            'merchantCustomerId' => '87657',
            'email' => 'user@example.com',
            'paymentProfiles' => array(
                'billTo' => array(
                    'firstName' => 'John',
                    'lastName' => 'Smith',
                    'address' => '123 Main Street',
                    'city' => 'Townsville',
                    'state' => 'NJ',
                    'zip' => '12345',
                    'phoneNumber' => '800-555-1234'
                ),
                'payment' => array(
                    'creditCard' => array(
                    'cardNumber' => '4111111111111111',
                    'expirationDate' => '2016-08',
                    ),
                ),
            ),
            'shipToList' => array(
                'firstName' => 'John',
                'lastName' => 'Smith',
                'address' => '123 Main Street',
                'city' => 'Townsville',
                'state' => 'NJ',
                'zip' => '12345',
                'phoneNumber' => '800-555-1234'
            ),
        ),
        'validationMode' => 'liveMode'
    ));

Handling The Response

There are some universal responses that can be found in every XML API response. These basic responses can tell you if the API call was successful or if an error occurred. Some are redundant and are offered to conform to different coding styles. These responses are:

  • $xml->messages->resultCode

    This will return Ok or Error if the API call is successful or not respectively. An API call is determined to be successful if the result code has a value of “Ok”. If a transaction is declined or no records are returned a transaction can still be considered successful.

  • $xml->messages->message->code

    All API calls return a code that offers more information about a transaction. Most are related to error messages but some are related to success messages. Codes that start with I are associated with successful API calls. Codes that start with E are associated with API calls that resulted in errors.

  • $xml->isSuccessful() and $xml->isError()

    These return true or false if the API call is successful or not respectively. The criteria for success is identical as $xml->messages->resultCode.

Getting to the data from an API call is fairly straightforward. As mentioned in the “Getting Started” section above, we use the __get() magic method to dynamically retrieve a value depending on the format of the XML response which will vary depending on the API call. We will need to know the structure of the response to properly retrieve any values that may be of use to us. We can get these from the associated integration guides available on the Authorize.Net website. You can also see examples with each API call in the sample code included in the AuthnetXML package.

For our example customer profile request, if our API call was successful we will receive a customer profile ID, a payment profile ID, and a shipping address ID. A very basic way to check to see if our API call was successful, and if so, access and save those values would be:

1
2
3
4
5
6
7
8
< ?php
  if ($xml->isSuccessful())
  {
    $customer_profile_id = $xml->customerProfileId;
    $payment_profile_id  = $xml->customerPaymentProfileIdList->numericString;
    $shipping_profile_id = $xml->customerShippingAddressIdList->numericString;
  }
?>

Naturally the values that are returned will vary depending on the API you are using and you should always read the API documentation so you are aware of what values may be returned to you. Remember, you can echo out the $xml object and see entire response, including the XML structure, which can help you find any value you are looking for.

Support

I do not officially support this software. Unofficially, I want to make sure this code is bug free and easy to use and helping others use it is a great way to ensure that. If you are having difficulty using this code please do not contact me through this website. Since this software is free to the community, the community can assist me in supporting it. I am an active participant at StackOverflow and in Authorize.Net’s community forums. Others also frequent it are also capable of assiting you with this code. When posting your question there, or anywhere for that matter, be sure to include the following:

  • A link to either this tutorial or the github repository so others can see what software you are using and can download it if necessary so if they attempt to reproduce the problem you are having.
  • Your code as it is implemented in your software. Make sure you format it so it is readable by others.
  • A description of what you are expecting your code to do but not seeing happen.
  • Any error message you are getting.

Related Posts:

9 thoughts on “Tutorial: Integrate All Authorize.Net XML APIs With One Universal PHP Class (AIM, ARB, CIM, Transaction Details)

  1. Hi John,

    Thanks for your valuable tutorial.. I need to clear one doubts with you. I am working on pinnacle cart product.AIM is a default payment. Here I have manually used ARB for automatic renewal. I mean, if any user purchase ‘Membership’, i will use AIM for payment transaction. After transaction, will do renewal for next year using ARB Payment. Is it fine?.. shall we use AIM and ARB at a time?

    Waiting for your valuable feedback.

  2. Your plan is exactly how you should do it. Always do the first payment with AIM so you get immediate feedback on the legitimacy of a credit card. Then, if the transaction is approved, set up the subscription using ARB.

  3. Nice clever idea, but you got one large bug in it. XML like this:


    <userFields>

    <userField>
    <name>MerchantDefinedFieldName1</name>
    <value>MerchantDefinedFieldValue1</value>
    </userField>

    <userField>
    <name>favorite_color</name>
    <value>blue</value>
    </userField>

    </userFields>

    Which you show as being encoded as an array


    array(
    'userFields' => array(

    'userField' => array(
    'name' => 'MerchantDefinedFieldName1',
    'value' => 'MerchantDefinedFieldValue1',
    ),
    'userField' => array(
    'name' => 'favorite_color',
    'value' => 'blue',
    ),
    ),
    );


    Cannot be input with an arry as you indicate. Only the last instance of multiple keys “userField” is going to survive because associative arrays don’t allow duplicate keys. It just gets replaced!

    Even your examples show the problem if you look at the sample XML. Only one instance of “userField”. Well I could work around that but “settings” requires 4 items and I’m not sure they are optional.

    But I still like it well enough that I’m going to see if I can adapt it.

  4. Hi John,

    Thanks for wonderful tutorial…
    I must admit at the outset that I am a complete newbie to this kind of stuff, but I was trying to follow the steps in this tutorial and I keep getting a “User authentication failed due to invalid authentication values.”
    I have triple checked my api login and transkey with authorize.net and they are fine, was wondering if you had any idea what it could be??
    Thanks so much for your time…

  5. Izzy,

    Make sure you are developing using a developer account if you have the third parameter of the constructor set to AuthnetXML::USE_DEVELOPMENT_SERVER. If you are using a production account simply remove that third parameter altogether.

  6. Arnold,

    You are correct and I appreciate you opening a bug on Github for me. I did fix this and it pushed it to the repository. Thanks again for the feedback as it is appreciated.

  7. John-
    Are there test credit cards that will return error conditions such as invalid CVV or address mis-match. Stripe lists several on their site:
    https://stripe.com/docs/testing

    I was wondering if authorize.net has some that they generate error codes.
    This helps developers to make sure they account for all error conditions.

Leave a Reply

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