Wolt is a Laravel package that allows you to integrate your restaurant with the Wolt platform. It provides a simple and easy-to-use API for syncing your menu and managing orders.
- Requirements
- Installation
- Usage
- Contribution Guidelines
- Changelog
- License
- Contact Information
- Acknowledgments
- PHP 8.1 or higher
- Laravel 10.0 or higher
To install Wolt, you need to run the following command:
composer require bored-programmers/laravel-wolt
Publish the configuration file and set up your environment variables.
php artisan vendor:publish --tag=wolt-config
Update your .env
file with the following variables:
WOLT_MENU_API_USERNAME=your_menu_api_username
WOLT_MENU_API_PASSWORD=your_menu_api_password
WOLT_ORDER_API_KEY=your_order_api_key
WOLT_VENUE_ID=your_venue_id
WOLT_IS_SANDBOX=true/false
To sync your menu with Wolt, use the WoltService::syncMenu
method.
Here is an example of how you can use the DTOs to create a menu and sync it with Wolt.
use BoredProgrammers\Wolt\DTO\MenuData;
use BoredProgrammers\Wolt\DTO\CategoryData;
use BoredProgrammers\Wolt\DTO\SubcategoryData;
use BoredProgrammers\Wolt\DTO\ItemData;
use BoredProgrammers\Wolt\DTO\TranslationData;
use BoredProgrammers\Wolt\DTO\CaffeineContentData;
use BoredProgrammers\Wolt\DTO\WeeklyAvailabilityData;
use BoredProgrammers\Wolt\DTO\WeeklyVisibilityData;
use BoredProgrammers\Wolt\DTO\OptionData;
use BoredProgrammers\Wolt\DTO\SelectionRangeData;
use BoredProgrammers\Wolt\DTO\OptionValueData;
use BoredProgrammers\Wolt\DTO\SubOptionValueData;
use BoredProgrammers\Wolt\DTO\ProductInformationData;
use BoredProgrammers\Wolt\DTO\NutritionInformationData;
use BoredProgrammers\Wolt\DTO\NutritionValuesData;
use BoredProgrammers\Wolt\DTO\NutrientData;
use Spatie\LaravelData\DataCollection;
// Example Translations for Category
$categoryNameTranslation = new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Beverages']),
TranslationData::from(['lang' => 'fr', 'value' => 'Boissons'])
]);
// Example Translations for Subcategory
$subCategoryNameTranslation = new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Hot Drinks']),
TranslationData::from(['lang' => 'fr', 'value' => 'Boissons Chaudes'])
]);
// Example Translations for Item
$itemNameTranslation = new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Espresso']),
TranslationData::from(['lang' => 'fr', 'value' => 'Espresso'])
]);
$itemDescriptionTranslation = new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Rich and bold espresso coffee']),
TranslationData::from(['lang' => 'fr', 'value' => 'Café espresso riche et audacieux'])
]);
// Example Translations for Options
$optionNameTranslation = new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Milk Type']),
TranslationData::from(['lang' => 'fr', 'value' => 'Type de Lait'])
]);
$optionValueNameTranslation1 = new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Whole Milk']),
TranslationData::from(['lang' => 'fr', 'value' => 'Lait entier'])
]);
$optionValueNameTranslation2 = new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Soy Milk']),
TranslationData::from(['lang' => 'fr', 'value' => 'Lait de soja'])
]);
// Example Sub-option Value
$subOptionValueNameTranslation = new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Vanilla Syrup']),
TranslationData::from(['lang' => 'fr', 'value' => 'Sirop de vanille'])
]);
// Example Nutrition Information
$nutritionInformation = NutritionInformationData::from([
'serving_size' => 'per_100_ml',
'nutrition_values' => NutritionValuesData::from([
'energy_kcal' => NutrientData::from(['unit' => 'kcal', 'value' => 5]),
'energy_kj' => NutrientData::from(['unit' => 'kj', 'value' => 20]),
'fats' => NutrientData::from(['unit' => 'g', 'value' => 0.1]),
'saturated_fats' => NutrientData::from(['unit' => 'g', 'value' => 0.05]),
'mono_unsaturated_fats' => NutrientData::from(['unit' => 'g', 'value' => 0.01]),
'poly_unsaturated_fats' => NutrientData::from(['unit' => 'g', 'value' => 0.01]),
'carbohydrate' => NutrientData::from(['unit' => 'g', 'value' => 0.5]),
'sugar' => NutrientData::from(['unit' => 'g', 'value' => 0.1]),
'starch' => NutrientData::from(['unit' => 'g', 'value' => 0.2]),
'polyols' => NutrientData::from(['unit' => 'g', 'value' => 0.0]),
'protein' => NutrientData::from(['unit' => 'g', 'value' => 0.2]),
'salt' => NutrientData::from(['unit' => 'g', 'value' => 0.01]),
'sodium' => NutrientData::from(['unit' => 'mg', 'value' => 5]),
'fibres' => NutrientData::from(['unit' => 'g', 'value' => 0.1]),
'vitamin_c' => NutrientData::from(['unit' => 'mg', 'value' => 0.0]),
'potassium' => NutrientData::from(['unit' => 'mg', 'value' => 80]),
'calcium' => NutrientData::from(['unit' => 'mg', 'value' => 10]),
'magnesium' => NutrientData::from(['unit' => 'mg', 'value' => 2]),
'chloride' => NutrientData::from(['unit' => 'mg', 'value' => 5]),
'fluoride' => NutrientData::from(['unit' => 'mg', 'value' => 0.0])
])
]);
// Example Product Information
$productInformation = ProductInformationData::from([
'ingredients' => new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Water, Coffee Beans']),
TranslationData::from(['lang' => 'fr', 'value' => 'Eau, Grains de café'])
]),
'additives' => new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'None']),
TranslationData::from(['lang' => 'fr', 'value' => 'Aucun'])
]),
'nutrition_facts' => new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Low calories']),
TranslationData::from(['lang' => 'fr', 'value' => 'Faible en calories'])
]),
'nutrition_information' => $nutritionInformation,
'allergens' => new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'None']),
TranslationData::from(['lang' => 'fr', 'value' => 'Aucun'])
]),
'producer_information' => new DataCollection([
TranslationData::from(['lang' => 'en', 'value' => 'Local Roastery']),
TranslationData::from(['lang' => 'fr', 'value' => 'Torréfacteur local'])
])
]);
// Example Option with Sub-option Values
$optionValue1 = OptionValueData::from([
'name' => $optionValueNameTranslation1,
'selection_range' => SelectionRangeData::from(['min' => 0, 'max' => 1]),
'price' => 0.50,
'enabled' => true,
'default' => true,
'external_data' => 'option-value-1',
'sub_option_values' => new DataCollection([
SubOptionValueData::from([
'name' => $subOptionValueNameTranslation,
'selection_range' => SelectionRangeData::from(['min' => 0, 'max' => 1]),
'price' => 0.20,
'enabled' => true,
'default' => false,
'external_data' => 'sub-option-value-1'
])
])
]);
$optionValue2 = OptionValueData::from([
'name' => $optionValueNameTranslation2,
'selection_range' => SelectionRangeData::from(['min' => 0, 'max' => 1]),
'price' => 0.60,
'enabled' => true,
'default' => false,
'external_data' => 'option-value-2',
'sub_option_values' => null
]);
$option = OptionData::from([
'name' => $optionNameTranslation,
'type' => 'SingleChoice',
'selection_range' => SelectionRangeData::from(['min' => 1, 'max' => 1]),
'external_data' => 'option-1',
'values' => new DataCollection([$optionValue1, $optionValue2])
]);
// Example Item
$item = ItemData::from([
'name' => $itemNameTranslation,
'description' => $itemDescriptionTranslation,
'image_url' => 'https://example.com/espresso.jpg',
'price' => 2.99,
'sales_tax_percentage' => 0.07,
'alcohol_percentage' => null,
'caffeine_content' => CaffeineContentData::from(['serving_size' => 'per_100_ml', 'value' => 212.0]),
'weekly_availability' => new DataCollection([
WeeklyAvailabilityData::from([
'opening_day' => 'MONDAY',
'opening_time' => '08:00',
'closing_day' => 'MONDAY',
'closing_time' => '20:00'
])
]),
'weekly_visibility' => new DataCollection([
WeeklyVisibilityData::from([
'opening_day' => 'MONDAY',
'opening_time' => '08:00',
'closing_day' => 'MONDAY',
'closing_time' => '20:00'
])
]),
'enabled' => true,
'delivery_methods' => ['takeaway', 'homedelivery'],
'options' => new DataCollection([$option]),
'external_data' => 'item-espresso-001',
'product_information' => $productInformation
]);
// Example Subcategory
$subcategory = SubcategoryData::from([
'name' => $subCategoryNameTranslation,
'description' => null,
'items' => new DataCollection([$item])
]);
// Example Category
$category = CategoryData::from([
'name' => $categoryNameTranslation,
'description' => null,
'subcategories' => new DataCollection([$subcategory])
]);
// Example Menu
$menu = MenuData::from([
'currency' => 'USD',
'primary_language' => 'en',
'categories' => new DataCollection([$category])
]);
$response = WoltService::syncMenu($menuData);
To retrieve an order, use the WoltService::getOrder
method.
You will get Order ID from your webhook called by Wolt.
More about it > here: Wolt Webhook Documentation.
I recommend using
spatie/laravel-webhook-client
package for handling webhooks.
use BoredProgrammers\Wolt\WoltService;
$orderId = 'your_wolt_order_id';
$response = WoltService::getOrder($orderId);
To accept an order, use the WoltService::acceptOrder
method.
use BoredProgrammers\Wolt\WoltService;
$orderId = 'your_wolt_order_id';
$response = WoltService::acceptOrder($orderId);
To reject an order, use the WoltService::rejectOrder
method.
use BoredProgrammers\Wolt\WoltService;
$orderId = 'your_wolt_order_id';
$response = WoltService::rejectOrder($orderId);
To mark an order as ready, use the WoltService::markReadyOrder
method.
use BoredProgrammers\Wolt\WoltService;
$orderId = 'your_wolt_order_id';
$response = WoltService::markReadyOrder($orderId);
To mark an order as delivered, use the WoltService::markDeliveredOrder
method.
use BoredProgrammers\Wolt\WoltService;
$orderId = 'your_wolt_order_id';
$response = WoltService::markDeliveredOrder($orderId);
To confirm a preorder, use the WoltService::confirmPreorder
method.
use BoredProgrammers\Wolt\WoltService;
$orderId = 'your_wolt_order_id';
$response = WoltService::confirmPreorder($orderId);
We welcome contributions to Wolt. If you'd like to contribute, please fork the repository, make your changes, and submit a pull request. We have a few requirements for contributions:
- Follow the PSR-2 coding standard.
- Only use pull requests for contributions.
For a detailed history of changes, see releases on GitHub.
This project is licensed under the MIT license.
For any questions or concerns, please feel free to create a discussion on GitHub.
Created by Matěj Černý from Bored Programmers.
We would like to thank all the contributors who have helped to make Wolt a better package.