Business::CyberSource (BC) has been going through API changes for a while now. If you’re using it you’ve probably noticed this and wondered why? The reason behind it was when I first made it I wanted it to be extremely simple to use, and I was realistically a Junior programmer. Over the past year I learned a lot about API design and Object Oriented Programming, as well as payment gateways and credit card transaction processing. From the first production ready release I knew that it had design problems due to a large quantity of duplicated code, but at the time I didn’t know how to get rid of it.
Domain Driven Design
My first refactor used some principles I learned from Domain-Driven Design: Tackling Complexity in the Heart of Software I changed the design of the objects to be more clear for experts in CyberSource. I also started restructuring them to more closely match the remote model. This meant making Requests and Responses to be made up of nested objects (Responses were only done as of 0.7.x). Another Change regarding this was to rename the
submit method to
run_transaction which is the name of the Remote Procedure call that is executed.
Composite Design Pattern
By making the Requests up of nested objects it allowed me to use the Composite Pattern from Design Patterns: Elements of Reusable Object-Oriented Software to serialize all of the objects into a simple nested hashref that XML::Compile::SOAP expects. Moving to these smaller objects that could
serialize allowed me to also add more offline tests.
Dependency Injection in Tests
Most of the tests for BC start out exactly the same, except for one change, the value in the amount part of the Credit Card Authorization. This is because CyberSource’s Test API uses special amounts to allow you to test getting different responses. e.g. something like (I’d have to look it up) 5000.05 is maybe a REJECT with a special processor code and cv code. Because of this I wired up my tests using Bread::Board to reduce the amount of boilerplate code in all tests that require actual remote interaction.
Ultimately changing my Remote Facade to make use of more design patterns and be designed after it’s niche domain has allowed me to have both simpler, deduplicated, more robust, more correct, and easier to modify code. Some things were not possible in earlier versions, or would have been incredibly complex to add. Now it’ll simply be an issue of adding a Moose attribute to add a feature present in the WSDL. Before certain calls could not return all of information that was in the actual SOAP response, now everything should be accessible.
One of the possible bugs of this last refactor is that I use MooseX::StrictConstructor for all of my Moose objects. It is possible now that the XML::Compile::SOAP hash is simply passed to the Response Object that if a key I didn’t account for were present that an exception would be thrown. If this is thrown on anything other than a 102 Invalid Field response, then it is a bug in BC and should be reported. I could have turned StrictConstructor off on the responses, but I believe that throwing the exceptions may ultimately make BC a better library. Also with a test suite that totals over 1400 (including generated generic ) tests, I’m fairly confident that there will be no problems in production.
In order to aid in finding bugs and diagnosing problems when they happen cybersource now has 2 debug setting levels. These can be set by having debug be 0 (off), 1 (request/response hash), 2 (full soap payloads). These should not be turned on in production and because they will print out Credit Card numbers.
Currently I’ve left BC v0.7.5 in trial, but barring any bugs being reported, or cpan testers tests failing, I’ll probably release v.0.7.6 as stable early early next week.
I do not forsee any more major API changes in the future of BC, all of my original problems have been weeded out. This means I’ll be able to focus on features and documentation with future work. It is possible that some changes to exceptions and error handling may happen, but I don’t see that being a big issue.