Skip to content

Background Jobs

Upvendo uses Laravel's queue system to offload time-consuming operations from the HTTP request cycle. Jobs handle email sending, payment capture, integration syncing, image processing, and order processing.

Key Files

All jobs live in app/Jobs/:

app/Jobs/
  CapturePaymentJob.php
  CompressImageJob.php
  ExportUpvendoMenuToShopifyJob.php
  GenerateQrCodesConsolidation.php
  GenerateQrCodesConsolidationBatch.php
  GenerateQrCodesFinalConsolidation.php
  GenerateQrCodesForSection.php
  GenerateQrCodesForSectionBatch.php
  GetKassanetBillJob.php
  ImportKassanetCategoriesJob.php
  ImportKassanetDisplayGroupsJob.php
  ImportKassanetMenuJob.php
  ImportKassanetProductsJob.php
  ImportShopifyMenuJob.php
  PayKassanetBillJob.php
  ProcessOrderJob.php
  RetranslatePublishedLanguagesJob.php
  SendBackofficeOTPMail.php
  SendDeviceActivationCodeMail.php
  SendGridMail.php
  SendLocalOrderDeliveredMail.php
  SendLocalOrderMissedDeliveryMail.php
  SendLocalOrderOutForDeliveryMail.php
  SendLocalOrderPickupCompletedMail.php
  SendLocalOrderReadyDeliveryMail.php
  SendLocalOrderReadyForPickupMail.php
  SendLocalOrderReceiptMail.php
  SendLoyaltyProgramAlertMail.php
  SendMerchantInvitationMail.php
  SendPasswordResetMail.php
  SendSMS.php
  SendVerificationCodeMail.php
  SyncLocationToDeliverooJob.php
  SyncSquareIntegrationJob.php
  SyncSquareInventoryJob.php
  SyncSquareJob.php
  SyncSquareLocationJob.php
  SyncSquareMenuJob.php
  UpdateD1StocksAfterPayment.php
  UpdateUserUberLocationsJob.php
  UberEats/
    ProcessUberEatsCancelNotificationJob.php
    ProcessUberEatsOrderNotificationJob.php
    ProcessUberEatsScheduledNotificationJob.php

Job Categories

Payment Jobs

JobPurposeDispatched By
CapturePaymentJobCapture payment after terminal confirmationKioskOrchestrator
ProcessOrderJobPost-payment order processing (KDS, notifications)PaymentCaptureService
UpdateD1StocksAfterPaymentSync stock changes to Cloudflare D1 edge databasePaymentCaptureService

ProcessOrderJob

The most important job in the payment flow. Dispatched after successful payment capture.

php
ProcessOrderJob::dispatch($transactionId, $locationId);

Dispatch timing (from PaymentCaptureService::processOrderJob()):

  • Kiosk orders: Dispatched immediately
  • Online ordering (dine-in): Dispatched immediately
  • Online ordering (delivery/pickup): May be delayed based on sent_at time
  • Local development: Executed synchronously via ->handle()

Delayed dispatch for scheduled orders:

php
$delayInSeconds = $processTime->diffInSeconds($now, true);
ProcessOrderJob::dispatch($transactionId, $locationId)->delay($delayInSeconds);

Email Jobs

JobPurposeTrigger
SendBackofficeOTPMailSend OTP code for 2FA loginAuthService::requestBackofficeOTP()
SendDeviceActivationCodeMailSend device activation codeDevice management
SendGridMailGeneric email via SendGridVarious
SendLocalOrderReceiptMailSend order receipt to customerAfter payment capture
SendLocalOrderReadyForPickupMailNotify customer order is readyKDS marks order ready
SendLocalOrderOutForDeliveryMailNotify customer order is out for deliveryOrder status change
SendLocalOrderDeliveredMailConfirm delivery to customerOrder status change
SendLocalOrderReadyDeliveryMailNotify order ready for deliveryKDS marks order ready
SendLocalOrderPickupCompletedMailConfirm pickup completeOrder status change
SendLocalOrderMissedDeliveryMailNotify missed deliveryDelivery status
SendLoyaltyProgramAlertMailLoyalty program notificationsLoyalty threshold reached
SendMerchantInvitationMailInvite user to merchant teamTeam management
SendPasswordResetMailPassword reset linkAuthService::forgetPassword()
SendVerificationCodeMailCustomer verification codeAuthCustomerService::sendOTP()

SMS Jobs

JobPurpose
SendSMSSend SMS via configured provider (customer OTP, notifications)

Integration Sync Jobs

JobPurposeTrigger
SyncSquareJobFull Square integration syncManual or scheduled
SyncSquareMenuJobSync menu catalog to SquareMenu publish
SyncSquareInventoryJobSync inventory to SquareInventory changes
SyncSquareLocationJobSync location data to SquareLocation update
SyncSquareIntegrationJobInitialize Square integrationOAuth completion
SyncLocationToDeliverooJobSync location data to DeliverooLocation update
ExportUpvendoMenuToShopifyJobExport menu to ShopifyManual trigger
ImportShopifyMenuJobImport menu from ShopifyManual trigger
UpdateUserUberLocationsJobUpdate Uber Eats store locationsOAuth/config change

Kassanet (POS Integration) Jobs

JobPurpose
ImportKassanetMenuJobImport full menu from Kassanet POS
ImportKassanetCategoriesJobImport categories from Kassanet
ImportKassanetDisplayGroupsJobImport display groups from Kassanet
ImportKassanetProductsJobImport products from Kassanet
GetKassanetBillJobRetrieve bill from Kassanet POS
PayKassanetBillJobSubmit payment to Kassanet POS

Uber Eats Jobs

Located in app/Jobs/UberEats/:

JobPurpose
ProcessUberEatsOrderNotificationJobProcess incoming Uber Eats order
ProcessUberEatsCancelNotificationJobHandle Uber Eats order cancellation
ProcessUberEatsScheduledNotificationJobHandle scheduled order updates

Image Processing Jobs

JobPurpose
CompressImageJobCompress uploaded images for storage optimization

QR Code Generation Jobs

JobPurpose
GenerateQrCodesForSectionGenerate QR codes for a table section
GenerateQrCodesForSectionBatchBatch generate QR codes for section
GenerateQrCodesConsolidationConsolidate generated QR codes
GenerateQrCodesConsolidationBatchBatch consolidation of QR codes
GenerateQrCodesFinalConsolidationFinal consolidation step

Translation Jobs

JobPurpose
RetranslatePublishedLanguagesJobRe-translate all published language content

Dispatch Patterns

Immediate Dispatch

Most jobs are dispatched immediately to the queue:

php
SendBackofficeOTPMail::dispatch($user, $otpCode);

Delayed Dispatch

Used for scheduled orders:

php
ProcessOrderJob::dispatch($transactionId, $locationId)->delay($delayInSeconds);

Synchronous Execution (Local Dev)

In local environment, some jobs execute synchronously for easier debugging:

php
if (app()->isLocal()) {
    (new ProcessOrderJob($transactionId, $locationId))->handle();
    return;
}

Deferred Execution

Some operations use defer() to execute after the HTTP response is sent:

php
defer(function () use ($transaction) {
    $this->publishPaymentStatus(...);
    $this->processOrderJob($transaction);
});

Queue Configuration

The queue driver and connection are configured via environment variables:

VariablePurpose
QUEUE_CONNECTIONQueue driver (redis, database, sqs, sync)
QUEUE_DEFAULTDefault queue name

Queue Names

Jobs may be dispatched to specific queues for priority management. Check individual job classes for $queue property or onQueue() calls.


Job Structure Pattern

All jobs follow Laravel's standard structure:

php
class ExampleJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        private string $someId,
        private string $tenantDatabase
    ) {}

    public function handle(): void
    {
        // Set tenant database if multi-tenant
        config()->set('database.connections.tenant.database', $this->tenantDatabase);

        // Execute job logic
        // ...
    }
}

Multi-Tenant Considerations

Jobs that access tenant data must set the tenant database at the start of handle():

php
config()->set('database.connections.tenant.database', $this->tenantDatabase);

The tenant database name is typically passed as a constructor parameter when dispatching:

php
UpdateD1StocksAfterPayment::dispatch(
    $transaction,
    config('database.connections.tenant.database')
);

Error Handling and Retries

Default Behavior

Laravel's queue system provides automatic retry behavior. Failed jobs are stored in the failed_jobs table.

Manual Retry

bash
php artisan queue:retry {job_id}
php artisan queue:retry all

Monitoring Failed Jobs

bash
php artisan queue:failed

Debugging Jobs

Common Issues

  1. Job not executing: Check queue worker is running (php artisan queue:work)
  2. Tenant database error: Verify tenant database name is passed to job constructor
  3. Serialization error: Ensure job constructor parameters are serializable (use IDs, not models)
  4. Job timing: For delayed jobs, check sent_at calculation in PaymentCaptureService::processOrderJob()

Logging

Jobs typically log their execution. Search for job class name in logs:

grep "ProcessOrderJob" storage/logs/laravel.log

Running Jobs Synchronously

For debugging, set QUEUE_CONNECTION=sync in .env to execute all jobs synchronously.