Order Processing
About
Simply put an Order is a collection of line items. A line item is a product times quantity. Order processing revolves around figuring available shipping options for the set of line items, pricing and taxes, availability and once the shipping has been chosen and payment made inserting the order into the merchant’s store.
Price discrepancies
It might occur that the product on Katalys’s side was priced differently than the same product in the merchant’s store. This is usually due to legal agreements between Katalys and the merchant. To account for possible differences the integration should ensure line items prices of the inserted order match those coming from Katalys GraphQL API order.lineItems.price
.
This can usually be achieved by changing line items on the inserted order. Options vary between the price or applying discounts on individual line items.
Firing order related directives from the UI
Preconditions
You successfully imported a product - Importing Products
Setup a test payment gateway to accept a test credit card
Click Settings - left menu, bottom
Click Payments & Payouts
Click Manual Integration
Input Live API Keys
Reach out to Katalys to get them
Steps
Create a shop
visit Products - left menu
Chose a product in the list, click three dots - right side of the row
Click create Shop
Preview the shop
visit Shops - left menu
click on a shop
click “Preview“ - top right
Performing a purchase
Click on BuyNow and follow through till the end
Test card - repeat “42” until all fields are filled.
This will results in:4242 4242 4242 4242
04/24
242
Expect an order to in the Orders list - left menu
Expect directives being fired and recorded in the Store’s Log tab
Settings, Stores, Store, Log
Servicing the update_available_shipping_rates
directive
When an order on Katalys is created or updated, Katalys might issue this directive to your integration.
{
"directives": [
{
"args": {
"order_id": "91997424-0c62-4525-8043-f00de23cb91c"
},
"directive": "update_available_shipping_rates",
"id": "45a42385-8ebd-45bb-9ac8-c7365aa157ac"
}
]
}
Once your integration has calculated shipping rates for the given order, it should use a mutation to update the order with this new information:
mutation UpdateShippingRatesExample($id: ID!, $input: OrderInput!) {
updateOrder(id: $id, input: $input) {
id
shippingRates {
handle
amount
title
}
}
}
{
"id": "235c9162-80c2-4fd4-9eb8-e398ed130329",
"input": {
"shippingRates": [
{
"handle": "economy-international-4.50",
"title": "Economy International",
"amount": 450
},
{
"handle": "express-international-15.0",
"title": "Express International",
"amount": 1500
}
]
}
}
Each shipping rate is specified with the following fields:
handle
is internal to you, the integrator. In the above example, the format is borrowed from Shopify and includes the amount (which makes it work like a cache key, smart!)title
is the title that Katalys will display to the buyer during the checkout process (among other places)amount
is the decimal amount represented in cents (as an integer). You should assume that the currency here is the same as the one used on the order (but let’s talk about this soon to see if the assumption is sane).
When shipping rates for an order can’t be calculated, Katalys expects the integration to set the shippingRatesError
field instead. For example, let’s consider the case where an order can’t be shipped to a given country:
{
"id": "235c9162-80c2-4fd4-9eb8-e398ed130329",
"input": {
"shippingRatesError": {
"code": "no_shipping_rates",
"userMessage": "Unfortunately, we're unable to ship to your address.",
"details": "(This is optional.)"
}
}
}
"no_shipping_rates"
is the only recognized error code at the moment of writing. However, your integration is free to set custom error codes for other purposes.
Servicing the update_tax_amounts
directive
{
"directives": [
{
"args": {
"order_id": "91997424-0c62-4525-8043-f00de23cb91c"
},
"directive": "update_tax_amounts",
"id": "6e1efe64-2f37-40c4-8198-c8b7827dba5d"
}
]
}
As the name suggests, this directive expects your integration to update tax amounts for a given order, according to the amounts calculated by the Merchant’s e-commerce platform.
There are two fields to update:
Mandatory: the
totalTax
field on Order should contain the total tax amount for the order, calculated based on the line items and thetotalShipping
amount (where applicable). This field is mandatory because it precludes the calculation of the order grand total. If the tax amount is zero (e.g. due to tax exemption) a value of0
must be specified. A value ofnull
signals that the tax still hasn’t been calculated, in which case the integration must indicate a failure in the response (see Forming a response below).Optional: the
tax
field on each LineItem should contain the tax amount for that specific line item. This field is for evidence only and does not directly affect the total tax amount on the order (totalTax
should to be independently set as described in the previous step)
mutation UpdateTaxAmountsExample($id: ID!, $input: OrderInput!) {
updateOrder(id: $id, input: $input) {
totalTax
lineItems {
tax
}
}
}
{
"id": "235c9162-80c2-4fd4-9eb8-e398ed130329",
"input": {
"totalTax": 250,
"lineItems": [
{
"id": "4af7128c-7f22-4efb-9f85-25bbc23d82e2",
"tax": 150
}
]
}
}
💰 totalTax
and tax
, like other money amounts, must be represented in cents. For example, the amount 12.99 is represented as the integer value 1299
.
🧾 usProductTaxCode
- For products that are either exempt from sales tax in some US jurisdictions or are taxed at reduced rates, a Product might have a tax code stored in the usProductTaxCode
field. The codes are specified by TaxJar. The product can be accessed through a LineItem, using product
and variant
fields.
Servicing the update_availability
directive
{
"directives": [
{
"args": {
"order_id": "91997424-0c62-4525-8043-f00de23cb91c"
},
"directive": "update_availability",
"id": "ee4d346f-de86-4285-934c-a9f1b9c5c0ed"
}
]
}
During checkout, there comes a time when the system must check inventory levels to determine if enough items are in stock to satisfy the order.
Katalys will issue this directive to the integration, which in turn should update each line item to signal its availability (or lack thereof).
The checkout process will only be allowed to continue if all line items are available.
mutation UpdateAvailabilityExample($id: ID!, $input: OrderInput!) {
updateOrder(id: $id, input: $input) {
id
lineItems {
id
available
}
}
}
{
"id": "235c9162-80c2-4fd4-9eb8-e398ed130329",
"input": {
"lineItems": [
{
"id": "711d0515-0dca-445d-b2a2-1478547763f9",
"available": false
},
{
"id": "fe46588f-57ea-4b5b-9970-25687b59286b",
"available": false
}
]
}
}
💼 An integration should consult the merchant’s inventory levels. It should also check whether a product is allowed to still be sold when stock reaches zero (in which case the line item should be marked as available).
Servicing the complete_order
directive
This directive is sent when an Katalys order’s paymentStatus
changes to paid
.
To service this directive the integration must update the Katalys order’s fulfilmentStatus
to "FULFILLED"
and set the externalId
value to the order id used by the eCommerce stack. Example: gid://shopify/Order/5113822838965
- Shopify order id
mutation CompleteOrder($id: ID!, $input: OrderInput!) {
updateOrder(id: $id, input: $input) {
id
fulfillmentStatus
}
}
{
"id": "235c9162-80c2-4fd4-9eb8-e398ed130329",
"input": {
"fulfillmentStatus": "FULFILLED"
"externalId": "gid://shopify/Order/5113822838965"
}
}
This is the time when your integration should insert an order into the Merchant’s store.
🧠 Note that Katalys could issue this directive more than once for the same order, so it must be serviced in an idempotent way – i.e., running it multiple times with the same order ID only results in a single order in the Merchant’s store.
Your integration might want to store additional metadata on the order through the Katalys GraphQL API. Read more about the externalData
field.
Forming a response
Once your integration is done servicing the directives and updating the order, it should form a response to the initial POST request that issued the directives. The response should have a 200 status if everything went OK and the directives could be successfully performed. In every other case the response should be non-200. We can decide on the format of the response, but for now this we recommend this (a list of objects, one for each received directive):