Aller au contenu

Adaptateur REST API Shopify

Compatible Admin API

Dernière mise à jour : 5 September 2026

Une REST API pour STOAR compatible sans modification, qui reproduit l'Admin REST API 2024-01 de Shopify — mêmes chemins d'URL (/admin/api/2024-01/...), même syntaxe de paramètres de requête à plat, même structure d'enveloppe JSON ({ "order": {…} }, { "orders": [...] }), même en-tête d'authentification, même pagination par curseur Link: <…>; rel="next". Les bibliothèques clientes Shopify existantes (shopify_api Ruby gem, @shopify/shopify-api-node, ShopifySharp, python-shopify-api) dialoguent avec STOAR sans être modifiées.

L'adaptateur est la troisième implémentation du framework d'adaptateurs API extensible de STOAR (après Magento et WooCommerce). Il illustre une déclinaison d'enveloppe encore différente — des wrappers nommés au premier niveau et une pagination par curseur — aux côtés de Magento et WC, sur la même couche de données indépendante de tout éditeur.


Sommaire #


Démarrage rapide #

TOKEN="4|abcdef…"     # Sanctum personal-access token with shopify:admin ability

# Discover shop currency / locale (real Shopify clients call this first)
curl -H "X-Shopify-Access-Token: $TOKEN" \
     "https://app.stoar.ai/admin/api/2024-01/shop.json" | jq '.shop | {name, currency}'

# List the most recent 5 orders
curl -H "X-Shopify-Access-Token: $TOKEN" \
     "https://app.stoar.ai/admin/api/2024-01/orders.json?limit=5" | jq '.orders[] | {id, financial_status, total_price}'

# Fetch a single order
curl -H "X-Shopify-Access-Token: $TOKEN" \
     "https://app.stoar.ai/admin/api/2024-01/orders/10126.json"

# Cancel a pending order
curl -X POST -H "X-Shopify-Access-Token: $TOKEN" \
     "https://app.stoar.ai/admin/api/2024-01/orders/123/cancel.json" \
     -H "Content-Type: application/json" -d '{"reason":"customer"}'

L'authentification Bearer fonctionne également — -H "Authorization: Bearer $TOKEN" est interchangeable avec l'en-tête natif Shopify.


Authentification #

L'en-tête d'authentification canonique de Shopify est X-Shopify-Access-Token: <token>. L'adaptateur STOAR l'accepte, avec un repli Bearer :

Méthode Format Cas d'usage
X-Shopify-Access-Token X-Shopify-Access-Token: <sanctum-token> Par défaut pour toutes les bibliothèques clientes Shopify
Bearer (extension STOAR) Authorization: Bearer <sanctum-token> Sanctum natif — interchangeable avec l'authentification de l'adaptateur Magento

Les jetons sont émis depuis la page /manager/api-tokens de STOAR, par programme via AdminUser::createToken('label', ['shopify:admin']) ou — pour les flux internes à STOAR — via le point de terminaison Magento /rest/V1/integration/admin/token, l'ability shopify:admin étant ensuite accordée au même enregistrement.

Correspondance avec Sanctum. Le AccessTokenMiddleware s'exécute avant auth:sanctum et promeut X-Shopify-Access-Token en en-tête Authorization: Bearer …. Du point de vue de STOAR, toute requête Shopify est une requête Sanctum authentifiée classique dont la ligne de jeton porte l'ability shopify:admin. Un même jeton peut porter simultanément les abilities de tous les adaptateurs (shopify:admin + magento:admin + woocommerce:admin + bigcommerce:admin).

OAuth et la vérification HMAC (utilisés par les véritables applications Shopify) ne sont pas pris en charge.


Points de terminaison #

Tous les chemins sont relatifs à /admin/api/2024-01 (namespace de l'API Shopify repris tel quel, version 2024-01 incluse).

GET /admin/api/2024-01/shop.json

Renvoie les métadonnées de la boutique, synthétisées depuis la table Setting de STOAR. Les vrais clients Shopify appellent ce point de terminaison en premier pour connaître la devise et la locale de la boutique avant toute autre opération.

Réponse :

{
  "shop": {
    "id":               1,
    "name":             "STOAR",
    "domain":           "app.stoar.ai",
    "myshopify_domain": "app.stoar.ai",
    "email":            "[email protected]",
    "currency":         "EUR",
    "country_code":     "DE",
    "country_name":     "Germany",
    "primary_locale":   "en",
    "iana_timezone":    "UTC",
    "weight_unit":      "kg",
    "plan_name":        "stoar",
    "plan_display_name":"STOAR",
    "shop_owner":       "STOAR",
    "money_format":     "€ {{amount}}",
    "checkout_api_supported": true,
    "has_storefront":   true,
    "..."
  }
}

GET /admin/api/2024-01/orders.json

Liste les commandes.

Authorization : shopify:admin. Query : voir Paramètres de requête.

Réponse (200) :

HTTP/1.1 200 OK
Link: <…?page_info=eyJwIjoyLCJzIjo1LCJmIjoiZjE…&limit=5>; rel="next"
Content-Type: application/json

{
  "orders": [
    { /* order object — see Response shape */ }
  ]
}

L'en-tête Link pilote la pagination (pas de compteur ?page=). Voir Pagination par curseur.


GET /admin/api/2024-01/orders/{id}.json

Commande unique par id numérique.

Réponse : enveloppe { "order": {...} }. Voir Structure de la réponse.

Erreurs :

{ "errors": "Not Found" }

POST /admin/api/2024-01/orders/{id}/cancel.json

Annule une commande en attente ou payée.

Authorization : shopify:admin. Corps (facultatif) :

{ "reason": "customer" }

Codes de motif acceptés par Shopify : customer, inventory, fraud, declined, other. STOAR l'enregistre dans l'OrderStatusLog résultant mais n'en tire aucune autre conséquence.

Réponse (200) : la commande annulée dans l'enveloppe standard.

Erreurs :

  • 404 — id inconnu
  • 422 — la commande est dans un état qui interdit l'annulation (p. ex. delivered, refunded)

GET /admin/api/2024-01/products.json

Liste les produits. Même enveloppe et même pagination par en-tête Link que pour les commandes.


GET /admin/api/2024-01/products/{id}.json

Produit unique. Les variantes sont incluses en ligne (pas seulement les ids, mais les objets variante complets), conformément au contrat exact de Shopify.

Réponse : { "product": {...} }. Voir Structure de la réponse.


Paramètres de requête #

Les points de terminaison de liste REST de Shopify utilisent des paramètres de requête à plat — bien plus simples que le searchCriteria[…] imbriqué de Magento. Le parseur se trouve dans app/Api/Adapters/Shopify/Search/.

Paramètres communs (commandes et produits)

Paramètre Défaut Rôle
limit 50 éléments par page (max. 250)
page_info curseur base64 opaque — prend le pas sur tout autre filtre (voir Pagination par curseur)
since_id uniquement les éléments dont id > since_id (alternative au curseur)
ids liste d'ids en CSV (?ids=1,2,3)
created_at_min / created_at_max ISO 8601
updated_at_min / updated_at_max ISO 8601
order created_at desc <field> <direction>. Direction : asc / desc

Commandes uniquement

Paramètre Exemple Effet
status open, closed, cancelled, any cycle de vie global — traduit en plusieurs statuts Stoar
financial_status paid, pending, refunded, voided, authorized traduit en un seul statut Stoar
fulfillment_status fulfilled, partial, unfulfilled, any traduit en statut Stoar

Produits uniquement

Paramètre Exemple Effet
status active, archived, draft traduit en active / inactive Stoar
title widget sous-chaîne (LIKE) sur le name du produit
handle widget-pro correspondance exacte sur slug
vendor / product_type acceptés mais ignorés — Stoar n'a pas de champs équivalents

Les paramètres inconnus sont ignorés. Les valeurs de filtre sans traduction sensée (p. ex. ?financial_status=foo) sont silencieusement écartées.


Pagination par curseur #

Shopify utilise des curseurs base64 opaques plutôt que des compteurs de page. STOAR reproduit le contrat :

  1. Le client envoie ?limit=N (pas de paramètre page).
  2. Le serveur renvoie les N premiers éléments et un en-tête Link: :
    Link: <…/orders.json?page_info=eyJwIjoyLCJzIjo1LCJmIjoiYWJjMTIzIn0&limit=5>; rel="next"
  3. Le client suit l'URL rel="next" telle quelle — il ne la construit jamais lui-même.
  4. En suivant ce lien, le serveur décode page_info pour obtenir (page=2, page_size=5, filter_hash=abc123) et renvoie la page suivante.

Le filter_hash est un MD5 stable calculé sur le jeu de filtres d'origine. Si un client tente de réutiliser un curseur sur un autre jeu de résultats (par exemple en passant de ?status=open à ?status=closed), le hash du curseur ne correspond plus et STOAR revient à une page 1 fraîche. Cela correspond au comportement du vrai Shopify, où un changement de paramètre invalide les curseurs.

L'en-tête Link peut contenir à la fois rel="next" et rel="previous" :

Link: <…?page_info=PREV>; rel="previous", <…?page_info=NEXT>; rel="next"

Correspondance des champs (Shopify ↔ STOAR) #

Commande

Champ Shopify Source STOAR Remarques
id id numérique
admin_graphql_api_id id (formaté) gid://shopify/Order/{id}
name id #{id} (Shopify affiche #1001 sur les commandes)
number id numérique
order_number id + 1000 Shopify démarre par défaut à 1000
email, contact_email customer_email
phone toujours null (non stocké sur l'Order Stoar)
currency, presentment_currency currency (en majuscules) eurEUR
financial_status status (traduit) voir Traduction des statuts
fulfillment_status status (traduit) null, partial ou fulfilled
status (cycle de vie) status (traduit) open, closed, cancelled
total_price, current_total_price total_amount chaîne à 2 décimales
total_price_set wrapper money_set autour de total_amount {shop_money, presentment_money}
subtotal_price, total_line_items_price subtotal chaîne à 2 décimales + money_set
total_tax, current_total_tax tax_amount chaîne à 2 décimales + money_set
total_shipping_price_set wrapper money_set autour de shipping_amount
total_discounts discount_amount chaîne à 2 décimales
total_outstanding total_amount - sum(succeeded payments) montant dû
total_paid non exposé au premier niveau (Shopify le calcule à partir des transactions) disponible via current_total_price - total_outstanding
gateway, payment_gateway_names[] premier OrderPayment.gateway non archivé
created_at, updated_at, processed_at created_at, updated_at ISO 8601
cancelled_at updated_at si status=cancelled null sinon
closed_at updated_at si status=delivered/refunded null sinon
cancel_reason 'other' si annulée null sinon
customer modèle Customer embarqué null pour les invités
billing_address colonne JSON billing_info structure à plat Shopify
shipping_address colonne JSON shipping_info structure à plat Shopify
line_items[] Order.items (avec variante + produit) voir ci-dessous
discount_codes[] dérivé de coupon_code + discount_amount vide sans code promo
tax_lines[] un élément lorsque tax_amount > 0 avec un taux de 0, Stoar ne stockant pas le taux au niveau commande
shipping_lines[] dérivé de shipping_method + shipping_amount vide sans livraison
refunds[] une entrée synthétique lorsque refunded_amount > 0
token lookup_token le jeton magic link de la commande — à garder secret
order_status_url /checkout/success?order_id=…&token=… les clients l'utilisent pour le suivi de commande en libre-service
tags "" non modélisé

line_item de commande

Champ Shopify Source STOAR
id OrderItem.id
variant_id OrderItem.variant_id
product_id OrderItem.product_id
title OrderItem.name
variant_title Variant.name (sauf si « Default »)
name "{title} - {variant_title}" lorsque la variante a un nom
sku SKU de la variante d'abord, à défaut SKU du produit
quantity OrderItem.quantity
price OrderItem.price (chaîne à 2 décimales)
price_set wrapper money_set
tax_lines[] un élément lorsque tax_amount > 0
vendor, properties[] toujours vide / null
fulfillment_service 'manual'
fulfillment_status null
gift_card, requires_shipping, taxable valeurs par défaut raisonnables

Produit

Champ Shopify Source STOAR Remarques
id id
admin_graphql_api_id gid://shopify/Product/{id}
title name
handle slug
body_html description transmis tel quel (HTML ou texte brut)
status status (traduit) activeactive ; inactivearchived
vendor, product_type toujours "" non modélisé dans Stoar
published_at created_at si actif null si inactif/archivé
tags "" non modélisé
variants[] en ligne — objets variante complets toujours au moins une (Default Title si Stoar n'en a aucune)
options[] dérivé des clés JSON attributes des variantes (max. 3) chaque option a name, position, values[]
images[] image_path + tableau gallery_paths position commence à 1
image première image (ou null)
template_suffix, published_scope null, 'web' codé en dur

Variante de produit

Champ Shopify Source STOAR
id Variant.id
product_id Variant.product_id
title Variant.name ("Default Title" lorsque le nom est "Default")
option1, option2, option3 valeurs issues du JSON attributes dans un ordre stable (max. 3 axes)
price Variant.price (chaîne à 2 décimales)
sku Variant.sku
inventory_quantity Variant.stock
inventory_management 'shopify' (toujours)
inventory_policy 'deny' (toujours)
weight Variant.weight
weight_unit 'kg'
grams weight * 1000 (arrondi)
requires_shipping, taxable toujours true
compare_at_price toujours null
barcode toujours null

Traduction des statuts #

Shopify décompose l'état d'une commande sur trois champs. Stoar les réunit en un seul. Le traducteur gère les deux sens.

Stoar → Shopify (rendu)

status Stoar financial_status fulfillment_status status (cycle de vie)
pending pending null open
paid paid null open
processing paid partial open
shipped paid fulfilled open
delivered paid fulfilled closed
cancelled voided null cancelled
refunded refunded null closed

Filtre Shopify → Stoar (analyse)

Entrée Shopify Correspondance Stoar
?status=open pending, paid, processing, shipped
?status=closed delivered, refunded
?status=cancelled cancelled
?status=any (aucun filtre)
?financial_status=pending pending
?financial_status=paid / =authorized paid
?financial_status=voided cancelled
?financial_status=refunded / =partially_refunded refunded
?fulfillment_status=fulfilled shipped
?fulfillment_status=partial processing
?fulfillment_status=unfulfilled paid

Statut produit

Shopify Stoar
active active
archived, draft inactive

Stoar n'a pas de notion de draft — les produits Shopify archivés et brouillons sont tous deux mappés sur inactive côté Stoar. La sortie utilise archived.


Structure de la réponse #

Commande — exemple annoté

{
  "order": {
    "id":                   12345,
    "admin_graphql_api_id": "gid://shopify/Order/12345",
    "name":                 "#12345",
    "number":               12345,
    "order_number":         13345,
    "token":                "abc123…",
    "email":                "[email protected]",
    "contact_email":        "[email protected]",
    "currency":             "EUR",
    "presentment_currency": "EUR",
    "financial_status":     "paid",
    "fulfillment_status":   null,
    "status":               "open",
    "gateway":              "stripe",
    "payment_gateway_names":["stripe"],
    "total_price":          "115.00",
    "total_price_set":      {
      "shop_money":        { "amount": "115.00", "currency_code": "EUR" },
      "presentment_money": { "amount": "115.00", "currency_code": "EUR" }
    },
    "subtotal_price":       "100.00",
    "total_tax":            "10.00",
    "total_shipping_price_set": {
      "shop_money":        { "amount": "5.00", "currency_code": "EUR" },
      "presentment_money": { "amount": "5.00", "currency_code": "EUR" }
    },
    "total_discounts":      "0.00",
    "total_outstanding":    "0.00",
    "total_tip_received":   "0.00",
    "created_at":           "2026-04-10T09:00:00+00:00",
    "updated_at":           "2026-04-10T09:30:00+00:00",
    "processed_at":         "2026-04-10T09:00:00+00:00",
    "cancelled_at":         null,
    "closed_at":            null,
    "cancel_reason":        null,
    "customer":             {
      "id":                 45,
      "email":              "[email protected]",
      "first_name":         "Jane",
      "last_name":          "Doe",
      "verified_email":     true,
      "state":              "enabled",
      "currency":           "EUR"
    },
    "billing_address":      {
      "first_name":         "Jane",
      "last_name":          "Doe",
      "name":               "Jane Doe",
      "address1":           "1 Test St",
      "address2":           null,
      "city":               "Berlin",
      "province":           null,
      "country":            null,
      "country_code":       "DE",
      "zip":                "10115",
      "phone":              null
    },
    "shipping_address":     { /* same shape as billing */ },
    "line_items": [
      {
        "id":           987,
        "variant_id":   12,
        "product_id":   456,
        "title":        "Widget",
        "variant_title":"Red",
        "name":         "Widget - Red",
        "sku":          "WID-RED",
        "quantity":     2,
        "price":        "50.00",
        "price_set":    {"shop_money": {"amount": "50.00", "currency_code": "EUR"}, "presentment_money": {"amount": "50.00", "currency_code": "EUR"}},
        "fulfillable_quantity": 2,
        "fulfillment_service":  "manual",
        "fulfillment_status":   null,
        "requires_shipping":    true,
        "taxable":              true,
        "tax_lines":            []
      }
    ],
    "shipping_lines": [
      {
        "id":           0,
        "title":        "Standard",
        "code":         "flat_rate",
        "source":       "shopify",
        "price":        "5.00",
        "price_set":    {"shop_money": {"amount": "5.00", "currency_code": "EUR"}, "presentment_money": {"amount": "5.00", "currency_code": "EUR"}},
        "tax_lines":    [],
        "discount_allocations": []
      }
    ],
    "tax_lines":            [],
    "discount_codes":       [],
    "discount_applications":[],
    "fulfillments":         [],
    "refunds":              []
  }
}

Produit — exemple abrégé

{
  "product": {
    "id":                  789,
    "admin_graphql_api_id":"gid://shopify/Product/789",
    "title":               "Widget",
    "body_html":           "<p>A great widget</p>",
    "vendor":              "",
    "product_type":        "",
    "handle":              "widget",
    "status":              "active",
    "published_at":        "2026-04-01T10:00:00+00:00",
    "published_scope":     "web",
    "tags":                "",
    "variants": [
      {
        "id":                 12,
        "admin_graphql_api_id":"gid://shopify/ProductVariant/12",
        "product_id":         789,
        "title":              "Red",
        "price":              "99.99",
        "sku":                "WID-RED",
        "position":           1,
        "inventory_policy":   "deny",
        "compare_at_price":   null,
        "fulfillment_service":"manual",
        "inventory_management":"shopify",
        "option1":            "Red",
        "option2":            null,
        "option3":            null,
        "weight":             0.5,
        "weight_unit":        "kg",
        "grams":              500,
        "inventory_quantity": 42
      }
    ],
    "options": [
      { "id": 0, "product_id": 789, "name": "Color", "position": 1, "values": ["Red", "Blue"] }
    ],
    "images": [
      {
        "id":         0,
        "admin_graphql_api_id": "gid://shopify/ProductImage/0",
        "product_id": 789,
        "position":   1,
        "alt":        null,
        "src":        "products/widget.webp",
        "variant_ids":[]
      }
    ],
    "image": { /* same shape as images[0] */ }
  }
}

Exemple réel #

GET /admin/api/2024-01/orders/10126.json en production (transformation en direct, au format Shopify, de la commande déjà présentée pour Magento et WooCommerce). Données personnelles anonymisées. La réponse s'intègre sans modification au gem Ruby shopify_api et à @shopify/shopify-api-node.

{
  "order": {
    "id":                   10126,
    "admin_graphql_api_id": "gid://shopify/Order/10126",
    "name":                 "#10126",
    "number":               10126,
    "order_number":         11126,
    "token":                "REDACTED-FOR-DOCS",
    "email":                "[email protected]",
    "contact_email":        "[email protected]",
    "currency":             "USD",
    "presentment_currency": "USD",
    "financial_status":     "paid",
    "fulfillment_status":   null,
    "status":               "open",
    "gateway":              "payid",
    "payment_gateway_names":["payid"],
    "total_price":          "936.98",
    "subtotal_price":       "936.98",
    "total_tax":            "0.00",
    "total_outstanding":    "936.98",
    "total_price_set":      { "shop_money": { "amount": "936.98", "currency_code": "USD" }, "presentment_money": { "amount": "936.98", "currency_code": "USD" } },
    "created_at":           "2025-06-03T04:56:43+00:00",
    "updated_at":           "2025-06-03T04:56:43+00:00",
    "processed_at":         "2025-06-03T04:56:43+00:00",
    "cancelled_at":         null,
    "closed_at":            null,
    "billing_address": {
      "first_name":   "Jane",
      "last_name":    "Doe",
      "name":         "Jane Doe",
      "address1":     "1 Example Street",
      "address2":     null,
      "city":         "Phoenix",
      "province":     "AZ",
      "country":      null,
      "country_code": "US",
      "zip":          "85001",
      "phone":        "+1-555-0100"
    },
    "shipping_address":     { /* same shape */ },
    "customer":             {
      "id":             5794,
      "email":          "[email protected]",
      "first_name":     "Jane",
      "last_name":      "Doe",
      "state":          "enabled",
      "verified_email": true,
      "currency":       "USD"
    },
    "line_items": [
      {
        "id":           30219,
        "variant_id":   95589,
        "product_id":   112238,
        "title":        "Reloop Terminal Mix 8",
        "variant_title":null,
        "name":         "Reloop Terminal Mix 8",
        "sku":          "RELOOP_TERMINALMIX8_025-DEF",
        "quantity":     3,
        "price":        "299.00",
        "fulfillable_quantity": 3,
        "fulfillment_service":  "manual",
        "fulfillment_status":   null,
        "requires_shipping":    true,
        "taxable":              false,
        "tax_lines":            []
      },
      {
        "id":           30220,
        "variant_id":   33857,
        "product_id":   51706,
        "title":        "Premium Skateboard Socks",
        "variant_title":null,
        "name":         "Premium Skateboard Socks",
        "sku":          "SK8-SOCK-027-DEF",
        "quantity":     2,
        "price":        "19.99",
        "fulfillable_quantity": 2,
        "fulfillment_service":  "manual",
        "fulfillment_status":   null,
        "requires_shipping":    true,
        "taxable":              false,
        "tax_lines":            []
      }
    ],
    "shipping_lines": [{
      "id":     0,
      "title":  "Free Shipping",
      "code":   "flat_rate",
      "source": "shopify",
      "price":  "0.00"
    }],
    "tax_lines":            [],
    "discount_codes":       [],
    "discount_applications":[],
    "fulfillments":         [],
    "refunds":              []
  }
}

Points d'attention pour les intégrateurs

Champ Observé Pourquoi
total_outstanding "936.98" pour une commande payée Calculé depuis le journal d'audit OrderPayment ; les commandes antérieures à ce journal affichent leur total complet comme dû.
gateway "payid" Il s'agit de la clé de passerelle Stoar — pas d'un nom canonique Shopify comme shopify_payments.
customer_locale, device_id, app_id toujours null Non modélisé sur l'Order Stoar.
tax_lines [] même lorsque total_tax > 0 Stoar suit la taxe au niveau de la commande uniquement, pas par juridiction ; nous synthétiserions une seule tax_line si nécessaire.
fulfillments, discount_applications toujours [] Pas de suivi d'expédition ; les codes promo sont modélisés uniquement sous forme de discount_codes à plat.
tags toujours "" Pas de système d'étiquettes dans Stoar.
token jeton magic link propre à la commande Ne jamais l'exposer dans du code client ni dans des journaux — sa seule possession suffit à consulter la commande sans authentification.
order_number id + 1000 Cosmétique — Shopify démarre par défaut toutes les boutiques à 1000.

Structure des erreurs #

Shopify utilise une enveloppe souple :

{ "errors": "Not Found" }                       // string
{ "errors": { "title": ["can't be blank"] } }   // map (validation)
HTTP Déclencheur Corps
401 jeton manquant ou invalide { "errors": "[API] Invalid API key or access token …" }
403 jeton avec la mauvaise ability { "errors": "Forbidden" }
404 id de ressource inconnu { "errors": "Not Found" }
422 précondition non satisfaite (annulation impossible, etc.) { "errors": "Order N cannot be cancelled in status 'delivered'." }
429 limite de débit dépassée { "errors": "Exceeded 2 calls per second for api client. …" } + Retry-After: 2
500 erreur serveur inattendue { "errors": "Internal Server Error" }

Les erreurs de validation (seul cancel.json accepte un corps) utilisent la structure de map indexée par champ.


Limitation de débit #

Le même throttle api-rest indexé sur le jeton Sanctum qui protège Magento et WooCommerce protège les routes Shopify. Configure les deux globalement sur /manager/api-settings. En cas de déclenchement, la réponse utilise l'enveloppe d'erreur de Shopify :

HTTP/1.1 429 Too Many Requests
Retry-After: 2
Content-Type: application/json

{ "errors": "Exceeded 2 calls per second for api client. Reduce request rates to resume uninterrupted service." }

La valeur de l'en-tête Retry-After correspond au comportement documenté par Shopify. Le vrai Shopify emploie un algorithme de seau percé ; STOAR utilise une fenêtre glissante à la minute — assez proche pour la compatibilité avec les bibliothèques clientes.

Un seul jeton Sanctum utilisé sur les quatre adaptateurs (Magento + WC + Shopify + BC) partage un seul seau — voir la section Limitation de débit de la documentation Magento pour la présentation complète de l'interface de configuration.


Voir aussi #

  • app/Api/Adapters/Shopify/Support/StatusTranslator.php — correspondance de vocabulaire bidirectionnelle
  • app/Api/Adapters/Shopify/Support/PageInfoCursor.php — encodage du curseur base64 opaque
  • Référence de l'Admin REST API Shopify — la spécification amont que reproduit cet adaptateur