Archive
Tags
android (3)
ant (2)
beautifulsoup (1)
debian (1)
decorators (1)
django (9)
dovecot (1)
encryption (1)
fix (4)
gotcha (2)
hobo (1)
htmlparser (1)
imaplib (2)
java (1)
json (2)
kerberos (2)
linux (7)
lxml (5)
markdown (4)
mechanize (6)
multiprocessing (1)
mysql (2)
nagios (2)
new_features (3)
open_source (5)
optparse (2)
parsing (1)
perl (2)
postgres (1)
preseed (1)
pxe (4)
pyqt4 (1)
python (41)
raid (1)
rails (1)
red_hat (1)
reportlab (4)
request_tracker (2)
rt (2)
ruby (1)
scala (1)
screen_scraping (7)
shell_scripting (8)
soap (1)
solaris (3)
sql (2)
sqlalchemy (2)
tips_and_tricks (1)
twitter (2)
ubuntu (1)
vmware (2)
windows (1)
zimbra (2)

I am releasing my package for accessing Authorize.net's CIM API under the LGPL license.

It requires lxml to function.

You can download pyauthdotnet here or you can clone the repository with this command, assuming you have mercurial installed:

hg clone http://code.tylerlesmann.com/pyauthdotnet

You will install it like many other packages:

python setup.py install

This package is not well documented as of yet, though it is written to coincide with Authorize.net's documentation. Here is a quick example of its use:

  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
#!/usr/bin/env python
from pyauthdotnet import cim
from copy import deepcopy
from lxml import etree

login = 'Your_API_login'
key = 'Your_API_key'

def printxml(xml):
    print(etree.tostring(xml, pretty_print=True, encoding='UTF-8',
        xml_declaration=True))

billTo = cim.constructAddress('billTo',
    firstName='John', lastName='Doe',
    company='Superhappy testing', address='123 Fake Street', city='Aurora',
    state='CO', zip='80014', country='USA', phoneNumber='(303)555-5555',
    faxNumber='(303)666-6666')
printxml(billTo)

ba = cim.constructBankAccount('011000015', '123123123', 'John Doe',
    'checking', 'CCD', 'Some Bank')
printxml(ba)

cc = cim.constructCreditCard('4007000000027', '2010-03', '123')
printxml(cc)

pp1 = cim.constructPaymentProfiles(customerType='individual',
    billTo=deepcopy(billTo), bankAccount=ba)
printxml(pp1)
pp2 = cim.constructPaymentProfiles(customerType='business',
    billTo=deepcopy(billTo), creditCard=cc)
printxml(pp2)

stl = deepcopy(billTo)
stl.tag = 'shipToList'
stl2 = deepcopy(stl)
newprof = cim.createCustomerProfileRequest(login, key,
    merchantCustomerId='1235', description='test1235', email='not2@valid.email',
    paymentProfiles=[pp2], shipToList=[stl, stl2], validationMode='liveMode')
printxml(newprof)
custid = newprof.customerProfileId

prof = cim.getCustomerProfileRequest(login, key, custid)
printxml(prof)

ppid = prof.profile.paymentProfiles.customerPaymentProfileId.text
said = prof.profile.shipToList.customerAddressId.text

tax = cim.constructAmount('tax', amount='1.00', name='tax',
    description='mininature american flags tax')

shipping = cim.constructAmount('shipping', amount='3.00',
    name='Fedex Express Saver')

duty = cim.constructAmount('duty', amount='5.00')

li1 = cim.constructLineItem('123F12', name='widget',
    description='Used with whatsits', quantity='5', unitPrice='5.00',
    taxable='true')

li2 = cim.constructLineItem('123F13', name='whatsit',
    description='Used with widgets', quantity='5', unitPrice='5.00',
    taxable='false')

trans = cim.createCustomerProfileTransactionRequest(login, key,
    '59.00', custid, ppid, refId='123', tax=tax, shipping=shipping, duty=duty,
    lineItems=[li1, li2], customerShippingAddressId=said,
    invoiceNumber='20090211', description='widget/whatsit purchase',
    purchaseOrderNumber='20090211123', taxExempt='false',
    recurringBilling='false', cardCode='123',
    extraOptions='x_customer_ip=100.0.0.1')
printxml(trans)

cc2 = cim.constructCreditCard('XXXX0002', '2013-12', '789')
pp3 = cim.constructPaymentProfile(
    customerType='individual', billTo=deepcopy(billTo), creditCard=cc2,
    customerPaymentProfileId=ppid)
ppr = cim.updateCustomerPaymentProfileRequest(login, key, custid, pp3,
    refId='123', validationMode='liveMode')
printxml(ppr)

address = cim.constructAddress('address',
    firstName='John', lastName='Doe',
    company='Superhappy testing', address='123 Fake Street', city='Aurora',
    state='CO', zip='80014', country='USA', phoneNumber='(303)555-5555',
    faxNumber='(303)666-6666', customerAddressId=said)
csa = cim.updateCustomerShippingAddressRequest(login, key, custid,
    address, refId='6')
printxml(csa)

profids = cim.getCustomerProfileIdsRequest(login, key)
printxml(profids)

valid = cim.validateCustomerPaymentProfileRequest(login, key,
    custid, ppid, customerShippingAddressId=said, cardCode='123',
    validationMode='liveMode')
printxml(valid)

up = cim.updateCustomerProfileRequest(login, key, custid,
    merchantCustomerId='whoa', description='man', email='no@no.no')
printxml(up)
Posted by Tyler Lesmann on February 25, 2009 at 13:02 and commented on 3 times
Tagged as: open_source python

If you do not know about ZoomInfo yet, it is a business intelligence search service. With it, you can find information about companies and people working in them. The great thing about ZoomInfo is they offer an API for accessing their service. I have written up a little convenience module for this API. The only requirement is lxml.

 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
import urllib2
from lxml import objectify
from urllib import urlencode

apiurl = 'http://api.zoominfo.com/PartnerAPI/XmlOutput.aspx?'

class ZoomInfoException(Exception):
    pass

def zoom_query(qtype, key, **kwargs):
    args = {
        'query_type': qtype,
        'pc': key,
    }
    args.update(kwargs)
    resp = urllib2.urlopen(''.join([apiurl, urlencode(args)]))
    resptree = objectify.parse(resp).getroot()
    if hasattr(resptree, 'ErrorMessage'):
        raise ZoomInfoException, resptree.ErrorMessage
    return resptree

def company_competitors(key, **kwargs):
    return zoom_query('company_competitors', key, **kwargs)

def company_detail(key, **kwargs):
    return zoom_query('company_detail', key, **kwargs)

def company_search_query(key, **kwargs):
    return zoom_query('company_search_query', key, **kwargs)

def people_search_query(key, **kwargs):
    return zoom_query('people_search_query', key, **kwargs)

This module is only 32 lines, but provides full functionality to the current ZoomInfo API. It is lacking in documentation, but it follows the API documentation to the letter. Here's some example of its use.

 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
#!/usr/bin/env python
from lxml import etree
from time import sleep
import zoominfo

key = 'Your_API_key_here'

xml = zoominfo.company_search_query(key, companyName="red hat")
for rec in xml.CompanySearchResults.CompanyRecord:
    print rec.CompanyID, rec.CompanyName, rec.Website
sleep(2)

xml = zoominfo.company_detail(key, CompanyDomain='www.redhat.com')
for person in xml.KeyPerson:
    print person.JobTitle, '-', person.FirstName, person.LastName
redhatid = xml.CompanyID
sleep(2)

try:
    xml = zoominfo.company_competitors(key, CompanyID=redhatid)
except zoominfo.ZoomInfoException, e:
    print 'Caught Error from ZoomInfo:', e
    print 'You need to upgrade your ZoomInfo to use this function'
sleep(2)

xml = zoominfo.people_search_query(key, firstName='Paul', lastName='Cormier')
rec = xml.PeopleSearchResults.PersonRecord[0]
print rec.CurrentEmployment.JobTitle, '@', rec.CurrentEmployment.Company.CompanyName

If you use this module, you are still subject to ZoomInfo's use restrictions and branding requirements. This is why my example includes a few time.sleeps in between queries.

If you have any suggestions, please leave a comment.

Posted by Tyler Lesmann on February 13, 2009 at 9:05
Tagged as: lxml python

I have been using lxml to generate XML to interface with Authorize.net's CIM API and I noticed something. Element does not behave as expected, which is supposed to be as a list. The key difference is with references to the same Element.

1
2
3
4
5
6
7
8
#!/usr/bin/env python
from lxml import etree

root = etree.Element('root')
child = etree.Element('child')
root.append(child)
root.append(child)
print etree.tostring(root, pretty_print=True)

If you run this, you will get the following output:

<root>
  <child/>
</root>

One child when we are expecting two. This is a bug. The workaround is to use deepcopy.

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python
from copy import deepcopy
from lxml import etree

root = etree.Element('root')
child = etree.Element('child')
root.append(child)
root.append(deepcopy(child))
print etree.tostring(root, pretty_print=True)

This results with the expected output:

<root>
  <child/>
  <child/>
</root>
Posted by Tyler Lesmann on February 10, 2009 at 12:55 and commented on 1 time
Tagged as: gotcha lxml python