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

jimmitjoo's avatar

Vonage notifications sent multiple times

Hi, in our app we are just triggering the notification once, however there are sent 3 or sometimes 4 sms notifications from Vonage.

Our logs looks like this:

[2024-07-12 07:45:02] production.DEBUG: About to send a vonage notification from Before Leave Notification.  
[2024-07-12 07:45:05] production.INFO: {"msisdn":"xxxxx273348","to":"xxxxx432606","network-code":"24001","messageId":"b769c6e5-c44f-4850-bc7c-59489043b912","price":"0.05440000","status":"delivered","scts":"2407120945","err-code":"0","api-key":"x","message-timestamp":"2024-07-12 07:45:05"}  
[2024-07-12 07:45:06] production.INFO: {"msisdn":"xxxxx273348","to":"xxxxx432606","network-code":"24001","messageId":"cb031e58-58ab-4afa-b667-0285812f6455","price":"0.05440000","status":"delivered","scts":"2407120945","err-code":"0","api-key":"x","message-timestamp":"2024-07-12 07:45:06"}  
[2024-07-12 07:45:06] production.INFO: {"msisdn":"xxxxx273348","to":"xxxxx432606","network-code":"24001","messageId":"a46d3bf8-bb33-40fd-af40-df5a5da9ed5e","price":"0.05440000","status":"delivered","scts":"2407120945","err-code":"0","api-key":"x","message-timestamp":"2024-07-12 07:45:06"}  
[2024-07-12 07:45:07] production.INFO: {"msisdn":"xxxxx273348","to":"xxxxx432606","network-code":"24001","messageId":"915b543d-6a24-4b78-b6fb-a8f2057d3c0f","price":"0.05440000","status":"delivered","scts":"2407120945","err-code":"0","api-key":"x","message-timestamp":"2024-07-12 07:45:07"}  

The "About to send vonage notification..." is logged on the line before we trigger the notification.

Here is one example of a command sending a notification:

<?php

namespace App\Console\Commands;

use App\Models\Order;
use App\Notifications\BeforeLeaveNotification;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class CheckUpcomingLeaveCars extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'app:check-upcoming-leave-cars';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $orders = Order::query()
            ->where(function ($query) {
                $query->where('paid', true)
                    ->orWhere('invoice', true);
            })
            ->whereDate('arrive_date', now()->addDay())
            ->whereTime('arrive_time', '<=', now()->format('H:i'))
            ->where('leave_notification_sent', false)
            ->with('user')
            ->get();

        foreach ($orders as $order) {
            // Send notification
            Log::info('Sending notification from CheckUpcomingLeaveCars: ' . $order->uuid);

            $order->user->notify(new BeforeLeaveNotification($order));

            $order->update([
                'leave_notification_sent' => true,
            ]);
        }
    }
}

And here is one of the notifications itself:

<?php

namespace App\Notifications;

use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\VonageMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;

class BeforeLeaveNotification extends Notification
{
    use Queueable;

    public Order $order;

    /**
     * Create a new notification instance.
     */
    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @return array<int, string>
     */
    public function via(object $notifiable): array
    {
        return ['vonage'];
    }

    public function toVonage(object $notifiable): VonageMessage
    {
        Log::debug('About to send a vonage notification from Before Leave Notification.');

        return (new VonageMessage)
            ->statusCallback(route('vonage.webhook'))
            ->content('Secret message...')
            ->unicode();
    }

    /**
     * Get the array representation of the notification.
     *
     * @return array<string, mixed>
     */
    public function toArray(object $notifiable): array
    {
        return [
            //
        ];
    }
}

I have been searching and making sure that every notification for real is only triggered once. Logging just before every notification is sent and that line is only printed once, but the callback logging from "Vonage webhook" is triggered multiple times.

Route::any('/vonage/webhook', function () {
    // Body of request to json string
    $body = json_encode(request()->all());
    // Log the body of the request
    Log::info('Vonage webhook body:' . $body);

    return response()->json([
        'status' => 'ok',
    ]);
})->name('vonage.webhook');

What on earth may cause this? I am so confused.

0 likes
4 replies
martinbean's avatar

@jimmitjoo Well how are the notifications actually sent? Because you show us a console command and then the notification class, but you don’t actually show or tell us how this app:check-upcoming-leave-cars command is actually being invoked.

jimmitjoo's avatar

Right, they are sent from console.php in the routes directory, like this:

Schedule::command('app:check-upcoming-leave-cars')->everyFiveMinutes();
martinbean's avatar

Right, they are sent from console.php in the routes directory, like this:

@jimmitjoo And how is your app hosted? Are you using a load balancer of some sort? As I was kinda expecting you were scheduling the command, I just wanted to be sure.

If you are running the scheduler on multiple servers, it’s going to dispatch the notifications that many times as well. So you’ll need to add the onOneServer modifier to your scheduled command:

Schedule::command('app:check-upcoming-leave-cars')
    ->everyFiveMinutes()
    ->onOneServer();
jimmitjoo's avatar

@martinbean actually it is just a single instance on AWS deployed using Laravel Forge. No load balancers or anything like that.

I have after starting this thread tried to set a lock on the messages, but they are still going out 3-4 times on each call. It doesn't matter which notification it is, every single one is sending multiple messages. Starting to think there are some issue with the vonage-notification-channel package. But cannot find anyone else reporting on this issue.

Laravel: 11.11.1 Vonage Notification Channel: 3.3.0

public function toVonage(object $notifiable): ?VonageMessage
    {
        $lockKey = 'order-paid-notification-' . $this->order->uuid;
        $lock = Cache::lock($lockKey, 60);

        if ($lock->get()) {
            try {
                Log::debug('About to send a vonage notification from Order Paid Notification.');

                return (new VonageMessage)
                    ->statusCallback(route('vonage.webhook'))
                    ->content('Thanks for your order!')
                    ->unicode();
            } finally {
                $lock->release();
            }
        } else {
            Log::info('Could not obtain lock for order: ' . $this->order->uuid);
            return null; // Returning null will prevent the notification from being sent
        }
    }

Please or to participate in this conversation.