# Enterprise Integration Middleware (for Enterprise resto chains)
(Generic docs for resto chain like KFC)
## Overview
The Enterprise integration middleware (EIM) or Jigsaw is the integration layer between partner enterprises and GoJek systems (like GoFood, GoPay).
The integrations with enterprises typically involve 2 parts
* The core enterprise API(Jigsaw)
* Adapters to each integration(Optional)
The functions supported by Jigsaw are mentioned below. All other enterprise-specific features, data mappings, translations will live in enterprise-specific adapters.
## Supported Features
### 1. Authentication/Authorization
EIM supports OAuth2 for all authentication to the system. Each enterprise is issued a long term token that needs to be sent for all accesses to the system.
Authorization mechanism for third party enterprise systems to push content updates to GoFood. All requests to GoFood systems from third parties need to be authorized with an access token, which can be generated via the token endpoint.
### 2. Update menu on GoFood by enterprise vendor for a particular store
This feature gives the ability for the enterprise outlet owners/cashier to update the menu catalog and menu item details for a particular outlet. Note that this outlet and enterprise should already be registered and onboarded onto GoFood as well as EIM first. Enterprise outlet should also be authorized to be able to update the menu resource for that particular outlet
Typical menu updates supported:
- Item level description updates (name, price, description, image, category, operational hours availability)
- In_stock flag
#### Product flow:


### 3. Order relay for GoFood orders
As part of the order relay notification subscription feature for enterprise partners, we allow the enterprise to configure the order state at which they would like to receive the order notification and to what endpoint to be notified.
#### Product flow:

## Technical Specification
## Authentication
### 1. Get Access Token
This is the endpoint to generate the access token for the client. The third party systems will be provided with a client_id and client_secret. These need to be passed into this request as a Basic Auth Authorization header. In addition to this, third party systems also need to pass 2 form parameters: grant_type and scope as described below.
#### Resource
```
POST /oauth2/token
```
#### Headers
```
Authorization: <client_ID(username)_and_client_secret(password)_encrypted_as_Basic_Auth>
X-Request-Id: <uuid_for_debug_purposes>
Content-Type: application/x-www-form-urlencoded
```
#### Form Parameters
```
grant_type=client_credentials
scope=food:read food:write
```
#### Parameters Definition
| Name | Type | Required | Description |
| :--------- | :----- | :------- | :------------------------------------------- |
| grant_type | string | Yes | The grant type to be used |
| scope | string | Yes | The space separated list of scopes requested |
#### Response Codes
| HTTP status code | HTTP status description | Description |
| :--------------- | :---------------------- | :---------------------------------- |
| 200 | Success | Token issued successfully |
| 400 | Bad Request | Headers or parameter is missing |
| 401 | Unauthorized | ClientID or ClientSecret is invalid |
#### Sample Responses
```json
200 Success
{
"access_token": <encrypted_token>,
"expires_in": <seconds_until_token_expires>,
"token_type": "Bearer",
"scope": "food:read food:write"
}
```
```json
400 Bad Request
{
"error": "bad_request",
"error_description": "The request was missing a required parameter",
"error_hint": "client_id missing",
"status_code": 400
}
```
```json
401 Unauthorized
{
"error": "invalid_client",
"error_description": "Client authentication failed",
"error_hint": "client id or secret is invalid",
"status_code": 401
}
```
##
## Update Menu Catalog
Third party systems can push their stores' menu content updates to GoFood directly via this endpoint. Whenever there is a change in a store's menu, the third party system will need to push the whole menu of that store for the changes to be reflected on the GoFood app.
### 1. Update menu
```
PUT /outlets/:outlet_id/catalog
```
#### Headers
```
Authorization: Bearer <access_token>
Content-Type: application/json
Accept: application/vnd.eim.v1+json
```
#### Sample Request JSON
```json
{
"menus": [{
"name": "Mains",
"menu_items": [{
"name": "Zinger Meal",
"description": "Burger with Fries and Coke",
"price": 13200,
"external_id": "m-menu-item-id",
"image": "https://image.com/img1.png",
"in_stock": true,
"operational_hours": {
"monday": [{
"start": "00:00",
"end": "05:00"
},
{
"start": "10:00",
"end": "12:00"
}
],
"tuesday": [{
"start": "11:00",
"end": "23:59"
}],
"wednesday": [],
"thursday": [{
"start": "04:00",
"end": "10:00"
},
{
"start": "12:00",
"end": "16:00"
},
{
"start": "17:00",
"end": "22:00"
}
],
"friday": [],
"saturday": [{
"start": "04:00",
"end": "10:00"
}],
"sunday": []
}
}]
}],
"request_id": "<uuid_for_debug_purposes>"
}
```
#### Parameters Definition
| Name | Location | Type | Mandatory | Description |
| :-------- | -------- | :----- | :-------- | :------------------------- |
| outlet_id | Path | String | Yes | Merchant's Gojek Outlet ID |
#### Request Body Definition
| Name | Type | Mandatory | Description |
| :--------------------------------------------------------- | :------ | :-------- | :----------------------------------------------------------- |
| menus | array | Yes | List of the menus/categories like Sides, Mains, Favorites etc |
| menus[].name | string | Yes | Name of the menu/category. Maximum length is **150** chars. |
| menus[].menu_items | array | No | List of menu items. Maximum menu items for each menu is 100 menu items. |
| menus[].menu_items[].name | string | Yes | The menu item name. Maximum length is **250** chars. |
| menus[].menu_items[].description | string | No | The menu item description. Maximum length is **250** chars. |
| menus[].menu_items[].price | decimal | Yes | The price of the menu item |
| menus[].menu_items[].external_id | string | Yes | The external unique id of the menu item on the vendor store/system |
| menus[].menu_items[].image | string | No | Public URL of the menu item image. |
| menus[].menu_items[].in_stock | boolean | Yes | Whether the menu item is in stock or not |
| menus[].menu_items[].operational_hours | array | Yes | Operational hours of the menu item, if item is available at specific times |
| menus[].menu_items[].operational_hours.day_of_week | array | Yes | Day of the week for menu item operational hour. If the value is blank, it means inactive for the whole day in the week. |
| menus[].menu_items[].operational_hours.day_of_week[].start | string | No | Start time of menu item activation. |
| menus[].menu_items[].operational_hours.day_of_week[].end | string | No | End time of menu item activation. |
#### Response Codes
| HTTP status code | HTTP status description | Description |
| :--------------- | :---------------------- | :----------------------------------------------------------- |
| 200 | Success | Menu processed successfully |
| 401 | Unauthorized | Access Token is invalid or expired. The Get Access Token API need to be called to get a new access token |
| 400 | Bad Request | Headers or parameter is missing |
| 422 | Unprocessable Entity | Validation error |
| 404 | Resource Not Found | Store does not exist or is not registered against vendor |
#### Sample Responses
```
200 Success
{
"success": true,
"data": {
"request_id": "<uuid_for_debug_purposes>"
}
}
```
```
400 Bad Request
{
"success": false,
"errors": [
{
"message_title": "Bad Request",
"message": <Raw Error Message>
}
]
}
```
```
401 Unauthorized
{
"errors": [
{
"code": "HTTP_401",
"message": "Your session is expired. Please login again.",
"message_title": "Session Expired",
"message_severity": "error"
}
]
}
```
```
404 Resource Not Found
{
"success": false,
"errors": [
{
"message_title": "Requested resource not found!",
"message": <Raw Error Message>
}
]
}
```
```
422 Unprocessable Entity
{
"success": false,
"errors": [
{
"message_title": "Bad Request",
"message": <Raw Validation Error Message>
}
]
}
```
## Notifications Subscriptions
Client can subscribe to specific events they are interested in. When the subscribed events triggered, Gojek Enterprise Integration Middleware sends the notification to configured URL.
### Notification Signature
In order to verify that the requests are from Gojek, the receiver should validate the signature. The idea is to compute a signature using a secret key known to Gojek and the Enterprise. The request should have a http header _X-Go-Signature_ which is computed using [HMAC](https://en.wikipedia.org/wiki/HMAC)*(sha256, notification_secret_key, request_body)*. Value of *notification_secret_key* is provided alongside *client_id* and *client_secret* during enterprise registration by Gojek team.
### 1. Register Subscriptions
```
POST /enterprises/:enterprise_id/notification-subscriptions
```
#### Headers
```
Authorization: Bearer <access_token>
Content-Type: application/json
Accept: application/vnd.eim.v1+json
```
#### Sample Request JSON
```json
{
"event": "gofood.order.driver_arrived",
"url": "https://merchants.com/notifications/orders"
}
```
#### Parameters Definition
| Name | Location | Type | Mandatory | Description |
| :------------ | -------- | :----- | :-------- | :----------------------------- |
| enterprise_id | Path | String | Yes | Merchant's Gojek Enterprise ID |
#### Request Body Definition
| Name | Type | Mandatory | Description |
| :---- | :----- | :-------- | :----------------------------------------------------------- |
| event | string | Yes | Subscribed events. Currently available value is `gofood.order.driver_arrived` |
| url | string | Yes | The Enterprise URL for sending notifications. |
#### Response Codes
| HTTP status code | HTTP status description | Description |
| :--------------- | :---------------------- | :----------------------------------------------------------- |
| 201 | Created | Notification Subscription created successfully |
| 400 | Bad Request | Headers or parameter is missing |
| 401 | Unauthorized | Access Token is invalid or expired. The Get Access Token API need to be called to get a new access token |
| 404 | Resource Not Found | Enterprise is not found. |
#### Sample Responses
```
201 Created
{
"success": true,
"data": {
"subscription": {
"url": "https://merchants.com/notifications/orders",
"updated_at": "2019-10-16T06:50:07.000000Z",
"id": "2dda2d83-6c30-4ef8-9342-5ad1c68728ce",
"event": "gofood.order.driver_arrived",
"enterprise_id": "7b63bf24-2166-484f-8a05-daf295ae8baf",
"created_at": "2019-10-16T06:50:07.000000Z",
"active": true
}
}
}
```
```
400 Bad Request
{
"success": false,
"errors": [
{
"message_title": "Bad Request",
"message": <Raw Error Message>
}
]
}
```
```
401 Unauthorized
{
"errors": [
{
"code": "HTTP_401",
"message": "Your session is expired. Please login again.",
"message_title": "Session Expired",
"message_severity": "error"
}
]
}
```
```
404 Resource Not Found
{
"success": false,
"errors": [
{
"message_title": "Requested resource not found!",
"message": <Raw Error Message>
}
]
}
```
### 2. Get Subscriptions
```
GET /enterprises/:enterprise_id/notification-subscriptions
```
#### Headers
```
Authorization: Bearer <access_token>
Accept: application/vnd.eim.v1+json
```
#### Parameters Definition
| Name | Location | Type | Mandatory | Description |
| :------------ | -------- | :----- | :-------- | :----------------------------- |
| enterprise_id | Path | String | Yes | Merchant's Gojek Enterprise ID |
#### Response Codes
| HTTP status code | HTTP status description | Description |
| :--------------- | :---------------------- | :----------------------------------------------------------- |
| 200 | Success | Request processed successfully |
| 401 | Unauthorized | Access Token is invalid or expired. The Get Access Token API need to be called to get a new access token |
| 404 | Resource Not Found | Enterprise is not found. |
#### Sample Responses
```
200 Success
{
"success": true,
"data": {
"subscriptions": [{
"url": "https://merchants.com/notifications/orders",
"updated_at": "2019-10-16T06:50:07.000000Z",
"id": "2dda2d83-6c30-4ef8-9342-5ad1c68728ce",
"event": "gofood.order.driver_arrived",
"enterprise_id": "7b63bf24-2166-484f-8a05-daf295ae8baf",
"created_at": "2019-10-16T06:50:07.000000Z",
"active": true
}
}]
}
```
```
401 Unauthorized
{
"errors": [
{
"code": "HTTP_401",
"message": "Your session is expired. Please login again.",
"message_title": "Session Expired",
"message_severity": "error"
}
]
}
```
```
404 Resource Not Found
{
"success": false,
"errors": [
{
"message_title": "Requested resource not found!",
"message": <Raw Error Message>
}
]
}
```
### 3. Delete Subscription
```
DELETE /enterprises/:enterprise_id/notification-subscriptions/:subscription_id
```
#### Headers
```
Authorization: Bearer <access_token>
```
#### Parameters Definition
| Name | Location | Type | Mandatory | Description |
| :-------------- | -------- | :----- | :-------- | :----------------------------- |
| enterprise_id | Path | String | Yes | Merchant's Gojek Enterprise ID |
| subscription_id | Path | String | Yes | Notification Subscription ID |
#### Response Codes
| HTTP status code | HTTP status description | Description |
| :--------------- | :---------------------- | :----------------------------------------------------------- |
| 200 | Success | Notification subscription deleted successfully |
| 401 | Unauthorized | Access Token is invalid or expired. The Get Access Token API need to be called to get a new access token |
| 404 | Resource Not Found | Enterprise or Notification is not found. |
#### Sample Responses
```
200 Success
{
"success": true
}
```
```
401 Unauthorized
{
"errors": [
{
"code": "HTTP_401",
"message": "Your session is expired. Please login again.",
"message_title": "Session Expired",
"message_severity": "error"
}
]
}
```
```
404 Resource Not Found
{
"success": false,
"errors": [
{
"message_title": "Requested resource not found!",
"message": <Raw Error Message>
}
]
}
```
## Order
Gojek Enterprise Integration Middleware sends the notification to the vendor based on the subscription configuration.
### 1. Push Order
#### Sample Request Header
```
X-Go-Signature: d95697f3425ebaa4469183e8e6137b91982ff714ccea9e2ef8b2b610441feaac
Content-Type: application/json
Accept: application/json
```
#### Sample Request Structure
```json
{
"status": "DRIVER_ARRIVED",
"outlet": {
id:
}
"outlet_id": "M123456",
"pin": "45122",
"order_total": 40000,
"order_number": "F-394903031",
"order_items": [{
"quantity": 1,
"price": 40000,
"notes": "Default note",
"name": "Zinger Meal",
"id": "a9b0090a-8be8-46ce-8163-a0c2e6aab9f1",
"external_id": "m-menu-item-id"
}],
"external_outlet_id": "19700001vendorstoreid",
"driver": {
"phone": "+62814844572411",
"name": "GoFood Driver 1"
},
"customer": {
"phone": "+6281234567815",
"name": "GoFood Consumer",
"email": "gofood_consumer@gmail.com"
},
"currency": "IDR",
"created_on": "2019-09-09T10:39:46+07:00"
}
```
#### Definition
| Name | Type | Description |
| :------------------------ | :------ | :------------------------------------------------------ |
| order_number | string | Unique order number |
| status | string | Order status |
| created_on | string | Order Time which the order was created (ISO8601) |
| customer | array | Customer details |
| customer[].email | string | Customer's email |
| customer[].phone | string | Customer's phone number |
| customer[].name | string | Customer's name |
| order_total | decimal | Order total amount |
| order_items | array | List of ordered item details |
| order_items[].id | string | Internal item ID |
| order_items[].name | string | Order item name |
| order_items[].quantity | int | Quantity of item ordered |
| order_items[].price | decimal | Price of item ordered |
| order_items[].notes | string | Notes added by customer for item ordered |
| order_items[].external_id | string | Unique item ID on vendor system |
| outlet_id | string | Gojek outlet ID |
| external_outlet_id | string | Vendor outlet ID |
| driver | array | Driver details |
| driver[].name | string | Driver's name |
| driver[].phone | string | Driver's phone number |
| pin | string | PIN for order to be shared with driver for verification |
| currency | string | Currency code (ISO4217) |