Judging by the popularity of my previous blog post Tutorial: Integrating the Authorize.Net AIM API with PHP as well as the articles I’ve written about Authorize.Net’s APIs on my merchant account information website I figured taking the opportunity to expand the code and tutorials I offered beyond PHP into other languages might be appreciated and helpful to others.
I’ve recently begun playing around with Python and since I am not a “Hello, world” kind of guy I decided a good first project would be to do something I know well already and Authorize.Net’s AIM API is something I know about as well as you can. Unfortunately for Python developers, although Authorize.Net offers sample code in a variety of programming languages, Python isn’t one of them. Also, based on my research (i.e. a quick Google search) it seems like there may be some room for a Python module that handles the Authorize.Net AIM API as the only one I found seems to be Zope related. The module provided and explained below is standalone and does not require Zope.
This code is written to work with Python 3. If you plan on using Python 2.* at the very least you will need to make some modifications to the print() statements and urllib calls for this code to work properly.
Getting Started
Here’s what you need to get started:
- Download the code. Place the Authnet directory somewhere in your include path.
- Download a copy of the Authorize.Net AIM integration guide.
- If you don’t already have one, apply for a developer account. This will allow you to make calls to their test server as if it were a live website and verify that your API calls are successful.
Basic Usage
To import the Authorize.Net module into your code be sure to include these line at the top of your code:
from Authnet.AIM import AIM
from Authnet.AIM import AuthnetAIMError
This will import the Authorize.Net class and well as our own custom AuthnetAIMError
exception which we will see later.
To process a transaction via Authorize.Net we need three basic pieces of information:
- A valid credit card number
- A valid expiration date
- The amount to be charged
Once we have these pieces of information we would call our code as follows:
creditcard = '4111111111111111'
expiration = '122009'
total = '1.00'
payment = AuthnetAIM('apiloginid', 'apipassword', True)
payment.setTransaction(creditcard, expiration, total)
payment.process()
First we create our Authorize.Net object and store it in a variable called payment
. We are passing it three pieces of information: API login ID, API Transaction Key, and True. True tells our class that we are operating in test mode. 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):
creditcard = '4111111111111111'
expiration = '122009'
total = '1.00'
cvv = '123'
payment = AuthnetAIM('apiloginid', 'apipassword', True)
payment.setTransaction(creditcard, expiration, total, cvv)
payment.setParameter('x_address', '1234 Main Street')
payment.setParameter('x_zip', '12345')
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.
Advanced Usage
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:
creditcard = '4111111111111111'
expiration = '122009'
total = '1.00'
cvv = '123'
tax = '0.00'
invoice = str(time())[4:10] # Use the last six digits of the current unix timestamp
payment = AuthnetAIM('apiloginid', 'apipassword', True)
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
payment.setParameter('x_duplicate_window', 180)
payment.setParameter('x_cust_id', '1324')
payment.setParameter('x_first_name', 'John')
payment.setParameter('x_last_name', 'Conde')
payment.setParameter('x_company', 'Test Company')
payment.setParameter('x_address', '1234 Main Street')
payment.setParameter('x_city', 'Townsville')
payment.setParameter('x_state', 'NJ')
payment.setParameter('x_zip', '12345')
payment.setParameter('x_country', 'US')
payment.setParameter('x_phone', '800-555-1234')
payment.setParameter('x_description', 'Test Transaction')
payment.setParameter('x_customer_ip', socket.gethostbyname(socket.gethostname()))
payment.setParameter('x_email', 'john@example.com')
payment.setParameter('x_email_customer', False)
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 a sale and is set automatically when you create the payment
object. Below is a sample AUTH_ONLY transaction:
creditcard = '4111111111111111'
expiration = '122009'
total = '1.00'
payment = AuthnetAIM('apiloginid', 'apipassword', True)
payment.setTransaction(creditcard, expiration, total)
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. In practice isError()
should rarely be used as an AuthnetAIMError is raised whenever an error occurs while processing the transaction.
Using these methods we can create a flow control structure to handle the response appropriately:
try:
# Process payment here
if payment.isApproved():
# approved!
elif payment.isDeclined():
# declined!
elif payment.isError():
# uh oh!
raise AuthnetAIMError('An uncaught error occurred')
except AuthnetAIMError as e:
print("Exception thrown: {0}".format(e))
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. We’ll throw in a few extra methods that are useful for gathering information set back to us by Authorize.Net after the transaction has been processed:
import socket
import sys
from time import time
from Authnet.AIM import AIM
from Authnet.AIM import AuthnetAIMError
creditcard = '4111111111111111'
expiration = '122009'
total = '1.00'
cvv = '123'
tax = '0.00'
invoice = str(time())[4:10]
try:
payment = AuthnetAIM('apiloginid', 'apipassword', True)
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
payment.setParameter('x_duplicate_window', 180)
payment.setParameter('x_cust_id', '1324')
payment.setParameter('x_first_name', 'John')
payment.setParameter('x_last_name', 'Conde')
payment.setParameter('x_company', 'Test Company')
payment.setParameter('x_address', '1234 Main Street')
payment.setParameter('x_city', 'Townsville')
payment.setParameter('x_state', 'NJ')
payment.setParameter('x_zip', '12345')
payment.setParameter('x_country', 'US')
payment.setParameter('x_phone', '800-555-1234')
payment.setParameter('x_description', 'Test Transaction')
payment.setParameter('x_customer_ip', socket.gethostbyname(socket.gethostname()))
payment.setParameter('x_email', 'john@example.com')
payment.setParameter('x_email_customer', False)
payment.process()
if payment.isApproved():
print('Response Code: ', payment.getResponse('ResponseCode'))
print('Response Text: ', payment.getResponse('ResponseText'))
print('Response: ', payment.getResultResponseFull())
print('Transaction ID: ', payment.getResponse('TransactionID'))
print('CVV Result: ', payment.getResponse('CVVResponse'))
print('Approval Code: ', payment.getResponse('AuthCode'))
print('AVS Result: ', payment.getResponse('AVSResponse'))
elif payment.isDeclined():
print('Your credit card was declined by your bank')
elif payment.isError():
raise AuthnetAIMError('An uncaught error occurred')
except AuthnetAIMError as e:
print("An error occured: {0}".format(e))
You can see that after a transaction is approved that a lot of information is available for you access. Here is a list of what names you can pass to the getResponse()
. There names should be self explanatory as to what they return.
ResultResponse | ResponseSubcode | ResponseCode | ResponseText |
AuthCode | AVSResponse | TransactionID | InvoiceNumber |
Description | Amount | PaymentMethod | TransactionType |
CustomerID | CHFirstName | CHLastName | Company |
BillingAddress | BillingCity | BillingState | BillingZip |
BillingCountry | Phone | Fax | |
ShippingFirstName | ShippingLastName | ShippingCompany | ShippingAddress |
ShippingCity | ShippingState | ShippingZip | ShippingCountry |
TaxAmount | DutyAmount | FreightAmount | TaxExemptFlag |
PONumber | MD5Hash | CVVResponse |
That’s it in a nutshell. This is my first Python program/module so if there are better ways of doing anything I’d be very interested in hearing about it. Feel free to leave a comment or send me an email.
EDIT March 31, 2014: The license for this code is BSD v3. Also, if you’re looking for a Python 2.4+ compatible version of this code there is a forked version available for use.
Thanks for this! I looked all over for an Authorize.net module that was standalone and didn’t have to be all hacked up to work in my project like Satchamo’s would have been.
I really enjoyed your comments. Keep them coming.
Great source. I’m all over this. However, I am also having issues with x_line_items (additional detail). How/Where in the code would you include these items to be sent and included in the “Merchant Defined” section of the email?
Dwayne, just use setParameter() to set a line item:
payment.setParameter(‘x_line_item’, ‘item1< |>golf balls< |>< |>2< |>18.95< |>Y’)
You can set as many line items as you want.
I don’t see where your code is checking the authenticity of authorize.net’s ssl cert, shouldn’t you be doing this to make sure that you are actually talking to authorize.net?
Chris, There is an updated version of this Python code in the web2py project. I’m not sure if that is included in it but it’s a good place to look.