Skip to content

OnNShiftCheckoutVariables Hook

nShift Checkout configurations are driven by an open set of variables — provider-specific key/value pairs interpreted by your checkout configuration on nShift's side. Every nShift account uses a different set: fromwarehouse, vipCustomer, isSplit, and so on.

Rather than hard-coding any of this, the nShift Checkout app fires the OnNShiftCheckoutVariables hook every time it builds a request to nShift. Add merchant rules to that hook to feed in whatever variables your configuration expects.

The hook fires on every shipping-product lookup the app makes for a delivery — including the Select shipping product action in the order view. Write your rules once and they apply to every lookup.

The resolved variables are stored on the nShiftCheckoutSession ticket actor that backs the lookup, so you can inspect "what variables were in play when this session was created" from the portal — useful for debugging merchant-rule behaviour.

Listening to the hook

Declare a rule with a param input that matches OnNShiftCheckoutVariables. The hook is fired with a delivery-rooted outline of the data the app already loaded — delivery + its order + its order lines, with dynamic on each. Only declare the fields your rule reads; Filtrera's type system narrows hook matching to rules whose declared shape is satisfied.

filtrera
param input: {
  hook: 'OnNShiftCheckoutVariables'
  delivery: {
    deliveryId: uuid | nothing
    deliveryAddress: {
      name: text | nothing
      careOf: text | nothing
      attention: text | nothing
      addressLine1: text | nothing
      addressLine2: text | nothing
      city: text | nothing
      state: text | nothing
      postalCode: text | nothing
      countryCode: text | nothing
      email: text | nothing
      phone: text | nothing
    }
    dynamic: { text -> value }
    lines: [{
      orderLineId: uuid | nothing
      productNumber: text | nothing
      quantity: number
      dynamic: { text -> value }
      taxFactor: number | nothing
    }]
    order: {
      orderId: uuid | nothing
      channelKey: text
      currencyCode: text
      locale: text | nothing
      dynamic: { text -> value }
    }
  }
}
FieldDescription
delivery.deliveryIdThe delivery being quoted, or nothing in a cart-rooted (pre-order) flow.
delivery.deliveryAddressThe destination address (graph Address shape).
delivery.dynamicDynamic fields on the delivery (e.g. inventoryKey, route tags). Read whatever your apps have stored there.
delivery.linesThe delivery's order lines, carrying Hantera-native system fields only. Read product attributes (weight, dimensions, item class) from line.dynamic after their owning app has projected them there.
delivery.lines[].taxFactorThe line's tax factor (e.g. 0.25), when known. Drives the default shipping-tax behaviour — see OnNShiftCheckoutShippingOptionTax.
delivery.order.orderIdThe order the delivery belongs to, or nothing in a cart-rooted (pre-order) flow.
delivery.order.channelKeyChannel of the order.
delivery.order.currencyCodeCurrency of the order.
delivery.order.localeLocale of the order (may be nothing).
delivery.order.dynamicDynamic fields on the order.

Ids can be nothing

Both delivery.deliveryId and delivery.order.orderId are uuid | nothing. A portal-rooted lookup (the Select shipping product action) has both ids; a cart-rooted (pre-order) lookup has neither, because no delivery or order exists yet. Guard for nothing before querying the graph from these ids.

Emitting a variable

A rule contributes one variable per custom/variable effect it emits:

filtrera
from {
  effect = 'custom'
  type   = 'variable'
  key    = 'fromwarehouse'
  value  = 'HQ'
}
FieldDescription
effectMust be 'custom'.
typeMust be 'variable'. The hook helper only collects variables; other effects are ignored.
keyVariable key as defined by your nShift checkout configuration.
valueVariable value. Usually text, but any JSON-serializable Filtrera value works.

If multiple rules contribute the same key, the last effect emitted wins. This makes it easy to layer a default rule with a more specific override. The app also pre-seeds channelKey from the order, which your rules can override.

Example: per-channel warehouse

A merchant operating two storefronts wants nShift to ship from a different warehouse per channel.

filtrera
param input: {
  hook: 'OnNShiftCheckoutVariables'
  delivery: {
    order: { channelKey: text }
  }
}

let warehouseByChannel = {
  'retail-se' -> 'WH-SE'
  'retail-no' -> 'WH-NO'
}

from warehouseByChannel->input.delivery.order.channelKey match
  (warehouse: text) |> {
    effect = 'custom'
    type   = 'variable'
    key    = 'fromwarehouse'
    value  = warehouse
  }

Example: VIP customers get free-shipping variants

Assume the tenant has added a custom customer edge from order (a common pattern). Read the customer's segment and set a vipCustomer variable that your nShift configuration uses to switch on free-shipping carrier products.

filtrera
param input: {
  hook: 'OnNShiftCheckoutVariables'
  delivery: {
    order: { orderId: uuid | nothing }
  }
}

from input.delivery.order.orderId match
  (orderId: uuid) |>
    let q =
      query orders(orderId)
      navigate customer(customerSegment)
      filter $'orderId == {orderId}'
    let segment = q first match
      { customer: { customerSegment: text } } |> q first.customer.customerSegment
    from segment match
      'vip' |> {
        effect = 'custom'
        type   = 'variable'
        key    = 'vipCustomer'
        value  = '1'
      }

Example: warehouse from inventory routing

When the inventory-routing app splits an order across warehouses, the delivery's inventoryKey dynamic field carries the actual fulfillment warehouse. Forward it to nShift as fromwarehouse — no graph query needed since delivery.dynamic is already in the hook input.

filtrera
param input: {
  hook: 'OnNShiftCheckoutVariables'
  delivery: {
    dynamic: { text -> value }
  }
}

from input.delivery.dynamic->'inventoryKey' match
  (k: text) |> {
    effect = 'custom'
    type   = 'variable'
    key    = 'fromwarehouse'
    value  = k
  }

See Also

© 2024 Hantera AB. All rights reserved.