# Embedded / Receivable Events

# Embedded API Events

The Shopfront Embedded API has several events that you can subscribe to in order to add functionality to the POS.
This document describes the events and what is expected to be returned from them.

> Any event can return a promise. </br>
>
> Some object tables may not include all fields, please refer to the Typescript types for all fields

## READY

This listener fires when the connection between Shopfront and the Embedded API is 
established and is ready to use.

**Arguments**

- None

**Returns**

 - None

**Ready**

```javascript
application.addEventListener("READY", () => ({
    console.log("Integration Ready to Process");
}));
```

## FORMAT_INTEGRATED_PRODUCT

This listener fires whenever a product with its type set to `integrated` is added to the sale

**Arguments**

| Field | Type                         | Description                                     |
|-------|------------------------------|-------------------------------------------------|
| event | FormatIntegratedProductEvent | Contains the product that was added to the sale |

**Returns**

| Field | Type                         | Description                   |
|-------|------------------------------|-------------------------------|
| event | FormatIntegratedProductEvent | Contains the modified product |

### FormatIntegratedProductEvent

| Field   | Type                 | Description          |
|---------|----------------------|----------------------|
| product | FormattedSaleProduct | The modified product |

#### FormattedSaleProduct

| Field        | Type                          | Description                          |
|--------------|-------------------------------|--------------------------------------|
| uuid         | string                        | The unique identifier of the product |
| type         | string                        | The type assigned to the product     |
| name         | string                        | The product name                     |
| caseQuantity | number                        | The case quantity of the product     |
| metaData     | Record<string, unknown> | Extra data about the product         |

**Format Integrated Product**

```javascript
application.addEventListener("FORMAT_INTEGRATED_PRODUCT", event => {
    const product = event.product;
    if (
        typeof product.additional === "object" &&
        product.additional !== null &&
        product.additional.my_integration === 1
    ) {
        return {
            product: {
                ...product,
                metaData: {
                    ...product.metaData,
                    customInfo: {
                        name: "myIntegration",
                    },
                },
            },
        };
    }
});
```

## PAYMENT_METHODS_ENABLED

This allows you to both enable and disable payment methods on the finalise payment screen

**Arguments**

| Field   | Type                                 | Description                                      |
|---------|--------------------------------------|--------------------------------------------------|
| methods | Array<SellScreenPaymentMethod> | An array of all current payment methods          |

**Returns**

- Array<SellScreenPaymentMethod>:

| Field             | Type   | Description                                               |
|-------------------|--------|-----------------------------------------------------------|
| background_colour | string | The background colour of the payment method option        |
| gateway_url       | string | The URL that is navigated too when the method is selected |
| name              | string | The name of the payment method                            |
| text_colour       | string | The colour of the payment method's name                   |
| type              | string | The type of payment method                                |
| uuid              | string | The unique identifier of the payment method               |

**Payment Methods Enabled**

```javascript
//Return a custom payment method
const getPaymentMethod = async () => {
    let customMethod: LocalPaymentMethod | undefined;
    const methods = await application.database.all<LocalPaymentMethod>("paymentMethods");

    for(let i = 0, l = methods.length; i < l; i++) {
        if (methods[i].type === "custom" || methods[i].gateway_url === "https://mycustom.url") {
            customMethod = methods[i];
        }
    }

    return customMethod;
};

application.addEventListener("PAYMENT_METHODS_ENABLED", async methods => {
    const method = await getPaymentMethod();

    if (method) {
        //Add the payment method
        methods.push({
            ...method,
            default_pay_exact: true,
            type             : "custom"
        });
    }

    return methods;
});
```

## REGISTER_CHANGED

This fires anytime the user, outlet or register changes

**Arguments**

| Field | Type                 | Description                                                   |
|-------|----------------------|---------------------------------------------------------------|
| event | RegisterChangedEvent | An object containing the current user, outlet and register id |

**Returns**

- None

### RegisterChangedEvent

| Field    | Type   | Description             |
|----------|--------|-------------------------|
| register | string | The current register id |
| outlet   | string | The current outlet id   |
| user     | string | The current user id     |

**Register Changed**

```javascript
application.addEventListener("REGISTER_CHANGED", event => ({
    console.log(`New register id: ${event.register}`);
}));
```

## REQUEST_BUTTONS

This allows you to add custom buttons to several pages within Shopfront.

**Arguments**

| Field | Type | Description |
| --- | --- | --- |
| location | string | The location that the button will be appearing (see below for locations) |

*Locations:*

- ORDER_VIEW
- REGISTER_CLOSURE_VIEW
- FULFILMENT_PAGE

**Returns**

- Array<[Button](/documentation/Embedded/Components#Button)>

**Request Buttons**

```javascript
import { Button } from "@shopfront/bridge";

const orderViewButtons = () => {
    const button = new Button("Send to Integration", "https://example.com/icon.png");

    button.addEventListener("click", () => {
        // Perform action here
    });

    return [button];
};

application.addEventListener("REQUEST_BUTTONS", location => {
    switch(location) {
        case "ORDER_VIEW":
            return orderViewButtons();
    }

    return [];
});
```

## REQUEST_CUSTOMER_LIST_OPTIONS

This allows you to add your own button to the Add Customer screen. Particularly useful if you wish to implement
a way for Shopfront users to add custom loyalty customers to a sale

**Arguments**

- None

**Returns**

- Array<SellScreenCustomerListOption>:

| Field    | Type     | Description                                      |
|----------|----------|--------------------------------------------------|
| contents | string   | The name of the new option                       |
| onClick  | function | The function that is executed when it is clicked |

**Request Customer List Options**

```javascript
application.addEventListener("REQUEST_CUSTOMER_LIST_OPTIONS", () => {
    return [
        {
            contents: "Add Loyalty Customer",
            onClick : () => {
                console.log("Adding loyalty customer");
            }
        }
    ];
});
```

## REQUEST_SALE_KEYS

This allows you to create custom Sale Keys for your integration. Users can then add them to their Sale Key layouts.

**Arguments**

- None

**Returns**

- Array<SaleKey>:

| Field           | Type                | Description                                             |
|-----------------|---------------------|---------------------------------------------------------|
| supportedEvents | Array<string> | The supported events that can be assigned to the button |
| id              | string              | The id of the Sale Key                                  |
| name            | string              | The name given to the Sale Key                          |

**Request Sale Keys**

```javascript
import { SaleKey } from "@shopfront/bridge";r

application.addEventListener("REQUEST_SALE_KEYS", () => ({
    const keys: SaleKey[] = []; 
    const saleKey = new SaleKey(
        "@onshopfront/example/newKey",
         "A New Sale Key"
    ); 

    saleKey.addEventListener("click", () => {
        console.log("Button clicked");
    };

    keys.push(saleKey);

    return keys;
});
```

## REQUEST_SELL_SCREEN_OPTIONS

Listening to this event will allow you to add options to the sell screen actions (next to Sales Keys and Parked Sales).

> Any URL specified **must** be using the HTTPS protocol.

**Arguments**

- None

**Returns**

- Array<SellScreenOption>:

| Field | Type | Description |
| --- | --- | --- |
| url | string | The URL to display in a frame when this option is selected |
| title | string | The button label of the option, this can either be plain text or any valid HTML (this will inherit style from the POS) |

**Request Sell Screen Options**

```javascript
application.addEventListener("REQUEST_SELL_SCREEN_OPTIONS", () => ({
    "url": "https://example.com",
    "title": "Example Option",
});
```

## REQUEST_SETTINGS

This allows you to add a settings page for your integration. It will be added next to
the `Revoke` button on the Integrations Settings page

> If you provide a URL to your settings page it **must** be using the HTTPS protocol.

**Arguments**

- None

**Returns**

- Object:

| Field | Type | Description |
| --- | --- | --- |
| logo | string? | The URL to the logo you would like to display |
| description | string? | The description of what your integration does |
| url | string? | The URL to the settings page you wish to display |

**Request Settings**

```javascript
application.addEventListener("REQUEST_SETTINGS", () => ({
    logo: "https://example.com/logo-location.png",
    description: "Sends purchases to your accounting software",
    url: "https://example.com/shopfront-settings"
}));
```

## REQUEST_TABLE_COLUMNS

Listening to this event will allow you to add additional columns to several tables within Shopfront.

**Arguments**

| Field | Type | Description |
| --- | --- | --- |
| location | string | The location that the table is from (see below for locations) |
| data | object | Information about the table to be added to (see below along with locations) |

*Locations:*

- ORDER_VIEW

| Field                     | Type                | Description                       |
|---------------------------|---------------------|-----------------------------------|
| id                        | string              | The ID of the order               |
| from                      | string              | The ID of who the order is from   |
| to                        | string              | The ID of who the order is to     |
| status                    | string              | The status of the order           |
| type                      | string              | The type of order                 |
| sentAt                    | string \| null      | The time the order was sent       |
| products                  | Array<object> | The products in the order         |
| products.id               | string              | The ID of the product             |
| products.caseQuantity     | number              | The case quantity of the product  |
| products.orderedQuantity  | number              | The number of items ordered       |
| products.receivedQuantity | number              | The number of items received      |
| products.baseCost         | number              | The base cost for the product     |
| products.fees             | number              | The fees for the product          |
| products.freight          | number              | The freight for the product       |
| products.paymentFees      | number              | The payment fees for the product  |
| products.rebate           | number              | The rebate amount for the product |
| products.supplierCode     | string              | The supplier code for the product |

**Returns**

- `null` or Object:

| Field | Type | Description |
| --- | --- | --- |
| headers | Array<object> | The headers for the table |
| headers.label | string | The label for the header |
| headers.key | string | The key for the body and footer values |
| headers.weight | number | The weight of the column, the higher the value the further to the right the column is |
| body | Array<Object<key, label>> | Each element in the array is a row which contains an object map to provide the cell contents |
| footer | Object<key, label> | The value of the footer | 

**Request Table Columns**

```javascript
application.addEventListener("REQUEST_TABLE_COLUMNS", (location, data) => {
    if(location === "ORDER_VIEW") {
        if(data.status === "SENT" && data.type === "ORDER") {
            return {
                headers: [{
                    label : "10 Or More",
                    key   : "more",
                    weight: 10, // Will be 10 from the left
                }],
                body: data.products.map(product => ({
                    more: product.orderedQuantity > 10 ? "Yes" : "No",
                })),
                footer: {
                    more: "",
                },
            }
        }
    }
    
    return null;
});
```

## SALE_COMPLETE

This listener fires whenever a sale is finalised.

**Arguments**

| Field | Type               | Description                                          |
|-------|--------------------|------------------------------------------------------|
| event | SaleCompletedEvent | An object containing an object of the finalised sale |

**Returns**

- None

### SaleCompletedEvent

| Field | Type          | Description                              |
|-------|---------------|------------------------------------------|
| sale  | CompletedSale | An object containing details of the sale |

### CompletedSale

| Field      | Type                              | Description                                                  |
|------------|-----------------------------------|--------------------------------------------------------------|
| products   | Array<CompletedSaleProduct> | An array containing details of each product in the sale      |
| customer   | false OR object                   | If set, containers a `uuid` field with the customers id      |
| payments   | Array<CompletedSalePayment> | An array containing all the payment methods used in the sale |
| notes      | object                            | An object containing both `internal` and `sale` notes        |
| totals     | object                            | An object containing information on the payment totals       |
| registerId | string                            | The register that the sale was finalised on                  |
| userId     | string                            | The user who finalised the sale                              |
| status     | string                            | The status of the sale                                       |
| createdAt  | string                            | The date and time the sale was finalised                     |
| metaData   | Record<string, unknown>     | A record containing extra information about the sale         |

#### CompletedSaleProduct

| Field        | Type                          | Description                                                                                          |
|--------------|-------------------------------|------------------------------------------------------------------------------------------------------|
| name         | string                        | The product name                                                                                     |
| caseQuantity | number                        | The amount of products in a case                                                                     |
| quantity     | number                        | The quantity being sold in this transaction                                                          |
| prices       | object                        | An object containing information on the different prices the product has                             |
| metaData     | Record<string, unknown> | An object containing all custom information about Shopfront, this doesn't have any predefined format |

#### CompletedSalePayment

| Field    | Type   | Description                                          |
|----------|--------|------------------------------------------------------|
| method   | string | The method of the payment (e.g `CASH`)               |
| type     | string | The type of the payment method (e.g `INTEGRATED`)    |
| status   | string | The processing status of the payment method          |
| amount   | number | The amount that was paid for by this method          |
| metadata | string | A string containing extra information about the sale |

**Sale Complete**

```javascript
application.addEventListener("SALE_COMPLETE", event => ({
    console.log("Sale was finalized");
}));
```

## AUDIO_READY

This listener fires when the Embedded API is ready to receive Audio events.

**Arguments**

- None

**Returns**

- None

**Audio Ready**

```javascript
application.addEventListener("AUDIO_READY", () => ({
    console.log("Audio Events Ready to Process");
}));
```

## UI_PIPELINE

The UI_PIPELINE allows you to modify the content which is displayed in certain parts of the POS. 
Each application is passed the previous application’s response allowing you to continually modify and add on 
to the content to be displayed in Shopfront. If you’re listening to this event and don’t need to modify the content, 
it is highly recommended to return the content which was passed in originally, otherwise content from other 
applications may be lost.

**Arguments**

| Field    | Type                            | Description                                                          |
|----------|---------------------------------|----------------------------------------------------------------------|
| existing | Array<UIPipelineResponse> | The current content in the UI Pipeline                               |
| context  | UIPipelineContext               | Information about the Shopfront context when this listener was fired |

**Returns**

- Array<UIPipelineResponse>:

| Field   | Type                | Description                         |
|---------|---------------------|-------------------------------------|
| name    | string              | The name of the content             |
| content | string              | The raw html that is being inserted |

### UIPipelineContext

| Field    | Type       | Description                                              |
|----------|------------|----------------------------------------------------------|
| location | string     | The location when the listener was fired                 |
| trigger  | () => void | A callback that will refresh the UI Pipeline when called |

**UI Pipeline**

```javascript
application.addEventListener("UI_PIPELINE", (existing, context) => ({
    if (context.location === "shopfront:sell:transaction:products:pre") {
        const content = `<p>Your new content</p>`
        existing.push({
            "My new content",
            content
        });
    }

    return existing;
});
```

## SALE_PRE_FINISH_PIPELINE

The SALE_PRE_FINISH_PIPELINE allows you to execute code and modify the sale state after a sale has entered the 
completing stage, but before it is fully finished, you can use this to do things like activate gift cards or perform
modifications to the sale.

If you don't want the sale to complete, you should return false which will then exit.

Data isn't verified after the state is returned from this event, so be careful to maintain the same shape of the object
that was passed in.

> This can be called multiple times for a single sale, other events in the pipeline may reject the sale which can lead
> to the sale being attempted again.

> All types of sales will trigger this, so make sure you check the status of the sale before performing actions on it.

**Arguments**

| Field   | Type                   | Description                                                                   |
|---------|------------------------|-------------------------------------------------------------------------------|
| sale    | SaleState              | The current sale to be processed through the sale pipeline                    |
| context | PreSalePipelineContext | Information about the current state of Shopfront when this listener was fired |

**Returns**

- `SaleState` or `false`

### PreSalePipelineContext

| Field    | Type            | Description                                                                                 |
|----------|-----------------|---------------------------------------------------------------------------------------------|
| user     | string \| false | The user who performed the sale                                                             |
| register | string          | The register the sale was performed on, this can result in an empty string if it is unknown |

**Pre Sale Finish Pipeline**

```javascript
application.addEventListener("SALE_PRE_FINISH_PIPELINE", (sale, context) => {
    if(shouldFailSale(sale, context)) {
        return false;
    }

    return {
        ...sale,
        notes: {
            ...sale.notes,
            internal: `${sale.notes.internal}\nAdded an internal note`,
        },
    };
});
```

## INTERNAL_PAGE_MESSAGE

This event occurs whenever one of your custom pages (such as a sell screen option, or the settings page) sends your 
application an internal message. You can reply to the message by using the provided reference's send method.

We expect you verify the contents of the message before processing it to ensure the message came from your application 
page.

**Arguments**

- InternalPageMessageEvent:

| Field          | Type                   | Description                                                                                         |
|----------------|------------------------|-----------------------------------------------------------------------------------------------------|
| method         | string                 | The method that this message is coming from, e.g. `REQUEST_SELL_SCREEN_OPTIONS`, `REQUEST_SETTINGS` |
| url            | string                 | The URL that the message originated from, this will be the URL you originally provided              |
| clientId       | string                 | The client ID of the custom page that the message was sent from                                     |
| message        | any                    | The message your application has sent                                                               |
| reference      | Object                 | A reference to the sender, you can use it to reply and send future messages to your page            |
| reference.send | (message: any) => void | A function that takes a message to send to the custom page                                          |

**Returns**

- None

**Internal Page Message (Embedded Application)**

```javascript
application.addEventListener("INTERNAL_PAGE_MESSAGE", event => {
    if(event.url !== process.env.CUSTOM_PAGE_URL || event.clientId !== process.env.CLIENT_ID) {
        return;
    }

    if(typeof event.message !== "object") {
        return;
    }

    if(event.method === "REQUEST_SELL_SCREEN") {
        if(event.message.type === "PING") {
            // You can keep a copy of event.reference around if you want
            // to send more messages without having to send them from the
            // custom page first.
            event.reference.send({
                type: "PONG",
            });
        }
    }
});
```

## INTERNAL_PAGE_MESSAGE (custom page side)

This event is separate from the embedded bridge, but it allows you to receive messages from your embedded application.
Before you're able to receive a message, you must first send a message to your application (in order for the embedded 
bridge to obtain a reference to your page).

Communication occurs using the browser's built-in 
[postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage).

To send a message you must perform a `postMessage` to the parent window (which is a Shopfront window for the Vendor that
has integrated the application), the message must be an object with the following structure:

| Field    | Type                    | Description                                                                                                |
|----------|-------------------------|------------------------------------------------------------------------------------------------------------|
| type     | "INTERNAL_PAGE_MESSAGE" | Must be the string "INTERNAL_PAGE_MESSAGE"                                                                 |
| from     | string                  | The URL that you are sending this from, it must match the original URL that your application was loaded on |
| clientId | string                  | Your application's client ID                                                                               |
| message  | any                     | Typically an object, this is the message to be sent to your application                                    | 

Once the message has been sent, your embedded application will receive it using the 
[INTERNAL_PAGE_MESSAGE event](#internalPageMessage) and can respond using the provided reference. To receive a reply
you must listen for messages on the Window. Messages will be received as a 
[`MessageEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent) with the following object as the `data`
property:

| Field    | Type                    | Description                                                                       |
|----------|-------------------------|-----------------------------------------------------------------------------------|
| type     | "INTERNAL_PAGE_MESSAGE" | Will be the string "INTERNAL_PAGE_MESSAGE"                                        |
| url      | string                  | Your settings URL that the message is for                                         |
| clientId | string                  | Your application's client ID                                                      |
| message  | any                     | Typically an object, this is the message that has been sent from your application |

**Internal Page Message (Custom Page)**

```javascript
window.addEventListener("message", event => {
    if(event.origin !== "https://<vendor>.onshopfront.com") {
        return;
    }

    if(typeof event.data !== "object") {
        return;
    }

    if(event.data.url !== process.env.CUSTOM_PAGE_URL || event.data.clientId !== process.env.CLIENT_ID) {
        return;
    }

    if(event.data.type !== "INTERNAL_PAGE_MESSAGE") {
        return;
    }

    if(typeof event.data.message !== "object") {
        return;
    }

    if(event.data.message.type === "PONG") {
        console.log("Communication Successful!");
    }
}, false);

window.parent.postMessage({
    type    : "INTERNAL_PAGE_MESSAGE",
    from    : process.env.CUSTOM_PAGE_URL,
    clientId: process.env.CLIENT_ID,
    message : {
        type: "PING",
    },
}, "https://<vendor>.onshopfront.com");
```

## GIFT_CARD_CODE_CHECK

Listening to this event (GIFT_CARD_CODE_CHECK) allows you check the gift card code being used in Shopfront against
the rules of your application.

If a conflict is found, a message should be returned from your application.

For example, it is used to verify that the Shopfront's in-built gift card code, entered on the sell screen, won't 
conflict with codes reserved by third-party integrations.

**Arguments**

| Field   | Type           | Description                                                                                    |
|---------|----------------|------------------------------------------------------------------------------------------------|
| code    | string         | The gift card code to be checked.                                                              |
| message | string \| null | A message containing the information/error returned by the application - `null` if no conflict |

**Returns**

| Field   | Type           | Description                                                                                    |
|---------|----------------|------------------------------------------------------------------------------------------------|
| code    | string         | The gift card code to be checked.                                                              |
| message | string \| null | A message containing the information/error returned by the application - `null` if no conflict |

**Gift Card Code Check**

```javascript
application.addEventListener("GIFT_CARD_CODE_CHECK", async event => {
    const reservedRange = ["00001002", "00002002"]

    if (event.message !== null) {
        return event
    }

    const codeInt = parseInt(event.code);

    if (codeInt >= parseInt(reservedRange[0]) && codeInt <= parseInt(reservedRange[1])) {
        // Check to make sure the length of the cardNumber is within Card Ranges length
        const minLength = reservedRange[0].length;
        const maxLength = reservedRange[1].length;

        if (event.code.length >= minLength && event.code.length <= maxLength) {
            return {
                code   : event.code,
                message: `Your Application: Gift card code refused - ${event.code} is within a reserved range of values.`
            };
        }
    }

    return event;
});
```

## Direct Events

Shopfront allows you to plug in to several internal events in order to react to changes made by the user. These events
don't provide any parameters nor do they expect any return value.

The currently supported events are:

- `SALE_ADD_PRODUCT`
- `SALE_REMOVE_PRODUCT`
- `SALE_CHANGE_QUANTITY`
- `SALE_UPDATE_PRODUCTS`
- `SALE_ADD_CUSTOMER`
- `SALE_REMOVE_CUSTOMER`
- `SALE_RETRIEVE`
- `SALE_CLEAR`

If you wish to hook into any trigger not listed above, feel free to reach out to us.

**Direct Events**

```javascript
application.addEventListener("SALE_PRODUCT_ADD", () => {
    // Do something...
});
```