# 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&#x20;
* product\_name&#x20;

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&#x20;

***

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:-&#x20;

* 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:-&#x20;

* 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:-&#x20;

* 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
<?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
<?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
<?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
<?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);
    }
}

```
