Tutorial: Integrate the Authorize.Net CIM API with PHP

This tutorial has been deprecated. You should read the new tutorial, “Tutorial: Integrate All Authorize.Net XML APIs With One Universal PHP Class (AIM, ARB, CIM, Transaction Details)” which offers new code for working with Authorize.Net’s XML APIs including sample code for every API call.

Just like Authorize.Net’s Advanced Integration Method (AIM) API and Automated Recurring Billing (ARB) APIs, working with their Customer Information Manager (CIM) API isn’t difficult if you have suitable code to work with. The sample code offered by Authorize.Net will demonstrate how to work with their API but is not production ready. As with their AIM API and ARB API I have created a class that abstracts their CIM API into something easier to use and maintain.

To follow this tutorial and integrate CIM API into your PHP website follow these steps:

  1. If you don’t already have one sign up for a 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 CIM Integration Guide (for reference)
  3. Download the PHP code for CIM

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

require('AuthnetCIM.class.php');

As with any class/object the first thing we need to do is instantiate it. Up to three parameters are accepted in the 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 (or assigned to you with your test account information). If you have an Authorize.Net developer account you can use it with this script by setting the third parameters to TRUE (you may omit the third parameter if you are using this with the live server):

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

There are quite a few things you can do through the CIM API all of which fall into one of three categories: createXXXXXXX, updateXXXXXX, and deleteXXXXXX where XXXXXXX is the request type (CustomerProfileRequest, CustomerPaymentProfileRequest, CustomerShippingAddressRequest, and CustomerProfileTransactionRequest). Since updating a profile is almost identical to creating a profile (you just call a updateXXXXXX instead of createXXXXXX) and deleting a profile is very straightforward this tutorial will focus solely on the creating of profiles and processing payments.

There can be as many as four API calls to create profiles and process a payment although only three are actually necessary. They are:

  1. createCustomerProfile() – Creates the customer’s payment profile which serves as their master record. There should be only one payment profile per customer.
  2. createCustomerPaymentProfile() – Creates a payment profile for a customer. There should be one of these for every credit card or checking account the customer wishes to use.
  3. createCustomerShippingAddress() – (Optional) Creates a shipping profile for a customer. There should be one of these for every shipping address the customer wishes to use.
  4. createCustomerProfileTransaction() – Processes a transaction for a customer.

Before we can process any payments for a customer we must create their main profile. There are two fields you should provide although I am going to show three since combined they serve a significant purpose. These three fields are important because if all three fields match another profile exactly, meaning all three fields are identical between both profiles in every way, the profile will not be created. If one of the fields is different, it doesn’t matter which one, the profile will be created.

To create customer profile you can use this code (naturally you can change the values passed to each parameter):

$cim->setParameter('email', 'fakeemail@example.com');
$cim->setParameter('description', 'Profile for Joe Smith'); // Optional
$cim->setParameter('merchantCustomerId', '123456789');
$cim->createCustomerProfile();
$profile_id = $cim->getProfileID();

The first three lines set the necessary parameters for createCustomerProfile() to function properly. email is the customer email address, description is a description of the profile, and merchantCustomerId is your customer ID number (e.g. one generated by your system). The fourth line calls the CIM API to create the customer profile. The fifth line retrieves the customer profile ID created by Authorize.Net. This number is important as it is required to process all CIM requests for this profile. This means you should store it along with any other data you store in your database for this customer.

Now that we have created our customer profile we need to create at least one payment profile to use to process their payments:

$cim->setParameter('customerProfileId', $profile_id);
$cim->setParameter('billToFirstName', 'Joe');
$cim->setParameter('billToLastName', 'Smith');
$cim->setParameter('billToAddress', '123 Main Street');
$cim->setParameter('billToCity', 'Townsville');
$cim->setParameter('billToState', 'NJ');
$cim->setParameter('billToZip', '12345');
$cim->setParameter('billToCountry', 'US');
$cim->setParameter('billToPhoneNumber', '800-555-1234');
$cim->setParameter('billToFaxNumber', '800-555-2345');
$cim->setParameter('cardNumber', '4111111111111111');
$cim->setParameter('expirationDate', '2016-12');
$cim->createCustomerPaymentProfile();
$payment_profile_id = $cim->getPaymentProfileId();

The first line refers to the profile we wish to attach this payment profile to. The next nine lines set the billing information for the user’s credit card including their billing address (i.e. the address they have on file with their credit card issuing bank). The next two lines set the credit card information for the payment profile. The last two lines call the CIM API to create the payment profile and retrieve its ID which should be stored in your database.

Now we can create our shipping profile:

$cim->setParameter('customerProfileId', $profile_id);
$cim->setParameter('shipToFirstName', 'Joe');
$cim->setParameter('shipToLastName', 'Smith');
$cim->setParameter('shipToAddress', '123 Main Street');
$cim->setParameter('shipToCity', 'Townsville');
$cim->setParameter('shipToState', 'NJ');
$cim->setParameter('shipToZip', '12345');
$cim->setParameter('shipToCountry', 'US');
$cim->setParameter('shipToPhoneNumber', '800-555-1234');
$cim->setParameter('shipToFaxNumber', '800-555-2345');
$cim->createCustomerShippingAddress();
$shipping_profile_id = $cim->getCustomerAddressId();

Since creating a shipping profile is nearly identical to creating a billing profile I won’t elaborate on it other then to say the names of the parameters are different and no payment information is provided.

The final step is to process a payment:

$cim->setParameter('amount', $purchase_amount);
$cim->setParameter('customerProfileId', $profile_id);
$cim->setParameter('customerPaymentProfileId', $payment_profile_id);
$cim->setParameter('customerShippingAddressId', $shipping_profile_id);
$cim->createCustomerProfileTransaction();
$approval_code = $cim->getAuthCode();

This is pretty straight forward. We start by setting the amount we wish to charge. Then we tell the CIM API which customer profile, payment profile, and shipping profile to use. Finally we use the CIM API to process the transaction and we retrieve the approval code.

The AuthnetCIM class takes advantage of exceptions which are new in PHP5. These are only generated when an “exceptional” event occurs. The only exceptional event that might occur when using this class is cURL may fail. This may be due to server, network, or user errors. To catch these exceptions and handle them use code similar to this:

try
{
    $cim = new AuthnetCIM('myapilogin', 'mYtRaNsaCTiOnKEy');
    // Do more CIM stuff here
}
catch (AuthnetCIMException $e)
{
    echo 'There was an error processing the transaction. Here is the error message: ';
    echo $e->__toString();
}

Now that we have an idea of how to use CIM to create profiles and process transactions, let’s put it all together into one script to demonstrate how it all works together. This one has some minor changes and additions that should better demonstrate how to use the AuthnetCIM class and should also make tinkering with this code easier to do:

< ?php
// Include AuthnetCIM class. Nothing works without it!
require('AuthnetCIM.class.php');
 
// Use try/catch so if an exception is thrown we can catch it
// and figure out what happened
try
{
    // Create AuthnetCIM object. Set third parameter to "true" for developer account
    // or use the built in constant USE_DEVELOPMENT_SERVER for better readability.
    $cim = new AuthnetCIM('myapilogin', 'mYtRaNsaCTiOnKEy',
                                               AuthnetCIM::USE_DEVELOPMENT_SERVER);
 
    // Step 1: create Customer Profile
    //
    // Create unique fake email address, description, and customer ID
    $email_address = 'user' . time() . '@domain.com';
    $description   = 'Monthly Membership No. ' . md5(uniqid(rand(), true));
    $customer_id   = substr(md5(uniqid(rand(), true)), 16, 16);
 
    // Create the profile
    $cim->setParameter('email', $email_address);
    $cim->setParameter('description', $description);
    $cim->setParameter('merchantCustomerId', $customer_id);
    $cim->createCustomerProfile();
 
    // Get the profile ID returned from the request
    if ($cim->isSuccessful())
    {
        $profile_id = $cim->getProfileID();
    }
 
    // Print the results of the request
    echo '<strong>createCustomerProfileRequest Response Summary:</strong> ' .
                                          $cim-&gt;getResponseSummary() . '';
    echo '<strong>Profile ID:</strong> ' . $profile_id . '
 
';
 
    // Step 2: create Payment Profile
    //
    // Create fake user billing information
    $b_first_name   = 'John';
    $b_last_name    = 'Conde';
    $b_address      = '123 Main Street';
    $b_city         = 'Townsville';
    $b_state        = 'NJ';
    $b_zip          = '12345';
    $b_country      = 'US';
    $b_phone_number = '800-555-1234';
    $b_fax_number   = '800-555-2345';
    $credit_card    = '4111111111111111';
    $expiration     = (date("Y") + 1) . '-12';
 
    // Create the Payment Profile
    $cim-&gt;setParameter('customerProfileId', $profile_id);
    $cim-&gt;setParameter('billToFirstName', $b_first_name);
    $cim-&gt;setParameter('billToLastName', $b_last_name);
    $cim-&gt;setParameter('billToAddress', $b_address);
    $cim-&gt;setParameter('billToCity', $b_city);
    $cim-&gt;setParameter('billToState', $b_state);
    $cim-&gt;setParameter('billToZip', $b_zip);
    $cim-&gt;setParameter('billToCountry', $b_country);
    $cim-&gt;setParameter('billToPhoneNumber', $b_phone_number);
    $cim-&gt;setParameter('billToFaxNumber', $b_fax_number);
    $cim-&gt;setParameter('cardNumber', $credit_card);
    $cim-&gt;setParameter('expirationDate', $expiration);
    $cim-&gt;createCustomerPaymentProfile();
 
    // Get the payment profile ID returned from the request
    if ($cim-&gt;isSuccessful())
    {
        $payment_profile_id = $cim-&gt;getPaymentProfileId();
    }
 
    // Print the results of the request
    echo '<strong>createCustomerPaymentProfileRequest Response Summary:</strong> ' .
                                              $cim-&gt;getResponseSummary() . '';
    echo '<strong>Payment Profile ID:</strong> ' . $payment_profile_id . '
 
';
 
    // Step 3: create Shipping Profile
    //
    // Create fake user shipping information
    $s_first_name   = 'John';
    $s_last_name    = 'Conde';
    $s_address      = '1001 Other Road';
    $s_city         = 'Townsville';
    $s_state        = 'NJ';
    $s_zip          = '12345';
    $s_country      = 'US';
    $s_phone_number = '800-555-3456';
    $s_fax_number   = '800-555-4567';
 
    // Create the shipping profile
    $cim-&gt;setParameter('customerProfileId', $profile_id);
    $cim-&gt;setParameter('shipToFirstName', $s_first_name);
    $cim-&gt;setParameter('shipToLastName', $s_last_name);
    $cim-&gt;setParameter('shipToAddress', $s_address);
    $cim-&gt;setParameter('shipToCity', $s_city);
    $cim-&gt;setParameter('shipToState', $s_state);
    $cim-&gt;setParameter('shipToZip', $s_zip);
    $cim-&gt;setParameter('shipToCountry', $s_country);
    $cim-&gt;setParameter('shipToPhoneNumber', $s_phone_number);
    $cim-&gt;setParameter('shipToFaxNumber', $s_fax_number);
    $cim-&gt;createCustomerShippingAddress();
 
    // Get the payment profile ID returned from the request
    if ($cim-&gt;isSuccessful())
    {
        $shipping_profile_id = $cim-&gt;getCustomerAddressId();
    }
 
    // Print the results of the request
    echo '<strong>createCustomerShippingAddressRequest Response Summary:</strong> ' .
                                               $cim-&gt;getResponseSummary() . '';
    echo '<strong>Shipping Profile ID:</strong> ' . $shipping_profile_id . '
 
';
 
    // Step 4: Process a transaction
    //
    // Create fake transaction information
    $purchase_amount = '5.00';
 
    // Process the transaction
    $cim-&gt;setParameter('amount', $purchase_amount);
    $cim-&gt;setParameter('customerProfileId', $profile_id);
    $cim-&gt;setParameter('customerPaymentProfileId', $payment_profile_id);
    $cim-&gt;setParameter('customerShippingAddressId', $shipping_profile_id);
    $cim-&gt;setParameter('cardCode', '123');
    $cim-&gt;setLineItem('12', 'test item', 'it lets you test stuff', '1', '1.00');
    $cim-&gt;createCustomerProfileTransaction();
 
    // Get the payment profile ID returned from the request
    if ($cim-&gt;isSuccessful())
    {
        $approval_code = $cim-&gt;getAuthCode();
    }
 
    // Print the results of the request
    echo '<strong>createCustomerProfileTransactionRequest Response Summary:</strong> ' .
                               $cim-&gt;getResponseSummary() . '';
    echo '<strong>Approval code:</strong> ' . $approval_code;
}
catch (AuthnetCIMException $e)
{
    echo $e;
    echo $cim;
}
?&gt;

Obviously this code is just one part of a larger system of managing user data. But this should make interacting with the Authorize.Net CIM API much easier to do allowing you to focus more on your user experience then learning new APIs.

Related Posts:

48 thoughts on “Tutorial: Integrate the Authorize.Net CIM API with PHP

  1. I really like the class you created for CIM Authorize.net managment. It is better designed than another one that I found on the web. I built a nice solution with your class and it was working great when I went to the test server, but when I switched it to production (and changed nothing else) I get this error:

    Notice: AuthnetCIM::setParameter() parameter “value” is empty or missing. in /home2/freecig/public_html/admin/AuthnetCIM.class.php on line 629

    Notice: AuthnetCIM::setParameter() parameter “value” is empty or missing. in /home2/freecig/public_html/admin/AuthnetCIM.class.php on line 629

    I spent a good day trying to debug it. I found that even if I hard-code the value passed into

    Warning: SimpleXMLElement::__construct() [simplexmlelement.--construct]: Entity: line 1: parser error : Start tag expected, ‘__construct(‘XMLSchema-insta…’) #1 /home2/freecig/public_html/admin/AuthnetCIM.class.php(123): AuthnetCIM->parseResults() #2 /home2/freecig/public_html/admin/AuthnetCIM.class.php(387): AuthnetCIM->process() #3 /home2/freecig/public_html/admin/process-payment.php(35): AuthnetCIM->createCustomerProfileTransaction() #4 {main} thrown in /home2/freecig/public_html/admin/AuthnetCIM.class.php on line 637

    The problem is in parseResults(). Specifically at this line: $xml = new SimpleXMLElement($response);

    I found that even if I hard-coded the exact same string that was returned when going through the test server and set that for $response and pass it into SimpleXMLElement it blows up with a similar error.

    I would like to use your class but I am stuck. Do you know if the production api handles xml differently? Did you encounter this problem at all?

  2. Chad,

    I’m guessing that the live server is sending back a slightly different response which is causing the XML parser to bork out on us. I made a change that should deal with this if that is indeed the case. Download a new copy of the class and let me know how it goes.

    John

  3. Pingback: Bookmarks for April 15th from 14:55 to 14:55 | Buddy's Blog

  4. I don’t think the updateCustomerPaymentProfile() method will work – you left out the customerPaymentProfileId in this function.

    Since you can have multiple payment profiles per customer, it can’t know which one it’s supposed to update.

    In the example shown for this request in the documentation, they do include it, and I’m sure it must be required? Although, in the documentation itself, they forgot it – but I don’t think that means it’s going to work…

  5. And another one – in deleteCustomerPaymentProfile() you’re passing the customerProfileId as the customerPaymentProfileId:

    ‘.$this->params['customerProfileId'].’

    This should read:

    ‘.$this->params['customerPaymentProfileId'].’

    I’m starting to think this library was never tested before release?

  6. The markup got stripped in the comment above – that’s line 411, and it needs to read $this->params['customerPaymentProfileId'], not $this->params['customerProfileId']

  7. Shame on me. I fixed that on my local copy but never ported it to the copy I have here. Good catch and once again for the bug reports!

  8. Thanks John for the great tutorial and class, you’ve made my life much easier :)

  9. Just thought I’d share that I ended up removing the CVV part of the class, because storing that information in the database is against PCI rules and I had to do automated payments using Cron, so I couldn’t ask the user to enter their CVV each time they want to make a payment. CIM doesn’t require the CVV, it is an optional parameter.

    Sincerely,
    Leighton Whiting

  10. Hi John,

    Line 374-378 reads:

    if (isset($this->params['orderInvoiceNumber']))
    {
    $this->xml .= ‘
    ‘. $this->params['approvalCode'].”;
    }

    I think it should check for approval code instead of orderInvoiceNumber like:

    if (isset($this->params['approvalCode']))
    {
    $this->xml .= ‘
    ‘. $this->params['approvalCode'].”;
    }

    Also, where can I download your latest code for this class? I can still see those bugs mentioned above in the code I downloaded 10 minutes ago.

    Thanks for the class, its such a time saver!

    -Kshama

  11. Everything is working here but how can i get the credit card details from autherize.net using customer profile payment id

  12. You can’t. The point of using CIM is so you don’t have to get that information. You just use the payment profile to do all of your credit card work.

  13. Hi Johnny,
    Thank you for your reply. One doubt, during the first time the user enters the Profile information, like shipping and billing address and also the Credit Card details like Card Type: AMEX, VISA etc and then enters the Card Number along with the CSC number. The payment is done successfully for the first time.

    My requirement is that next time when the user comes to the payment page, the data in the fields is prefilled using the CIM method returned values from the Authoirze.net. Is this possible?

  14. Sort of, you can get non-credit card information back but you cannot get the actual credit card information since the purpose of CIM is to store it for you and let you charge against it without having to access it again. In fact, you shouldn’t need to retrieve anything from CIM since it stores everything for you and all you need to do is refer to the proper profile ID to use it.

  15. Thanks a lot for this class. It’s working great except for one thing — I need to redirect the user after calling it, however, my code is erroring, saying the CIM class has already sent header information. I’m unable to pinpoint exactly where its outputting any content. Could you point me in the right direction?

  16. Usually PHP will tell you what line has sent out the headers. But if I had to guess I would say there is a blank line at the end of the AuthnetCIM.class.php file. If so removing it should solve your problem.

  17. No blank lines or spaces at the beginning or end of the file, and unfortunately, its not giving me a specific line number:

    Cannot modify header information – headers already sent by (output started at /[...]/classes/AuthnetCIM.class.php:1)

  18. I know what it is but I don’t know to explain it so bear with me as I dance around this. The file is probably being saved as UTF-8 with BOM which somehow has data at the beginning of the file that causes this to happen. Try re-saving the file in notepad and try it again.

  19. (which website? mine? listed above, or the one in question? listed here:
    http://www.amorosabella.com/marketplace/wineclub/createCustomer/createCustomerInterface.php )

    First, THANKS.

    Second, these complete successfully:
    createCustomerProfileRequest
    createCustomerPaymentProfileRequest
    createCustomerShippingAddressRequest

    but createCustomerProfileTransactionRequest
    fails with: E00027 Message: The transaction was unsuccessful

    I’m not in TestMode, I’m not requiring CCV.

    Suggestions?

  20. Hi John,

    Many thanks for your tutorials!

    I have downloaded and used your AIM/ARB classes in a couple of sites and now I need to add the CIM.

    I was taking a look at the SDK provided by authnet, and noticed there is a certificate included and used to increase security with CURL. However, I haven’t seen it in your classes and I was thinking about adding those lines. Have you (or anybody) ever tried it yet? Does your class still work if I include that??

    BTW, isn’t it an important security issue??

    Thanks!

  21. Hi John, i am already using ARB for doing payments using Authorize.Net, now i need to integrate CIM for those payments. I want to know that can i stick to existing ARB api and just add the additional functionality of CIM for saving/retrieving customer payment profiles as in documentation, the functions used in both approaches are almost same. Or shall i need to change whole process from ARB to CIM. Please suggest.

  22. Hina,

    Unfortunately ARB and CIM do not work together as you cannot retrieve ARB information with it. So if you wish to use CIM you will need to change the whole process and use CIM from start to finish.

  23. I want to display information about the users current customer payment profile in CIM. But i dont see any way of displaying the information stored based on their profileid and paymentprofileid.

    Can you help me out with this.

  24. Hello,
    Why i Receive two emails with “Merchant Email Receipt” subject, one for 0.00 amount, and other is with valid amount.

    Tarsem Singh

  25. Thank you very much for this script. It works nicely. Expect more (e.g. ARB, AIM etc) from you. Its really helpful.

    Regards,
    Niladri
    Calcutta
    India

  26. Thanks for this, haven’t gotten to apply it yet but I can already tell you just saved me days of work…

  27. Is there any method to get a customer’s profile using his/her email instead of using the Auth.Net Profile ID.

  28. After painfully working with creating the XML myself, I stumbled across your CLASS and it helped me to do everything I needed (which was importing thousands of profiles) within a few minutes of coding. You saved me tons of time, thanks for the work.

  29. Thanks for sharing this with us John….saved me hours and hours.

    I had to make a modification to the class to get it to work..Maybe there is a better way to do what I’ve done, but if so I wasn’t able to figure it out.

    If i plug in your code exactly as in the example you provided (changing the fields and what not) it will error out in the case of a repeat visitor, as when running createCustomerPaymentProfile it errors out stating that there is a duplicate payment profile.

    CIM doesn’t, however, tell us what the ID of the duplicate profile is. In your code you have a wrapper function for getCustomerProfile, but in the parseResults() function it doesn’t seem to be prepared for the output of getCustomer Profile.

    I therefore added another property to the class private $paymentProfiles;

    I also added one private function to parse a nested xml element into an array:

    private function xml2array ( $xmlObject, $out = array () ){
    foreach ( (array) $xmlObject as $index => $node )
    $out[$index] = ( is_object ( $node ) ) ? $this->xml2array ( $node ) : $node;

    return $out;
    }

    then, on parseResults() i added: $this->paymentProfiles = (array) $this->xml2array($xml->profile->paymentProfiles);

    This way you can run getCustomerProfile, it will return the entire profile object and we can through payment profiles into the $cim->paymentProfiles variable. Then you can write a simple function that checks the last 4 of the CC against all existing profiles, and if there is a match it grabs that profile instead of trying to recreate it.

    I’m skipping creating Shipping profiles for simplicity’s sake.

    Is there a simpler way to go about what I just did? Otherwise my way is working…I guess I feel like I might be doing it wrong since nobody else has posted any issues in this thread.

  30. Hi John:
    I am trying to create a ‘Buy Now’ button with CIM.

    Create transaction returns E00027. No help changing validationMode to testMode.

    What am I doing wrong?

    Thank you, Mitchell

  31. Unfortunately ARB and CIM do not work together as you cannot retrieve ARB information with it. So if you wish to use CIM you will need to change the whole process and use CIM from start to finish.

    So how does that work, exactly? If I wanted to use CIM because it manages my customer profiles, how can I automate the recurring billing?

    Is there a way to tell whether or not someone’s “subscription” is due and run a script to process the payment? If so, what would said script generally look like?

  32. I just wanted to say thank you for taking the time to share this. I came across this via some google searching and it indeed made my life easier and does warrant me to take a moment to thank you.

  33. FYI, the CIM class download link points to AuthnetCIM.class.phps (note .phps) but your code example says require(‘AuthnetCIM.class.php’). Changing the d/l extension might cut down on “this doesn’t work” comments.

    I’ve used your ARB class and it worked like a charm! Thanks for your work.

  34. Hello,

    I have downloaded the standard class. I’m getting error E00003 and can’t figure out where the XML parser is hanging up. Please help

    Bruce

  35. I actually had to add a tweak for a different delimiter – since when someone includes a comma in their address it kills the default comma delimiter of the direct response.

    To save someone time, you can find the delimiter in the code around line 647 in the code. Look for this:

    $this->results = explode(',', $this->directResponse);

    Change the ‘,’ to ‘|’. Or get more creative and allow it to be set at run-time. I just hardcoded it.

  36. I need obtain the cuntomerPaymentProfile, but i dont know how i can do that. If some body here can help me please.

  37. Hi

    I want to fetch only customer profile id of users from auth.net.
    Because, in our system customer profile id created every user at auth.net but not stored to our database.
    and we are not storing users cc details also.

    So I have only users email id.
    So I want to fetch custoemr pofile id by user’s email id.

    Is it possible?
    If yes, then HOW?

    Thanks

  38. John, do you have a set of example forms we could use with this class?

    Many thanks in advance, Len

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>