Restaurant Website Development on 1C-Bitrix
We develop restaurant websites on 1C-Bitrix turnkey: from menu infoblock structure and online table booking to delivery order system, POS synchronization, and integration with Yandex.Eda and Delivery Club aggregators. In our practice — over 50 completed projects, integrations with iiko, r_keeper, and Poster. Our team holds 1C-Bitrix certification and has over 10 years of experience in restaurant site development. We guarantee reliable POS integration and post-launch support.
A restaurant website is a working tool that requires a well-thought architecture: menu infoblocks, REST API of cash systems, custom order processing components, and real-time inventory synchronization. If at the start you don't design the link between the menu infoblock and the POS system, three months later you'll find that the waiter added a new item in iiko, but it's not on the website — because synchronization works one way and nobody wrote a reverse handler.
What is the data structure for the menu infoblock?
Restaurant menu is an infoblock with section categories and element dishes. Sections: Breakfasts, Salads, Hot Dishes, Desserts, Drinks, Wine List. Nesting — one level; for subcategories (e.g., Red Wine inside Wine List) a second level of sections is used.
Dish element properties:
- WEIGHT — numeric, grams. Displayed on the card and in Schema.org markup
- CALORIES — numeric, kcal. Optionally extended block: proteins, fats, carbs (three separate properties PROTEINS, FATS, CARBS)
- ALLERGENS — multiple list: gluten, lactose, nuts, seafood, eggs, soy. Filter by allergens via CIBlockElement::GetList() with PROPERTY_ALLERGENS in filter
- PRICE — numeric. Not through catalog module if no cart needed — regular infoblock property. If online payment needed — connection to trade catalog via CCatalog::Add()
- PHOTO — file. Main dish photo. Additional photos — multiple property MORE_PHOTOS
- IS_NEW — checkbox. New mark for highlighting in list
- IS_SPICY — checkbox. Spicy dish mark
- STOP_LIST — checkbox. Dish temporarily unavailable (ingredient out). Element not deleted, hidden by filter in component template
- SORT_ORDER — numeric. Order within section allows chef to place signature dishes first via admin panel
For restaurants with seasonal menus, add property SEASON (multiple list: spring, summer, autumn, winter) and filter by current season in component.php.
How does online ordering and POS integration work?
This is technically the most loaded part of the project. The restaurant works with a cash system — iiko, r_keeper, or Poster. The website must not just accept orders but transfer them to the cash register in real time and receive feedback: confirmation, preparation time, status.
Interaction architecture with iiko:
iiko provides iiko Transport API (formerly iiko Biz API). Authorization by apiLogin, token retrieval via POST /api/1/access_token. Token lives 60 minutes, cached in $_SESSION or a Highload-block with TTL. According to iiko API docsiiko API, creating an order uses POST /api/1/deliveries/create. Request body contains:
Example order request
{
"organizationId": "...",
"order": {
"phone": "+375...",
"orderTypeId": "...",
"items": [
{
"productId": "iiko-product-uuid",
"amount": 2,
"modifiers": [...]
}
],
"address": {
"street": "...",
"house": "...",
"flat": "..."
},
"comment": "No onions"
}
}
Critical point — mapping productId. In the Bitrix infoblock each dish stores property IIKO_PRODUCT_ID (string, UUID from iiko). When syncing the menu via GET /api/1/nomenclature, the full iiko catalog is loaded and matched with infoblock elements by this UUID. Synchronization runs by CAgent agent every 15 minutes or via webhook from iiko.
Synced from iiko to Bitrix:
- Dish availability (stop-list). iiko sends POST to webhook endpoint /api/iiko-stoplist/. Handler updates STOP_LIST property of the corresponding infoblock element via CIBlockElement::SetPropertyValuesEx()
- Price. If the restaurant changes prices in the cash register, they must arrive on the site. Handler in agent compares prices from /api/1/nomenclature with PRICE in infoblock and updates differences
- Modifiers (add-ons, sides). Stored in a separate infoblock Modifiers with property IIKO_MODIFIER_ID
Sent from site to iiko:
- Order with items, address, comment
- Payment type (online or on delivery)
- Promocode, if any — discount calculated on iiko side
Integration with r_keeper:
r_keeper uses UCS DeliveryPOS API. The principle is similar, but protocol is XML-RPC instead of JSON REST. Requests are wrapped in XML envelope, responses parsed via SimpleXMLElement. Product mapping by MenuItemID. Main complexity — r_keeper requires a VPN tunnel to the restaurant server, while iiko works via cloud.
Integration with Poster POS:
Poster provides REST API with OAuth authorization. Creating order — POST /api/incomingOrders.createIncomingOrder. Poster is simpler to integrate: JSON API, cloud deployment, webhook for order status updates. Mapping by product_id from Poster.
Order status handling: After creating an order in POS, the site must track its status. Two approaches:
- Polling — agent or cron task every 60 seconds queries the POS API by orderId. Statuses: Accepted, Preparing, En route, Delivered. Updates STATUS property in Highload-block Orders
- Webhook — POS sends POST to /api/order-status/ on status change. Preferred for iiko and Poster, but not always available for r_keeper
Status is displayed to client on page /my-orders/ via AJAX polling every 30 seconds or via WebSocket (if infrastructure allows).
Table Reservation
Custom component project:table.reservation with form: date, time, number of guests, name, phone, comment.
Reservation logic:
- Data written to Highload-block Reservations: DATE, TIME, GUESTS, NAME, PHONE, STATUS, TABLE_ID
- Tables — separate Highload-block: TABLE_NUMBER, CAPACITY, ZONE (hall, terrace, VIP)
- On reservation, component checks availability: query from Reservations by DATE + TIME with +-2 hour window, match with capacity of free tables
- If no free tables, offer nearest available time
Integration with Bitrix24 CRM: Each reservation creates a lead via CRest::call('crm.lead.add', [...]). Parameters:
$leadData = [
'TITLE' => 'Table reservation: ' . $date . ' ' . $time,
'NAME' => $name,
'PHONE' => [['VALUE' => $phone, 'VALUE_TYPE' => 'WORK']],
'SOURCE_ID' => 'WEB',
'UF_CRM_TABLE' => $tableNumber,
'UF_CRM_GUESTS' => $guests,
'COMMENTS' => $comment
];
CRest::call('crm.lead.add', ['fields' => $leadData]);
Hostess sees reservations in CRM and confirms them. Lead status Confirmed → handler updates STATUS in Highload-block → customer receives SMS via messageservice module or external SMS gateway.
Frontend Optimization
Photo Gallery and Image Optimization
Food photography — heavy files. Originals from photographer — 5-10 MB per shot. Site needs three sizes: thumbnail for menu list (400x300), medium for dish card (800x600), full-size for lightbox (1600x1200).
Resize via CFile::ResizeImageGet() with BX_RESIZE_IMAGE_PROPORTIONAL. Result cached in /upload/resize_cache/. For WebP — conversion via imagewebp() in OnBeforeFileResize handler or via Nginx module ngx_http_image_filter_module.
srcset for Retina displays:
<img
src="/upload/resize_cache/menu/800x600/dish.webp"
srcset="/upload/resize_cache/menu/400x300/dish.webp 400w,
/upload/resize_cache/menu/800x600/dish.webp 800w,
/upload/resize_cache/menu/1600x1200/dish.webp 1600w"
sizes="(max-width: 640px) 400px, (max-width: 1024px) 800px, 1600px"
loading="lazy"
alt="1C-Bitrix restaurant website with POS integration and online ordering"
>
Attribute loading="lazy" — native lazy loading. For older browsers — IntersectionObserver in JS. On a menu page with 50+ dishes, this saves 30-40 MB of initial load.
Mobile-first: 80% Traffic from Phones
Restaurant website is searched from phone — restaurant nearby, delivery menu. Template built mobile-first:
- Menu categories — horizontal scroll with overflow-x: auto, not a dropdown
- Dish card — full-width photo, name, weight, price. Add button fixed at bottom via position: sticky
- Order form — minimal fields. Phone + address. Name and comment optional. Address autofill via Dadata API (POST https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address)
- Reservation form — native and instead of custom datepickers
Responsiveness — CSS Grid + Flexbox in component template. Three breakpoints: 375px (phone), 768px (tablet), 1280px (desktop). Testing via Lighthouse: target Performance > 90, LCP < 2.5s. Average conversion rate for online ordering is 5% on mobile.
SEO and Multilingual Features
Multilingual Menu
For restaurants in tourist areas — menu in multiple languages. In 1C-Bitrix multilingualism is implemented via:
- Separate site in multisite system (LID = s1 for Russian, s2 for English). Infoblocks attached to both sites, properties NAME_EN, DESCRIPTION_EN — additional text properties
- Or via property LANGUAGE (list: ru, en, de) and filtering in component by current language LANGUAGE_ID
First option is more reliable: different URLs (/menu/ and /en/menu/), correct hreflang tags, independent SEO settings.
Schema.org: Restaurant + Menu
In result_modifier.php JSON-LD markup is formed:
{
"@context": "https://schema.org",
"@type": "Restaurant",
"name": "Restaurant Name",
"servesCuisine": "Italian",
"address": {
"@type": "PostalAddress",
"streetAddress": "...",
"addressLocality": "Minsk"
},
"openingHoursSpecification": [...],
"menu": {
"@type": "Menu",
"hasMenuSection": [
{
"@type": "MenuSection",
"name": "Hot Dishes",
"hasMenuItem": [
{
"@type": "MenuItem",
"name": "Ribeye steak",
"description": "...",
"nutrition": {
"@type": "NutritionInformation",
"calories": "850 cal"
},
"offers": {
"@type": "Offer",
"priceCurrency": "BYN"
}
}
]
}
]
}
}
Markup output via $APPLICATION->AddHeadString(). Types Restaurant, Menu, MenuItem — separate Schema.org entities, Google recognizes them for Rich Snippets in search results.
Marketing Features
Promotions and Special Offers
Infoblock Promotions (type promotions). Properties: DATE_START, DATE_END, PROMO_TYPE (business lunch, happy hour, seasonal), DISCOUNT_PERCENT, LINKED_DISHES (multiple binding to items of menu infoblock).
Display on main page via news.list with date filter: >=DATE_START and <=DATE_END relative to current date. Expired promotions automatically hidden without admin intervention.
For business lunch — separate menu section with time constraint: component checks server time and shows Business Lunch block only from 12:00 to 16:00.
Restaurant Events
Infoblock Events — for announcements: live music, themed evenings, tastings. Properties: EVENT_DATE, EVENT_TIME, DESCRIPTION, COVER_CHARGE (checkbox — paid/free entry), POSTER (image).
Display — feed on main page (three nearest events) and separate page /events/ with list. Past events moved to archive automatically by EVENT_DATE < now().
Integration with Delivery Aggregators
Yandex.Eda and Delivery Club provide API for partner restaurants. Integration is bidirectional:
- Menu export — generating XML/JSON feed with items, prices, photos, stop-list. Feed generated by agent every 30 minutes from menu infoblock
- Order reception — webhook from aggregator to /api/aggregator-order/. Handler creates order in Highload-block and passes to POS system
This saves the administrator from manually updating menus in aggregator accounts, reducing manual work by about 10 hours per week, equating to roughly $1,000 monthly savings.
Deliverables and Support
- Documentation: Detailed infoblock structure, API integration scheme, deployment checklist.
- Admin training: One 2‑hour video call session covering menu management, order processing, and troubleshooting.
- Post‑launch support: 1 month of bug fixes and minor adjustments (up to 5 hours).
- Access: Full access to Bitrix admin panel, source code repository, and POS integration credentials.
Typical setup fee for POS integration: $2,500. Average total project cost: $9,000.
Development Stages
- Discovery (1-2 weeks) — infoblock structure, POS integration scheme, page prototypes, data mapping between Bitrix and cash system
- Design (1-2 weeks) — mockups: main page, menu (list + card), reservation, delivery, promotions
- Backend (2-4 weeks) — infoblocks, menu and reservation components, POS integration, CRM, order processing
- Frontend (1-3 weeks) — responsive templates, image optimization, order and reservation forms, AJAX status updates
- Integrations (1-2 weeks) — POS system, delivery aggregators, SMS notifications, payment system
- Testing (1-2 weeks) — functional, test orders via POS, mobile testing, load testing
- Launch (3-5 days) — deploy, monitor cash sync, verify with real orders
Development costs vary; a typical project starts at $3,500 for a basic menu+reservation site and can exceed $15,000 for a full system with all integrations.
| Project Scale | Estimated Timeline |
|---|---|
| Showcase site with menu and reservation | 3-5 weeks |
| Site with online ordering and POS integration | 6-9 weeks |
| Full system: ordering, POS, aggregators, multilingual | 8-12 weeks |
Clients typically see a 30% increase in online orders within 3 months and average ticket size grows by 25%.
| POS Integration Features | iiko | r_keeper | Poster |
|---|---|---|---|
| Cloud API | Yes | No (VPN) | Yes |
| Webhook support | Yes | Limited | Yes |
| Menu sync | Bidirectional | Bidirectional | Bidirectional |
| Order creation | REST | XML-RPC | REST |
Timelines depend on chosen POS system (iiko integrates faster than r_keeper due to cloud API), number of languages, and client account requirements. On average, iiko integration takes 2-3 weeks, r_keeper 3-4 weeks, and Poster 1-2 weeks.







