DEV Community

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

Posted on • Originally published at apidog.com

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

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

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

ทดลองใช้ Apidog วันนี้

บทนำ

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, สร้างการตอบกลับจำลอง และแบ่งปันสถานการณ์การทดสอบกับทีมของคุณ

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

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

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

  • การดึงข้อมูลคำสั่งซื้อและการอัปเดตสถานะ
  • การจัดการสินค้าคงคลังในตลาดต่างๆ
  • การสร้าง อัปเดต และลบรายการสินค้า
  • การจัดการการจัดส่ง FBA (Fulfillment by Amazon)
  • การกำหนดราคาสินค้าและการวิเคราะห์คู่แข่ง
  • การสร้างรายงานและการวิเคราะห์
  • การจัดการ A+ Content
  • ข้อมูลการวิเคราะห์แบรนด์และโฆษณา

การเปรียบเทียบ 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)
Enter fullscreen mode Exit fullscreen mode

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

  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. กรอกรายละเอียดแอปพลิเคชัน:
  • ชื่อแอปพลิเคชัน: ชื่อที่ชัดเจนและสื่อความหมาย
  • ประเภทแอปพลิเคชัน: เลือก “Self-developed” หรือ “Third-party”
  • กรณีการใช้งาน: อธิบายวัตถุประสงค์ของการผสานรวมของคุณ
  • Redirect URI: URL HTTPS สำหรับการเรียกกลับ OAuth

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

  • Application ID
  • Client ID
  • Client Secret

ข้อควรระวังด้านความปลอดภัย:

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

# .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"
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 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 สำหรับภูมิภาคของคุณ:
  • อเมริกาเหนือ: 906394416454
  • ยุโรป: 336853085554
  • ตะวันออกไกล: 774466381866

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

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

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

ตั้งชื่อ role เช่น SellingPartnerApiRole และจด ARN ไว้

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

  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)
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 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}`);
Enter fullscreen mode Exit fullscreen mode

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,
    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');
  }
});
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 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;
};
Enter fullscreen mode Exit fullscreen mode

ขั้นตอนที่ 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;
};
Enter fullscreen mode Exit fullscreen mode

การลงนามคำขอ 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
});
Enter fullscreen mode Exit fullscreen mode

การใช้ 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();
};
Enter fullscreen mode Exit fullscreen mode

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
});
Enter fullscreen mode Exit fullscreen mode

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

{
  "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",
        "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=="
  }
}
Enter fullscreen mode Exit fullscreen mode

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

ดึงข้อมูลรายการสินค้า (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');
Enter fullscreen mode Exit fullscreen mode

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

ส่งสถานะการจัดส่งด้วยข้อมูล tracking:

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,
      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 }
  ]
});
Enter fullscreen mode Exit fullscreen mode

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

ผู้ให้บริการขนส่ง รหัสผู้ให้บริการขนส่ง
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',
    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']
});
Enter fullscreen mode Exit fullscreen mode

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

{
  "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"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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

การจัดการสินค้าคงคลังทำได้ผ่าน:

  1. การจัดส่ง FBA – ส่งสินค้าคงคลังไปยังคลัง Amazon
  2. คำสั่งซื้อ MFN – ระบบจะตัด stock อัตโนมัติเมื่อจัดส่ง
  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);
};
Enter fullscreen mode Exit fullscreen mode

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']
});
Enter fullscreen mode Exit fullscreen mode

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

{
  "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
}
Enter fullscreen mode Exit fullscreen mode

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

ใช้ 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);
};
Enter fullscreen mode Exit fullscreen mode

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

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

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);
};
Enter fullscreen mode Exit fullscreen mode

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),
  endTime: new Date()
});
Enter fullscreen mode Exit fullscreen mode

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

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

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;
};
Enter fullscreen mode Exit fullscreen mode

Notifications API

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

ตั้งค่า webhooks สำหรับเหตุการณ์แบบ real-time:

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']
});
Enter fullscreen mode Exit fullscreen mode

การตั้งค่าปลายทาง 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'
    }
  ]
};
Enter fullscreen mode Exit fullscreen mode

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

ตั้งค่า 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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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

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

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

หมวดหมู่จุดสิ้นสุด ข้อจำกัดอัตรา ขีดจำกัดสูงสุดชั่วคราว
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

ตรวจสอบ header x-amzn-RateLimit-Limit ใน response สำหรับข้อจำกัดปัจจุบัน

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

ใช้ exponential backoff สำหรับ retry:

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;
      }
    }
  }
};
Enter fullscreen mode Exit fullscreen mode

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

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

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));
Enter fullscreen mode Exit fullscreen mode

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

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

ห้าม hardcode ข้อมูลรับรอง ใช้ environment variables หรือ 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);
};
Enter fullscreen mode Exit fullscreen mode

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

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

  1. จัดเก็บ refresh token และ access token ใน encrypted storage (เช่น database ที่เข้ารหัสหรือ Secrets Manager)
  2. ไม่ควร log หรือ expose token ใน response หรือระบบ monitoring
  3. จำกัดการเข้าถึง token เฉพาะ service ที่จำเป็น
  4. หมุนเวียน token ทันทีเมื่อพบ compromise หรือหมดอายุ

อ่านรายละเอียดเพิ่มเติมและดูเอกสารฉบับเต็มได้ที่ Amazon SP-API Documentation

ทดลองและทดสอบ SP-API endpoints ได้ง่ายขึ้นด้วย Apidog

Top comments (0)