Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

mdev11's avatar

Multiple payment gateways

The project includes integrating multiple payment gateways.

I created an abstract class, not an interface to be able to add properties and full functions not only skeleton functions:

abstract class PaymentAbstract{
    public $cost = 30;
    public $currency = 'USD';
    public $returnUrl;
    public $cancelUrl; 

    public function __construct()
    {
        $this->returnUrl = route('return');
        $this->cancelUrl = route('purchase');
    }

    abstract public function create_order($quantity);
    abstract public function check_order();
}

Each payment gateway requires different parameters and options so I implement the create_order and check_order functions separately.

Payment1 class:

class payment1 extends PaymentAbstract{
    protected $user;
    protected $password;
    protected $baseUrl = 'https://example1.com/api';

    public function __construct()
    {
        $this->user = env('PAYMENT1_USER');
        $this->password = env('PAYMENT1_PASSWORD');
    }
    
    public function create_order($quantity)
    {
        try{
            $data = [
                'apiOperation' => 'CREATE_CHECKOUT_SESSION',                
                'interaction' => [
                    'returnUrl' => $this->returnUrl,
                    'cancelUrl' => $this->cancelUrl
                ],
                'order' => [
                    'amount' => $this->cost * $quantity,
                    'currency' => $this->currency
                ]
            ];

            $order = Http::withBasicAuth($this->user, $this->password)
                ->post($this->baseUrl  $data);
        }catch(\Exception $e){
            return 'error';
        }

        $response = json_decode($order->body());

        return $response->session->id;
    }
}

Payment2 class:

class Payment2 implements PaymenAbstract{
    protected $token;

    public function __construct()
    {
        $this->token = = env('PAYMENT2_TOKEN');
    }

    public function create_order($quantity)
    {
        try{
            $order = Http::asJson()
                ->withToken($this->token)
                ->withHeaders([
                    'Request-Id' => 'id',
                ])
                ->post('https://example2.com/api', [
                    "intent" => "CAPTURE",
                    "purchase_units" => [
                        "amount"=> [
                            "currency_code" => $this->currency,
                            "value" => $this->cost * $quantity
                        ]
                    ],
                ]);
        }catch(\Exception $e){
            return 'error';
        }

        $response = json_encode($order->body());

        return $response->id;
    }

PaymentController:

public function index(Request $request)
{
    switch($request->method){
        case 1:
           $order = new Payment1;
            break;
        case 2:
            $order = new Payment2;
            break;
    }

    $order->create_payment($request->quantity);
}

I think some code is repeated and there are better ways or more optimized code but for example, if I create one function that sends the HTTP request for all methods it would contain multiple conditions to suit each method and it will end up with more code.

0 likes
1 reply
LaryAI's avatar
Level 58

The provided solution seems to be working fine. However, there are some minor improvements that can be made to the code.

  1. Instead of using a switch statement in the PaymentController, we can use a factory pattern to create the payment object. This will make the code more scalable and easier to maintain.
class PaymentFactory {
    public static function create($method) {
        switch($method){
            case 1:
                return new Payment1;
            case 2:
                return new Payment2;
            default:
                throw new Exception('Invalid payment method');
        }
    }
}

public function index(Request $request)
{
    $payment = PaymentFactory::create($request->method);
    $payment->create_payment($request->quantity);
}
  1. In the PaymentAbstract class, we can make the returnUrl and cancelUrl properties static since they are the same for all payment gateways.
abstract class PaymentAbstract{
    public $cost = 30;
    public $currency = 'USD';
    public static $returnUrl;
    public static $cancelUrl; 

    public function __construct()
    {
        self::$returnUrl = route('return');
        self::$cancelUrl = route('purchase');
    }

    abstract public function create_order($quantity);
    abstract public function check_order();
}
  1. In the Payment2 class, there is a typo in the constructor. The assignment operator is used twice instead of once.
public function __construct()
{
    $this->token = env('PAYMENT2_TOKEN');
}
  1. In the Payment2 class, the json_encode function is used instead of json_decode to decode the response body.
$response = json_decode($order->body());

With these improvements, the code will be more maintainable and easier to read.

Please or to participate in this conversation.