วิธีเชื่อมต่อ Amazon SP API: สอนทีละขั้นตอน

Ashley Innocent

Ashley Innocent

20 March 2026

วิธีเชื่อมต่อ Amazon SP API: สอนทีละขั้นตอน

Apidog สำหรับองค์กร

ติดตั้งภายในองค์กร

SSO & RBAC

รองรับ SOC 2

สำรวจ Apidog Enterprise

สรุปสาระสำคัญ

Amazon Selling Partner API (SP-API) คือ API แบบ REST ที่ช่วยให้เข้าถึงข้อมูลผู้ขายสำหรับคำสั่งซื้อ สินค้าคงคลัง รายการสินค้า และการจัดการการจัดส่งได้ด้วยการเขียนโปรแกรม โดยใช้การยืนยันตัวตนแบบ OAuth 2.0 ร่วมกับ IAM roles, กำหนดให้ต้องมีการลงนาม AWS SigV4 และบังคับใช้ข้อจำกัดอัตราการเรียกใช้ (rate limits) ที่แตกต่างกันไปตามจุดสิ้นสุด (endpoint) (0.1 ถึง 100 คำขอต่อวินาที) คู่มือนี้ครอบคลุมการตั้งค่าบัญชี, การยืนยันตัวตน, จุดสิ้นสุดหลัก, การสมัครสมาชิก webhook และกลยุทธ์การปรับใช้ในสภาพแวดล้อมจริง (production).

บทนำ

Amazon ประมวลผลสินค้ากว่า 350 ล้านรายการในตลาดกว่า 200 แห่งทั่วโลก สำหรับนักพัฒนาที่สร้างเครื่องมืออีคอมเมิร์ซ ระบบจัดการสินค้าคงคลัง หรือแพลตฟอร์มวิเคราะห์ การผสานรวม Amazon SP-API ไม่ใช่ทางเลือก แต่เป็นสิ่งจำเป็น

นี่คือความจริง: ผู้ขายที่จัดการการดำเนินงานของ Amazon ต้องเสียเวลา 20-30 ชั่วโมงต่อสัปดาห์ไปกับการป้อนข้อมูลด้วยตนเองสำหรับคำสั่งซื้อ สินค้าคงคลัง และรายการสินค้า การผสานรวม SP-API ที่แข็งแกร่งจะช่วยให้การซิงค์คำสั่งซื้อ การอัปเดตสินค้าคงคลัง และการจัดการรายการสินค้าในตลาดต่างๆ เป็นไปโดยอัตโนมัติ

คู่มือนี้จะนำคุณไปสู่กระบวนการผสานรวม Amazon SP-API อย่างครบถ้วน คุณจะได้เรียนรู้การตั้งค่าบทบาท IAM, การอนุญาต OAuth 2.0, การลงนามคำขอ AWS SigV4, การจัดการคำสั่งซื้อและสินค้าคงคลัง, การสมัครรับการแจ้งเตือน และการแก้ไขปัญหาข้อผิดพลาด เมื่อจบแล้ว คุณจะมีการผสานรวม Amazon ที่พร้อมใช้งานจริง

💡
Apidog ทำให้การทดสอบการผสานรวม API ง่ายขึ้น ทดสอบจุดสิ้นสุด SP-API ของคุณ, ตรวจสอบความถูกต้องของโฟลว์ OAuth, ตรวจสอบลายเซ็นคำขอ และแก้ไขปัญหาการยืนยันตัวตนในพื้นที่ทำงานเดียว นำเข้าข้อกำหนด API, สร้างการตอบกลับจำลอง และแบ่งปันสถานการณ์การทดสอบกับทีมของคุณ
button

Amazon SP-API คืออะไร?

Amazon Selling Partner API (SP-API) เป็น API แบบ REST ที่ช่วยให้สามารถเข้าถึงข้อมูลผู้ขายจาก Seller Central ได้ด้วยการเขียนโปรแกรม โดยได้เข้ามาแทนที่ Marketplace Web Service (MWS) เวอร์ชันเก่า ด้วยการปรับปรุงด้านความปลอดภัย ประสิทธิภาพ และฟังก์ชันการทำงาน

ความสามารถหลัก

การเปรียบเทียบ SP-API กับ MWS

คุณสมบัติ SP-API MWS (แบบเก่า)
สถาปัตยกรรม RESTful JSON XML-based
การยืนยันตัวตน OAuth 2.0 + IAM MWS Auth Token
ความปลอดภัย การลงนาม AWS SigV4 โทเค็นธรรมดา
ข้อจำกัดอัตรา ไดนามิกตามจุดสิ้นสุด โควตาคงที่
ตลาด จุดสิ้นสุดรวม เฉพาะภูมิภาค
สถานะ ปัจจุบัน เลิกใช้งาน (ธ.ค. 2025)

ย้ายการผสานรวม MWS ใดๆ ไปยัง SP-API ทันที Amazon ได้ประกาศยุติการใช้งาน MWS อย่างสมบูรณ์ในเดือนธันวาคม 2025

ภาพรวมสถาปัตยกรรม API

Amazon ใช้โครงสร้าง API ระดับภูมิภาคพร้อมการอนุญาตแบบรวมศูนย์:

https://sellingpartnerapi-na.amazon.com (North America)
https://sellingpartnerapi-eu.amazon.com (Europe)
https://sellingpartnerapi-fe.amazon.com (Far East)

คำขอทั้งหมดต้องมี:

  1. ลายเซ็น AWS SigV4
  2. โทเค็นการเข้าถึงจากโฟลว์ OAuth
  3. สิทธิ์ของ IAM role ที่เหมาะสม
  4. Request ID สำหรับการติดตาม

ตลาดที่รองรับ

ภูมิภาค ตลาด จุดสิ้นสุด API
อเมริกาเหนือ สหรัฐอเมริกา, แคนาดา, เม็กซิโก sellingpartnerapi-na.amazon.com
ยุโรป สหราชอาณาจักร, เยอรมนี, ฝรั่งเศส, อิตาลี, สเปน, เนเธอร์แลนด์, สวีเดน, โปแลนด์, ตุรกี, อียิปต์, อินเดีย, สหรัฐอาหรับเอมิเรตส์, ซาอุดีอาระเบีย sellingpartnerapi-eu.amazon.com
ตะวันออกไกล ญี่ปุ่น, ออสเตรเลีย, สิงคโปร์, บราซิล sellingpartnerapi-fe.amazon.com

เริ่มต้นใช้งาน: การตั้งค่าบัญชีและ IAM

ขั้นตอนที่ 1: สร้างบัญชีนักพัฒนา Amazon ของคุณ

ก่อนที่จะเข้าถึง SP-API คุณต้องมีการเข้าถึงบัญชีที่เหมาะสม:

  1. เยี่ยมชม Amazon Developer Central
  2. ลงชื่อเข้าใช้ด้วยบัญชี Amazon ของคุณ (ต้องมีการเข้าถึง Seller Central)
  3. ไปที่ Selling Partner API ในแดชบอร์ด
  4. ยอมรับข้อตกลงนักพัฒนา

ขั้นตอนที่ 2: ลงทะเบียนแอปพลิเคชันของคุณ

สร้างโปรไฟล์แอปพลิเคชันใน Seller Central:

  1. เข้าสู่ระบบ Seller Central
  2. ไปที่ Apps and Services > Develop Apps
  3. คลิก Add New App
  4. กรอกรายละเอียดแอปพลิเคชัน:

หลังจากส่งแล้ว คุณจะได้รับ:

ข้อควรระวังด้านความปลอดภัย: จัดเก็บข้อมูลรับรองในตัวแปรสภาพแวดล้อม ห้ามเก็บไว้ในโค้ดโดยเด็ดขาด:

# .env file
AMAZON_APPLICATION_ID="amzn1.application.xxxxx"
AMAZON_CLIENT_ID="amzn1.account.xxxxx"
AMAZON_CLIENT_SECRET="your_client_secret_here"
AMAZON_SELLER_ID="your_seller_id_here"
AWS_ACCESS_KEY_ID="your_aws_access_key"
AWS_SECRET_ACCESS_KEY="your_aws_secret_key"
AWS_REGION="us-east-1"

ขั้นตอนที่ 3: สร้าง IAM Role สำหรับ SP-API

SP-API กำหนดให้ต้องมี IAM role ที่มีสิทธิ์เฉพาะ:

  1. เข้าสู่ระบบ AWS IAM Console
  2. ไปที่ Roles > Create Role
  3. เลือก Another AWS account เป็นเอนทิตีที่เชื่อถือได้
  4. ป้อน Account ID ของ Amazon สำหรับภูมิภาคของคุณ:

ขั้นตอนที่ 4: กำหนดค่านโยบาย IAM

แนบนโยบายนี้เข้ากับ IAM role ของคุณ:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "execute-api:Invoke"
      ],
      "Resource": [
        "arn:aws:execute-api:*:*:*/prod/*/sellingpartnerapi/*"
      ]
    }
  ]
}

ตั้งชื่อ role ของคุณให้สื่อความหมาย เช่น SellingPartnerApiRole และจด ARN ไว้

ขั้นตอนที่ 5: เชื่อมโยง IAM Role กับแอปพลิเคชัน

เชื่อมต่อ IAM role ของคุณเข้ากับแอปพลิเคชัน SP-API:

  1. กลับไปที่ Seller Central > Develop Apps
  2. เลือกแอปพลิเคชันของคุณ
  3. คลิก Edit > IAM Role ARN
  4. ป้อน IAM role ARN ของคุณ
  5. บันทึกการเปลี่ยนแปลง

Amazon จะตรวจสอบ IAM role ภายในไม่กี่นาที คุณจะเห็นสถานะ “Linked” เมื่อพร้อมใช้งาน

ขั้นตอนการยืนยันตัวตน OAuth 2.0

ทำความเข้าใจ SP-API OAuth

Amazon ใช้ OAuth 2.0 สำหรับการอนุญาต นี่คือโฟลว์ที่สมบูรณ์:

1. Seller clicks "Authorize" in your application
2. Your app redirects to Amazon authorization URL
3. Seller logs in and grants permissions
4. Amazon redirects back with authorization code
5. Your app exchanges code for LWA (Login with Amazon) token
6. Your app exchanges LWA token for SP-API access token
7. Your app uses access token for API calls (SigV4 signed)
8. Refresh token when access token expires (1 hour)

ขั้นตอนที่ 6: สร้าง URL การอนุญาต

สร้าง URL การอนุญาต OAuth:

const generateAuthUrl = (clientId, redirectUri, state) => {
  const baseUrl = 'https://www.amazon.com/sp/apps/oauth/authorize';
  const params = new URLSearchParams({
    application_id: process.env.AMAZON_APPLICATION_ID,
    client_id: clientId,
    redirect_uri: redirectUri,
    state: state, // Random string for CSRF protection
    scope: 'sellingpartnerapi::notifications'
  });

  return `${baseUrl}?${params.toString()}`;
};

// Usage
const authUrl = generateAuthUrl(
  process.env.AMAZON_CLIENT_ID,
  'https://your-app.com/callback',
  crypto.randomBytes(16).toString('hex')
);

console.log(`Redirect user to: ${authUrl}`);

OAuth Scopes ที่จำเป็น

ร้องขอเฉพาะสิทธิ์ที่แอปพลิเคชันของคุณต้องการเท่านั้น:

Scope คำอธิบาย กรณีการใช้งาน
sellingpartnerapi::notifications รับการแจ้งเตือน การสมัครสมาชิก Webhook
sellingpartnerapi::migration ย้ายข้อมูลจาก MWS การผสานรวมแบบเก่า

การเข้าถึง API ส่วนใหญ่ถูกควบคุมโดยนโยบาย IAM ไม่ใช่ OAuth scopes

ขั้นตอนที่ 7: แลก Code เพื่อรับ LWA Token

จัดการการเรียกกลับ OAuth และแลกเปลี่ยนรหัสการอนุญาต:

const exchangeCodeForLwaToken = async (code, redirectUri) => {
  const response = await fetch('https://api.amazon.com/auth/o2/token', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: process.env.AMAZON_CLIENT_ID,
      client_secret: process.env.AMAZON_CLIENT_SECRET,
      redirect_uri: redirectUri,
      code: code
    })
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`LWA Token Error: ${error.error_description}`);
  }

  const data = await response.json();

  return {
    access_token: data.access_token,
    refresh_token: data.refresh_token,
    expires_in: data.expires_in, // Typically 3600 seconds (1 hour)
    token_type: data.token_type
  };
};

// Handle callback route
app.get('/callback', async (req, res) => {
  const { spapi_oauth_code, state } = req.query;

  // Verify state matches what you sent (CSRF protection)
  if (state !== req.session.oauthState) {
    return res.status(400).send('Invalid state parameter');
  }

  try {
    const tokens = await exchangeCodeForLwaToken(spapi_oauth_code, 'https://your-app.com/callback');

    // Store tokens in database associated with seller
    await db.sellers.update(req.session.sellerId, {
      amazon_lwa_access_token: tokens.access_token,
      amazon_lwa_refresh_token: tokens.refresh_token,
      amazon_token_expires: Date.now() + (tokens.expires_in * 1000)
    });

    res.redirect('/dashboard');
  } catch (error) {
    console.error('Token exchange failed:', error);
    res.status(500).send('Authentication failed');
  }
});

ขั้นตอนที่ 8: แลก LWA Token เพื่อรับข้อมูลรับรอง SP-API

ใช้ LWA access token เพื่อรับข้อมูลรับรอง AWS ชั่วคราว:

const assumeRole = async (lwaAccessToken) => {
  const response = await fetch('https://api.amazon.com/auth/o2/token', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: process.env.AMAZON_CLIENT_ID,
      client_secret: process.env.AMAZON_CLIENT_SECRET,
      scope: 'sellingpartnerapi::notifications'
    })
  });

  const data = await response.json();

  // Exchange for AWS credentials via STS
  const stsResponse = await fetch('https://sts.amazonaws.com/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Authorization': `Bearer ${data.access_token}`
    },
    body: new URLSearchParams({
      Action: 'AssumeRole',
      RoleArn: 'arn:aws:iam::YOUR_ACCOUNT:role/SellingPartnerApiRole',
      RoleSessionName: 'sp-api-session',
      Version: '2011-06-15'
    })
  });

  return stsResponse;
};

ขั้นตอนที่ 9: ใช้การรีเฟรช Token

Access token จะหมดอายุหลังจาก 1 ชั่วโมง ใช้การรีเฟรชอัตโนมัติ:

const refreshLwaToken = async (refreshToken) => {
  const response = await fetch('https://api.amazon.com/auth/o2/token', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: new URLSearchParams({
      grant_type: 'refresh_token',
      client_id: process.env.AMAZON_CLIENT_ID,
      client_secret: process.env.AMAZON_CLIENT_SECRET,
      refresh_token: refreshToken
    })
  });

  const data = await response.json();

  return {
    access_token: data.access_token,
    refresh_token: data.refresh_token, // Always save the new refresh token
    expires_in: data.expires_in
  };
};

// Middleware to ensure valid token
const ensureValidToken = async (sellerId) => {
  const seller = await db.sellers.findById(sellerId);

  // Check if token expires within 5 minutes
  if (seller.amazon_token_expires < Date.now() + 300000) {
    const newTokens = await refreshLwaToken(seller.amazon_lwa_refresh_token);

    await db.sellers.update(sellerId, {
      amazon_lwa_access_token: newTokens.access_token,
      amazon_lwa_refresh_token: newTokens.refresh_token,
      amazon_token_expires: Date.now() + (newTokens.expires_in * 1000)
    });

    return newTokens.access_token;
  }

  return seller.amazon_lwa_access_token;
};

การลงนามคำขอ AWS SigV4

ทำความเข้าใจ SigV4

คำขอ SP-API ทั้งหมดต้องมีการลงนาม AWS Signature Version 4 (SigV4) ซึ่งช่วยให้มั่นใจได้ถึงความถูกต้องและความสมบูรณ์ของคำขอ

กระบวนการลงนาม SigV4

const crypto = require('crypto');

class SigV4Signer {
  constructor(accessKey, secretKey, region, service = 'execute-api') {
    this.accessKey = accessKey;
    this.secretKey = secretKey;
    this.region = region;
    this.service = service;
  }

  sign(method, url, body = '', headers = {}) {
    const parsedUrl = new URL(url);
    const now = new Date();

    // Step 1: Create canonical request
    const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, '');
    const dateStamp = amzDate.slice(0, 8);

    headers['host'] = parsedUrl.host;
    headers['x-amz-date'] = amzDate;
    headers['x-amz-access-token'] = this.accessToken;
    headers['content-type'] = 'application/json';

    const canonicalHeaders = Object.entries(headers)
      .sort(([a], [b]) => a.localeCompare(b))
      .map(([k, v]) => `${k.toLowerCase()}:${v.trim()}`)
      .join('\n');

    const signedHeaders = Object.keys(headers)
      .sort()
      .map(k => k.toLowerCase())
      .join(';');

    const payloadHash = crypto.createHash('sha256').update(body).digest('hex');

    const canonicalRequest = [
      method.toUpperCase(),
      parsedUrl.pathname,
      parsedUrl.search.slice(1),
      canonicalHeaders,
      '',
      signedHeaders,
      payloadHash
    ].join('\n');

    // Step 2: Create string to sign
    const algorithm = 'AWS4-HMAC-SHA256';
    const credentialScope = `${dateStamp}/${this.region}/${this.service}/aws4_request`;

    const stringToSign = [
      algorithm,
      amzDate,
      credentialScope,
      crypto.createHash('sha256').update(canonicalRequest).digest('hex')
    ].join('\n');

    // Step 3: Calculate signature
    const kDate = this.hmac(`AWS4${this.secretKey}`, dateStamp);
    const kRegion = this.hmac(kDate, this.region);
    const kService = this.hmac(kRegion, this.service);
    const kSigning = this.hmac(kService, 'aws4_request');
    const signature = this.hmac(kSigning, stringToSign, 'hex');

    // Step 4: Add authorization header
    const authorization = `${algorithm} Credential=${this.accessKey}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;

    return {
      headers: {
        ...headers,
        'Authorization': authorization
      },
      canonicalRequest,
      stringToSign,
      signature
    };
  }

  hmac(key, data, encoding = 'buffer') {
    return crypto.createHmac('sha256', key).update(data).digest(encoding);
  }
}

// Usage
const signer = new SigV4Signer(
  process.env.AWS_ACCESS_KEY_ID,
  process.env.AWS_SECRET_ACCESS_KEY,
  'us-east-1'
);

const signedRequest = signer.sign('GET', 'https://sellingpartnerapi-na.amazon.com/orders/v0/orders', '', {
  'x-amz-access-token': accessToken
});

การใช้ AWS SDK สำหรับ SigV4

ทำให้การลงนามง่ายขึ้นด้วย AWS SDK:

const { SignatureV4 } = require('@aws-sdk/signature-v4');
const { Sha256 } = require('@aws-crypto/sha256-js');

const signer = new SignatureV4({
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
  },
  region: 'us-east-1',
  service: 'execute-api',
  sha256: Sha256
});

const makeSpApiRequest = async (method, endpoint, accessToken, body = null) => {
  const url = new URL(endpoint);

  const headers = {
    'host': url.host,
    'content-type': 'application/json',
    'x-amz-access-token': accessToken,
    'x-amz-date': new Date().toISOString().replace(/[:-]|\.\d{3}/g, '')
  };

  const signedRequest = await signer.sign({
    method,
    hostname: url.hostname,
    path: url.pathname,
    query: Object.fromEntries(url.searchParams),
    headers,
    body: body ? JSON.stringify(body) : undefined
  });

  const response = await fetch(endpoint, {
    method,
    headers: signedRequest.headers,
    body: signedRequest.body
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`SP-API Error: ${error.errors?.[0]?.message || response.statusText}`);
  }

  return response.json();
};

Orders API

การดึงข้อมูลคำสั่งซื้อ

ดึงข้อมูลคำสั่งซื้อพร้อมตัวเลือกการกรอง:

const getOrders = async (accessToken, options = {}) => {
  const params = new URLSearchParams({
    createdAfter: options.createdAfter, // ISO 8601 format
    createdBefore: options.createdBefore,
    orderStatuses: options.orderStatuses?.join(',') || '',
    marketplaceIds: options.marketplaceIds?.join(',') || ['ATVPDKIKX0DER'], // US
    maxResultsPerPage: options.maxResultsPerPage || 100
  });

  // Remove empty params
  for (const [key, value] of params.entries()) {
    if (!value) params.delete(key);
  }

  const endpoint = `https://sellingpartnerapi-na.amazon.com/orders/v0/orders?${params.toString()}`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Usage example
const orders = await getOrders(accessToken, {
  createdAfter: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), // Last 24 hours
  orderStatuses: ['Unshipped', 'PartiallyShipped'],
  marketplaceIds: ['ATVPDKIKX0DER'] // US marketplace
});

โครงสร้างการตอบกลับของคำสั่งซื้อ

{
  "payload": {
    "orders": [
      {
        "amazon_order_id": "112-1234567-1234567",
        "seller_order_id": "ORDER-001",
        "purchase_date": "2026-03-19T10:30:00Z",
        "last_update_date": "2026-03-19T14:45:00Z",
        "order_status": "Unshipped",
        "fulfillment_channel": "AFN", // AFN = FBA, MFN = Merchant
        "sales_channel": "Amazon.com",
        "order_channel": "Amazon.com",
        "ship_service_level": "Std US D2D Dom",
        "order_total": {
          "currency_code": "USD",
          "amount": "89.99"
        },
        "number_of_items_shipped": 0,
        "number_of_items_unshipped": 2,
        "payment_execution_detail": [],
        "payment_method": "CreditCard",
        "payment_method_details": ["CreditCard"],
        "marketplace_id": "ATVPDKIKX0DER",
        "shipment_service_level_category": "Standard",
        "easy_ship_shipment_status": null,
        "is_business_order": false,
        "is_prime": true,
        "is_premium_order": false,
        "is_global_express_enabled": false
      }
    ],
    "next_token": "eyJleHBpcmF0aW9uVGltZU9mTmV4dFRva2VuIjoxNzEwOTUwNDAwfQ=="
  }
}

การดึงข้อมูลรายการสินค้าในคำสั่งซื้อ

ดึงข้อมูลรายการสินค้า (line items) โดยละเอียดสำหรับคำสั่งซื้อ:

const getOrderItems = async (accessToken, orderId) => {
  const endpoint = `https://sellingpartnerapi-na.amazon.com/orders/v0/orders/${orderId}/orderItems`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Usage
const orderItems = await getOrderItems(accessToken, '112-1234567-1234567');

// Expected response
{
  "payload": {
    "order_items": [
      {
        "asin": "B08N5WRWNW",
        "seller_sku": "MYSKU-001",
        "title": "Wireless Bluetooth Headphones",
        "quantity_ordered": 2,
        "quantity_shipped": 0,
        "product_info": {
          "number_of_items": 2
        },
        "item_price": {
          "currency_code": "USD",
          "amount": "44.99"
        },
        "item_total": {
          "currency_code": "USD",
          "amount": "89.98"
        },
        "tax_collection": {
          "tax_collection_model": "MarketplaceFacilitator",
          "responsible_party": "Amazon Services, Inc."
        }
      }
    ]
  }
}

การอัปเดตสถานะการจัดส่ง

ระบุสถานะคำสั่งซื้อว่าจัดส่งแล้วพร้อมข้อมูลการติดตาม:

const confirmShipment = async (accessToken, orderId, shipmentData) => {
  const endpoint = `https://sellingpartnerapi-na.amazon.com/orders/v0/orders/${orderId}/shipmentConfirmation`;

  const payload = {
    packageDetails: {
      packageReferenceId: shipmentData.packageReferenceId || '1',
      carrier_code: shipmentData.carrierCode, // e.g., 'USPS', 'FEDEX', 'UPS'
      tracking_number: shipmentData.trackingNumber,
      ship_date: shipmentData.shipDate || new Date().toISOString(),
      items: shipmentData.items.map(item => ({
        order_item_id: item.orderItemId,
        quantity: item.quantity
      }))
    }
  };

  return makeSpApiRequest('POST', endpoint, accessToken, payload);
};

// Usage
await confirmShipment(accessToken, '112-1234567-1234567', {
  carrierCode: 'USPS',
  trackingNumber: '9400111899223456789012',
  items: [
    { orderItemId: '12345678901234', quantity: 2 }
  ]
});

รหัสผู้ให้บริการขนส่งทั่วไป

ผู้ให้บริการขนส่ง รหัสผู้ให้บริการขนส่ง
USPS USPS
FedEx FEDEX
UPS UPS
DHL DHL
Canada Post CANADA_POST
Royal Mail ROYAL_MAIL
Australia Post AUSTRALIA_POST
Amazon Logistics AMZN_UK

Inventory API

การดึงข้อมูลสรุปสินค้าคงคลัง

ดึงข้อมูลระดับสินค้าคงคลังในตลาดต่างๆ:

const getInventorySummaries = async (accessToken, options = {}) => {
  const params = new URLSearchParams({
    granularityType: options.granularityType || 'Marketplace',
    granularityId: options.granularityId || 'ATVPDKIKX0DER', // US
    startDateTime: options.startDateTime || '',
    sellerSkus: options.sellerSkus?.join(',') || ''
  });

  const endpoint = `https://sellingpartnerapi-na.amazon.com/fba/inventory/v1/summaries?${params.toString()}`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Usage
const inventory = await getInventorySummaries(accessToken, {
  granularityId: 'ATVPDKIKX0DER',
  sellerSkus: ['MYSKU-001', 'MYSKU-002']
});

โครงสร้างการตอบกลับของสินค้าคงคลัง

{
  "payload": {
    "inventorySummaries": [
      {
        "asin": "B08N5WRWNW",
        "seller_sku": "MYSKU-001",
        "condition": "NewItem",
        "details": {
          "quantity": 150,
          "fulfillable_quantity": 145,
          "inbound_working_quantity": 0,
          "inbound_shipped_quantity": 5,
          "inbound_receiving_quantity": 0,
          "reserved_quantity": 5,
          "unfulfillable_quantity": 0,
          "warehouse_damage_quantity": 0,
          "distributor_damaged_quantity": 0,
          "carrier_damaged_quantity": 0,
          "defective_quantity": 0,
          "customer_damaged_quantity": 0
        },
        "marketplace_id": "ATVPDKIKX0DER"
      }
    ]
  }
}

การอัปเดตสินค้าคงคลัง

หมายเหตุ: SP-API ไม่มีจุดสิ้นสุดการอัปเดตสินค้าคงคลังโดยตรง การจัดการสินค้าคงคลังทำได้โดย:

  1. การจัดส่ง FBA - ส่งสินค้าคงคลังไปยังคลังสินค้าของ Amazon
  2. คำสั่งซื้อ MFN - สินค้าคงคลังจะลดลงโดยอัตโนมัติเมื่อมีการจัดส่งคำสั่งซื้อ
  3. การอัปเดตรายการสินค้า - ปรับปริมาณผ่าน Listings API

สำหรับ FBA ให้สร้างแผนการจัดส่ง:

const createInboundShipmentPlan = async (accessToken, shipmentData) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/fba/inbound/v0/plans';

  const payload = {
    ShipFromAddress: {
      Name: shipmentData.shipFromName,
      AddressLine1: shipmentData.shipFromAddress,
      City: shipmentData.shipFromCity,
      StateOrProvinceCode: shipmentData.shipFromState,
      CountryCode: shipmentData.shipFromCountry,
      PostalCode: shipmentData.shipFromPostalCode
    },
    LabelPrepPreference: 'SELLER_LABEL',
    InboundPlanItems: shipmentData.items.map(item => ({
      SellerSKU: item.sku,
      ASIN: item.asin,
      Quantity: item.quantity,
      Condition: 'NewItem'
    }))
  };

  return makeSpApiRequest('POST', endpoint, accessToken, payload);
};

Listings API

การดึงข้อมูลรายการสินค้า

ดึงข้อมูลรายการสินค้าพร้อมตัวเลือกการกรอง:

const getListings = async (accessToken, options = {}) => {
  const params = new URLSearchParams({
    marketplaceIds: options.marketplaceIds?.join(',') || ['ATVPDKIKX0DER'],
    itemTypes: options.itemTypes?.join(',') || ['ASIN', 'SKU'],
    identifiers: options.identifiers?.join(',') || '',
    issuesLocale: options.locale || 'en_US'
  });

  const endpoint = `https://sellingpartnerapi-na.amazon.com/listings/2021-08-01/items?${params.toString()}`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Usage
const listings = await getListings(accessToken, {
  identifiers: ['B08N5WRWNW', 'B09JQKJXYZ'],
  itemTypes: ['ASIN']
});

โครงสร้างการตอบกลับของรายการสินค้า

{
  "identifiers": {
    "marketplaceId": "ATVPDKIKX0DER",
    "sku": "MYSKU-001",
    "asin": "B08N5WRWNW"
  },
  "attributes": {
    "title": "Wireless Bluetooth Headphones",
    "description": "Premium wireless headphones with noise cancellation",
    "brand": "MyBrand",
    "color": "Black",
    "size": "One Size",
    "item_weight": "0.5 pounds",
    "product_dimensions": "7 x 6 x 3 inches"
  },
  "product_type": "LUGGAGE",
  "sales_price": {
    "currency_code": "USD",
    "amount": "89.99"
  },
  "list_price": {
    "currency_code": "USD",
    "amount": "129.99"
  },
  "fulfillment_availability": [
    {
      "fulfillment_channel_code": "AFN",
      "quantity": 150
    }
  ],
  "condition_type": "New",
  "status": "ACTIVE",
  "procurement": null
}

การสร้างหรืออัปเดตรายการสินค้า

ใช้ submitListingsSubmission สำหรับการดำเนินการแบบกลุ่ม:

const submitListingUpdate = async (accessToken, listingData) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/listings/2021-08-01/items/MYSKU-001';

  const payload = {
    productType: 'LUGGAGE',
    patches: [
      {
        op: 'replace',
        path: '/attributes/title',
        value: 'Updated Wireless Bluetooth Headphones - Premium Sound'
      },
      {
        op: 'replace',
        path: '/salesPrice',
        value: {
          currencyCode: 'USD',
          amount: '79.99'
        }
      }
    ]
  };

  return makeSpApiRequest('PATCH', endpoint, accessToken, payload);
};

การลบรายการสินค้า

ลบหรือปิดใช้งานรายการสินค้า:

const deleteListing = async (accessToken, sku, marketplaceIds) => {
  const params = new URLSearchParams({
    marketplaceIds: marketplaceIds.join(',')
  });

  const endpoint = `https://sellingpartnerapi-na.amazon.com/listings/2021-08-01/items/${sku}?${params.toString()}`;

  return makeSpApiRequest('DELETE', endpoint, accessToken);
};

Reports API

การสร้างกำหนดการรายงาน

ทำให้การสร้างรายงานเป็นไปโดยอัตโนมัติ:

const createReport = async (accessToken, reportType, dateRange) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/reports/2021-06-30/reports';

  const payload = {
    reportType: reportType,
    marketplaceIds: dateRange.marketplaceIds || ['ATVPDKIKX0DER'],
    dataStartTime: dateRange.startTime?.toISOString(),
    dataEndTime: dateRange.endTime?.toISOString()
  };

  return makeSpApiRequest('POST', endpoint, accessToken, payload);
};

// Common report types
const REPORT_TYPES = {
  ORDERS: 'GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_GENERAL',
  ORDER_ITEMS: 'GET_FLAT_FILE_ORDER_ITEMS_DATA_BY_LAST_UPDATE_GENERAL',
  INVENTORY: 'GET_MERCHANT_LISTINGS_ALL_DATA',
  FBA_INVENTORY: 'GET_FBA_MYI_UNSUPPRESSED_INVENTORY_DATA',
  SETTLEMENT: 'GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE',
  SALES_AND_TRAFFIC: 'GET_SALES_AND_TRAFFIC_REPORT',
  ADVERTISING: 'GET_BRAND_ANALYTICS_SEARCH_TERMS_REPORT'
};

// Usage
const report = await createReport(accessToken, REPORT_TYPES.ORDERS, {
  marketplaceIds: ['ATVPDKIKX0DER'],
  startTime: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // Last 7 days
  endTime: new Date()
});

การดึงเอกสารรายงาน

ดาวน์โหลดรายงานที่สร้างขึ้น:

const getReportDocument = async (accessToken, reportId) => {
  const endpoint = `https://sellingpartnerapi-na.amazon.com/reports/2021-06-30/reports/${reportId}/document`;

  return makeSpApiRequest('GET', endpoint, accessToken);
};

// Download and parse report
const downloadReport = async (accessToken, reportId) => {
  const documentInfo = await getReportDocument(accessToken, reportId);

  const response = await fetch(documentInfo.payload.url);
  const content = await response.text();

  // Reports are typically tab-delimited or JSON
  if (documentInfo.payload.compressionAlgorithm === 'GZIP') {
    const decompressed = await decompressGzip(content);
    return decompressed;
  }

  return content;
};

Notifications API

การสร้างการสมัครสมาชิก

ตั้งค่า webhooks สำหรับเหตุการณ์แบบเรียลไทม์:

const createSubscription = async (accessToken, subscriptionData) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/notifications/v1/subscriptions';

  const payload = {
    payload: {
      destination: {
        resource: subscriptionData.destinationArn, // SNS topic ARN
        name: subscriptionData.name
      },
      modelVersion: '1.0',
      eventFilter: {
        eventCode: subscriptionData.eventCode,
        marketplaceIds: subscriptionData.marketplaceIds
      }
    }
  };

  return makeSpApiRequest('POST', endpoint, accessToken, payload);
};

// Available event types
const EVENT_CODES = {
  ORDER_STATUS_CHANGE: 'OrderStatusChange',
  ORDER_ITEM_CHANGE: 'OrderItemChange',
  ORDER_CHANGE: 'OrderChange',
  FBA_ORDER_STATUS_CHANGE: 'FBAOrderStatusChange',
  FBA_OUTBOUND_SHIPMENT_STATUS: 'FBAOutboundShipmentStatus',
  INVENTORY_LEVELS: 'InventoryLevels',
  PRICING_HEALTH: 'PricingHealth'
};

// Usage
await createSubscription(accessToken, {
  destinationArn: 'arn:aws:sns:us-east-1:123456789012:sp-api-notifications',
  name: 'OrderStatusNotifications',
  eventCode: EVENT_CODES.ORDER_STATUS_CHANGE,
  marketplaceIds: ['ATVPDKIKX0DER']
});

การตั้งค่าปลายทาง SNS

Amazon ส่งการแจ้งเตือนไปยัง SNS topics:

const createSnsDestination = async (accessToken, destinationData) => {
  const endpoint = 'https://sellingpartnerapi-na.amazon.com/notifications/v1/destinations';

  const payload = {
    resource: destinationData.snsTopicArn,
    name: destinationData.name
  };

  return makeSpApiRequest('POST', endpoint, accessToken, { payload });
};

// SNS topic policy must allow Amazon SES to publish
const snsTopicPolicy = {
  Version: '2012-10-17',
  Statement: [
    {
      Effect: 'Allow',
      Principal: {
        Service: 'notifications.amazon.com'
      },
      Action: 'SNS:Publish',
      Resource: 'arn:aws:sns:us-east-1:123456789012:sp-api-notifications'
    }
  ]
};

การประมวลผลการแจ้งเตือน

ตั้งค่า SNS endpoint เพื่อรับการแจ้งเตือน:

const express = require('express');
const crypto = require('crypto');
const app = express();

app.post('/webhooks/amazon', express.raw({ type: 'application/json' }), async (req, res) => {
  const signature = req.headers['x-amz-sns-message-signature'];
  const payload = req.body;

  // Verify SNS message signature
  const isValid = await verifySnsSignature(payload, signature);

  if (!isValid) {
    console.error('Invalid SNS signature');
    return res.status(401).send('Unauthorized');
  }

  const message = JSON.parse(payload.toString());

  // Handle different message types
  switch (message.Type) {
    case 'SubscriptionConfirmation':
      // Auto-confirm subscription
      await fetch(message.SubscribeURL);
      break;
    case 'Notification':
      const notification = JSON.parse(message.Message);
      await handleSpApiNotification(notification);
      break;
  }

  res.status(200).send('OK');
});

async function handleSpApiNotification(notification) {
  const { notificationType, payload } = notification;

  switch (notificationType) {
    case 'OrderStatusChange':
      await syncOrderStatus(payload.amazonOrderId);
      break;
    case 'OrderChange':
      await syncOrderDetails(payload.amazonOrderId);
      break;
    case 'InventoryLevels':
      await updateInventoryCache(payload);
      break;
  }
}

การจำกัดอัตราและโควตา

ทำความเข้าใจข้อจำกัดอัตรา

SP-API บังคับใช้ข้อจำกัดอัตราแบบไดนามิกต่อจุดสิ้นสุด:

หมวดหมู่จุดสิ้นสุด ข้อจำกัดอัตรา ขีดจำกัดสูงสุดชั่วคราว
Orders 10 requests/second 20
Order Items 5 requests/second 10
Inventory 2 requests/second 5
Listings 10 requests/second 20
Reports 0.5 requests/second 1
Notifications 1 request/second 2
FBA Inbound 2 requests/second 5

ตรวจสอบเฮดเดอร์ x-amzn-RateLimit-Limit ในการตอบกลับสำหรับข้อจำกัดปัจจุบัน

การนำการจัดการข้อจำกัดอัตราไปใช้

ใช้ exponential backoff สำหรับการลองใหม่:

const makeRateLimitedRequest = async (method, endpoint, accessToken, body = null, maxRetries = 5) => {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await makeSpApiRequest(method, endpoint, accessToken, body);

      // Check rate limit headers
      const rateLimit = response.headers.get('x-amzn-RateLimit-Limit');
      const retryAfter = response.headers.get('Retry-After');

      if (retryAfter) {
        console.warn(`Rate limited. Retry after: ${retryAfter} seconds`);
      }

      return response;
    } catch (error) {
      if (error.message.includes('429') && attempt < maxRetries) {
        // Extract retry-after header or use exponential backoff
        const retryAfter = error.headers?.get('Retry-After') || Math.pow(2, attempt);
        console.log(`Rate limited. Retrying in ${retryAfter}s...`);
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      } else if (error.message.includes('503') && attempt < maxRetries) {
        // Service unavailable - exponential backoff
        const delay = Math.pow(2, attempt) * 1000;
        console.log(`Service unavailable. Retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
};

การนำการจัดคิวคำขอไปใช้

ใช้คิวเพื่อให้อยู่ภายใต้ข้อจำกัด:

class RateLimitedQueue {
  constructor(rateLimit, burstLimit = null) {
    this.rateLimit = rateLimit; // requests per second
    this.burstLimit = burstLimit || rateLimit * 2;
    this.tokens = this.burstLimit;
    this.lastRefill = Date.now();
    this.queue = [];
    this.processing = false;
  }

  async add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.process();
    });
  }

  refillTokens() {
    const now = Date.now();
    const elapsed = (now - this.lastRefill) / 1000;
    const tokensToAdd = elapsed * this.rateLimit;

    this.tokens = Math.min(this.burstLimit, this.tokens + tokensToAdd);
    this.lastRefill = now;
  }

  async process() {
    if (this.processing || this.queue.length === 0) return;

    this.processing = true;

    while (this.queue.length > 0) {
      this.refillTokens();

      if (this.tokens < 1) {
        const waitTime = (1 / this.rateLimit) * 1000;
        await new Promise(r => setTimeout(r, waitTime));
        continue;
      }

      this.tokens--;
      const { requestFn, resolve, reject } = this.queue.shift();

      try {
        const result = await requestFn();
        resolve(result);
      } catch (error) {
        reject(error);
      }
    }

    this.processing = false;
  }
}

// Usage - Orders API queue (10 req/s)
const ordersQueue = new RateLimitedQueue(10, 20);
const orders = await ordersQueue.add(() => getOrders(accessToken, options));

แนวทางปฏิบัติที่ดีที่สุดด้านความปลอดภัย

การจัดการข้อมูลรับรอง

ห้ามเขียนข้อมูลรับรองลงในโค้ดต้นฉบับโดยตรง (hardcode) โดยเด็ดขาด ใช้ตัวแปรสภาพแวดล้อมหรือ Secrets Manager แทน:

// Bad - never do this
const AWS_ACCESS_KEY = 'AKIAIOSFODNN7EXAMPLE';
const AWS_SECRET = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY';

// Good - use environment variables
const AWS_ACCESS_KEY = process.env.AWS_ACCESS_KEY_ID;
const AWS_SECRET = process.env.AWS_SECRET_ACCESS_KEY;

// Better - use AWS Secrets Manager or similar
const { SecretsManagerClient } = require('@aws-sdk/client-secrets-manager');
const secretsClient = new SecretsManagerClient({ region: 'us-east-1' });

const getCredentials = async () => {
  const response = await secretsClient.send({
    Name: 'prod/sp-api/credentials'
  });
  return JSON.parse(response.SecretString);
};

ข้อกำหนดในการจัดเก็บ Token

SP-API กำหนดมาตรการความปลอดภัยเฉพาะสำหรับการจัดเก็บ token:

ฝึกการออกแบบ API แบบ Design-first ใน Apidog

ค้นพบวิธีที่ง่ายขึ้นในการสร้างและใช้ API