Overview

The Order service provides a set of features for managing order flow. It serves as the primary order storage system and exposes events and notifications for connected fulfillment systems.

You can use a standard REST call to perform all operations related to creating, deleting, viewing, and updating orders, as well as to perform a simple workflow on orders.

Use the Order API to:

  • Create and store orders submitted by customers.
  • Provide order history and details to customers.
  • Support editing and viewing in the seller's back office order management views.
  • Process orders through a workflow to track their fulfillment.
  • Notify connected systems about placed orders and their workflow transitions.

The Order service can be connected to back-end systems for extended order management, logistics, and fulfillment functionality. The Order service offers an up-to-date, fast, and easily accessible view on orders to customers and sellers alike.


API Reference

/{tenant}

/{tenant}/salesorders

Create and retrieve orders (as a merchant).

get

Retrieves created orders, as a merchant, for administrative purposes.

Requires scope hybris.order_read.

post

Creates an order, as a merchant, on behalf of a customer.

Requires scope hybris.order_create.

/{tenant}/salesorders/{orderId}

get

Retrieves a specific order, as a merchant.

Requires scope hybris.order_read.

put

Updates an order with the specified ID.

Requires scope hybris.order_update.

Deprecated

patch

Updates an order with the specified ID.

Requires scope hybris.order_update.

delete

Deletes an order with the specified ID.

Requires scope hybris.order_delete.

/{tenant}/salesorders/{orderId}/transitions

get

Possible status transitions for the specific order (as a merchant).

Requires scope hybris.order_update.

post

Updates the status of the specific order (as a merchant).

Requires scope hybris.order_update.

/{tenant}/orders

Create and retrieve orders (as a customer).

get

Retrieves a list of orders (as a customer).

Requires anonymous customer or scope hybris.order_view_history for logged in customers.

post

Creates an order (as a customer).

Requires scope hybris.order_post.

/{tenant}/orders/{orderId}

get

Get details about an order (as a customer).

Requires anonymous customer or scope hybris.order_view_history for logged in customers.

/{tenant}/orders/{orderId}/transitions

get

Possible status transitions for the specific order (as a customer).

Requires scope hybris.order_update_as_customer.

post

Updates the status of an order (as a customer).

Requires scope hybris.order_update_as_customer.


Events

For more information about events, see the PubSub service documentation.

The topic owner client is: hybris.product

TYPEDESCRIPTIONSCHEMAPAYLOAD EXAMPLE
order-createdOrder createdschema
{"user":"C123456789", "sessionId":"ab675c09-6ac1-46c8-87ea-8d9945306e35", "orderId":"FB3WI56", "preferredLanguage":"en-us", "shippingAddress": {..full address data..}, "billingAddress": {..full address data.., "createdBy":"seller@test.com"}}
order-status-changedOrder transition occurred (status changed)schema
{"user":"C123456789", "sessionId":"ab675c09-6ac1-46c8-87ea-8d9945306e35", "orderId":"FB3WI56", "preferredLanguage":"en-us", "orderStatus": "SHIPPED"}
order-updatedOrder modification occurred (excluding transitions)schema
{"user":"C123456789", "sessionId":"ab675c09-6ac1-46c8-87ea-8d9945306e35", "orderId":"ZZW70K7N", "preferredLanguage":"de-DE","hybrisClient":"localtest.localtestapp"}


Order Creation Details

When creating a new order, you must provide all the required data as declared by the appropriate schema. You can also add additional optional information to the request body. This section outlines the details of the structure of the order body and the information that is sent when a new order is created.

This example shows a newly created order body:

{
    "id": "orderId123456",
    "created": "2015-02-11T03:12:33.345Z",
    "status": "CREATED",
    "siteCode": "Germany",
    "entries": [
      {
        "amount": 1,
        "originalAmount": 450,
        "unitPrice": 420,
        "measurementUnit": {
          "value": 1,
          "unit": "kg"
              },
        "id": "1234546",
        "subTotalPrice": 420,
        "totalPrice": 420,
        "link": "https://api.beta.yaas.io/hybris/product/v2/products/cocoa",
        "product": {
          "description": "A cocoa you've always wanted !",
          "name": "Cocoa",
          "sku": "cocoa-product",
          "published": true,
          "images": [
            {
              "id": "media123456",
              "url": "https://api.beta.yaas.io/hybris/product/v2/media/media123456?tenantInfo"
            }
          ]
        }
      }
    ],
    "customer": {
      "id": "C1234567890",
      "title": "Dr.",
      "name": "Example Buyer",
      "firstName": "Example",
      "lastName": "Buyer",
      "email": "noreply@yaastest.com",
      "company": "hybris"
    },
    "billingAddress": {
      "contactName": "Example Buyer",
      "street": "Nymphenburger Str.",
      "streetNumber": "86",
      "city": "Munich",
      "state": "Bavaria",
      "country": "DE",
      "zipCode": "132 45",
      "contactPhone": "+49 1111 222 333"
    },
    "shippingAddress": {
      "contactName": "Joe Smith",
      "companyName": "Cargo Services Airfreight",
      "street": "Cheng Xiang Zhen Guan Tang Lu",
      "streetNumber": "1031",
      "extraLine1": "No. 2058",
      "extraLine2": "Suzhou City",
      "zipCode": "201202",
      "city": "Taicang City",
      "state": "Jiangsu Province",
      "country": "CN",
      "contactPhone": "+86 182 9349 4663"
    },
    "subTotalPrice": 420,
    "totalPrice": 420,
    "currency": "USD",
    "channel": {
        "name": "Twitter",
        "source": "https://twitter.com/hybriskiwis/status/707599225782214656"
    }
  }

Product information

Not all product properties need to be attached to the order. As a result, the order only contains product properties that are important for further processing:

Product entries in order
  • amount: Number of products with the same SKU
  • originalAmount: Optional property that contains original price of an order entry, product unit.
  • unitPrice: Effective price of an order entry - product unit -, after any possible changes of price due to special offers or reduced prices.
  • sku: Unique product identifier
  • totalPrice: Total price of the order in this entry
  • subTotalPrice: Total price of all items in this entry (only item price)
If there are multiple products in a single order, each product is a separate entry in the list, and each product includes its own set of product properties.

Optionally, you can also attach this information to an order line item (order entry):

  • measurementUnit: The unit of measurement for the order line item unit price. If the measurementUnit is provided, then this information will be shown in the order confirmation emails sent to customers and merchants. This object is optional and consists of 2 fields: value and unit:
    • value: Type: number. Required - if you used measurementUnit, then the object must have the value field defined.
    • unit: Optional. Unit for the value. We recommend the SI units and their derivates, e.g. "kg", "g", "m", etc.
  • link: Hypermedia reference to the original product provided by the Product service.
  • product: Inline product information that is a copy of the product properties taken from the Product service. However, there is no validation of any of the fields of the product schema.

This is an example of the product-related part of the order:

{
  "amount": 1,
  "originalAmount": 420,
  "unitPrice": 420,
  "measurementUnit": {
    "value": 1,
    "unit": "kg"
  },
  "id": "1234546",
  "subTotalPrice": 420,
  "totalPrice": 420,
  "link": "https://api.beta.yaas.io/hybris/product/v2/products/cocoa",
  "product": {
    "description": "A cocoa you've always wanted !",
    "name": "Cocoa",
    "sku": "cocoa-product",
    "published": true,
    "images": [
      {
        "id": "media123456",
        "url": "https://api.beta.yaas.io/hybris/product/v2/media/media123456?tenantInfo"
      }
    ]
  }
}

Product Variants

Product variants enable product customization by adding variant-specific attributes. To make your product variants available, use variant attributes in the order creation schema. You can use the following attributes:

  • id - unique identifier
  • name - variant name
  • code - unique mandatory user-defined identifier
  • images - graphic representations of the product
  • options - essential attributes that define a product variant, such as size, color, etc.
  • mixins - customized user-defined attributes that extend variant definitions and can be used for multiple variants

This is an example of the variant-related part of the order:

"variant": {
  "metadata": {
    "mixins": {
      "additionalVariantCode": "https://api.stage.yaas.io/hybris/schema/v1/kiwistest/example-schema.json"
    },
    "options": {
      "mug": "https://api.stage.yaas.io/hybris/schema/v1/kiwistest/example-mug-variant.json"
    }
  },
  "id": "438954845899094",
  "name": "Black Small YaaS Mug",
  "code": "yaas-mug-black-small",
  "images": [
    {
      "id":"12345678",
      "url": "https://api.stage.yaas.io/hybris/product/v2/media/12345678?tenantInfo"
    }
  ],
  "options": {
    "mug": {
      "color": "black",
      "size": "small"
    }
  }

You can use variants by including variant information into email notification templates. To learn how to create a product with variants, visit the Variants tutorial.

Customer information

Each order contains data that allows email notifications to be sent to the customer who created the order. This information is part of the request body and should be completed, especially for orders placed by guest customers. The customer and session information encoded in the access token submitted when creating the order is used to associate the order with a registered customer.

When the order data is updated by a seller using the PUT method, then the same applies to the name-related properties.

Several customer properties can be used during order creation:

  • id: Customer ID, which is extracted from the header and not sent in the request body
  • name: Customer name (See note below)
  • email: Customer email for order confirmation or other notifications
  • firstName: Customer's first name (See note below)
  • lastName: Customer's last name (See note below)
  • middleName: Customer's middle name (Optional)
  • title: Customer's title (Optional)
If the name property is not provided, but the firstName and lastName properties are provided, then the value of name is the string firstName lastName.

The required values must be sent in the body of the POST request:

"customer" : {
  "firstName": "John",
  "lastname": "Smith",
  "email": "noreply@yaastest.com"
}

Address information

The billing and shipping addresses are optional during order creation. If this data is submitted with an order, the schema from the address service is used. The data structure for both the billing and shipping addresses is defined by the appropriate schema.

If an address is submitted, these properties are validated to ensure that they are not empty:

  • contactName
  • city
  • street
  • zipCode
  • country
If you use the GET method to retrieve details of a specific order, then the details also include complete information for both the shipping and billing addresses.

The country field in both the shipping address and the billing address only accepts ISO2 country codes. However, for existing addresses that were previously added, two options exist:

  • Full country names that can be identified are automatically converted to ISO2 country codes.
  • If the country name could not be identified, the country defaults to US.

Example of the billing addreess:

"billingAddress": {
    "city": "Munich",
    "contactName": "Example Buyer",
    "contactPhone": "+49 1111 222 333",
    "country": "DE",
    "state": "Bavaria",
    "street": "Nymphenburger Str.",
    "streetNumber": "86",
    "zipCode": "132 45"
}

Payment information

Payment information is an integral part of the order. It adds this information to the order:

  • status: Payment status, such as success, pending, or failure
  • method: The method of payment, such as Stripe or PayPal
  • paymentResponse: Response string from the payment processor
  • paidAmount: The amount paid given as a number, such as 2023
  • currency: Currency used for the payment given as a three-letter code, such as EUR

The payment information is structured as a list. This is useful for splitting payments between multiple credit cards or keeping records of failed payments.

Tax Information

Tax information is a crucial part of the whole order data. The tax information consists of:

  • lines: An array of tax data that serves as the basis to calculate taxes.
  • total: An actual tax amount to be charged.

It is not required to specify the tax attribute for an order. However, if the tax attribute is added in the order, then you must also specify the total attribute as well. The lines attribute is not required.

Tax lines have required and non-required properties:

  • amount: Required.
  • currency: Required.
  • sequenceId: Required. A sequence number that is used to provide support ordering of tax records.
  • inclusive: Required. A flag that determines if the tax is already included in the line price. The default value is false.
  • code: Optional tax code, for example VAT_DE_REGULAR.
  • name: Optional tax name, for example Mehrwertsteuer 19%.
  • rate: Optional tax rate in a number format, for example 0.19 for German VAT.
  • taxable: It is a sum of subtotals of amount considered for tax.

The tax total has the following required properties:

  • amount
  • currency
  • inclusive

Tax lines and tax total can be surfaced using:

  • Orders
  • Sales orders
  • Order details

You can add tax information on the order level. Here is an example of how the tax information might look. This is the most extensive tax information that can be added in the order. The lines attribute is not required, but it is added here to provide an overview of all possible tax attributes.

"tax": {
  "total": {
      "amount": 150.00,
      "currency": "USD"
    }
    ,
  "lines": [ {
      "amount": 150.00,
      "currency": "USD",
      "code": "VAT",
      "name": "Regular VAT",
      "rate": 10,
      "taxable": 1500.00,
      "sequenceId": 1,
      "inclusive": false
    }
  ]
}

Orders can have entries, which hold information related to that particular order component. As each product or other ordered goods can be a subject of taxation, the tax information can also be added on the order entry level. The structure of the tax information is the same as in the example above.

Order origin

An optional property – channel - can be found in an order's details. If present, it brings the information where the order originates from. The channel consists of two simple properties:

  • name: A simple string denoting the origin of an order, for example Twitter
  • source: A string URL that points to the original order location, for example a link to a particular tweet
"channel": {
    "name": "Twitter",
    "source": "https://twitter.com/hybriskiwis/status/707599225782214656"
  }


Email Format and Data

Email Format and Data

The shape and content of emails are defined by email templates. You can have a default email template for generating order confirmation emails for each tenant.

The email templates are fully customizable and can be configured to provide access to as much of the order details as possible.

The Order service uses the Email service to send emails, so the email templates make use of the Velocity Template Language. For more details on how to use Velocity templates, see Using Velocity Templates in the Email service documentation.

The default template uses several macros to shorten the template code:

  • #format_amount() is a formatter for a currency amount in the format ###,###,##0.00.
#* must be in a single line to avoid an unwanted trailing space *#
#macro(format_amount $num)$!{tools.number.format('###,###,##0.00', $num, $tools.date.locale)}#end
  • #esc_with_line_break() inserts a line break at the end if the argument string is not empty.
#* Escapes string, and if not null, also inserts a line break after the escaped string *#
#macro(esc_with_line_break $arg)
    #if("$!arg.trim()" != "")
        $!{tools.esc.html($arg)} <br>
    #end
#end

Order Confirmation Email Data

When the status changes to CREATED, the buyer receives an Order Confirmation email which confirms the order creation.

Variables

The Order service provides several variables that can be used in the order confirmation email template.

Order information

The following variables can carry the order information in the order confirmation email.

VariableDescription
(${d.order_id})Order ID
(${d.order.created})Timestamp of order creation
${d.order.entries}Collection of order entries (iterable)
(${d.order.shippingCost})Shipping cost of the order
(${d.order.tax})Total tax of the order
(${d.order.currency})Currency, for example, USD, EUR, GBP
(${d.order.subTotalPrice})Total price of the ordered items
(${d.order.totalPrice})Total price of the order
${d.order.mixins}Map of mixins defined for the order
${d.order.mixins.NAME}Mixin with a name NAME defined for the order

Shipping information

The following variables can carry the shipping information in the order confirmation email.

VariableDescription
(${d.order.shipping.total.amount})Total shipping cost of an order
(${d.order.shipping.total.currency})Currency

Each discount

VariableDescription
(${discount.code})Discount code
(${discount.amount})Discount amount
(${discount.currency})Discount currency
(${discount.name})Discount name
(${discount.discountRate})Discount rate

Each order entry - an order line

The following is an example of order entries:

#foreach($entry in ${d.order.entries})
  <!-- BEGIN: ORDER_ENTRY, PRODUCT, VARIANT -->
  $entry.product.name
  <!-- END: ORDER_ENTRY, PRODUCT, VARIANT -->
#end

For more information about Velocity and looping, visit the Apache Velocity guide.

VariableDescription
($entry.unitPrice)Unit price
($entry.amount)Quantity
($entry.totalPrice)Total price for the order line
($entry.tax)Line tax
($entry.product.id)Product ID
($entry.product.sku)Product SKU
($entry.product.name)Product name
($entry.product.description)Product description
${entry.product.images.get(0).url}Gets the URL of the first image of the product
${entry.variant}Product variant
${entry.variant.id}Product varliant ID
${entry.variant.name}Product variant name
${entry.variant.options }Product variant attributes
${entry.mixins}Map of mixins defined for the order entry
${entry.mixins.NAME}Mixin with a name NAME defined for the order entry

If there are additional attributes in the product mixins, they can be accessed as ($entry.product.mixins.NAME.FIELD). To do so, replace:

  • NAME with the name of mixin
  • FIELD with key of the field

For example, if you have the following mixin in a product:

"product":{
   ...
   "mixins":{
      "inventory":{
         "inStock":true
      }
   }
   ...
}

Then the following statement can be used to access the value of the inStock attribute:

($entry.product.mixins.inventory.inStock)

In general, you can access any other mixin in the order payload in the same way, such as mixins for extending billing or shipping address, customer data, order entry mixins, and so on.

Customer information

The following variables can carry the customer information in the order confirmation email.

VariableDescription
(${d.order.customer.id})Customer ID
(${d.order.customer.email})Customer email
(${d.order.customer.title})Customer title, such as Mr., Mrs. etc.
(${d.order.customer.name})Customer's full name
(${d.order.customer.firstName})First name of a customer
(${d.order.customer.middleName})Middle name of a customer
(${d.order.customer.lastName})Last name of a customer
(${d.order.customer.company})Customer's company


You can make use of the above data in your order confirmation email templates. You can also use additional information based on the mixins-based attributes assigned to your customer object.

To do this, you can use the following example and modify it to suit your needs, according to your actual mixin contents:

#if($!{d.order.customer.mixins} && $!{d.order.customer.mixins.secondaryContactPhone})      ($!{d.order.customer.mixins.secondaryContactPhone.number})     #end

This example assumes that the secondaryContactPhone attribute is included in the mixin, but you can have any other attribute in your mixins, depending on your particular requirements.

Addresses information

The following variables can carry the address information in the order confirmation email, separately for billing and for shipping address.

Billing address
VariableDescription
(${d.order.billingAddress.contactName})Name for billing purposes
(${d.order.billingAddress.companyName})Company name for billing purposes
(${d.order.billingAddress.street})Billing address: street
(${d.order.billingAddress.streetNumber})Billing address: house number
(${d.order.billingAddress.streetAppendix})Billing address: street appendix
(${d.order.billingAddress.zipCode})Billing address: ZIP Code
(${d.order.billingAddress.city})Billing address: city
(${d.order.billingAddress.country})Billing address: country
(${d.order.billingAddress.state})Billing address: state
(${d.order.billingAddress.contactPhone})Billing address: contact phone number
${d.order.billingAddress.mixins}Map of mixins defined for the billing address
${d.order.billingAddress.mixins.NAME}Mixin with a name NAME defined for the billing address
Shipping address
VariableDescription
(${d.order.shippingAddress.contactName})Shipping address: contact name
(${d.order.shippingAddress.companyName})Shipping address: company name
(${d.order.shippingAddress.street})Shipping address: street
(${d.order.shippingAddress.streetNumber})Shipping address: house number
(${d.order.shippingAddress.streetAppendix})Shipping address: street appendix
(${d.order.shippingAddress.zipCode})Shipping address: ZIP Code
(${d.order.shippingAddress.city})Shipping address: city
(${d.order.shippingAddress.country})Shipping address: country
(${d.order.shippingAddress.state})Shipping address: state
(${d.order.shippingAddress.contactPhone})Shipping address: contact phone
${d.order.shippingAddress.mixins}Map of mixins defined for the shipping address
${d.order.shippingAddress.mixins.NAME}Mixin with a name NAME defined for the shipping address

Order Shipped Email Data

When you, as a merchant, add the shipment information to the order, and when the order status changes from CONFIRMED to SHIPPED, the buyer receives an Order Shipped notification email. The email can contain such data as the expected delivery date, a shipment address, item name, quantity, tax information, and the price.

The following additional variables are applicable:

VariableDescription
(${d.order.lastStatusChange})Date and time when the order status was changed to SHIPPED
(${d.order.shipments})Shipment(s) for the order
(${d.order.shipments.get(0).carrier})Shipment carrier, such as UPS or DHL
(${d.order.shipments.get(0).trackingNumber})The tracking number of the shipment

Order Canceled Email Data

When either the merchant or the buyer changes the order status to DECLINED, it triggers an Order Canceled notification email. The email can contain such data as the date and time of order placement, item name, quantity, tax information, and the price.

The following additional variable is applicable:

VariableDescription
(${d.order.lastStatusChange})Date and time when the order status was changed to DECLINED (once the order has been canceled)


Sites and Orders

Orders can be placed in a specific site. As a merchant, you can check in which site the orders have been placed. Your customers can also check in which site they placed their orders.

Let's start with some important remarks related to this feature and functionality:

  • The order payload can contain a siteCode code. This is the identifier of the site where the order is created.
    • Alternatively, the site identifier can be provided in the hybris-site header while creating an order.
  • Do not add the site identifier both in the siteCode in payload and in the hybris-site header.
  • If no site identifier is given, then no site information is stored with the order.
  • If the default code is used for a site, then no site information is stored with the order. The siteCode should be given explicitly, as the default value will be ignored.
  • The siteCode code is returned with both /orders and /salesorders endpoints.
  • You can filter by siteCode code using both /orders and /salesorders endpoints.
  • If the order history (GET /orders) is retrieved by the customer and the hybris-site header is submitted, the orders are automatically filtered by that code.

Merchant scenario example

As a merchant, you can POST an order of a specific customer to the site, which can be defined in the siteCode property of the order. After the order has been placed in a particular site, this information can be easily retrieved.

The example consists of two parts:

  • Create an order with a siteCode property (steps 1 and 2).
  • Retrieve the order and check if the siteCode information is available (steps 3 and 4).
  1. To do so, use:
    • Method: POST
    • Request URI: https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders
    • Headers
      • hybris-site: The header contains information about the site. Note: if you add the siteCode code in the order payload, this header is not required and can be omitted.
      • Authorization: You must provide a proper scope that enables users to perform these operations. These scopes should be granted in an access token from OAuth 2.0 service. For more information about the authorization and authentication used in hybris services, see the Authorization section.
    • Scope: hybris.order_create
  2. After you have posted your order, the return message contains the order ID in the payload:
    {
     "ID": "I2OXSGZZ"
    }
    
  3. Now, you can use the order id to retrieve the order and check the returned payload. Observe the change of the scope, which is required to retrieve the orders.
    • Method: GET
    • Request URI: https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders/I2OXSGZZ
    • Headers
      • hybris-site: This header's value is the site code.
      • Authorization: You must provide a proper scope that enables users to perform these operations. These scopes should be granted in an access token from OAuth 2.0 service. For more information about the authorization and authentication used in hybris services, see the Authorization section.
    • Scope: hybris.order_read
  4. Check the payload. The siteCode property in the returned payload includes the information about which site the order was placed on.

Customer scenario example

Following is an example of how it works for a customer. As a customer, you also can retrieve the information about which site the order was placed on.

Observe the change of the endpoint and the scope, which is required to retrieve the orders by a customer.

  • Method: GET
    • Request URI: https://api.beta.yaas.io/hybris/order/v1/{tenant}/orders
    • Headers
      • hybris-site: The value of this header defines which site information will be retrieved.
      • Authorization: You must provide a proper scope that enables users to perform these operations. These scopes should be granted in an access token from OAuth 2.0 service. For more information about the authorization and authentication used in hybris services, see the Authorization section.
    • Scope: hybris.order_view_history


Scopes in Order Service

Scopes let you specify exactly what type of permissions you need to access resources and operations in the Order service.

The table presents the scopes supported by the Order service.

ScopeDescription
hybris.order_postUse this scope to create a new order.
hybris.order_view_historyThis scope is granted to customers when they log in. They use this scope to view their own order history.
hybris.order_createAs a seller, use this scope to create an order on behalf of a customer.
hybris.order_readAs a seller, use this scope to view orders and order details.
hybris.order_updateAs a seller, use this scope to edit order information and manage fulfillment workflow and consignment.
hybris.order_deleteAs a seller, use this scope to delete orders.

For more information about the authorization and authentication used in SAP Hybris services, and also about the scopes in general, see:


Order Creation Details

When creating a new order, you must provide all the required data as declared by the appropriate schema. You can also add additional optional information to the request body. This section outlines the details of the structure of the order body and the information that is sent when a new order is created.

This example shows a newly created order body:

{
    "id": "orderId123456",
    "created": "2015-02-11T03:12:33.345Z",
    "status": "CREATED",
    "siteCode": "Germany",
    "entries": [
      {
        "amount": 1,
        "originalAmount": 450,
        "unitPrice": 420,
        "measurementUnit": {
          "value": 1,
          "unit": "kg"
              },
        "id": "1234546",
        "subTotalPrice": 420,
        "totalPrice": 420,
        "link": "https://api.beta.yaas.io/hybris/product/v2/products/cocoa",
        "product": {
          "description": "A cocoa you've always wanted !",
          "name": "Cocoa",
          "sku": "cocoa-product",
          "published": true,
          "images": [
            {
              "id": "media123456",
              "url": "https://api.beta.yaas.io/hybris/product/v2/media/media123456?tenantInfo"
            }
          ]
        }
      }
    ],
    "customer": {
      "id": "C1234567890",
      "title": "Dr.",
      "name": "Example Buyer",
      "firstName": "Example",
      "lastName": "Buyer",
      "email": "noreply@yaastest.com",
      "company": "hybris"
    },
    "billingAddress": {
      "contactName": "Example Buyer",
      "street": "Nymphenburger Str.",
      "streetNumber": "86",
      "city": "Munich",
      "state": "Bavaria",
      "country": "DE",
      "zipCode": "132 45",
      "contactPhone": "+49 1111 222 333"
    },
    "shippingAddress": {
      "contactName": "Joe Smith",
      "companyName": "Cargo Services Airfreight",
      "street": "Cheng Xiang Zhen Guan Tang Lu",
      "streetNumber": "1031",
      "extraLine1": "No. 2058",
      "extraLine2": "Suzhou City",
      "zipCode": "201202",
      "city": "Taicang City",
      "state": "Jiangsu Province",
      "country": "CN",
      "contactPhone": "+86 182 9349 4663"
    },
    "subTotalPrice": 420,
    "totalPrice": 420,
    "currency": "USD",
    "channel": {
        "name": "Twitter",
        "source": "https://twitter.com/hybriskiwis/status/707599225782214656"
    }
  }

Product information

Not all product properties need to be attached to the order. As a result, the order only contains product properties that are important for further processing:

Product entries in order
  • amount: Number of products with the same SKU
  • originalAmount: Optional property that contains original price of an order entry, product unit.
  • unitPrice: Effective price of an order entry - product unit -, after any possible changes of price due to special offers or reduced prices.
  • sku: Unique product identifier
  • totalPrice: Total price of the order in this entry
  • subTotalPrice: Total price of all items in this entry (only item price)
If there are multiple products in a single order, each product is a separate entry in the list, and each product includes its own set of product properties.

Optionally, you can also attach this information to an order line item (order entry):

  • measurementUnit: The unit of measurement for the order line item unit price. If the measurementUnit is provided, then this information will be shown in the order confirmation emails sent to customers and merchants. This object is optional and consists of 2 fields: value and unit:
    • value: Type: number. Required - if you used measurementUnit, then the object must have the value field defined.
    • unit: Optional. Unit for the value. We recommend the SI units and their derivates, e.g. "kg", "g", "m", etc.
  • link: Hypermedia reference to the original product provided by the Product service.
  • product: Inline product information that is a copy of the product properties taken from the Product service. However, there is no validation of any of the fields of the product schema.

This is an example of the product-related part of the order:

{
  "amount": 1,
  "originalAmount": 420,
  "unitPrice": 420,
  "measurementUnit": {
    "value": 1,
    "unit": "kg"
  },
  "id": "1234546",
  "subTotalPrice": 420,
  "totalPrice": 420,
  "link": "https://api.beta.yaas.io/hybris/product/v2/products/cocoa",
  "product": {
    "description": "A cocoa you've always wanted !",
    "name": "Cocoa",
    "sku": "cocoa-product",
    "published": true,
    "images": [
      {
        "id": "media123456",
        "url": "https://api.beta.yaas.io/hybris/product/v2/media/media123456?tenantInfo"
      }
    ]
  }
}

Customer information

Each order contains data that allows email notifications to be sent to the customer who created the order. This information is part of the request body and should be completed, especially for orders placed by guest customers. The customer and session information encoded in the access token submitted when creating the order is used to associate the order with a registered customer.

When the order data is updated by a seller using the PUT method, then the same applies to the name-related properties.

Several customer properties can be used during order creation:

  • id: Customer ID, which is extracted from the header and not sent in the request body
  • name: Customer name (See note below)
  • email: Customer email for order confirmation or other notifications
  • firstName: Customer's first name (See note below)
  • lastName: Customer's last name (See note below)
  • middleName: Customer's middle name (Optional)
  • title: Customer's title (Optional)
If the name property is not provided, but the firstName and lastName properties are provided, then the value of name is the string firstName lastName.

The required values must be sent in the body of the POST request:

"customer" : {
  "firstName": "John",
  "lastname": "Smith",
  "email": "noreply@yaastest.com"
}

Address information

The billing and shipping addresses are optional during order creation. If this data is submitted with an order, the schema from the address service is used. The data structure for both the billing and shipping addresses is defined by the appropriate schema.

If an address is submitted, these properties are validated to ensure that they are not empty:

  • contactName
  • city
  • street
  • zipCode
  • country
If you use the GET method to retrieve details of a specific order, then the details also include complete information for both the shipping and billing addresses.

The country field in both the shipping address and the billing address only accepts ISO2 country codes. However, for existing addresses that were previously added, two options exist:

  • Full country names that can be identified are automatically converted to ISO2 country codes.
  • If the country name could not be identified, the country defaults to US.

Example of the billing addreess:

"billingAddress": {
    "city": "Munich",
    "contactName": "Example Buyer",
    "contactPhone": "+49 1111 222 333",
    "country": "DE",
    "state": "Bavaria",
    "street": "Nymphenburger Str.",
    "streetNumber": "86",
    "zipCode": "132 45"
}

Payment information

Payment information is an integral part of the order. It adds this information to the order:

  • status: Payment status, such as success, pending, or failure
  • method: The method of payment, such as Stripe or PayPal
  • paymentResponse: Response string from the payment processor
  • paidAmount: The amount paid given as a number, such as 2023
  • currency: Currency used for the payment given as a three-letter code, such as EUR

The payment information is structured as a list. This is useful for splitting payments between multiple credit cards or keeping records of failed payments.

Tax Information

Tax information is a crucial part of the whole order data. The tax information consists of:

  • lines: An array of tax data that serves as the basis to calculate taxes.
  • total: An actual tax amount to be charged.

It is not required to specify the tax attribute for an order. However, if the tax attribute is added in the order, then you must also specify the total attribute as well. The lines attribute is not required.

Tax lines have required and non-required properties:

  • amount: Required.
  • currency: Required.
  • sequenceId: Required. A sequence number that is used to provide support ordering of tax records.
  • inclusive: Required. A flag that determines if the tax is already included in the line price. The default value is false.
  • code: Optional tax code, for example VAT_DE_REGULAR.
  • name: Optional tax name, for example Mehrwertsteuer 19%.
  • rate: Optional tax rate in a number format, for example 0.19 for German VAT.
  • taxable: It is a sum of subtotals of amount considered for tax.

The tax total has the following required properties:

  • amount
  • currency
  • inclusive

Tax lines and tax total can be surfaced using:

  • Orders
  • Sales orders
  • Order details

You can add tax information on the order level. Here is an example of how the tax information might look. This is the most extensive tax information that can be added in the order. The lines attribute is not required, but it is added here to provide an overview of all possible tax attributes.

"tax": {
  "total": {
      "amount": 150.00,
      "currency": "USD"
    }
    ,
  "lines": [ {
      "amount": 150.00,
      "currency": "USD",
      "code": "VAT",
      "name": "Regular VAT",
      "rate": 10,
      "taxable": 1500.00,
      "sequenceId": 1,
      "inclusive": false
    }
  ]
}

Orders can have entries, which hold information related to that particular order component. As each product or other ordered goods can be a subject of taxation, the tax information can also be added on the order entry level. The structure of the tax information is the same as in the example above.

Order origin

An optional property – channel - can be found in an order's details. If present, it brings the information where the order originates from. The channel consists of two simple properties:

  • name: A simple string denoting the origin of an order, for example Twitter
  • source: A string URL that points to the original order location, for example a link to a particular tweet
"channel": {
    "name": "Twitter",
    "source": "https://twitter.com/hybriskiwis/status/707599225782214656"
  }


Orders and Salesorders

Both orders created by customers and salesorders created by the merchants land in the same order pool. Each new order is given a unique id, which can be used later to retrieve the order details.

There is no difference between orders and salesorders, except that they are created with help of different endpoints by different kind of users: customers and merchants.

  • Customers use the https://api.beta.yaas.io/hybris/order/v1/{tenant}/orders endpoint to create or retrieve their own orders.
  • Sellers use the https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders endpoint to manage all orders, which in this case are called sales orders.

Customer orders

  • Customers can only retrieve their own orders. They cannot see any orders of other customers.
  • Anonymous customers can retrieve only their own orders created within the same session.
  • Authorized customers can retrieve any of their own orders or they can get all of their own orders.
  • The response body can contain one or many orders.

For more information orders, see the Basics - Orders interactive tutorial.

Merchants salesorders

  • Merchants have full control over the orders. They can create, update, retrieve, and delete any order.
  • While customers can only retrieve their own orders, sellers can retrieve all orders that exist in the tenant.
  • Merchant can create an order on behalf of a customer. For example, if a customer sends you an order by email, you can create the order for your customer based on the email data. This is also useful in call center situations, as agents can create orders for customers that call in their orders.
    • The customer's unique identifier is provided in the sales order payload. It is the id of the customer on whose behalf the order is created.
  • The payload schema for creating a sales order is identical to the schema for the customer order, except that:
    • The customer identifier can be submitted with the payload or it can be left empty for non-registered users.
    • The createdBy field is only returned with sales orders by using a GET method on https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders. The createdBy field contains the email address of the user that is logged into the Builder.

For more information sales orders, see the Basics - Salesorders interactive tutorial.


Order data - optional vs required

Upon the order creation some of the payload fields are optional, while other are required. For example, to create a new order, you always must provide:

  • entries: List of products that are part of the current order
  • customer: Buyer's name on the order
  • totalPrice: Total price of the entire order

For more information about order payload fields, see the Order Creation Details section.


Email Notifications for Orders

The Order service enables you to send email notifications to merchants regarding customer orders and to send order confirmation emails to customers.

As soon as an order is successfully created, both merchants and customers can be notified of the order. There are separate email templates for both. If the template is not installed yet, it will be installed when the email is about to be sent.

This image demonstrates a generic flow of how notifications work in the context of orders and other services:
Order Process and Email Notification

When an order is created, an email is sent to the order creator. You can define the form of these emails by using the email template feature.

Email notifications for sellers

Sellers can use the Administrator user interface to set up their email in the Configuration service. They can create new setups or update existing ones.

Create email notifications

  • Method: POST
  • Resource: https://api.beta.yaas.io/hybris/configuration/v1/{tenant}/configurations
  • Header: For example: tenant: <seller's tenantId>
  • Body: { "key": "order.notification.merchant.email.to", "value": "noreply@myproject.mail.yaas.io" }

Update email notifications

  • Method: PUT
  • Resource: https://api.beta.yaas.io/hybris/configuration/v1/{tenant}/configurations/order.notification.merchant.email.to
  • Header: For example: tenant: <seller's tenantId>
  • Body: { "value": "noreply@myproject.mail.yaas.io" }

Email notifications for customers

Customers do not need to set up anything to receive email notifications. These are set up automatically based on the buyer id. The customer id must be provided in the request header when creating new orders.

The order confirmation email is automatically sent to each customer that successfully places an order.

Set up the sender's email address for email sent to customers

Merchants can set the sender's email address on the emails that are sent to customers using the notification.email.from configuration property. For more information about customizing domains and the restrictions, see Custom SMTP Server Integration in the Email service documentation.

Create the sender's email address

  • Method: POST
  • Resource: https://api.beta.yaas.io/hybris/configuration/v1/{tenant}/configurations
  • Header: For example: tenant: <seller's tenantId>
  • Body: { "key": "order.notification.merchant.email.to", "value": "noreply@myproject.mail.yaas.io" }

Update the sender's email address

  • Method: PUT
  • Resource: https://api.beta.yaas.io/hybris/configuration/v1/{tenant}/configurations/order.notification.merchant.email.to
  • Header: For example: tenant: <seller's tenantId>
  • Body: { "value": "noreply@myproject.mail.yaas.io" }

Email confirmation settings

Merchants can enable or disable email notifications for their customers when an order is placed. All other order-related notifications can be enabled or disabled, as well.

  • To disable email notifications, set the order.notification.email.disabled configuration property to true.
  • To enable email notifications, one the following two options applies:
    • Set the order.notification.email.disabled configuration property to false.
    • Remove the order.notification.email.disabled configuration property.


Extending Order Information

About order mixins

Each order contains a set of properties that uniquely identify it for the external world. For example, the order id identifies each particular order instance. The status property can tell us if the order is confirmed, declined, shipped, or has any other status.

However, these properties, defined by the order schema, are few. The order mixins can help you to define set of properties once and reuse them in many different orders. You could assign one mixin to many orders, but you also could use many mixins in one order. This approach supports flexibility, re-usability, and structural consistency among all your orders.

Mixins on the order details level

Generally speaking, you can associate mixins with orders. Having mixins associations in mind, however, it is also possible to descend into the order details. For example, each order can have many order entries and you can associate mixins with each order entry that is present in the order. Such associations work the same way as those on the order level.

Also, there are attributes like billingAddress and shippingAddress, that can be associated with mixins as well, thus giving you a chance to extend the address information with mixin-based custom attributes. Again, such associations work the same way as those on the order level.

Mixins on the customer details level

Orders include customer data. Similar to the order details data, you can associate mixins data with a customer data object. Your checkout application, which is responsible for handling orders from your customers, can create orders to contain customer-level mixins. To make it possible, the schema of a mixin should be specified in the metadata attribute. For example:

"metadata": {
   "mixins": {
     "secondaryContactPhone": "https://api.beta.yaas.io/hybris/schema/v1/kiwistest/contact-number.json"
   }
 },
"mixins": {
 "secondaryContactPhone": {
       "number": "+86 334 0033 9934"
     }
}

Later, you can make use of this data in your order confirmation email templates.

Mixin schema

Mixins define the set of properties you can add in your order. The mixins itself are supported by the Schema service and are always referred from Order service by URLs.

The mixin schema is used to validate the contents of the mixin and is also a unique namespace identifier for the mixin. When the schema is specified, you can create a mixin by adding mixins.{name} attribute to an order.

Following is an example of the color mixin structure:

  • Schema Link: In this example, it could be https://api.beta.yaas.io/hybris/schema/v1/kiwistest/{tenant}/color
  • Schema Structure:
    {
    "$schema": "http://json-schema.org/draft-04/schema#",
    "description": "Color Schema",
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "red": {
        "type": "number"
      },
      "green": {
        "type": "number"
      },
     "blue": {
        "type": "number"
      }
    },
    "required": ["red", "green", "blue"]
    }
    

Now that you have the mixin in the Schema service, you can use it to extend any kind of order with additional properties. These properties are not part of the order schema, they are part of the mixin schema.

Mixin data

When an order is created, or updated with new information, these mixin-based properties can be added to the whole order configuration. In other words, it is possible to use mixins to associate additional data with orders.

Each mixin is associated with a JSON schema. The link to the schema is put into an attribute, which in the next example is additionalCode. To include a mixin with a name additionalCode, the metadata.mixins.additionalCode attribute must be defined and provide a link to the schema for the mixin.

Following is an example:

{
  ...,
  "metadata": {
    "mixins": {
      "additionalCode": "https://api.beta.yaas.io/hybris/schema/v1/kiwistest/example-schema.json"
    }
  },
  "mixins": {
    "additionalCode": {
      "code": "DFG-334-98330-X"
    }
  },
  ...
}

The Order service assumes that the schema content is immutable. If the schema referenced by a URL changes, you can experience some unpredictable results, for example existing orders could fail to validate.

The service validates that the mixin data adheres to the schema. If the mixin does not validate, or there is no schema declaration for a mixin, a validation error message would be returned: HTTP status code 400. If this happens, the response body would contain more details.


Order Confirmation Email Data

The shape and content of emails are defined by email templates. You can have a default email template for generating order confirmation emails for each tenant.

The email templates are fully customizable and can be configured to provide access to as much of the order details as possible.

The Order service uses the Email service to send emails, so the email templates make use of the Velocity Template Language. For more details on how to use Velocity templates, see Using Velocity Templates in the Email service documentation.

The default template uses several macros to shorten the template code:

  • #e() is shorthand for ${tools.esc.html() to escape HTML in a string. This should almost always be used. For more information, see Secure Your Templates in the Email service documentation.
  • #format_amount() is a formatter for a currency amount in the format ###,###,##0.00.
  • #esc_with_line_break() is similar to #e(), but it also inserts a line break at the end if the argument string is not empty.

Variables

The Order service provides several variables that can be used in the order confirmation email template.

Order information

The following variables can carry the order information in the order confirmation email.

VariableDescription
#e(${d.order_id})Order ID
#e(${d.order.created})Timestamp of order creation
${d.order.entries}Collection of order entries (iterable)
#e(${d.order.shippingCost})Shipping cost of the order
#e(${d.order.tax})Total tax of the order
#e(${d.order.currency})Currency, for example, USD, EUR, GBP
#e(${d.order.subTotalPrice})Total price of the ordered items
#e(${d.order.totalPrice})Total price of the order
${d.order.mixins}Collection of mixins defined for the order
${d.order.mixins.NAME}Mixin with a name NAME defined for the order

Payment information

The following variables can carry the payment information in the order confirmation email.

VariableDescription
#e(${payment.status})Order status
#e(${payment.method})Method of payment
#e(${payment.paymentResponse})Information from the payment provider about the payment
#e(${payment.paidAmount})Amount of payment
#e(${payment.currency})Currency

Discount information

The following variables can carry the discount information in the order confirmation email.

VariableDescription
#e(${discount.code})Discount code
#e(${discount.amount})Discount amount
#e(${discount.currency})Discount currency
#e(${discount.name})Name of discount
#e(${discount.discountRate})Discount rate

Shipping information

The following variables can carry the shipping information in the order confirmation email.

VariableDescription
#e(${d.order.shipping.total.amount})Total shipping cost of an order
#e(${d.order.shipping.total.currency})Currency

Shipping lines

VariableDescription
#e($ship.amount)Shipping cost of a shipping line
#e($ship.currency)Currency
#e($ship.name)Shipping name
#e($ship.discounts)Discount on a shipping line

Each discount

VariableDescription
#e(${discount.code})Discount code
#e(${discount.amount})Discount amount
#e(${discount.currency})Discount currency
#e(${discount.name})Discount name
#e(${discount.discountRate})Discount rate

Each order entry - an order line

VariableDescription
#e($entry.unitPrice)Unit price
#e($entry.amount)Quantity
#e($entry.totalPrice)Total price for the order line
#e($entry.tax)Line tax
#e($entry.product.id)Product ID
#e($entry.product.sku)Product SKU
#e($entry.product.name)Product name
#e($entry.product.description)Product description
${entry.product.images.get(0).url}Gets the URL of the first image of the product
${entry.mixins}Collection of mixins defined for the order entry
${entry.mixins.NAME}Mixin with a name NAME defined for the order entry

If there are additional attributes in the product mixins, they can be accessed as #e($entry.product.mixins.NAME.FIELD). To do so, replace:

  • NAME with the name of mixin
  • FIELD with key of the field

For example, if you have the following mixin in a product:

"product":{
   ...
   "mixins":{
      "inventory":{
         "inStock":true
      }
   }
   ...
}

Then the following statement can be used to access the value of the inStock attribute:

#e($entry.product.mixins.inventory.inStock)

In general, you can access any other mixin in the order payload in the same way, such as mixins for extending billing or shipping address, customer data, order entry mixins, and so on.

Customer information

The following variables can carry the customer information in the order confirmation email.

VariableDescription
#e(${d.order.customer.id})Customer ID
#e(${d.order.customer.email})Customer email
#e(${d.order.customer.title})Customer title, such as Mr., Mrs. etc.
#e(${d.order.customer.name})Customer's full name
#e(${d.order.customer.firstName})First name of a customer
#e(${d.order.customer.middleName})Middle name of a customer
#e(${d.order.customer.lastName})Last name of a customer
#e(${d.order.customer.company})Customer's company


You can make use of the above data in your order confirmation email templates. You can also use additional information based on the mixins-based attributes assigned to your customer object.

To do this, you can use the following example and modify it to suit your needs, according to your actual mixin contents:

#if($!{d.order.customer.mixins} && $!{d.order.customer.mixins.secondaryContactPhone})      #e($!{d.order.customer.mixins.secondaryContactPhone.number})     #end

This example assumes that the secondaryContactPhone attribute is included in the mixin, but you can have any other attribute in your mixins, depending on your particular requirements.

Addresses information

The following variables can carry the address information in the order confirmation email, separately for billing and for shipping address.

Billing address
VariableDescription
#e(${d.order.billingAddress.contactName})Name for billing purposes
#e(${d.order.billingAddress.companyName})Company name for billing purposes
#e(${d.order.billingAddress.street})Billing address: street
#e(${d.order.billingAddress.streetNumber})Billing address: house number
#e(${d.order.billingAddress.streetAppendix})Billing address: street appendix
#e(${d.order.billingAddress.zipCode})Billing address: ZIP Code
#e(${d.order.billingAddress.city})Billing address: city
#e(${d.order.billingAddress.country})Billing address: country
#e(${d.order.billingAddress.state})Billing address: state
#e(${d.order.billingAddress.contactPhone})Billing address: contact phone number
${d.order.billingAddress.mixins}Collection of mixins defined for the billing address
${d.order.billingAddress.mixins.NAME}Mixin with a name NAME defined for the billing address
Shipping address
VariableDescription
#e(${d.order.shippingAddress.contactName})Shipping address: contact name
#e(${d.order.shippingAddress.companyName})Shipping address: company name
#e(${d.order.shippingAddress.street})Shipping address: street
#e(${d.order.shippingAddress.streetNumber})Shipping address: house number
#e(${d.order.shippingAddress.streetAppendix})Shipping address: street appendix
#e(${d.order.shippingAddress.zipCode})Shipping address: ZIP Code
#e(${d.order.shippingAddress.city})Shipping address: city
#e(${d.order.shippingAddress.country})Shipping address: country
#e(${d.order.shippingAddress.state})Shipping address: state
#e(${d.order.shippingAddress.contactPhone})Shipping address: contact phone
${d.order.shippingAddress.mixins}Collection of mixins defined for the shipping address
${d.order.shippingAddress.mixins.NAME}Mixin with a name NAME defined for the shipping address


Status transitions

Every order at any point in time has one of the following statuses:

  • CREATED: New order
  • CONFIRMED: Seller confirmed the order
  • DECLINED: Seller declined the order
  • COMPLETED: Order has been successfully fulfilled
  • SHIPPED: Order goods have been successfully shipped

The order status denotes the current state of the order:

  • When an order is created by a buyer, its initial status is always CREATED.
  • When the seller receives and accepts the order, the status changes to CONFIRMED.
  • If the seller receives the order and denies the order for some reason, the status changes to DECLINED. When an order moves to a DECLINED state, that is its final state. No further transitions are possible.
  • After the seller prepares and ships the order, the seller can update the order with shipment information and change the order status to SHIPPED.
  • After the product has been received by the customer, the seller changes the status to COMPLETED. When an order moves to a COMPLETED state, that is its final state. No further transitions are possible.

The order status can change. An authorized user, such as the merchant, can change the order status in several ways. Some buyer-related actions can also cause order status transitions. The possible order transitions are:

  • CREATED -> CONFIRMED
  • CREATED -> DECLINED (final state)
  • CONFIRMED -> CONFIRMED (idempotent)
  • CONFIRMED -> SHIPPED (only possible if an order contains at least one shipment)
  • CONFIRMED -> DECLINED (final state)
  • SHIPPED -> SHIPPED (idempotent)
  • SHIPPED -> COMPLETED (final state)


Page Query and Sort

Paging results

You can set parameters for paging results for methods that return more than one value, such as a GET method applied on the /{tenant}/salesorders resource. There are two parameters that can be used for paging results:

  • pageSize: Number of result entries allocated per page. If the number of results is greater than than this number, additional pages become available.
  • pageNumber: Page number of the page to be retrieved. Page numbering starts with 1.

The search results also include headers that contain links to the previous, current, and next pages.

The total number of search results is kept in the relevant header.

Example

This example URL displays the second page of results with three order entries per page.

https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders?pageNumber=2&pageSize=3

Filtering results

You can set parameters to filter results for methods that return more than one value, such as a GET method applied on the /{tenant}/salesorders resource. Generally, using the GET method on the /{tenant}/salesorders resource returns all existing orders. However, you can reduce the number of results by providing certain query parameters. You can filter your results by any order property.

The query starts with the letter q followed by an equal sign (=). You can use any order property as a parameter, such as currency or shippingCost. This is an example of the syntax for complex properties:

?q=customer.name:"John Smith"

Examples

Filtering by name

This example demonstrates how to see all the orders that have been created by a customer named John Smith. To get all the orders that belong to John Smith, run this query:

  • https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders?q=customer.name:"John Smith"
Filtering by multiple parameters

You can use one or more parameters in a query. The search query format uses spaces as separators between multiple query parameters to be in line with the Document service.

This example demonstrates how to get orders that meet these criteria:

  • The order is confirmed.
  • The shipping cost is less than 25.
  • The total price of the order is greater than 2500.

Note that all conditions need to be met for this query to return the expected results:

https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders?q=status:"CONFIRMED" shippingCost:<25 totalPrice:>2500

You can also restrict the set of values to retrieve in your Product service queries. For example, you can retrieve all the products with the customer name values listed in parentheses as shown:

https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders?q=customer.name:("John Smith", "Sven Svensson")

Filtering by address

This example demonstrates how to find customers living in a certain city, such as Munich:

https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders?q=city:"Munich")

Sorting results

You can set parameters to filter results for methods that return more than one value, such as a GET method applied on the /{tenant}/salesorders resource. You can sort the results by using the order properties as sort criteria. To sort the results, append the sort parameters to the URL. There are two types of sorting:

  • Ascending: /{tenant}/salesorders?sort=created
  • Descending: /{tenant}/salesorders?sort=-created

By default, results are sorted by the created field in descending order. The newest orders are listed first. No query parameter is required to perform default sorting.

You can sort by any field. This example sorts the results by status:

  • Ascending: /{tenant}/salesorders?sort=status
  • Descending: /{tenant}/salesorders?sort=-status

You can also sort the orders by more than one criterion by supplementing the URL with a list of comma-separated values. You can also sort results in ascending order for one criterion while sorting in descending order for a different criterion. For example: /{tenant}/salesorders?sort=status,-created

Example

This example demonstrates how to sort orders by two properties using the example URL https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders?sort=status,-created.

The results of this query are sorted in ascending order by status and descending order by created:

[
  {
    "id" : "53b3df64fa6ed8b01e4cd05f",
    "created" : "2014-07-02T10:31:00.645Z",
    "status" : "CONFIRMED",
  },
  {
    "id" : "53b2b8092d6e4701d55c8582",
    "created" : "2014-07-01T13:30:49.603Z",
    "status" : "CONFIRMED",
  },
  {
    "id" : "53b156fcd1ae4d788047f327",
    "created" : "2014-06-30T12:24:28.055Z",
    "status" : "CONFIRMED",
  },
  {
    "id" : "53b2b7d42d6e4701d55c8580",
    "created" : "2014-07-01T13:29:56.342Z",
    "status" : "CREATED",
  },
  {
    "id" : "53b2b7a72d6e4701d55c857e",
    "created" : "2014-07-01T13:29:11.001Z",
    "status" : "CREATED",
  },
  {
    "id" : "53b2b76e2d6e4701d55c857d",
    "created" : "2014-07-01T13:28:14.251Z",
    "status" : "CREATED",
  }
]


Customer Data Deletion

An order contains private data of customers such as name, contact information, etc. These private information can be anonymized as required by a merchant in two distinct steps.

  • Find the orders belonging to the customer
  • update the customer data with anynymous data

Find the orders belonging to the customer data

  • Method: GET
  • Request URI: https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders
  • Headers
    • hybris-site: The header contains information about the site. Note: if you add the siteCode code in the order payload, this header is not required and can be omitted.
    • Authorization: You must provide a proper scope that enables users to perform these operations. These scopes should be granted in an access token from OAuth 2.0 service. For more information about the authorization and authentication used in hybris services, see the Authorization section.
  • Scope: hybris.order_read
  • Query Parameters
    • customer.id: <customer's ID>
    • customer.email: <customer's email>

This example shows one of the orders filtered with the query: https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders?q=customer.email:"noreply@yaastest.com"

{
    "id": "orderId123456",
    "created": "2015-02-11T03:12:33.345Z",
    "status": "CREATED",
    "siteCode": "Germany",
    "entries": [
      {
        "amount": 1,
        "originalAmount": 450,
        "unitPrice": 420,
        "measurementUnit": {
          "value": 1,
          "unit": "kg"
              },
        "id": "1234546",
        "subTotalPrice": 420,
        "totalPrice": 420,
        "link": "https://api.beta.yaas.io/hybris/product/v2/products/cocoa",
        "product": {
          "description": "A cocoa you've always wanted !",
          "name": "Cocoa",
          "sku": "cocoa-product",
          "published": true,
          "images": [
            {
              "id": "media123456",
              "url": "https://api.beta.yaas.io/hybris/product/v2/media/media123456?tenantInfo"
            }
          ]
        }
      }
    ],
    "customer": {
      "id": "C1234567890",
      "title": "Dr.",
      "name": "Example Buyer",
      "firstName": "Example",
      "lastName": "Buyer",
      "email": "noreply@yaastest.com",
      "company": "hybris"
    },
    "billingAddress": {
      "contactName": "Example Buyer",
      "street": "Nymphenburger Str.",
      "streetNumber": "86",
      "city": "Munich",
      "state": "Bavaria",
      "country": "DE",
      "zipCode": "132 45",
      "contactPhone": "+49 1111 222 333"
    },
    "shippingAddress": {
      "contactName": "Joe Smith",
      "companyName": "Cargo Services Airfreight",
      "street": "Cheng Xiang Zhen Guan Tang Lu",
      "streetNumber": "1031",
      "extraLine1": "No. 2058",
      "extraLine2": "Suzhou City",
      "zipCode": "201202",
      "city": "Taicang City",
      "state": "Jiangsu Province",
      "country": "CN",
      "contactPhone": "+86 182 9349 4663"
    },
    "subTotalPrice": 420,
    "totalPrice": 420,
    "currency": "USD",
    "channel": {
        "name": "Twitter",
        "source": "https://twitter.com/hybriskiwis/status/707599225782214656"
    }
  }

Update the order with anynymous customer data

  • Method: PUT
  • Request URI: https://api.beta.yaas.io/hybris/order/v1/{tenant}/salesorders/{orderID}
  • Headers
    • Authorization: You must provide a proper scope that enables users to perform these operations. These scopes should be granted in an access token from OAuth 2.0 service. For more information about the authorization and authentication used in hybris services, see the Authorization section.
  • Scope: hybris.order_update
  • Body: Order with anonymous customer data

This example shows one the order with anonymized customer data.

{
    "customer": {
      "id": "C1234567890",
      "title": "Anonymous",
      "name": "Anonymous",
      "firstName": "Anonymous",
      "lastName": "Anonymous",
      "email": "Anonymous@Anonymous.com",
      "company": "Anonymous"
    },
    "billingAddress": {
      "contactName": "Anonymous",
      "street": "Anonymous",
      "streetNumber": "Anonymous",
      "city": "Anonymous",
      "state": "Anonymous",
      "country": "DE",
      "zipCode": "Anonymous",
      "contactPhone": "Anonymous"
    },
    "shippingAddress": {
      "contactName": "Anonymous",
      "companyName": "Anonymous",
      "street": "Anonymous",
      "streetNumber": "Anonymous",
      "extraLine1": "Anonymous",
      "extraLine2": "Anonymous",
      "zipCode": "Anonymous",
      "city": "Anonymous",
      "state": "Anonymous",
      "country": "DE",
      "contactPhone": "Anonymous"
    }
  }


Basics - Salesorders

Introduction

Order service comes with separate endpoints devoted for orders created by the customers and salesorders managed by the merchants. Generally, they provide the same functionality, and both orders and salesorders land in the same pool of orders. These separate endpoints, however, support different set of methods. Simplifying these differences, merchants can do anything with any order, while customers can only create and retrieve their orders.

In this tutorial, you can see examples of methods the merchants use to manage salesorders.

Let's get started. Assume you have a call office where you collect requests from your customers. Next, your employees set the actual orders in the order processing system. They must be able to do this on behalf of your customers. Moreover, they must be able to create, update, retrieve, or remove orders.

Setup

Assertion

Define variable assert:

assert = chai.assert;

Get access token

To perform any operations with a specific service, you always need an access token. For this purpose, create an API Client for the oAuth2 service:

API.createClient('oAuth2Service', '/services/oauth2/v1/api.raml');

Now, get the token:

tenant = projectIdPlaceholder;

AccessToken = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret': clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant+' hybris.order_create hybris.order_read hybris.order_update hybris.order_delete'
});

To make the calls simple and the code clean, assign the access token to a variable:

access_token = AccessToken.body.access_token;

Create API client for Order service

API.createClient('orderService',
'/services/order/v1/api.raml');

Examples

Create a sales order

As these examples mostly focus on presenting the functionality, use a simple payload with some basic properties. This will allow you to catch the main flow behind the scene. You can also modify some properties here and run the whole notebook again to see the results of your modifications.

Let's see how the sales order is created by running this simple example:

response = orderService.tenant(tenant).salesorders.post({
    'entries': [ {
          'amount': '2',
          'originalAmount': 450,
          'unitPrice': '420',
          'totalPrice': '840',
          'product': {
            'name': 'MUG',
            'sku': 'mug-product'
            }
          },
          {
          'amount': '5',
          'originalAmount': 250,
          'unitPrice': '240',
          'totalPrice': '1200',
          'product': {
            'name': 'GUM',
            'sku': 'gum-product'
            }
          }
  ],
  'customer': {
      'id': 'C8837738909',
      'firstName': 'John',
      'lastName': 'Smith',
      'email': 'noreply@yaastest.com'
  },
  'totalPrice': '2040',
  'currency': 'USD',
  }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 201);

response;

The response body contains the order id value and link to the resource that includes all order details. Having this link, anyone who needs feedback from Order service can get a direct access to the order details.

The id unique and auto-created for each order. You can use this value to retrieve details of the newly created sales order later. To make it easy to reuse, let's store it in a variable, for example orderId:

orderId = response.body.id;

Retrieve a sales order

All you need to know to retrieve a single sales order - aside of standard elements like endpoint and method - is its id. There is a new sales order that we already created in the previous section. Its id is now stored in a variable that we are going to use right now.

We can try to retrieve the previously created sales order and see if it really contains all the data that has been provided upon creation.

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.id, orderId);
assert.equal(response.body['entries'][0].amount, '2');
assert.equal(response.body['entries'][0].originalAmount, '450');
assert.equal(response.body['entries'][0].unitPrice, '420');
assert.equal(response.body['entries'][0].totalPrice, '840');
assert.equal(response.body['entries'][0].product.name, 'MUG');
assert.equal(response.body['entries'][0].product.sku, 'mug-product');

assert.equal(response.body['entries'][1].amount, '5');
assert.equal(response.body['entries'][1].originalAmount, '250');
assert.equal(response.body['entries'][1].unitPrice, '240');
assert.equal(response.body['entries'][1].totalPrice, '1200');
assert.equal(response.body['entries'][1].product.name, 'GUM');
assert.equal(response.body['entries'][1].product.sku, 'gum-product');

assert.equal(response.body.customer.id, 'C8837738909');
assert.equal(response.body.customer.firstName, 'John');
assert.equal(response.body.customer.lastName, 'Smith');
assert.equal(response.body.customer.email, 'noreply@yaastest.com');
assert.equal(response.body.totalPrice, '2040');
assert.equal(response.body.currency, 'USD');

response;

As the code has finished successfully and we got the status 200, it is a sign all the sales order details we retrieved here are in fact matching the data provided in the payload upon the order creation.

Update a sales order

As a merchant you have full control over the orders. Let's assume something has gone wrong with an order. Someone made a mistake that should now be corrected, not by replacing the old order with a new one, but modifying the content of the one that already exists.

While updating an sales order, you can change some order data or you can change it all. Whatever you modify, one attribute remains constant, and that's id, which uniquely identifies an order.

Now, take a look at the example, execute it, see the results:

response = orderService.tenant(tenant).salesorders.orderId(orderId).put({
    'entries': [ {
          'amount': '2',
          'originalAmount': 450,
          'unitPrice': '420',
          'totalPrice': '840',
          'product': {
            'name': 'MUG',
            'sku': 'mug-product'
            }
          },
          {
          'amount': '5',
          'originalAmount': 250,
          'unitPrice': '240',
          'totalPrice': '1200',
          'product': {
            'name': 'GUM',
            'sku': 'gum-product'
            }
          },
          {
          'amount': '1',
          'unitPrice': '100',
          'totalPrice': '100',
          'product': {
            'name': 'T-shirt',
            'sku': 'tshirt-product'
            }
          }          
    ],
    'customer': {
        'id': 'A8457328301',
        'firstName': 'Julia',
        'lastName': 'Johansson',
        'email': 'noreply@yaastest.com'
      },
      'totalPrice': '2140',
      'currency': 'USD',
    }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 204);

response;

It seems the update operation has finished with success. The status code is ok, but how can we really be sure the content has been properly updated? Well, you could make use of a method you already know from the previous section. You can retrieve this sales order, check its details, and see if it really has been updated with new data. Let's verify our updated order now:

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
);

assert.equal(response.status, 200);
assert.equal(response.body.id, orderId);
assert.equal(response.body['entries'][0].amount, '2');
assert.equal(response.body['entries'][0].originalAmount, '450');
assert.equal(response.body['entries'][0].unitPrice, '420');
assert.equal(response.body['entries'][0].totalPrice, '840');
assert.equal(response.body['entries'][0].product.name, 'MUG');
assert.equal(response.body['entries'][0].product.sku, 'mug-product');

assert.equal(response.body['entries'][1].amount, '5');
assert.equal(response.body['entries'][1].originalAmount, '250');
assert.equal(response.body['entries'][1].unitPrice, '240');
assert.equal(response.body['entries'][1].totalPrice, '1200');
assert.equal(response.body['entries'][1].product.name, 'GUM');
assert.equal(response.body['entries'][1].product.sku, 'gum-product');

assert.equal(response.body['entries'][2].amount, '1');
assert.equal(response.body['entries'][2].unitPrice, '100');
assert.equal(response.body['entries'][2].totalPrice, '100');
assert.equal(response.body['entries'][2].product.name, 'T-shirt');
assert.equal(response.body['entries'][2].product.sku, 'tshirt-product');


assert.equal(response.body.customer.id, 'A8457328301');
assert.equal(response.body.customer.firstName, 'Julia');
assert.equal(response.body.customer.lastName, 'Johansson');
assert.equal(response.body.customer.email, 'noreply@yaastest.com');
assert.equal(response.body.totalPrice, '2140');
assert.equal(response.body.currency, 'USD');

response;
You cannot change an order status by updating it with help of the /{tenant}/salesorders/{orderId} resource. If you include such an update in your request body and use the PUT method, then the update will be ignored. The transition must be performed with help of the /{tenant}/order/<orderId>/transitions resource. For more details on status transitions, see Status Transitions section and Order Status tutorial.

Delete a sales order

Once in a while you may need to remove an order that is no longer needed. You can use the id attribute to specify which order you want to remove. Execute a piece of code below and see the results. Our sales order we used in previous examples in this tutorial will be removed as a result of this operation.

response = orderService.tenant(tenant).salesorders.orderId(orderId).delete({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
);

assert.equal(response.status, 204);
response;

Status code 204 assures us the operation has finished successfully. To verify the accuracy of this indication, let's try to retrieve this order once more:

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
);

assert.equal(response.status, 404);

response;

We got error message in response which - in this context - is perfectly fine as we attempted to retrieve the order that has been removed in the preceding step.


Basics - Orders

Introduction

Customers can only retrieve their orders with help of the Order service. In this tutorial, you will see an example of method used to retrieve customer orders. To see examples of the salesorders methods, go to the Basics - Salesorders section.

Setup

Assertion

Define variable assert:

assert = chai.assert;

Get access token

To perform any operations with a specific service, you always need an access token. For this purpose, create an API Client for the oAuth2 service:

API.createClient('oAuth2Service', '/services/oauth2/v1/api.raml');

Now, we get the token:

tenant = projectIdPlaceholder;

AccessToken = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret': clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant+' hybris.order_post hybris.order_view_history'
});

To make the calls simple and the code clean, assign the access token to a variable:

access_token = AccessToken.body.access_token;

Create API clients for Order and Customer service

Order service:

API.createClient('orderService', '/services/order/v1/api.raml');

Customer service:

API.createClient('customerService', '/services/customer/v1/api.raml');

Examples

Clients can retrieve their own orders with help of the Order service. To present this example, we therefore need a customer in a first place. Let's create a customer that will later retrieve own orders.

customerServiceResponse = customerService.tenant(tenant).signup.post({
  "email": "yaasCustomer@yaastest.com",
  "password": "secret"
}, {
    headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type': 'application/json'
      }
});

loginResponse = customerService.tenant(tenant).login.post({
  "email": "yaasCustomer@yaastest.com",
  "password": "secret"
}, {
    headers: {
      'Authorization': 'Bearer ' + access_token,   
      'Content-Type': 'application/json'
    }
  })

customerToken = loginResponse.body.accessToken;

customerServiceResponse = customerService.tenant(tenant).me.get({
}, {
    headers: {
    'Authorization': 'Bearer ' + customerToken,
    'Content-Type': 'application/json'
    }
});

customerId = customerServiceResponse.body.customerNumber;

Second, we need an order that will belong to our customer. Let's create a simple order with some basic properties:

response = orderService.tenant(tenant).orders.post({
    'entries': [ {
          'amount': '2',
          'unitPrice': '420',
          'totalPrice': '840',
          'product': {
            'name': 'MUG',
            'sku': 'mug-product'
            }
          },
          {
          'amount': '5',
          'unitPrice': '240',
          'totalPrice': '1200',
          'product': {
            'name': 'GUM',
            'sku': 'gum-product'
            }
          }
  ],
  'customer': {
      'id': customerId,
      'firstName': 'John',
      'lastName': 'Smith',
      'email': 'yaasCustomer@yaastest.com'
  },
  'totalPrice': '2040',
  'currency': 'USD',
  }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 201);

response;
For the purpose of our tutorial, we created an order to be used in our example presenting how a customer can retrieve their orders. However, in real situations the Checkout service is responsible for creating an order for a customer. You may want to read more about this topic in the Checkout service documentation.

Note that the field id in the payload should contain the unique identifier of the customer we have created in preceding step. In this case, it is loaded from a variable where we have it saved.

The response body contains the order id value and link to the resource that includes all order details. Having this link, anyone who needs feedback from Order service can get a direct access to the order details.

The id is unique and auto-created. You can use this value to retrieve details of the newly created order later. To make it easy to reuse, let's put it into a variable, for example orderId:

orderId = response.body.id;

Retrieve an order

Now, clients can see and retrieve their own orders only. In this section, we go and check how a client can actually do this. At this point of the tutorial, we have a customer and we have an order that belongs to that customer.

All you need to know to retrieve a single order - aside of standard elements like endpoint and method - is its id. There is a new order that we already created in the previous section. The order id is stored in a variable that we are going to use right now.

We can try to retrieve the order and see if it really contains all the data that has been provided upon creation. Specifically, check the customerId field to verify the order really belongs to the proper customer.

response = orderService.tenant(tenant).orders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + customerToken,
        'Content-Type' : 'application/json'
      }
    } );

assert.equal(response.status, 200);
assert.equal(response.body.id, orderId);
assert.equal(response.body['entries'][0].amount, '2');
assert.equal(response.body['entries'][0].unitPrice, '420');
assert.equal(response.body['entries'][0].totalPrice, '840');
assert.equal(response.body['entries'][0].product.name, 'MUG');
assert.equal(response.body['entries'][0].product.sku, 'mug-product');

assert.equal(response.body['entries'][1].amount, '5');
assert.equal(response.body['entries'][1].unitPrice, '240');
assert.equal(response.body['entries'][1].totalPrice, '1200');
assert.equal(response.body['entries'][1].product.name, 'GUM');
assert.equal(response.body['entries'][1].product.sku, 'gum-product');

assert.equal(response.body.customer.id, customerId);
assert.equal(response.body.customer.firstName, 'John');
assert.equal(response.body.customer.lastName, 'Smith');
assert.equal(response.body.customer.email, 'yaasCustomer@yaastest.com');
assert.equal(response.body.totalPrice, '2040');
assert.equal(response.body.currency, 'USD');

response;

As the code has finished successfully and we got the status 200, it is a sign all the order details we retrieved here are in fact matching the data provided in the payload upon the order creation.


Add Order Shipment

Introduction

An authorized user, such as merchant, can add shipment information to an existing order. This specific piece of data contains details about when the order is sent to the customer, which company delivers the order, and optional information that gives customers more details about the current state of their order. Merchants must add at least one shipment to the order before they can mark the order as SHIPPED.

Setup

Assertion

Define variable assert:

assert = chai.assert;

Get access token

To perform any operations with a specific service, you always need an access token. For this purpose, create an API Client for the oAuth2 service:

API.createClient('oAuth2Service', '/services/oauth2/v1/api.raml');

Now, get the token:

tenant = projectIdPlaceholder;

AccessToken = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret': clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant+' hybris.order_create hybris.order_read hybris.order_update hybris.order_delete'
});

To make the calls simple and the code clean, assign the access token to a variable:

access_token = AccessToken.body.access_token;

Create API client for Order service

API.createClient('orderService',
'/services/order/v1/api.raml');

Example

Adding shipment information to an order

A newly created order does not have any shipment information at first. You, as a merchant, must first confirm an order by giving it the CONFIRMED status before it can be processed any further. Next, the shipment information needs to be added, as only an order that has a shipment data can transit from CONFIRMED to the SHIPPED status.

For the purpose of our example, we create a simple order now. Having the order, we can later retrieve it, check its status, and update it. Our example will consists of a few steps providing insight in the process of adding a shipment information to the newly created and confirmed order, and eventually marking such order as shipped to the customer.

1. Creating an order

Let's create a simple order now. It will be used in all next sections to show how to add shipment and mark the order as shipped.

response = orderService.tenant(tenant).salesorders.post({
    'entries': [ {
          'amount': '2',
          'originalAmount': 450,
          'unitPrice': '420',
          'totalPrice': '840',
          'product': {
            'name': 'MUG',
            'sku': 'mug-product'
            }
          },
          {
          'amount': '5',
          'originalAmount': 250,
          'unitPrice': '240',
          'totalPrice': '1200',
          'product': {
            'name': 'GUM',
            'sku': 'gum-product'
            }
          }
  ],
  'customer': {
      'id': 'C8837738909',
      'firstName': 'John',
      'lastName': 'Smith',
      'email': 'noreply@yaastest.com'
  },
  'totalPrice': '2040',
  'currency': 'USD'
  }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 201);

response;

To make the order id easy to reuse, let's store it in a variable, for example orderId:

orderId = response.body.id;
2. Verifying the order

At this point, we verify our order's existence:

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.id, orderId);
assert.equal(response.body.status, 'CREATED');
assert.equal(response.body['entries'][0].amount, '2');
assert.equal(response.body['entries'][0].unitPrice, '420');
assert.equal(response.body['entries'][0].totalPrice, '840');
assert.equal(response.body['entries'][0].product.name, 'MUG');
assert.equal(response.body['entries'][0].product.sku, 'mug-product');

assert.equal(response.body['entries'][1].amount, '5');
assert.equal(response.body['entries'][1].unitPrice, '240');
assert.equal(response.body['entries'][1].totalPrice, '1200');
assert.equal(response.body['entries'][1].product.name, 'GUM');
assert.equal(response.body['entries'][1].product.sku, 'gum-product');

assert.equal(response.body.customer.id, 'C8837738909');
assert.equal(response.body.customer.firstName, 'John');
assert.equal(response.body.customer.lastName, 'Smith');
assert.equal(response.body.customer.email, 'noreply@yaastest.com');
assert.equal(response.body.totalPrice, '2040');
assert.equal(response.body.currency, 'USD');

response;

As our verification proves, the newly created order has the status: CREATED. Let's check what kind of transitions are possible from here.

3. Checking possible status transitions for the order

You could change the order status now to SHIPPED. But is it possible? Let's check now what kind of transitions are possible for the order with the status: CREATED.

response = orderService.tenant(tenant).salesorders.orderId(orderId).transitions.get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body[0].status, 'CONFIRMED');
assert.equal(response.body[1].status, 'DECLINED');

response;

It seems you have two options for the newly created order. You can confirm that order, or you can decline it. Obviously, you cannot mark it as shipped if you decline it first. As a result, the only logical course of action is to mark the order as CONFIRMED.

For the list of possible status transitions and some other details on order status, see the Status transitions document, in the Details section.

4. Confirming the order

Now, you change the created order status to CONFIRMED.

response = orderService.tenant(tenant).salesorders.orderId(orderId).transitions.post({
  'status': 'CONFIRMED'
  }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 204);

response;

As always, verification is recommended. Retrieve the order and check the value of the modified status attribute.

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.status, 'CONFIRMED');

response;

The order status is now CONFIRMED. In the next step, you can add the shipment information.

5. Adding shipment information to the order

To add a shipment information, you need to include a shipments list in the payload. This list may contain one or more defined shipments. Only one shipment is required for the order to be able to gain SHIPPED status. The carrier and shippedDate are required attributes. Other shipment attributes are optional.

response = orderService.tenant(tenant).salesorders.orderId(orderId).put({
    'entries': [ {
          'amount': '2',
          'originalAmount': 450,
          'unitPrice': '420',
          'totalPrice': '840',
          'product': {
            'name': 'MUG',
            'sku': 'mug-product'
            }
          },
          {
          'amount': '5',
          'originalAmount': 250,
          'unitPrice': '240',
          'totalPrice': '1200',
          'product': {
            'name': 'GUM',
            'sku': 'gum-product'
            }
          }
  ],
  'customer': {
      'id': 'C8837738909',
      'firstName': 'John',
      'lastName': 'Smith',
      'email': 'noreply@yaastest.com'
  },
  'totalPrice': '2040',
  'currency': 'USD',
  'shipments': [
        {
          'trackingNumber': '123987456',
          'carrier': 'UPS',
          'shippedDate': '2016-06-25T16:22:52.966Z',
          'expectDeliveryOn': '2016-06-27',
        }
    ]
  }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 204);

response;

Now, we can retrieve the order to see if the shipment information has really been added in the order.

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.id, orderId);

assert.equal(response.body.status, 'CONFIRMED');

assert.equal(response.body['shipments'][0].trackingNumber, '123987456');
assert.equal(response.body['shipments'][0].carrier, 'UPS');
assert.equal(response.body['shipments'][0].shippedDate, '2016-06-25T16:22:52.966Z');
assert.equal(response.body['shipments'][0].expectDeliveryOn, '2016-06-27');

response;
6. Marking order as shipped

Order has now the shipment information. However, you can do one more thing. Since you have a confirmed order that includes the shipment information, you can mark this order as shipped. Let's check what kind of status transitions are possible for it now, and if SHIPPED is one of them.

response = orderService.tenant(tenant).salesorders.orderId(orderId).transitions.get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body[0].status, 'SHIPPED');
assert.equal(response.body[1].status, 'DECLINED');

response;

Even at this step, merchants can still decline an order. In our example, however, you want to have it marked as shipped. Let's do this final step.

response = orderService.tenant(tenant).salesorders.orderId(orderId).transitions.post({
  'status': 'SHIPPED'
  }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 204);

response;

The order seems to be shipped now. However, verification always is recommended. Retrieve the order and check the value of the modified status attribute.

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.status, 'SHIPPED');

response;

The order has now the necessary shipment information and its status is SHIPPED, which concludes our example.


Locate Orders in Sites

Introduction

Both customers and merchants can create orders and provide the information about the site where the order could have been placed. Later on, they also can locate and retrieve these orders by site if necessary.

Setup

Assertion

Define variable assert:

assert = chai.assert;

Get access token

To perform any operations with a specific service, you always need an access token. For this purpose, create an API Client for the oAuth2 service:

API.createClient('oAuth2Service', '/services/oauth2/v1/api.raml');

Now, get the token:

tenant = projectIdPlaceholder;

AccessToken = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret': clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant+' hybris.order_create hybris.order_read'
});

To make the calls simple and the code clean, assign the access token to a variable:

access_token = AccessToken.body.access_token;

Create API client for Order service

API.createClient('orderService', '/services/order/v1/api.raml');

Examples

It is quite possible that some day you could have several sites which would collect orders from your customers, or even from you, creating orders on behalf of your customers. Perhaps you would be interested to list all orders that belong to a specific site. Or, you might want to know who buys in a specific site, or what is the most sold product in a specific site. You can always list all your orders created in a specific site and do further processing to extract just the information you are interested in.

Let's create a few simple orders, so that we have something to work with.

Creating orders to use in example scenarios

The first order has the site information provided in the header. The site is Germany.

response = orderService.tenant(tenant).salesorders.post({
   'entries': [ {
      'amount': '2',
      'unitPrice': '420',
      'totalPrice': '840',
      'product': {
      'name': 'MUG',
      'sku': 'mug-product'
      }
      },
      {
      'amount': '5',
      'unitPrice': '240',
      'totalPrice': '1200',
      'product': {
      'name': 'GUM',
      'sku': 'gum-product'
      }
      }
   ],
   'customer': {
      'id': 'C8837738909',
      'firstName': 'John',
      'lastName': 'Smith',
      'email': 'noreply@yaastest.com'
   },
   'totalPrice': '2040',
   'currency': 'USD'
   }, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
   }
});

assert.equal(response.status, 201);

response;

To make the order id easy to reuse, let's store it in a variable, for example orderId:

orderId = response.body.id;

Now, create a second order. This time we skip the site information for the order.

response = orderService.tenant(tenant).salesorders.post({
   'entries': [ {
      'amount': '5',
      'unitPrice': '10',
      'totalPrice': '50',
      'product': {
      'name': 'PENCIL',
      'sku': 'pencil-product'
      }
      }
   ],
   'customer': {
      'id': 'C88372352AF1',
      'firstName': 'Johan',
      'lastName': 'Svensson',
      'email': 'noreply@yaastest.com'
   },
   'totalPrice': '50',
   'currency': 'USD'
}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json',
      'Accept-Language': 'en'
   }
});

assert.equal(response.status, 201);

response;

Let's store our second order's id in the orderIdent_2 variable:

orderIdent_2 = response.body.id;

The 3rd order also contains the site information. This time, however, we provide this information in the payload rather than in the header.

response = orderService.tenant(tenant).salesorders.post({
   'entries': [{
      'amount': '2',
      'unitPrice': '99',
      'totalPrice': '198',
      'product': {
         'name': 'Moon Crescent Stick Wand',
         'sku': 'moon-crescent-stick-wand-product'
      }
   },{
      'amount': '5',
      'unitPrice': '12',
      'totalPrice': '60',
      'product': {
         'name': 'Wasabi',
         'sku': 'wasabi-product'
      }
   }],
   'customer': {
      'id': 'C8837738909',
      'firstName': 'Musashi',
      'lastName': 'Johnson',
      'email': 'noreply@yaastest.com'
   },
   'siteCode': 'England',
   'totalPrice': '258',
   'currency': 'USD',
}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json',
      'Accept-Language': 'en'
   }
});

assert.equal(response.status, 201);

response;

We store the id of the 3rd in the orderIdent_3 variable:

orderIdent_3 = response.body.id;

Now, let's verify our three orders by retrieving them by their id.

Order one:

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json'
   }
});

assert.equal(response.status, 200);
assert.equal(response.body.id, orderId);
assert.equal(response.body.status, 'CREATED');
assert.equal(response.body.siteCode, 'Germany');

response;

Order two:

response = orderService.tenant(tenant).salesorders.orderId(orderIdent_2).get({}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json'
   }
});

assert.equal(response.status, 200);
assert.equal(response.body.id, orderIdent_2);
assert.equal(response.body.status, 'CREATED');

response;

Order three:

response = orderService.tenant(tenant).salesorders.orderId(orderIdent_3).get({}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json'
   }
});

assert.equal(response.status, 200);
assert.equal(response.body.id, orderIdent_3);
assert.equal(response.body.status, 'CREATED');
assert.equal(response.body.siteCode, 'England');

response;

Our verification proves that our newly created orders - those that have been given this information - have in fact been retrieved with the siteCode property in payload.

The second order has not been given any site information during creation. As a result, it does not have anything about the site in its returned payload.

Retrieve orders from a specific site - case 1

You now have orders in two different sites. Additionally, there is one order that does not belong to any site at all. You may have one order per site, or you may have hundreds or more - it does not matter as the process of locating the orders per site is the same for any of those cases.

For example, you have a shop site for Germany and a separate one for England. Assume it is the first day, the first minute after your business started, and you are already eager to check how many orders you have in your English site. Let's run simple and indicate the site you are interested in the header:

response = orderService.tenant(tenant).salesorders.get({}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json',
      'hybris-site'  : 'England'
   }
});

assert.equal(response.status, 200);

if (response.body.length > 0) {
  for (i=0; i< response.body.length; i++){
    assert.equal(response.body[i].siteCode, 'England')
  }
};

response;

The result is an array of objects, each of which represents an order. In our case, there is only one object returned in the array and this is perfectly fine, since we only created one order for the English site. Had there been more orders created for the English site, more objects would have been returned this way.

Retrieve orders from a specific site - case 2

In case of your English site, you added the site information with help of the siteCode attribute placed in the order payload. The other case was the German site, where the site information was relayed with help of the hybris-site header upon the order creation. The result, however, should be exactly the same. You still can retrieve all orders that belong to the German site, even if this site information has been added to order in a different way.

Again, in our example we do not try to retrieve any order by its id. In fact, we try to retrieve all possible orders that match one condition: they are located in a specific site, indicated by the header.

We already have one order in the German site. Perhaps, it makes sense to add one more order here to have a better insight in the response:

response = orderService.tenant(tenant).salesorders.post({
   'entries': [ {
      'amount': '10',
      'unitPrice': '17',
      'totalPrice': '170',
      'product': {
         'name': 'Deep Frozen Schnitzel',
         'sku': 'deep-frozen-schnitzel-product'
      }
   }],
   'customer': {
      'id': 'G88372342901',
      'firstName': 'Johan',
      'lastName': 'van den Budenmaier',
      'email': 'noreply@yaastest.com'
   },
   'totalPrice': '170',
   'currency': 'USD'
}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
   }
});

assert.equal(response.status, 201);

response;

Let's store it in the orderIdent_4 variable, in case we need it later:

orderIdent_4 = response.body.id;

Now, you should have two orders in the German site. Let's see how many orders you really get if you retrieve orders in the German site now:

response = orderService.tenant(tenant).salesorders.get({}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-Type' : 'application/json',
      'hybris-site'  : 'Germany'
   }
});

assert.equal(response.status, 200);

if (response.body.length > 0) {
   for (i=0; i < response.body.length; i++) {
      assert.equal(response.body[i].siteCode, 'Germany')
   }
};

response;

At this point of this tutorial, we have four orders:

  • One order that belongs to English site
  • Two orders that belong to German site
  • One order that does not belong to any site

The last example above shows that the response body contains two orders, which is a proof that you indeed have been able to get all the orders that belong to the German site.

Retrieve orders that do not have a site defined

You can use hybris-site header to get only those orders that belong to a certain site, indicated in the header. But what about those orders that have not been given any site information? We have one such order in this tutorial. Well, you cannot retrieve just them. If you skip the hybris-site header or you set it as null, the result will be the list of all orders that belong to any site, including those that have no site defined.

response = orderService.tenant(tenant).salesorders.get({}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-type' : 'application/json',
      'hybris-site'  : ''
   }
});

assert.equal(response.status, 200);

response;

Retrieve orders from a non-existing site

As our final test, let's see what would happen if you try to retrieve orders from a site that does not exist. In our tutorial, you have seen sites like Germany or England. There are orders here that belong to those sites, too. What if you now try to get orders that belong to, for example, Japan.

response = orderService.tenant(tenant).salesorders.get({
}, {
   headers: {
      'Authorization': 'Bearer ' + access_token,
      'Content-type' : 'application/json',
      'hybris-site'  : 'Japan'
   }
});

assert.equal(response.status, 200);

response;

The correct number of returned orders is: zero. If there is no site, there cannot any orders that belongs to it. The response will be an empty array, which is correct as there are no order elements that could have been returned this time.


Order Status

Introduction

Through its whole life cycle, order can gain several statuses. Some statuses can be given automatically, while other are given by a merchant. When you change the order status, the order transits from one state to another. Some transitions are allowed, while other do not make much sense anyway.

This tutorial will present some operations which can change the order status from one to another. To dig into more details about order status and possible order status transitions, see the Status Transitions section.

Setup

Assertion

Define variable assert:

assert = chai.assert;

Get access token

To perform any operations with a specific service, you always need an access token. For this purpose, create an API Client for the oAuth2 service:

API.createClient('oAuth2Service', '/services/oauth2/v1/api.raml');

Now, get the token:

tenant = projectIdPlaceholder;

AccessToken = oAuth2Service.token.post({
  'client_id' : clientIdPlaceholder,
  'client_secret': clientSecretPlaceholder,
  'grant_type' : 'client_credentials',
  'token_type': 'Bearer',
  'scope': 'hybris.tenant='+tenant+' hybris.order_create hybris.order_read hybris.order_update'
});

To make the calls simple and the code clean, assign the access token to a variable:

access_token = AccessToken.body.access_token;

Create API client for Order service

API.createClient('orderService',
'/services/order/v1/api.raml');

Examples

For the purpose of our examples, we would create a simple order now. Having the order, we can later retrieve it, check its status, change its status. We can then also use it to find out what kind of status transitions are possible for such order.

Let's create an order now.

response = orderService.tenant(tenant).salesorders.post({
    'entries': [ {
          'amount': '2',
          'unitPrice': '420',
          'totalPrice': '840',
          'product': {
            'name': 'MUG',
            'sku': 'mug-product'
            }
          },
          {
          'amount': '5',
          'unitPrice': '240',
          'totalPrice': '1200',
          'product': {
            'name': 'GUM',
            'sku': 'gum-product'
            }
          }
  ],
  'customer': {
      'id': 'C8837738909',
      'firstName': 'John',
      'lastName': 'Smith',
      'email': 'noreply@yaastest.com'
  },
  'totalPrice': '2040',
  'currency': 'USD',
  }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 201);

response;

To make it easy to reuse, let's store the order id in a variable, for example orderId:

orderId = response.body.id;

Verifying our order's existence:

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.id, orderId);
assert.equal(response.body.status, 'CREATED');
assert.equal(response.body['entries'][0].amount, '2');
assert.equal(response.body['entries'][0].unitPrice, '420');
assert.equal(response.body['entries'][0].totalPrice, '840');
assert.equal(response.body['entries'][0].product.name, 'MUG');
assert.equal(response.body['entries'][0].product.sku, 'mug-product');

assert.equal(response.body['entries'][1].amount, '5');
assert.equal(response.body['entries'][1].unitPrice, '240');
assert.equal(response.body['entries'][1].totalPrice, '1200');
assert.equal(response.body['entries'][1].product.name, 'GUM');
assert.equal(response.body['entries'][1].product.sku, 'gum-product');

assert.equal(response.body.customer.id, 'C8837738909');
assert.equal(response.body.customer.firstName, 'John');
assert.equal(response.body.customer.lastName, 'Smith');
assert.equal(response.body.customer.email, 'noreply@yaastest.com');
assert.equal(response.body.totalPrice, '2040');
assert.equal(response.body.currency, 'USD');

response;

As our verification proves, the newly created order has the status: CREATED. In our next section, we can try to see what kind of status transitions are possible for that order.

Check possible status transitions

You could change the order status now. But how can you know which transition - a transfer from one state to another - is possible for an order and which not? Some are pretty much obvious as it does not make much sense to transit order from DECLINED to CONFIRMED. But some may seem not so clear.

Well, there is a way out of this dilemma. You could simply check it by using a method presented in the following example. To do so, we can reuse the order created in the previous section.

We will check now what kind of transitions are possible for the order with the status: CREATED.

response = orderService.tenant(tenant).salesorders.orderId(orderId).transitions.get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body[0].status, 'CONFIRMED');
assert.equal(response.body[1].status, 'DECLINED');

response;

We got the status 200 that indicates our operation has been successful. It seems you have two options for the created order. You can confirm that order, or you could decline it. There are other status options for orders, like SHIPPED, but you cannot transit an order from the CREATED to the SHIPPED status.

For the list of possible status transitions and some other details on order status, see the Status Transitions document, in the Details section.

Changing an order status

Let's get started. Assume you have an order system, where your customers can purchase goods, check out, and place the orders online. At this moment the order is created. The status of such order is obviously a CREATED status. Every new order has that status. In the next step, however divergent possibilities for an order exist. Order could be confirmed, or sometimes it could be declined. Sometimes it happens automatically, sometimes you as a merchant would have to do this, but the general rule and the way of executing this change is the same.

Let's take a closer look at the method with which we can change the order status. In this example, we change our created order status to CONFIRMED. Observe that the payload only needs this one attribute defining the order's new status.

response = orderService.tenant(tenant).salesorders.orderId(orderId).transitions.post({
  'status': 'CONFIRMED'
  }, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json',
      'Accept-Language': 'en',
      'hybris-site': 'Germany'
      }
    }
)

assert.equal(response.status, 204);

response;

As always, verification is recommended. We retrieve our order again and check the value of the modified status attribute.

response = orderService.tenant(tenant).salesorders.orderId(orderId).get({}, {
      headers: {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type' : 'application/json'
      }
    }
)

assert.equal(response.status, 200);
assert.equal(response.body.status, 'CONFIRMED');

response;

As the new status value is CONFIRMED, the conclusion is that the order transition from CREATED to CONFIRMED has been completed successfully.


Glossary

TermDescription
tenantA tenant is a group of users on a project sharing common access, with specific privileges to a service.


  • Send feedback

    If you find any information that is unclear or incorrect, please let us know so that we can improve the Dev Portal content.

  • Get Help

    Use our private help channel. Receive updates over email and contact our specialists directly.

  • hybris Experts

    If you need more information about this topic, visit hybris Experts to post your own question and interact with our community and experts.