Skip to content

Create a Product and Complete Checkout

Create a Product and Complete Checkout ​

This guide shows a tested local end-to-end flow for Shopware development:

  1. Create a category and product with the Admin API
  2. Read the product with the Store API
  3. Add the product to a cart
  4. Register a customer in the current Store API context
  5. Place an order
  6. Handle payment if needed

It is written as a golden path for local development on http://127.0.0.1:8000. Troubleshooting tips appear below.

What to expect in local development ​

A few details matter a lot in local setups:

  • Store API requests use sw-access-key, not sw-access-token
  • On this setup, /store-api/context is called with GET
  • Store API context tokens are ephemeral and may expire during longer debugging sessions
  • register or login may return a new Sw-Context-Token
  • If the context changes, your cart may no longer contain the items you added earlier
  • Product creation requires a price in the system default currency
  • Customer registration requires real IDs such as salutationId and countryId

Because of that, the safest approach is to follow one known good sequence from start to finish.

Before you start ​

Make sure your local Shopware instance is running:

  • Storefront: http://127.0.0.1:8000
  • Administration: http://127.0.0.1:8000/admin

You also need:

  • curl
  • jq
  • An admin user, for example admin / shopware in a local default setup
  • A Store API sales channel access key

Step 1: Get an Admin API token ​

For local development, you can use the Administration password grant shortcut:

bash
ADMIN_TOKEN=$(curl -s -X POST "http://127.0.0.1:8000/api/oauth/token" \
 -H "Content-Type: application/json" \
  -d '{
 "grant_type": "password",
 "client_id": "administration",
 "scopes": "write",
 "username": "admin",
 "password": "shopware"
 }' | jq -r '.access_token')

printf '%s\n' "$ADMIN_TOKEN"

Step 2: Inspect your local API schemas ​

Use the generated schemas for two different questions:

  • OpenAPI spec: Which endpoint should I call, and what does the payload look like?
  • Entity schema: Which fields and associations exist on product, category, order, and other entities?

Download the Admin API schemas:

bash
curl -s "http://127.0.0.1:8000/api/_info/openapi3.json" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -o openapi.json

Download the entity schema as well:

bash
curl -s "http://127.0.0.1:8000/api/_info/open-api-schema.json" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -o entity-schema.json

Quick checks:

bash
ls -lh openapi.json entity-schema.json
head -n 5 openapi.json
head -n 5 entity-schema.json

You can do the same for the Store API later with /store-api/_info/openapi3.json.

Step 3: Look up the IDs you need before creating products ​

A product usually needs related IDs, such as:

  • taxId
  • salesChannelId
  • currencyId

Use Admin API search endpoints for this.

Find a tax ID ​

bash
curl -s -X POST "http://127.0.0.1:8000/api/search/tax" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
 -d '{
    "limit": 10,
    "sort": [{ "field": "name", "order": "ASC" }]
 }' | jq

Choose the tax rate you want to use — for example, the standard rate.

Find a sales channel ID and Store API access key ​

bash
curl -s -X POST "http://127.0.0.1:8000/api/search/sales-channel" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
 -d '{
    "limit": 10,
    "includes": {
      "sales_channel": ["id", "name", "accessKey"]
 }
 }' | jq

Pick the sales channel you want to test against — for example, Storefront.

Find a currency ID ​

Use the system default currency, because product writes require a price in the default currency:

bash
curl -s -X POST "http://127.0.0.1:8000/api/search/currency" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
 -d '{
    "limit": 10,
    "includes": {
      "currency": ["id", "name", "isoCode", "isSystemDefault"]
 }
 }' | jq

Save the IDs you want to use ​

bash
TAX_ID="<example string>"
SALES_CHANNEL_ID="<example string>"
CURRENCY_ID="<example string>"
STORE_API_ACCESS_KEY="<example string>"

Step 4: Create a category with the Admin API ​

bash
CATEGORY_ID=$(uuidgen | tr '[:upper:]' '[:lower:]' | tr -d '-')

curl -s -X POST "http://127.0.0.1:8000/api/category" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
 -d "{
    \"id\": \"$CATEGORY_ID\",
    \"name\": \"My Example Category\",
    \"active\": true
 }"

Verify it:

bash
curl -s -X POST "http://127.0.0.1:8000/api/search/category" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
 -d "{
    \"ids\": [\"$CATEGORY_ID\"],
    \"includes\": {
      \"category\": [\"id\", \"name\", \"active\"]
 }
 }" | jq

The ids and includes criteria are standard search-criteria features.

Step 5: Create a product with the Admin API ​

bash
PRODUCT_ID=$(uuidgen | tr '[:upper:]' '[:lower:]' | tr -d '-')
PRODUCT_NUMBER="MyExample-001"

curl -s -X POST "http://127.0.0.1:8000/api/product" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
 -d "{
    \"id\": \"$PRODUCT_ID\",
    \"name\": \"My Example Product\",
    \"productNumber\": \"$PRODUCT_NUMBER\",
    \"stock\": 10,
    \"active\": true,
    \"taxId\": \"$TAX_ID\",
    \"price\": [
 {
        \"currencyId\": \"$CURRENCY_ID\",
        \"gross\": 19.99,
        \"net\": 16.80,
        \"linked\": true
 }
 ],
    \"visibilities\": [
 {
        \"salesChannelId\": \"$SALES_CHANNEL_ID\",
        \"visibility\": 30
 }
 ],
    \"categories\": [
 { \"id\": \"$CATEGORY_ID\" }
 ]
 }"

About visibilities ​

Use visibilities to assign a product to one or more sales channels and define how visible it should be in each channel.

Each visibility entry contains:

bash
{
  "salesChannelId": "<sales-channel-id>",
  "visibility": 30
}

visibility is required because product availability is resolved per sales channel. A product is only considered available in a sales-channel context when it has a matching visibility entry for that salesChannelId.

Allowed values:

  • 10 (VISIBILITY_LINK): hide in listings and search
  • 20 (VISIBILITY_SEARCH): hide in listings
  • 30 (VISIBILITY_ALL): visible everywhere

Verify it:

bash
curl -s -X POST "http://127.0.0.1:8000/api/search/product" \
 -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
 -H "sw-inheritance: 1" \
  -d "{
    \"ids\": [\"$PRODUCT_ID\"],
    \"associations\": {
      \"categories\": {}
 },
    \"includes\": {
      \"product\": [\"id\", \"name\", \"productNumber\", \"active\", \"translated\", \"categories\"],
      \"category\": [\"id\", \"name\"]
 }
 }" | jq

The sw-inheritance header tells the API to consider parent-child inheritance when reading products and variants.

Step 6: Create a Store API context ​

The Store API uses two important headers:

  • sw-access-key: authenticates against the Store API sales channel
  • sw-context-token: identifies the shopper context

Get a fresh Store API context:

bash
curl -i "http://127.0.0.1:8000/store-api/context" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY"

Look for the Sw-Context-Token response header and store its value:

bash
STORE_CONTEXT_TOKEN="REPLACE_ME"
echo "$STORE_CONTEXT_TOKEN"

You can also extract it automatically:

bash
STORE_CONTEXT_TOKEN=$(curl -si "http://127.0.0.1:8000/store-api/context" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  | tr -d '\r' | awk -F': ' 'tolower($1)=="sw-context-token" {print $2}')

echo "$STORE_CONTEXT_TOKEN"

Step 7: Read the product with the Store API ​

Use a Store API search request with search criteria. The following example searches by term and sorts by name:

bash
curl -s -X POST "http://127.0.0.1:8000/store-api/search" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" \
 -H "Content-Type: application/json" \
  -d '{
 "term": "My Example Product",
 "limit": 5,
 "sort": [
 { "field": "name", "order": "ASC", "naturalSorting": true }
 ],
 "includes": {
 "product": ["id", "name", "translated", "calculatedPrice"]
 }
 }' | jq

The following example shows how to find the product you created in a storefront-style listing:

bash
curl -s -X POST "http://127.0.0.1:8000/store-api/search" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" \
 -H "Content-Type: application/json" \
  -d "{
    \"filter\": [
 { \"type\": \"equals\", \"field\": \"active\", \"value\": true },
 { \"type\": \"equals\", \"field\": \"productNumber\", \"value\": \"$PRODUCT_NUMBER\" }
 ],
    \"sort\": [
 { \"field\": \"name\", \"order\": \"ASC\" }
 ],
    \"page\": 1,
    \"limit\": 10,
    \"includes\": {
      \"product\": [\"id\", \"name\", \"productNumber\", \"translated\", \"calculatedPrice\"]
 }
 }" | jq

Useful search-criteria options:

  • includes restricts the response to the fields you need
  • page and limit control pagination
  • filter applies exact or nested filtering
  • term performs a weighted text search
  • sort controls ordering

If your project uses a different Store API search/listing endpoint, confirm the exact path in your local store-api/_info/openapi3.json.

Step 8: Add the product to the cart ​

Use the product ID from the previous step:

bash
curl -s -X POST "http://127.0.0.1:8000/store-api/checkout/cart/line-item" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" \
 -H "Content-Type: application/json" \
  -d "{
    \"items\": [
 {
        \"id\": \"$PRODUCT_ID\",
        \"referencedId\": \"$PRODUCT_ID\",
        \"type\": \"product\",
        \"quantity\": 1
 }
 ]
 }" | jq

Verify the cart:

bash
curl -s -X GET "http://127.0.0.1:8000/store-api/checkout/cart" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" | jq

If your local schema shows slightly different cart payload requirements, trust your local store-api OpenAPI spec.

Step 9: Prepare checkout state ​

Before placing an order, you typically need:

  • a customer in the current Store API context
  • active billing and shipping addresses
  • a shipping method
  • a payment method

You can inspect your local Store API reference in the browser at http://127.0.0.1:8000/store-api/_info/stoplightio.html, or inspect the raw schema:

bash
curl -s "http://127.0.0.1:8000/store-api/_info/openapi3.json" -o store-openapi.json
jq -r '.paths | keys[]' store-openapi.json | grep -E 'checkout|account|address|payment|shipping|order|context'

Typical relevant endpoints include:

  • /account/register
  • /account/login
  • /account/address
  • /context
  • /payment-method
  • /shipping-method
  • /checkout/cart
  • /checkout/order
  • /handle-payment

Rule of thumb:

  • Admin API: create and manage data
  • Store API: act like a shopper

Step 10: Register or log in as a customer, then place the order ​

On a typical local setup, POST /store-api/checkout/order requires a logged-in customer. If you call it with an anonymous context, Shopware returns Customer is not logged in. First, register a customer or log in within the current Store API context.

10.1 Fetch salutationId and countryId ​

For registration, fetch real IDs for:

  • salutationId from /store-api/salutation
  • countryId from /store-api/country

For example:

bash
COUNTRY_ID="019d2b446e29724fa4715c70c9c3eae1"
SALUTATION_ID="019d2b446e2573ba9a3ea2d002050cbe"

10.2 Register the customer ​

bash
curl -i -X POST "http://127.0.0.1:8000/store-api/account/register" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" \
 -H "Content-Type: application/json" \
  -d "{
    \"salutationId\": \"$SALUTATION_ID\",
    \"firstName\": \"FirstName\",
    \"lastName\": \"LastName\",
    \"email\": \"name-test@example.com\",
    \"password\": \"shopware123!\",
    \"acceptedDataProtection\": true,
    \"storefrontUrl\": \"http://127.0.0.1:8000\",
    \"billingAddress\": {
      \"firstName\": \"FirstName\",
      \"lastName\": \"LastName\",
      \"street\": \"Teststr. 1\",
      \"zipcode\": \"12345\",
      \"city\": \"Test City\",
      \"countryId\": \"$COUNTRY_ID\"
 }
 }"

Use the Sw-Context-Token value from the response for subsequent requests:

bash
STORE_CONTEXT_TOKEN="REPLACE_ME_WITH_NEW_TOKEN"

10.3 Re-add the product if the context token changed ​

After /store-api/account/register or /store-api/account/login, Shopware may return a new Sw-Context-Token. If the token changes, add the product to the cart again in that new context before calling /store-api/checkout/order.

bash
curl -s -X POST "http://127.0.0.1:8000/store-api/checkout/cart/line-item" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" \
 -H "Content-Type: application/json" \
  -d "{
    \"items\": [
 {
        \"id\": \"$PRODUCT_ID\",
        \"referencedId\": \"$PRODUCT_ID\",
        \"type\": \"product\",
        \"quantity\": 1
 }
 ]
 }" | jq

Verify that the cart is not empty:

bash
curl -s "http://127.0.0.1:8000/store-api/checkout/cart" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" | jq

10.4 Place the order ​

Once the Store API context has:

  • A logged-in customer
  • A cart with your product
  • Valid billing and shipping data

You can usually place the order directly on a local default setup. If your instance requires an explicit payment or shipping selection first, inspect /payment-method, /shipping-method, and /context.

Create the order:

bash
curl -s -X POST "http://127.0.0.1:8000/store-api/checkout/order" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" \
 -H "Content-Type: application/json" \
  -d '{
 "customerComment": "Test order from local API walkthrough"
 }' | jq

If the cart is empty, Shopware returns the message: Cart is empty.

In that case, add the product to the cart again in the current context, then retry the order request.

10.5 Handle payment if required ​

Some payment methods require an extra payment step after order creation:

bash
curl -s -X POST "http://127.0.0.1:8000/store-api/handle-payment" \
 -H "sw-access-key: $STORE_API_ACCESS_KEY" \
  -H "sw-context-token: $STORE_CONTEXT_TOKEN" \
 -H "Content-Type: application/json" \
  -d '{
 "orderId": "REPLACE_ME",
 "finishUrl": "http://127.0.0.1:8000/checkout/finish",
 "errorUrl": "http://127.0.0.1:8000/checkout/confirm"
 }' | jq

If /store-api/handle-payment returns "redirectUrl": null, the selected payment method does not require an additional redirect-based flow.

Troubleshooting ​

Schema endpoints return 500 or missing-table errors ​

Your database may not be initialized correctly. Re-run installation and setup.

Product does not show up in the Store API ​

Check all of the following:

  • The product is active
  • The product has a valid price
  • The product has visibilities for your sales channel
  • You are using the correct Store API access key
  • Your Storefront sales channel domain matches your local URL

Which headers matter most? ​

For this walkthrough:

  • Admin API: Authorization: Bearer $ADMIN_TOKEN, optionally sw-language-id, sw-version-id, sw-inheritance, sw-currency-id depending on your use case
  • Store API: sw-access-key, sw-context-token
Was this page helpful?
UnsatisfiedSatisfied
Be the first to vote!
0.0 / 5  (0 votes)