Aller au contenu
ShopentlyLearn

Modifier — Produits

Modifiez les données indiquées dans « Produits ». Les champs requis et règles d’écriture sont décrits ci-dessous.

PATCH/api/v1/products/{product_id}

Authentification
Bearer
Disponible à partir de
STANDARD
Autorisations requises
catalog.product:update
Autorisations supplémentaires pour certains champs
  • Si variant_value_ids est fourni: catalog.product_variant:create, catalog.product_variant:update, catalog.product_variant:delete
  • Si variants_enabled est fourni: catalog.product_variant:update
En savoir plus

Exemples de code

Exemple de requête

cURL

: "${SHOPENTLY_SHOP_URL:?}"

curl --disable --globoff --fail-with-body --silent --show-error \
  --proto '=https' --connect-timeout 5 --max-time 30 \
  --request PATCH \
  --header "Authorization: Bearer ${SHOPENTLY_ACCESS_TOKEN:?}" \
  --header 'Idempotency-Key: example-update_product_api_v1_products__product_id__patch' \
  --header 'Content-Type: application/json' \
  --data-raw '{
  "expected_version": 1,
  "sku": "example"
}' \
  --url "${SHOPENTLY_SHOP_URL%/}"/api/v1/products/example

PHP

<?php
declare(strict_types=1);

$baseUrl = rtrim(getenv('SHOPENTLY_SHOP_URL') ?: '', '/');
$origin = parse_url($baseUrl);
if (!$origin || ($origin['scheme'] ?? '') !== 'https' || empty($origin['host']) ||
    isset($origin['user'], $origin['pass']) || isset($origin['user']) || isset($origin['path']) ||
    isset($origin['query']) || isset($origin['fragment'])) {
    throw new RuntimeException('SHOPENTLY_SHOP_URL must be an HTTPS origin');
}
$credential = getenv('SHOPENTLY_ACCESS_TOKEN') ?: '';
if ($credential === '' || strpbrk($credential, "\r\n") !== false) {
    throw new RuntimeException('Invalid credential');
}
$headers = ['Idempotency-Key: example-update_product_api_v1_products__product_id__patch', 'Content-Type: application/json'];
$headers[] = 'Authorization: Bearer ' . $credential;
$responseHeaders = [];
$handle = curl_init($baseUrl . '/api/v1/products/example');
curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => false,
    CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_SSL_VERIFYHOST => 2,
    CURLOPT_CONNECTTIMEOUT => 5,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HEADERFUNCTION => static function ($handle, string $line) use (&$responseHeaders): int {
        if (str_contains($line, ':')) {
            [$name, $value] = explode(':', $line, 2);
            $responseHeaders[strtolower(trim($name))] = trim($value);
        }
        return strlen($line);
    },
]);
curl_setopt($handle, CURLOPT_POSTFIELDS, '{
  "expected_version": 1,
  "sku": "example"
}');
$raw = curl_exec($handle);
$status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
$error = curl_error($handle);
curl_close($handle);
if ($raw === false) {
    throw new RuntimeException('Transport error: ' . $error);
}
if ($status < 200 || $status >= 300) {
    throw new RuntimeException('HTTP ' . $status . '; request_id=' .
        ($responseHeaders['x-request-id'] ?? '') . '; retry_after=' . ($responseHeaders['retry-after'] ?? ''));
}
$result = $raw === '' ? null : json_decode($raw, true, 512, JSON_THROW_ON_ERROR | JSON_BIGINT_AS_STRING);
echo json_encode($result, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), PHP_EOL;

Python

from contextlib import ExitStack
import json
import os
from urllib.parse import urlsplit

import requests  # python -m pip install requests

base_url = os.environ["SHOPENTLY_SHOP_URL"].rstrip("/")
origin = urlsplit(base_url)
if origin.scheme != "https" or not origin.hostname or origin.username or origin.password or origin.path or origin.query or origin.fragment:
    raise ValueError("SHOPENTLY_SHOP_URL must be an HTTPS origin")
credential = os.environ['SHOPENTLY_ACCESS_TOKEN']
if not credential or "\r" in credential or "\n" in credential:
    raise ValueError("Invalid credential")
headers = {'Idempotency-Key': 'example-update_product_api_v1_products__product_id__patch'}
headers["Authorization"] = 'Bearer' + " " + credential
body = '{\n  "expected_version": 1,\n  "sku": "example"\n}'
if body is not None:
    headers["Content-Type"] = "application/json"

with ExitStack() as stack:
    session = stack.enter_context(requests.Session())
    files = None
    if False:
        image = stack.enter_context(open(os.environ["SHOPENTLY_IMAGE_FILE"], "rb"))
        if os.fstat(image.fileno()).st_size > 5242880:
            raise ValueError("Image exceeds 5 MiB")
        files = {"file": ("article.png", image, "image/png")}
    response = session.request(
        'PATCH', base_url + '/api/v1/products/example', headers=headers,
        data=body.encode("utf-8") if body is not None else None, files=files,
        timeout=(5, 30), allow_redirects=False,
    )
    if not 200 <= response.status_code < 300:
        raise RuntimeError(f"HTTP {response.status_code}; request_id={response.headers.get('X-Request-ID', '')}; retry_after={response.headers.get('Retry-After', '')}")
    if False:
        if response.headers.get("Content-Type") != "image/webp" or len(response.content) > 5242880:
            raise ValueError("Unexpected image response")
        with open(os.environ["SHOPENTLY_OUTPUT_FILE"], "xb") as output:
            output.write(response.content)
    else:
        result = response.json() if response.content else None
        print(json.dumps(result, ensure_ascii=False, indent=2))

Exemple de réponse 200

JSON
{
  "data": {
    "deleted": false,
    "id": "example",
    "version": 1
  },
  "meta": {
    "request_id": "example"
  }
}

Fonctionnement

  • L'intégration peut lire les produits non supprimés, y compris les brouillons, sauf ceux bloqués par une limite du forfait.
  • Ils sont exclus des listes et un accès direct renvoie entitlements.resource_blocked.
  • La visibilité API diffère donc de la publication dans la boutique.
  • Utilisez des identifiants existants pour les marques, catégories, caractéristiques et médias.
  • Les prix précisent devise, liste de prix, traitement fiscal et versions de concurrence.
  • Les prix des produits et variantes sont distincts ; utilisez la structure prévue et des chaînes décimales exactes.

Paramètres

Paramètres de chemin

product_idChaîneObligatoire

Identifiant du produit.

Contraintes
pattern
"^[a-z0-9][a-z0-9_-]{0,62}$"
Exemple: "example"

Paramètres de requête

localeChaîne nullFacultatif

Langue sélectionnée pour cette requête.

Chaîne:minLength: 2maxLength: 20
market_idChaîne nullFacultatif

Identifiant de marché renvoyé par le point d'accès au contexte.

Contraintes · Chaîne 1
pattern
"^[a-z0-9][a-z0-9_-]{0,62}$"
market_codeChaîne nullFacultatif

Code de marché renvoyé par le point d'accès au contexte.

Chaîne:minLength: 1maxLength: 16

En-têtes de requête

Idempotency-KeyChaîneObligatoire

Identifiant unique d'une opération logique d'écriture.

minLength: 1maxLength: 128
Contraintes
pattern
"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$"
Exemple: "example-update_product_api_v1_products__product_id__patch"

Corps de la requête

application/json · Obligatoire

ProductUpdate
brand_idChaîne nullFacultatif

Identifiant de la marque.

Chaîne:minLength: 1maxLength: 63
conditionChaîneFacultatif

État du produit.

Contraintes
enum
["new", "refurbished", "used", "returned", "display", "damaged"]
descriptionChaîne nullFacultatif

Description dans la langue sélectionnée.

Chaîne:maxLength: 100000
Exemple: null
expected_variant_price_versionsObjetChamps supplémentaires: Entier
minimum: 0maximum: 9223372036854775807
Facultatif

Versions des prix lues précédemment, indexées par identifiant de variante.

maxProperties: 250
Contraintes
propertyNames
{"maxLength": 63, "minLength": 1}
expected_versionEntierObligatoire

Version lue avant l'écriture ; une différence entraîne le rejet de la modification.

minimum: 1maximum: 9223372036854775807
Exemple: 1
feature_value_idsTableau <Chaîne> Facultatif

Identifiants des valeurs de caractéristiques sélectionnées.

maxItems: 1000
Tableau:minLength: 1maxLength: 63
identifiersProductIdentifiers nullFacultatif

Codes d'identification du produit.

market_idsTableau <Chaîne> Facultatif

Identifiants des marchés sélectionnés.

maxItems: 1000
Tableau:minLength: 1maxLength: 63
media_idsTableau <Chaîne> Facultatif

Identifiants de médias déjà existants.

maxItems: 20
Tableau:minLength: 1maxLength: 63
meta_descriptionChaîne nullFacultatif

Description pour les moteurs de recherche dans la langue sélectionnée.

Chaîne:maxLength: 160
meta_keywordsTableau <Chaîne> nullFacultatif

Mots-clés pour les moteurs de recherche dans la langue sélectionnée.

Tableau:maxItems: 20
Tableau / Tableau:maxLength: 50
meta_titleChaîne nullFacultatif

Titre pour les moteurs de recherche dans la langue sélectionnée.

Chaîne:maxLength: 70
nameChaîne nullFacultatif

Nom de la ressource dans le contexte concerné.

Chaîne:maxLength: 300
Exemple: null
pricingProductPrice Facultatif

Configuration du prix et versions nécessaires au contrôle de concurrence.

Exemple: {"amount":"0.00","currency":"PLN","expected_price_list_version":1,"expected_version":0,"kind":"base","price_list_id":"example","tax_included":true}
primary_category_idChaîne nullFacultatif

Identifiant de la catégorie principale.

Chaîne:minLength: 1maxLength: 63
publishedBooléenFacultatif

Indique si la ressource est publiée.

skuChaîneFacultatif

Code de l'unité de gestion des stocks.

minLength: 1maxLength: 100
Contraintes
pattern
"^\\S+$"
Exemple: "example"
slugChaîne nullFacultatif

Segment du chemin URL dans la langue sélectionnée.

Chaîne:maxLength: 160
tax_category_idChaîne nullFacultatif

Identifiant de la catégorie fiscale.

Chaîne:minLength: 1maxLength: 63
variant_value_idsTableau <Chaîne> Facultatif

Identifiants des valeurs de variante sélectionnées.

maxItems: 250
Tableau:minLength: 1maxLength: 63
variants_enabledBooléenFacultatif

Indique si le produit utilise des variantes.

Réponses

Réponse 200

dataMutationData Obligatoire

Données d'une réponse réussie.

Champs · data
deletedBooléenObligatoire

Indique si la suppression a été appliquée.

idChaîneObligatoire

Identifiant de ressource ; conserver la valeur renvoyée.

versionEntier nullObligatoire

Version de ressource utilisée pour le contrôle de concurrence.

metaContextPageMeta ContextMeta PageMeta RequestMeta Obligatoire

Métadonnées de la réponse.

Champs · meta

ContextPageMeta

localeChaîneObligatoire

Langue sélectionnée pour cette requête.

market_codeChaîneObligatoire

Code de marché renvoyé par le point d'accès au contexte.

market_idChaîneObligatoire

Identifiant de marché renvoyé par le point d'accès au contexte.

paginationPagination Obligatoire

Métadonnées de pagination par curseur.

request_idChaîneObligatoire

Identifiant de demande ; dans les métadonnées, identifiant de la requête HTTP.

minLength: 1maxLength: 100

ContextMeta

localeChaîneObligatoire

Langue sélectionnée pour cette requête.

market_codeChaîneObligatoire

Code de marché renvoyé par le point d'accès au contexte.

market_idChaîneObligatoire

Identifiant de marché renvoyé par le point d'accès au contexte.

request_idChaîneObligatoire

Identifiant de demande ; dans les métadonnées, identifiant de la requête HTTP.

minLength: 1maxLength: 100

PageMeta

paginationPagination Obligatoire

Métadonnées de pagination par curseur.

request_idChaîneObligatoire

Identifiant de demande ; dans les métadonnées, identifiant de la requête HTTP.

minLength: 1maxLength: 100

RequestMeta

request_idChaîneObligatoire

Identifiant de demande ; dans les métadonnées, identifiant de la requête HTTP.

minLength: 1maxLength: 100
Exemple de réponse

Statut HTTP

200
Réponse réussie Schéma
400
Requête invalide
401
Authentification requise
403
Accès refusé
404
Ressource introuvable
409
Conflit de version
422
Échec de validation
429
Limite de requêtes dépassée
Autres statuts HTTP (7)
405
Méthode non autorisée
413
Requête trop volumineuse
415
Format non pris en charge
500
Erreur du serveur
502
Erreur de passerelle API
503
Service indisponible
504
Délai de passerelle dépassé

Catalogue des erreurs: ErrorResponse

En-têtes

401

WWW-AuthenticateChaîneFacultatif

Schéma d'authentification exigé par le serveur.

405

AllowChaîneFacultatif

Méthodes HTTP prises en charge par cette ressource.

429, 503

Retry-AfterEntierFacultatif

Délai minimal avant une nouvelle tentative, en secondes.

minimum: 0