Эхлэх

Дөрвөн алхамд холбогдоно

Жишээнүүд бодит хост, бодит endpoint-оор бичигдсэн. demo-г өөрийн дэлгүүрийн нэрээр солиход л хангалттай.

  1. Алхам 1

    API түлхүүр авах

    Дэлгүүрийнхээ консолд Тохиргоо → Интеграц → «API түлхүүр» хэсэгт түлхүүр үүсгэж, хэрэгтэй эрхээ (scope) сонгоно. Түлхүүр зөвхөн нэг удаа харагдана — алдагдвал тэндээсээ шууд хүчингүй болгоно.

    # Түлхүүрээ орчны хувьсагчид хадгална — кодонд бүү бич
    export OPENSHOP_API_KEY="osk_live_…"
  2. Алхам 2

    Анхны хүсэлт

    Каталог токенгүй нээлттэй — дэлгүүрийн өөрийн хост дээр шууд уншина. Захиалга унших бол order:read эрхтэй түлхүүрээ Authorization толгойд тавина.

    # Каталог — токенгүй, дэлгүүрийн өөрийн хост
    curl "https://demo.openshop.mn/api/v1/shop/products?limit=5"
    
    # Захиалга — order:read эрхтэй түлхүүрээр
    curl "https://api.openshop.mn/v1/shop/orders?status=paid&limit=20" \
      -H "Authorization: Bearer $OPENSHOP_API_KEY"
  3. Алхам 3

    Webhook холбох

    Консолын мөн л Интеграц хэсэгт HTTPS хаягаа бүртгэж үйл явдлаа сонгоно. Хүсэлт бүрийн X-OpenShop-Signature толгойг түүхий биеийг задлахаас ӨМНӨ шалгана.

    import crypto from "node:crypto";
    
    // rawBody = задлаагүй Buffer (жишээ нь express.raw({ type: "application/json" }))
    export function verifyOpenShop(secret, header, rawBody, nowMs = Date.now()) {
      const parts = header.split(",").map((p) => p.trim().split("="));
      const t = parts.find(([k]) => k === "t")?.[1];
      const sigs = parts.filter(([k]) => k === "v1").map(([, v]) => v);
      if (!t || sigs.length === 0) return false;
      if (Math.abs(Math.floor(nowMs / 1000) - Number(t)) > 300) return false;
    
      const expected = crypto
        .createHmac("sha256", secret)
        .update(t + ".")
        .update(rawBody)
        .digest("hex");
      // Нууц солих 24 цагт толгойд хоёр v1 ирнэ — аль нэг нь таарвал хүчинтэй.
      return sigs.some(
        (s) =>
          s.length === expected.length &&
          crypto.timingSafeEqual(Buffer.from(s), Buffer.from(expected)),
      );
    }
    Webhook-ийн дэлгэрэнгүй
  4. Алхам 4

    Апп бүтээх (OAuth)

    Олон дэлгүүрт үйлчлэх апп бол app.openshop.mn → Хөгжүүлэгч хэсэгт апп бүртгэнэ. Мерчант таны аппыг суулгахдаа эрхүүдийг зөвшөөрч, та дэлгүүр тус бүрийн токен авна.

    # 1) Мерчантыг зөвшөөрлийн хуудас руу илгээнэ (PKCE S256 заавал)
    https://id.openshop.mn/oauth2/authorize?response_type=code
      &client_id=app_…&redirect_uri=https://your.app/callback
      &scope=order:read%20product:read&state=…
      &code_challenge=…&code_challenge_method=S256
    
    # 2) Буцаж ирсэн code-оо токеноор солино
    curl -X POST https://id.openshop.mn/oauth2/token \
      -u "$CLIENT_ID:$CLIENT_SECRET" \
      -d grant_type=authorization_code \
      -d code=… -d redirect_uri=https://your.app/callback \
      -d code_verifier=…
    Апп бүтээх дараалал
Боломжууд

API-аар юу хийж болох вэ

Зөвхөн өнөөдөр ажиллаж буй зүйлсийг жагсаав. Бүх endpoint-ийн параметр, хариуны хэлбэр лавлагаанд бий.

Бүх endpoint

Каталог

Бараа, хувилбар, ангилал, багцыг токенгүй уншина. Монгол хайлт (?q=) дэлгүүрийнхтэй ижил хөдөлгүүрээр ажиллана.

Каталогийн endpoint-ууд

Захиалга

Захиалгын жагсаалт, дэлгэрэнгүй, «хүргэсэн» төлөв. Худалдан авагчийн холбоо барих мэдээлэл тусдаа customer:read эрхээр.

Эрхүүд

Барааны feed

Google Merchant ба Meta Commerce Manager-т зориулсан CSV, XML feed — дэлгүүр бүрийн өөрийн хост дээр.

Feed-ийн формат

Апп ба OAuth

Олон дэлгүүрт суулгадаг апп: authorization code + PKCE, эргэлддэг refresh токен, суулгац бүрийн webhook.

Апп бүтээх

Хязгаар ба алдаа

Хувин бүрийн хязгаар, 429 + Retry-After, алдааны дугтуй {error, code} — салаалах нь зөвхөн code-оор.

Хязгаарын хүснэгт
Нэвтрэлт

API түлхүүр эсвэл OAuth апп

Нэг дэлгүүр өөрийн системээ холбох бол API түлхүүр. Олон дэлгүүрт суулгадаг апп бол OAuth — түлхүүрийг гараар дамжуулахгүй.

Дэлгүүрийн API түлхүүр

Консолын Тохиргоо → Интеграц хэсгээс үүсгэнэ. Түлхүүр osk_live_… хэлбэртэй, нэг л удаа харагдана.

GET https://api.openshop.mn/v1/shop/orders
Authorization: Bearer osk_live_…
  • Хост: https://{slug}.openshop.mn/api/v1/shop/* эсвэл https://api.openshop.mn/v1/shop/* — түлхүүр өөрөө дэлгүүрээ нэрлэдэг. Өөр дэлгүүрийн хост дээр 401 invalid_api_key.
  • Каталог унших (GET /shop/products*, /shop/categories*) токенгүй нээлттэй; product:write түлхүүртэй бол ноорог ч харагдана.
  • POST /shop/checkout-д Idempotency-Key: 24 цагийн турш ижил хариу, өөр биетэй бол 422 idempotency_key_reuse.
  • Өртөг (cost_mnt) ямар ч түлхүүрт буцахгүй.

Апп (OAuth 2.0 + PKCE)

app.openshop.mn → Хөгжүүлэгч хэсэгт хөгжүүлэгчийн нөхцөл-ийг зөвшөөрч (ДАН-аар баталгаажсан байна) апп бүртгэнэ.

  1. Апп үүсгэнэ (≤5): нэр, лого, https redirect URI (яг таарна), хүсэх эрх, webhook URL. client_secret нэг удаа харагдана.
  2. Хөгжүүлэлтийн горим: ноорог аппыг зөвхөн өөрийн эзэмшдэг дэлгүүрт суулгаж туршина.
  3. «Хянуулахаар илгээх» → OpenShop шалгаж туршилтын (сонгосон дэлгүүрт) эсвэл нийтлэгдсэн (Аппын лавлах) болгоно, эсвэл шалтгаантай буцаана.
  • Токен: osa_live_… (1 цаг) · osr_live_… — хэрэглэх бүрд шинэ хос; хуучныг дахин илгээвэл суулгацын бүх токен хүчингүй болно.
  • POST /oauth2/revoke (RFC 7009). Мерчант аппыг салгамагц дараагийн хүсэлт 401 invalid_token.
  • GET /shop/orders/export апп-д хаалттай — жагсаалт + webhook-оор синк хийнэ.

Эрхүүд (scope)

Scope = консолын эрхийн нэр — API түлхүүр ба апп хоёуланд ижил хаалттай жагсаалт. Ажилтан, эрх, түлхүүр удирдах эрх ямар ч credential-д олгогдохгүй. Эрх дутвал 403 insufficient_scope + required_scope.

  • product:read
  • product:write
  • product:delete · мэдрэмтгий
  • order:read
  • order:write
  • customer:read · мэдрэмтгий

customer:read-гүй бол захиалгын худалдан авагчийн талбарууд null + pii_redacted: true. Мэдрэмтгий эрхийг апп хүсвэл хяналтад бичгээр үндэслэнэ.

Webhook

Гарын үсэгтэй, дор хаяж нэг удаа

Консолын Интеграц хэсэгт HTTPS хаягаа бүртгэж үйл явдлаа сонгоно. Бие нь хувийн мэдээлэлгүй нимгэн дугтуй — дэлгэрэнгүйг API-аас татна.

Хүсэлтийн хэлбэр

order.createdorder.paidorder.expiredorder.fulfilledorder.unfulfilledproduct.createdproduct.updatedproduct.deleted
POST https://your.app/openshop-webhook
X-OpenShop-Event-Id: <үйл явдлын id — давхардлыг үүгээр шүү>
X-OpenShop-Delivery-Id: <оролдлого бүрд шинэ>
X-OpenShop-Event-Type: order.paid
X-OpenShop-Api-Version: 2026-09
X-OpenShop-Signature: t=1750000000,v1=06f35f44…
User-Agent: OpenShop-Webhooks/1.0 (+https://openshop.mn)

{"id":"evt_1","type":"order.paid","occurred_at":"…",
 "created_at":"…","api_version":"2026-09",
 "shop":{"slug":"demo"},
 "data":{"order_id":"…","status":"paid","total_mnt":50000,
         "discount_mnt":0,"paid_at":"…"}}

Гарын үсэг шалгах

v1 = HMAC-SHA256(secret, t + "." + rawBody), hex. Түүхий биеийг задлахаас өмнө шалгаж, t-г ±300 секундээр хязгаарла. Нууц солих 24 цагт толгойд хоёр v1 ирнэ — аль нэг таарвал хүчинтэй. Node.js, Go жишээ нь Алхам 3-т.

Тест вектор

secret
s3cr3t
body
{"id":"evt_1","type":"order.paid"}
t
1750000000
v1
06f35f44b88eceb2f2df6696c68710a8265597b7ca7d225a71d588159757f0d9

Дахин оролдлого ба дараалал

  • Нэг үйл явдал давхар ирж болно → X-OpenShop-Event-Id-ээр давхардлыг шүү. Дараалал баталгаагүй: order.expired-ийн дараа order.paid ирж болно — эцсийн төлөв нь GET /shop/orders/{id}.
  • 2xx-ыг шууд буцаа (10 секундын хугацаа). 5xx · 408 · 429 · хугацаа хэтрэх → 1м · 5м · 30м · 2ц · 6ц · 12ц · 24ц (±20%), нийт 8 оролдлого ≈ 45 цаг. Бусад 4xx → шууд зогсоно; 410 → endpoint унтарна.
  • Endpoint = https заавал, нийтийн хаяг; redirect дагахгүй.

Бөөнөөр дахин илгээх

Тасалдлын дараа консолын «Бүх амжилтгүйг дахин илгээх» эсвэл POST /shop/webhooks/{id}/replay — амжилтгүй бүрд нэг шинэ оролдлого (ижил X-OpenShop-Event-Id), нэг хүсэлтэд ≤100, endpoint тус бүрд минутад 1.

Аппын webhook: суулгац бүрд эрхээр шүүгдсэн endpoint автоматаар үүсч, толгойд X-OpenShop-Installation-Id · X-OpenShop-Shop ирнэ. Салгахад app.uninstalled — тухайн дэлгүүрийн өгөгдлийг 30 хоногт устгана.

Барааны feed

Google Merchant · Meta Commerce Manager

Дэлгүүр бүр өөрийн хост дээр токенгүй feed-тэй. Каталогийн «Scheduled fetch»-д холбоосыг нь л өгнө.

Хаяг ба формат

GET https://{slug}.openshop.mn/api/v1/shop/feed.csv
    → text/csv (UTF-8 BOM, RFC 4180)
GET https://{slug}.openshop.mn/api/v1/shop/feed.xml
    → RSS 2.0 + xmlns:g (Google Merchant)

Кэш 5 минут, ETag → If-None-Match = 304. Зочны IP тус бүрд 30 хүсэлт/мин.

Юу орох вэ

  • Идэвхтэй бараа бүр; ноорог хэзээ ч орохгүй. Өртөг, үлдэгдлийн тоо гарахгүй.
  • Нэг хувилбартай бараа = нэг мөр, id = барааны UUID (Pixel-ийн content_ids-тэй ижил). 2+ хувилбар → хувилбар бүр мөр, item_group_id = барааны UUID.
  • Үнэ "50000 MNT"; sale_price зөвхөн хямдрал идэвхтэй ба үнийн түүхээр батлагдсан үед — дэлгүүрийнхтэй ижил тооцоо.
Хязгаар ба алдаа

429, Retry-After ба {error, code}

Хувин бүр тусдаа — нэг интегратор бусдыг хаахгүй. Алдааг текстээр биш, машин уншдаг code-оор салаална.

Хязгаарууд

Rate limit хувингууд
ГадаргууХязгаарХувин
API түлхүүртэй унших хүсэлт600 / минтүлхүүр бүрд
API түлхүүртэй бичих хүсэлт120 / минтүлхүүр бүрд
Апп токентой унших / бичих600 / 120 минсуулгац бүрд
Нэг аппын бүх суулгац нийлээд6000 / минclient_id
POST /oauth2/token (апп)60 / минclient_id
POST /shop/checkout (токенгүй)30 / минзочны IP
GET /shop/feed.csv · feed.xml30 / минзочны IP
GET /shop/suggest120 / минзочны IP
POST /shop/orders/lookup10 / минзочны IP

Түлхүүртэй хүсэлт X-RateLimit-Limit / Remaining / Reset толгойтой; хэтэрвэл 429 rate_limited + Retry-After.

Алдааны дугтуй

HTTP 409
{"error": "Нөөц хүрэлцэхгүй байна.",
 "code": "insufficient_stock", "remaining": 2}

error = хэрэглэгчид харуулах Монгол текст (өөрчлөгдөж болно). code = гэрээ: шинэ нь нэмэгдэнэ, байгаа нь нэрээ солихгүй. Танихгүй код гарвал HTTP статусаар нь ханд.

  • out_of_stock
  • insufficient_stock
  • payments_not_ready
  • payable_closed
  • rate_limited
  • unauthorized
  • invalid_api_key
  • insufficient_scope
  • api_key_scope_invalid
  • api_key_limit_reached
  • webhook_url_invalid
  • webhook_events_invalid
  • webhook_limit_reached
  • not_found
  • validation_error
  • idempotency_key_invalid
  • idempotency_in_progress
  • idempotency_key_reuse
  • endpoint_sunset
  • invalid_signature
  • connector_required
  • connector_not_linked
  • invalid_token
  • app_route_not_allowed
  • webhook_app_managed
  • webhook_endpoint_inactive
  • webhook_replay_in_flight
  • buyer_email_required
  • digital_qty_invalid
  • already_enrolled

Хасах өөрчлөлт = Deprecation + Sunset толгой, дор хаяж 6 сарын мэдэгдэл, дараа нь 410 endpoint_sunset. Одоогоор хуучирсан endpoint байхгүй.

Өөрчлөлтүүд

v1 дотор нэмэгдсэн зүйлс

Нэмэлт өөрчлөлт л v1 дотор гарна; эвдэх өөрчлөлт бол /v2.

  1. 2026-09-24

    Гуравдагч апп (OAuth), customer:read

    • appOAuth: authorization code + PKCE (S256 заавал), confidential client; мерчант нэг дэлгүүрт суулгаж бүх scope-ыг зөвшөөрнө.
    • Токен: osa_live_… (1 цаг) · osr_live_… (эргэлддэг, дахин ашиглавал суулгацын бүх токен хүчингүй) · POST /oauth2/revoke (RFC 7009).
    • Шинэ эрх customer:read — захиалгын худалдан авагчийн мэдээлэл; байхгүй бол null + pii_redacted. Өмнөх order:read түлхүүр/эрхэд автоматаар нэмэгдсэн.
    • Аппын webhook: суулгац бүрд scope-оор шүүгдсэн endpoint, X-OpenShop-Installation-Id · X-OpenShop-Shop, app.uninstalled.
  2. 2026-09-22

    Барааны feed (CSV · XML)

    • GET /shop/feed.csv, GET /shop/feed.xml — Google Merchant / Meta Commerce Manager формат, токенгүй.
    • Дэлгүүр, формат тус бүрд 5 минутын кэш, ETag / If-None-Match → 304, 30 хүсэлт/мин.
  3. 2026-09-16

    API түлхүүр, webhook, Idempotency-Key, машин уншдаг гэрээ

    • Authorization: Bearer osk_live_… — дэлгүүр тус бүрийн түлхүүр, scope = эрхийн slug (product:read/write/delete, order:read/write).
    • Webhook OUT: order.* · product.* үйл явдал, X-OpenShop-Signature (HMAC-SHA256), 8 оролдлого ≈ 45 цаг.
    • POST /shop/checkout дээр Idempotency-Key (24 цаг replay, идэвхтэй үед 409, өөр биетэй 422).
    • Deprecation / Sunset бодлого (RFC 9745 / 8594, ≥6 сар); OpenAPI 3.1 гэрээ CI-д spectral + oasdiff-ээр хамгаалагдсан.
  4. 2026-09-08

    Нийтийн дэлгүүрийн API — 5 засвар

    • Public хариунд cost_mnt орохоо болив.
    • Rate-limit хувин зочин тус бүрд (нэг дундын хувин биш).
    • GET /shop/checkout/{id} төлөгдсөн үед paid_at буцаана.
    • CORS: зөвхөн PUBLIC_API_CORS_ORIGINS-д нэрлэсэн origin, унших route-уудад л.
    • Алдааны дугтуйд машин уншдаг code (out_of_stock · insufficient_stock + remaining …).