API usage examples

document version: 0.1
last modification date: 2020-12-31

Initial notes

Test environment server is: https://ordering.3e.pl. You can test these requests on this server setting {BASE_URL} as this value.

Authorization - get token

We need to call POST {BASE_URL}/auth-oauth2/oauth/token

Request:

POST /auth-oauth2/oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
x-tenant: {TENANT}
Authorization: Basic {BASIC_AUTH}
grant_type=password&scope=read&username=anonymous&password=

Parameters: | Parameter name | Description |——— |— | BASIC_AUTH |Authorization for OAuth2 service. Header value is calculated as string “user:password” encoded in base64. JS code for Authorization header value: ‘Basic ‘ + Buffer.from(username + ‘:’ + password).toString(‘base64’); Each tenant has its own OAuth Base Auth credentials. | grant_type |OAuth2 parameter - type of passed credentials. Here we use ‘password’. | score | value ‘read’ | username |use ‘anonymous’ for users without an account. | password |for anonymous should be empty. | TENANT |(header param) Tenant Id for example 8724ef16-20c8-4008-b183-3504cedc38af.

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhbm9ueW1vdXMiLCJzY29wZSI6W….",
  "token_type": "bearer",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhbm9ueW1vdXMiLCJzY29wZSI6Wy….",
  "expires_in": 3600,
  "scope": "read",
  "UUID": "4a2d12c3-5967-4745-8806-79b195fae6c6",
  "TENANT": "8724ef16-20c8-4008-b183-3504cedc38af",
  "jti": "77af3db6-26c3-49b2-acb5-52112ad018f3"
}

End customers who don’t have an account always get a token as an anonymous user. User generated id is in response (UUID field). Client app can save refresh token to re-generate main token (typical for OAuth2).

Documentation: https://docs.orderingstack.com/api/auth-api.html

Get restaurants for take-away

To get venues (restaurants) list we need to call: GET {BASE_URL}/venue-api/api/nearest?lat=0&lng=0

Request:

GET /venue-api/api/nearest?lat=0&lng=0 HTTP/1.1
Host: ordering.3e.pl
Authorization: Bearer {ACCESS_TOKEN}

Parameters: | Parameter name | Description |——— |— | lat |latitude - pointing place from which we look up for nearest restaurant (this request will return all restaurants ordered from the closest to the farthest) | lng |longitude

Response:

[
    {
        "id": "2de9a0c3-4b21-407c-83d1-031ea0735eb3",
        "name": "restaurant 1",
        "menu": "MENU_default",
        "active": true,
        "address": {
            "street": "Kilińskiego",
            "number": "64",
            "postal": "33-300",
            "city": "Nowy Sącz",
            "country": "Polska"
        },
        "timeZone": "Europe/Warsaw",
        "geoPosition": {
            "lat": 49.619367,
            "lng": 20.8046623
        },
        "channelConstraints": {
            "DELIVERY": {
                "active": true,
                "minDeliveryTime": "00:35:00",
                "minOrderValue": "30.0",
                "week": {
                    "MON": [
                        {
                            "from": "00:01",
                            "to": "23:59"
                        }
                    ],
                    "TUE": [
                        {
                            "from": "00:01",
                            "to": "23:59"
                        }
                    ],
                    "WED": [
                        {
                            "from": "00:01",
                            "to": "23:59"
                        }
                    ],
                    "THU": [
                        {
                            "from": "00:01",
                            "to": "23:59"
                        }
                    ],
                    "FRI": [
                        {
                            "from": "00:01",
                            "to": "23:59"
                        }
                    ],
                    "SAT": [
                        {
                            "from": "00:01",
                            "to": "23:59"
                        }
                    ],
                    "SUN": [
                        {
                            "from": "00:01",
                            "to": "23:59"
                        }
                    ]
                }
            },
            "TAKE_AWAY": {
                "active": true,
                "minDeliveryTime": "00:15:00",
                "week": {
                    "MON": [
                        {
                            "from": "00:00:01",
                            "to": "23:59:59"
                        }
                    ],
                    "TUE": [
                        {
                            "from": "00:01:00",
                            "to": "23:59:59"
                        }
                    ],
                    "WED": [
                        {
                            "from": "10:00:00",
                            "to": "23:59:59"
                        }
                    ],
                    "THU": [
                        {
                            "from": "00:00:01",
                            "to": "23:59:00"
                        }
                    ],
                    "FRI": [
                        {
                            "from": "00:00:01",
                            "to": "23:59:00"
                        }
                    ],
                    "SAT": [
                        {
                            "from": "00:01:00",
                            "to": "23:59:00"
                        }
                    ],
                    "SUN": [
                        {
                            "from": "00:01:00",
                            "to": "23:59:59"
                        }
                    ]
                }
            }
        },
        "warehouse": "2de9a0c3-4b21-407c-83d1-031ea0735eb3"
    }

In this response we have a number of restaurants. For each we have opening hours for certain order types (DELIVERY, TAKE-AWAY). Please note that opening hours is in the form of array (it can be for example: [“from”: “10:00:00”, to: “14:00:00”], [“from”:”17:00:00”, “to”:”23:00”]). There are also other constraints like minOrderTime or minDeliveryTime. Please note that the “menu” attribute is present for each restaurant. Based on it we know what menu is associated with this restaurant.

Get menu and items

To get menu and items from the venue we need to call /menu-api/api/menu/{MENU}?channel=TAKE_AWAY&instance=

Request:

GET /menu-api/api/menu/{MENU}?channel=TAKE_AWAY&instance=2de9a0c3-4b21-407c-83d1-031ea0735eb3  HTTP/1.1
Host: ordering.3e.pl
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhbm9ueW1vdXMiLCJz...

Parameters: | Parameter name | Description |——— |— | menu |Menu (“menu set” - in backoffice) we want to fetch. | channel |it is a delivery type (TAKE_AWAY, DELIVERY, DINE_IN) | instance |this is venue (restaurant) id we want to

Response:

{
  "root": {
    "kind": "3e/group/category",
    "id": "rootcat",
    "details": {
      "literals": {
        "name": "Menu"
      }
    },
    "items": [
      {
        "kind": "3e/group/category",
        "id": "Category 1609196793480",
        "details": {
          "literals": {
            "name": "Main dish"
          }
        },
        "items": [
          {
            "kind": "3e/product",
            "id": "test-01",
            "details": {
              "literals": {
                "name": "Test product 01",
                "description": "Test product"
              },
              "media": [
                {
                  "url": "https://baconmockup.com/320/320/",
                  "name": "MAIN_IMG"
                },
                {
                  "url": "",
                  "name": "DETAILS_IMG"
                }
              ]
            },
            "price": "12.99",
            "minPrice": null,
            "selCtx": "test-01",
            "fltCtx": "test-01",
            "_": {
              "category": "STREET FOOD"
            }
          }
        ]
      }
    ]
  },
  "locks": []
}

This is a simple response, with one category and one product in this category. Also the product (“test-01”) is the simplest possible. Please read documentation of Ordering Stack product structure: ….. (TODO).

Documentation: https://docs.orderingstack.com/api/menu-api.html#operation/menu

Orders workflow concepcion

order_workflow

Diagram above shows the example processes of request from the user (notice that origins of requests could be different). User calls the request endpoint method and the system proceeds that request as a command which has to be queued. User receives “200 OK Response” with minimum details as a confirmation of a successful queued command.

Thanks to this method, there is no risk that our performance will drop drastically with a high API load. With sending our first request we are automatically connected via web socket with the server so we are receiving the latest information on the ongoing basis. In the final response we receive very detailed JSON information.

Create a new order

To create a new order we need to call: POST {BASE_URL}/ordering-api/api/order/new

We are able to create an order with a minimal required amount of sent data and edit it later. All orders are in relations with user id - “UUID”. In case our access_token has expired we can retrieve the access_token with refrest_token. Otherwise our connection will be lost.

Request:

POST /ordering-api/api/order/new HTTP/1.1
Host: ordering.3e.pl
Content-Type: application/json
Authorization: Bearer {ACCESS_TOKEN}

Parameters: | Parameter name | Description |——— |— | orderType |Type of an order. Enumerated: “DELIVERY”, “TAKE_AWAY”, “DINE_IN_OPEN”, “DINE_IN” |venue | Point of sales (Restaurant) we are ordering from.

Response:

{
  "correlationId": "dfce17f0-9a84-4280-9561-c763ab1a4b6b",
  "orderId": "22c5e003-7676-4338-b963-718dd5ca0d5f",
  "lines": []
}

Request body with additional parameters:

{
    "orderType": "TAKE_AWAY",
    "buckets": [{
        "venue": "restaurant1",
        "lines": [
            {
                "productId": "test-01",
                "price":12.34, "quantity": 1,
                "status": "NEW" }
                ]}
    ],
    "contact": {
        "name": "testname",
        "phone": "123456789",
        "email": "test@gmail.com"
    },
    "extra": {"X-SOURCE": "WEB" }
}

Response:

Note that despite more parameters, the generic answer is the same.

{
  "correlationId": "cf6be2b1-ccec-4e64-a29a-0eb471b22ece",
  "orderId": "7f68159e-3273-4887-9ae5-6d9f87d35ad4",
  "lines": ["b6f21a01-ae3e-4f6f-8d31-77246c37dce6"]
}

Web socket tool

After creating an order we receive all information about its status via web socket connection. The connection to the websocket should be established before creating the order - to do this we need an active access_token.

We prepared the tool to easily check the current status of a web socket communication while playing with the API.

Via web socket receiver: https://tools.orderingstack.com/websocket-receiver/ we are able to see all information we are receiving in real time. To establish connection in the web socket receiver we have to provide all response information we have received in POST {BASE_URL}/auth-oauth2/oauth/token

Response:

{
  "access_token": "dasd124dsgsd5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhbm9ueW1vdXMiLCJzY29wZSI6WyJyZWFkIi….",
  "token_type": "bearer",
  "refresh_token": "zxczx2eezcoij2mxIkpXVCJ9.eyJ1cZXasjsd1312we…...",
  "expires_in": 3599,
  "scope": "read write",
  "UUID": "2f35a892-6ee4-4708-bfed-3ff6348e9436",
  "TENANT": "8724ef16-20c8-4008-b183-3504cedc38a",
  "jti": "a7c531c0-9013-45f0-aa78-8738ae14d4c7"
}

Then we need to press the “Connect to Websocket” button and we should receive “Websocket connected.” information as in the example below.

web_socket_tool

Creating an empty order

To create a new empty order we need to call: POST {BASE_URL}/ordering-api/api/order/new

As mentioned before there are no contradictions for creating an empty order for further edition.

Request body example:

{
  "orderType": "TAKE_AWAY",
  "buckets": [{ "venue": "restaurant1" }]
}

Respone on API call:

{
  "correlationId": "7239705f-3df8-4153-9e2a-447bf0c9413e",
  "orderId": "ac7cfc03-50cf-41ff-83b8-ac2af626c7a4",
  "lines": []
}

Web socket status information:

ORDER CHANGE: {"tenant":"8724ef16-20c8-4008-b183-3504cedc38af","id":"ac7cfc03-50cf-41ff-83b8-ac2af626c7a4","created":"2021-01-22T13:07:29.039Z","lastChanged":"2021-01-22T13:07:29.031Z","source":"central","users":[{"userId":"4fcef6a4-56e2-463b-9af9-40deccd8a69d","roles":["CREATOR","CUSTOMER"]}],"orderType":"TAKE_AWAY","total":"0","editTotal":"0","status":"NEW","claimCode":"417868","buckets":[{"venue":"restaurant1","name":"Restaurant 1","menu":"MENU_default","lines":[],"extra":{"minDeliveryMinutes":"10","minDeliveryTime":"00:10:00","minOrderValue":"0"}}],"closed":false,"completed":false}

Via web socket connection we have all necessary data in JSON format. We obtain details such as:

{
  "ORDER CHANGE": {
    "tenant": "8724ef16-20c8-4008-b183-3504cedc38af",
    "id": "ac7cfc03-50cf-41ff-83b8-ac2af626c7a4",
    "created": "2021-01-22T13:07:29.039Z",
    "lastChanged": "2021-01-22T13:07:29.031Z",
    "source": "central",
    "users": [
      {
        "userId": "4fcef6a4-56e2-463b-9af9-40deccd8a69d",
        "roles": ["CREATOR", "CUSTOMER"]
      }
    ],
    "orderType": "TAKE_AWAY",
    "total": "0",
    "editTotal": "0",
    "status": "NEW",
    "claimCode": "417868",
    "buckets": [
      {
        "venue": "restaurant1",
        "name": "Restaurant 1",
        "menu": "MENU_default",
        "lines": [],
        "extra": {
          "minDeliveryMinutes": "10",
          "minDeliveryTime": "00:10:00",
          "minOrderValue": "0"
        }
      }
    ],
    "closed": false,
    "completed": false
  }
}

Empty order - edition

Appending products - simple product.

To add a product to an order we have to call POST: {BASE_URL}/ordering-api/api/order/{order_id}/append

Request:

POST /ordering-api/api/order/{order_id}/append
Host: ordering.3e.pl
Content-Type: application/json
Authorization: Bearer {ACCESS_TOKEN}

Parameters: | Parameter name | Description |——— |— | order_id |orderID we received from response to “Creating an empty order” request

Request body example:

{
  "venue": "restaurant1",
  "lines": [
    {
      "productId": "test-01",
      "price": 12.34,
      "quantity": 1,
      "status": "NEW"
    }
  ]
}

Response on request API call:

{
  "correlationId": "4cc0da05-a452-497d-a3c5-b13cbaf5be6b",
  "orderId": "556c93e1-ab3e-44ec-8489-ef700900ace3",
  "lines": ["558b65a0-48a4-4343-94cc-71e08e750ca8"]
}

After successfull request we are receiving web socket status information:

ORDER CHANGE: {"tenant":"8724ef16-20c8-4008-b183-3504cedc38af","id":"649f6d0b-4076-4fc4-ac1c-4f34143d7cf5","created":"2021-01-26T12:15:48.721Z","lastChanged":"2021-01-26T12:15:54.452Z","source":"central","users":[{"userId":"33b9266e-5f1b-494a-84ea-693ad5b3a7d6","roles":["CREATOR","CUSTOMER"]}],"orderType":"TAKE_AWAY","total":"0","editTotal":"12.99","status":"NEW","claimCode":"279317","buckets":[{"venue":"restaurant1","name":"Restaurant 1","menu":"MENU_default","lines":[{"id":"6d1eba0f-15b9-4b99-a383-3d8c4e65b28b","creator":"33b9266e-5f1b-494a-84ea-693ad5b3a7d6","created":"2021-01-26T12:15:54.452Z","updated":"2021-01-26T12:15:54.479Z","source":"central","quantity":1,"price":"12.99","productId":"test-01","product":{"kind":"product","id":"test-01","literals":{"name":"Test product 01"},"img":"https://baconmockup.com/320/320/","quantity":"1","price":"12.99","extra":{"category":"STREET FOOD"}},"status":"NEW","hash":"qzZJHALIw40"}],"extra":{"minDeliveryMinutes":"10","minDeliveryTime":"00:10:00","minOrderValue":"0"}}],"closed":false,"completed":false}

Via web socket connection we are receiving detailed order information with changes, such as:

{
    "lastChanged": "the date of the last change we made",
	"editTotal": "total amount which is changed with latest update",
	"lines": "total amount which is changed with latest update"

Appending products - multiple products.

We are able to add multiple products at the same time. It is just the matter of adding one more product as an object into “lines”: array [ ].

We are calling the same endpoint with request POST: {BASE_URL}/ordering-api/api/order/{order_id}/append

Request body example:

{
  "venue": "restaurant1",
  "lines": [
    {
      "productId": "test-01",
      "price": 12.34,
      "quantity": 1,
      "status": "NEW"
    },
    {
      "productId": "test-02",
      "price": 22.56,
      "quantity": 2,
      "status": "NEW"
    }
  ]
}

Response on request API call:

{
  "correlationId": "bd1cb386-5030-4675-9ff1-641685cc60ee",
  "orderId": "649f6d0b-4076-4fc4-ac1c-4f34143d7cf5",
  "lines": [
    "b71e2742-eca9-4567-ba53-f09935ba9a00",
    "75a12cb7-7bd7-43f8-9f26-186178adcc21"
  ]
}

Web socket detailed order information:

Web socket information will be mostly the same as the notification sent above (when we were adding one product to our empty order). The difference will be in the “lines” parameter as now we will be presented with detailed information about two products (two json objects).

Error handling

In case we have any error in our request body or elsewhere we will receive detailed notification via web socket connection:

Web socket detailed order information:

NOTIFICATION: {"type":"OrderEventError","users":["33b9266e-5f1b-494a-84ea-693ad5b3a7d6"],"orderId":"649f6d0b-4076-4fc4-ac1c-4f34143d7cf5","event":{"correlationId":"bd1cb386-5030-4675-9ff1-641685cc60ee","user":"33b9266e-5f1b-494a-84ea-693ad5b3a7d6","orderId":"649f6d0b-4076-4fc4-ac1c-4f34143d7cf5"},"message":"Line '75a12cb7-7bd7-43f8-9f26-186178adcc21': null","codes":[{"code":"invalidProduct","message":"Line '75a12cb7-7bd7-43f8-9f26-186178adcc21': null","args":["75a12cb7-7bd7-43f8-9f26-186178adcc21","test-021"]}]}

As we see there is immediate information about the cause of an error - invalid “product_Id” parameter.