# Online orders API

## Glossary

| Term              | Definition                                                                                                               |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Term              | Definition                                                                                                               |
| PORTOS            | The cash register solution developed by Nine Digit, s.r.o.                                                               |
| External system   | The 3rd party system (such as e-shop or food delivery platform) from which unprocessed orders are imported to PORTOS.    |
| Venue             | The restaurant processing the order.                                                                                     |
| Cashier           | The Venue personnel                                                                                                      |
| Customer          | User who orders the goods trough external system.                                                                        |
| Order             | The purchase request made by customer trough the external system, containing data about products, delivery address, etc. |
| Unprocessed order | Order accepted by external system, but not yet confirmed nor rejected by PORTOS.                                         |
| Processed order   | Order confirmed or rejected by PORTOS.                                                                                   |
| Product           | The product (such as food or drink) that is ordered by customer.                                                         |

## How it works

The online orders API is a mechanism that allows importing online orders from "external system" (such as e-shops or food delivery platforms) to the PORTOS system.

The PORTOS system acts as an "client" initiating HTTP requests targeting the *external system*, that acts as an "server" and handles the following requests:

1. Fetch unprocessed orders
2. Process order

After unprocessed order is fetched, cashier is requested to process (either accept or reject) the order trough the PORTOS application. After cashier confirms or rejects the order, PORTOS application will initiate second request (*process order*) to the *external system.*

The PORTOS system expects that responses from *external system* follows data format described below.&#x20;

## Fetch unprocessed orders

<mark style="color:blue;">`GET`</mark> `{external-system}{unprocessed-orders-url}`

The PORTOS system will periodically fetch the unprocessed orders from the external system, by initiating GET HTTP request to the external system. External system returns the unprocessed orders for given venue. If the collection of orders is paginated by the external system (so response does not contain all unprocessed orders), the most oldest orders should be returned.

#### Query Parameters

| Name    | Type   | Description                                                                  |
| ------- | ------ | ---------------------------------------------------------------------------- |
| version | string | The version of the exchange protocol. For versions 1.X, value is set to `1`. |
| key     | string | The API key.                                                                 |

{% tabs %}
{% tab title="200 " %}

```javascript
[
  {
    "id": "b478396ad654",
    "type": "delivery",
    "delivery": {
      "address": {
        "line1": "Kresankova 12",
        "line2": "",
        "city": "Bratislava",
        "zipCode": 84105,
        "note": "Please knock twice"
      },
      "fee": 1.99
    },
    "createdAt": "2021-02-01T12:01:00.000Z",
    "scheduledAt": null,
    "customer": {
      "name": "John Doe",
      "phone": "+421 900 123 456",
      "email": "john.doe@example.com"
    },
    "products": [
      {
        "id": "1234",
        "name": "Burger",
        "quantity": 1,
        "baseUnitPrice": 8.99,
        "note": "",
        "additions": [
          {
            "id": "11002",
            "name": "No cheese",
            "quantity": 1,
            "unitPrice": 0.00,
            "note": ""
          },
          {
            "id": "11002",
            "name": "Extra bacon",
            "quantity": 1,
            "unitPrice": 1.99,
            "note": ""
          }
        ]
      }
    ],
    "currency": "EUR",
    "payment": {
      "isSettled": true,
      "method": "card"
    },
    "tip": 0,
    "totalPrice": 12.97,
    "note": null
  },
  {
    "id": "6a2ad048e32d",
    "type": "takeAway",
    "delivery": null,
    "createdAt": "2021-02-01T12:01:00.000Z",
    "scheduledAt": "2021-02-01T13:00:00.000Z",
    "customer": {
      "name": "John Doe",
      "phone": "+421 900 123 456",
      "email": "john.doe@example.com"
    },
    "products": [
      {
        "id": "2345",
        "name": "Fries",
        "quantity": 2,
        "baseUnitPrice": 4.99,
        "note": "",
        "additions": [
          {
            "id": "11024",
            "name": "Ketchup",
            "quantity": 1,
            "unitPrice": 1.99,
            "note": ""
          }
        ]
      }
    ],
    "currency": "EUR",
    "payment": {
      "isSettled": false,
      "method": null
    },
    "wrappingFee": 0,
    "packagingDeposit": 0,
    "tip": 0,
    "totalPrice": 13.96,
    "note": null
  }
]
```

{% endtab %}

{% tab title="401 The bearer authorization token is invalid." %}

```
{
  "message": "This is the optional message describing the error."
}
```

{% endtab %}

{% tab title="403 The orders cannot be fetched. The reason may be provided in body." %}

```
{
  "message": "This is the optional message describing the error."
}
```

{% endtab %}
{% endtabs %}

## Process order

<mark style="color:green;">`POST`</mark> `{external-system}{process-order-url}`

After the order is confirmed or rejected by Cashier trough the PORTOS cash register system user interface, the PORTOS system will initiate the POST HTTP request to the external system, to mark this specific order as processed.

#### Query Parameters

| Name                  | Type   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| --------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| version               | string | The version of the exchange protocol. For versions 1.X, value is set to `1`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| key                   | string | The API key.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| externalId            | string | The external identifier of the order                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| status                | string | The order processing result. May be one of following: `accepted`, `rejected`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| estimatedCompletionAt | string | <p>The estimated date and time of order completion, formatted in ISO\_8601 format.<br><br>The meaning may vary based on order type:<br>- If order type is <code>delivery</code>: represents estimated time when products will be delivered to customer's address.<br>- If  order type is <code>takeAway</code>: expected time that the order is ready to pick up at the Venue.<br>- If order type is <code>dineIn</code>, value is equal to <code>order.scheduledAt</code> (the time of reservation).<br><br>This value is provided only when the status is <code>accepted</code>. Value is not set (or equal to <code>null</code>), if order status is <code>rejected</code>.</p> |
| rejectionReason       | string | Briefly described reason of the rejection. Is optionally provided only when the status is `rejected`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |

{% tabs %}
{% tab title="200 The order confirmation or rejection has been acknowledged by the external system. The order will not longer appear in unprocessed orders response." %}

```
/* No response body is required by PORTOS. */
```

{% endtab %}

{% tab title="401 The bearer authorization token is invalid." %}

```javascript
{
  "message": "This is the optional message describing the error."
}
```

{% endtab %}

{% tab title="403 The order has already been processed." %}

```javascript
{
  "message": "This is the optional message describing the error."
}
```

{% endtab %}
{% endtabs %}

## Data model

The data model of online orders exchange is described below.

{% hint style="info" %}
All prices include taxes.
{% endhint %}

### Order

Order represents purchase request made by customer trough the external system, containing data about products, delivery address, etc.

<table><thead><tr><th>Name</th><th>Type</th><th data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>externalId</code></strong></td><td><code>string</code></td><td>true</td><td>The unique identifier of order, generated by the external system.</td></tr><tr><td><code>type</code></td><td><code>string</code> (please see allowed values)</td><td>false</td><td>The type of order. Contains one of following values:<code>delivery</code> (products will be delivered to customers address), <code>takeAway</code>(customer will pick up the order at the venue) or<code>dineIn</code>(customer will eat at the venue, e.g. table reservation). If type is not specified, <code>delivery</code> is used.</td></tr><tr><td><code>delivery</code></td><td><a href="#orderdelivery"><code>OrderDelivery</code></a></td><td>false</td><td>Information about the order delivery. Required, when <code>type</code> is <code>delivery</code>. Otherwise must be <code>null</code> (or not specified at all).</td></tr><tr><td><strong><code>createdAt</code></strong></td><td><code>string</code></td><td>true</td><td>The ISO_8601 formatted date and time of order creation (when the order has been created in external system).</td></tr><tr><td><code>scheduledAt</code></td><td><code>string</code></td><td>false</td><td>The optional  ISO_8601 formatted date and time of scheduled order. If not specified, order should be delivered ASAP. This field is required when order type is <code>dineIn</code>.</td></tr><tr><td><strong><code>customer</code></strong></td><td><a href="#customer"><code>Customer</code></a></td><td>true</td><td>Customer contact information.</td></tr><tr><td><strong><code>products</code></strong></td><td><a href="#product"><code>Product[]</code></a></td><td>true</td><td>The collection of products. Must contain at least one product.</td></tr><tr><td><strong><code>currency</code></strong></td><td><code>string</code></td><td>true</td><td>Currency code (in <a href="https://en.wikipedia.org/wiki/ISO_4217">ISO 4217</a> standard) for order price information. </td></tr><tr><td><code>payment</code></td><td><a href="#payment"><code>Payment</code></a></td><td>false</td><td>The information about payment.</td></tr><tr><td><code>wrappingFee</code></td><td><code>number</code></td><td>false</td><td>Wrapping fee (not applicable in Slovak republic due to Slovak legislation).</td></tr><tr><td><code>packagingDeposit</code></td><td><code>number</code></td><td>false</td><td>Packaging deposit (associated with non-taxable VAT rate)</td></tr><tr><td><code>tip</code></td><td><code>number</code></td><td>false</td><td>Tip</td></tr><tr><td><strong><code>totalPrice</code></strong></td><td><code>number</code></td><td>true</td><td>Total price including VAT of order, including all products, product additions, delivery and wrapping fee. Precision up to 2 decimal places.</td></tr><tr><td><code>note</code></td><td><code>string</code></td><td>false</td><td>Optional comment related to the order.</td></tr></tbody></table>

### OrderDelivery

Order delivery information.

<table><thead><tr><th>Name</th><th>Type</th><th data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>address</code></strong></td><td><a href="#address"><code>Address</code></a></td><td>true</td><td>The delivery address.</td></tr><tr><td><code>fee</code></td><td><code>number</code></td><td>false</td><td>The cost of delivery including VAT. Precision up to 2 decimal places. If not specified, zero value will be used.   </td></tr></tbody></table>

### Address

The customer's address.

<table><thead><tr><th>Name</th><th>Type</th><th data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>line1</code></strong></td><td><code>string</code></td><td>true</td><td>1st line of delivery address (e.g. street name, building number)</td></tr><tr><td><code>line2</code></td><td><code>string</code></td><td>false</td><td>Optional 2nd line of delivery address.</td></tr><tr><td><strong><code>city</code></strong></td><td><code>string</code></td><td>true</td><td>Name of the city.</td></tr><tr><td><strong><code>zipCode</code></strong></td><td><code>string</code></td><td>true</td><td>The ZIP code (postal code).</td></tr><tr><td><code>note</code></td><td>string</td><td>false</td><td>Optional comment related to the address or delivery.</td></tr></tbody></table>

### Customer

The customer's contact information.

<table data-full-width="false"><thead><tr><th>Name</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>Name</td><td>Type</td><td>Required</td><td>Description</td></tr><tr><td><strong><code>name</code></strong></td><td><code>string</code></td><td><strong>Yes</strong></td><td>Name of customer.</td></tr><tr><td><strong><code>phone</code></strong></td><td><code>string</code></td><td><strong>Yes</strong></td><td>Phone number.</td></tr><tr><td><code>email</code></td><td><code>string</code></td><td>No</td><td>E-mail address.</td></tr></tbody></table>

### Product

The product (such as food or drink).

<table><thead><tr><th>Name</th><th>Type</th><th data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>id</code></td><td><code>string</code></td><td>false</td><td>The product identifier in PORTOS system. <strong>If this field will not be provided, the processing of order will not affect stock quantity of the material in PORTOS stock management system</strong>.</td></tr><tr><td><strong><code>name</code></strong></td><td><code>string</code></td><td>true</td><td>Name of product.</td></tr><tr><td><strong><code>quantity</code></strong></td><td><code>int</code></td><td>true</td><td>Positive, non-zero integer quantity of the product (e.g. 1, 2, ...).</td></tr><tr><td><strong><code>baseUnitPrice</code></strong></td><td><code>number</code></td><td>true</td><td>The base unit price of product including VAT (product additions are not included). Both positive and negative value are supported. Use negative value to indicate discount.</td></tr><tr><td><code>note</code></td><td><code>string</code></td><td>false</td><td>The optional product-related comment, provided by customer.</td></tr><tr><td><code>additions</code></td><td><a href="#productaddition"><code>ProductAddition[]</code></a></td><td>false</td><td><p>The product additions (modifiers). If product <code>quantity</code> is greater than 1, each one will have same <code>additions</code> applied.<br></p><p>Example #1: if customer orders two burgers with fries for each burger, order will contain product with name "Burger", quantity equal to 2 and addition with name "Fires" and quantity equal to one.</p><p></p><p>Example #2: if customer orders two burgers, one with fries and second with salad. In this case, order will contain two separate products - first with quantity equal to one and fries in additions; second with quantity equal to one and salad in additions.  </p></td></tr></tbody></table>

### ProductAddition

The product addition (option, modifier) that adds more information about how the product should be prepared.&#x20;

<table><thead><tr><th>Name</th><th>Type</th><th data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong>Name</strong></td><td>Type</td><td>true</td><td>Description</td></tr><tr><td><code>id</code></td><td><code>string</code></td><td>false</td><td>The product identifier in PORTOS system. <strong>If this field will not be provided, the processing of order will not affect stock quantity of the material in PORTOS stock management system</strong>.</td></tr><tr><td><strong><code>name</code></strong></td><td><code>string</code></td><td>true</td><td>Name of product.</td></tr><tr><td><strong><code>quantity</code></strong></td><td><code>int</code></td><td>true</td><td>Positive, non-zero integer quantity of the product option (e.g. 1, 2, ...).</td></tr><tr><td><strong><code>unitPrice</code></strong></td><td><code>number</code></td><td>true</td><td>The unit price of product option, including VAT. Value may be positive or negative, if affects the price of product. Value also may be equal to zero, if product price is not affected (e.g. steak doeness).</td></tr><tr><td><code>note</code></td><td><code>string</code></td><td>false</td><td>The optional attribute-related comment, provided by customer.</td></tr></tbody></table>

### Payment

The product payment information.

<table><thead><tr><th>Name</th><th>Type</th><th data-type="checkbox">Required</th><th>Description</th></tr></thead><tbody><tr><td><strong><code>isSettled</code></strong></td><td><code>boolean</code></td><td>true</td><td><code>true</code>, if payment has already been completed (e.g. transaction has been transferred), <code>false</code> otherwise.</td></tr><tr><td><code>method</code></td><td><code>string</code> (please see allowed values)</td><td>false</td><td><p>One of following: <code>cash</code>, <code>card</code></p><p></p><p>The description of payment method that was used (when <code>isSettled</code>is <code>true</code>) or will be used (when <code>isSettled</code> is <code>false</code>).</p></td></tr></tbody></table>

## Configuration

To communicate with external system properly, the external system administrators must provide values stated below to the venue owners or PORTOS support.

| Value                                  | Type     | Description                                                                                                                                        |
| -------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Value                                  | Type     | Description                                                                                                                                        |
| External system unprocessed orders URL | `string` | The URL hosted by external system, that will be requested by PORTOS to fetch the unprocessed orders.                                               |
| External system process order URL      | `string` | The URL hosted by external system, that will be requested by PORTOS to process (confirm or reject) order.                                          |
| API key                                | `string` | The venue-specific API key (authorization token) that will be placed in the `key` query parameter of all HTTP requests.                            |
| Fetch period                           | `int`    | The period (in seconds) that specifies, how often will PORTOS system invoke the "fetch unprocessed orders" API call. Default value is `15`seconds. |

## Example

In all request examples below, we will assume following configuration:&#x20;

* external system unprocessed orders URL: `https://the-foo-bar.sk/unprocessed_orders`
* external system process order URL: `https://the-foo-bar.sk/process_order`
* API key is: `my-token`

### Step 1: Fetching unprocessed orders

The PORTOS will request following URL:

```javascript
GET https://the-foo-bar.sk/unprocessed_orders?version=1&key=my-token
```

The external system returns following payload as an response:

```javascript
[
  {
    "externalId": "b478396ad654",
    "type": "delivery",
    "delivery": {
      "address": {
        "line1": "Kresankova 12",
        "line2": "",
        "city": "Bratislava",
        "zipCode": 84105,
        "note": "Please knock twice"
      },
      "fee": 1.99
    },
    "createdAt": "2021-02-01T12:01:00.000Z",
    "scheduledAt": null,
    "customer": {
      "name": "John Doe",
      "phone": "+421 900 123 456",
      "email": "john.doe@example.com"
    },
    "products": [
      {
        "id": "1234",
        "name": "Burger",
        "quantity": 1,
        "baseUnitPrice": 8.99,
        "note": "",
        "additions": [
          {
            "id": "11002",
            "name": "No cheese",
            "quantity": 1,
            "unitPrice": 0.00,
            "note": ""
          },
          {
            "id": "11002",
            "name": "Extra bacon",
            "quantity": 1,
            "unitPrice": 1.99,
            "note": ""
          }
        ]
      }
    ],
    "currency": "EUR",
    "payment": {
      "isSettled": true,
      "method": "card"
    },
    "wrappingFee": 0,
    "tip": 0,
    "totalPrice": 12.97,
    "note": null
  },
  {
    "externalId": "6a2ad048e32d",
    "type": "takeAway",
    "delivery": null,
    "createdAt": "2021-02-01T12:01:00.000Z",
    "scheduledAt": "2021-02-01T13:00:00.000Z",
    "customer": {
      "name": "John Doe",
      "phone": "+421 900 123 456",
      "email": "john.doe@example.com"
    },
    "products": [
      {
        "id": "2345",
        "name": "Fries",
        "quantity": 2,
        "baseUnitPrice": 4.99,
        "note": "",
        "additions": [
          {
            "id": "11024",
            "name": "Ketchup",
            "quantity": 1,
            "unitPrice": 1.99,
            "note": ""
          }
        ]
      }
    ],
    "currency": "EUR",
    "payment": {
      "isSettled": false,
      "method": null
    },
    "wrappingFee": 0,
    "packagingDeposit": 0,
    "tip": 0,
    "totalPrice": 13.96,
    "note": null
  }
]
```

As we can see, the external systems returns two unprocessed orders.

### Step 2: Order acceptation

The PORTOS will mark first order as accepted by requesting following URL:

```javascript
POST https://the-foo-bar.sk/process_order?version=1&key=my-token&externalId=b478396ad654&status=accepted&estimatedCompletionAt=2021-02-01T13%3A01%3A00.000Z
```

{% hint style="info" %}
Please note that all query parameters are URL encoded. In URL above, the `estimatedCompletionAt` query parameter value `2021-02-01T13:01:00.000Z` is URL encoded as `2021-02-01T13%3A01%3A00.000Z`.
{% endhint %}

### Step 3: Fetching unprocessed orders

The PORTOS will request unprocessed requests again:

```javascript
GET https://the-foo-bar.sk/unprocessed_orders?version=1&key=my-token
```

The external system returns following payload as an response:

```javascript
[
  {
    "externalId": "6a2ad048e32d",
    "type": "takeAway",
    "delivery": null,
    "createdAt": "2021-02-01T12:01:00.000Z",
    "scheduledAt": "2021-02-01T13:00:00.000Z",
    "customer": {
      "name": "John Doe",
      "phone": "+421 900 123 456",
      "email": "john.doe@example.com"
    },
    "products": [
      {
        "id": "2345",
        "name": "Fries",
        "quantity": 2,
        "baseUnitPrice": 4.99,
        "note": "",
        "additions": [
          {
            "id": "11024",
            "name": "Ketchup",
            "quantity": 1,
            "unitPrice": 1.99,
            "note": ""
          }
        ]
      }
    ],
    "currency": "EUR",
    "payment": {
      "isSettled": false,
      "method": null
    },
    "wrappingFee": 0,
    "packagingDeposit": 0,
    "tip": 0,
    "totalPrice": 13.96,
    "note": null
  }
]
```

The recently accepted order no longer appears in unprocessed orders collection.

### Step 4: Order rejection

The PORTOS will mark remaining order as rejected by requesting following URL:

```javascript
POST https://the-foo-bar.sk/process_order?version=1&key=my-token&externalId=6a2ad048e32d&status=rejected&rejectionReason=The%20food%20is%20out%20of%20stock.
```

{% hint style="info" %}
Please note that all query parameters are URL encoded. In URL above, the  `rejectionReason` query parameter value `The food is out of stock.` is URL encoded as `The%20food%20is%20out%20of%20stock.`.
{% endhint %}

### Step 5: Fetching unprocessed orders

The PORTOS will request unprocessed requests again:

```javascript
GET https://the-foo-bar.sk/unprocessed_orders?version=1&key=my-token
```

All requests has been processed (rejected or accepted). The external system returns empty collection as an response.

```javascript
[]
```

## Changelog

### Version 1.4 (2024-02-05)

* Added support for [products](#product) with negative `baseUnitPrice` as well for [product additions](#productaddition) with negative `unitPrice`.
* Requires PORTOS Pokladňa version 4.1.13 or later.

### Version 1.3 (2022-12-07)

* New `packagingDeposit` field added.
* Supported in PORTOS Pokladňa version 4.0.25 or later.
* Supported in PORTOS Online Objednávky version 3.3.8 or later.

### Version 1.2 (2022-08-12)

* New `tip` field added.
* Supported in PORTOS Pokladňa version 4.0.9 or later.

### Version 1.1 (2022-03-17)

* New `note` field added.
* Supported in PORTOS Online Objednávky version 1.0.15 or later.

### Version 1.0

* Initial version
