跳至内容
ShopentlyLearn

第一个请求

检查与商店的连接。按照步骤创建 API 密钥、获取令牌并发送第一个请求。

快速开始

  1. 创建密钥
  2. 获取令牌
  3. 检查连接

快速开始

  1. 创建 API 密钥

    在商店管理后台打开 API 设置,然后创建密钥。

    请将密钥保存在安全的地方。关闭窗口后,你将无法再次查看它。

    创建 API 密钥

    SHOPENTLY_SHOP_URL
    商店的 HTTPS 地址,不含 /api/v1。请将示例替换为您自己的地址。
    https://twoj-sklep.example
    SHOPENTLY_API_KEY
    在商店管理面板中生成的密钥。
    身份验证

    cURL 示例中的 SHOPENTLY_SHOP_URL、SHOPENTLY_API_KEY 和 SHOPENTLY_ACCESS_TOKEN 是本地占位符。请在集成服务器上私密设置这些变量;切勿在源代码、浏览器页面、截图或日志中公开真实凭据。

  2. 获取 · 令牌

    POST/api/v1/auth/token

    使用密钥获取令牌。后续请求将携带这个令牌。

    示例

    请求示例

    cURL

    : "${SHOPENTLY_SHOP_URL:?}"
    
    response=$(curl --disable --globoff --fail-with-body --silent --show-error \
      --proto '=https' --connect-timeout 5 --max-time 30 \
      --write-out '\n%{http_code}' \
      --request POST \
      --header "Authorization: ApiKey ${SHOPENTLY_API_KEY:?}" \
      --header 'Content-Type: application/json' \
      --data-raw '{}' \
      --url "${SHOPENTLY_SHOP_URL%/}"/api/v1/auth/token) || exit 1
    status=$(printf '%s' "$response" | tail -n 1)
    case "$status" in
      2??) printf '%s\n' "$response" | sed '$d' ;;
      *) printf 'HTTP %s\n' "$status" >&2; exit 1 ;;
    esac
    

    PHP

    <?php
    $base = rtrim(getenv('SHOPENTLY_SHOP_URL') ?: '', '/');
    $origin = parse_url($base);
    if (!$origin || ($origin['scheme'] ?? '') !== 'https' || empty($origin['host']) ||
        array_intersect(['user', 'pass', 'path', 'query', 'fragment'], array_keys($origin))) {
        throw new RuntimeException('SHOPENTLY_SHOP_URL must be an HTTPS origin');
    }
    $credential = getenv('SHOPENTLY_API_KEY') ?: '';
    if ($credential === '' || preg_match('/\s/', $credential)) {
        throw new RuntimeException('Invalid credential');
    }
    
    $curl = curl_init($base . '/api/v1/auth/token');
    curl_setopt_array($curl, [
        CURLOPT_CUSTOMREQUEST => 'POST',
        CURLOPT_HTTPHEADER => ['Authorization: ApiKey ' . $credential, 'Content-Type: application/json'],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_POSTFIELDS => '{}',
    ]);
    $raw = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
    curl_close($curl);
    if ($raw === false || $status < 200 || $status >= 300) {
        throw new RuntimeException('Request failed; HTTP ' . $status);
    }
    $result = json_decode($raw, true, 512, JSON_THROW_ON_ERROR | JSON_BIGINT_AS_STRING);
    echo json_encode($result, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT), PHP_EOL;
    

    Python

    import json
    import os
    from urllib.parse import urlsplit
    import requests
    
    base = os.environ["SHOPENTLY_SHOP_URL"].rstrip("/")
    origin = urlsplit(base)
    if (origin.scheme != "https" or not origin.hostname or origin.username is not None
            or origin.path or origin.query or origin.fragment):
        raise ValueError("SHOPENTLY_SHOP_URL must be an HTTPS origin")
    credential = os.environ["SHOPENTLY_API_KEY"]
    if not credential or any(c.isspace() for c in credential):
        raise ValueError("Invalid credential")
    
    response = requests.request(
        "POST", base + "/api/v1/auth/token",
        headers={"Authorization": "ApiKey " + credential},
        timeout=(5, 30), allow_redirects=False, json={},
    )
    if not 200 <= response.status_code < 300:
        raise RuntimeError(f"HTTP {response.status_code}")
    result = response.json()
    print(json.dumps(result, ensure_ascii=False, indent=2))
    

    响应示例 · HTTP 200

    JSON
    {
      "data": {
        "access_token": "EXAMPLE_ACCESS_TOKEN",
        "expires_at": "2026-01-01T12:00:00Z",
        "expires_in": 900,
        "rate_limits": {
          "auth_per_minute": 10,
          "read_per_minute": 60,
          "write_per_minute": 10
        },
        "token_type": "Bearer"
      },
      "meta": {
        "request_id": "example"
      }
    }
    

    响应

    data.access_token
    用于 Authorization: Bearer 请求头的令牌。运行独立示例前,请设置 SHOPENTLY_ACCESS_TOKEN。
    data.expires_in
    自签发起 900 秒,即 15 分钟。发送请求不会延长有效期。
    data.expires_at
    以 UTC 表示的到期时间。当前的访问限制可能会提前阻止令牌访问。
    data.rate_limits
    Engine ≥ 4.143.0. 每次令牌响应都包含签发时店铺策略中的 data.rate_limits。所有密钥共享这些额度;获取新令牌不会重置额度。套餐变更可能在令牌过期前生效。
  3. 检查与商店的连接

    GET/api/v1/context

    携带令牌发送请求。响应会显示商店中可用的语言和市场。

    请求示例

    cURL

    : "${SHOPENTLY_SHOP_URL:?}"
    
    response=$(curl --disable --globoff --fail-with-body --silent --show-error \
      --proto '=https' --connect-timeout 5 --max-time 30 \
      --write-out '\n%{http_code}' \
      --request GET \
      --header "Authorization: Bearer ${SHOPENTLY_ACCESS_TOKEN:?}" \
      --url "${SHOPENTLY_SHOP_URL%/}"/api/v1/context) || exit 1
    status=$(printf '%s' "$response" | tail -n 1)
    case "$status" in
      2??) printf '%s\n' "$response" | sed '$d' ;;
      *) printf 'HTTP %s\n' "$status" >&2; exit 1 ;;
    esac
    

    PHP

    <?php
    $base = rtrim(getenv('SHOPENTLY_SHOP_URL') ?: '', '/');
    $origin = parse_url($base);
    if (!$origin || ($origin['scheme'] ?? '') !== 'https' || empty($origin['host']) ||
        array_intersect(['user', 'pass', 'path', 'query', 'fragment'], array_keys($origin))) {
        throw new RuntimeException('SHOPENTLY_SHOP_URL must be an HTTPS origin');
    }
    $credential = getenv('SHOPENTLY_ACCESS_TOKEN') ?: '';
    if ($credential === '' || preg_match('/\s/', $credential)) {
        throw new RuntimeException('Invalid credential');
    }
    
    $curl = curl_init($base . '/api/v1/context');
    curl_setopt_array($curl, [
        CURLOPT_CUSTOMREQUEST => 'GET',
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $credential],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_PROTOCOLS => CURLPROTO_HTTPS,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT => 30,
    ]);
    $raw = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
    curl_close($curl);
    if ($raw === false || $status < 200 || $status >= 300) {
        throw new RuntimeException('Request failed; HTTP ' . $status);
    }
    $result = json_decode($raw, true, 512, JSON_THROW_ON_ERROR | JSON_BIGINT_AS_STRING);
    echo json_encode($result, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT), PHP_EOL;
    

    Python

    import json
    import os
    from urllib.parse import urlsplit
    import requests
    
    base = os.environ["SHOPENTLY_SHOP_URL"].rstrip("/")
    origin = urlsplit(base)
    if (origin.scheme != "https" or not origin.hostname or origin.username is not None
            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 any(c.isspace() for c in credential):
        raise ValueError("Invalid credential")
    
    response = requests.request(
        "GET", base + "/api/v1/context",
        headers={"Authorization": "Bearer " + credential},
        timeout=(5, 30), allow_redirects=False,
    )
    if not 200 <= response.status_code < 300:
        raise RuntimeError(f"HTTP {response.status_code}")
    result = response.json()
    print(json.dumps(result, ensure_ascii=False, indent=2))
    

    响应示例 · HTTP 200

    JSON
    {
      "data": {
        "base_market_id": "example",
        "default_locale": "en",
        "global_enabled": false,
        "languages": [
          {
            "code": "en",
            "direction": "ltr",
            "is_default": true,
            "name": "English",
            "native_name": "English",
            "status": "draft"
          }
        ],
        "markets": [
          {
            "code": "PL",
            "currency": {
              "code": "PLN",
              "minor_units": 2
            },
            "id": "example",
            "is_default": true,
            "name": "Poland",
            "price_list": null,
            "status": "draft"
          }
        ],
        "multilingual_enabled": false
      },
      "meta": {
        "locale": "en",
        "market_code": "PL",
        "market_id": "example",
        "request_id": "example"
      }
    }
    

    响应

    在后续请求中使用返回的语言和市场标识符。

如何确认连接成功?

收到包含商店数据的 HTTP 200 响应,即可确认连接正常。

完整程序在一次运行中获取令牌并读取上下文。额外代码用于验证配置、处理传输和错误。

完整示例

运行示例

你可以在终端中运行完整示例,也可以使用 PHP 或 Python。

PHP 8.3 (64-bit, ext-curl, JSON) · Python 3.12 + requests · cURL 8 + jq 1.7.

cURL

#!/bin/sh
# POSIX shell, curl 8+, jq 1.7. Credentials stay in environment variables.
set -eu
: "${SHOPENTLY_SHOP_URL:?}"
base=${SHOPENTLY_SHOP_URL%/}
case "$base" in https://*) ;; *) echo 'Use an HTTPS origin' >&2; exit 1;; esac
authority=${base#https://}
case "$authority" in ''|*/*|*\?*|*\#*|*@*) echo 'Use an HTTPS origin' >&2; exit 1;; esac
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT HUP INT TERM
chmod 700 "$work"
encode() { printf '%s' "$1" | jq -sRr @uri; }
api() {
    method=$1; path=$2; body=${3-}; key=${4-}
    scheme=Bearer; credential=${SHOPENTLY_ACCESS_TOKEN-}
    if [ "$path" = /api/v1/auth/token ]; then scheme=ApiKey; credential=${SHOPENTLY_API_KEY-}; fi
    [ -n "$credential" ] || { echo 'Missing credential' >&2; return 1; }
    set -- --disable --globoff --silent --show-error --proto '=https' --connect-timeout 5 --max-time 30 \
        --request "$method" --header "Authorization: $scheme $credential" \
        --dump-header "$work/headers" --output "$work/body" --write-out '%{http_code}'
    if [ -n "$key" ]; then set -- "$@" --header "Idempotency-Key: $key"; fi
    if [ -n "$body" ]; then set -- "$@" --header 'Content-Type: application/json' --data-raw "$body"; fi
    status=$(curl "$@" --url "$base$path") || return 1
    case "$status" in
        2??) if [ -s "$work/body" ]; then jq -e . "$work/body"; else printf 'null\n'; fi ;;
        *) printf 'HTTP %s\n' "$status" >&2
           jq -c '.error | {code, fields}' "$work/body" >&2 2>/dev/null || true
           return 1 ;;
    esac
}

token=$(api POST /api/v1/auth/token '{}')
SHOPENTLY_ACCESS_TOKEN=$(printf '%s' "$token" | jq -er '.data.access_token')
export SHOPENTLY_ACCESS_TOKEN
api GET /api/v1/context

PHP

<?php
declare(strict_types=1);
// PHP 8.3+, 64-bit, ext-curl. Credentials stay in environment variables.
function env(string $name): string {
    $value = getenv($name);
    if ($value === false || $value === '' || strpbrk($value, "\r\n") !== false) {
        throw new RuntimeException('Invalid environment variable: ' . $name);
    }
    return $value;
}
$base = rtrim(env('SHOPENTLY_SHOP_URL'), '/');
$origin = parse_url($base);
if (!$origin || ($origin['scheme'] ?? '') !== 'https' || empty($origin['host']) ||
    isset($origin['user']) || isset($origin['pass']) || isset($origin['path']) ||
    isset($origin['query']) || isset($origin['fragment']) || PHP_INT_SIZE !== 8) {
    throw new RuntimeException('Use a 64-bit PHP runtime and an HTTPS origin');
}
class ApiError extends RuntimeException {
    public function __construct(public int $status, public array $headers, public array $error) {
        parent::__construct('HTTP ' . $status . '; code=' . ($error['code'] ?? '') .
            '; request_id=' . ($headers['x-request-id'] ?? ''));
    }
}
function api(string $method, string $path, array $query = [], ?array $body = null, ?string $key = null, bool $apiKey = false): ?array {
    global $base;
    $credential = env($apiKey ? 'SHOPENTLY_API_KEY' : 'SHOPENTLY_ACCESS_TOKEN');
    $headers = ['Authorization: ' . ($apiKey ? 'ApiKey ' : 'Bearer ') . $credential];
    if ($key !== null) $headers[] = 'Idempotency-Key: ' . $key;
    if ($body !== null) $headers[] = 'Content-Type: application/json';
    $url = $base . $path . ($query ? '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986) : '');
    $responseHeaders = [];
    $h = curl_init($url);
    curl_setopt_array($h, [
        CURLOPT_CUSTOMREQUEST => $method, 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 ($h, string $line) use (&$responseHeaders): int {
            if (str_contains($line, ':')) {
                [$name, $value] = explode(':', $line, 2);
                $responseHeaders[strtolower(trim($name))] = trim($value);
            }
            return strlen($line);
        },
    ]);
    if ($body !== null) {
        $json = $apiKey && $body === [] ? '{}' : json_encode($body, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
        curl_setopt($h, CURLOPT_POSTFIELDS, $json);
    }
    $raw = curl_exec($h);
    $status = curl_getinfo($h, CURLINFO_RESPONSE_CODE);
    $error = curl_error($h);
    curl_close($h);
    if ($raw === false) throw new RuntimeException('Transport error: ' . $error);
    if ($status < 200 || $status >= 300) {
        $data = json_decode($raw, true);
        throw new ApiError($status, $responseHeaders, is_array($data) ? ($data['error'] ?? []) : []);
    }
    return $raw === '' ? null : json_decode($raw, true, 512, JSON_THROW_ON_ERROR | JSON_BIGINT_AS_STRING);
}
function output(mixed $value): void {
    echo json_encode($value, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE), PHP_EOL;
}
$token = api('POST', '/api/v1/auth/token', body: [], apiKey: true)['data'];
putenv('SHOPENTLY_ACCESS_TOKEN=' . $token['access_token']);
output(api('GET', '/api/v1/context'));

Python

"""Run with Python 3.12 and requests. Credentials stay in environment variables."""
import json
import os
import random
import time
from urllib.parse import quote, urlencode, urlsplit
import requests

def env(name):
    value = os.environ[name]
    if not value or "\r" in value or "\n" in value:
        raise ValueError("Invalid environment variable: " + name)
    return value

base = env("SHOPENTLY_SHOP_URL").rstrip("/")
origin = urlsplit(base)
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")

class ApiError(RuntimeError):
    def __init__(self, response):
        self.status = response.status_code
        self.request_id = response.headers.get("X-Request-ID", "")
        self.retry_after = response.headers.get("Retry-After", "")
        try:
            self.error = response.json().get("error", {})
        except (ValueError, AttributeError):
            self.error = {}
        super().__init__(f"HTTP {self.status}; code={self.error.get('code', '')}; request_id={self.request_id}")

def api(method, path, *, query=None, body=None, key=None, api_key=False):
    credential = env("SHOPENTLY_API_KEY" if api_key else "SHOPENTLY_ACCESS_TOKEN")
    headers = {"Authorization": ("ApiKey " if api_key else "Bearer ") + credential}
    if key:
        headers["Idempotency-Key"] = key
    if body is not None:
        headers["Content-Type"] = "application/json"
    response = requests.request(method, base + path, params=query, headers=headers,
        data=json.dumps(body, ensure_ascii=False, allow_nan=False).encode() if body is not None else None,
        timeout=(5, 30), allow_redirects=False)
    if not 200 <= response.status_code < 300:
        raise ApiError(response)
    return response.json() if response.content else None

def segment(value):
    return quote(value, safe="")

token = api("POST", "/api/v1/auth/token", body={}, api_key=True)["data"]
os.environ["SHOPENTLY_ACCESS_TOKEN"] = token["access_token"]
print(json.dumps(api("GET", "/api/v1/context"), ensure_ascii=False, indent=2))

错误与恢复