Install the Orders Backend API feature
Edit on GitHubThis document describes how to install the Orders Backend API, which exposes placed sales orders over the Spryker Backend API: read them, create them, fire order-management (OMS) events on their line items, and read or add order comments.
The Orders Backend API is part of the same spryker-feature/order-experience-management package as the Recurring Orders feature. If you already installed Recurring Orders, skip to 2) Install the API Platform module.
The write side (order intake) does not reimplement checkout: it assembles a quote from the request payload and hands it to the existing checkout flow, so an API-placed order goes through the same pre-condition plugins, calculation, OMS bootstrapping, and mail as one placed on the Storefront.
Install feature core
Follow the steps below to install the Orders Backend API feature core.
Prerequisites
To start feature integration, review and install the necessary features:
| NAME | VERSION | INSTALLATION GUIDE |
|---|---|---|
| Spryker Core | 202608.0 | Install the Spryker Core feature |
| Checkout | 202608.0 | Install the Checkout feature |
| Company Account | 202608.0 | Install the Company Account feature |
| Purchasing Control | 202608.0 | Install the Purchasing Control feature |
Purchasing Control is optional. Install it if you want POST /orders to accept a budgetUuid and charge the order against a budget. Without it, budget charging is unavailable and the budget attribute is not reported on reads.
1) Install the required modules
composer require spryker-feature/order-experience-management:"^1.0.0" --update-with-dependencies
The package requires spryker/checkout, spryker/sales, spryker/oms, spryker/calculation, spryker/price-cart-connector, spryker/price-product, spryker/product-option, spryker/product-measurement-unit, spryker/product-packaging-unit, spryker/shipment, spryker/payment, spryker/cart-code, spryker/company-user, spryker/company-business-unit, and spryker/glue-application, among others, so Composer installs them automatically with the command above.
2) Install the API Platform module
The Backend API endpoints run on API Platform, which spryker-feature/order-experience-management does not require unconditionally—install it explicitly:
composer require spryker/api-platform:"^1.0.0" --with-dependencies
3) Set up database schema and transfer objects
Apply database changes and generate entity and transfer changes:
console propel:install
console transfer:generate
Make sure the following change has been applied in the database:
| DATABASE ENTITY | TYPE | EVENT |
|---|---|---|
| spy_sales_order.created_at | index | created |
Without this index, the createdAtFrom and createdAtTo filters and the createdAt sort on GET /orders scan the whole sales order table—typically the largest table in the system.
4) Generate the API resources
The resource contracts live in the module at src/SprykerFeature/OrderExperienceManagement/resources/api/backend/. Regenerate the API Platform resource classes and drop the cached OpenAPI document:
GLUE_APPLICATION=GLUE_BACKEND vendor/bin/glue api:generate
GLUE_APPLICATION=GLUE_BACKEND vendor/bin/glue cache:clear
GLUE_APPLICATION=GLUE_BACKEND vendor/bin/glue cache:warmup
Make sure src/Generated/Api/Backend/ contains OrdersResource.php, OrderTransitionsResource.php, and OrderCommentsResource.php.
api:generate alone regenerates these classes but leaves the Swagger UI at /docs on the pre-edit OpenAPI document cache—cache:clear is the step that drops it.
5) Set up behavior
Both plugin stacks below are optional and default to empty. Register them only if you installed Purchasing Control and want budget charging on API-placed orders.
Set up the order resource expander plugin
| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
|---|---|---|---|
| BudgetOrderResourceExpanderPlugin | Adds budgetUuid as a writable attribute on POST /orders and reports the resolved budget on reads. |
Purchasing Control feature | SprykerFeature\Glue\PurchasingControl\Plugin\OrderExperienceManagement |
src/Pyz/Glue/OrderExperienceManagement/OrderExperienceManagementDependencyProvider.php
<?php
namespace Pyz\Glue\OrderExperienceManagement;
use SprykerFeature\Glue\OrderExperienceManagement\OrderExperienceManagementDependencyProvider as SprykerOrderExperienceManagementDependencyProvider;
use SprykerFeature\Glue\PurchasingControl\Plugin\OrderExperienceManagement\BudgetOrderResourceExpanderPlugin;
class OrderExperienceManagementDependencyProvider extends SprykerOrderExperienceManagementDependencyProvider
{
/**
* @return array<\SprykerFeature\Glue\OrderExperienceManagement\Dependency\Plugin\OrderResourceExpanderPluginInterface>
*/
protected function getOrderResourceExpanderPlugins(): array
{
return [
new BudgetOrderResourceExpanderPlugin(), #PurchasingControlFeature
];
}
}
Set up the order intake quote expander plugin
| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
|---|---|---|---|
| BudgetOrderIntakeQuoteExpanderPlugin | Resolves budgetUuid from the intake payload onto the quote during order creation, and appends a validation issue when the budget cannot be charged. |
Purchasing Control feature | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\OrderExperienceManagement |
src/Pyz/Zed/OrderExperienceManagement/OrderExperienceManagementDependencyProvider.php
<?php
namespace Pyz\Zed\OrderExperienceManagement;
use SprykerFeature\Zed\OrderExperienceManagement\OrderExperienceManagementDependencyProvider as SprykerOrderExperienceManagementDependencyProvider;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\OrderExperienceManagement\BudgetOrderIntakeQuoteExpanderPlugin;
class OrderExperienceManagementDependencyProvider extends SprykerOrderExperienceManagementDependencyProvider
{
/**
* @return array<\SprykerFeature\Zed\OrderExperienceManagement\Dependency\Plugin\OrderIntakeQuoteExpanderPluginInterface>
*/
protected function getOrderIntakeQuoteExpanderPlugins(): array
{
return [
new BudgetOrderIntakeQuoteExpanderPlugin(), #PurchasingControlFeature
];
}
}
The DI container caches the plugin chain, so clear it after wiring either stack:
console cache:empty-all
Add a field to the order response through OrderResourceExpanderPluginInterface rather than building a new Backend API module. Pure mapping over OrderTransfer—no I/O—keeps the response fast for every caller, not only the one that needs the new field.
Install Purchasing Control, register both plugins, and assign a budget to a company business unit. Send POST /orders with budgetUuid set to that budget. Make sure the response reports the charged budget, and that the budget’s spent amount increases by the order total.
6) Exempt API-placed orders from the duplicate-checkout lock
The Storefront guards against double submission by locking a quote for the duration of checkout, keyed on the quote UUID. An intake quote has no UUID, so without an exemption the lock falls back to a guest name and email hash that collides across unrelated API orders.
src/Pyz/Zed/QuoteCheckoutConnector/QuoteCheckoutConnectorConfig.php
<?php
namespace Pyz\Zed\QuoteCheckoutConnector;
use Spryker\Zed\QuoteCheckoutConnector\QuoteCheckoutConnectorConfig as SprykerQuoteCheckoutConnectorConfig;
use SprykerFeature\Zed\OrderExperienceManagement\OrderExperienceManagementConfig;
class QuoteCheckoutConnectorConfig extends SprykerQuoteCheckoutConnectorConfig
{
/**
* @return array<string>
*/
public function getQuoteCheckoutLockExemptSources(): array
{
return [
OrderExperienceManagementConfig::SOURCE_API,
];
}
}
With the exemption in place, an intake order is locked on orderCustomReference when the caller supplies one—so a retried request is rejected as a duplicate—and is not locked at all when they do not. If your integration retries on timeout, send a stable orderCustomReference.
Send the same POST /orders request twice with the same orderCustomReference. Make sure the second request is rejected rather than placing a duplicate order.
7) Configure the fallback locale
This step is only required if your project does not ship an en_US locale.
Checkout errors are translated into the locale the request resolved. When none resolves, the module falls back to en_US.
src/Pyz/Zed/OrderExperienceManagement/OrderExperienceManagementConfig.php
<?php
namespace Pyz\Zed\OrderExperienceManagement;
use SprykerFeature\Zed\OrderExperienceManagement\OrderExperienceManagementConfig as SprykerOrderExperienceManagementConfig;
class OrderExperienceManagementConfig extends SprykerOrderExperienceManagementConfig
{
public function getFallbackLocaleName(): string
{
return 'de_DE';
}
}
Send POST /orders with a checkout validation error and no resolvable locale. Make sure the error message is translated into the configured fallback locale.
Verify
TOKEN=$(curl -s -X POST http://glue-backend.mysprykershop.com/token \
-d 'grant_type=password&[email protected]&password=change123' \
| python3 -c 'import sys,json; print(json.load(sys.stdin)["access_token"])')
curl -s "http://glue-backend.mysprykershop.com/orders?page[limit]=1" \
-H "Authorization: Bearer $TOKEN" -H 'Accept: application/vnd.api+json'
A 200 response with a data array and meta.pagination confirms the resource is wired. The generated OpenAPI document is at http://glue-backend.mysprykershop.com/docs.
To learn how to use the endpoints, see Retrieve orders.
Thank you!
For submitting the form