Comment migrer les Shopify Scripts vers les Functions : le tutoriel de code complet (édition 2026)
Comment migrer les Shopify Scripts vers les Functions : le tutoriel de code complet (édition 2026)
Comment migrer les Shopify Scripts vers les Functions : le tutoriel de code complet (édition 2026)

Nous sommes le 16 avril 2026. Hier — le 15 avril — était le jour où Shopify a verrouillé définitivement l’éditeur de Scripts. Vous ne pouvez plus créer ni publier de nouveau Script. L’arrêt d’exécution arrivera dans 75 jours, le 30 juin 2026.
Si vous êtes développeur Shopify Plus — ou l’agence qui gère une boutique Plus — et que vous avez repoussé cette migration à « la prochaine sprint » pendant les douze derniers mois, vous avez un problème. Pas un problème du type « ce serait bien à corriger ». Un problème du type « votre checkout casse à minuit le 1er juillet ». La plupart des boutiques Plus ont accumulé 5 à 20 Scripts au fil des ans, chacun alimentant discrètement une règle de remise, un masquage d’expédition ou une restriction de paiement dont plus personne ne se souvient avoir écrit le code.
Ce guide est le manuel technique de migration que nous aurions voulu avoir en janvier. Il couvre le vrai code — pas seulement la stratégie. Une fois votre lecture terminée, vous saurez comment scaffold une Function avec Shopify CLI, écrire la logique Rust ou JavaScript pour les remises, les personnalisations de livraison et les personnalisations de paiement, la tester prudemment sur un sous-ensemble balisé de vos clients, puis la déployer en production sans casser votre checkout existant.
Retirons les Scripts de votre boutique et mettons-y les Functions.

Réponse rapide : Scripts → Functions en 60 secondes
La migration en un paragraphe : les Shopify Scripts (code Ruby dans l’éditeur de Scripts, réservé à Plus) sont remplacés par les Shopify Functions (modules WebAssembly écrits en Rust ou en JavaScript, disponibles sur tous les plans). Vous créez une Function avec
shopify app generate extension, vous écrivez une requêterun.graphqlqui récupère les données de panier dont vous avez besoin, vous écrivez un fichierrun.rsourun.jsqui renvoie des opérations (remises, méthodes d’expédition masquées, etc.), puis vous déployez avecshopify app deployet l’activez via l’Admin ou une mutation GraphQL. Les Functions s’exécutent en WASM compilé avec une latence inférieure à 5 ms, fonctionnent sur tous les plans et constituent désormais le seul chemin de personnalisation pris en charge par Shopify.
Ce qui change réellement le 30 juin
Avant de toucher au code, mettons les dates au clair. Il y en a deux, et elles comptent toutes les deux.
Date | Ce qui se passe | Votre action |
|---|---|---|
15 avril 2026 (passé) | L’éditeur de Scripts est en lecture seule. Plus de nouveaux Scripts. Plus de modifications des Scripts existants. | Les Scripts existants s’exécutent encore. Migrez maintenant ou figez votre logique. |
30 juin 2026 | Tous les Shopify Scripts cessent de s’exécuter. Point final. | Votre remplacement par une Function doit être en ligne avant cette date. |
La migration est binaire. Soit votre Function est déployée d’ici le 30 juin et votre checkout continue de fonctionner, soit ce n’est pas le cas — et chaque panier concerné retombe silencieusement sur la tarification standard, les frais de livraison standard et tous les moyens de paiement activés. Il n’y a pas de demi-mesure. Le Script s’exécute ou non, et après le 30 juin, il ne s’exécute plus.
Astuce : Ouvrez
Settings → Checkout → Customizations Reportdans votre administration Shopify. Cela liste chaque Script actif de votre boutique, ce qu’il fait et le type de remplacement par Function recommandé. Commencez par là.
Functions vs Scripts : ce qui a vraiment changé
Dimension | Shopify Scripts (obsolètes) | Shopify Functions (remplacement) |
|---|---|---|
Langage | DSL Ruby (spécifique à Shopify) | Rust, JavaScript, TypeScript |
Runtime | Ruby sandboxé sur l’infrastructure Shopify | WebAssembly (WASM) — exécution sous les 5 ms |
Disponibilité selon le plan | Uniquement Plus | Tous les plans (les apps personnalisées nécessitent Plus ; les apps publiques sont ouvertes) |
Éditeur | Éditeur de Scripts dans l’admin | IDE local + Shopify CLI |
Versioning | Aucun — modifications en direct | Compatible Git — contrôle de version complet |
Tests | Manuel dans le checkout | Développement local avec |
Déploiement | Cliquer sur « Enregistrer » dans l’admin |
|
Cibles | Articles, expédition, paiements | Remises, transformation du panier, validation, personnalisation de livraison, personnalisation de paiement, routage des commandes, contraintes de fulfillment, et plus |
Le changement architectural est important. Les Scripts, c’était « retoucher du Ruby dans une zone de texte ». Les Functions, c’est « écrire une vraie application, la versionner, la tester localement, la déployer via un vrai pipeline CI ». La courbe d’apprentissage est plus raide. Mais c’est aussi la dernière migration que vous ferez pour la logique de checkout dans un avenir prévisible — les Functions sont l’engagement à long terme de Shopify, pas un palliatif comme l’étaient finalement les Scripts.
Associer vos Scripts au bon type de Function
Chaque Script que vous avez aujourd’hui correspond à une seule API Function. Voici le tableau de correspondance à garder sous les yeux.
Ancien type de Script | Ce qu’il faisait | Nouvelle API Function | Cible de la Function |
|---|---|---|---|
Line Item Script | Appliquer des remises à des produits / clients / conditions de panier spécifiques | Cart & Checkout Discounts API |
|
Shipping Script (remise) | Livraison gratuite / remisée selon les règles du panier | Cart & Checkout Discounts API |
|
Shipping Script (masquer / renommer / réordonner) | Masquer un tarif de livraison au-dessus de X $, renommer « Standard » en « Gratuit au-delà de 50 $ » | Delivery Customization API |
|
Payment Script | Masquer PayPal pour le B2B, masquer le paiement à la livraison au-dessus de 500 $, réordonner les moyens | Payment Customization API |
|
Script de modification du panier (rare) | Regrouper des produits, remplacer des line items | Cart Transform API |
|
Script de blocage du checkout | Refuser le panier si le mélange de SKU est invalide | Cart & Checkout Validation API |
|
Si vous avez dix Scripts, vous construirez probablement trois à cinq Functions — plusieurs Scripts se regroupent souvent en une seule Function avec une logique de branchement plus propre.

Prérequis : configurer votre environnement de développement local
Avant de scaffold une Function, vous devez installer trois éléments en local. Lancez ces vérifications dans votre terminal.
1. Node.js 18+
node --version # Doit être >= 18.0.0
node --version # Doit être >= 18.0.0
Si votre version est plus ancienne, installez-la via nvm ou téléchargez-la depuis nodejs.org.
2. Shopify CLI 3+
npm install -g @shopify/cli@latest shopify version # Devrait afficher 3.x ou plus
npm install -g @shopify/cli@latest shopify version # Devrait afficher 3.x ou plus
3. Chaîne d’outils Rust (uniquement si vous écrivez vos Functions en Rust)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup target add wasm32-wasip1 cargo --version
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup target add wasm32-wasip1 cargo --version
Les Functions JavaScript n’ont pas besoin de Rust. Choisissez un langage pour votre équipe et tenez-vous-y — en utiliser deux ajoute une surcharge de maintenance.
4. Une boutique de développement
Connectez-vous à votre tableau de bord Partner et créez une nouvelle boutique de développement, ou utilisez-en une existante. Vous déploierez les Functions sur cette boutique avant de passer en production.
Scaffolder votre première Function
La CLI génère la majeure partie du boilerplate. Dans n’importe quel répertoire :
# Créez une nouvelle app Shopify (ignorez cette étape si vous en avez déjà une) shopify app init my-checkout-functions cd my-checkout-functions # Générez une extension Function shopify app generate extension
# Créez une nouvelle app Shopify (ignorez cette étape si vous en avez déjà une) shopify app init my-checkout-functions cd my-checkout-functions # Générez une extension Function shopify app generate extension
La CLI vous guide à travers les invites. Pour une Function de remise, vous choisiriez :
Type : Function
Template :
discount(oucart_checkout_validation,delivery_customization,payment_customization, etc.)Langage : Rust ou JavaScript
Nom : quelque chose comme
volume-discount-fn
Cela crée extensions/volume-discount-fn/ avec :
extensions/volume-discount-fn/ ├── shopify.extension.toml # Configuration de la Function — cibles, build, version ├── src/ │ ├── cart_lines_discounts_generate_run.graphql # Requête d’entrée │ └── cart_lines_discounts_generate_run.rs # Logique de la Function ├── Cargo.toml # Dépendances Rust (Rust uniquement) └── README.md
extensions/volume-discount-fn/ ├── shopify.extension.toml # Configuration de la Function — cibles, build, version ├── src/ │ ├── cart_lines_discounts_generate_run.graphql # Requête d’entrée │ └── cart_lines_discounts_generate_run.rs # Logique de la Function ├── Cargo.toml # Dépendances Rust (Rust uniquement) └── README.md
Les trois fichiers que vous modifierez constamment sont le .toml (configuration), le .graphql (entrée) et le .rs / .js (logique). C’est tout.
Tutoriel 1 : remplacer un Line Item Script (remise sur volume)
Supposons que votre ancien Script accordait 10 % de remise sur le sous-total de la commande lorsque le panier contenait 5 unités ou plus d’une collection spécifique. Voici l’équivalent en Function.
Étape 1.1 : la configuration (shopify.extension.toml)
api_version = "2026-01" [[extensions]] name = "volume-discount-fn" handle = "volume-discount-fn" type = "function" [[extensions.targeting]] target = "cart.lines.discounts.generate.run" input_query = "src/cart_lines_discounts_generate_run.graphql" export = "cart_lines_discounts_generate_run" [extensions.build] command = "cargo build --target=wasm32-wasip1 --release" path = "target/wasm32-wasip1/release/volume-discount-fn.wasm" watch = ["src/**/*.rs"]
api_version = "2026-01" [[extensions]] name = "volume-discount-fn" handle = "volume-discount-fn" type = "function" [[extensions.targeting]] target = "cart.lines.discounts.generate.run" input_query = "src/cart_lines_discounts_generate_run.graphql" export = "cart_lines_discounts_generate_run" [extensions.build] command = "cargo build --target=wasm32-wasip1 --release" path = "target/wasm32-wasip1/release/volume-discount-fn.wasm" watch = ["src/**/*.rs"]
Étape 1.2 : la requête d’entrée (src/cart_lines_discounts_generate_run.graphql)
query Input { cart { lines { id quantity cost { subtotalAmount { amount } } merchandise { ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/123456789"]) } } } } } discount { discountClasses } }
query Input { cart { lines { id quantity cost { subtotalAmount { amount } } merchandise { ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/123456789"]) } } } } } discount { discountClasses } }
Astuce : Les Functions ne voient que les données que vous interrogez. Gardez le GraphQL minimal — chaque champ que vous omettez signifie une exécution plus rapide et moins coûteuse.
Étape 1.3 : la logique (src/cart_lines_discounts_generate_run.rs)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { // Quitter si la classe de remise ne correspond pas let has_order_discount = input .discount() .discount_classes() .contains(&schema::DiscountClass::Order); if !has_order_discount { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Additionner les quantités des articles de la collection cible let qualifying_qty: i64 = input .cart() .lines() .iter() .filter(|line| { if let schema::Merchandise::ProductVariant(v) = line.merchandise() { *v.product().in_any_collection() } else { false } }) .map(|line| *line.quantity()) .sum(); if qualifying_qty < 5 { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Appliquer 10 % de remise sur le sous-total de la commande Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some("Volume discount: 10% off".to_string()), value: schema::OrderDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal(10.0) } ), conditions: None, associated_discount_code: None, }], }, )], }) }
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { // Quitter si la classe de remise ne correspond pas let has_order_discount = input .discount() .discount_classes() .contains(&schema::DiscountClass::Order); if !has_order_discount { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Additionner les quantités des articles de la collection cible let qualifying_qty: i64 = input .cart() .lines() .iter() .filter(|line| { if let schema::Merchandise::ProductVariant(v) = line.merchandise() { *v.product().in_any_collection() } else { false } }) .map(|line| *line.quantity()) .sum(); if qualifying_qty < 5 { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Appliquer 10 % de remise sur le sous-total de la commande Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some("Volume discount: 10% off".to_string()), value: schema::OrderDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal(10.0) } ), conditions: None, associated_discount_code: None, }], }, )], }) }
Étape 1.4 : tester, déployer, activer
# Développement local avec rechargement à chaud shopify app dev # Une fois prêt, déployez shopify app deploy # Dans le panneau GraphiQL qui s’ouvre (appuyez sur `g` dans le terminal de dev), # créez la remise automatique qui utilise votre Function :
# Développement local avec rechargement à chaud shopify app dev # Une fois prêt, déployez shopify app deploy # Dans le panneau GraphiQL qui s’ouvre (appuyez sur `g` dans le terminal de dev), # créez la remise automatique qui utilise votre Function :
mutation { discountAutomaticAppCreate( automaticAppDiscount: { title: "Volume Discount (5+ collection items)" functionHandle: "volume-discount-fn" discountClasses: [ORDER] startsAt: "2026-04-16T00:00:00Z" } ) { automaticAppDiscount { discountId } userErrors { field message } } }
mutation { discountAutomaticAppCreate( automaticAppDiscount: { title: "Volume Discount (5+ collection items)" functionHandle: "volume-discount-fn" discountClasses: [ORDER] startsAt: "2026-04-16T00:00:00Z" } ) { automaticAppDiscount { discountId } userErrors { field message } } }
C’est tout. La Function est en ligne, versionnée, et remplace entièrement l’ancien Script.
Tutoriel 2 : remplacer un Shipping Script (masquer une méthode au-dessus d’un seuil de panier)
Script courant : « Masquer la livraison Express lorsque le sous-total du panier dépasse 500 $ afin d’éviter une expédition de nuit coûteuse sur les grosses commandes. » Voici la version Function en Delivery Customization.
Étape 2.1 : scaffold
shopify app generate extension --template delivery_customization --name hide-express-fn
shopify app generate extension --template delivery_customization --name hide-express-fn
Étape 2.2 : requête d’entrée (src/run.graphql)
query Input { cart { cost { subtotalAmount { amount } } deliveryGroups { deliveryOptions { handle title } } } }
query Input { cart { cost { subtotalAmount { amount } } deliveryGroups { deliveryOptions { handle title } } } }
Étape 2.3 : logique (src/run.js — variante JavaScript)
// @ts-check /** * @typedef {import("../generated/api").RunInput} RunInput * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult */ const NO_CHANGES = { operations: [] }; const THRESHOLD = 500.0; const HIDE_TITLES = ["Express", "Overnight"]; /** * @param {RunInput} input * @returns {FunctionRunResult} */ export function run(input) { const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount); if (subtotal < THRESHOLD) return NO_CHANGES; const operations = input.cart.deliveryGroups.flatMap((group) => group.deliveryOptions .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t))) .map((opt) => ({ hide: { deliveryOptionHandle: opt.handle }, })) ); return { operations }; }
// @ts-check /** * @typedef {import("../generated/api").RunInput} RunInput * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult */ const NO_CHANGES = { operations: [] }; const THRESHOLD = 500.0; const HIDE_TITLES = ["Express", "Overnight"]; /** * @param {RunInput} input * @returns {FunctionRunResult} */ export function run(input) { const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount); if (subtotal < THRESHOLD) return NO_CHANGES; const operations = input.cart.deliveryGroups.flatMap((group) => group.deliveryOptions .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t))) .map((opt) => ({ hide: { deliveryOptionHandle: opt.handle }, })) ); return { operations }; }
Étape 2.4 : activation via l’Admin (aucun GraphQL requis)
Les Delivery Customizations disposent aussi d’une interface d’administration intégrée. Après shopify app deploy :
Allez dans Settings → Shipping and delivery
Faites défiler jusqu’à la section Customizations en bas
Cliquez sur Add customization → sélectionnez votre Function
Enregistrez
La règle de masquage est active en production. Aucune mutation n’est nécessaire.

Tutoriel 3 : remplacer un Payment Script (masquer le paiement à la livraison pour le B2B)
Ancien Script : « Masquer le paiement à la livraison pour tout client ayant le tag 'B2B'. » Voici la version Payment Customization.
Étape 3.1 : scaffold
shopify app generate extension --template payment_customization --name hide-cod-b2b-fn
shopify app generate extension --template payment_customization --name hide-cod-b2b-fn
Étape 3.2 : requête d’entrée
query Input { cart { buyerIdentity { customer { hasTags(tags: [{ tag: "B2B" }]) { tag hasTag } } } } paymentMethods { id name } }
query Input { cart { buyerIdentity { customer { hasTags(tags: [{ tag: "B2B" }]) { tag hasTag } } } } paymentMethods { id name } }
Étape 3.3 : logique (src/run.js)
const NO_CHANGES = { operations: [] }; export function run(input) { const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0]; const isB2B = tagCheck?.hasTag === true; if (!isB2B) return NO_CHANGES; const codMethod = input.paymentMethods.find((pm) => pm.name.toLowerCase().includes("cash on delivery") ); if (!codMethod) return NO_CHANGES; return { operations: [{ hide: { paymentMethodId: codMethod.id } }], }; }
const NO_CHANGES = { operations: [] }; export function run(input) { const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0]; const isB2B = tagCheck?.hasTag === true; if (!isB2B) return NO_CHANGES; const codMethod = input.paymentMethods.find((pm) => pm.name.toLowerCase().includes("cash on delivery") ); if (!codMethod) return NO_CHANGES; return { operations: [{ hide: { paymentMethodId: codMethod.id } }], }; }
Étape 3.4 : activation
Les Payment Customizations disposent aussi d’une interface d’administration sous Settings → Payments → Customizations. Même processus que pour la livraison — choisissez votre Function, enregistrez, terminé.
Puisque vous lisez ceci — un mot sur l’après-achat
Petite précision puisque c’est le blog de Revize. Revize gère ce que les Functions ne peuvent pas toucher — une fois qu’une commande est passée, les clients veulent ajouter un article, changer une taille, corriger l’adresse de livraison, appliquer une remise qu’ils ont oubliée. Les Functions vivent au checkout. Revize vit après le checkout. Les Functions décident de ce qui est autorisé dans le panier ; Revize permet à vos clients et à votre équipe support de modifier la commande ensuite sans passer par un cycle remboursement + recréation. Cela compte pour deux types de boutiques : celles qui ont suffisamment de volume de commandes pour que l’édition manuelle s’effondre (les opérateurs Plus, évidemment, mais aussi les boutiques Advanced à fort débit), et celles dont toute la marque repose sur l’expérience client — où un e-mail du type « désolé, nous ne pouvons pas modifier cela » tue les achats répétés.
Si votre plan de migration couvre Scripts → Functions mais que vous n’avez jamais réglé les modifications de commandes après achat, vous allez heurter le prochain mur « pourquoi est-ce si compliqué ? » dans environ trois semaines. Le guide de gestion des commandes que nous venons de publier décrit tout le plan de bataille post-checkout.
Retour à la migration.
Stratégie de test : le modèle du client balisé
Les Functions n’ont pas de « mode brouillon » que vous pouvez activer ou désactiver dans l’admin. La méthode professionnelle consiste à conditionner la nouvelle Function à un tag client, à faire fonctionner en parallèle l’ancien Script et la nouvelle Function, à vérifier qu’ils produisent une sortie identique pour les utilisateurs balisés, puis à basculer.
Étape 1 : taguez vos utilisateurs de test
Dans Customers, ajoutez le tag FN-TESTER à deux ou trois comptes internes.
Étape 2 : branchez la Function sur la présence du tag
// En haut de votre fonction run let is_tester = input .cart() .buyer_identity() .and_then(|bi| bi.customer()) .map(|c| c.has_any_tag()) .unwrap_or(&false); if !*is_tester { return Ok(default_result); // Laisser passer vers le Script existant } // La nouvelle logique de la Function ne s’exécute que pour les utilisateurs balisés
// En haut de votre fonction run let is_tester = input .cart() .buyer_identity() .and_then(|bi| bi.customer()) .map(|c| c.has_any_tag()) .unwrap_or(&false); if !*is_tester { return Ok(default_result); // Laisser passer vers le Script existant } // La nouvelle logique de la Function ne s’exécute que pour les utilisateurs balisés
Étape 3 : ajoutez hasAnyTag à votre requête d’entrée
cart { buyerIdentity { customer { hasAnyTag(tags: ["FN-TESTER"]) } } }
cart { buyerIdentity { customer { hasAnyTag(tags: ["FN-TESTER"]) } } }
Étape 4 : vérifiez dans le checkout
Connectez-vous en tant qu’utilisateur balisé, parcourez le checkout, confirmez que la Function se déclenche. Connectez-vous en tant qu’utilisateur non balisé, confirmez que l’ancien Script s’exécute toujours. Quand la parité tient pendant quelques jours, retirez la vérification du tag et laissez la Function s’exécuter pour tout le monde.
Étape 5 : dépubliez l’ancien Script
Allez dans Apps → Script Editor → [Your Script] → Unpublish. Une fois dépublié, la Function devient la seule source de vérité.
Un workflow de déploiement qui passe vraiment à l’échelle
Ne déployez pas éternellement depuis l’ordinateur portable d’un développeur. Une fois un ou deux Scripts migrés, mettez en place un vrai pipeline CI.
Le workflow minimal viable
# .github/workflows/deploy-functions.yml name: Deploy Shopify Functions on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '20' } - uses: dtolnay/rust-toolchain@stable with: { targets: wasm32-wasip1 } - run: npm install -g @shopify/cli@latest - run: shopify app deploy --force env: SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}
# .github/workflows/deploy-functions.yml name: Deploy Shopify Functions on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '20' } - uses: dtolnay/rust-toolchain@stable with: { targets: wasm32-wasip1 } - run: npm install -g @shopify/cli@latest - run: shopify app deploy --force env: SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}
Générez le jeton Partner depuis votre tableau de bord Partner sous Settings → Tokens. Désormais, chaque fusion dans main déploie vos Functions. Fini les fils Slack du genre « Mike a-t-il déployé ça ? ».
Versioning et rollback
shopify app deploy crée un instantané versionné. Pour revenir en arrière :
shopify app versions list shopify app release --version <previous-version-id
shopify app versions list shopify app release --version <previous-version-id
Comparé aux Scripts — où le rollback voulait dire « se souvenir de l’ancien code et le recoller » — c’est le jour et la nuit.

Ce que la plupart des équipes font mal
Après avoir accompagné des marchands Plus dans cette migration au cours de l’année écoulée, on retrouve toujours les cinq mêmes erreurs.
1. Traiter les Functions comme un portage Script 1:1. Ce n’est pas le cas. Une seule Function peut remplacer trois Scripts avec un branchement plus propre. Auditez vos Scripts comme un système avant de les réécrire.
2. Oublier les scopes de lecture. De nombreuses Functions ont besoin de read_customers, read_orders ou write_discounts. Ajoutez-les dans shopify.app.toml sous scopes et réautorisez l’app, sinon votre requête d’entrée renverra null.
3. Exécuter les Functions sur des clients non balisés sans test de parité. Même si votre code semble correct, les cas limites (panier vide, cartes-cadeaux, crédit boutique, brouillons B2B) révéleront des problèmes. Un déploiement progressif par tag vous coûte deux jours et vous évite une panne P1.
4. Sauter le Customizations Report. C’est le meilleur inventaire de ce qui tourne réellement. Ne migrez pas depuis la mémoire — migrez depuis le rapport.
5. Codifier en dur les IDs de collections et les tags clients. Utilisez la configuration de Function via des metafields si vous avez besoin de valeurs ajustables par le marchand. La CLI peut scaffold une configuration basée sur des metafields — voir la documentation Shopify sur la configuration des Functions.
Checklist de migration pour les 75 prochains jours
Un plan réaliste semaine par semaine pour arriver sereinement au 30 juin.
Semaine | Action |
|---|---|
Semaine 1 (cette semaine) | Récupérez le Customizations Report. Inventoriez chaque Script. Décidez : Function, app publique ou suppression. |
Semaines 2–3 | Configurez l’environnement de développement local. Scaffold la première Function. Migrez le Script le plus simple (généralement une règle de masquage de paiement). |
Semaines 4–6 | Migrez les Scripts de remise. Ce sont eux qui prennent le plus de temps, car l’API Discounts est la plus riche. Testez minutieusement avec des tags. |
Semaines 7–8 | Migrez les Scripts d’expédition / livraison. Activez les Delivery Customizations via l’Admin. |
Semaines 9–10 | Mettez en place le pipeline CI. Déplacez tous les déploiements hors des ordinateurs portables des développeurs. |
Semaine 11 (mi-juin) | Vérification finale de parité. Dépubliez tous les Scripts. Faites tourner la boutique uniquement sur les Functions pendant deux semaines. |
30 juin | Le jour de la bascule arrive. Rien ne casse parce que vous avez terminé tôt. |
Si vous commencez cette semaine, vous avez de la marge. Si vous commencez en juin, vous n’en avez pas.

Questions fréquemment posées
Ai-je besoin de Shopify Plus pour utiliser les Functions ?
Les Functions personnalisées nécessitent Shopify Plus, mais les Functions d’apps publiques fonctionnent sur tous les plans. Si vous n’êtes pas sur Plus, vous avez deux options : installer depuis le Shopify App Store une app publique qui fournit la Function pour vous, ou passer à Plus pour écrire vos propres Functions personnalisées. La plupart des grands marchands qui avaient des Scripts étaient déjà sur Plus, donc cela ne change que rarement quoi que ce soit en pratique.
Puis-je écrire des Functions en TypeScript ?
Oui — TypeScript est entièrement pris en charge et la CLI le scaffold pour vous. Quand vous exécutez shopify app generate extension et choisissez « JavaScript », le projet généré inclut des déclarations de types depuis import("../generated/api"). Vous pouvez convertir les fichiers en .ts et ajouter un tsconfig.json si vous préférez. Le résultat compilé (WASM) est identique quel que soit le langage source.
À quelle vitesse s’exécutent les Functions par rapport aux Scripts ?
Les Functions s’exécutent généralement en moins de 5 ms — nettement plus vite que les Scripts Ruby. Comme elles sont compilées en WebAssembly et tournent dans un runtime très serré, Shopify impose une limite d’exécution de 5 ms. Si votre Function la dépasse, l’opération est ignorée et votre Function ne renvoie aucune opération. En pratique, une Function bien écrite utilise 1 à 2 ms. Le plafond de performance est bien plus élevé que celui des Scripts.
Une Function peut-elle appeler une API externe ?
Non — les Functions ne peuvent pas effectuer de requêtes réseau. Elles font uniquement du calcul pur : données d’entrée du panier → opérations de sortie. Si vous avez besoin de données externes (une recherche CRM, une vérification d’inventaire en temps réel), vous devrez soit stocker ces données à l’avance dans des metafields, soit utiliser une autre surface (App Proxy, webhooks, Cart Transform avec recherche côté backend). C’est la raison la plus courante pour laquelle les équipes doivent repenser la solution plutôt que simplement porter le code.
Quelle est la différence entre Cart Transform et Discounts ?
Les Discounts appliquent des changements de prix ; Cart Transform modifie le contenu du panier. Utilisez l’API Discounts pour appliquer -10 %, la livraison gratuite ou un BOGO. Utilisez Cart Transform pour regrouper deux produits en une seule ligne, ou découper un variant en plusieurs. Beaucoup d’anciens Scripts mélangeaient les deux sujets — lors de la migration, séparez-les en deux Functions.
Comment tester une Function localement sans boutique de développement ?
Vous pouvez exécuter des tests unitaires avec cargo test (Rust) ou npm test (JS), mais le test d’intégration complet nécessite une boutique de développement. La CLI fournit shopify app function run, qui exécute votre Function sur un fichier d’entrée exemple — utile pour itérer rapidement. Mais pour confirmer le comportement du checkout de bout en bout, il vous faut une vraie boutique avec un vrai panier.
Puis-je avoir plusieurs Functions du même type ?
Oui — Shopify prend en charge plusieurs Functions par cible, et elles s’exécutent dans un ordre déterministe. Pour les remises, l’ordre est régi par les règles de cumul de remises de Shopify. Pour les Delivery et Payment Customizations, vous pouvez chaîner des Functions, mais chaque sortie alimente la suivante. La plupart des équipes utilisent une Function par type pour simplifier.
Que devient mon Script après le déploiement de la Function ?
Les deux s’exécutent en parallèle jusqu’à ce que vous dépubliiez le Script dans Apps → Script Editor. C’est intentionnel — cela vous donne la fenêtre de test en parallèle. Après avoir vérifié que la Function fonctionne, dépubliez manuellement le Script. Après le 30 juin 2026, tous les Scripts cessent de s’exécuter, que vous les ayez dépubliés ou non.
La migration affectera-t-elle mon SEO ou mon thème ?
Non — les Functions s’exécutent côté serveur au checkout et ne touchent jamais votre thème ni vos pages produit. Elles ne modifient que les remises, les options d’expédition et les moyens de paiement au checkout. Votre vitrine, vos templates produit et votre SEO restent totalement intacts.
Comment migrer un Script qui utilise Input.line_items avec des propriétés personnalisées ?
Les propriétés personnalisées sont accessibles via le champ attribute sur les lignes de panier dans l’entrée GraphQL. Ajoutez attribute(key: "your-key") { value } à l’intérieur de la sélection lines. La Function les lit de la même manière que les Scripts lisaient les propriétés de line item — simplement via GraphQL au lieu d’appels de méthodes Ruby.
Qu’en est-il des analytics et des tags de commande ? Les Functions peuvent-elles écrire des données ?
Les Functions ne peuvent pas écrire de tags de commande ni déclencher elles-mêmes des webhooks — elles ne renvoient que des opérations sur le panier actuel. Pour le tagging ou les workflows en aval, utilisez Shopify Flow déclenché par l’événement de création de commande. De nombreux marchands combinent une Function (pour la remise) avec un Flow (pour tagger la commande avec « VOLUME-DISCOUNT-APPLIED »).
Existe-t-il une app publique que je peux installer au lieu de construire une Function personnalisée ?
Oui — le Shopify App Store propose des dizaines d’apps qui encapsulent des Functions pour les cas d’usage courants. Cherchez « discount function », « delivery customization » ou « payment customization ». Pour des cas simples (remises sur volume, masquage de moyens de paiement par tag, livraison gratuite au-dessus de X), une app existante peut vous faire gagner des jours de travail. Réservez les Functions personnalisées à une logique réellement unique à votre activité.
Que se passe-t-il si je rate la date limite du 30 juin ?
Le Script cesse de s’exécuter — il n’y a ni repli, ni délai de grâce, ni prolongation. Ce que faisait le Script (la remise, le tarif de livraison masqué, le moyen de paiement bloqué) revient au comportement par défaut à minuit UTC le 1er juillet. Si votre activité dépend de cette logique, prévoyez d’être en ligne bien avant cette date. La migration prend plus de temps que la plupart des équipes ne l’estiment, surtout avec les tests de parité.
Puis-je supprimer complètement l’application Script Editor ?
Vous pouvez la désinstaller après le 30 juin 2026, mais Shopify la supprimera probablement automatiquement. Une fois les Scripts arrêtés, l’éditeur n’a plus d’utilité. Vous pouvez aussi dépublier tous les Scripts maintenant et désinstaller l’app immédiatement si vous avez terminé la migration — vos Functions sont indépendantes.
Que faire cette semaine
Ne lisez pas cet article pour fermer ensuite l’onglet. Faites ces quatre choses dans les sept prochains jours.
1. Récupérez le Customizations Report. Allez dans Settings → Checkout → Customizations Report. Exportez-le. C’est votre backlog de migration.
2. Configurez votre environnement de développement. Installez Node 18+, Shopify CLI et Rust (le cas échéant). Vérifiez que shopify version fonctionne. Temps total : 30 minutes.
3. Scaffold et expédiez une toute petite Function sur une boutique de développement. Prenez le Script le plus simple que vous avez — généralement une règle de masquage de paiement. Migrez-le de bout en bout. Même s’il n’arrive jamais en production, vous aurez validé la chaîne d’outils.
4. Réservez du temps dans le calendrier pour les huit prochaines semaines. Les migrations ne se font pas dans les moments libres entre deux sprints. Bloquez un créneau récurrent — par exemple tous les mardis et jeudis après-midi — et traitez-le comme une release.
D’après les équipes que nous avons vues migrer, le schéma est toujours le même : deux semaines d’hésitation, trois semaines de vrai travail, une semaine de nettoyage. Cela fait six semaines. Vous en avez dix. La marge existe — utilisez-la pour les revues de code et l’assurance qualité, pas pour repousser le démarrage.
Et une fois votre logique de checkout réglée, la prochaine chose que la plupart des équipes traitent est l’après-achat — changements d’adresse client, échanges, ajouts de remises après la commande. Si cela fait partie de votre feuille de route (et cela devrait l’être, que vous soyez un opérateur Plus à gros volume ou une boutique Advanced centrée sur l’expérience client), Revize est sur le Shopify App Store et fonctionne aux côtés de chaque Function que vous construirez ici.
Ressources
Articles associés
Nous sommes le 16 avril 2026. Hier — le 15 avril — était le jour où Shopify a verrouillé définitivement l’éditeur de Scripts. Vous ne pouvez plus créer ni publier de nouveau Script. L’arrêt d’exécution arrivera dans 75 jours, le 30 juin 2026.
Si vous êtes développeur Shopify Plus — ou l’agence qui gère une boutique Plus — et que vous avez repoussé cette migration à « la prochaine sprint » pendant les douze derniers mois, vous avez un problème. Pas un problème du type « ce serait bien à corriger ». Un problème du type « votre checkout casse à minuit le 1er juillet ». La plupart des boutiques Plus ont accumulé 5 à 20 Scripts au fil des ans, chacun alimentant discrètement une règle de remise, un masquage d’expédition ou une restriction de paiement dont plus personne ne se souvient avoir écrit le code.
Ce guide est le manuel technique de migration que nous aurions voulu avoir en janvier. Il couvre le vrai code — pas seulement la stratégie. Une fois votre lecture terminée, vous saurez comment scaffold une Function avec Shopify CLI, écrire la logique Rust ou JavaScript pour les remises, les personnalisations de livraison et les personnalisations de paiement, la tester prudemment sur un sous-ensemble balisé de vos clients, puis la déployer en production sans casser votre checkout existant.
Retirons les Scripts de votre boutique et mettons-y les Functions.

Réponse rapide : Scripts → Functions en 60 secondes
La migration en un paragraphe : les Shopify Scripts (code Ruby dans l’éditeur de Scripts, réservé à Plus) sont remplacés par les Shopify Functions (modules WebAssembly écrits en Rust ou en JavaScript, disponibles sur tous les plans). Vous créez une Function avec
shopify app generate extension, vous écrivez une requêterun.graphqlqui récupère les données de panier dont vous avez besoin, vous écrivez un fichierrun.rsourun.jsqui renvoie des opérations (remises, méthodes d’expédition masquées, etc.), puis vous déployez avecshopify app deployet l’activez via l’Admin ou une mutation GraphQL. Les Functions s’exécutent en WASM compilé avec une latence inférieure à 5 ms, fonctionnent sur tous les plans et constituent désormais le seul chemin de personnalisation pris en charge par Shopify.
Ce qui change réellement le 30 juin
Avant de toucher au code, mettons les dates au clair. Il y en a deux, et elles comptent toutes les deux.
Date | Ce qui se passe | Votre action |
|---|---|---|
15 avril 2026 (passé) | L’éditeur de Scripts est en lecture seule. Plus de nouveaux Scripts. Plus de modifications des Scripts existants. | Les Scripts existants s’exécutent encore. Migrez maintenant ou figez votre logique. |
30 juin 2026 | Tous les Shopify Scripts cessent de s’exécuter. Point final. | Votre remplacement par une Function doit être en ligne avant cette date. |
La migration est binaire. Soit votre Function est déployée d’ici le 30 juin et votre checkout continue de fonctionner, soit ce n’est pas le cas — et chaque panier concerné retombe silencieusement sur la tarification standard, les frais de livraison standard et tous les moyens de paiement activés. Il n’y a pas de demi-mesure. Le Script s’exécute ou non, et après le 30 juin, il ne s’exécute plus.
Astuce : Ouvrez
Settings → Checkout → Customizations Reportdans votre administration Shopify. Cela liste chaque Script actif de votre boutique, ce qu’il fait et le type de remplacement par Function recommandé. Commencez par là.
Functions vs Scripts : ce qui a vraiment changé
Dimension | Shopify Scripts (obsolètes) | Shopify Functions (remplacement) |
|---|---|---|
Langage | DSL Ruby (spécifique à Shopify) | Rust, JavaScript, TypeScript |
Runtime | Ruby sandboxé sur l’infrastructure Shopify | WebAssembly (WASM) — exécution sous les 5 ms |
Disponibilité selon le plan | Uniquement Plus | Tous les plans (les apps personnalisées nécessitent Plus ; les apps publiques sont ouvertes) |
Éditeur | Éditeur de Scripts dans l’admin | IDE local + Shopify CLI |
Versioning | Aucun — modifications en direct | Compatible Git — contrôle de version complet |
Tests | Manuel dans le checkout | Développement local avec |
Déploiement | Cliquer sur « Enregistrer » dans l’admin |
|
Cibles | Articles, expédition, paiements | Remises, transformation du panier, validation, personnalisation de livraison, personnalisation de paiement, routage des commandes, contraintes de fulfillment, et plus |
Le changement architectural est important. Les Scripts, c’était « retoucher du Ruby dans une zone de texte ». Les Functions, c’est « écrire une vraie application, la versionner, la tester localement, la déployer via un vrai pipeline CI ». La courbe d’apprentissage est plus raide. Mais c’est aussi la dernière migration que vous ferez pour la logique de checkout dans un avenir prévisible — les Functions sont l’engagement à long terme de Shopify, pas un palliatif comme l’étaient finalement les Scripts.
Associer vos Scripts au bon type de Function
Chaque Script que vous avez aujourd’hui correspond à une seule API Function. Voici le tableau de correspondance à garder sous les yeux.
Ancien type de Script | Ce qu’il faisait | Nouvelle API Function | Cible de la Function |
|---|---|---|---|
Line Item Script | Appliquer des remises à des produits / clients / conditions de panier spécifiques | Cart & Checkout Discounts API |
|
Shipping Script (remise) | Livraison gratuite / remisée selon les règles du panier | Cart & Checkout Discounts API |
|
Shipping Script (masquer / renommer / réordonner) | Masquer un tarif de livraison au-dessus de X $, renommer « Standard » en « Gratuit au-delà de 50 $ » | Delivery Customization API |
|
Payment Script | Masquer PayPal pour le B2B, masquer le paiement à la livraison au-dessus de 500 $, réordonner les moyens | Payment Customization API |
|
Script de modification du panier (rare) | Regrouper des produits, remplacer des line items | Cart Transform API |
|
Script de blocage du checkout | Refuser le panier si le mélange de SKU est invalide | Cart & Checkout Validation API |
|
Si vous avez dix Scripts, vous construirez probablement trois à cinq Functions — plusieurs Scripts se regroupent souvent en une seule Function avec une logique de branchement plus propre.

Prérequis : configurer votre environnement de développement local
Avant de scaffold une Function, vous devez installer trois éléments en local. Lancez ces vérifications dans votre terminal.
1. Node.js 18+
node --version # Doit être >= 18.0.0
Si votre version est plus ancienne, installez-la via nvm ou téléchargez-la depuis nodejs.org.
2. Shopify CLI 3+
npm install -g @shopify/cli@latest shopify version # Devrait afficher 3.x ou plus
3. Chaîne d’outils Rust (uniquement si vous écrivez vos Functions en Rust)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup target add wasm32-wasip1 cargo --version
Les Functions JavaScript n’ont pas besoin de Rust. Choisissez un langage pour votre équipe et tenez-vous-y — en utiliser deux ajoute une surcharge de maintenance.
4. Une boutique de développement
Connectez-vous à votre tableau de bord Partner et créez une nouvelle boutique de développement, ou utilisez-en une existante. Vous déploierez les Functions sur cette boutique avant de passer en production.
Scaffolder votre première Function
La CLI génère la majeure partie du boilerplate. Dans n’importe quel répertoire :
# Créez une nouvelle app Shopify (ignorez cette étape si vous en avez déjà une) shopify app init my-checkout-functions cd my-checkout-functions # Générez une extension Function shopify app generate extension
La CLI vous guide à travers les invites. Pour une Function de remise, vous choisiriez :
Type : Function
Template :
discount(oucart_checkout_validation,delivery_customization,payment_customization, etc.)Langage : Rust ou JavaScript
Nom : quelque chose comme
volume-discount-fn
Cela crée extensions/volume-discount-fn/ avec :
extensions/volume-discount-fn/ ├── shopify.extension.toml # Configuration de la Function — cibles, build, version ├── src/ │ ├── cart_lines_discounts_generate_run.graphql # Requête d’entrée │ └── cart_lines_discounts_generate_run.rs # Logique de la Function ├── Cargo.toml # Dépendances Rust (Rust uniquement) └── README.md
Les trois fichiers que vous modifierez constamment sont le .toml (configuration), le .graphql (entrée) et le .rs / .js (logique). C’est tout.
Tutoriel 1 : remplacer un Line Item Script (remise sur volume)
Supposons que votre ancien Script accordait 10 % de remise sur le sous-total de la commande lorsque le panier contenait 5 unités ou plus d’une collection spécifique. Voici l’équivalent en Function.
Étape 1.1 : la configuration (shopify.extension.toml)
api_version = "2026-01" [[extensions]] name = "volume-discount-fn" handle = "volume-discount-fn" type = "function" [[extensions.targeting]] target = "cart.lines.discounts.generate.run" input_query = "src/cart_lines_discounts_generate_run.graphql" export = "cart_lines_discounts_generate_run" [extensions.build] command = "cargo build --target=wasm32-wasip1 --release" path = "target/wasm32-wasip1/release/volume-discount-fn.wasm" watch = ["src/**/*.rs"]
Étape 1.2 : la requête d’entrée (src/cart_lines_discounts_generate_run.graphql)
query Input { cart { lines { id quantity cost { subtotalAmount { amount } } merchandise { ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/123456789"]) } } } } } discount { discountClasses } }
Astuce : Les Functions ne voient que les données que vous interrogez. Gardez le GraphQL minimal — chaque champ que vous omettez signifie une exécution plus rapide et moins coûteuse.
Étape 1.3 : la logique (src/cart_lines_discounts_generate_run.rs)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { // Quitter si la classe de remise ne correspond pas let has_order_discount = input .discount() .discount_classes() .contains(&schema::DiscountClass::Order); if !has_order_discount { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Additionner les quantités des articles de la collection cible let qualifying_qty: i64 = input .cart() .lines() .iter() .filter(|line| { if let schema::Merchandise::ProductVariant(v) = line.merchandise() { *v.product().in_any_collection() } else { false } }) .map(|line| *line.quantity()) .sum(); if qualifying_qty < 5 { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Appliquer 10 % de remise sur le sous-total de la commande Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some("Volume discount: 10% off".to_string()), value: schema::OrderDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal(10.0) } ), conditions: None, associated_discount_code: None, }], }, )], }) }
Étape 1.4 : tester, déployer, activer
# Développement local avec rechargement à chaud shopify app dev # Une fois prêt, déployez shopify app deploy # Dans le panneau GraphiQL qui s’ouvre (appuyez sur `g` dans le terminal de dev), # créez la remise automatique qui utilise votre Function :
mutation { discountAutomaticAppCreate( automaticAppDiscount: { title: "Volume Discount (5+ collection items)" functionHandle: "volume-discount-fn" discountClasses: [ORDER] startsAt: "2026-04-16T00:00:00Z" } ) { automaticAppDiscount { discountId } userErrors { field message } } }
C’est tout. La Function est en ligne, versionnée, et remplace entièrement l’ancien Script.
Tutoriel 2 : remplacer un Shipping Script (masquer une méthode au-dessus d’un seuil de panier)
Script courant : « Masquer la livraison Express lorsque le sous-total du panier dépasse 500 $ afin d’éviter une expédition de nuit coûteuse sur les grosses commandes. » Voici la version Function en Delivery Customization.
Étape 2.1 : scaffold
shopify app generate extension --template delivery_customization --name hide-express-fn
Étape 2.2 : requête d’entrée (src/run.graphql)
query Input { cart { cost { subtotalAmount { amount } } deliveryGroups { deliveryOptions { handle title } } } }
Étape 2.3 : logique (src/run.js — variante JavaScript)
// @ts-check /** * @typedef {import("../generated/api").RunInput} RunInput * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult */ const NO_CHANGES = { operations: [] }; const THRESHOLD = 500.0; const HIDE_TITLES = ["Express", "Overnight"]; /** * @param {RunInput} input * @returns {FunctionRunResult} */ export function run(input) { const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount); if (subtotal < THRESHOLD) return NO_CHANGES; const operations = input.cart.deliveryGroups.flatMap((group) => group.deliveryOptions .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t))) .map((opt) => ({ hide: { deliveryOptionHandle: opt.handle }, })) ); return { operations }; }
Étape 2.4 : activation via l’Admin (aucun GraphQL requis)
Les Delivery Customizations disposent aussi d’une interface d’administration intégrée. Après shopify app deploy :
Allez dans Settings → Shipping and delivery
Faites défiler jusqu’à la section Customizations en bas
Cliquez sur Add customization → sélectionnez votre Function
Enregistrez
La règle de masquage est active en production. Aucune mutation n’est nécessaire.

Tutoriel 3 : remplacer un Payment Script (masquer le paiement à la livraison pour le B2B)
Ancien Script : « Masquer le paiement à la livraison pour tout client ayant le tag 'B2B'. » Voici la version Payment Customization.
Étape 3.1 : scaffold
shopify app generate extension --template payment_customization --name hide-cod-b2b-fn
Étape 3.2 : requête d’entrée
query Input { cart { buyerIdentity { customer { hasTags(tags: [{ tag: "B2B" }]) { tag hasTag } } } } paymentMethods { id name } }
Étape 3.3 : logique (src/run.js)
const NO_CHANGES = { operations: [] }; export function run(input) { const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0]; const isB2B = tagCheck?.hasTag === true; if (!isB2B) return NO_CHANGES; const codMethod = input.paymentMethods.find((pm) => pm.name.toLowerCase().includes("cash on delivery") ); if (!codMethod) return NO_CHANGES; return { operations: [{ hide: { paymentMethodId: codMethod.id } }], }; }
Étape 3.4 : activation
Les Payment Customizations disposent aussi d’une interface d’administration sous Settings → Payments → Customizations. Même processus que pour la livraison — choisissez votre Function, enregistrez, terminé.
Puisque vous lisez ceci — un mot sur l’après-achat
Petite précision puisque c’est le blog de Revize. Revize gère ce que les Functions ne peuvent pas toucher — une fois qu’une commande est passée, les clients veulent ajouter un article, changer une taille, corriger l’adresse de livraison, appliquer une remise qu’ils ont oubliée. Les Functions vivent au checkout. Revize vit après le checkout. Les Functions décident de ce qui est autorisé dans le panier ; Revize permet à vos clients et à votre équipe support de modifier la commande ensuite sans passer par un cycle remboursement + recréation. Cela compte pour deux types de boutiques : celles qui ont suffisamment de volume de commandes pour que l’édition manuelle s’effondre (les opérateurs Plus, évidemment, mais aussi les boutiques Advanced à fort débit), et celles dont toute la marque repose sur l’expérience client — où un e-mail du type « désolé, nous ne pouvons pas modifier cela » tue les achats répétés.
Si votre plan de migration couvre Scripts → Functions mais que vous n’avez jamais réglé les modifications de commandes après achat, vous allez heurter le prochain mur « pourquoi est-ce si compliqué ? » dans environ trois semaines. Le guide de gestion des commandes que nous venons de publier décrit tout le plan de bataille post-checkout.
Retour à la migration.
Stratégie de test : le modèle du client balisé
Les Functions n’ont pas de « mode brouillon » que vous pouvez activer ou désactiver dans l’admin. La méthode professionnelle consiste à conditionner la nouvelle Function à un tag client, à faire fonctionner en parallèle l’ancien Script et la nouvelle Function, à vérifier qu’ils produisent une sortie identique pour les utilisateurs balisés, puis à basculer.
Étape 1 : taguez vos utilisateurs de test
Dans Customers, ajoutez le tag FN-TESTER à deux ou trois comptes internes.
Étape 2 : branchez la Function sur la présence du tag
// En haut de votre fonction run let is_tester = input .cart() .buyer_identity() .and_then(|bi| bi.customer()) .map(|c| c.has_any_tag()) .unwrap_or(&false); if !*is_tester { return Ok(default_result); // Laisser passer vers le Script existant } // La nouvelle logique de la Function ne s’exécute que pour les utilisateurs balisés
Étape 3 : ajoutez hasAnyTag à votre requête d’entrée
cart { buyerIdentity { customer { hasAnyTag(tags: ["FN-TESTER"]) } } }
Étape 4 : vérifiez dans le checkout
Connectez-vous en tant qu’utilisateur balisé, parcourez le checkout, confirmez que la Function se déclenche. Connectez-vous en tant qu’utilisateur non balisé, confirmez que l’ancien Script s’exécute toujours. Quand la parité tient pendant quelques jours, retirez la vérification du tag et laissez la Function s’exécuter pour tout le monde.
Étape 5 : dépubliez l’ancien Script
Allez dans Apps → Script Editor → [Your Script] → Unpublish. Une fois dépublié, la Function devient la seule source de vérité.
Un workflow de déploiement qui passe vraiment à l’échelle
Ne déployez pas éternellement depuis l’ordinateur portable d’un développeur. Une fois un ou deux Scripts migrés, mettez en place un vrai pipeline CI.
Le workflow minimal viable
# .github/workflows/deploy-functions.yml name: Deploy Shopify Functions on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '20' } - uses: dtolnay/rust-toolchain@stable with: { targets: wasm32-wasip1 } - run: npm install -g @shopify/cli@latest - run: shopify app deploy --force env: SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}
Générez le jeton Partner depuis votre tableau de bord Partner sous Settings → Tokens. Désormais, chaque fusion dans main déploie vos Functions. Fini les fils Slack du genre « Mike a-t-il déployé ça ? ».
Versioning et rollback
shopify app deploy crée un instantané versionné. Pour revenir en arrière :
shopify app versions list shopify app release --version <previous-version-id
Comparé aux Scripts — où le rollback voulait dire « se souvenir de l’ancien code et le recoller » — c’est le jour et la nuit.

Ce que la plupart des équipes font mal
Après avoir accompagné des marchands Plus dans cette migration au cours de l’année écoulée, on retrouve toujours les cinq mêmes erreurs.
1. Traiter les Functions comme un portage Script 1:1. Ce n’est pas le cas. Une seule Function peut remplacer trois Scripts avec un branchement plus propre. Auditez vos Scripts comme un système avant de les réécrire.
2. Oublier les scopes de lecture. De nombreuses Functions ont besoin de read_customers, read_orders ou write_discounts. Ajoutez-les dans shopify.app.toml sous scopes et réautorisez l’app, sinon votre requête d’entrée renverra null.
3. Exécuter les Functions sur des clients non balisés sans test de parité. Même si votre code semble correct, les cas limites (panier vide, cartes-cadeaux, crédit boutique, brouillons B2B) révéleront des problèmes. Un déploiement progressif par tag vous coûte deux jours et vous évite une panne P1.
4. Sauter le Customizations Report. C’est le meilleur inventaire de ce qui tourne réellement. Ne migrez pas depuis la mémoire — migrez depuis le rapport.
5. Codifier en dur les IDs de collections et les tags clients. Utilisez la configuration de Function via des metafields si vous avez besoin de valeurs ajustables par le marchand. La CLI peut scaffold une configuration basée sur des metafields — voir la documentation Shopify sur la configuration des Functions.
Checklist de migration pour les 75 prochains jours
Un plan réaliste semaine par semaine pour arriver sereinement au 30 juin.
Semaine | Action |
|---|---|
Semaine 1 (cette semaine) | Récupérez le Customizations Report. Inventoriez chaque Script. Décidez : Function, app publique ou suppression. |
Semaines 2–3 | Configurez l’environnement de développement local. Scaffold la première Function. Migrez le Script le plus simple (généralement une règle de masquage de paiement). |
Semaines 4–6 | Migrez les Scripts de remise. Ce sont eux qui prennent le plus de temps, car l’API Discounts est la plus riche. Testez minutieusement avec des tags. |
Semaines 7–8 | Migrez les Scripts d’expédition / livraison. Activez les Delivery Customizations via l’Admin. |
Semaines 9–10 | Mettez en place le pipeline CI. Déplacez tous les déploiements hors des ordinateurs portables des développeurs. |
Semaine 11 (mi-juin) | Vérification finale de parité. Dépubliez tous les Scripts. Faites tourner la boutique uniquement sur les Functions pendant deux semaines. |
30 juin | Le jour de la bascule arrive. Rien ne casse parce que vous avez terminé tôt. |
Si vous commencez cette semaine, vous avez de la marge. Si vous commencez en juin, vous n’en avez pas.

Questions fréquemment posées
Ai-je besoin de Shopify Plus pour utiliser les Functions ?
Les Functions personnalisées nécessitent Shopify Plus, mais les Functions d’apps publiques fonctionnent sur tous les plans. Si vous n’êtes pas sur Plus, vous avez deux options : installer depuis le Shopify App Store une app publique qui fournit la Function pour vous, ou passer à Plus pour écrire vos propres Functions personnalisées. La plupart des grands marchands qui avaient des Scripts étaient déjà sur Plus, donc cela ne change que rarement quoi que ce soit en pratique.
Puis-je écrire des Functions en TypeScript ?
Oui — TypeScript est entièrement pris en charge et la CLI le scaffold pour vous. Quand vous exécutez shopify app generate extension et choisissez « JavaScript », le projet généré inclut des déclarations de types depuis import("../generated/api"). Vous pouvez convertir les fichiers en .ts et ajouter un tsconfig.json si vous préférez. Le résultat compilé (WASM) est identique quel que soit le langage source.
À quelle vitesse s’exécutent les Functions par rapport aux Scripts ?
Les Functions s’exécutent généralement en moins de 5 ms — nettement plus vite que les Scripts Ruby. Comme elles sont compilées en WebAssembly et tournent dans un runtime très serré, Shopify impose une limite d’exécution de 5 ms. Si votre Function la dépasse, l’opération est ignorée et votre Function ne renvoie aucune opération. En pratique, une Function bien écrite utilise 1 à 2 ms. Le plafond de performance est bien plus élevé que celui des Scripts.
Une Function peut-elle appeler une API externe ?
Non — les Functions ne peuvent pas effectuer de requêtes réseau. Elles font uniquement du calcul pur : données d’entrée du panier → opérations de sortie. Si vous avez besoin de données externes (une recherche CRM, une vérification d’inventaire en temps réel), vous devrez soit stocker ces données à l’avance dans des metafields, soit utiliser une autre surface (App Proxy, webhooks, Cart Transform avec recherche côté backend). C’est la raison la plus courante pour laquelle les équipes doivent repenser la solution plutôt que simplement porter le code.
Quelle est la différence entre Cart Transform et Discounts ?
Les Discounts appliquent des changements de prix ; Cart Transform modifie le contenu du panier. Utilisez l’API Discounts pour appliquer -10 %, la livraison gratuite ou un BOGO. Utilisez Cart Transform pour regrouper deux produits en une seule ligne, ou découper un variant en plusieurs. Beaucoup d’anciens Scripts mélangeaient les deux sujets — lors de la migration, séparez-les en deux Functions.
Comment tester une Function localement sans boutique de développement ?
Vous pouvez exécuter des tests unitaires avec cargo test (Rust) ou npm test (JS), mais le test d’intégration complet nécessite une boutique de développement. La CLI fournit shopify app function run, qui exécute votre Function sur un fichier d’entrée exemple — utile pour itérer rapidement. Mais pour confirmer le comportement du checkout de bout en bout, il vous faut une vraie boutique avec un vrai panier.
Puis-je avoir plusieurs Functions du même type ?
Oui — Shopify prend en charge plusieurs Functions par cible, et elles s’exécutent dans un ordre déterministe. Pour les remises, l’ordre est régi par les règles de cumul de remises de Shopify. Pour les Delivery et Payment Customizations, vous pouvez chaîner des Functions, mais chaque sortie alimente la suivante. La plupart des équipes utilisent une Function par type pour simplifier.
Que devient mon Script après le déploiement de la Function ?
Les deux s’exécutent en parallèle jusqu’à ce que vous dépubliiez le Script dans Apps → Script Editor. C’est intentionnel — cela vous donne la fenêtre de test en parallèle. Après avoir vérifié que la Function fonctionne, dépubliez manuellement le Script. Après le 30 juin 2026, tous les Scripts cessent de s’exécuter, que vous les ayez dépubliés ou non.
La migration affectera-t-elle mon SEO ou mon thème ?
Non — les Functions s’exécutent côté serveur au checkout et ne touchent jamais votre thème ni vos pages produit. Elles ne modifient que les remises, les options d’expédition et les moyens de paiement au checkout. Votre vitrine, vos templates produit et votre SEO restent totalement intacts.
Comment migrer un Script qui utilise Input.line_items avec des propriétés personnalisées ?
Les propriétés personnalisées sont accessibles via le champ attribute sur les lignes de panier dans l’entrée GraphQL. Ajoutez attribute(key: "your-key") { value } à l’intérieur de la sélection lines. La Function les lit de la même manière que les Scripts lisaient les propriétés de line item — simplement via GraphQL au lieu d’appels de méthodes Ruby.
Qu’en est-il des analytics et des tags de commande ? Les Functions peuvent-elles écrire des données ?
Les Functions ne peuvent pas écrire de tags de commande ni déclencher elles-mêmes des webhooks — elles ne renvoient que des opérations sur le panier actuel. Pour le tagging ou les workflows en aval, utilisez Shopify Flow déclenché par l’événement de création de commande. De nombreux marchands combinent une Function (pour la remise) avec un Flow (pour tagger la commande avec « VOLUME-DISCOUNT-APPLIED »).
Existe-t-il une app publique que je peux installer au lieu de construire une Function personnalisée ?
Oui — le Shopify App Store propose des dizaines d’apps qui encapsulent des Functions pour les cas d’usage courants. Cherchez « discount function », « delivery customization » ou « payment customization ». Pour des cas simples (remises sur volume, masquage de moyens de paiement par tag, livraison gratuite au-dessus de X), une app existante peut vous faire gagner des jours de travail. Réservez les Functions personnalisées à une logique réellement unique à votre activité.
Que se passe-t-il si je rate la date limite du 30 juin ?
Le Script cesse de s’exécuter — il n’y a ni repli, ni délai de grâce, ni prolongation. Ce que faisait le Script (la remise, le tarif de livraison masqué, le moyen de paiement bloqué) revient au comportement par défaut à minuit UTC le 1er juillet. Si votre activité dépend de cette logique, prévoyez d’être en ligne bien avant cette date. La migration prend plus de temps que la plupart des équipes ne l’estiment, surtout avec les tests de parité.
Puis-je supprimer complètement l’application Script Editor ?
Vous pouvez la désinstaller après le 30 juin 2026, mais Shopify la supprimera probablement automatiquement. Une fois les Scripts arrêtés, l’éditeur n’a plus d’utilité. Vous pouvez aussi dépublier tous les Scripts maintenant et désinstaller l’app immédiatement si vous avez terminé la migration — vos Functions sont indépendantes.
Que faire cette semaine
Ne lisez pas cet article pour fermer ensuite l’onglet. Faites ces quatre choses dans les sept prochains jours.
1. Récupérez le Customizations Report. Allez dans Settings → Checkout → Customizations Report. Exportez-le. C’est votre backlog de migration.
2. Configurez votre environnement de développement. Installez Node 18+, Shopify CLI et Rust (le cas échéant). Vérifiez que shopify version fonctionne. Temps total : 30 minutes.
3. Scaffold et expédiez une toute petite Function sur une boutique de développement. Prenez le Script le plus simple que vous avez — généralement une règle de masquage de paiement. Migrez-le de bout en bout. Même s’il n’arrive jamais en production, vous aurez validé la chaîne d’outils.
4. Réservez du temps dans le calendrier pour les huit prochaines semaines. Les migrations ne se font pas dans les moments libres entre deux sprints. Bloquez un créneau récurrent — par exemple tous les mardis et jeudis après-midi — et traitez-le comme une release.
D’après les équipes que nous avons vues migrer, le schéma est toujours le même : deux semaines d’hésitation, trois semaines de vrai travail, une semaine de nettoyage. Cela fait six semaines. Vous en avez dix. La marge existe — utilisez-la pour les revues de code et l’assurance qualité, pas pour repousser le démarrage.
Et une fois votre logique de checkout réglée, la prochaine chose que la plupart des équipes traitent est l’après-achat — changements d’adresse client, échanges, ajouts de remises après la commande. Si cela fait partie de votre feuille de route (et cela devrait l’être, que vous soyez un opérateur Plus à gros volume ou une boutique Advanced centrée sur l’expérience client), Revize est sur le Shopify App Store et fonctionne aux côtés de chaque Function que vous construirez ici.
Ressources
Articles associés
Nous sommes le 16 avril 2026. Hier — le 15 avril — était le jour où Shopify a verrouillé définitivement l’éditeur de Scripts. Vous ne pouvez plus créer ni publier de nouveau Script. L’arrêt d’exécution arrivera dans 75 jours, le 30 juin 2026.
Si vous êtes développeur Shopify Plus — ou l’agence qui gère une boutique Plus — et que vous avez repoussé cette migration à « la prochaine sprint » pendant les douze derniers mois, vous avez un problème. Pas un problème du type « ce serait bien à corriger ». Un problème du type « votre checkout casse à minuit le 1er juillet ». La plupart des boutiques Plus ont accumulé 5 à 20 Scripts au fil des ans, chacun alimentant discrètement une règle de remise, un masquage d’expédition ou une restriction de paiement dont plus personne ne se souvient avoir écrit le code.
Ce guide est le manuel technique de migration que nous aurions voulu avoir en janvier. Il couvre le vrai code — pas seulement la stratégie. Une fois votre lecture terminée, vous saurez comment scaffold une Function avec Shopify CLI, écrire la logique Rust ou JavaScript pour les remises, les personnalisations de livraison et les personnalisations de paiement, la tester prudemment sur un sous-ensemble balisé de vos clients, puis la déployer en production sans casser votre checkout existant.
Retirons les Scripts de votre boutique et mettons-y les Functions.

Réponse rapide : Scripts → Functions en 60 secondes
La migration en un paragraphe : les Shopify Scripts (code Ruby dans l’éditeur de Scripts, réservé à Plus) sont remplacés par les Shopify Functions (modules WebAssembly écrits en Rust ou en JavaScript, disponibles sur tous les plans). Vous créez une Function avec
shopify app generate extension, vous écrivez une requêterun.graphqlqui récupère les données de panier dont vous avez besoin, vous écrivez un fichierrun.rsourun.jsqui renvoie des opérations (remises, méthodes d’expédition masquées, etc.), puis vous déployez avecshopify app deployet l’activez via l’Admin ou une mutation GraphQL. Les Functions s’exécutent en WASM compilé avec une latence inférieure à 5 ms, fonctionnent sur tous les plans et constituent désormais le seul chemin de personnalisation pris en charge par Shopify.
Ce qui change réellement le 30 juin
Avant de toucher au code, mettons les dates au clair. Il y en a deux, et elles comptent toutes les deux.
Date | Ce qui se passe | Votre action |
|---|---|---|
15 avril 2026 (passé) | L’éditeur de Scripts est en lecture seule. Plus de nouveaux Scripts. Plus de modifications des Scripts existants. | Les Scripts existants s’exécutent encore. Migrez maintenant ou figez votre logique. |
30 juin 2026 | Tous les Shopify Scripts cessent de s’exécuter. Point final. | Votre remplacement par une Function doit être en ligne avant cette date. |
La migration est binaire. Soit votre Function est déployée d’ici le 30 juin et votre checkout continue de fonctionner, soit ce n’est pas le cas — et chaque panier concerné retombe silencieusement sur la tarification standard, les frais de livraison standard et tous les moyens de paiement activés. Il n’y a pas de demi-mesure. Le Script s’exécute ou non, et après le 30 juin, il ne s’exécute plus.
Astuce : Ouvrez
Settings → Checkout → Customizations Reportdans votre administration Shopify. Cela liste chaque Script actif de votre boutique, ce qu’il fait et le type de remplacement par Function recommandé. Commencez par là.
Functions vs Scripts : ce qui a vraiment changé
Dimension | Shopify Scripts (obsolètes) | Shopify Functions (remplacement) |
|---|---|---|
Langage | DSL Ruby (spécifique à Shopify) | Rust, JavaScript, TypeScript |
Runtime | Ruby sandboxé sur l’infrastructure Shopify | WebAssembly (WASM) — exécution sous les 5 ms |
Disponibilité selon le plan | Uniquement Plus | Tous les plans (les apps personnalisées nécessitent Plus ; les apps publiques sont ouvertes) |
Éditeur | Éditeur de Scripts dans l’admin | IDE local + Shopify CLI |
Versioning | Aucun — modifications en direct | Compatible Git — contrôle de version complet |
Tests | Manuel dans le checkout | Développement local avec |
Déploiement | Cliquer sur « Enregistrer » dans l’admin |
|
Cibles | Articles, expédition, paiements | Remises, transformation du panier, validation, personnalisation de livraison, personnalisation de paiement, routage des commandes, contraintes de fulfillment, et plus |
Le changement architectural est important. Les Scripts, c’était « retoucher du Ruby dans une zone de texte ». Les Functions, c’est « écrire une vraie application, la versionner, la tester localement, la déployer via un vrai pipeline CI ». La courbe d’apprentissage est plus raide. Mais c’est aussi la dernière migration que vous ferez pour la logique de checkout dans un avenir prévisible — les Functions sont l’engagement à long terme de Shopify, pas un palliatif comme l’étaient finalement les Scripts.
Associer vos Scripts au bon type de Function
Chaque Script que vous avez aujourd’hui correspond à une seule API Function. Voici le tableau de correspondance à garder sous les yeux.
Ancien type de Script | Ce qu’il faisait | Nouvelle API Function | Cible de la Function |
|---|---|---|---|
Line Item Script | Appliquer des remises à des produits / clients / conditions de panier spécifiques | Cart & Checkout Discounts API |
|
Shipping Script (remise) | Livraison gratuite / remisée selon les règles du panier | Cart & Checkout Discounts API |
|
Shipping Script (masquer / renommer / réordonner) | Masquer un tarif de livraison au-dessus de X $, renommer « Standard » en « Gratuit au-delà de 50 $ » | Delivery Customization API |
|
Payment Script | Masquer PayPal pour le B2B, masquer le paiement à la livraison au-dessus de 500 $, réordonner les moyens | Payment Customization API |
|
Script de modification du panier (rare) | Regrouper des produits, remplacer des line items | Cart Transform API |
|
Script de blocage du checkout | Refuser le panier si le mélange de SKU est invalide | Cart & Checkout Validation API |
|
Si vous avez dix Scripts, vous construirez probablement trois à cinq Functions — plusieurs Scripts se regroupent souvent en une seule Function avec une logique de branchement plus propre.

Prérequis : configurer votre environnement de développement local
Avant de scaffold une Function, vous devez installer trois éléments en local. Lancez ces vérifications dans votre terminal.
1. Node.js 18+
node --version # Doit être >= 18.0.0
Si votre version est plus ancienne, installez-la via nvm ou téléchargez-la depuis nodejs.org.
2. Shopify CLI 3+
npm install -g @shopify/cli@latest shopify version # Devrait afficher 3.x ou plus
3. Chaîne d’outils Rust (uniquement si vous écrivez vos Functions en Rust)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup target add wasm32-wasip1 cargo --version
Les Functions JavaScript n’ont pas besoin de Rust. Choisissez un langage pour votre équipe et tenez-vous-y — en utiliser deux ajoute une surcharge de maintenance.
4. Une boutique de développement
Connectez-vous à votre tableau de bord Partner et créez une nouvelle boutique de développement, ou utilisez-en une existante. Vous déploierez les Functions sur cette boutique avant de passer en production.
Scaffolder votre première Function
La CLI génère la majeure partie du boilerplate. Dans n’importe quel répertoire :
# Créez une nouvelle app Shopify (ignorez cette étape si vous en avez déjà une) shopify app init my-checkout-functions cd my-checkout-functions # Générez une extension Function shopify app generate extension
La CLI vous guide à travers les invites. Pour une Function de remise, vous choisiriez :
Type : Function
Template :
discount(oucart_checkout_validation,delivery_customization,payment_customization, etc.)Langage : Rust ou JavaScript
Nom : quelque chose comme
volume-discount-fn
Cela crée extensions/volume-discount-fn/ avec :
extensions/volume-discount-fn/ ├── shopify.extension.toml # Configuration de la Function — cibles, build, version ├── src/ │ ├── cart_lines_discounts_generate_run.graphql # Requête d’entrée │ └── cart_lines_discounts_generate_run.rs # Logique de la Function ├── Cargo.toml # Dépendances Rust (Rust uniquement) └── README.md
Les trois fichiers que vous modifierez constamment sont le .toml (configuration), le .graphql (entrée) et le .rs / .js (logique). C’est tout.
Tutoriel 1 : remplacer un Line Item Script (remise sur volume)
Supposons que votre ancien Script accordait 10 % de remise sur le sous-total de la commande lorsque le panier contenait 5 unités ou plus d’une collection spécifique. Voici l’équivalent en Function.
Étape 1.1 : la configuration (shopify.extension.toml)
api_version = "2026-01" [[extensions]] name = "volume-discount-fn" handle = "volume-discount-fn" type = "function" [[extensions.targeting]] target = "cart.lines.discounts.generate.run" input_query = "src/cart_lines_discounts_generate_run.graphql" export = "cart_lines_discounts_generate_run" [extensions.build] command = "cargo build --target=wasm32-wasip1 --release" path = "target/wasm32-wasip1/release/volume-discount-fn.wasm" watch = ["src/**/*.rs"]
Étape 1.2 : la requête d’entrée (src/cart_lines_discounts_generate_run.graphql)
query Input { cart { lines { id quantity cost { subtotalAmount { amount } } merchandise { ... on ProductVariant { product { inAnyCollection(ids: ["gid://shopify/Collection/123456789"]) } } } } } discount { discountClasses } }
Astuce : Les Functions ne voient que les données que vous interrogez. Gardez le GraphQL minimal — chaque champ que vous omettez signifie une exécution plus rapide et moins coûteuse.
Étape 1.3 : la logique (src/cart_lines_discounts_generate_run.rs)
use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_lines_discounts_generate_run( input: schema::cart_lines_discounts_generate_run::Input, ) -> Result<schema::CartLinesDiscountsGenerateRunResult> { // Quitter si la classe de remise ne correspond pas let has_order_discount = input .discount() .discount_classes() .contains(&schema::DiscountClass::Order); if !has_order_discount { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Additionner les quantités des articles de la collection cible let qualifying_qty: i64 = input .cart() .lines() .iter() .filter(|line| { if let schema::Merchandise::ProductVariant(v) = line.merchandise() { *v.product().in_any_collection() } else { false } }) .map(|line| *line.quantity()) .sum(); if qualifying_qty < 5 { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } // Appliquer 10 % de remise sur le sous-total de la commande Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![schema::CartOperation::OrderDiscountsAdd( schema::OrderDiscountsAddOperation { selection_strategy: schema::OrderDiscountSelectionStrategy::First, candidates: vec![schema::OrderDiscountCandidate { targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal( schema::OrderSubtotalTarget { excluded_cart_line_ids: vec![], }, )], message: Some("Volume discount: 10% off".to_string()), value: schema::OrderDiscountCandidateValue::Percentage( schema::Percentage { value: Decimal(10.0) } ), conditions: None, associated_discount_code: None, }], }, )], }) }
Étape 1.4 : tester, déployer, activer
# Développement local avec rechargement à chaud shopify app dev # Une fois prêt, déployez shopify app deploy # Dans le panneau GraphiQL qui s’ouvre (appuyez sur `g` dans le terminal de dev), # créez la remise automatique qui utilise votre Function :
mutation { discountAutomaticAppCreate( automaticAppDiscount: { title: "Volume Discount (5+ collection items)" functionHandle: "volume-discount-fn" discountClasses: [ORDER] startsAt: "2026-04-16T00:00:00Z" } ) { automaticAppDiscount { discountId } userErrors { field message } } }
C’est tout. La Function est en ligne, versionnée, et remplace entièrement l’ancien Script.
Tutoriel 2 : remplacer un Shipping Script (masquer une méthode au-dessus d’un seuil de panier)
Script courant : « Masquer la livraison Express lorsque le sous-total du panier dépasse 500 $ afin d’éviter une expédition de nuit coûteuse sur les grosses commandes. » Voici la version Function en Delivery Customization.
Étape 2.1 : scaffold
shopify app generate extension --template delivery_customization --name hide-express-fn
Étape 2.2 : requête d’entrée (src/run.graphql)
query Input { cart { cost { subtotalAmount { amount } } deliveryGroups { deliveryOptions { handle title } } } }
Étape 2.3 : logique (src/run.js — variante JavaScript)
// @ts-check /** * @typedef {import("../generated/api").RunInput} RunInput * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult */ const NO_CHANGES = { operations: [] }; const THRESHOLD = 500.0; const HIDE_TITLES = ["Express", "Overnight"]; /** * @param {RunInput} input * @returns {FunctionRunResult} */ export function run(input) { const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount); if (subtotal < THRESHOLD) return NO_CHANGES; const operations = input.cart.deliveryGroups.flatMap((group) => group.deliveryOptions .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t))) .map((opt) => ({ hide: { deliveryOptionHandle: opt.handle }, })) ); return { operations }; }
Étape 2.4 : activation via l’Admin (aucun GraphQL requis)
Les Delivery Customizations disposent aussi d’une interface d’administration intégrée. Après shopify app deploy :
Allez dans Settings → Shipping and delivery
Faites défiler jusqu’à la section Customizations en bas
Cliquez sur Add customization → sélectionnez votre Function
Enregistrez
La règle de masquage est active en production. Aucune mutation n’est nécessaire.

Tutoriel 3 : remplacer un Payment Script (masquer le paiement à la livraison pour le B2B)
Ancien Script : « Masquer le paiement à la livraison pour tout client ayant le tag 'B2B'. » Voici la version Payment Customization.
Étape 3.1 : scaffold
shopify app generate extension --template payment_customization --name hide-cod-b2b-fn
Étape 3.2 : requête d’entrée
query Input { cart { buyerIdentity { customer { hasTags(tags: [{ tag: "B2B" }]) { tag hasTag } } } } paymentMethods { id name } }
Étape 3.3 : logique (src/run.js)
const NO_CHANGES = { operations: [] }; export function run(input) { const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0]; const isB2B = tagCheck?.hasTag === true; if (!isB2B) return NO_CHANGES; const codMethod = input.paymentMethods.find((pm) => pm.name.toLowerCase().includes("cash on delivery") ); if (!codMethod) return NO_CHANGES; return { operations: [{ hide: { paymentMethodId: codMethod.id } }], }; }
Étape 3.4 : activation
Les Payment Customizations disposent aussi d’une interface d’administration sous Settings → Payments → Customizations. Même processus que pour la livraison — choisissez votre Function, enregistrez, terminé.
Puisque vous lisez ceci — un mot sur l’après-achat
Petite précision puisque c’est le blog de Revize. Revize gère ce que les Functions ne peuvent pas toucher — une fois qu’une commande est passée, les clients veulent ajouter un article, changer une taille, corriger l’adresse de livraison, appliquer une remise qu’ils ont oubliée. Les Functions vivent au checkout. Revize vit après le checkout. Les Functions décident de ce qui est autorisé dans le panier ; Revize permet à vos clients et à votre équipe support de modifier la commande ensuite sans passer par un cycle remboursement + recréation. Cela compte pour deux types de boutiques : celles qui ont suffisamment de volume de commandes pour que l’édition manuelle s’effondre (les opérateurs Plus, évidemment, mais aussi les boutiques Advanced à fort débit), et celles dont toute la marque repose sur l’expérience client — où un e-mail du type « désolé, nous ne pouvons pas modifier cela » tue les achats répétés.
Si votre plan de migration couvre Scripts → Functions mais que vous n’avez jamais réglé les modifications de commandes après achat, vous allez heurter le prochain mur « pourquoi est-ce si compliqué ? » dans environ trois semaines. Le guide de gestion des commandes que nous venons de publier décrit tout le plan de bataille post-checkout.
Retour à la migration.
Stratégie de test : le modèle du client balisé
Les Functions n’ont pas de « mode brouillon » que vous pouvez activer ou désactiver dans l’admin. La méthode professionnelle consiste à conditionner la nouvelle Function à un tag client, à faire fonctionner en parallèle l’ancien Script et la nouvelle Function, à vérifier qu’ils produisent une sortie identique pour les utilisateurs balisés, puis à basculer.
Étape 1 : taguez vos utilisateurs de test
Dans Customers, ajoutez le tag FN-TESTER à deux ou trois comptes internes.
Étape 2 : branchez la Function sur la présence du tag
// En haut de votre fonction run let is_tester = input .cart() .buyer_identity() .and_then(|bi| bi.customer()) .map(|c| c.has_any_tag()) .unwrap_or(&false); if !*is_tester { return Ok(default_result); // Laisser passer vers le Script existant } // La nouvelle logique de la Function ne s’exécute que pour les utilisateurs balisés
Étape 3 : ajoutez hasAnyTag à votre requête d’entrée
cart { buyerIdentity { customer { hasAnyTag(tags: ["FN-TESTER"]) } } }
Étape 4 : vérifiez dans le checkout
Connectez-vous en tant qu’utilisateur balisé, parcourez le checkout, confirmez que la Function se déclenche. Connectez-vous en tant qu’utilisateur non balisé, confirmez que l’ancien Script s’exécute toujours. Quand la parité tient pendant quelques jours, retirez la vérification du tag et laissez la Function s’exécuter pour tout le monde.
Étape 5 : dépubliez l’ancien Script
Allez dans Apps → Script Editor → [Your Script] → Unpublish. Une fois dépublié, la Function devient la seule source de vérité.
Un workflow de déploiement qui passe vraiment à l’échelle
Ne déployez pas éternellement depuis l’ordinateur portable d’un développeur. Une fois un ou deux Scripts migrés, mettez en place un vrai pipeline CI.
Le workflow minimal viable
# .github/workflows/deploy-functions.yml name: Deploy Shopify Functions on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '20' } - uses: dtolnay/rust-toolchain@stable with: { targets: wasm32-wasip1 } - run: npm install -g @shopify/cli@latest - run: shopify app deploy --force env: SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}
Générez le jeton Partner depuis votre tableau de bord Partner sous Settings → Tokens. Désormais, chaque fusion dans main déploie vos Functions. Fini les fils Slack du genre « Mike a-t-il déployé ça ? ».
Versioning et rollback
shopify app deploy crée un instantané versionné. Pour revenir en arrière :
shopify app versions list shopify app release --version <previous-version-id
Comparé aux Scripts — où le rollback voulait dire « se souvenir de l’ancien code et le recoller » — c’est le jour et la nuit.

Ce que la plupart des équipes font mal
Après avoir accompagné des marchands Plus dans cette migration au cours de l’année écoulée, on retrouve toujours les cinq mêmes erreurs.
1. Traiter les Functions comme un portage Script 1:1. Ce n’est pas le cas. Une seule Function peut remplacer trois Scripts avec un branchement plus propre. Auditez vos Scripts comme un système avant de les réécrire.
2. Oublier les scopes de lecture. De nombreuses Functions ont besoin de read_customers, read_orders ou write_discounts. Ajoutez-les dans shopify.app.toml sous scopes et réautorisez l’app, sinon votre requête d’entrée renverra null.
3. Exécuter les Functions sur des clients non balisés sans test de parité. Même si votre code semble correct, les cas limites (panier vide, cartes-cadeaux, crédit boutique, brouillons B2B) révéleront des problèmes. Un déploiement progressif par tag vous coûte deux jours et vous évite une panne P1.
4. Sauter le Customizations Report. C’est le meilleur inventaire de ce qui tourne réellement. Ne migrez pas depuis la mémoire — migrez depuis le rapport.
5. Codifier en dur les IDs de collections et les tags clients. Utilisez la configuration de Function via des metafields si vous avez besoin de valeurs ajustables par le marchand. La CLI peut scaffold une configuration basée sur des metafields — voir la documentation Shopify sur la configuration des Functions.
Checklist de migration pour les 75 prochains jours
Un plan réaliste semaine par semaine pour arriver sereinement au 30 juin.
Semaine | Action |
|---|---|
Semaine 1 (cette semaine) | Récupérez le Customizations Report. Inventoriez chaque Script. Décidez : Function, app publique ou suppression. |
Semaines 2–3 | Configurez l’environnement de développement local. Scaffold la première Function. Migrez le Script le plus simple (généralement une règle de masquage de paiement). |
Semaines 4–6 | Migrez les Scripts de remise. Ce sont eux qui prennent le plus de temps, car l’API Discounts est la plus riche. Testez minutieusement avec des tags. |
Semaines 7–8 | Migrez les Scripts d’expédition / livraison. Activez les Delivery Customizations via l’Admin. |
Semaines 9–10 | Mettez en place le pipeline CI. Déplacez tous les déploiements hors des ordinateurs portables des développeurs. |
Semaine 11 (mi-juin) | Vérification finale de parité. Dépubliez tous les Scripts. Faites tourner la boutique uniquement sur les Functions pendant deux semaines. |
30 juin | Le jour de la bascule arrive. Rien ne casse parce que vous avez terminé tôt. |
Si vous commencez cette semaine, vous avez de la marge. Si vous commencez en juin, vous n’en avez pas.

Questions fréquemment posées
Ai-je besoin de Shopify Plus pour utiliser les Functions ?
Les Functions personnalisées nécessitent Shopify Plus, mais les Functions d’apps publiques fonctionnent sur tous les plans. Si vous n’êtes pas sur Plus, vous avez deux options : installer depuis le Shopify App Store une app publique qui fournit la Function pour vous, ou passer à Plus pour écrire vos propres Functions personnalisées. La plupart des grands marchands qui avaient des Scripts étaient déjà sur Plus, donc cela ne change que rarement quoi que ce soit en pratique.
Puis-je écrire des Functions en TypeScript ?
Oui — TypeScript est entièrement pris en charge et la CLI le scaffold pour vous. Quand vous exécutez shopify app generate extension et choisissez « JavaScript », le projet généré inclut des déclarations de types depuis import("../generated/api"). Vous pouvez convertir les fichiers en .ts et ajouter un tsconfig.json si vous préférez. Le résultat compilé (WASM) est identique quel que soit le langage source.
À quelle vitesse s’exécutent les Functions par rapport aux Scripts ?
Les Functions s’exécutent généralement en moins de 5 ms — nettement plus vite que les Scripts Ruby. Comme elles sont compilées en WebAssembly et tournent dans un runtime très serré, Shopify impose une limite d’exécution de 5 ms. Si votre Function la dépasse, l’opération est ignorée et votre Function ne renvoie aucune opération. En pratique, une Function bien écrite utilise 1 à 2 ms. Le plafond de performance est bien plus élevé que celui des Scripts.
Une Function peut-elle appeler une API externe ?
Non — les Functions ne peuvent pas effectuer de requêtes réseau. Elles font uniquement du calcul pur : données d’entrée du panier → opérations de sortie. Si vous avez besoin de données externes (une recherche CRM, une vérification d’inventaire en temps réel), vous devrez soit stocker ces données à l’avance dans des metafields, soit utiliser une autre surface (App Proxy, webhooks, Cart Transform avec recherche côté backend). C’est la raison la plus courante pour laquelle les équipes doivent repenser la solution plutôt que simplement porter le code.
Quelle est la différence entre Cart Transform et Discounts ?
Les Discounts appliquent des changements de prix ; Cart Transform modifie le contenu du panier. Utilisez l’API Discounts pour appliquer -10 %, la livraison gratuite ou un BOGO. Utilisez Cart Transform pour regrouper deux produits en une seule ligne, ou découper un variant en plusieurs. Beaucoup d’anciens Scripts mélangeaient les deux sujets — lors de la migration, séparez-les en deux Functions.
Comment tester une Function localement sans boutique de développement ?
Vous pouvez exécuter des tests unitaires avec cargo test (Rust) ou npm test (JS), mais le test d’intégration complet nécessite une boutique de développement. La CLI fournit shopify app function run, qui exécute votre Function sur un fichier d’entrée exemple — utile pour itérer rapidement. Mais pour confirmer le comportement du checkout de bout en bout, il vous faut une vraie boutique avec un vrai panier.
Puis-je avoir plusieurs Functions du même type ?
Oui — Shopify prend en charge plusieurs Functions par cible, et elles s’exécutent dans un ordre déterministe. Pour les remises, l’ordre est régi par les règles de cumul de remises de Shopify. Pour les Delivery et Payment Customizations, vous pouvez chaîner des Functions, mais chaque sortie alimente la suivante. La plupart des équipes utilisent une Function par type pour simplifier.
Que devient mon Script après le déploiement de la Function ?
Les deux s’exécutent en parallèle jusqu’à ce que vous dépubliiez le Script dans Apps → Script Editor. C’est intentionnel — cela vous donne la fenêtre de test en parallèle. Après avoir vérifié que la Function fonctionne, dépubliez manuellement le Script. Après le 30 juin 2026, tous les Scripts cessent de s’exécuter, que vous les ayez dépubliés ou non.
La migration affectera-t-elle mon SEO ou mon thème ?
Non — les Functions s’exécutent côté serveur au checkout et ne touchent jamais votre thème ni vos pages produit. Elles ne modifient que les remises, les options d’expédition et les moyens de paiement au checkout. Votre vitrine, vos templates produit et votre SEO restent totalement intacts.
Comment migrer un Script qui utilise Input.line_items avec des propriétés personnalisées ?
Les propriétés personnalisées sont accessibles via le champ attribute sur les lignes de panier dans l’entrée GraphQL. Ajoutez attribute(key: "your-key") { value } à l’intérieur de la sélection lines. La Function les lit de la même manière que les Scripts lisaient les propriétés de line item — simplement via GraphQL au lieu d’appels de méthodes Ruby.
Qu’en est-il des analytics et des tags de commande ? Les Functions peuvent-elles écrire des données ?
Les Functions ne peuvent pas écrire de tags de commande ni déclencher elles-mêmes des webhooks — elles ne renvoient que des opérations sur le panier actuel. Pour le tagging ou les workflows en aval, utilisez Shopify Flow déclenché par l’événement de création de commande. De nombreux marchands combinent une Function (pour la remise) avec un Flow (pour tagger la commande avec « VOLUME-DISCOUNT-APPLIED »).
Existe-t-il une app publique que je peux installer au lieu de construire une Function personnalisée ?
Oui — le Shopify App Store propose des dizaines d’apps qui encapsulent des Functions pour les cas d’usage courants. Cherchez « discount function », « delivery customization » ou « payment customization ». Pour des cas simples (remises sur volume, masquage de moyens de paiement par tag, livraison gratuite au-dessus de X), une app existante peut vous faire gagner des jours de travail. Réservez les Functions personnalisées à une logique réellement unique à votre activité.
Que se passe-t-il si je rate la date limite du 30 juin ?
Le Script cesse de s’exécuter — il n’y a ni repli, ni délai de grâce, ni prolongation. Ce que faisait le Script (la remise, le tarif de livraison masqué, le moyen de paiement bloqué) revient au comportement par défaut à minuit UTC le 1er juillet. Si votre activité dépend de cette logique, prévoyez d’être en ligne bien avant cette date. La migration prend plus de temps que la plupart des équipes ne l’estiment, surtout avec les tests de parité.
Puis-je supprimer complètement l’application Script Editor ?
Vous pouvez la désinstaller après le 30 juin 2026, mais Shopify la supprimera probablement automatiquement. Une fois les Scripts arrêtés, l’éditeur n’a plus d’utilité. Vous pouvez aussi dépublier tous les Scripts maintenant et désinstaller l’app immédiatement si vous avez terminé la migration — vos Functions sont indépendantes.
Que faire cette semaine
Ne lisez pas cet article pour fermer ensuite l’onglet. Faites ces quatre choses dans les sept prochains jours.
1. Récupérez le Customizations Report. Allez dans Settings → Checkout → Customizations Report. Exportez-le. C’est votre backlog de migration.
2. Configurez votre environnement de développement. Installez Node 18+, Shopify CLI et Rust (le cas échéant). Vérifiez que shopify version fonctionne. Temps total : 30 minutes.
3. Scaffold et expédiez une toute petite Function sur une boutique de développement. Prenez le Script le plus simple que vous avez — généralement une règle de masquage de paiement. Migrez-le de bout en bout. Même s’il n’arrive jamais en production, vous aurez validé la chaîne d’outils.
4. Réservez du temps dans le calendrier pour les huit prochaines semaines. Les migrations ne se font pas dans les moments libres entre deux sprints. Bloquez un créneau récurrent — par exemple tous les mardis et jeudis après-midi — et traitez-le comme une release.
D’après les équipes que nous avons vues migrer, le schéma est toujours le même : deux semaines d’hésitation, trois semaines de vrai travail, une semaine de nettoyage. Cela fait six semaines. Vous en avez dix. La marge existe — utilisez-la pour les revues de code et l’assurance qualité, pas pour repousser le démarrage.
Et une fois votre logique de checkout réglée, la prochaine chose que la plupart des équipes traitent est l’après-achat — changements d’adresse client, échanges, ajouts de remises après la commande. Si cela fait partie de votre feuille de route (et cela devrait l’être, que vous soyez un opérateur Plus à gros volume ou une boutique Advanced centrée sur l’expérience client), Revize est sur le Shopify App Store et fonctionne aux côtés de chaque Function que vous construirez ici.
Ressources
Articles associés
Repensez votre boutique Shopify. Misez sur l’expérience client.
© Copyright 2024, Tous droits réservés
Repensez votre boutique Shopify. Misez sur l’expérience client.
© Copyright 2024, Tous droits réservés
Repensez votre boutique Shopify. Misez sur l’expérience client.
© Copyright 2024, Tous droits réservés
Repensez votre boutique Shopify. Misez sur l’expérience client.
© Copyright 2024, Tous droits réservés



