💲
Laravel Payable
  • Laravel Payable
  • Introduction
  • Installation
  • Usages
  • Fiscal
  • Payment
  • Payment History
  • Payment Gateway
  • Payabel Facade
  • Configurations
Powered by GitBook
On this page
  • Payment Gateway Service Class
  • Payment Gateway
  • Example Walk Through
  • Some of the premade payment gateways
  • Esewa
  • Khalti
  • Fonepay

Payment Gateway

Payment Gateway Service Class

php artisan payable:gateway Paypal

This will generate Paypal class implementing PaymentGatewayInterface located in app\PaymentGateway implementing 5 methods :-


pay(float $amount, $return_url, $purchase_order_id, $purchase_order_name);

Accepts 4 arguments:-

  • amount(float): Payment amount to be processed via payment gateway

  • return_url : Payment gateway redirect url

  • product_id

  • product_name

Responsible for payment process requests to payment gateway


initiate(float $amount, $return_url, ?array $arguments = null)

Accepts 3 arguments:-

  • amount(float): Payment amount to be processed via payment gateway

  • return_url : Payment gateway redirect url

  • arguments(array) : Any additional data needed for the transaction process

Responsible for payment process requests to payment gateway


inquiry($transaction_id, ?array $arguments = null) : array

Accepts 2 arguments:-

  • transaction_id: Uniquely identifiable key given by payment gateway vendor

  • arguments(array) : Any additional data needed for the inquiry process

Responsible for lookup requests for payment transactions to verify their authenticity


isSuccess(array $inquiry, ?array $arguments = null): bool

Accepts 2 arguments:-

  • inquiry: Array response from inquiry method

  • arguments(array) : Any additional data needed for the isSuccess process

Responsible for verifying transaction success status


requestedAmount(array $inquiry, ?array $arguments = null): float

Accepts 2 arguments:-

  • inquiry: Array response from inquiry method

  • arguments(array) : Any additional data needed for the isSuccess process

Responsible for returning requested amount to be processed in payment gateway

Payment Gateway

This class accepts payment gateway service class and is responsible for initiating and processing payment from the gateway to registering verified payment to the database

__construct(PaymentGatewayInterface $gateway, $model)

Accepts 2 arguments:-

  • Payment gateway service class (Paypal instance for example)

  • Model instance which has HasPayable trait


pay(float $amount, $return_url, $product_id = null, $product_name = null)

Accepts 3 arguments:-

  • Amount to be processed

  • product_id(optional) if left empty takes model instance id ($model->id)

  • product_name (optional) if left empty takes the model instance name ($model->name)

Responsible for payment process requests to payment gateway


process($transaction_id, ?array $arguments = null): Payment

Accepts 2 arguments:-

  • transaction_id: Uniquely identifiable key given by payment gateway vendor

  • arguments(array) : Any additional data needed for the inquiry process

Responsible for verifying payment status and registering payments.

  • Register a transaction

  • Register a payment

  • Register payment history

Example Walk Through

Creating payment generator

Generate Paypal payment gateway using the payment gateway generator command

<?php

namespace App\PaymentGateway;

use Exception;
use Illuminate\Support\Facades\Http;
use Pratiksh\Payable\Contracts\PaymentGatewayInterface;

class Paypal  implements PaymentGatewayInterface
{
    /**
    *
    * Function to perform some logic before payment process
    *
    */
    public function pay(float $amount, $return_url)
    {
        // Some Actions
        return $this->initiate($amount, $return_url);
    }

    /**
    *
    * Initiate Payment Gateway Transaction
    * @param float amount : Amount requested for payment transaction
    * @param return_url : Redirect url after payment transaction
    * @param array arguments : Additional dataset
    *
    */
    public function initiate(float $amount, $return_url, ?array $arguments = null)
    {
        // Some Actions
    }

    /**
     *
     * Success status of payment transaction 
     * @param array inquiry : Payment transaction response
     * @param array arguments : Additional dataset
     * @return bool 
     *
     */
    public function isSuccess(array $inquiry, ?array $arguments = null): bool
    {
        return true;
    }

    /**
     *
     * Requested amount to be registered
     * @param array inquiry : Payment transaction response
     * @param array arguments : Additional dataset
     * @return float 
     *
     */
    public function requestedAmount(array $inquiry, ?array $arguments = null): float
    {
        return 0;
    }

    /**
     *
     * Payment status lookup request
     * @param mixed transaction_id : Code provided by payment gateway vendor to uniquely identify the payment transaction 
     * @param array arguments : Additional dataset
     * @return array 
     *
     */
     public function inquiry($transaction_id, ?array $arguments = null): array
     {
        return [];
     }
}

Controller to handle checkout and verification

<?php

namespace App\Http\Controllers\PaymentGateway;

use Illuminate\Http\Request;
use App\Models\Booking;
use App\Http\Controllers\Controller;
use App\PaymentGateway\Paypal;
use Pratiksh\Payable\Services\PaymentGateway;

class PaypalController extends Controller
{
    // Booking Checkout
    public function checkout(Booking $booking){
        $paypal= new Paypal;
        $return_url = route('paypal.booking.verification', ['booking' => $booking->id]);
        return (new PaymentGateway($paypal, $booking))->pay($booking->fee, $return_url);
    }
    // Adventure Booking Verification
    public function verification(Request $request,Booking $booking)
    {
        $decodedString = base64_decode($request->data);
        $data = json_decode($decodedString, true);
        $transaction_code = $data['transaction_code'] ?? null;
        $status = $data['status'] ?? null;
        $total_amount = $data['total_amount'] ?? null;
        $transaction_uuid = $data['transaction_uuid'] ?? null;
        $product_code = $data['product_code'] ?? null;
        $signed_field_names = $data['signed_field_names'] ?? null;
        $signature = $data['signature'] ?? null;
        $payment = (new PaymentGateway(new Paypal, $booking))->process($transaction_uuid, [
            'transaction_code' => $transaction_code,
            'status' => $status,
            'total_amount' => $total_amount,
            'transaction_uuid' => $transaction_uuid,
            'product_code' => $product_code,
            'signed_field_names' => $signed_field_names,
            'signature' => $signature,
        ]);
       dd('Payment Success');
    }
}

Routes to handle checkout and verification

Route::get('paypal-booking-checkout/{booking}',[PaypalController::class,'checkout'])->name('paypal.booking.checkout');
Route::get('paypal-booking-verification/{booking}',[PaypalController::class,'verification'])->name('paypal.booking.verification');

Some of the premade payment gateways

Esewa

<?php

namespace App\PaymentGateway;

use Exception;
use Illuminate\Support\Facades\Http;
use Pratiksh\Payable\Contracts\PaymentGatewayInterface;

class Esewa  implements PaymentGatewayInterface
{
    public $inquiry;
    public $amount;
    public $base_url;
    public $purchase_order_id;
    public $purchase_order_name;

    public function __construct()
    {
        $this->base_url = env('APP_DEBUG') ? 'https://uat.esewa.com.np/api/epay/' : 'https://epay.esewa.com.np/api/epay/';
    }

    /**
    *
    * Function to perform some logic before payment process
    */
    public function pay(float $amount, $return_url, $purchase_order_id, $purchase_order_name)
    {
        $this->purchase_order_id = $purchase_order_id;
        $this->purchase_order_name = $purchase_order_name;
        return $this->initiate($amount, $return_url);
    }

    /**
     *
     * Initiate Payment Gateway Transaction
     * @param float amount : Amount requested for payment transaction
     * @param return_url : Redirect url after payment transaction
     * @param array arguments : Additional dataset
     *
     */
    public function initiate(float $amount, $return_url, ?array $arguments = null)
    {
        $this->amount = env('APP_DEBUG') ? 1000 : $amount;
        $process_url = $this->base_url . 'main/v2/form/';
        $tuid = now()->timestamp;
        $merchant_id = env('ESEWA_MERCHENT_ID');
        $message = "total_amount=$amount,transaction_uuid=$tuid,product_code=$merchant_id";
        $s = hash_hmac('sha256', $message, env('ESEWA_SECRET_KEY'), true);
        $signature = base64_encode($s);
        $data = [
            "amount" => $amount,
            "failure_url" => url('/'),
            "product_delivery_charge" => "0",
            "product_service_charge" => "0",
            "product_code" => "EPAYTEST",
            "signature" => $signature,
            "signed_field_names" => "total_amount,transaction_uuid,product_code",
            "success_url" => $return_url,
            "tax_amount" => "0",
            "total_amount" => $amount,
            "transaction_uuid" => $tuid
        ];

        // generate form from attributes
        $htmlForm = '<form method="POST" action="' . ($process_url) . '" id="esewa-form">';

        foreach ($data as $name => $value) :
            $htmlForm .= sprintf('<input name="%s" type="hidden" value="%s">', $name, $value);
        endforeach;

        $htmlForm .= '</form><script type="text/javascript">document.getElementById("esewa-form").submit();</script>';

        // output the form
        echo $htmlForm;
    }

    /**
     *
     * Success status of payment transaction 
     * @param array inquiry : Payment transaction response
     * @param array arguments : Additional dataset
     * @return bool 
     *
     */
    public function isSuccess(array $inquiry, ?array $arguments = null): bool
    {
        return ($inquiry['status'] ?? null) == 'COMPLETE';
    }

    /**
     *
     * Requested amount to be registered
     * @param array inquiry : Payment transaction response
     * @param array arguments : Additional dataset
     * @return float 
     *
     */
    public function requestedAmount(array $inquiry, ?array $arguments = null): float
    {
        return $inquiry['total_amount'];
    }

    /**
     *
     * Payment status lookup request
     * @param mixed transaction_id : Code provided by payment gateway vendor to uniquely identify payment transaction 
     * @param array arguments : Additional dataset
     * @return array 
     *
     */
    public function inquiry($transaction_id, ?array $arguments = null): array
    {
        $process_url = $this->base_url . 'transaction/status/';
        $total_amount = $arguments['total_amount'] ?? null;
        if (!is_null($total_amount)) {
            $payload = [
                'product_code' => env('ESEWA_MERCHENT_ID'),
                'transaction_uuid' => $transaction_id,
                'total_amount' => $total_amount
            ];
            $response = Http::get($process_url, $payload);
            $this->inquiry =  json_decode($response->body(), true);
            return $this->inquiry;
        } else {
            throw new Exception('total_amount is required');
        }
    }
}

Khalti

<?php

namespace App\PaymentGateway;

use Exception;
use Stripe\Refund;
use Illuminate\Support\Facades\Http;
use Pratiksh\Payable\Contracts\PaymentGatewayInterface;

class Khalti  implements PaymentGatewayInterface
{
    public $amount;
    public $base_url;

    public $purchase_order_id;
    public $purchase_order_name;

    public $inquiry_response;

    /*
    |--------------------------------------------------------------------------
    | Customer Detail
    |--------------------------------------------------------------------------
    | 
    */
    public $customer_name;
    public $customer_phone;
    public $customer_email;

    public function __construct()
    {
        $this->base_url = env('APP_DEBUG') ? 'https://a.khalti.com/api/v2/' : 'https://khalti.com/api/v2/';
    }

    public function byCustomer($name,$email,$phone){
        $this->customer_name = $name;
        $this->customer_email = $email;
        $this->customer_phone = $phone;
        return $this;
    }

    public function pay(float $amount, $return_url, $purchase_order_id, $purchase_order_name)
    {
        $this->purchase_order_id = $purchase_order_id;
        $this->purchase_order_name = $purchase_order_name;
        return $this->initiate($amount, $return_url);
    }

    public function initiate(float $amount, $return_url, ?array $arguments = null)
    {
        $this->amount = env('APP_DEBUG') ? 1000 : ($amount * 100);
        $process_url = $this->base_url . 'epayment/initiate/';

        $return_url = $return_url;
        $website_url = url('/');
        $purchase_order_id = $this->purchase_order_id;
        $purchase_order_name = $this->purchase_order_name;
        $customer_name = $this->customer_name;
        $customer_email = $this->customer_email;
        $customer_phone = $this->customer_phone;

        // Build the data array
        $data = [
            "return_url" => $return_url,
            "website_url" => $website_url,
            "amount" =>  $this->amount,
            "purchase_order_id" => $purchase_order_id,
            "purchase_order_name" => $purchase_order_name,
            "customer_info" => [
                "name" => $customer_name,
                "email" => $customer_email,
                "phone" => $customer_phone
            ]
        ];

        $response = Http::withHeaders([
            'Content-Type' => 'application/json',
            'Authorization' => 'key ' . env('KHALTI_SECRET_KEY'), // Replace with your authorization token
        ])->post($process_url, $data);
        if ($response->ok()) {
            $body = json_decode($response->body());
            return redirect()->to($body->payment_url);
        } else {
            throw new Exception('Khalti transaction failed');
        }
    }

    public function isSuccess(array $inquiry, ?array $arguments = null): bool
    {
        return ($inquiry['status'] ?? null) == 'Completed';
    }

    public function requestedAmount(array $inquiry, ?array $arguments = null): float
    {
        return $inquiry['total_amount'];
    }

    public function inquiry($transaction_id, ?array $arguments = null) : array
    {
        $process_url = $this->base_url . 'epayment/lookup/';
        $payload = [
            'pidx' => $transaction_id
        ];
        $response = Http::withHeaders([
            'Content-Type' => 'application/json',
            'Authorization' => 'key ' . env('KHALTI_SECRET_KEY'),
        ])->post($process_url, $payload);
        $this->inquiry_response =  json_decode($response->body(),true);
        return $this->inquiry_response;
    }
}

Fonepay

<?php
namespace App\PaymentGateway;

use Exception;
use Carbon\Carbon;
use Illuminate\Support\Facades\Http;
use Pratiksh\Payable\Contracts\PaymentGatewayInterface;

class Fonepay implements PaymentGatewayInterface
{
    public $inquiry;
    public $amount;
    public $base_url;
    public $purchase_order_id;
    public $purchase_order_name;

    public function __construct()
    {
        $this->base_url = env('APP_DEBUG') ? 'https://dev-clientapi.fonepay.com/' : 'https://clientapi.fonepay.com/';
    }

    public function pay(float $amount, $return_url, $purchase_order_id, $purchase_order_name)
    {
        $this->purchase_order_id = $purchase_order_id;
        $this->purchase_order_name = $purchase_order_name;
        return $this->initiate($amount, $return_url);
    }


    public function initiate(float $amount, $return_url, ?array $arguments = null)
    {
        $this->amount = env('APP_DEBUG') ? 1000 : $amount;
        $process_url = $this->base_url . 'api/merchantRequest';
        $sharedSecretKey  = env('FONEPAY_SECRET_KEY');
        $merchant_id = env('FONEPAY_MERCHANT_ID');
        $RU = $return_url; // Return URL
        $PID = $merchant_id; // Merchant Code, Defined by fonepay system
        $PRN = $this->purchase_order_id; //Product Reference Number, need to send by merchant
        $AMT = $this->amount; // Payable Amount
        $CRN = 'NPR';
        $DT =  date('m/d/Y');
        $R1 = $this->purchase_order_name; // Additional Info 1
        $R2 = title(); // Additional Info 2
        $MD = 'P'; // P - Payment
        $DV = hash_hmac('sha512', $PID . ',' . $MD . ',' . $PRN . ',' . $AMT . ',' . $CRN . ',' . $DT . ',' . $R1 . ',' . $R2 . ',' . $RU, $sharedSecretKey);

        $data = [
            'RU' => $RU,
            'PID' => $PID,
            'PRN' => $PRN,
            'AMT' => $AMT,
            'CRN' => $CRN,
            'DT' => $DT,
            'R1' => $R1,
            'R2' => $R2,
            'MD' => $MD,
            'DV' => $DV,
        ];

        // generate form from attributes
        $htmlForm = '<form method="POST" action="' . ($process_url) . '" id="esewa-form">';

        foreach ($data as $name => $value) :
            $htmlForm .= sprintf('<input name="%s" type="hidden" value="%s">', $name, $value);
        endforeach;

        $htmlForm .= '</form><script type="text/javascript">document.getElementById("esewa-form").submit();</script>';

        // output the form
        echo $htmlForm;
    }

    public function isSuccess(array $inquiry, ?array $arguments = null): bool
    {
        return ($inquiry['success'] ?? null) == 'true';
    }

    public function requestedAmount(array $inquiry, ?array $arguments = null): float
    {
        return $inquiry['amount'];
    }

    public function inquiry($transaction_id, ?array $arguments = null) : array
    {
        $process_url = $this->base_url . 'api/merchantRequest/verificationMerchant';
        $sharedSecretKey  = env('FONEPAY_SECRET_KEY');
        $PID = env('FONEPAY_MERCHANT_ID');
        $UID = $transaction_id;
        $PRN = $arguments['PRN'] ?? '';
        $BID = $arguments['BID'] ?? '';
        $R_AMT = $arguments['R_AMT'] ?? '';

        $data =[

            'PRN' => $PRN,

            'PID' => $PID,

            'BID' => $BID,

            'AMT' => $R_AMT, // original payment amount

            'UID' => $UID,

            'DV' => hash_hmac('sha512', $PID . ',' . $R_AMT . ',' . $PRN . ',' . $BID . ',' . $UID, $sharedSecretKey),

        ];

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $process_url . '?' . http_build_query($data));

        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $responseXML = curl_exec($ch);

        // Load XML string
        $response = simplexml_load_string($responseXML);

        // Convert XML to JSON and then to array
        return json_decode(json_encode($response), true);
    }
}
PreviousPayment HistoryNextPayabel Facade

Last updated 1 year ago