# StylusOP V3 API wrapper for NodeJS (Official Module)

A NodeJS wrapper for connecting to the Stylus order processing system using an authenticated token provided by Stylus. You can view the full documentation of StylusOP Merchant APIs [here](https://stylusopapiv3-staging.stylusapparel.com/api-docs/)

## Installing

```bash 
$ npm install @stylusapparel/opv3-merchant-api-nodejs --save
```
OR 
```bash 
$ yarn add @stylusapparel/opv3-merchant-api-nodejs 
```
## Basic Usage

Create a client instance:
```js
const stylusWrapper = require("@stylusapparel/opv3-merchant-api-nodejs");
const stylusClient = stylusWrapper.createClient("YOUR_MERCHANT_ID", "YOUR_API_SECRET_TOKEN", 
    // client configurations
    { 
        "merchantName": "YOUR_MERCHANT_NAME" // Required, your Stylus-provided merchant name
        "sandbox": true, // Optional, enable only for development mode
        "apiVersion": "v3", // Optional, by default client connects to the latest API version
        "tokenType": "basic" // Optional, you can use JWT token or Basic Auth token. Allowed values: "basic", "jwt". Default is "basic" 
    }
);
```

Note: If a STYLUSOP_API_URL environment variable is set, it will be used as the API endpoint and the "sandbox" setting will be ignored.

## Authentication using Oauth2 (NEW)

Create an access token (JWT) using the Oauth2 flow. ** This is now the preferred method to authenticate your client instance. **
The access token is valid for 1 hour and can be used to make API requests to Stylus. After the token expires, you will need to create a new one by using the same method.

```js
// Promise-based approach
stylusClient.oauthToken()
    .then(accessToken => {
        console.log(`Access token: ${accessToken}`);
        console.log('Client is authenticated:', stylusClient.isAuthenticated());
    })   
    .catch((error) => {
        // Handle error here
        handleError(error);
    }); 

// Async/await approach
async function authenticate() {
    const accessToken = await stylusClient.oauthToken();
    return accessToken;
}
```

Once you have the access token, you can use it to make API requests to Stylus. To verify the token, you can use the `verifyToken` method:

```js
// Promise-based approach
stylusClient.verifyToken()
    .then(tokenVerified => { 
        console.log('Token is valid! \nToken verification response:', tokenVerified);
    })
    .catch(error => {
        console.error(`Error verifying the access token: ${error}`);
        // Handle error here
        handleError(error);
    })

// Async/await approach
async function verifyToken() {
    const verifySuccessResponse = await stylusClient.verifyToken();
    return verifySuccessResponse;
}
```


<!-- ## Validate Secret Token

Check the validity of your secret token before connecting to Stylus processing functions:

```js
// Promise-based approach
stylusClient.isTokenValid()
    .then((response) => {
        console.log("success", response);  // true
        // Continue...
    })
    .catch((error) => {
        console.log("error", error.errorCode, error.message);
    }); 

// Async/await approach
async function validateToken() {
    const response = await stylusClient.isTokenValid();
    return response;
}
``` -->

## Push Order

Push an order to Stylus OP API for production:

```js
// Promise-based usage
stylusClient.orders.create({
    ...orderObject,
})
    .then(response => {
        console.log("Order created:", response.orderKey);
    })
    .catch(handleError);

// Async/await usage
async function createOrder(orderObject) {
    try {
        const response = await stylusClient.orders.create(orderObject);
        console.log("Order created:", response.orderKey);
        return response;
    } catch (error) {
        handleError(error);
    }
}
```

More information on the format of order object can be view [here](https://stylusopapiv3-staging.stylusapparel.com/api-docs/)

Note: **Store the `orderKey` on your side for future reference when using the Stylus API**

## Fetch Orders

Fetch previous order details based on pagination. If no filter is provided, this will return all orders in descending order:

```js
// Promise-based approach
stylusClient.orders.list({
    offset: 0,
    limit: 15,
    sort: {
        field: "createdDate",
        order: "asc"
    }, 
    filters: {
        customerName: 'test',
        dateRange: [
            "2020-07-03T00:00:00.130Z", 
            "2020-08-03T00:00:00.130Z"
        ],
        priority: "normal",
        status: "printed,shipped"
    }
})
    .then((orders) => {
        console.log("orders", orders); // Array of order objects
    })
    .catch(handleError);


// Async/await approach
async function fetchOrders(options) {
    const orders = await stylusClient.orders.list(options);
    return orders;
}
```
## Fetch Order Details

Fetch the details of an order by its key:

```js
// Promise-based approach
stylusClient.orders.get("ORDER_KEY") // ORDER_KEY will be got from 'pushOrder' response
    .then( (orderDetails) => {
        console.log("order Detail",orderDetails); // Order detail object
    })
    .catch(handleError);

// Async/await approach
async function getOrderDetails(orderKey) {
    const orderDetails = await stylusClient.orders.get(orderKey); 
    return orderDetails;
}
```

## Cancel Order

Cancel an existing order that has already been pushed to Stylus for production:

```js
// Promise-based approach
stylusClient.orders.cancel("ORDER_KEY", {
    "items": [
        100001,
        100003
    ],
    "cancelReason": "Customer requested chargeback"
})
    .then((cancelStatus) => {
        console.log("cancel response: ", cancelStatus); // true, when cancellation success
    })
    .catch(handleError);

// Async/await approach
async function cancelOrder(orderKey, cancelOptions) {
    const cancelStatus = await stylusClient.orders.cancel(orderKey, cancelOptions);
    return cancelStatus;
}
```

More information on the format of order cancel object can be view [here](https://stylusopapiv3-staging.stylusapparel.com/api-docs/)

## Update Order

Update an existing order that has already been pushed to Stylus for production:

```js
// Promise-based approach
stylusClient.orders.update("ORDER_KEY", orderUpdateObject)
    .then((updateStatus) => {
        console.log("order updated status", updateStatus);
    })
    .catch(handleError);

// Async/await approach
async function updateOrder(orderKey, updateObject) {
    const updateStatus = await stylusClient.orders.update(orderKey, updateObject);
    return updateStatus;
}
```

More information on the format of order update object can be view [here](https://stylusopapiv3-staging.stylusapparel.com/api-docs/)

## Update/Push Order Item

Update existing or add a new line item to an existing order that has already been pushed to Stylus for production:

```js
// Promise-based approach
stylusClient.orders.updateItems("ORDER_KEY", orderItemsUpdateObject)
    .then((updateStatus) => {
        console.log("order item updated status", updateStatus);
    })
    .catch(handleError);

// Async/await approach
async function updateOrderItems(orderKey, orderItemsUpdateObject) {
    const updateStatus = await stylusClient.orders.updateItems(orderKey, orderItemsUpdateObject);
    return updateStatus;
}
```

More information on the format of order update object can be view [here](https://stylusopapiv3-staging.stylusapparel.com/api-docs/)



## Fetch Order Activities

Fetch the activities of an order by its key

```js
// Promise-based approach
stylusClient.orders.activities("ORDER_KEY")
    .then((activities) => {
        console.log("order activities", activities); // Array of order activity objects
    })
    .catch(handleError);

// Async/await approach
async function getOrderActivities(orderKey) {
    const activities = await stylusClient.orders.activities(orderKey);
    return activities;
}
```

## List Products

Perform full/partial text-based search for your products by title, name, or SKU code. If no search value is provided, it will fetch all products:

```js
// Promise-based approach
stylusClient.products.list({
    offset: 0,
    limit: 15,
    search: 'leggings',
    filters: {
        productIds: ['MERMTN1a70', 'MERMTN1a71'],
        enabled: true,
        decorationMethods: 'dtg',
    },
    sort: {
        field: 'createdAt',
        order: 'asc',
    }
})
    .then((products) => {
        console.log("products", products);
    })
    .catch(handleError);

// Async/await approach
async function listProducts(options) {
    const products = await stylusClient.products.list(options);
    return products;
}
```
## Get Product Details

Fetch the details of an product by its id:

```js
stylusClient.products.get("PRODUCT_ID") 
    .then( (product) => {
        console.log("Product", product); // Product details object
    })
    .catch(handleError);

// Async/await approach
async function getProductDetails(productId) {
    const product = await stylusClient.products.get(productId);
    return product;
}
```

## Get Product Pricing Details

Fetch the details of an product pricing by product id:

```js
stylusClient.products.pricing("PRODUCT_ID") 
    .then( (pricing) => {
        console.log("Product Pricing",pricing); // Product pricing object
    })
    .catch(handleError);

// Async/await approach
async function getProductPricing(productId) {
    const pricing = await stylusClient.products.pricing(productId);
    return pricing;
}
```

## Get Product variant

Fetch the details of an product variant by variantid or variant sku:

```js
stylusClient.products.variants.get("VARIANT_ID | VARIANT_SKU") 
    .then( (productVariant) => {
        console.log("Product Variant",productVariant); // Product variant object
    })
    .catch( (error) => {
        console.log("error",error.errorCode,error.message,error.status);
    });

// Async/await approach
async function getProductVariant(variantIdOrSku) {
    const productVariant = await stylusClient.products.variants.get(variantIdOrSku);
    return productVariant;
}
```

## Get Product Variant Pricing Details

Fetch the details of an product variant pricing by variantid or variant sku:

```js
stylusClient.products.variants.pricing("VARIANT_ID | VARIANT_SKU") 
    .then( (variantPricing) => {
        console.log("Product Variant Pricing", variantPricing); // Product variant pricing object
    })
    .catch(handleError);

// Async/await approach
async function getProductVariantPricing(variantIdOrSku) {
    const variantPricing = await stylusClient.products.variants.pricing(variantIdOrSku);
    return variantPricing;
}
```

## Get Inventory Details

Fetch the inventory details by inventory id or inventory sku:

```js
stylusClient.inventory.get("INVENTORY_ID | INVENTORY_SKU") 
    .then( (inventory) => {
        console.log("inventory",inventory); // Inventory detail object
    })
    .catch(handleError);

// Async/await approach
async function getInventoryDetails(inventoryIdOrSku) {
    const inventory = await stylusClient.inventory.get(inventoryIdOrSku);
    return inventory;
}
```

## List All Products Inventories

List inventories of all products:

```js
// Promise-based approach
stylusClient.inventory.products({
        offset:0, // optional,  offset value for pagination implementation
        limit:15,   // optional, if not provided fetch all matching result
        sort: {
            field: 'createdAt', // optional, Allowed values: 'createdAt' | 'updatedAt' 
            order: 'asc', // optional, Allowed values: 'asc' | 'desc'
        }
        
    })
    .then( (inventories) => {
        console.log("inventories",inventories); // Array of products
    })
    .catch(handleError);

// Async/await approach
async function listAllProductsInventories(options) {
    const inventories = await stylusClient.inventory.products(options);
    return inventories;
}
```

## List A Product Inventories

List inventories of a product by productId:

```js
// Promise-based approach
stylusClient.inventory.product("PRODUCT_ID")
    .then( (inventories) => {
        console.log("Product Inventories",inventories); // Array of products
    })
    .catch(handleError);

// Async/await approach 
async function listAProductInventories(productId) {
    const inventories = await stylusClient.inventory.product(productId);
    return inventories;
}
```
## List A Product Variant Inventory

List inventory of a product variant by variantid or variant sku:

```js
// Promise-based approach
stylusClient.inventory.variant("VARIANT_ID | VARIANT_SKU")
    .then( (inventory) => {
        console.log("Variant Inventory",inventory); // Array of products
    })
    .catch(handleError);

// Async/await approach
async function listAProductVariantInventory(variantIdOrSku) {
    const inventory = await stylusClient.inventory.variant(variantIdOrSku);
    return inventory;
}
```
## List A Product Variant Inventory By Facility Name

List inventory of a product in a facility variant by variantid or variant sku:

```js
// Promise-based approach
stylusClient.inventory.variant("FACILITY_NAME", "VARIANT_ID | VARIANT_SKU")
    .then( (inventory) => {
        console.log("Variant Inventory",inventory); // Array of products
    })
    .catch(handleError);

// Async/await approach
async function listAProductVariantInventory(facilityName, variantIdOrSku) {
    const inventory = await stylusClient.inventory.variant(facilityName, variantIdOrSku);
    return inventory;
}
```
## List All Shipments

List all shipments:

```js
// Promise-based approach
stylusClient.shipments.list({
        offset:0, // optional,  offset value for pagination implementation
        limit:15,   // optional, if not provided fetch first recent 100
        status: 'in_transit',   //optional, Allowed values: 'unknown', 'pre_transit', 'in_transit', 'out_for_delivery', 'delivered', 'available_for_pickup', 'return_to_sender', 'failure', 'cancelled', 'error', 'voided', 'voided_and_refunded', 'awaiting_payment', 'awaiting_shipment', 'shipped', 'on_hold', 'pending_fulfillment'
        trackingNumber: 123433, // fetch shipment based on a tracking  provided
        shipDate: '2024-04-29T18:46:44.000Z' | ['2024-01-24T18:46:44.000Z', '2024-042-27T16:22:44.000Z'], // optional, fetching shipments based on dates. Support as array of start and end date as well as a single date
        orderRefId: '2344sdc4342323sa43' // optional, fetch all shipments of an order
        sort: {
            field: 'createdAt', // optional, Allowed values: 'createdAt' | 'shipDate' 
            order: 'asc', // optional, Allowed values: 'asc' | 'desc'
        }
        
    })
    .then( (shipments) => {
        console.log("shipments",shipments); // Array of shipments
    })
    .catch(handleError);

// Async/await approach
async function listAllShipments(options) {
    const shipments = await stylusClient.shipments.list(options);
    return shipments;
}
```

## Get Shipment Details

Fetch the shipment details by shipment id:

```js
stylusClient.shipments.get("SHIPMENT_ID") 
    .then( (shipment) => {
        console.log("shipment",shipment); // shipment detail object
    })
    .catch(handleError);

// Async/await approach
async function getShipmentDetails(shipmentId) {
    const shipment = await stylusClient.shipments.get(shipmentId);
    return shipment;
}
```

## Get Shipment Status

Fetch the shipment status by shipment id:

```js
stylusClient.shipments.status("SHIPMENT_ID") 
    .then( (status) => {
        console.log("shipment status",status); 
    })
    .catch(handleError);

// Async/await approach
async function getShipmentStatus(shipmentId) {
    const status = await stylusClient.shipments.status(shipmentId);
    return status;
}
```

## COMMON ERROR CODES   

- **TOKEN_MISSING** - Secret token is missing in client configuration
- **MERCHANT_MISSING** - merchantName is missing in client configuration
- **VERSION_ISSUE** - The apiVersion provided in the client configuration is not valid/supported
- **TOKEN_EXPIRE** - Your token has expired and cannot access Stylus service(s)
- **TOKEN_INVALID** - Your token is not valid or you don't have access to Stylus service(s)
- **UNKNOWN_ERROR** - Error occurred due to internal connectivity issues on Stylus's side
- **PAGINATION_LIMIT_ERROR** - An unprocessable value was passed for 'limit' in the 'getOrders' function
- **ORDER_ID_INVALID** - An invalid order ID was passed to 'getOrder', 'updateOrder', or 'cancelOrder' function
- **ORDER_PAYLOAD_INVALID** - An invalid order payload was pushed to Stylus for order creation
- **ORDER_PRODUCT_INVALID** - Order payload contains item(s) that are not mapped to Stylus
- **ORDER_ITEM_PROPERTY_INVALID** - Order payload contains item(s) with properties unknown to Stylus
- **ORDER_DUPLICATE** - Same order was pushed again or order contains item numbers from a previous order
- **ORDER_STATUS_ISSUE** - Attempted to change the status of an order that is already in a non-updatable status (e.g., trying to cancel an order that is already printed/shipped)
- **PRODUCT_NOT_FOUND** - Product not found in our database or not found under the client's account
- **PRODUCT_VARIANT_NOT_FOUND** - Product variant not found in our database or not found under the client's account
- **INVENTORY_NOT_FOUND** - Inventory details not found
- **SHIPMENT_NOT_FOUND** - Shipment details not found