Overview
Icon for Boxtal

Boxtal

Ship via Boxtal with relay points, labels, and tracking

medusa-plugin-boxtal-v2

Plugin Medusa.js v2 pour Boxtal API v3 :

  • Provider de fulfillment (Mondial Relay point relais + Chronopost domicile)
  • Recherche / détail de points relais (Store API)
  • Création d’expédition, étiquettes PDF, tracking
  • Webhooks HMAC (Copy to clipboardDOCUMENT_CREATED, Copy to clipboardTRACKING_CHANGED)
  • Calcul poids / dimensions / valeur déclarée depuis les produits

Compatible Medusa ≥ 2.12.

Table des matières

  1. Installation
  2. Configuration backend
  3. Variables d’environnement
  4. Créer les shipping options
  5. Webhooks
  6. API référence
  7. Intégration storefront
  8. Admin — sync étiquette
  9. Comportement à la commande
  10. Troubleshooting

1. Installation

npm install medusa-plugin-boxtal-v2
# ou
yarn add medusa-plugin-boxtal-v2

Développement local (yalc)

# dans medusa-plugin-boxtal-v2
npm run build
npx medusa plugin:publish
# dans votre app Medusa
npx medusa plugin:add medusa-plugin-boxtal-v2

Ou dépendance fichier :

{
"dependencies": {
"medusa-plugin-boxtal-v2": "file:../medusa-plugin-boxtal-v2"
}
}

2. Configuration backend

Deux enregistrements sont obligatoires dans Copy to clipboardmedusa-config.ts :

  1. Copy to clipboardplugins — charge les routes API, middlewares webhook, subscribers
  2. Copy to clipboardfulfillment.providers — enregistre le provider Copy to clipboardboxtal
import { defineConfig, loadEnv } from "@medusajs/framework/utils"
loadEnv(process.env.NODE_ENV || "development", process.cwd())
module.exports = defineConfig({
plugins: [
{
resolve: "medusa-plugin-boxtal-v2",
options: {},
},
],
modules: [
{
resolve: "@medusajs/medusa/fulfillment",
options: {
providers: [
{
resolve: "@medusajs/medusa/fulfillment-manual",
id: "manual",
},

ID runtime du provider

Medusa compose Copy to clipboard{id}_{identifier}Copy to clipboardboxtal_boxtal.

Utilisez cet ID pour détecter les options shipping côté storefront :

option.provider_id?.includes("boxtal")

3. Variables d’environnement

Copiez dans le Copy to clipboard.env de votre backend Medusa :

# --- Boxtal API ---
BOXTAL_ACCESS_KEY=
BOXTAL_SECRET_KEY=
BOXTAL_ENVIRONMENT=sandbox
# Production : https://api.boxtal.com | Sandbox : https://api.boxtal.build
BOXTAL_API_BASE_URL=https://api.boxtal.build
# Offres (codes fournis par Boxtal)
BOXTAL_RELAY_OFFER_CODE=MONR-CpourToi
BOXTAL_HOME_OFFER_CODE=CHRP-Chrono18
# Libellés checkout (optionnel)
BOXTAL_RELAY_NAME=Mondial Relay - Livraison en point Relais
BOXTAL_RELAY_TYPE_LABEL=Point Relais
BOXTAL_RELAY_DESCRIPTION=Livraison en point relais — 3 à 5 jours ouvrés
BOXTAL_HOME_NAME=Chronopost - Livraison à domicile
BOXTAL_HOME_TYPE_LABEL=Domicile
BOXTAL_HOME_DESCRIPTION=Livraison à domicile — 1 jour ouvré
# Tarifs flat (euros) utilisés par le script setup

4. Créer les shipping options

Après configuration, créez les 2 options (relais + domicile) liées au provider :

# Depuis le code source du plugin (recommandé en monorepo)
cd medusa-plugin-boxtal-v2
# Pointer DATABASE_URL vers la DB de l’app, puis :
npx medusa exec ./src/scripts/setup-boxtal-shipping.ts

Ou copiez Copy to clipboardsrc/scripts/setup-boxtal-shipping.ts dans votre app et exécutez-le avec Copy to clipboardmedusa exec.

Le script crée :

Code type Copy to clipboarddata.deliveryType Usage Copy to clipboardboxtal-relay Copy to clipboardrelay Point relais (sélection obligatoire) Copy to clipboardboxtal-home Copy to clipboardhome Livraison à domicile

Assurez-vous que vos produits ont un shipping profile relié à la même zone France que le script.

5. Webhooks

  1. Exposez publiquement Copy to clipboardPOST /hooks/boxtal (tunnel Cloudflare / ngrok en local).
  2. Définissez Copy to clipboardBOXTAL_WEBHOOK_SECRET et Copy to clipboardBOXTAL_WEBHOOK_CALLBACK_URL.
  3. Enregistrez les subscriptions :
npx medusa exec ./src/scripts/setup-boxtal-webhook.ts
npx medusa exec ./src/scripts/list-boxtal-webhooks.ts

Événements gérés : Copy to clipboardDOCUMENT_CREATED (étiquette), Copy to clipboardTRACKING_CHANGED (suivi).

Le middleware du plugin active Copy to clipboardpreserveRawBody sur Copy to clipboard/hooks/boxtal pour la vérif HMAC (Copy to clipboardx-bxt-signature).

6. API référence

Toutes les routes Store nécessitent le header Copy to clipboardx-publishable-api-key.

Copy to clipboardGET /store/boxtal/relay-points

Recherche de points relais.

Query params :

Param Type Description Copy to clipboardzipCode string Code postal (recommandé) Copy to clipboardcity string Ville Copy to clipboardlatitude / Copy to clipboardlongitude number Origine GPS (tri proximité) Copy to clipboardcart_id string Optionnel — poids du panier pour filtrer

Réponse 200 :

{
"relayPoints": [
{
"id": "71039",
"code": "71039",
"name": "TABAC DE LA GARE",
"address": "12 rue Example",
"city": "Paris",
"zipCode": "75001",
"country": "FR",
"latitude": 48.86,
"longitude": 2.34,
"network": "MONR",
"distance": 0.4,
"schedule": ["Lundi - Vendredi : 09:00 – 19:00"],
"scheduleDetailed": ["Lundi : 09:00 – 12:00, 14:00 – 19:00", "..."]
}
],
"meta": {
"totalFound": 12,

Copy to clipboardGET /store/boxtal/relay-points/:id

Détail d’un point (Copy to clipboardid = code parcel point).

Mêmes query optionnels : Copy to clipboardzipCode, Copy to clipboardcity, Copy to clipboardcart_id.

Réponse 200 : Copy to clipboard{ "relayPoint": { ... } }

Copy to clipboardPOST /hooks/boxtal

Webhook Boxtal (pas de publishable key). Body JSON + signature HMAC.

Copy to clipboardPOST /admin/orders/:id/boxtal-shipping/sync

Auth admin requise. Force la récupération étiquette / tracking depuis Boxtal.

Réponse :

{
"order_id": "order_01...",
"synced": true,
"results": [{ "fulfillment_id": "ful_...", "synced": true, "label_url": "...", "tracking_number": "..." }]
}

7. Intégration storefront

7.1 Détecter les options Boxtal

Après Copy to clipboardGET /store/shipping-options?cart_id=... :

function isBoxtalProvider(providerId?: string | null) {
return !!providerId?.includes("boxtal")
}
function isBoxtalRelayOption(option: {
provider_id?: string | null
type?: { code?: string | null }
data?: { deliveryType?: string }
}) {
if (!isBoxtalProvider(option.provider_id)) return false
return (
option.type?.code === "boxtal-relay" ||
option.data?.deliveryType === "relay"
)
}
function isBoxtalHomeOption(option: {
provider_id?: string | null
type?: { code?: string | null }
data?: { deliveryType?: string }

7.2 Client HTTP (exemple)

const BACKEND = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!
const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY!
async function searchRelayPoints(params: {
zipCode?: string
city?: string
latitude?: number
longitude?: number
cartId?: string
}) {
const q = new URLSearchParams()
if (params.cartId) q.set("cart_id", params.cartId)
if (params.zipCode) q.set("zipCode", params.zipCode)
if (params.city) q.set("city", params.city)
if (params.latitude != null) q.set("latitude", String(params.latitude))
if (params.longitude != null) q.set("longitude", String(params.longitude))
const res = await fetch(`${BACKEND}/store/boxtal/relay-points?${q}`, {
headers: {
"Content-Type": "application/json",
Next.js : vous pouvez proxifier via Copy to clipboard/api/store/boxtal/... côté app pour éviter d’exposer l’URL backend, ou appeler Medusa directement depuis le serveur.

7.3 Metadata panier (obligatoire)

Avant de finaliser le checkout, stockez le choix dans cart.metadata et dans shipping method data.

Point relais :

const metadata = {
carrier: "boxtal",
deliveryType: "relay",
parcelPointCode: point.code, // code Boxtal (obligatoire)
relayPointId: point.code,
relayPointName: point.name,
relayPointAddress: `${point.address}, ${point.zipCode} ${point.city}`,
relayPointNetwork: point.network,
}

Domicile :

const metadata = {
carrier: "boxtal",
deliveryType: "home",
}

7.4 Attacher la shipping method

// 1) Mettre à jour le panier
await sdk.store.cart.update(cartId, { metadata: { ...cart.metadata, ...metadata } })
// 2) Sélectionner l’option + data pour validateFulfillmentData
await sdk.store.cart.addShippingMethod(cartId, {
option_id: shippingOptionId, // id Medusa de l’option boxtal-relay ou boxtal-home
data: {
carrier: "boxtal",
deliveryType: metadata.deliveryType, // "relay" | "home"
parcelPointCode: metadata.parcelPointCode,
relayPointId: metadata.relayPointId,
relayPointName: metadata.relayPointName,
relayPointAddress: metadata.relayPointAddress,
},
})

Le provider valide que Copy to clipboardparcelPointCode / Copy to clipboardrelayPointId est présent pour Copy to clipboarddeliveryType: "relay".

7.5 Flux checkout recommandé

1. Charger shipping options du cart
2. Afficher options Boxtal (relais / domicile)
3. Si relais :
a. Demander code postal (ou utiliser shipping_address)
b. GET /store/boxtal/relay-points
c. L’utilisateur choisit un point
d. setBoxtalShipping (metadata + shipping method data)
4. Si domicile :
a. setBoxtalShipping avec deliveryType "home"
5. Continuer paiement → complete cart

7.6 Afficher le point relais après commande

Lire Copy to clipboardorder.metadata ou Copy to clipboardshipping_methods[0].data :

Clé Description Copy to clipboardcarrier Copy to clipboard"boxtal" Copy to clipboarddeliveryType Copy to clipboard"relay" | Copy to clipboard"home" Copy to clipboardrelayPointName Nom du point Copy to clipboardrelayPointAddress Adresse formatée Copy to clipboardparcelPointCode / Copy to clipboardrelayPointId Code Boxtal

Le subscriber Copy to clipboardorder-placed-boxtal copie ces champs du panier vers la commande.

7.7 Types TypeScript utiles

export type BoxtalRelayPoint = {
id: string
code: string
name: string
address: string
city: string
zipCode: string
country: string
latitude?: number
longitude?: number
network?: string
distance?: number
schedule?: string[] | null
scheduleDetailed?: string[] | null
}

8. Admin — sync étiquette

Après fulfillment, l’étiquette peut arriver avec quelques secondes de délai.

POST /admin/orders/{order_id}/boxtal-shipping/sync
Authorization: Bearer <admin_token>

Ou script :

npx medusa exec ./src/scripts/sync-boxtal-label.ts order_01...

Données stockées sur le fulfillment (Copy to clipboarddata) :

  • Copy to clipboardboxtal_order_id
  • Copy to clipboardboxtal_label_url
  • Copy to clipboardboxtal_tracking_number
  • Copy to clipboardboxtal_tracking_url
  • Copy to clipboardcarrier: "boxtal"

9. Comportement à la commande

Moment Action Copy to clipboardorder.placed Copie metadata Boxtal cart → order Copy to clipboardorder.placed Copie poids/dims variante→produit vers metadata lignes Création fulfillment Appel Boxtal Copy to clipboardcreateShippingOrder avec colis calculé Webhook / sync Met à jour label + tracking

Poids : variante → metadata → produit parent (grammes). Min. 0,1 kg.
Dimensions : variante → metadata → produit → défauts env (cm).
Valeur déclarée : somme des lignes (euros) par défaut.

Renseignez Copy to clipboardproduct.weight / Copy to clipboardlength / Copy to clipboardwidth / Copy to clipboardheight (ou sur chaque variante) dans l’admin.

10. Troubleshooting

Symptôme Cause probable Fix Options shipping absentes Setup non exécuté / mauvais provider_id Relancer Copy to clipboardsetup-boxtal-shipping.ts Erreur « point relais manquant » Copy to clipboarddata.parcelPointCode non passé Vérifier Copy to clipboardaddShippingMethod data Colis 0,1 kg Poids produit / variante vide Remplir poids (g) en admin Valeur déclarée 1 € Ancien bug centimes — versions ≥ 0.1.0 corrigées Utiliser Copy to clipboardBOXTAL_DECLARED_VALUE_MODE=items Pas d’étiquette Webhook local / délai Boxtal Copy to clipboardPOST .../boxtal-shipping/sync 401 sur Store API Publishable key manquante Header Copy to clipboardx-publishable-api-key

Scripts de test (repo plugin)

npx medusa exec ./src/scripts/test-boxtal-connection.ts
npx medusa exec ./src/scripts/test-boxtal-package-payload.ts order_xxx
npx medusa exec ./src/scripts/test-boxtal-live-shipment.ts order_xxx

Licence

MIT

You may also like

Browse all integrations

Build your own

Develop your own custom integration

Build your own integration with our API to speed up your processes. Make your integration available via npm for it to be shared in our Library with the broader Medusa community.

gift card interface

Ready to build your custom commerce setup?