...
Posted on Dec 12, 2023 | Category Laravel | 191
Share:

Create an Instant Push Notification(IPN) Service in Laravel


Let’s create an Instant Push Notificaion(IPN) service for user’s card add and delete in a payment gateway service. We will notifify the IPN listener about this user action.

First, we will create notifications table using php artisan command

php artisan make:migration create_notifications_table --create=notifications

please add the scemea inside the migration file

Schema::create('notifications', function (Blueprint $table) {
            $table->id();
            $table->string('action_type');
            $table->string('ipn_listener_url');
            $table->text('post_data');
            $table->integer('tries')->default(0);
            $table->timestamp('last_tried_at')->nullable();
            $table->timestamps();
            $table->timestamp('deleted_at')->nullable(); //for soft delete
        });
    }

Now, migrate this file using the command

php artisan migrate

Let’s create a model call Notification.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Notification extends Model
{
    use SoftDeletes;

    protected $table = 'notifications';
}

Now, we will save a notification to this notification table when a user add/delete a card.

First, create a route for action of a card. We will send card index, action type to indentify which card is added/deleted.

$router->post('card/action', 'CardController@cardAction');

Create a controller CardController.php and add a method called cardAction

public function cardAction(Request $request){
  // store this action to notificaion table
  $notification = new Notification();
  $notification->action_type = $request->action_type;
  $notification->ipn_listener_url = 'http://localhost/my-ipn-listener'; // please change it to your ipn listener url who will receive the IPN data
  $notification->post_data = json_encode(['action_type'=>$request->action_type, 'card_index'=>$request->card_index]);
  $notification->tries = 0;
  $notification->last_tried_at = null;
  
  if(!$notification->save())
      return false;
  return true;
}

It’s time to create our artisan command which will send notificiaon to the IPN listener. Let’s create a artisan command first. In your terminal type,

php artisan make:command IpnNotification --command=ipn:notification

We will see a new file IpnNotification.php file is created inside app/console/Commands folder.

In the IpnNotification.php class we will first add the description of the command.

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'notify data to IPN';

Next, to send the IPN data we will add our notificaion send logic inside the handle method which is already provided in the class.

public function handle()
{
   $tryLimit = 3; // max try limit for a failed notificaion
   $intervalMinute = 5; // try a failed one after each five minutes

   // send all the notifications between yesterday and today
   $notifications = Notification::whereBetween('created_at', [Carbon::yesterday()->startOfDay(), Carbon::now()]) 
                              ->where(function($query) use($intervalMinute){
                                    $query->where('last_tried_at', '<', Carbon::now()->subMinutes($intervalMinute))
                                       ->orWhereNull('last_tried_at');
                              })
                              ->where('tries', '<=', $tryLimit)
                              ->get();
   
   if(!$notifications->isEmpty()){
      foreach($notifications as $notification) {
            $payload = ['ipn_data'=>$notification->post_data];
            
            // sending notification using CURL request
            $isSent = $this->sendNotification($notification->ipn_listener_url, $payload);  

            if($isSent) {
               $notification->delete(); // soft delete the notifcaion as it's successful and no need to send further
            }
            else{
               $notification->tries = $notification->tries + 1;
               $notification->save();
            }
      }
   }
}

private function sendNotification(string $url, array $body)
{
   try{
      $curl = curl_init();

      curl_setopt_array($curl, array(
            CURLOPT_URL => $url,
            CURLOPT_TIMEOUT_MS => 60000, // Set the timeout value in milliseconds
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "POST",
            CURLOPT_POSTFIELDS => $body,
      ));

      $curlResponse = curl_exec($curl);
      $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
      $err = curl_error($curl);

      curl_close($curl);

      if (curl_errno($curl) == CURLE_OPERATION_TIMEDOUT || $err || $httpcode != 200)
         return false;

      return true;
   } catch(\Throwable $e) {
      return false;
   }
}

Now, we will register our notification command inside app/console/Kernel.php file.

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Laravel\Lumen\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        'App\Console\Commands\IpnNotification',
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('ipn:notification')
            ->everyMinute()
            ->withoutOverlapping();
    }
}

To Send the notificaion we will run our specific schedule command

php artisan ipn:notification

Or we can run the scheduer

php artisan schedule:run

If we always want to run the scheduler every minute then we can achieve it by adding the following Cron entry to our server.

* * * * * php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1

Tags: Laravel PHP Web Development IPN Push Notification