Developing an Object Oriented Credit Card Transaction Processor

This article will walk readers through the process of outlining a flexible Object Oriented design that will facilitate adding Merchant Services and Payment Methods in the future without affecting the client code.

Introduction

I was presented with the problem recently of extending an e-commerce product that my company had developed to support an arbitrary number of merchant services and transaction types with minimal changes to the client (in this case the client would be the script that is requesting a credit card transaction, not the end user of the website). The intent of this article is to walk readers through the process of outlining a flexible Object Oriented design that will facilitate the following objectives:

  1. Allow for future addition of Merchant Services without affecting the client code.

  2. Allow for future addition of Payment Methods without affecting the client code.

  3. Allow for administrators to select different Merchant Services in real time without requiring hard coding the client.

Before continuing I would like to define a few of the terms used in these three objectives. We will define “Merchant Service” as a provider of Electronic Funds Transfers (ETF) such as Verisign, Authorizenet, and LinkPoint. We will define “Payment Method” or “Transaction Method” as a type of EFT, such as Credit Card, Electronic Debit, or Virtual Check.

This article will go through the process of creating this architecture. I will avoid providing actual code as much as possible, because the implementation of this architecture can vary independently of the design, thus providing code samples to perform “Task Y” will not be the focus of this article. Rather, we will examine some of the more important points in Object Oriented design and apply them to our set of objectives. A note before we begin, the example class definitions will be given in C++, as it’s fairly neutral and has superior object constructs compared to PHP (which was the language in which this system was developed).

{mospagebreak title=Principles}

There are some principles of Object Oriented design that we must discuss before delving into this particular design. Many of you may be familiar with the notion of developing “Modular” or “Reusable” software. You may also be completely unaware of approaching a problem requiring this type of design… or even worse, you may not know how to determine when a Reusable Object Oriented design is appropriate.

I can argue the point at length and, quite effectively that, for the vast majority of web projects, an Object Oriented design is not needed. More often than not, the cost of designing a highly reusable OOP solution for a web project far outweighs the benefits. This has a lot to do with the technologies used on the web and how implementation of any given design can vary wildly between platforms and software versions, but it has even more to do with the simple fact that most dynamic websites provide nothing more than a rudimentary frontend to a database. There are, however, many situations such as the one we are examining in this article, that require an Object Oriented approach. Here are some considerations to keep in mind when you are planning a new web project with a linear, non OOP approach:

  1. How heavily can we expect this system to be modified to meet future customer demands on their website?

  2. How likely is it that future developers will be required to work on your code base?

  3. How much refactoring will be involved if the customer wants to add a new section or feature to the website?

If the answer to any of these questions is “A Lot” or “Very Likely”, then an Object Oriented Approach would in all likelihood be valuable. The hard fact of web development is that these situations do not describe most websites. Given that, let’s weigh our objective against these three considerations.

How heavily can we expect this system to be modified to meet future customer demands on their website?

One of the principle requirements of this system is that it allow the addition of an arbitrary number of Merchant Providers and Transaction Methods. We can expect a high volume of changes to be made to the product in time to accommodate new customers who prefer Merchant Providers that may be unsupported in our product.

How likely is it that future developers will be required to work on your code base?

Again, looking at our first two objectives, we are required to consider that other developers will be working to provide Merchant Providers and Transaction Methods to clients. It is not unlikely that a customer will have an on-staff developer and prefer to have them work on the clock for an unsupported feature rather than paying your company to develop a new module for transaction processing.

How much refactoring will be involved if the customer wants to add a new section or feature to the website?

We want to avoid refactoring as much as possible because we do not want the client to notice changes to our system architecture when it is presented with the need to support a new Merchant Provider. This means that using a non-OOP approach would force us to refactor each time a customer needed a module for an unsupported Merchant or Transaction Method.

{mospagebreak title=Approaching the Problem}

A key concept that you will see me use again and again is that the client should not know about implementation. What does that mean? For example, let’s define the following class:

public class EFTProcessor {
 CCPaymentService();
 
public:
 bool Execute();

private:
 EFT* SendTransaction(EFT*);
 string _paymentMethod;
 string _merchantService;
}

What we have defined is a class with one public method: Execute(). It has a private method called SendTransaction that Execute will call. It requires some values, _paymentMethod and _merchantService, that could be passed into the constructor or perhaps they were a component of our EFT datatype (which we have not yet defined). The point to this example is this: no matter what we do with SendTransaction and the EFT data type, the client can always call EFTProcessor::Execute and always get a boolean response indicating success or failure. Do not get too attached to this class, as it is only an example and will not be seen again.

The concept illustrated in the class example that we just looked at leads us to the next big concept that you will see me use: Design to an Interface, not an Implementation. This is a big one. It says that we should approach our design from the standpoint of how our objects interact, and more importantly, how our client will use them, rather than from the standpoint of how to write a specific routine. Approaching design in this fashion should lead to Loose Coupling of our design and implementation, meaning that the way in which our client interacts with our objects should be able to vary independently of how our objects complete the tasks that the client requests. Those of you who are familiar with Object Oriented Programming techniques understand that one of the purposes of this type of design, from a coding standpoint, is to facilitate polymorphism, meaning it facilitates replacing objects of one type with objects of another type transparently to the other objects that are interacting with it.

{mospagebreak title=Digging In}

Now that we have completed our introductory analysis, it’s time to get to the meat and potatoes of this discussion. We need to solve for our objectives, so the first thing that should be established is a rough idea of how the client should interact with the object that will complete an ETF. Keep in mind that we want to hide implementation from the client entirely; the client should not have to perform a series of operations to have our object process an ETF on its behalf. Let’s look at a rough class definition:

public class ETFTransaction {
public:
 ETFTransaction();
 bool Execute();
}

Simple enough, right? Obviously this object will need some data to work with, so our next problem is to determine a flexible way to give that data to the ETFTransaction object. The data that the object would need could be summarized as: Customer Billing Information, Customer Shipping Information, and Information that is specific to our Payment Method. We want to be able to add Payment Methods as our system expands, so we should avoid looking at specific class implementations for any one Payment Method. Let’s define the following class:

public class ETFPaymentMethod {
public:
 ETFPaymentMethod(string*);
private:
 string* _servicename;
 Address* ShipToAddress;
 Address* BillToAddress;
}

This class will act as a token of sorts. It will be created by the client and passed to the ETFTransaction when it requires an ETF. The Address class that you see in the definition is a simple object that behaves as a data container for address information. It will also contain the customer name. This class defines our interface for the ETFPaymentMethod object. We would inherit from this class to build specific Payment Methods, such as CreditCard:ETFPaymentMethod, VirtualCheck:ETFPaymentMethod, and so on. Each one would have additional data stored in the object. This data would be collected from a form in the checkout process or registration process on the client website and passed to the ETFPaymentObject.

How does the client know what Payment Methods it supports and which one to use? I do not wish to get too far into the clientside implementation of this design, but to answer that question, we could create a registry of values either in a database or a flat file that is accessed by our client script and used to populate some data structure representing the available Payment Methods and what information needs to be collected in a form. For the case in point, my choice was to create an object that built a composite data structure representing the Payment Methods that were stored in the database. This structure also represented the data that was specific to the payment method (for a credit card, for example, the card number, expiration data, and cvv2 code).

Since we have defined an object to represent our Payment Method, let’s expand our definition of the ETFTransaction class:

public class ETFTransaction {
public:
 ETFTransaction(ETFPaymentMethod*);
 bool Execute();
private:
 ETFPaymentMethod* _pm;
}

We have now defined how the client will initiate and execute an ETF using the ETFTransaction object… but how will the ETFTransaction object process these miscellaneous Payment Methods for an arbitrary number of Merchants? And how will we let the object discover what Payment Methods are supported by which Merchants without hard coding? Read on.

{mospagebreak title=Factory Work}

You are probably already wondering why this page is named Factory Work. To satisfy that curiosity, consider what a factory does. Put simply, a factory works under given parameters to produce a desired product. How is this applicable to the problem at hand? Well, we want our ETFTransaction object to be capable of utilizing any Merchant that we provide a class for. To allow this without hard coding the ETFTransaction object, we have to provide an intermediary class, a Factory class, to provide the proper Merchant object to the ETFTransaction object. The purpose of our factory is this: take a parameter object and return the appropriate Merchant object to the ETFTransaction object. Let’s look at a definition:

public class ETFPaymentServiceFactory {
protected:
 ETFPaymentServiceFactory();
private:
 ETFPaymentServiceFactory* _instance;
public:
 ETFPaymentServiceFactory* Instance();
 ETFPaymentService* Create(ETFPaymentMethod*);
}

We would extend our ETFTransaction object again:

public class ETFTransaction {
public:
 ETFTransaction(ETFPaymentMethod*);
 bool Execute();
private:
 ETFPaymentMethod* _pm;
 ETFPaymentService* _ps;
}

You will note that the constructor is protected; this is because we do not wish to allow this object to be instantiated more than once. _instance is a reference to the sole instance of this object. Obviously you cannot use this method in PHP, but there are ways. The Instance() method would look something like this:

ETFPaymentServiceFactory* Instance() {
 if (_instance = 0) {
  _instance = new ETFPaymentServiceFactory;
 }
 return _instance;
}

To use the factory, we would have code such as this:

ETFPaymentMethod* pm = new ETFPaymentMethod;
ETFTransaction* trans = new ETFTransaction(pm);

bool Execute:ETFTransaction() {
 ETFPaymentServiceFactory* factory = ETFPaymentServiceFactory::Instance();
 _ps = factory->Create(pm);
}

So now we’ve outlined how our Transaction object will go about getting a Payment Service object, but we still have not defined an interface for the ETFPaymentService class. Let’s do so now:

public class ETFPaymentService {
public:
 ETFPaymentService();
 virtual bool SendRequest();
private:
 ETFPaymentMethod* _pm;
 virtual bool SupportsPaymentMethod();
}

As you can see, this is the abstract class for all Payment Services to inherit from; you would see objects like AuthorizeNet:ETFPaymentService and LinkPoint:ETFPaymentService to handle specific requests. We could simply add a call to _ps->SendRequest() in our Execute:ETFTransaction function to process the ETF. The private method of ETFPaymentService SupportsPaymentMethod() is used internally to check against whatever data registry we have provided to ensure that the PaymentMethod object represents and Payment Method that our Payment Service is capable of handling.

Lastly, we would add a method such as the following to allow retrieval of status information from our ETFTransaction object. We would also want a method to store messages from our Payment Service. The way these messages are represented in XML from the Payment Service are not important to this discussion, because they will be adapted to fit the interface of a generic ETFMessage object.

bool isApproved:ETFTransaction();

public class ETFMessage {
public:
 ETFMessage(int code);
 int GetMessage();
private
 int _code;
 string* _message;
}

And add this to our ETFTransaction class:

public:
 ETFMessage* CurrentMessage();
 bool NextMessage();
private:
 ETFMessage* messages[];

You see a basic Iterator pattern here, which could be expanded if you want. We simply want to provide a standard method of message retrieval to our client.

Now that it is established how our objects will interact, and we have an idea how the objects will receive their parameters, we can briefly discuss client implementation. To review the process involved in using these classes, we have the following steps:

  1. Create a structure from our registry representing available Payment Methods and Payment Services.

  2. Once the Method has been selected by the customer using the website, relative to the Service selected by the site administrator, create our Address and Payment Method objects.

  3. Create our Transaction Object.

  4. Call the Execute() method of our Transaction object.

  5. Check the status of our transaction and review any messages.

And… viola! That’s it. Now that we have a design, the rest is gravy. Remember that routines and functions will do whatever you want them to do – it is the design that truly makes a system work.

{mospagebreak title=Conclusion}

We have covered a lot of ground in this article. We have discussed and demonstrated the principles of loose coupling and designing for interfaces rather than implementations. We have introduced the concept of Factory methods as a way to defer instantiation of an object to an intermediary (the Factory) to allow the client to remain independent of the object creation process, which allows the client to simply request an object and get it back, without knowing what is required to create that object, or even the name of the class the object belongs to.

This could be extended into a substantial discussion about general practices of Object Oriented design, but that isn’t necessary. If you found this article helpful, feel free to contact me and tell me what you liked and what you did not like. If you would like to read more about some of the design concepts we discussed, check out “Design Patterns” (Addison Wesley 1994).

David Fells is the IT Director for Turning Minds Media (www.turningminds.com). He is 23 years old and has been in the IT industry for five years. He is MCDBA and MCSE certified. He is proficient in Transact SQL, MySQL, PHP, C#, Perl, ASP, CSS, Javascript and Flash.

Google+ Comments

Google+ Comments