HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Class library for processing credit cards

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
creditcardsforlibraryprocessingclass

Problem

I am developing a class library for processing credit cards. There is a thought in management in the short-term future to look for a new credit card processor. So I want to write once and use everywhere (I should be anyway, right?).

We are currently using First Data's Global Gateway Web Service API. I need a separate web reference setup for live and test.

I am trying to do the following:

```
//using ProcessCreditCard.WebReference;
using ProcessCreditCard.WebReferenceTest;

public class ProcessCreditCard
{
private FDGGWSApiOrderService oFDGGWSApiOrderService = null;

#region Intialize the object
///
/// Initializes a new instance of the class.
///
public ProcessCreditCard()
{
ServicePointManager.Expect100Continue = false;
// Initialize Service Object
oFDGGWSApiOrderService = new FDGGWSApiOrderService();
// Set the WSDL URL
oFDGGWSApiOrderService.Url = @Properties.Settings.Default.CcApiUrl;
// Configure Client Certificate
oFDGGWSApiOrderService.ClientCertificates.Add(X509Certificate.CreateFromCertFile(Properties.Settings.Default.CertFile));
// Set the Authentication Credentials
NetworkCredential nc = new NetworkCredential(Properties.Settings.Default.CertUser, Properties.Settings.Default.CertPass);
oFDGGWSApiOrderService.Credentials = nc;
}

///
/// Initializes a new instance of the test version of the class.
///
/// if set to true [test].
public ProcessCreditCard(bool test)
{
ServicePointManager.Expect100Continue = false;
// Initialize Service Object
oFDGGWSApiOrderService = new FDGGWSApiOrderService();
// Set the WSDL URL
oFDGGWSApiOrderService.Url = @Properties.Settings.Default.CcApiUrl_Test;
// Configure Client Certificate
oFDGGWSApiOrderService.ClientCertificates.Add(X509Certificate.CreateFromCertFile(Properties.Settings.Default.CertFile_Test));
// Set

Solution

First, I hope this question is still relevant in your company. One point that is of great interest to me in the question, is the management's vision to use other 3rd party payment processing providers. This directly implies that the code should accomodate this by abstracting the operation of processing orders and resolving dependecies on external systems.

I will give some simple suggestions intended to decouple the code from a specific implementation (therefore enhancing code re-use and testability)

Step I, Resolve dependency on 3rd party credit services

This is acheived by abstracting the functionalities exposed by the service (of course every service will have a different API, but the processing logic will follow more or less the same workflow):

public interface IPaymentGateWayService
{
    List ClientCertificates { get; }
    string Url { set; }
    NetworkCredential Credential { set; }
}


So now your Payment processor is agnostic to which service it is using as such:

class PaymentProessor
{
    private readonly IPaymentGateWayService _paymentGateWayService;

    public PaymentProessor(IPaymentGateWayService paymentGateWayService)
    {
        _paymentGateWayService = paymentGateWayService;
        _propertyProvider = propertyProvider;
    }

    public void ProcessCreditCardPayment()
    {
        _paymentGateWayService.Url = /*service url*/;
        _paymentGateWayService.ClientCertificates.Add(/*X509 certificate*/)
        _paymentGateWayService.Credential = new NetworkCredential(/*username and password*/);

    }
}


You then create a wrapper for your FDGGWApiOrderService that implements the IPaymentGateEayService interface:

class FDGGWS : IPaymentGateWayService
{
    private FDGGWSApiOrderService _fdggwsApiOrderService;

    public FDGGWS()
    {
        _fdggwsApiOrderService = new FDGGWSApiOrderService();
    }

    public List ClientCertificates
    {
        /*delegate to _fdggwsApiOrderService*/
    }

    public string Url
    {
        /*delegate to _fdggwsApiOrderService*/
    }

    public NetworkCredential Credential
    {
        /*delegate to _fdggwsApiOrderService*/
    }
}


So now when you create your payment processor, you pass the instance of the service you want to use.

new PaymentProessor(new FDGGWS());


This simply allows you to reuse your code to accomodate different service providers, also, you can support multiple providers at the same time by creating a service factory that returns the desired concrete instance of teh service depending on a configuration setting.

Step II, Handle environment settings

There are tons of techniques that allow you to read values from configuration stores, like App.config or a web.config. But for the purpose of my answer I will leverage the dependency injection technique and resolve the dependency on environment properties using constructor injection, similar to what is used to abstract the service interface.

So we create an interface for the configuration settings:

internal interface IPropertyProvider
{
    string Cert_File { get; }
    string Cer_User { get; }
    string Cer_Password { get; }
    string ServiceUrl { get; }
}


And you can create as many flavours of the interface as there are environments, in your case of for test and one for production, here is an abridged example of either:

class ProductionPropertyProvider : IPropertyProvider
{
    public string Cert_File
    {
        get { return "Path to Production file"; }
    }

    /* other properties */
}

class TestPropertyProvider : IPropertyProvider
{
    public string Cert_File
    {
        get { return "Path to test file"; }
    }

    /* other properties */
}


And here is how you perform the dependency injection:

class PaymentProessor
{
    private readonly IPaymentGateWayService _paymentGateWayService;
    private readonly IPropertyProvider _propertyProvider;

    public PaymentProessor()
        :this(new FDGGWS(), new ProductionPropertyProvider())
    {}
    public PaymentProessor(IPaymentGateWayService paymentGateWayService, IPropertyProvider propertyProvider)
    {
        _paymentGateWayService = paymentGateWayService;
        _propertyProvider = propertyProvider;
    }

    public void ProcessCreditCardPayment()
    {
        _paymentGateWayService.Url = _propertyProvider.ServiceUrl;
        _paymentGateWayService.ClientCertificates.Add(X509Certificate.CreateFromCertFile(_propertyProvider.Cert_File));
        _paymentGateWayService.Credential = new NetworkCredential(_propertyProvider.Cer_User, _propertyProvider.Cer_Password);

    }
}


Notice the default constructor that creates default service and property provider instances. This is just to demonstrate that you can specify the default dependencies in the default constructor

Lastly, all you need is one configuration key in your app.config that specifies what environment is it.. we use that key to determine the type of the IPropertyProvider.

Exam

Code Snippets

public interface IPaymentGateWayService
{
    List<X509Certificate> ClientCertificates { get; }
    string Url { set; }
    NetworkCredential Credential { set; }
}
class PaymentProessor
{
    private readonly IPaymentGateWayService _paymentGateWayService;

    public PaymentProessor(IPaymentGateWayService paymentGateWayService)
    {
        _paymentGateWayService = paymentGateWayService;
        _propertyProvider = propertyProvider;
    }

    public void ProcessCreditCardPayment()
    {
        _paymentGateWayService.Url = /*service url*/;
        _paymentGateWayService.ClientCertificates.Add(/*X509 certificate*/)
        _paymentGateWayService.Credential = new NetworkCredential(/*username and password*/);

    }
}
class FDGGWS : IPaymentGateWayService
{
    private FDGGWSApiOrderService _fdggwsApiOrderService;

    public FDGGWS()
    {
        _fdggwsApiOrderService = new FDGGWSApiOrderService();
    }

    public List<X509Certificate> ClientCertificates
    {
        /*delegate to _fdggwsApiOrderService*/
    }

    public string Url
    {
        /*delegate to _fdggwsApiOrderService*/
    }

    public NetworkCredential Credential
    {
        /*delegate to _fdggwsApiOrderService*/
    }
}
new PaymentProessor(new FDGGWS());
internal interface IPropertyProvider
{
    string Cert_File { get; }
    string Cer_User { get; }
    string Cer_Password { get; }
    string ServiceUrl { get; }
}

Context

StackExchange Code Review Q#4621, answer score: 4

Revisions (0)

No revisions yet.