# How To / Fulfilment

# How-To: Fulfil Your Sales Using Shopfront

> [We presume you've already authenticated your application before reading this document.](/documentation/How-To/Authenticate)

> The term *order* and *sale* are used interchangeably in this document as it mainly depends on the perspective of the
> caller as to which is correct.

## Getting Started

This guide will go over embedding an application into Shopfront's UI through the use of Shopfront's Embedded API,
sending sales to Shopfront for users within Shopfront to fulfil and handling the events that occur in relation to
the fulfilment process.

It's designed for a number of use cases, including integrated eCommerce stores and integrated with delivery services.

The guide doesn't go over obtaining or sending data to and from your server as each integration will handle this in
a different way, instead we've just substituted in generate functions that you can use to assist with guiding you
with where to perform network requests to your API.

Because the Embedded API is executed on the client-side of Shopfront, all code needs to execute as JavaScript, whether
you write the code in a language that compiles to JavaScript (such as TypeScript) or directly in JavaScript is up to
you.

> [The full Fulfilment API can be found here](/documentation/Embedded/Fulfilment).  

## Setting up the Embedded API

Shopfront loads your application by embedding an HTML iFrame onto the page, to do this, you'll need to specify a 
location to serve it from.

You can inform Shopfront of the location of your application by going to your [applications](/applications), logging
in (if you haven't already), pressing *Edit* next to your application and specifying the location of your HTML file
which will load your JavaScript application in the *Embedded API URI* field, finally press the *Confirm* button to 
save your changes.

> Ensure your URL uses HTTPS otherwise your page won't load.

> Shopfront will load this URL directly in the web browser, so it doesn't have to be accessible from outside where
> your browser can currently access (e.g. using localhost with HTTPS will work fine).

You'll then need to setup an application to be served, this is currently outside the scope of this documentation, but 
you will need to install the Shopfront Embedded Bridge. Information about basic usage and installation 
[can be found here](/documentation/Embedded/Bridge).

After your application is setup, you'll need to edit your main file to import the bridge and connect your application
to Shopfront (example on the right).

**Setting up the Embedded Bridge**

```javascript
import { Bridge, Fulfilment, Sales } from "@shopfront/bridge";

// You'll need the Vendor's subdomain to initialize the bridge.
// Shopfront passes this in as a search parameter, but if you only want it to work
// with a specific Vendor we would suggest ignoring the parameter and specifying
// manually
const url = new URL(window.location.href);

const application = Bridge.createApplication({
    id    : process.env.CLIENT_ID, // Your application's CLIENT_ID
    vendor: url.searchParams.get("vendor"),
});
```

After your application has been setup and is communicating to Shopfront, you'll need to opt-in to the Fulfilment API.
Whilst this step is optional, it provides a better user experience as it enables the Fulfilment tab on the sell screen
instantly.

You can also specify whether your application requires an approval / accepting step (typically for delivery service
integrations). In our examples, we'll presume your application requires the approval step as the related code
can be easily ignored if it does not.   

**Opting-in to the Fulfilment API**

```javascript
application.addEventListener("READY", async () => {
    await application.send(new Fulfilment.RegisterIntent({
        requireApproval: false,
    }));
});
```

## Sending Shopfront Updates

In order for Shopfront to display your sales, you'll need to send them to Shopfront somehow, that's where the actions
`OrdersSync`, `OrderCreate`, `OrderUpdate` and `OrderCancel` come in.

> Data you provide is determined as the source of truth, for example, if Shopfront changes the status and your 
> application returns a previous status, Shopfront will go back to using your status.

On initial load, you'll need to get the currently outstanding orders from your server (typically using a `GET` request)
and send them in bulk to Shopfront using the `OrdersSync` action.

**Performing Initial Synchronisations of Sales**

```javascript
application.addEventListener("READY", async () => {
    // ... Opt-in ...

    // Fetch the outstanding sales from your server
    const sales = await fetchOutstandingSales();

    // Transform the sales into the format Shopfront wants
    // and send them to Shopfront using OrdersSync
    await application.send(new Fulfilment.OrdersSync({
        merge : false,
        orders: sales.map(transformSaleToShopfront),
    }));
});
```

To keep orders up-to-date, you'll typically have some method which obtains sale updates either on schedule or 
through the use of something like websockets. Once you receive the orders you'll want to update Shopfront by sending
either `OrderCreate` (for new orders), `OrderUpdate` (for updates to existing orders) or `OrderCancel` (to remove
orders from Shopfront).

Optionally you can also use `OrdersSync` with the `merge` parameter set to `false` to remove all the existing sales and
update them with the currently available sales.

**Updating Existing Sales in Shopfront**

```javascript
// onSaleUpdate is a method you would define which accepts a callback
// which is called whenever you receive a sale update, we've presumed
// it returns an object with two fields, `action` and `sale`.
// `action` is the action to be performed to the sale
// `sale` is your raw sale object
onSaleUpdate(async ({ sale, action }) => {
    if(action === "CANCELLED") {
        await application.send(new Fulfilment.OrderCancel(sale.id));
    } else if(action === "CREATED") {
        await application.send(new Fulfilment.OrderCreate(transformSaleToShopfront(sale)));
    } else if(action === "UPDATED") {
        await application.send(new Fulfilment.OrderUpdate(transformSaleToShopfront(sale)));
    }
});
```

## Handling Events from Shopfront

In addition to your application updating Shopfront, Shopfront is able to send your application back updates on how
the sale has progressed after the user interacts with the sale.

Shopfront has the following statuses available for orders and typically moves through them from top to bottom:

- `PENDING_APPROVAL`: The order is pending approval, this status is typically only used when you've set 
`requiresApproval` to `true` on opt-in.
- `WAITING_FOR_PACKING`: The order has been approved and is waiting for the user to pack and match the order.
- `PACKED`: This occurs after the order is packed and is waiting to be shipped / picked up
- `COLLECTED`: The order has been collected / shipped
- `COMPLETED`: The order is completed and will receive no further events through the Fulfilment API

> Your application is able to move backwards or skip statuses if required, just send an `OrderUpdate` with the desired
> status.

For each step along the way there is a specific event, each of which sends your application a different set of data
and expects a different response (if any).

When you send updates to Shopfront, you only send a summary instead of the entire order, when Shopfront requires
the latest and entire order, it will send a `FULFILMENT_GET_ORDER` event which it expects the entire order to be
returned.

It also includes information on how products should be matched in Shopfront, for details on how this algorithm works,
check out the details on the [Fulfilment API page](/documentation/Embedded/Fulfilment#product-matching-algorithm). 

> Ideally you should attempt to respond to this method within one second for a good user experience. If your
> application takes longer than 30 seconds to respond, Shopfront will stop waiting and display an error to the user.
> In the future the wait time may be reduced.

**Sending Full Sale Details to Shopfront**

```javascript
application.addEventListener("FULFILMENT_GET_ORDER", async id => {
    // NOTE: id will be the ID that you provide originally
    const sale = await fetchFullSaleDetails(id);
    return transformFullSaleToShopfront(sale);
});
```

If you've enabled requiring order approval during the opt-in process, you'll want to listen to the
`FULFILMENT_ORDER_APPROVAL` event which will contain the ID of your order and whether it was approved or not.

**Receive Order Approval Status**

```javascript
application.addEventListener("FULFILMENT_ORDER_APPROVAL", async ({ id, approved }) => {
    if(approved) {
        // Make sure as part of this process you update the order's status as WAITING_FOR_PACKING when you next send
        // the order to Shopfront
        await markOrderAsApproved(id);
    } else {
        // Make sure this order isn't sent back to Shopfront in the future
        await cancelOrder(id);
    }
});
```

Once the order has been matched and packed by a user through Shopfront, your application will receive the 
`FULFILMENT_PROCESS_ORDER` event which contains the ID of your order and a 
[`Sale` object](/documentation/Embedded/Sale#sale) which you can use to either process the sale in Shopfront straight
away or hold for later processing.

> Sales through the fulfilment process need to be manually processed, this can either be done through the use of 
> GraphQL, or can be done directly through the Embedded API.

> Sales from the fulfilment API do not have a payment method attached, you'll need to manually mark how they're being
> paid for.

> If you elect to process the sale later, make sure you can still access the data required after the user potentially
> performs a page refresh.

**Process Orders After Packing**

```javascript
const transformFromShopfrontSale = (id, shopfront) => {
    return {
        // ... perform the general transformation from the provided Shopfront object
        items: shopfront.getProducts().map(product => ({
            shopfrontId: product.getId(),
            internalId : product.getMapped(), // Your original ID provided for the product
            quantity   : product.getQuantity(),
        })),
    };
};

application.addEventListener("FULFILMENT_PROCESS_ORDER", async ({ id, sale }) => {
    await processOrder(transformFromShopfrontSale(id, sale));

    // In this example we're going to process the order in Shopfront instantly, but you might want to wait
    // until the status changes to something like `COLLECTED` or `COMPLETED`
    sale.addPayment(
        new Sales.SalePayment("<< ID of payment method >>", sale.getSaleTotal())
    );

    const response = await sale.create(application);
    if(!response.success) {
        // Handle the error, probably best to inform your server so it can be manually processed
        console.error("Received an error while processing the sale", response.message);
    }
});
```

After the order has been either picked up by the customer (if it was a click & collect order), courier (if it's a
delivery platform) or shipped, Shopfront will trigger the `FULFILMENT_ORDER_COLLECTED` event with the ID of your order.

**Receive Order Collection Status**

```javascript
application.addEventListener("FULFILMENT_ORDER_COLLECTED", async (id) => {
    await markOrderAsCollected(id);
});
```

Finally, after the sale has been finished and is marked as completed by the user, Shopfront will trigger the 
`FULFILMENT_ORDER_COMPLETED` event with the ID of your order.

**Receive Order Completion Status**

```javascript
application.addEventListener("FULFILMENT_ORDER_COMPLETED", async (id) => {
    await markOrderAsCompleted(id);
});
```

If for some reason the store needs to cancel the order and the user hasn't been marked as `COMPLETED`, they can press
a cancel button in Shopfront's UI which will trigger the `FULFILMENT_VOID_ORDER` event which includes the ID of the 
order they are voiding.

It's up to you how you want to handle this, if it's not possible to void the order at that point you could display a
dialog box or a toast informing the user it isn't possible to redeem and then send the order back to Shopfront to add
to the Fulfilment list again.

**Receive Order Void Events**

```javascript
application.addEventListener("FULFILMENT_VOID_ORDER", async (id) => {
    if(determineIfOrderCanBeVoid(id)) {
        await voidOrder(id);
    } else {
        // Show a toast or dialog and send the order back to Shopfront
    }
});
```