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 clipboard
DOCUMENT_CREATED, Copy to clipboardTRACKING_CHANGED) - Calcul poids / dimensions / valeur déclarée depuis les produits
Compatible Medusa ≥ 2.12.
Table des matières
- Installation
- Configuration backend
- Variables d’environnement
- Créer les shipping options
- Webhooks
- API référence
- Intégration storefront
- Admin — sync étiquette
- Comportement à la commande
- Troubleshooting
1. Installation
123npm install medusa-plugin-boxtal-v2# ouyarn add medusa-plugin-boxtal-v2
Développement local (yalc)
123456# dans medusa-plugin-boxtal-v2npm run buildnpx medusa plugin:publish# dans votre app Medusanpx medusa plugin:add medusa-plugin-boxtal-v2
Ou dépendance fichier :
12345{"dependencies": {"medusa-plugin-boxtal-v2": "file:../medusa-plugin-boxtal-v2"}}
2. Configuration backend
Deux enregistrements sont obligatoires dans Copy to clipboardmedusa-config.ts :
- Copy to clipboard
plugins— charge les routes API, middlewares webhook, subscribers - Copy to clipboard
fulfillment.providers— enregistre le provider Copy to clipboardboxtal
1234567891011121314151617181920import { 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 :
1option.provider_id?.includes("boxtal")
3. Variables d’environnement
Copiez dans le Copy to clipboard.env de votre backend Medusa :
1234567891011121314151617181920# --- Boxtal API ---BOXTAL_ACCESS_KEY=BOXTAL_SECRET_KEY=BOXTAL_ENVIRONMENT=sandbox# Production : https://api.boxtal.com | Sandbox : https://api.boxtal.buildBOXTAL_API_BASE_URL=https://api.boxtal.build# Offres (codes fournis par Boxtal)BOXTAL_RELAY_OFFER_CODE=MONR-CpourToiBOXTAL_HOME_OFFER_CODE=CHRP-Chrono18# Libellés checkout (optionnel)BOXTAL_RELAY_NAME=Mondial Relay - Livraison en point RelaisBOXTAL_RELAY_TYPE_LABEL=Point RelaisBOXTAL_RELAY_DESCRIPTION=Livraison en point relais — 3 à 5 jours ouvrésBOXTAL_HOME_NAME=Chronopost - Livraison à domicileBOXTAL_HOME_TYPE_LABEL=DomicileBOXTAL_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 :
1234# 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
- Exposez publiquement Copy to clipboard
POST /hooks/boxtal(tunnel Cloudflare / ngrok en local). - Définissez Copy to clipboard
BOXTAL_WEBHOOK_SECRETet Copy to clipboardBOXTAL_WEBHOOK_CALLBACK_URL. - Enregistrez les subscriptions :
12npx medusa exec ./src/scripts/setup-boxtal-webhook.tsnpx 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 :
1234567891011121314151617181920{"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 :
12345{"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=... :
1234567891011121314151617181920function isBoxtalProvider(providerId?: string | null) {return !!providerId?.includes("boxtal")}function isBoxtalRelayOption(option: {provider_id?: string | nulltype?: { code?: string | null }data?: { deliveryType?: string }}) {if (!isBoxtalProvider(option.provider_id)) return falsereturn (option.type?.code === "boxtal-relay" ||option.data?.deliveryType === "relay")}function isBoxtalHomeOption(option: {provider_id?: string | nulltype?: { code?: string | null }data?: { deliveryType?: string }
7.2 Client HTTP (exemple)
1234567891011121314151617181920const BACKEND = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL!const PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY!async function searchRelayPoints(params: {zipCode?: stringcity?: stringlatitude?: numberlongitude?: numbercartId?: 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 :
123456789const 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 :
1234const metadata = {carrier: "boxtal",deliveryType: "home",}
7.4 Attacher la shipping method
123456789101112131415// 1) Mettre à jour le panierawait sdk.store.cart.update(cartId, { metadata: { ...cart.metadata, ...metadata } })// 2) Sélectionner l’option + data pour validateFulfillmentDataawait sdk.store.cart.addShippingMethod(cartId, {option_id: shippingOptionId, // id Medusa de l’option boxtal-relay ou boxtal-homedata: {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é
123456789101. Charger shipping options du cart2. Afficher options Boxtal (relais / domicile)3. Si relais :a. Demander code postal (ou utiliser shipping_address)b. GET /store/boxtal/relay-pointsc. L’utilisateur choisit un pointd. 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
123456789101112131415export type BoxtalRelayPoint = {id: stringcode: stringname: stringaddress: stringcity: stringzipCode: stringcountry: stringlatitude?: numberlongitude?: numbernetwork?: stringdistance?: numberschedule?: string[] | nullscheduleDetailed?: string[] | null}
8. Admin — sync étiquette
Après fulfillment, l’étiquette peut arriver avec quelques secondes de délai.
12POST /admin/orders/{order_id}/boxtal-shipping/syncAuthorization: Bearer <admin_token>
Ou script :
1npx medusa exec ./src/scripts/sync-boxtal-label.ts order_01...
Données stockées sur le fulfillment (Copy to clipboarddata) :
- Copy to clipboard
boxtal_order_id - Copy to clipboard
boxtal_label_url - Copy to clipboard
boxtal_tracking_number - Copy to clipboard
boxtal_tracking_url - Copy to clipboard
carrier: "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)
123npx medusa exec ./src/scripts/test-boxtal-connection.tsnpx medusa exec ./src/scripts/test-boxtal-package-payload.ts order_xxxnpx medusa exec ./src/scripts/test-boxtal-live-shipment.ts order_xxx
Licence
MIT

