Aller au contenu

Adaptateur REST API WooCommerce

Compatible v3

Dernière mise à jour : 5 September 2026

Une API REST compatible et directement substituable pour STOAR, qui reproduit le format de protocole de WooCommerce v3 — mêmes chemins d'URL (/wp-json/wc/v3/...), même syntaxe de paramètres de requête à plat, mêmes noms de champs dans les réponses JSON, mêmes options d'authentification. Les bibliothèques clientes WooCommerce existantes (p. ex. automattic/woocommerce PHP/JS, klarna/woocommerce-rest-api Node) dialoguent avec STOAR sans modification.

L'adaptateur est la deuxième implémentation du framework d'adaptateurs d'API extensible de STOAR (la première étant Magento). Il montre comment une variante fournisseur fondamentalement différente — paramètres de requête à plat au lieu du searchCriteria[…] imbriqué, Basic Auth au lieu du Bearer exclusif, vocabulaire de statuts processing/completed au lieu de paid/delivered — coexiste avec Magento sur la même couche de données.


Sommaire #


Démarrage rapide #

# 1. Get a Sanctum token with woocommerce:admin ability — issued via /manager/api-tokens
#    or via the Magento token endpoint (POST /rest/V1/integration/admin/token), then
#    grant the woocommerce:admin ability inside /manager.
TOKEN="2|L5pXj63e881lkgtHPK7Hjozyq7Pb3trHCy0iaDRf76405f72"

# 2. List the last 5 orders (Bearer auth — STOAR extension; works alongside Basic)
curl -s "https://app.stoar.ai/wp-json/wc/v3/orders?per_page=5&orderby=date&order=desc" \
  -H "Authorization: Bearer $TOKEN" \
  | jq '.[] | {id, status, total, currency}'

# 3. Use HTTP Basic just like a real WooCommerce store
curl -s "https://app.stoar.ai/wp-json/wc/v3/products?per_page=3" \
  -u "any-key:$TOKEN"

# 4. Or stuff credentials into the query string (still over HTTPS only)
curl -s "https://app.stoar.ai/wp-json/wc/v3/products?consumer_key=any&consumer_secret=$TOKEN"

Authentification #

WooCommerce propose trois variantes d'authentification ; l'adaptateur les accepte toutes et les convertit en interne vers la même vérification de token Sanctum personal-access. Les tokens sont émis depuis la page /manager/api-tokens de STOAR (ou par programme via AdminUser::createToken('label', ['woocommerce:admin'])).

Méthode Format Cas d'usage
HTTP Basic Authorization: Basic base64(consumer_key:consumer_secret) Valeur par défaut de la plupart des bibliothèques clientes WC — en HTTPS uniquement
Paramètres de requête ?consumer_key=…&consumer_secret=… Test rapide en curl — en HTTPS uniquement
Bearer (extension STOAR) Authorization: Bearer <token> Sanctum natif — interchangeable avec l'adaptateur Magento

Correspondance avec Sanctum

L'adaptateur ignore consumer_key (tu peux transmettre n'importe quelle chaîne — "any-key" convient). consumer_secret DOIT être un token Sanctum personal-access valide dont le tableau abilities contient woocommerce:admin.

Cette correspondance n'est qu'une couche d'interprétation ; du point de vue de STOAR, toute requête WC n'est qu'une requête Sanctum authentifiée en Bearer. La même ligne de token personal-access sert à la fois :

  • un client Magento utilisant /rest/V1/orders (nécessite l'ability magento:admin)
  • un client WooCommerce utilisant /wp-json/wc/v3/orders (nécessite l'ability woocommerce:admin)

Tu peux émettre un seul token portant les deux abilities, ou deux tokens distincts — à toi de voir.

Tokens client

Les points de terminaison à portée client ne sont pas encore disponibles. L'ability woocommerce:customer est réservée pour un usage futur.


Points de terminaison #

Tous les chemins sont relatifs à /wp-json/wc/v3 (l'espace de noms REST de WordPress, à l'identique).

GET /wp-json/wc/v3/orders

Liste les commandes.

Autorisation : Bearer / Basic / query, avec l'ability woocommerce:admin.

Paramètres de requête : voir Paramètres de requête.

Réponse (200) : tableau JSON plat de commandes. La pagination transite par les en-têtes, PAS dans le corps :

HTTP/1.1 200 OK
X-WP-Total:      150
X-WP-TotalPages: 8
Content-Type:    application/json

[
  { /* order */ },
  { /* order */ }
]

Pourquoi des en-têtes plutôt qu'une enveloppe ? C'est la convention réelle de WooCommerce — différente du wrapper {items, search_criteria, total_count} de Magento. Les bibliothèques clientes WC (les clients officiels PHP/JS d'Automattic) lisent X-WP-Total pour piloter leurs curseurs de pagination.


GET /wp-json/wc/v3/orders/{id}

Commande unique par id numérique.

Réponse (200) : voir Structure des réponses.

Erreurs :

{
  "code":    "woocommerce_rest_shop_order_invalid_id",
  "message": "Invalid shop_order ID.",
  "data":    { "status": 404, "id": 99999 }
}

GET /wp-json/wc/v3/orders/{id}/notes

Liste les lignes d'historique de statut de la commande, remises en forme de notes WooCommerce.

Réponse (200) :

[
  {
    "id":               42,
    "author":           "system",
    "date_created":     "2026-04-10T12:00:00+00:00",
    "date_created_gmt": "2026-04-10T12:00:00+00:00",
    "note":             "Payment received",
    "customer_note":    false,
    "_links":           { "self": [...], "collection": [...], "up": [...] }
  }
]

customer_note vaut toujours false, car Stoar ne distingue pas, au niveau des données, les notes visibles par le client de celles réservées à l'administration.


POST /wp-json/wc/v3/orders/{id}/notes

Ajoute une note (traduite par une nouvelle ligne OrderStatusLog dans Stoar).

Requête :

{ "note": "Customer phoned to confirm delivery slot" }

customer_note (booléen) est accepté, mais n'a actuellement aucun effet.

Réponse (201) : la nouvelle note, dans la même structure que les éléments de GET .../notes.

Erreurs :

  • 400 rest_invalid_paramnote manquant
  • 404 — id de commande inconnu

GET /wp-json/wc/v3/orders/{id}/refunds

Liste les remboursements d'une commande. Stoar n'a pas de table de remboursements distincte — l'adaptateur renvoie soit :

  • [] lorsque refunded_amount === 0, soit
  • [{...}] avec une ligne de remboursement synthétique dont id === parent_id === order.id
[
  {
    "id":               123,
    "parent_id":        123,
    "date_created":     "2026-04-12T08:00:00+00:00",
    "amount":           "30.00",
    "reason":           "",
    "refunded_by":      0,
    "refunded_payment": true,
    "meta_data":        [],
    "line_items":       [],
    "api_refund":       true
  }
]

POST /wp-json/wc/v3/orders/{id}/refunds

Émet un remboursement. Délègue au Order::processRefund() existant de Stoar, qui appelle Stripe via StripeService::processRefund(). Stoar passe la commande en refunded lors d'un remboursement total, ou cumule refunded_amount pour les remboursements partiels.

Requête :

{ "amount": 30, "reason": "Customer changed mind" }

Les deux champs sont facultatifs. Si amount est omis, Stoar rembourse la totalité du solde remboursable restant.

Réponse (201) : la ligne de remboursement, dans la structure ci-dessus.

Erreurs :

  • 404 — id de commande inconnu
  • 422 woocommerce_rest_invalid_state — la commande n'est pas remboursable (pas de payment intent Stripe, statut différent de paid/processing/shipped/delivered)

GET /wp-json/wc/v3/products

Liste les produits.

Paramètres de requête : voir Paramètres de requête.

Réponse (200) : tableau plat, avec les en-têtes X-WP-Total / X-WP-TotalPages.


GET /wp-json/wc/v3/products/{id}

Produit unique par id numérique (PAS par SKU comme dans Magento).

Réponse (200) : voir Structure des réponses.

Erreurs : 404 woocommerce_rest_product_invalid_id.


Paramètres de requête #

WooCommerce utilise des paramètres de query string à plat — bien plus simples que la grammaire imbriquée searchCriteria[…] de Magento. La traduction vers les mêmes objets-valeurs OrderQuery / ProductQuery, indépendants du fournisseur, s'opère dans les classes Search/OrderQueryParser et Search/ProductQueryParser de l'adaptateur.

Paramètres communs (commandes + produits)

Paramètre Défaut Objet
per_page 10 Résultats par page (max. 100)
page 1 Numéro de page, indexé à partir de 1
orderby date Champ de tri — voir les listes par ressource ci-dessous
order desc asc ou desc
include Liste d'id séparés par des virgules (?include=1,2,3)
exclude Liste d'id à exclure, séparés par des virgules
search Correspondance sur une sous-chaîne — voir les remarques par ressource

Commandes uniquement

Paramètre Exemple Effet
status processing,completed CSV — traduit en valeurs de statut Stoar ; combinés par OU au sein du groupe
customer 42 Filtrer par customer_id
after 2024-01-01T00:00:00 created_at >= …
before 2024-12-31T23:59:59 created_at <= …
search jane LIKE sur customer_email
valeurs de orderby date (défaut), id, include, title datecreated_at, titlecustomer_email

Produits uniquement

Paramètre Exemple Effet
status publish, draft Traduit vers active / inactive côté Stoar
type simple, variable variable se traduit par configurable côté Stoar
featured true / false Filtre sur is_featured
category 5 Id de catégorie unique
sku WID-1 Correspondance exacte
slug widget-pro Correspondance exacte
min_price 10 price >= …
max_price 100 price <= …
search widget LIKE sur le name du produit
valeurs de orderby date, id, include, title, price, slug titlename, slugslug

Les paramètres inconnus sont ignorés silencieusement (comme le fait WC). Les valeurs de filtre qui ne se traduisent en rien de cohérent (p. ex. ?status=on-hold pour des produits) produisent un groupe vide, pas une erreur 400.


Correspondance des champs (WooCommerce ↔ STOAR) #

Commande

Champ WooCommerce Source STOAR Remarques
id id numérique
number id (converti en chaîne) convention WC
order_key lookup_token le token de lien magique par commande de Stoar
status status (traduit) voir Traduction des statuts
currency currency (en majuscules) eurEUR
total total_amount chaîne à 2 décimales
cart_tax, total_tax tax_amount chaîne à 2 décimales
shipping_total shipping_amount chaîne à 2 décimales
discount_total discount_amount chaîne à 2 décimales
customer_id customer_id ?? 0 les invités reçoivent 0
customer_note customer_notes
billing.* colonne JSON billing_info structure WC à plat
shipping.* colonne JSON shipping_info structure WC à plat
payment_method premier OrderPayment.gateway non archivé se rabat sur Order.payment_method
payment_method_title déduit de la clé de passerelle stripe → « Credit / Debit Card », etc.
transaction_id stripe_payment_intent_id
date_created, date_modified created_at, updated_at ISO8601
date_paid premier OrderPayment.created_at réussi null si aucun paiement enregistré
date_completed updated_at si status === delivered null sinon
line_items[] Order.items (avec variante + produit) voir ci-dessous
coupon_lines[] déduit de coupon_code + discount_amount vide en l'absence de code promo
refunds[] une entrée synthétique lorsque refunded_amount > 0 voir Points de terminaison de remboursement
meta_data[] contient toujours _stoar_status et _stoar_lookup_token données d'extension

Ligne de commande line_item

Champ WC Source Stoar
id OrderItem.id
name OrderItem.name
product_id OrderItem.product_id ?? 0
variation_id OrderItem.variant_id ?? 0
quantity OrderItem.quantity
subtotal, total price * quantity (chaîne à 2 décimales)
subtotal_tax, total_tax OrderItem.tax_amount
sku SKU de la variante d'abord, à défaut celui du produit
price OrderItem.price

Produit

Champ WC Source STOAR Remarques
id id
name, slug, sku, description, short_description repris tel quel
permalink url('/product/' . slug)
type type (traduit) configurablevariable
status status (traduit) activepublish
featured is_featured booléen
regular_price price (brut) chaîne à 2 décimales
sale_price special_price (ou "" si null) chaîne à 2 décimales
price min(regular, sale) chaîne à 2 décimales
on_sale special_price !== null && special_price < price
purchasable stock > 0 && status === 'active'
manage_stock toujours true
stock_quantity stock ; pour un configurable, somme du stock des variantes
stock_status instock / outofstock
weight weight (chaîne)
tax_class tax_class_id (chaîne)
categories[] un élément issu de la relation category Stoar n'a qu'une seule catégorie principale
images[] image_path + tableau gallery_paths la première entrée est l'image principale
variations[] ids des variantes (uniquement pour les produits variable)
meta_data[] contient toujours _stoar_id, _stoar_low_stock_threshold, _stoar_image_prompt
attributes[], default_attributes[] [] Stoar n'a pas de système d'attributs global
tags[], related_ids[], upsell_ids[], cross_sell_ids[] [] non modélisés dans Stoar
dimensions vide non suivi
average_rating, rating_count "0.00", 0 non agrégés à ce niveau

Traduction des statuts #

WooCommerce et Stoar utilisent des vocabulaires de statuts différents. L'adaptateur traduit dans les deux sens : lors d'un filtrage (?status=processing), les valeurs WC sont converties vers Stoar ; lors de la sérialisation des réponses, le status de Stoar est reconverti vers WC.

Statut de commande

WooCommerce Stoar Remarques
pending pending impayée, en attente de paiement
processing paid (au rendu) / accepte paid (en filtre) « paiement encaissé, préparation en cours »
processing processing, shipped (au rendu) les statuts Stoar processing et shipped sont tous deux rendus en processing WC
completed delivered
cancelled cancelled
refunded refunded
on-hold pending (filtre uniquement) Stoar n'a pas d'état on-hold explicite
failed cancelled (filtre uniquement) meilleure approximation ; Stoar n'a pas d'état failed explicite

Statut produit

WooCommerce Stoar
publish active
draft, pending, private inactive

Type de produit

WooCommerce Stoar
simple simple
variable configurable
grouped, external (non pris en charge — absents de Stoar)

Les valeurs de filtre qui se traduisent par null (p. ex. ?status=trash pour des produits) sont ignorées silencieusement — elles ne provoquent pas d'erreur.


Structure des réponses #

Commande — exemple annoté

{
  "id":             456,
  "parent_id":      0,
  "number":         "456",
  "order_key":      "abc123…",
  "created_via":    "checkout",
  "version":        "8.5.0",
  "status":         "processing",
  "currency":       "EUR",
  "date_created":   "2026-04-10T09:00:00+00:00",
  "date_modified":  "2026-04-10T09:30:00+00:00",
  "discount_total": "0.00",
  "discount_tax":   "0.00",
  "shipping_total": "5.00",
  "shipping_tax":   "0.00",
  "cart_tax":       "10.00",
  "total":          "115.00",
  "total_tax":      "10.00",
  "prices_include_tax": false,
  "customer_id":    45,
  "customer_note":  "",
  "billing":        { /* WC address shape (flat) */ },
  "shipping":       { /* WC address shape (flat) */ },
  "payment_method": "stripe",
  "payment_method_title": "Credit / Debit Card",
  "transaction_id": "pi_test_…",
  "date_paid":      "2026-04-10T09:05:00+00:00",
  "date_completed": null,
  "cart_hash":      "",
  "meta_data":      [
    { "id": 0, "key": "_stoar_status",       "value": "paid" },
    { "id": 0, "key": "_stoar_lookup_token", "value": "abc123…" }
  ],
  "line_items": [
    {
      "id":            456,
      "name":          "Widget",
      "product_id":    789,
      "variation_id":  12,
      "quantity":      2,
      "tax_class":     "",
      "subtotal":      "100.00",
      "subtotal_tax":  "10.00",
      "total":         "100.00",
      "total_tax":     "10.00",
      "taxes":         [],
      "meta_data":     [],
      "sku":           "WID-1-A",
      "price":         50
    }
  ],
  "tax_lines":      [],
  "shipping_lines": [
    {
      "id":           0,
      "method_title": "Standard",
      "method_id":    "flat_rate",
      "instance_id":  "",
      "total":        "5.00",
      "total_tax":    "0.00",
      "taxes":        [],
      "meta_data":    []
    }
  ],
  "fee_lines":    [],
  "coupon_lines": [],
  "refunds":      [],
  "_links":       {
    "self":       [{ "href": "https://app.stoar.ai/wp-json/wc/v3/orders/456" }],
    "collection": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/orders" }]
  }
}

Produit — exemple abrégé

{
  "id":                 789,
  "name":               "Widget",
  "slug":               "widget",
  "permalink":          "https://app.stoar.ai/product/widget",
  "type":               "simple",
  "status":             "publish",
  "featured":           true,
  "catalog_visibility": "visible",
  "description":        "<p>A great widget</p>",
  "short_description":  "Great widget",
  "sku":                "WID-1",
  "price":              "79.99",
  "regular_price":      "99.99",
  "sale_price":         "79.99",
  "on_sale":            true,
  "purchasable":        true,
  "manage_stock":       true,
  "stock_quantity":     42,
  "stock_status":       "instock",
  "weight":             "1.5",
  "categories":         [{ "id": 5, "name": "Widgets", "slug": "widgets" }],
  "images":             [
    { "id": 0, "src": "products/widget.webp", "name": "primary",   "position": 0, "alt": "" },
    { "id": 0, "src": "products/g1.webp",     "name": "gallery-1", "position": 1, "alt": "" }
  ],
  "attributes":         [],
  "variations":         [],
  "meta_data":          [
    { "id": 0, "key": "_stoar_id", "value": "789" },
    { "id": 0, "key": "_stoar_low_stock_threshold", "value": "3" }
  ],
  "_links":             {
    "self":       [{ "href": "https://app.stoar.ai/wp-json/wc/v3/products/789" }],
    "collection": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/products" }]
  }
}

Pour un produit configurable / variable, variations est renseigné avec les ids des variantes, et stock_quantity correspond à la somme du stock des variantes.


Exemple réel #

Réponse en direct de la production pour GET /wp-json/wc/v3/orders/10126 (936,98 USD, deux lignes de produits simples, payée par PayID). Données personnelles anonymisées.

Requête

GET /wp-json/wc/v3/orders/10126
Authorization: Bearer 2|L5pXj63e881lkgtHPK7Hjozyq7Pb3trHCy0iaDRf76405f72

Réponse

{
  "id":               10126,
  "parent_id":        0,
  "number":           "10126",
  "order_key":        "REDACTED-FOR-DOCS",
  "created_via":      "checkout",
  "version":          "8.5.0",
  "status":           "processing",
  "currency":         "USD",
  "date_created":     "2025-06-03T04:56:43+00:00",
  "date_modified":    "2025-06-03T04:56:43+00:00",
  "discount_total":   "0.00",
  "discount_tax":     "0.00",
  "shipping_total":   "0.00",
  "shipping_tax":     "0.00",
  "cart_tax":         "0.00",
  "total":            "936.98",
  "total_tax":        "0.00",
  "prices_include_tax": false,
  "customer_id":      5794,
  "customer_note":    "",
  "billing": {
    "first_name": "Jane",
    "last_name":  "Doe",
    "company":    "",
    "address_1":  "1 Example Street",
    "address_2":  "",
    "city":       "Phoenix",
    "state":      "AZ",
    "postcode":   "85001",
    "country":    "US",
    "email":      "",
    "phone":      "+1-555-0100"
  },
  "shipping":         { /* same shape as billing */ },
  "payment_method":   "payid",
  "payment_method_title": "PayID",
  "transaction_id":   "",
  "date_paid":        null,
  "date_completed":   null,
  "cart_hash":        "",
  "meta_data": [
    { "id": 0, "key": "_stoar_status",       "value": "paid" },
    { "id": 0, "key": "_stoar_lookup_token", "value": "REDACTED-FOR-DOCS" }
  ],
  "line_items": [
    {
      "id":           30219,
      "name":         "Reloop Terminal Mix 8",
      "product_id":   112238,
      "variation_id": 95589,
      "quantity":     3,
      "tax_class":    "",
      "subtotal":     "897.00",
      "subtotal_tax": "0.00",
      "total":        "897.00",
      "total_tax":    "0.00",
      "taxes":        [],
      "meta_data":    [],
      "sku":          "RELOOP_TERMINALMIX8_025-DEF",
      "price":        299
    },
    {
      "id":           30220,
      "name":         "Premium Skateboard Socks",
      "product_id":   51706,
      "variation_id": 33857,
      "quantity":     2,
      "tax_class":    "",
      "subtotal":     "39.98",
      "subtotal_tax": "0.00",
      "total":        "39.98",
      "total_tax":    "0.00",
      "taxes":        [],
      "meta_data":    [],
      "sku":          "SK8-SOCK-027-DEF",
      "price":        19.99
    }
  ],
  "tax_lines":      [],
  "shipping_lines": [
    {
      "id":           0,
      "method_title": "Free Shipping",
      "method_id":    "flat_rate",
      "instance_id":  "",
      "total":        "0.00",
      "total_tax":    "0.00",
      "taxes":        [],
      "meta_data":    []
    }
  ],
  "fee_lines":    [],
  "coupon_lines": [],
  "refunds":      [],
  "_links":       {
    "self":       [{ "href": "https://app.stoar.ai/wp-json/wc/v3/orders/10126" }],
    "collection": [{ "href": "https://app.stoar.ai/wp-json/wc/v3/orders" }]
  }
}

Points de vigilance pour les intégrateurs

Ils recoupent ceux de Magento — les données Stoar sous-jacentes sont les mêmes, simplement rendues dans une autre enveloppe :

Champ Observé Pourquoi
transaction_id, date_paid "" / null pour des commandes payées Extraits du journal d'audit OrderPayment de Stoar ; les commandes antérieures à ce journal apparaissent vides
payment_method "payid" Il s'agit de la clé de passerelle STOAR (stripe / bank_transfer / cash_on_delivery / payid / invoice) — pas d'un nom de méthode canonique WC
payment_method_title déduit de la clé de passerelle correspondance codée en dur — modifie OrderResource::paymentMethodTitle() pour l'étendre
version toujours "8.5.0" codé en dur — ce n'est pas la version de WC réellement exécutée
created_via toujours "checkout" Stoar n'a pas encore de flux de création via l'API
customer_ip_address, customer_user_agent toujours "" non stockés sur la commande
cart_hash toujours "" non stocké
tax_lines toujours [] Stoar suit la taxe au seul niveau commande, pas par juridiction
attributes, default_attributes (produits) toujours [] Stoar n'a pas de taxonomie d'attributs globale
meta_data._stoar_lookup_token token de lien magique par commande Même avertissement de sécurité que pour Magento — sa seule possession suffit à consulter la commande sans authentification. Ne l'expose jamais dans du code côté client ni dans les journaux.

Structure des erreurs #

Les erreurs WooCommerce se présentent ainsi :

{
  "code":    "machine_readable_code",
  "message": "Human-readable message",
  "data":    { "status": 404, "id": 99999 }
}

… et NON comme le {message, parameters, trace} de Magento.

HTTP Déclencheur code
400 paramètre de requête incorrect / corps obligatoire manquant rest_invalid_param
401 token absent ou invalide woocommerce_rest_cannot_view
403 token à l'ability incorrecte (p. ex. token limité à magento:admin, ou token client) woocommerce_rest_authorization_required
404 id de ressource inconnu woocommerce_rest_shop_order_invalid_id / woocommerce_rest_product_invalid_id
422 précondition non remplie (remboursement d'une commande non remboursable, …) woocommerce_rest_invalid_state
429 limite de débit dépassée woocommerce_rest_too_many_requests
500 erreur serveur inattendue woocommerce_rest_internal_error

data.status reflète toujours le code HTTP. Des clés propres à la ressource (data.id) sont ajoutées lorsqu'elles ont du sens.


Limitation de débit #

Le même throttle indexé sur le token Sanctum qui protège Magento (api-rest, 60 req/min par défaut) protège les routes WC. Les deux se configurent globalement sur /manager/api-settings. En cas de déclenchement, la réponse utilise l'enveloppe d'erreur WC :

{
  "code":    "woocommerce_rest_too_many_requests",
  "message": "Too many requests. Please retry after a short pause.",
  "data":    { "status": 429 }
}

Voir la section Limitation de débit de Magento pour la visite guidée complète de l'interface de configuration.


Voir aussi #

  • app/Api/Core/OrderRepository.php, app/Api/Core/ProductRepository.php — la frontière d'abstraction sur laquelle repose chaque adaptateur
  • app/Api/Adapters/WooCommerce/Support/StatusTranslator.php — correspondance bidirectionnelle des vocabulaires
  • Référence de la REST API WooCommerce — la spécification amont que cet adaptateur reproduit