OnNShiftCheckoutShippingOptionTax Hook
Shipping options returned by nShift carry a price but not always a tax rate. The nShift Checkout app needs a tax amount (or factor) per option so the delivery's shippingTax lands correctly when an option is selected.
The app resolves this per option from several sources. The OnNShiftCheckoutShippingOptionTax hook lets your rules compute custom shipping tax — for example a flat rate for a tax jurisdiction that nShift doesn't price, or a carrier-specific override.
When it fires
The hook fires once per call to getOptions (not once per option), right after options are fetched from nShift and before they're persisted on the session ticket. Firing once lets a listener that wants to price several options do it with a single registry or graph traversal.
The hook only fires when the caller passes a context to getOptions. The portal's Select shipping product action always supplies one; a bare module caller that passes context = nothing skips the hook entirely (see Shipping Module — Context).
Resolution order
For each option, the app picks the effective tax from the first source that yields a value:
- nShift's own
taxRate(when present on the option) — used as ashippingTaxFactor. - This hook — a listener emission for that
optionId. An emittedshippingTax(absolute) wins over an emittedshippingTaxFactor. - Line-derived default —
shippingTaxFactor = max(taxFactor)across the context's order lines. This matches European VAT markets where shipping inherits the highest line tax rate. Skipped when no line has a positivetaxFactor. - Otherwise — both
shippingTaxandshippingTaxFactorstaynothingand no shipping tax is set on the delivery.
Because nShift's own rate takes precedence, your hook only changes the outcome for options where nShift returned no taxRate.
Listening to the hook
Declare a rule with a param input that matches OnNShiftCheckoutShippingOptionTax. The input carries the same delivery-rooted context as OnNShiftCheckoutVariables plus the freshly fetched options. Only declare the fields your rule reads.
param input: {
hook: 'OnNShiftCheckoutShippingOptionTax'
delivery: {
deliveryId: uuid | nothing
deliveryAddress: {
countryCode: text | nothing
postalCode: text | nothing
state: 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 }
}
}
options: [{
optionId: uuid
carrierId: text
carrierProductId: text
name: text
price: number
nShiftTaxRate: number | nothing
}]
}The delivery branch is identical to the variables hook context — same caveats about deliveryId / order.orderId being uuid | nothing.
options[] field | Description |
|---|---|
optionId | The option's id. Echo it back on each effect so the app can map your value to the right option. |
carrierId | nShift carrier id — useful for carrier-specific rules. |
carrierProductId | nShift carrier product id within the carrier. |
name | Display name of the option. |
price | The option's price, in the request's currency — multiply by your factor to compute an amount. |
nShiftTaxRate | nShift's own tax rate for the option, when present. If set, it already takes precedence over your emission for that option. |
Emitting tax
Emit one effect per option you want to set tax for. Two effect types are accepted:
// Absolute amount in the request's currency
from {
effect = 'custom'
type = 'shippingTax'
optionId = <option.optionId>
value = 39
}
// Or a factor (e.g. 0.25 == 25%)
from {
effect = 'custom'
type = 'shippingTaxFactor'
optionId = <option.optionId>
value = 0.25
}| Field | Description |
|---|---|
effect | Must be 'custom'. |
type | 'shippingTax' (absolute amount) or 'shippingTaxFactor' (factor in [0, 1]). Other types are ignored. |
optionId | The optionId from the matching input.options[] entry. |
value | The amount or factor. Must be a number. |
For the same optionId, an emitted shippingTax wins over a shippingTaxFactor. Within a single type, the last listener emission wins. Options you don't emit for fall through to the line-derived default.
Example: flat US shipping tax
apps.nshift-checkout's default is max(line.taxFactor), appropriate for European VAT markets. For US destinations we want a flat factor instead, applied to every option.
import 'iterators'
param input: {
hook: 'OnNShiftCheckoutShippingOptionTax'
delivery: {
deliveryAddress: { countryCode: text | nothing }
}
options: [{
optionId: uuid
}]
}
let isUs = input.delivery.deliveryAddress.countryCode match
'US' |> true
|> false
from isUs match
true |>
input.options
select o => {
effect = 'custom'
type = 'shippingTaxFactor'
optionId = o.optionId
value = 0.08
}
buffer
|> []Example: carrier-specific absolute amount
Override a single carrier's options with a fixed tax amount, leaving the rest to fall through to the default.
import 'iterators'
param input: {
hook: 'OnNShiftCheckoutShippingOptionTax'
options: [{
optionId: uuid
carrierId: text
price: number
}]
}
from
input.options
where o => o.carrierId == 'postnord'
select o => {
effect = 'custom'
type = 'shippingTax'
optionId = o.optionId
value = o.price * 0.25
}
bufferSee Also
- nShift Checkout overview — Set up the app and configure channels.
- Shipping Module reference —
getOptions, thecontextparameter, and the resolvedshippingTax/shippingTaxFactorfields. OnNShiftCheckoutVariablesHook — Customize the request with merchant rules from the same context.- Custom Hooks — How custom hooks work.