0

I'm looking at building a set of classes to handle interactions with various similar but competing APIs. A good illustration of this would be for credit card processing. I want a class with methods like charge() or refund() that the application developer uses, independent of who the merchant processor is. Then I want to be able to build classes that handle the interaction with the specific merchant processor API.

So I might have one that interacts with the Stripe API, another for Authorize.Net, etc. Then some sort of master or wrapper class that abstracts the API specifics from the application developer.

In the past, I've done this with a wrapper class, where I created a class for each API using the same methods (with their respective API interactions), and then a wrapper class that is used in the application. A usage example might look like this:

$merchant = new Merchant( 'Stripe' );
$merchant->set_credentials( 'api_user', 'api_password' );
$merchant->set_cc( '4111-1111-1111-1111' );
$merchant->set_exp( '0121' );
$merchant->set_amount( 100.00 );
$merchant->charge();

Instantiating this class with the value "Stripe" would mean that behind the scenes this class is passing the workload off to the appropriate class to handle this interaction.

My goals are to:

  • Abstract the API from the application developer from having to know anything about the specific processor, other than the name (so they can create an instance of the class), or having to make any code changes if the processor changes.
  • As I end up needing to support more merchant processors, be able to drop in new classes to handle interactions with their API.

Is a wrapper class the way to do this, or does PHP provide other more efficient mechanisms for handling this type of setup?

2
  • Have you considered inheritance/interfaces? Commented Apr 15, 2019 at 17:53
  • You could also simply create an interface with all the required methods. Then you create classes for each implementation and let them implement the same interface. Then you only need to type hint the interface in your application and you will be able to inject which ever implementation you want. Commented Apr 15, 2019 at 17:54

1 Answer 1

1

I would create an interface for your internal API, one implementation per external processor, and a factory class to create the right instance.

Code Example (PHP 7.1):

interface MerchantInterface {
    public function set_credentials(string $username, string $password);
    public function set_cc(string $number);
    public function set_exp(string $number);
    public function set_amount(float $amount);
    public function charge();
}

class StripeMerchant implements MerchantInterface {
    public function set_credentials(string $username, string $password) {}
    public function set_cc(string $number)  {}
    public function set_exp(string $number) {}
    public function set_amount(float $amount) {}
    public function charge() {}
}

class AuthorizeNetMerchant implements MerchantInterface {
    public function set_credentials(string $username, string $password) {}
    public function set_cc(string $number)  {}
    public function set_exp(string $number) {}
    public function set_amount(float $amount) {}
    public function charge() {}
}

class MerchantFactory {
    public const MERCHANT_STRIPE = 'Stripe';
    public const MERCHANT_AUTHORIZE_NET = 'Authorize.Net';

    public static function create(string $merchant): MerchantInterface {
        switch ($merchant) {
            case self::MERCHANT_STRIPE:
                return new StripeMerchant();
            case self::MERCHANT_AUTHORIZE_NET:
                return new AuthorizeNetMerchant();
            default:
                throw new Exception('Unexpected Merchant');
        }
    }
}

$stripeMerchant = MerchantFactory::create(MerchantFactory::MERCHANT_STRIPE);
$authorizeNetMerchant = MerchantFactory::create(MerchantFactory::MERCHANT_AUTHORIZE_NET);

Depending on your requirements, you could also use the builder pattern instead of a factory to create your different instances. The builder would take care of your setters. That might be useful if you have many optional parameters (does not seem to be the case here) or you want to make your Merchants immutable.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.