Skip to content

A Laravel Package that makes implementation of multiple payment Gateways such as Paystack, Flutterwave and Stripe endpoints and webhooks seamless.

License

Notifications You must be signed in to change notification settings

andyz1974/laravel-multipayment-gateways

 
 

Repository files navigation

A Laravel Package that makes implementation of multiple payment Gateways endpoints and webhooks seamless

Latest Version on Packagist PHP from Packagist Laravel Version Test Status Code Style Status Total Downloads

The laravel-multipayment-gateways package provides a convenient way to handle payments through multiple payment gateways in a Laravel 8, 9 and 10 application. The package currently supports multiple gateways such as Paystack, Flutterwave and Stripe. The package offers an easy to use interface that abstracts the complexities of integrating with these payment gateways. It also provides a way to handle webhooks from the payment gateways.

Installation

You can install the package via composer:

composer require musahmusah/laravel-multipayment-gateways

You can publish and run the migrations with:

php artisan vendor:publish --tag="multipayment-gateways-migrations"
php artisan migrate

You can publish the config file with:

php artisan vendor:publish --tag="multipayment-gateways-config"

This is the contents of the published config file:

return [
    'paystack' => [
        'base_uri' => env('PAYSTACK_BASE_URI'),
        'secret' => env('PAYSTACK_SECRET'),
        'currency' => env('PAYSTACK_CURRENCY'),
    ],
    
    'flutterwave' => [
        'base_uri' => env('FLUTTERWAVE_BASE_URI'),
        'secret' => env('FLUTTERWAVE_SECRET'),
        'currency' => env('FLUTTERWAVE_CURRENCY'),
        'encryption_key' => env('FLUTTERWAVE_ENCRYPTION_KEY'),
    ],

    'stripe' => [
        'base_uri' => env('STRIPE_BASE_URI'),
        'secret' => env('STRIPE_SECRET'),
        'webhook_secret' => env('STRIPE_WEBHOOK_SECRET'),
        'currency' => env('STRIPE_CURRENCY'),
        'plans' => [
            'monthly' => env('STRIPE_MONTHLY_PLAN'),
            'yearly' => env('STRIPE_YEARLY_PLAN'),
        ],
    ],

    'webhooks' => [
        [
            /*
             * This refers to the name of the payment gateway being used.
             */
            'name' => 'stripe',

            /*
             * This secret key is used to validate the signature of the webhook call.
             */
            'signing_secret' => '',

            /*
             * This refers to the header that holds the signature.
             */
            'signature_header_name' => 'Stripe-Signature',

            /*
             *  This class is responsible for verifying the validity of the signature header.
             *
             * It should implement the interface \MusahMusah\LaravelMultipaymentGateways\SignatureValidator\PaymentWebhookSignatureValidator.
             */
            'signature_validator' => \MusahMusah\LaravelMultipaymentGateways\SignatureValidator\DefaultSignatureValidator::class,

            /**
             * The webhook handler option allows you to choose how webhook requests are handled in your application.
             *
             * Available options:
             * - 'job': Webhook requests will be handled by a job.
             * - 'event': Webhook requests will be handled by an event.
             *
             * Default: 'job'
             */
            'payment_webhook_handler' => 'job',

            /**
             * The payment_webhook_job option allows you to specify the job class that will be used to process webhook requests for payment methods.
             *
             * This should be set to a class that extends \MusahMusah\LaravelMultipaymentGateways\Jobs\ProcessPaymentWebhookJob.
             */
            'payment_webhook_job' => '',

            /**
             * The payment_webhook_event option allows you to specify the event class that will be used to process webhook requests for payment methods.
             *
             * This should be set to a class that extends \MusahMusah\LaravelMultipaymentGateways\Events\PaymentWebhookReceivedEvent.
             */
            'payment_webhook_event' => '',
        ],

        [
            /*
             * This refers to the name of the payment gateway being used.
             */
            'name' => 'paystack',

            /*
             * This secret key is used to validate the signature of the webhook call.
             */
            'signing_secret' => '',

            /*
             * This refers to the header that holds the signature.
             */
            'signature_header_name' => 'Paystack-Signature',

            /*
             *  This class is responsible for verifying the validity of the signature header.
             *
            * It should implement the interface \MusahMusah\LaravelMultipaymentGateways\SignatureValidator\PaymentWebhookSignatureValidator.
             */
            'signature_validator' => \MusahMusah\LaravelMultipaymentGateways\SignatureValidator\DefaultSignatureValidator::class,

            /**
             * The webhook handler option allows you to choose how webhook requests are handled in your application.
             *
             * Available options:
             * - 'job': Webhook requests will be handled by a job.
             * - 'event': Webhook requests will be handled by an event.
             *
             * Default: 'job'
             */
            'payment_webhook_handler' => 'event',

            /**
             * The payment_webhook_job option allows you to specify the job class that will be used to process webhook requests for payment methods.
             *
             * This should be set to a class that extends \MusahMusah\LaravelMultipaymentGateways\Jobs\ProcessPaymentWebhookJob.
             */
            'payment_webhook_job' => '',

            /**
             * The payment_webhook_event option allows you to specify the event class that will be used to process webhook requests for payment methods.
             *
             * This should be set to a class that extends \MusahMusah\LaravelMultipaymentGateways\Events\PaymentWebhookReceivedEvent.
             */
            'payment_webhook_event' => '',
        ],
    ],
];

To prepare your application to handle payments using any of the payment gateways, you need to add the values to your .env file. Each payment gateway has its own set of values that you need to add to your .env file as documented in the config file above. For example, to use the paystack payment gateway, you need to add the following values to your .env file:

PAYSTACK_BASE_URI=https://api.paystack.co
PAYSTACK_SECRET=xxxxxxxxxxxxx
PAYSTACK_CURRENCY=NGN

To prepare your application to handle webhooks for any of the payment gateways, you need to update the configs array in the config/multipayment_gateways.php file generated by the package. Each payment gateway has to be configured with the following values:

  • name: This refers to the name of the payment gateway being used.
  • signing_secret: This secret key is used to validate the signature of the webhook call.
  • signature_header_name: This refers to the header that holds the signature.
  • signature_validator: This class is responsible for verifying the validity of the signature header. It should implement the interface \MusahMusah\LaravelMultipaymentGateways\SignatureValidator\PaymentWebhookSignatureValidator.
  • payment_webhook_handler: The webhook handler option allows you to choose how webhook requests are handled in your application. Available options are:
    • job: Webhook requests will be handled by a job.
    • event: Webhook requests will be handled by an event.
    • Default: job
  • payment_webhook_job: The payment_webhook_job option allows you to specify the job class that will be used to process webhook requests for payment methods. This should be set to a class that extends \MusahMusah\LaravelMultipaymentGateways\Jobs\ProcessPaymentWebhookJob.
  • payment_webhook_event: The payment_webhook_event option allows you to specify the event class that will be used to process webhook requests for payment methods. This should be set to a class that extends \MusahMusah\LaravelMultipaymentGateways\Events\PaymentWebhookReceivedEvent by default the package dispatches the PaymentWebhookReceivedEvent event that you can create a listener for. However, you can create your own event class and specify it in the payment_webhook_event option.

Usage

All payment gateways methods and properties can be accessed using their respective facade, helper or dependency injection. This ensures consistency in the way you access the payment gateways.
The idea is to provide a way to handle payments and webhooks in laravel web and api based applications.

Handling Payments with Paystack

Web Payment can be handled in the following ways:

  1. Prepare your route to handle the payment request.

    use Illuminate\Support\Facades\Route;
    use App\Http\Controllers\PaymentController;
    
    Route::post('/payment', [PaystackPaymentController::class, 'initiatePayment'])->name('payment.initiate');
  2. Create a controller to handle the payment request. In the controller, you can use your desired Payment Gateway to handle the payment request using the facade, helper or dependency injection.

    • Create a controller to handle the payment request using Facade.

      use Illuminate\Http\Request;
      use MusahMusah\LaravelMultipaymentGateways\Facades\Paystack;
      
      class PaystackPaymentController extends Controller
      {
          public function initiatePayment(Request $request)
          {
              $payment = Paystack::redirectToCheckout([
                  'amount' => 1000,
                  'email' => '[email protected]',
                  'reference' => '123456789',
                  'callback_url' => 'https://example.com',
              ]);
             
              return $payment;
          }
      }
    • Create a controller to handle the payment request using Helper.

      use Illuminate\Http\Request;
         
      class PaystackPaymentController extends Controller
      {
          public function initiatePayment(Request $request)
          {
              $payment = paystack()->redirectToCheckout([
                  'amount' => 1000,
                  'email' => '[email protected]',
                  'reference' => '123456789',
              ]);
             
              return $payment;
          }
      }

      In the above example, the redirectToCheckout() method was called without the parameters callback_url in the array. This means you have to add callback url in your paystack dashboard here to handle redirect after payment.

    • Create a controller to handle the payment request using Dependency Injection through the PaystackContract interface.

      use Illuminate\Http\Request;
      use MusahMusah\LaravelMultipaymentGateways\Contracts\PaystackContract;
      
      class PaystackPaymentController extends Controller
      {
          public function initiatePayment(Request $request, PaystackContract $paystack)
          {
              $payment = $paystack->redirectToCheckout();
             
              return $payment;
          }
      }

      In the above example, the PaystackContract interface was injected into the controller through dependency injection. The redirectToCheckout method was called without passing any parameters, this is because the package has been configured to use the values from the request() object to make the payment request if no parameters are passed to the method. This allows you to make payment requests without having to pass any parameters to the method instead you can send the data using hidden inputs in your form or as a json object in your request body.
      Example of a blade form that can be used to make such a request:

       <form action="{{ route('payment.initiate') }}" method="POST">
          @csrf
          <input type="hidden" name="amount" value="1000">
          <input type="hidden" name="email" value="[email protected]">
          <input type="hidden" name="reference" value="123456789">
          <input type="hidden" name="metadata" value="{{ json_encode(['custom_fields' => ['name' => 'Musah Musah']]) }}"
          <input type="hidden" name="callback_url" value="https://example.com">
          <button type="submit">Pay</button>
       </form>

      This way when the form is submitted, the request() object will be used to extract the data in the hidden inputs inside the redirectToCheckout method and make the payment request, allowing you to call the redirectToCheckout method without passing any parameters.
      The metadata field is optional, you can add any custom fields you want to the metadata field.
      Additionally, you need to generate a unique reference for each payment request. You can readmore about paystack payment requests here

  3. Handle the payment response:
    Upon successful payment, you will be redirected to the callback_url that you set in your paystack dashboard or the callback_url you passed in the payment request.

    • Add a route to handle the payment response.
      use Illuminate\Support\Facades\Route;
      use App\Http\Controllers\PaymentController;
      
      Route::get('/payment/callback', [PaystackPaymentController::class, 'handlePaymentResponse'])->name('payment.callback');
    • Create a controller to handle the payment response.
      use Illuminate\Http\Request;
      use MusahMusah\LaravelMultipaymentGateways\Contracts\PaystackContract;
      
      class PaystackPaymentController extends Controller
      {
          public function handlePaymentResponse(Request $request, PaystackContract $paystack)
          {
              $paymentResponse = $paystack->getPaymentData();
              
              // Handle payment response here
          }
      }

For an api based application where the client is served by a mobile app or on a separate domain, you can use this approach instead:

  1. Initialize the payment with the client (mobile app or web app built with react, vue etc) served on a separate domain or port. This can be done using the Paystack Inline Popup here.
  2. Verify the payment using the verifyPayment method provided by the package. This method will verify the payment using the reference that has to be passed to the request body.
    Your route should look like this:
    use Illuminate\Support\Facades\Route;
    use App\Http\Controllers\PaymentController;
    
    Route::post('/payment/verify', [PaystackPaymentController::class, 'verifyPayment'])->name('payment.verify');
    Your Controller should look like this:
    use Illuminate\Http\Request;
    use MusahMusah\LaravelMultipaymentGateways\Contracts\PaystackContract;
    
    class PaystackPaymentController extends Controller
    {
        public function verifyPayment(Request $request, PaystackContract $paystack)
        {
            $paymentResponse = $paystack->verifyTransaction($request->reference);
            
            if ($paymentResponse->status === 'success') {
                // Handle payment response here
            }
        }
    }

Handling Payments with Stripe

Stripe Payment can be handled in similar ways as Paystack Payment. The package allow you to make payment requests using the facade, helper or dependency injection.

  1. Prepare your route to handle the payment request.

    use Illuminate\Support\Facades\Route;
    use App\Http\Controllers\StripePaymentController;
    
    Route::post('/payment/stripe', [StripePaymentController::class, 'initiatePayment'])->name('payment.stripe.initiate');
  2. Create a controller to handle the payment request. In the controller, you can use your desired Payment Gateway to handle the payment request using the facade, helper or dependency injection

    • Create a controller to handle the payment request using Facade.

      use Illuminate\Http\Request;
      use MusahMusah\LaravelMultipaymentGateways\Facades\Stripe;
      
      class StripePaymentController extends Controller
      {
          public function initiatePayment(Request $request)
          {
              $payment = Stripe::createIntent([
                  'amount' => 1000,
                  'currency' => 'usd',
                  'payment_method_types' => ['card'],
                  'payment_method' => 'xxxxxxx',
                  'metadata' => ['custom_fields' => ['name' => 'Musah Musah']],
              ]);
           
              return $payment;
          }
      }
    • Create a controller to handle the payment request using Dependency Injection through the StripeContract interface.

      use Illuminate\Http\Request;
      use MusahMusah\LaravelMultipaymentGateways\Contracts\StripeContract;
      
      class StripePaymentController extends Controller
      {
          public function initiatePayment(Request $request, StripeContract $stripe)
          {
              $payment = $stripe->createIntent([
                  'amount' => 1000,
                  'currency' => 'usd',
                  'payment_method_types' => ['card'],
                  'payment_method' => 'xxxxxxx',
                  'metadata' => ['custom_fields' => ['name' => 'Musah Musah']],
              ]);
           
              return $payment;
          }
      }
    • Create a controller to handle the payment request using Helper.

      use Illuminate\Http\Request;
      
      class StripePaymentController extends Controller
      {
          public function initiatePayment(Request $request)
          {
              $payment = stripe()->createIntent([
                  'amount' => 1000,
                  'currency' => 'usd',
                  'payment_method_types' => ['card'],
                  'payment_method' => 'xxxxxxx',
                  'metadata' => ['custom_fields' => ['name' => 'Musah Musah']],
              ]);
           
              return $payment;
          }
      }

The createIntent method will create a payment intent and return the client secret to be used in the frontend to confirm the payment. In addition, the package also provides a method to confirm the payment intent. You can confirm the payment intent in the following ways:

  1. Prepare your route to handle the payment confirmation request.

    use Illuminate\Support\Facades\Route;
    use App\Http\Controllers\StripePaymentController;
    
    Route::post('/payment/stripe/confirm', [StripePaymentController::class, 'confirmPayment'])->name('payment.stripe.confirm');
  2. Create a controller to handle the payment confirmation request using Facade.

    use Illuminate\Http\Request;
    use MusahMusah\LaravelMultipaymentGateways\Facades\Stripe;
    
    class StripePaymentController extends Controller
    {
        public function confirmPayment(Request $request)
        {
            $payment = Stripe::confirmIntent($request->payment_intent_id);
            
            if ($payment->status === 'succeeded') {
                // Payment was successful
            }
            
            return $payment;
        }
    }

    The confirmIntent method will confirm the payment intent and return the payment response. This can also be done using the helper or dependency injection.

Handling Webhooks

Webhooks can be handled in the following ways:

  1. Prepare your route to handle in-coming webhook request.

    use Illuminate\Support\Facades\Route;
    
    Route::webhook('/payment/your-payment-gateway-name', 'your-payment-gateway-name');
  2. Creating a Job Class that extends the MusahMusah\LaravelMultipaymentGateways\Jobs\ProcessPaymentWebhookJob class

    use MusahMusah\LaravelMultipaymentGateways\Jobs\ProcessPaymentWebhookJob;
    
    class PaymentWebhookJob extends ProcessPaymentWebhookJob implements ShouldQueue
    {
        public function handle()
        {
            // Get the webhook data
            $webhookData = $this->webhookPayload;
            
            // Handle the webhook
        }
    }

    OR

    Listening to the MusahMusah\LaravelMultipaymentGateways\Events\PaymentWebhookReceived event dispatched by the package.

    • Create an event listener class that will listen to the MusahMusah\LaravelMultipaymentGateways\Events\PaymentWebhookReceived event.
      use MusahMusah\LaravelMultipaymentGateways\Events\PaymentWebhookReceivedEvent;
      class PaymentWebhookListener
      {
          public function handle(PaymentWebhookReceivedEvent $event)
          {
              // Get the webhook data
              $webhookData = $event->webhookPayload;
            
              // Handle the webhook
          }
      }
    • Register the event listener in the EventServiceProvider class.
      use MusahMusah\LaravelMultipaymentGateways\Events\PaymentWebhookReceivedEvent;
      use App\Listeners\PaymentWebhookListener;
      
      protected $listen = [
          PaymentWebhookReceivedEvent::class => [
              PaymentWebhookListener::class,
          ],
      ];
  3. Performing Webhook Signature Validation:
    The package provides PaymentWebhookSignatureValidator interface to validate the signature of the in-coming webhook request. You can create your own signature validator class by implementing the \MusahMusah\LaravelMultipaymentGateways\SignatureValidator\PaymentWebhookSignatureValidator interface.

    use Illuminate\Http\Request;
    use MusahMusah\LaravelMultipaymentGateways\SignatureValidator\PaymentWebhookSignatureValidator;
    
    class StripeWebhookSignatureValidator implements PaymentWebhookSignatureValidator
    {
        public function isValid(Request $request, PaymentWebhookConfig $config): bool
        {
            // Validate the signature
        }
    }

    Then, register the signature validator class in the config/multipayment-gateways.php file.

    'signature_validators' =>  \App\SignatureValidators\StripeWebhookSignatureValidator::class,

    This will ensure that the signature of the in-coming webhook request is valid before the webhook is processed. Allowing you to handle invalid signatures as you wish.

Testing

php artisan test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

About

A Laravel Package that makes implementation of multiple payment Gateways such as Paystack, Flutterwave and Stripe endpoints and webhooks seamless.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • PHP 100.0%