Here is the prelude to the question I am asking: I set about creating an API for agnostic gateways for Python for my company. At the moment, I just wrote the code to support Authorize.net and wanted to receive feedback on the clarity of my API design from Python programmers with a little more experience than me.
I chose my own video because other existing packages are more like thoughts about them or specific to Authorize.net (I want to write a more general package with a cleaner interface). I got some inspiration from the package in particular ( pythorize ), but didn't like their API.
Before I begin to describe what I am doing, here is a link to the public repository on the package bitpack: paypy (note for those who can use it: the code is stable, but the documents are very missing).
My current strategy uses a nested dictionary and passes it to the constructor of the payment method class. Example of creating a new user profile in the Authorize.net CIM API:
>>> options = {'tran_key' : 'test_tran_key', ... 'login' : 'developer_login', ... 'testing' : True, ... 'validation': 'testMode', ... 'customer': {'description': 'Some description of the customer profile', ... 'id' : 22, ... 'email' : ' johnny_doe@gmail.com '}, ... 'billing': [{'type': 'individual', ... 'profile': {'city' : 'Carlsbad', ... 'state' : 'California', ... 'zip' : '92009', ... 'firstname' : 'John', ... 'address' : '12 Alicante Rd. Suite 9', ... 'lastname' : 'Doe', ... 'country' : 'USA', ... 'phone' : '(858) 557-2674'}, ... 'payment': {'card': {'ccv' : '524', ... 'number' : '4111111111111111', ... 'expiration' : '2014-04'}}}, ... {'type' : 'individual', ... 'profile' : {'city' : 'Las Vegas', ... 'state' : 'Nevada', ... 'zip' : '79112', ... 'firstname' : 'John', ... 'address' : '78 Cloud Front', ... 'lastname' : 'Doe', ... 'country' : 'USA', ... 'phone' : '(858) 557-2674'}, ... 'payment': {'card': {'ccv' : '499', ... 'number' : '4111111111111111', ... 'expiration' : '2012-11'}}}, ... {'profile': {'city' : 'Carlsbad', ... 'state' : 'California', ... 'zip' : '92009', ... 'firstname' : 'John', ... 'address' : '12 Alicante Rd. Suite 9', ... 'lastname' : 'Doe', ... 'company' : 'Xmarks', ... 'country' : 'USA', ... 'phone' : '(858) 557-2674'}, ... 'payment': {'bank': {'name_on_account' : 'John Doe', ... 'account' : '829330184383', ... 'type' : 'checking', ... 'name' : 'Bank of America', ... 'routing' : '122400724'}}}], ... 'shipping': [{'city' : 'Carlsbad', ... 'state' : 'California', ... 'zip' : '92009', ... 'firstname' : 'John', ... 'address' : '12 Alicante Rd. Suite 9', ... 'lastname' : 'Doe', ... 'country' : 'USA', ... 'phone' : '(858) 557-2674'}]} >>> profile = Profile(options) >>> result = profile.create() >>> result.code 'I00001' >>> print 'Customer Profile ID:' + str(result) Customer Profile ID: 2758851 >>> print 'Customer Payment Profile IDs:' + repr(result.payment_ids) Customer Payment Profile IDs: ['2380878', '2380879', '2380880'] >>> print 'Customer Shipping Profile IDs:' + repr(result.shipping_ids) Customer Shipping Profile IDs: ['2427568'] >>> >>> >>> options = {'id' : str(result), ... 'tran_key' : '86U5pvA9TcxZ5b8D', ... 'testing' : True, ... 'login' : '5b3PhGX68'} >>> profile = Profile(options) >>> result = profile.remove() >>> result.code 'I00001' >>> ^D
You will notice that I am using a couple of magic methods (e.g. str , etc.) for result objects. I use this dictionary strategy for AIM and ARB methods, and I find out that this is the easiest way to transfer โoptionsโ to the payment API, because at some point as an adapter for GoogleCheckout, Paypal, etc. Adapters will be used ...
Another thought I had was to use descriptors and objects instead of dictionaries to pass parameter data to adapters.
As with all payment gateway APIs (in particular, PayPal and Authorize.net), the interfaces are usually a bit messy and in no way standardized, so itโs hard to avoid some gateway-dependent parameters.