Making TurboGears and Authorize.net play nice together
by Seth on Jul.18, 2009, under Python, Web Development
I recently had to wire up an Authorize.net form for a TurboGears project I’m building, and since I’m a bit new to custom form validation in TG I had quite a bit of trouble figuring out the “right” way to do it.
The goal was to get the form to go through two layers of validation before passing:
- Use the validation packages provided by TG (tw.forms and formencode)
- If the first layer of validation passes, try to run the authorize.net charge. If this returns a response code of 1 (approved) then all validation has passed. Otherwise, invalidate the form and flash the authorize.net error.
tw.forms and formencode are awesome packages, but at first glance there didn’t seem to be “one obvious way” to do things. I found the documentation and tutorials to be scant at best, and eventually went to the TurboGears mailing list for help. In spite of the fact that I’d probably give TurboGears a “C” at best for its documentation (not to mention the availability of “verbose” tutorials), the folks on the mailing list and IRC channel (when it’s actually active) seem to be quite helpful.
Eventually (and with additional help from Google’s source code search) I was able to hack together something that worked as planned, and was overjoyed to observe chained validators in action. Below is some example code provided for your hacking pleasure. To download the full example file, see the links at the bottom of the post.
# FancyValidator to process the Credit Card using the authorize package class ProcessCard(validators.FancyValidator): def _to_python(self, value, state): from authorize import aim as aim_api # Setup the aim Api object. aim = aim_api.Api(AUTHNET_LOGIN, AUTHNET_KEY, is_test=False) # Create a transaction against a credit card result_dict = aim.transaction( amount=u"16.00", card_num=unicode(value['card_number']), exp_date=unicode(value['card_expiry']), # ...and others... ) if result_dict['code'] == '1': # success return value else: # failure raise validators.Invalid(result_dict['reason_text'], value, state) class AuthnetForm(TableForm): submit_text='Process Card' validator = validators.Schema( chained_validators = [ validators.CreditCardValidator('card_type','card_number'), validators.CreditCardSecurityCode('card_type','card_cvv'), # you could also add an expiry validator, but authnet will handle this for you ProcessCard() ] ) class fields(WidgetsList): name = TextField(validator=validators.String(not_empty=True)) # ...and others like address, city, state, zip... spacer = Spacer(suppress_label=True) card_type = SingleSelectField(options=[('visa', 'Visa'), ('mastercard', 'Master Card'), ('discover', 'Discover'), ('amex', 'American Express')], validator=validators.NotEmpty) card_expiry = CalendarDatePicker(date_format="%m/%Y", validator=validators.NotEmpty) card_number = TextField(label_text='Card #', validator=validators.NotEmpty) card_cvv = TextField(label_text='CVV Code', validator=validators.NotEmpty) authnet_form = AuthnetForm('authnet_form', action=url('/authnet/process/'))
Links of interest: