nShift Checkout fulfillment provider plugin for Medusa V2
nShift Checkout fulfillment provider for Medusa v2.
Brings nShift's delivery options into a Medusa storefront: real carrier options, real prices for the customer's address and basket, and a partial shipment created in nShift when the order is fulfilled.
Register the provider on the Fulfillment Module, and register the plugin so its build output is loaded:
Define the Fulfillment Module once. If it appears twice in , the last definition wins and the provider in the earlier one is never registered — which makes every cart that references an nShift shipping option fail.
If your client secret contains , do not escape it as in — dotenv keeps the backslash and nShift answers . Wrap the value in quotes instead.
| Option | Type | Default | Description |
|---|---|---|---|
| — | Required. nShift API Client ID. | ||
| — | Required. nShift API Client Secret. | ||
| — | Required. nShift Checkout Connection ID. | ||
| Language for option names and descriptions (ISO 639). | |||
| Locale for formatted price/date strings. | |||
| — | Country used to list delivery options in the admin (ISO 3166-1 alpha-2). Required for the admin dropdown. | ||
| — | Postal code used to list delivery options in the admin. Required for the admin dropdown. | ||
| — | State/province sent when the Medusa address has none. Some markets need it. | ||
| Currency used when the calculation context carries none. | |||
| Whether nShift prices already include tax. | |||
| Unit of in your data. Set to if you store grams. | |||
| Unit of / / . | |||
| Weight sent when no item weights are known. | |||
| Send the basket value as so nShift price rules can evaluate. | |||
| Push partial shipments to your Book & Print platform. | |||
| Count sessions towards nShift's conversion-rate metric. | |||
| Per-request timeout against the nShift API. | |||
| How long one session and its delivery options are reused for an unchanged cart. | |||
| Override the nShift API host. | |||
| Override the nShift token endpoint. |
Check . Medusa does not define a unit for . The default matches earlier versions of this plugin; if your catalogue stores grams, set or nShift will price a 500 g shirt as 500 kg.
Check . nShift Checkout prices are normally the consumer-facing gross price, hence the default. Set it to if your configuration holds net prices, otherwise your cart's tax totals will be wrong.
The chosen delivery option is stored on . If that dropdown is empty or errors, the message tells you why — most often a missing / , or no delivery options configured for that country.
Calculated options come back from without a price; fetch each price with , then add the method to the cart.
Both snake_case and the nShift widget's camelCase spelling are accepted.
| Key | Alias | Description |
|---|---|---|
| A from the option's . | ||
| A from the option's . | ||
| — | , or . | |
| Shorthand: . | ||
| — | or a map. |
Selections are validated against the delivery option before the method is stored: an unknown pickup point, time slot or addon is rejected with a naming it. Addon prices are added to the shipping price.
Anything else you put in is preserved verbatim, so you can carry your own state through checkout.
After fulfillment, additionally carries , , and .
The provider never breaks a cart. reports an option as unavailable — no — instead of throwing, whenever:
Medusa treats a missing price as "not available in this context": removes the shipping method, and an explicit selection is rejected with . So changing the shipping country to one a carrier does not serve drops that method rather than failing the address update. Every case is logged with the nShift status and so you can see the reason in the server log.
Errors are raised where they are actionable instead:
| Situation | Result |
|---|---|
| Missing / / | throws at startup |
| Admin option list without / | naming the missing option |
| Admin option list, nShift request fails | including the nShift status and issues |
| Selecting an option nShift no longer offers | , pick another method |
| Unknown pickup point, time slot or addon | naming it |
| Option requires a pickup point and none was sent | |
| Fulfilling without checkout session data | |
| Order address nShift cannot ship to | |
| nShift refuses to delete a cancelled shipment | logged, cancellation still succeeds |
MIT
1yarn add @solteq-excom/medusa-fulfillment-nshift2# or3npm install @solteq-excom/medusa-fulfillment-nshift1// medusa-config.ts2import { defineConfig } from "@medusajs/framework/utils"3
4export default defineConfig({5 plugins: [6 {7 resolve: "@solteq-excom/medusa-fulfillment-nshift",8 options: {},9 },10 ],11 modules: [12 {13 resolve: "@medusajs/medusa/fulfillment",14 options: {15 providers: [16 {17 resolve: "@medusajs/medusa/fulfillment-manual",18 id: "manual",19 },20 {21 resolve: "@solteq-excom/medusa-fulfillment-nshift/providers/nshift",22 id: "nshift",23 options: {24 client_id: process.env.NSHIFT_CLIENT_ID,25 client_secret: process.env.NSHIFT_CLIENT_SECRET,26 connection_id: process.env.NSHIFT_CONNECTION_ID,27 language_code: process.env.NSHIFT_LANGUAGE_CODE,28 locale_id: process.env.NSHIFT_LOCALE_ID,29 default_country: process.env.NSHIFT_DEFAULT_COUNTRY,30 default_postal_code: process.env.NSHIFT_DEFAULT_POSTAL_CODE,31 send_to_book_and_print:32 process.env.NSHIFT_SEND_TO_BOOK_AND_PRINT === "true",33 },34 },35 ],36 },37 },38 ],39})1NSHIFT_CLIENT_ID=your_client_id2NSHIFT_CLIENT_SECRET=your_client_secret3NSHIFT_CONNECTION_ID=your_connection_id4NSHIFT_LANGUAGE_CODE=en5NSHIFT_LOCALE_ID=en-GB6NSHIFT_DEFAULT_COUNTRY=FI7NSHIFT_DEFAULT_POSTAL_CODE=001008NSHIFT_SEND_TO_BOOK_AND_PRINT=false1import { sdk } from "../lib/config"2
3// 1. List the options available for the cart.4const { shipping_options } = await sdk.store.fulfillment.listCartOptions({5 cart_id: cart.id,6})7
8// 2. Price the calculated ones.9const priced = await Promise.all(10 shipping_options.map(async (option) => {11 if (option.price_type !== "calculated") {12 return option13 }14 const { shipping_option } = await sdk.store.fulfillment.calculate(option.id, {15 cart_id: cart.id,16 data: {},17 })18 return shipping_option19 })20)21
22// 3. Hide the ones nShift does not offer for this cart.23const selectable = priced.filter((option) => option.amount !== undefined)24
25// 4. Add the customer's choice to the cart.26await sdk.store.cart.addShippingMethod(cart.id, {27 option_id: selected.id,28 data: {29 pickup_point_id: "K384", // optional30 time_slot_id: "slot-1", // optional31 addons: [{ addon_id: "948058" }], // optional32 fields: [{ field_id: "DOORCODE", value: "1234" }], // optional33 },34})1{2 "session_id": "77933415-a756-4eab-821a-98fb6fc9aa75",3 "option_id": "834827b2-abc2-4e05-8520-7707c1d2c4d8",4 "carrier_id": "948",5 "carrier_product_id": "10543",6 "carrier_product_name": "Posti Home Parcel (2104)(10543) Finland only",7 "price": 98,8 "currency_code": "EUR",9 "addons": [{ "addonId": "948058" }],10 "delivery_time": {11 "earliest": "2026-08-29T00:00:00",12 "latest": "2026-08-29T00:00:00",13 "description": "Delivery on Saturday",14 "timeZone": "Europe/Helsinki"15 }16}