Skip to content

Kiosk Setup

Overview

Kiosks are self-service ordering terminals placed in-store. Customers can browse the menu, customize items, and pay without staff assistance.

Key Purpose: Enable self-service ordering for in-store customers.

Purpose

This page lets you register, configure, and manage self-service kiosk devices that allow in-store customers to browse menus, place orders, and pay independently.

Key Concepts

  • Kiosk Device: A physical terminal (Android tablet) running the Upvendo kiosk application, registered to a specific location via an activation code.
  • Device Profile: A configuration template assigned to a kiosk that controls its menus, dining options, idle behaviour, screensaver, schedule, receipt options, customer-flow layout, and order-number display.
  • Activation Code: A time-limited code generated in the backoffice that pairs a new kiosk device to a location; subject to a resend cooldown (default 15 minutes — ACTIVATION_CODE_RESEND_COOLDOWN_MINUTES — allowing 3 resends per window before it blocks). (Verified: upvendo-backend app/Services/BackOffice/DeviceService.php lines 650-664; config/upvendo.php line 33.)
  • Idempotency Key: A unique identifier attached to each kiosk transaction to prevent duplicate orders during payment retries or network interruptions.
  • Subscription: Each kiosk device carries a subscription record. For directly-onboarded merchants that is a Stripe subscription; for reseller-onboarded merchants a local subscription with a grace period is created instead, and no Stripe subscription exists until the merchant converts to paying. The price SKU differs per location: kiosk_first for the first kiosk, kiosk_additional for each one after it. (Verified: upvendo-backend app/Services/BackOffice/DeviceService.php lines 192-198 and 349-395; app/Services/Subscription/SubscriptionPricingService.php lines 126-165.)

Actions

Register a New Kiosk

Create a kiosk device in the backoffice, generate an activation code, and enter it on the physical terminal to pair and download menu data.

Assign a Payment Terminal

Pair a card terminal to the kiosk per device. Depending on the merchant's payment provider this is a Viva Wallet terminal, a Stripe Terminal reader, or a Square terminal (paired via a device code) so customers can pay by card directly at the device.

Configure a Device Profile

Set the profile's menus, dining options, idle settings, idle screensaver, schedule, receipt options, customer-flow layout, customer identification, and order-number display for the kiosk experience. (Upsell suggestions are configured separately as Upsell Groups, not in the device profile.)

Send Remote Commands

Send a remote-control command to a kiosk device from the backoffice (POST .../devices/{id}/remote-control). Supported commands are restart, logout, firmware_update, and update_apk; the command is delivered to the device over Firebase Cloud Messaging (FCM). Non-Kiosk device types are silently ignored — the service returns without dispatching. (Verified: upvendo-backend app/Enums/RemoteControlCommands.php lines 7-10; app/Services/BackOffice/DeviceService.php lines 1093-1105; routes/api/backoffice/devices.php line 16.)

Cancel a Kiosk Transaction

Cancel a pending kiosk payment, which aborts the active terminal session and publishes a cancellation or failure status to the device.

Location

  • Backoffice Route: /device-management/devices (kiosks are a device type on this page; device profiles live at /device-management/profiles)
  • Backend Controller: app/Http/Controllers/Api/KioskController.php (device management also uses DeviceController.php and DeviceProfileController.php)
  • Vue Component: src/pages/device-management/devices/index.vue (device profile form: src/views/device-profiles/DeviceProfileForm.vue)
  • Kiosk App: upvendo-kiosk

Concepts

Kiosk Device

Physical terminal running the kiosk application.

Device Profile

Configuration template applied to kiosks.

Kiosk Mode

Full-screen, locked-down ordering interface.


Kiosk Device Fields

Device Name

PropertyValue
Field IDname
LabelDevice Name
TypeText
RequiredYes
ValidationMust be unique per merchant. No length limit.

Description: Identifier for this kiosk device.

Examples:

  • "Kiosk 1 - Entrance"
  • "Kiosk 2 - Counter"
  • "Self-Order Terminal A"

Assigned Location

PropertyValue
Field IDlocation_id
LabelLocation
TypeSelect
RequiredYes

Description: Which location this kiosk belongs to.

Business Logic:

  • Kiosk uses this location's menu and settings
  • Orders created for this location
  • Receipts show this location's info

Device Profile

PropertyValue
Field IDdevice_profile_id
LabelDevice Profile
TypeSelect
RequiredNo

Description: Configuration profile applied to this kiosk. The profile is where the menu(s), dining options, idle behaviour, schedule, receipt options and other kiosk settings live. The menu is not set on the device itself.


Payment Terminal

PropertyValue
Field IDviva_wallet_terminal_id / stripe_terminal_id (reader) / external_data.square
LabelReader / Terminal
TypePer-device pairing
RequiredNo

Description: Card terminal paired to this device. The pairing differs by the merchant's payment provider: a Viva Wallet terminal id, a paired Stripe Terminal reader, or a Square terminal device code. There is no single "terminal" select with a "None" option — a device simply has no terminal paired until one is added.


Device Status

PropertyValue
Field IDstatus (derived)
LabelStatus
TypeDisplay
ValuesOnline, Offline

Description: Current status of the kiosk device, derived from activation state and last-seen time.

Statuses:

  • Online: Activated and seen within the offline threshold (default 900 seconds / 15 minutes, overridable via DEVICE_OFFLINE_THRESHOLD_SECONDS).
  • Offline: Not activated, never checked in, explicitly offline, or not seen within the offline threshold.

(Verified: upvendo-backend app/RawModels/Device.php line 346; config/devices.php line 8.)


Device Profile Fields

Profile Name

PropertyValue
Field IDname
LabelProfile Name
TypeText
RequiredYes

Description: Name for this configuration profile.


Location

PropertyValue
Field IDlocation_id
LabelLocation
TypeSelect
RequiredYes

Description: Location this profile belongs to. Devices assigned to the profile serve this location's data.


Default Menu

PropertyValue
Field IDdefault_menu_id (with custom_default_menu)
LabelDefault Menu
TypeSelect

Description: The menu always served to kiosks using this profile. When custom_default_menu is off the profile falls back to the location's in-house default menu; when on, the profile's own default_menu_id is used.


Extra Menus

PropertyValue
Field IDextra_menu_ids
LabelExtra Menus
TypeMulti-select

Description: Additional menus, each scheduled for specific days/times, served alongside the default menu. Like the default menu, these follow custom_default_menu: when it is off the profile serves the location's in-house extra_menu_ids instead of its own. (Verified: upvendo-backend app/RawModels/DeviceProfile.php lines 93-105 and 322-338.)


Idle Timeout

PropertyValue
Field IDidle_timeout_type, idle_timeout_seconds, show_warning_for_seconds
LabelIdle Timeout
TypeRadio (Standard / Custom) + Number (seconds)
Optionsstandard, custom
Defaultstandard

Description: Ends an in-progress session and clears entered data after inactivity, returning the kiosk to the home screen.

Business Logic:

  • Standard: warning given after 45 seconds and shown for 15 seconds — hard-coded return values, so any stored custom numbers are ignored while the type is standard.
  • Custom: you set "give warning after" (idle_timeout_seconds) and "show warning for" (show_warning_for_seconds), in seconds (minimum 1; both required when the type is custom).

(Verified: upvendo-backend app/RawModels/DeviceProfile.php lines 163-179; app/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.php lines 223-225.)


Idle Screensaver

PropertyValue
Field IDidle_screensaver.enabled, idle_screensaver.minutes, screensaver_image (stored as screensaver_content_id)
LabelIdle Screensaver
TypeToggle + Number (minutes) + Image upload
DefaultOff

Description: An optional full-screen image shown on an idle kiosk. This is separate from Idle Timeout: the timeout resets the order to the welcome screen, while the screensaver displays your uploaded image after a set period of inactivity.

Setup (Device Management → Profiles → edit profile → Idle settings):

  1. Turn Idle Screensaver on.
  2. Set the minutes of inactivity before the screensaver appears.
  3. Upload the screensaver image.
  4. Save.

Business Logic:

  • Requires an uploaded image when enabled.
  • Shown on the kiosk after the configured idle minutes; touching the screen returns to ordering.

PropertyValue
Field IDmenu_item_columns
LabelMenu Item Columns
TypeSelect
Options2, 3
Default2

Description: Number of columns used to lay out menu items in the customer flow.


Order Number Display

PropertyValue
Field IDorder_number_display
LabelOrder Number Display
TypeRadio
Optionsqueue_number, order_number, both
Defaultboth

Description: Which identifier is shown to the customer after an order is placed — the queue number, the order number, or both.


Customer Identification

PropertyValue
Field IDallow_customer_identification, customer_identification_methods
LabelAllow Customer Identification
TypeToggle + method toggles
Methodsmember_code, email, phone
Defaultsallow_customer_identification false; methods default to member_code true, email false, phone false

Description: When enabled, customers can identify themselves at the kiosk using the enabled methods; with loyalty active, identified customers see their points and rewards. (Verified: upvendo-backend app/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.php lines 132-149 and 219-222.)


Sell Without Stock

PropertyValue
Field IDsell_without_stock
LabelSell Without Stock
TypeToggle
Defaultfalse

Description: Whether out-of-stock items can still be ordered at the kiosk.


Pager

PropertyValue
Field IDuse_pager, pager_instructions
LabelUse Pager
TypeToggle + instructions

Description: Whether the kiosk hands out a pager and the instructions shown to the customer.


Receipt Options

PropertyValue
Field IDreceipt_options.print, receipt_options.email, receipt_options.phone, auto_print_receipt
LabelReceipt Options
TypeToggles
Defaultsprint true, email true, phone false, auto_print_receipt false

Description: How the customer can receive a receipt at the kiosk — printed, by email, and/or by phone (SMS) — plus whether a receipt prints automatically without the customer choosing. (Verified: upvendo-backend app/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.php lines 78-95 and 200-204; app/Enums/ReceiptOptions.php lines 5-10.)


Order Instructions

PropertyValue
Field IDorder_instructions.enabled, .use_same_for_all, .all, .for_here, .takeout, .translations
LabelOrder Instructions
TypeToggle + text (max 300 characters each)
Defaultsenabled false, use_same_for_all true, texts empty

Description: Optional instruction text shown to the customer after ordering. With use_same_for_all on, the single all text is used; otherwise separate for_here and takeout texts apply. Each text is capped at 300 characters and can carry translations. (Verified: upvendo-backend app/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.php lines 102-125 and 205-210.)


Section Selection

PropertyValue
Field IDsection_selection_enabled, section_ids[]
LabelSection Selection
TypeToggle + multi-select
Requiredsection_selection_enabled is required (boolean); section_ids is required when it is true
Defaultfalse

Description: When enabled, the kiosk asks the customer to pick a section (e.g. a seating area) from the selected section_ids. (Verified: upvendo-backend app/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.php lines 197-199.)


Schedule

PropertyValue
Field IDschedule_type, schedule.<Day>.is_available, schedule.<Day>.times[].from / .to
LabelSchedule
TypeSelect + per-day time windows
OptionsDeactivated, Default, Custom
Requiredschedule_type required; with Custom, every weekday needs is_available and, when available, times entries in H:i

Description: When the kiosk is on. Default follows the location's operating hours, Custom uses the per-day windows configured here, and Deactivated leaves the device on continuously. (Verified: upvendo-backend app/Enums/DeviceScheduleTypes.php lines 5-9; app/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.php lines 228 and 266-277.)


Reset Language On Order Complete

PropertyValue
Field IDreset_language_on_order_complete
LabelReset Language On Order Complete
TypeToggle
Defaultfalse

Description: Whether the kiosk resets to the default language after an order completes.


Upsell Suggestions

Upsell suggestions on the kiosk are not a device-profile toggle. They are configured separately as Upsell Groups (Marketing → Upsell Groups). Active groups for the location are served to the kiosk and suggested during ordering. See the Upsell Groups feature for setup, triggers, and display surfaces.

Note on tipping: Tip collection is not a device-profile field. Tipping is read from the location's in-house settings (collect_tips) and surfaced to the kiosk from there.


Dining Options

Each profile has two dining-option slots, dining_option_1 and dining_option_2, each a select from For Here, Takeout, or None. Setting a slot to None means customers are not asked to choose and all kiosk orders are labelled with the other option. The two slots must differ — saving the same value in both fails with "The dining options is invalid." (Verified: upvendo-backend app/Enums/DiningOptions.php lines 35-43; app/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.php lines 58, 192-193, 237-239 and 286.)

Dining Option 1

PropertyValue
Field IDdining_option_1
LabelDining Option 1
TypeSelect
OptionsFor Here, Takeout, None
RequiredYes

Dining Option 2

PropertyValue
Field IDdining_option_2
LabelDining Option 2
TypeSelect
OptionsFor Here, Takeout, None
RequiredYes

Custom Dining Option Pictures

PropertyValue
Field IDuse_custom_pictures_for_dining_options
LabelUse Custom Pictures
TypeToggle
Defaultfalse

Description: When enabled, you can upload a custom image for each non-None dining option; otherwise default plate/shop images are used.


Business Logic

Kiosk Order Flow

Customer approaches kiosk


Welcome / home screen (language selection)


Dining option: "For Here" / "Takeout"
(skipped if a slot is set to None)


Browse categories


Select item → Customize (modifiers)
             (+ party size, on a per-person item)


Add to cart → Continue or Checkout


Review order


Tips (if enabled on the location's in-house settings)


Payment:
├── Card → Terminal activated
│         Customer taps/inserts card
│         Payment processed

└── Cash → Receipt printed
          Pay at counter


Order confirmation
- Order number displayed
- Receipt printed


Order sent to KDS


Return to welcome screen

Device Registration

New kiosk device


Install kiosk app


Enter registration code (from backoffice)


Device registered and configured


Download menu and settings


Ready for customers

Offline Mode

Network disconnected


Kiosk enters offline mode:
- Cached menu still available
- Orders queued locally
- Card payments may fail


Network restored


Queued orders synced

Customer Impact

Kiosk Experience

  1. Welcome: Language selection, start order
  2. Dining: Choose "For Here" or "Takeout" (skipped if a slot is set to None)
  3. Menu: Browse categories, view items
  4. Item: See details, select modifiers
  5. Cart: Review, modify quantities
  6. Checkout: Tips (if enabled on the location), payment
  7. Confirmation: Queue/order number, receipt

Per-person items: on an item with a MplusKassa bestelbeperking the customize step also asks "For how many people" — a picker limited to the allowed party sizes, shown above the quantity stepper, which stays and still picks how many of the dish. The party size defaults to the first allowed value. Party size is part of the bag line's identity, so the same dish for a party of 3 and for a party of 2 stay as two separate lines. In the bag the +/- moves a whole party at a time and "-" at one dish removes the line. The printed kiosk receipt shows the dish count with "For N people" on its own line underneath; the label is translated in all seven kiosk locales. Full rules — including what quantity means on the resulting line — are in Menu Items → Per-Person Items. (Verified: upvendo-kiosk src/components/PanelItemDetail.vue lines 145-151, 617; src/components/OrderItemDisplay.vue lines 157-170; src/stores/transaction.ts lines 570-577 and 1560-1597.)

Accessibility

  • Large touch targets
  • Multiple languages
  • Clear visual feedback

Relations

Depends On

  • Locations: Kiosk serves a location's data
  • Device Profiles: Profile supplies the kiosk's menu(s) and settings
  • Menus: Served via the device profile (default + extra menus)
  • Payment Terminals: For card payments

Affects

  • Transactions: Orders created
  • KDS: Orders sent to kitchen
  • Inventory: Stock decremented
  • Reports: Kiosk sales tracked

Business Rules

  • A kiosk device cannot be deleted while its subscription is active — the back office returns "Device still has an active subscription. Please unsubscribe first if you haven't already and try again next month." (Verified: upvendo-backend app/Services/BackOffice/DeviceService.php lines 635-646.)
  • Activation code resend is rate-limited: after 3 resends within the cooldown window (default 15 minutes), further requests are blocked and the error reports the remaining minutes. The counter resets once the window lapses.
  • Remote-control commands only reach Kiosk-type devices; for any other device type the request succeeds but nothing is dispatched.
  • A device profile's two dining-option slots must hold different values.
  • If a completed transaction already exists for a given idempotency key, the system returns the existing order details instead of creating a duplicate.
  • Kiosk orders use an idempotency-keyed payment flow, publishing status updates (Submitting, Processing, Succeeded, Failed, Canceled) to the device in real time via Firebase.
  • Tip validation enforces that a request cannot specify both a tip percentage and a tip amount simultaneously; only one is accepted per transaction.

FAQs

  • "How does a kiosk connect to the backend?" The kiosk app is activated by entering a one-time activation code from the backoffice, which registers the device and downloads location-specific menu data and settings.

  • "Can different kiosks at the same location show different menus?" Yes. Menus live on the device profile (default menu plus scheduled extra menus), so assigning kiosks to different profiles gives them different offerings and settings.

  • "What happens if the network drops during a kiosk payment?" The system tracks payment sessions via idempotency keys; when connectivity returns, it checks session status with the payment provider and either completes or cancels the transaction accordingly.

  • "How is kiosk pricing structured?" The first kiosk at a location has a specific subscription price tier; each additional kiosk uses a separate (typically lower) per-unit price tier, both managed through Stripe.

  • "Can I remotely restart a kiosk?" Yes. The backoffice can send remote-control commands (restart, logout, firmware update, update APK) to a Kiosk device; the command is delivered over Firebase Cloud Messaging (FCM).

  • "How do I activate a new kiosk after creating it in the back office?" Create the kiosk device in Device Management, then generate an activation code. On the kiosk tablet, enter that activation code to pair the device; you can copy the code or send it by email/phone from the activation dialog.

  • "I keep getting an error when resending the activation code — why?" Activation-code resends are rate-limited. After 3 resends within the cooldown window (15 minutes by default), further resends are blocked and the back office tells you how many minutes remain.

  • "Where do I assign which menu shows on a kiosk?" Menus are set on the device profile, not the device itself. In Device Management → Profiles → edit profile → Menus, add a default menu (always available), then optionally add extra menus scheduled for specific days/times.

  • "Can two kiosks at the same location run different menus or settings?" Yes. Each kiosk is assigned a device profile, and the profile controls its menus, dining options, idle behaviour and more, so kiosks pointed at different profiles behave differently.

  • "How do I connect a card terminal to a kiosk?" Card terminals are paired per device in the device's Reader / Terminal section. Depending on your payment provider you pair a Stripe Terminal reader (enter the code shown on the reader, e.g. pheno-blue-kayak), assign a Viva Wallet terminal from the list, or generate a pairing code to enter on a Square Terminal.

  • "How do I unpair or rename a Stripe Terminal reader from a kiosk?" Open the device's Reader section and choose Edit. There you can rename the reader's label or Unpair it; unpairing removes it from Stripe and the device needs a new pairing before it can take card payments again.

  • "What's the difference between Idle Timeout and the Idle Screensaver?" They're separate settings in the profile's Idle Settings. Idle Timeout ends an in-progress session and clears entered data after inactivity (returning to the home screen), while the Idle Screensaver displays custom content on an untouched home screen until someone taps it.

  • "How do I set the idle timeout, and can I keep the default?" In Device Management → Profiles → edit profile → Idle Settings, choose Standard (warning after 45 seconds, shown for 15 seconds) or Custom, where you set your own "give warning after" and "show warning for" seconds.

  • "How do I turn on the screensaver and how soon does it appear?" In the profile's Idle Settings, enable Idle Screensaver, set the Show after value in minutes, and upload a Screensaver Image. It appears on the idle home screen after that many minutes and disappears when someone interacts with the kiosk.

  • "What dining options can customers pick at the kiosk?" Each profile has two dining-option slots configured in Dining Options, chosen from For Here, Takeout, or None. Setting an option to None means customers aren't asked to choose, and all kiosk orders are labelled with the other option.

  • "Can I schedule when a kiosk powers on and off?" Yes, in the profile's Schedule section. Choose Default to match your location's operating hours, Custom to set your own on/off times, or deactivate the schedule to keep the device on 24/7.

  • "Why does my kiosk show as Offline even though it seems on?" A kiosk is treated as Offline if it isn't activated, hasn't checked in, or hasn't been seen within the offline threshold (15 minutes by default). Check the device's network/connectivity and confirm it's powered on and connected.

  • "Can customers identify themselves at the kiosk for loyalty?" Yes, if you enable Allow customer identification in the profile's Customer Flow settings. You choose which methods are available — member code, email, or phone — and when loyalty is active, identified customers see their points and rewards.

Troubleshooting

Problem: Kiosk showing blank/no menu

Causes:

  1. No device profile assigned, or the profile has no default menu
  2. Menu has no items
  3. Network connectivity issue
  4. App needs update

Solutions:

  1. Assign a device profile and set its default menu
  2. Add items to the menu
  3. Check network connection
  4. Update kiosk app

Problem: Payment terminal not working

Causes:

  1. Terminal not paired
  2. Terminal offline
  3. Wrong terminal assigned
  4. Payment provider issue

Solutions:

  1. Pair terminal in settings
  2. Check terminal power/network
  3. Verify terminal assignment
  4. Check Stripe/Viva dashboard

Problem: Receipts not printing

Causes:

  1. Printer offline
  2. Paper out
  3. Receipt options disabled on the profile
  4. Connection issue

Solutions:

  1. Check printer power/status
  2. Refill paper
  3. Enable the print receipt option / auto-print on the device profile
  4. Check USB/network connection

Problem: Kiosk keeps resetting

Causes:

  1. Idle timeout too short
  2. App crashing
  3. Network drops

Solutions:

  1. Use the Standard idle setting, or raise the Custom "give warning after" value
  2. Check app logs, update app
  3. Improve network stability

Problem: Orders not appearing in KDS

Causes:

  1. KDS not configured
  2. Network issue
  3. Order not completing

Solutions:

  1. Set up KDS for location
  2. Check network
  3. Verify order completes (payment success)

Examples

These payloads are illustrative, not complete. A real device-profile save also requires custom_default_menu, use_pager, use_custom_pictures_for_dining_options, section_selection_enabled, schedule_type and idle_screensaver.enabled, and location_id on create. (Verified: upvendo-backend app/Http/Requests/BackOffice/DeviceProfile/StoreDeviceProfileRequest.php lines 183-229.)

Basic Kiosk Setup

json
{
  "device": {
    "name": "Kiosk 1 - Entrance",
    "location_id": "loc-123",
    "device_profile_id": "profile-main",
    "viva_wallet_terminal_id": "viva-term-1"
  },
  "profile": {
    "name": "Main Kiosk Profile",
    "location_id": "loc-123",
    "custom_default_menu": true,
    "default_menu_id": "menu-main",
    "extra_menu_ids": [],
    "dining_option_1": "For Here",
    "dining_option_2": "Takeout",
    "use_custom_pictures_for_dining_options": false,
    "idle_timeout_type": "standard",
    "idle_timeout_seconds": 45,
    "show_warning_for_seconds": 15,
    "idle_screensaver": { "enabled": false, "minutes": null },
    "menu_item_columns": 2,
    "order_number_display": "both"
  }
}

Fast Food Profile (Custom Idle, Both Numbers)

json
{
  "profile": {
    "name": "Fast Food",
    "dining_option_1": "For Here",
    "dining_option_2": "Takeout",
    "idle_timeout_type": "custom",
    "idle_timeout_seconds": 60,
    "show_warning_for_seconds": 15,
    "menu_item_columns": 3,
    "order_number_display": "both",
    "sell_without_stock": false
  }
}

Takeout-Only Profile (No "For Here")

json
{
  "profile": {
    "name": "Takeout Only",
    "dining_option_1": "Takeout",
    "dining_option_2": "None",
    "idle_timeout_type": "standard",
    "menu_item_columns": 2,
    "order_number_display": "queue_number"
  }
}

Profile with Idle Screensaver + Customer Identification

json
{
  "profile": {
    "name": "Cafe Kiosk",
    "dining_option_1": "For Here",
    "dining_option_2": "Takeout",
    "idle_timeout_type": "standard",
    "idle_screensaver": { "enabled": true, "minutes": 5 },
    "screensaver_content_id": "content-screensaver-1",
    "menu_item_columns": 2,
    "order_number_display": "order_number",
    "allow_customer_identification": true,
    "customer_identification_methods": { "member_code": true, "email": false, "phone": false }
  }
}