Storefront for partners
What’s a Storefront?
The Storefront is one of the most important entities in Headless. It’s a basic building block and the foundation were all the other commerce entities will be created. In fact, you need to have a storefront setup, because it’s a requirement to create products, add stock, set prices, configure the session, cart, orders, etc.
To illustrate this, let’s see a snippet of the GraphQL mutation used to create a new product. Take note of the first parameter of the mutation: the name of a Storefront (storefrontName
) is a requirement to create it.
mutation CreateProductMutation(
$storefrontName: String!
$data: ProductInputType!) {
createProduct(storefrontName: $storefrontName, data: $data) {
...
}
}
Anatomy of a Storefront
Here’s the Storefront and all its fields (in pseudo code) that are currently used.
enum StorefrontStatus {
CREATING = 'CREATING'
RUNNING = 'RUNNING'
PUBLISH = 'PUBLISH'
DRAFT = 'DRAFT'
}
enum AddOnTypes {
catalog = 'CATALOG',
createOrder = 'CREATE_ORDER',
createSession = 'CREATE_SESSION',
getProductBySKU = 'GET_PRODUCT_BY_SKU',
searchProduct = 'SEARCH_PRODUCT',
}
enum StockType {
INFINITE = 'INFINITE',
SELF_MANAGED = 'SELF_MANAGED',
REAL_TIME = 'REAL_TIME',
}
const CheckoutRules = {
cartConditionPendingMinAmount: {
quantity: number,
message: string,
isActive: boolean,
},
cartConditionPendingMaxAmount: {
quantity: number,
message: string,
isActive: boolean,
},
cartConditionPendingMinQty: {
quantity: number,
message: string,
isActive: boolean,
},
cartConditionPendingMaxQty: {
quantity: number,
message: string,
isActive: boolean,
},
cartConditionPendingMinAdditionalMeasurement: {
type: string,
unit: string,
quantity: number,
message: string,
isActive: boolean,
},
cartConditionPendingMaxAdditionalMeasurement: {
type: string,
unit: string,
quantity: number,
message: string,
isActive: boolean,
},
cartConditionPendingUserValidation: {
message: string,
sku: string[],
attribute: string,
accepted: boolean,
isActive: boolean,
},
cartWarnignCheckReturnables: {
message: string,
sku: string[],
attribute: string,
accepted: boolean,
isActive: boolean,
},
orderDailyLimit: {
quantity: number,
message: string,
isActive: boolean,
}
}
const **Storefront** = {
name: string
owner: string
status: StorefrontStatus
endpoints: [
{
url: string
status: StorefrontStatus
}]
templateID: string
configuration: {
useStores: boolean,
stock: {
type: StockType,
threshold: {
lowWarning: {
id: string
warningMessage: string
quantity: number
},
criticalLowWarning: {
id: string
warningMessage: string
quantity: number
}
},
timer: {
expirationMinutes: number
},
key: string
// @deprecated
enabled: boolean,
// @deprecated
stockThreshold: number
},
priceless: boolean,
promotions: {
enabled: boolean,
maximumActive: number
},
i18n: {
language: string,
currencyFormat: string,
currencySymbol: string,
currencyCode: string,
country: string,
timezone: string,
},
workflow: {
*workflowName*: {
name: string,
channel: string,
channelUid: string,
ng: boolean,
stephook: {
*stephookName*: string
},
jwtToken: string,
body: JSON,
}
},
checkoutRulesByType:[{
type: string,
checkoutRules: CheckoutRules,
}],
checkoutRules: CheckoutRules,
sessionTtl: number,
groupers: {
cart: {
active: boolean,
session: string[],
product: string[],
},
order: {
active: boolean,
session: string[],
product: string[],
}
},
splitters: {
order: {
active: boolean,
maxProduct: number,
}
}
},
addons: {
instead: [AddOnTypes],
after: [AddOnTypes]
},
showRecommendations: boolean
}
Top level properties
Property name | Type | Description | Req. | Default value | Notes |
---|---|---|---|---|---|
name | string | A unique non-null name that identifies the Storefront. This value is used in almost all the Headless CRUD operations. | ✅ | N/A | name is converted to lowercase before use. |
owner | string | Who owns the Storefront. | ✅ | N/A | name and owner can be different |
status | enum | The current status of the Storefront as an enum value. | CREATING | The enum values can be: CREATING, RUNNING, PUBLISH and DRAFT |
configuration
properties
configuration
propertiesThe configuration contains a plethora of options used to establish how the Storefront is going to work. From stock management and price, to promotions, internationalization and business rules. This is a big object with several nested properties (with their own properties), so the definitions will be broken down for better understanding.
useStores
useStores
Boolean value declaring if the stock and catalog is distributed by store.
💡 The stores features is not fully implemented yetstock
stock
Options to activate and configures the handling of stock
Property name | Type | Description |
---|---|---|
type | enum | An enum value that indicates the stock type that is being used. Current values: INFINITE, SELF_MANAGED and REAL_TIME. |
threshold | object | Has warning messages for threshold values of stock. |
timer | object | Indicates how much time in minutes the stock will be reserved when added to the cart. |
key | string | Key used to consume the stock from the database. This key should be the same that has been loaded to the stock collection (stock.key). |
boolean | Deprecated: use type instead. Indicate if stock should be checked during product catalog/search | |
number | Deprecated: use threshold instead. The number of items that should be retained as stock reserve when the catalog is requested. |
priceless
priceless
A boolean property indicating whether the Storefront is priceless (this means that the Storefront doesn’t handle prices). Its default value is false
.
promotions
promotions
Options to enable an configure promotions in this Storefront:
Property name | Type | Default value | Description |
---|---|---|---|
enabled | boolean | false | Indicate if the promotions should be enabled and handled in this Storefront. |
maximumActive | number | Indicate the maximum number of active promotions that a cart or order can have for the Storefront. The restriction only applies if the promotions.enable field is true |
i18n
i18n
The i18n
section is intended to contain the configuration related with the internationalization of the Storefront instance. Some fields should be inferred given the country.
Property name | Type | Description |
---|---|---|
language | string | Declare the language used by the Storefront instance. This value should be used for text i18n in the front end. The format for the language must be declared as xx_YY where, xx is the ISO 639 language codes and YY is the ISO 3166 country codes. Example: es_MX should be the language value used for Mexican Spanish. |
currencyFormat | string | The currency format should be declared using locales. e.g to declare the currency format for Mexico, the value should be es-MX. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString#using_locales for more information. |
currencySymbol | string | The currency symbol should comply with the https://www.newbridgefx.com/currency-codes-symbols/ standard. |
currencyCode | string | The currency code should comply with the https://www.newbridgefx.com/currency-codes-symbols/ standard |
country | string | The country should comply with the https://www.google.com/search?hl=en&q=iso country codes |
timezone | string | The timezone used for date operations in the storefront. |
workflow
workflow
This section will contain the data required for workflow interaction during the run-time execution. The structure of this section is of a group of nested, mapped objects. And the key for accessing the workflow
, is its name. That way one or more workflows can be set up in the Storefront.
For example, the following snippet shows two different workflows inside the workflow
property, both with different names (keys) but the same structure:
workflow: {
*bimbo-br-whatsapp*: {
name": "bimbo-br-whatsapp",
channel: "botslug-wa",
// Other fields
},
*bimbo-br-messenger*: {
name: "bimbo-br-messenger",
channel: "botslug-ms",
// Other fields
}
}
Property name | Type | Description |
---|---|---|
name | string | Name of the workflow where this Storefront would live |
channel | string | Channel name where the workflow is being executed. eg: WhatsApp, Messenger |
channelUid | string | The identifier of the channel, in the case of WhatsApp, this value should be the WA phone number |
ng | boolean | Indicates whether this workflow is Studio NG (true ) or Studio OG (false ) |
stephook | object | An object with mapped string values. Its use depends on the value of the ng property. |
jwtToken | string | JWT token required to consume the hook |
body | object | An object with custom properties that will be passed as payload when the Studio NG/OG trigger is called. |
checkoutRulesByType
checkoutRulesByType
An array of checkoutRules
that are keyed by type. This is the way to configure checkout rules per customer, using the type
property to match with the customer type. In case the key doesn’t match any customer, or the checkoutRulesByType
is empty, then the checkout rules applied will be the ones in the configuration.checkoutRules
property.
Property name | Type | Description |
---|---|---|
type | string | The key that will be matched with the customer type. |
checkoutRule | object | The group of checkout rules that will be applied (if possible) to the customer with the same type as the type used. This group or rules are the same as the ones in the configuration.checkoutRules property. |
checkoutRules
checkoutRules
All the values that should be applied for the checkout rules by default. When the Session is created this values should be added to the checkoutRules
object inside the Session.
These values may be overridden during the workflow execution given that are modules that handle these values. That being said, these values are the default for any user of the Storefront workflow.
Property name | Type | Description |
---|---|---|
cartConditionPendingMinAmount | object | Value of the default minimum amount in currency that a user is allowed to spend |
cartConditionPendingMaxAmount | object | Value of the default maximum amount in currency that a user is allowed to spend |
cartConditionPendingMinQty | object | Value of the default minimum quantity of items that a user should be allowed to buy |
cartConditionPendingMaxQty | object | Value of the default maximum quantity of items that a user should be allowed to buy |
cartConditionPendingMinAdditionalMeasurement | object | Value of the default minimum in additional measures of items that a user should be allowed to buy |
cartConditionPendingMaxAdditionalMeasurement | object | Value of the default maximum in additional measures of items that a user should be allowed to buy |
cartConditionPendingUserValidation | object | Rule that verifies that the user accepted a condition/validation (e.g. he es 18 years old if some products are alcoholic beverages) |
cartWarnignCheckReturnables | object | Verifies that the user accepted to return product packages (e.g. plastic bottles for some soft drinks). |
orderDailyLimit | object | Rules how many orders can be created per day for the given customer |
sessionTtl
sessionTtl
Indicates the time to live in minutes of the Sessions created for this Storefront. This property is numeric and its default value is 1,440 (number of minutes in one day).
groupers
⚠️ Groupers and splitters **must not be active at the same time**. If you try to create or modify a storefront with both `groupers` and `splitters` activated, an error will be returned.
ℹ️ Groupers is an optional property.
groupers
This property enables the grouping of multiple carts and/or orders, and configure how this grouping will be made. Currently there are two properties inside groupers
that have the same fields, but different purpose:
**cart**
: Enables and configures multiple groups in a cart**order**
: Enables and configures multiple groups in an order
For each one of these two fields, the following options are available
Property name | Type | Description |
---|---|---|
active | boolean | Enables or disables the grouper |
session | string[] | A list of properties from the session that indicates what labels will be used in the groupBy field to identify a particular cart group. Examples: “paymentMethod" |
product | string[] | A list of properties from the product that indicates what labels will be used in the groupBy field to identify a particular cart group. Examples: "brand" , "promotion" |
{
"groupers": {
"cart": {
"active": true,
"session": ["paymentMethod"],
"product": ["brand", "promotion"]
},
"order": {
"active": true,
"session": ["paymentMethod"],
"product": ["brand", "promotion"]
}
}
}
splitters
⚠️ Groupers and splitters **must not be active at the same time**. If you try to create or modify a storefront with both `groupers` and `splitters` activated, an error will be returned.
ℹ️ Splitters is an optional property.
splitters
This property enables the spliting of multiple orders, and configure how this splitting will be made. Currently there is one property inside splitters
:
**order**
: Enables and configures multiple splitters in an order. Has the following fields.
Property name | Type | Description |
---|---|---|
active | boolean | Enables or disables the grouper |
maxProduct | number | Indicates the maximum numbers of products that an order can have in each splitter. Example: if maxProduct = 15 , if we create an order with 20 products, 15 will be in one splitter and 5 in another. |
{
"splitters": {
"order": {
"active": true,
"maxProduct": 15
}
}
}
showRecommendations
showRecommendations
Indicates if the storefront has the show recommendations option available. Consumers (like Cinnamon) will query this property to decide whether or not call the frequentlyBoughtTogether
query to get recommended products.
addons
properties
addons
propertiesThe addons
property contains the list of the add-ons that should be called when certain operations are done with entities created in the Storefront. It may be undefined or if it’s defined it should contain one or the two of the following properties:
instead
: If this property exists, it will contain an array with all the operations whose return value will be replaced by a call to a specific, valid add-on that must exist in the Storefront. While the end result will be the same, internally, the add-on system will make a call to an external service (configured in the corresponding add-on) and return whatever that service returned.after
: If this property exists, it will contain an array with all the operations that will trigger a call to a specific, valid add-on that must exist in the Storefront. The add-on system will make a call to an external service (configured in the corresponding add-on) that will be called after the operation (and will not replace its return value).
It’s valid for an operation to be in both the instead
and the after
arrays. The operations that currently support add-ons, and that can be defined as the values of the addons
property, are the following:
enum AddOnTypes {
catalog = 'CATALOG',
createOrder = 'CREATE_ORDER',
createSession = 'CREATE_SESSION',
getProductBySKU = 'GET_PRODUCT_BY_SKU',
searchProduct = 'SEARCH_PRODUCT',
}
An example of the addons
property set to some valid values is:
addons: {
instead: [
"CREATE_SESSION",
"CATALOG"
],
after: [
"CREATE_SESSION",
"CREATE_ORDER"
]
}
Storefront Operations
This document will guide you through the basic CRUD operations for the Storefront. There are two options to work with Storefronts: The Admin/User API and the Commerce Manager.
Admin/User API
Headless uses GraphQL as the query and data manipulation language, so all the examples will be using GraphQL queries and JSON variables. The operations that you can execute, depend of the workspace or server where you are working:
- In the Admin API, you can Create, Update, Remove and Query a Storefront (or Storefronts)
- In the User API, you can only Query for a Storefronts
Admin Workspace
Create a Storefront
To create a new Storefront, use the following GraphQL mutation:
mutation CreateStorefrontMutation($data: StorefrontInput!) {
createStorefront(data: $data) {
id
name
status
owner
configuration {
useStores
priceless
stock {
enabled
}
}
}
}
Here’s an example of the variables as JSON passed to the previous mutation:
{
"data": {
"name": "apollo-storefront-09",
"status": "CREATING",
"owner": "apollo-storefront-09",
"configuration": {
"useStores": true,
"priceless": false,
"stock": {
"enabled": true,
"stockThreshold": 100,
"key": "YALO"
},
"promotions": {
"enabled": false
"maximumActive": 20
},
"i18n": {
"language": "es_MX",
"currencyFormat": "es-MX",
"currencySymbol": "$",
"currencyCode": "MXN",
"country": "MX"
},
"workflow": {
"whatsapp": {
"name": "whatsapp",
"channel": "botslug-wa",
"channelUid": "5255555555555",
"ng": true,
"stephook": {
"forgottenCart": "786876fdsf87678sd"
},
"jwtToken": "qwerty1234uiop.qwerty1234uiop.qwerty1234uiop",
"body": {
"message": "GREAT SUCCESS!"
}
}
},
"checkoutRules": {
"cartConditionPendingMinAmount": {
"quantity": 10,
"message": "you should met the min amount of",
"isActive": true
},
"cartConditionPendingMaxAmount": {
"quantity": 20,
"message": "you should met the max amount of",
"isActive": false
},
"cartConditionPendingMinQty": {
"quantity": 30,
"message": "you should met the min quantity of",
"isActive": true
},
"cartConditionPendingMaxQty": {
"quantity": 20,
"message": "you should met the max quantity of",
"isActive": false
},
"cartConditionPendingUserValidation": {
"message": "Are you over 18 years old?",
"sku": [
"YALO000001",
"YALO000006"
],
"attribute": "alcohol",
"isActive": true
},
"cartWarningCheckReturnables": {
"message": "You should return your plastic bottles",
"sku": [
"YALO000010",
"YALO000011"
],
"attribute": "returnable",
"isActive": false
}
},
"sessionTtl": 1440
},
"addons": {
"instead": [
"CREATE_SESSION",
"CATALOG"
],
"after": [
"CREATE_SESSION",
"CREATE_ORDER"
]
}
}
}
Example of a returned document that result of a successful Storefront creation:
{
"data": {
"createStorefront": {
**"id": "61ec75d4c969e39e82eca95a",**
"name": "apollo-storefront-09",
"status": "CREATING",
"owner": "apollo-storefront-09",
"configuration": {
"useStores": true,
"priceless": false,
"stock": {
"enabled": true
},
"workflow": {
"whatsapp": {
"name": "whatsapp",
"channel": "botslug-wa",
"channelUid": "5255555555555",
"ng": true,
"stephook": {
"forgottenCart": "786876fdsf87678sd"
},
"jwtToken": "qwerty1234uiop.qwerty1234uiop.qwerty1234uiop",
"body": {
"message": "GREAT SUCCESS!"
}
}
}
}
}
}
}
Update a Storefront
To update the Storefront, you can use the following mutation:
mutation UpdateStorefrontMutation($id: ID!, $data: UpdateStorefrontInput!) {
updateStorefront(id: $id, data: $data) {
id
name
status
owner
configuration {
useStores
priceless
promotions {
enabled
}
workflow
sessionTtl
}
}
}
For this example, the following JSON will be passed as GraphQL variables. You pass the id
of the Storefront that you need to update, and also a data
property with all the information that you need to update.
In this case, there’s a mixture of adding and updating. Some properties (like priceless
and promotions
) are being modified, and also a new workflow
(with the messenger
key) is being added:
{
"id": "61ec75d4c969e39e82eca95a",
"data": {
"configuration": {
"priceless": true,
"promotions": {
"enabled": true
},
"workflow": {
"messenger": {
"name": "messenger",
"channel": "botslug-ms",
"channelUid": "5255555555555",
"ng": true,
"stephook": {
"createOrder": "786876fdsf87678sd"
},
"jwtToken": "qwerty1234uiop.qwerty1234uiop.qwerty1234uiop"
}
},
"sessionTtl": 2880
}
}
}
This is the response JSON document. The workflow
now has two entries (whatsapp
and messenger
) and also the changed properties have their new values:
{
"data": {
"updateStorefront": {
"id": "61ec75d4c969e39e82eca95a",
"name": "apollo-storefront-09",
"status": "CREATING",
"owner": "apollo-storefront-09",
"configuration": {
"useStores": true,
"priceless": true,
"promotions": {
"enabled": true
},
"workflow": {
"whatsapp": {
"name": "whatsapp",
"channel": "botslug-wa",
"channelUid": "5255555555555",
"ng": true,
"stephook": {
"forgottenCart": "786876fdsf87678sd"
},
"jwtToken": "qwerty1234uiop.qwerty1234uiop.qwerty1234uiop",
"body": {
"message": "GREAT SUCCESS!"
}
},
"messenger": {
"name": "messenger",
"channel": "botslug-ms",
"channelUid": "5255555555555",
"ng": true,
"stephook": {
"createOrder": "786876fdsf87678sd"
},
"jwtToken": "qwerty1234uiop.qwerty1234uiop.qwerty1234uiop"
}
},
"sessionTtl": 2880
}
}
}
}
Remove a Storefront
⚠️ Be careful when removing a **Storefront**. If it has dependencies (like sessions, customers, products, or other entities that were created using the **Storefront** `name`) trying to access some of those dependencies will result in an error.
The following mutation allows you to remove a Storefront:
mutation RemoveStorefrontMutation($id: ID!) {
removeStorefront(id: $id) {
id
name
status
owner
}
}
You pass the id
of the Storefront you want to remove as variable:
{
"id": "61ec75a2c969e39e82eca952"
}
Query a Storefront
To get the information of a Storefront, you only indicate the fields that you want to query:
query StorefrontQuery($id: ID!) {
storefront(id: $id) {
id
name
status
owner
configuration {
useStores
}
}
}
And pass the id
of the Storefront as variable:
{
"id": "61ec75d4c969e39e82eca95a"
}
Example of the JSON document returned:
{
"data": {
"storefront": {
"id": "61ec75d4c969e39e82eca95a",
"name": "apollo-storefront-09",
"status": "CREATING",
"owner": "apollo-storefront-09",
"configuration": {
"useStores": true
}
}
}
}
Query all the Storefronts
To get all the Storefronts stored, use the following GraphQL query:
query StorefrontsQuery(
$pagination: PaginationInput
$filter: FilterStorefrontInput) {
storefronts(pagination: $pagination, filter: $filter) {
id
name
status
owner
configuration {
useStores
}
}
}
Both the pagination
and filter
parameters are optional. The first one is used when there’s a lot of documents stored and you want to select a few, for performance reasons. The filter
is used to pass a criteria search, based on one of more properties of the Storefront. Here’s an example of variables passed to the previous query that uses both parameters:
{
"pagination": {
"pageNumber": 1,
"pageSize": 10
},
"filter": {
"status": "CREATING"
}
}
In this case, we are requesting the first group of 10 Storefronts stored, and also we want to filter the query and only bring the Storefronts with status of CREATING
. Here’s an example of the returned JSON document for the previous query:
{
"data": {
"storefronts": [
{
"id": "61ec75d4c969e39e82eca95a",
"name": "apollo-storefront-09",
"status": "CREATING",
"owner": "apollo-storefront-09",
"configuration": {
"useStores": true
}
}
]
}
}
User Workspace
Query a Storefront
In the User Workspace, you can query for a Storefront by id
or name
, but not both. Passing both parameters (or none) as variables results in an error. This is the query used:
query StorefrontQuery($id: ID, $name: String) {
storefront(id: $id, name: $name) {
id
name
status
owner
}
}
And you can pass either the id
of the Storefront you are looking for:
{
"id": "61ec75d4c969e39e82eca95a"
}
Or the Storefront name
:
{
"name": "apollo-storefront-09"
}
Example of the return value of the query using either of the previous variables:
{
"data": {
"storefront": {
"id": "61ec75d4c969e39e82eca95a",
"name": "apollo-storefront-09",
"status": "CREATING",
"owner": "apollo-storefront-09",
"configuration": {
"useStores": true
}
}
}
}
Commerce Manager
Coming soon!🍿
Updated about 1 year ago