การติดตั้งใช้งานวงจรคำสั่งซื้อ

คู่มือนี้มีข้อมูลอ้างอิงทางเทคนิคของ API และสคีมาเพย์โหลดสำหรับการพุชข้อมูลอัปเดตสถานะคำสั่งซื้อทั้งหมด เหตุการณ์การดำเนินการตามคำสั่งซื้อ และการปรับไปยัง Google โดยใช้เว็บฮุคสำหรับ Universal Commerce Protocol (UCP) เวอร์ชัน 2026-04-08

ก่อนสร้างปลายทาง โปรดตรวจสอบว่าคุณได้อ่านภาพรวม ของวงจรชีวิตของคำสั่งซื้อเพื่อทำความเข้าใจแนวคิดระดับสูง เหตุการณ์ที่ต้องมี และรายละเอียดปลายทางของเว็บฮุคแล้ว

การตรวจสอบสิทธิ์และการลงนามคำขอ

การเปลี่ยนแปลงที่สำคัญในเวอร์ชัน 2026-04-08 ได้แก่ การเปิดตัวส่วนหัวของเว็บฮุคใหม่ที่ต้องมีและขั้นตอนการลงนามคำขอที่เฉพาะเจาะจง

ส่วนหัวของเว็บฮุคที่ต้องมี

ส่วนหัว HTTP ต่อไปนี้เป็นส่วนหัวที่ต้องมีสำหรับคำขอเว็บฮุคทั้งหมด

  • Webhook-Id: ตัวระบุที่ไม่ซ้ำกันสำหรับเหตุการณ์เว็บฮุคนี้ รหัสนี้ควรตรงกับ id ของเหตุการณ์หลักที่ส่ง (เช่น รหัสเหตุการณ์การดำเนินการตามคำสั่งซื้อหรือรหัสเหตุการณ์การปรับ)
  • Webhook-Timestamp: การประทับเวลาที่ระบุเวลาที่เกิดเหตุการณ์

ส่วนหัวเหล่านี้จะแทนที่ช่อง id และ created_time ที่คาดไว้ก่อนหน้านี้ในเพย์โหลดคำสั่งซื้อ

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

  1. คำนวณไดเจสต์ SHA-256 ของเนื้อหาคำขอแบบดิบและตั้งค่าส่วนหัว Content-Digest
  2. เลือกคีย์การลงนามจาก signing_keys ในโปรไฟล์ UCP
  3. สร้างฐานลายเซ็นตาม RFC 9421
    • ดูข้อมูลจำเพาะสำหรับคอมโพเนนต์ที่มีการลงนาม
  4. ตั้งค่าส่วนหัว UCP-Agent, Signature-Input และ Signature
    • UCP-Agent คือลิงก์ไปยังโปรไฟล์ UCP ในรูปแบบ profile="https://merchant.example.com/.well-known/ucp"
    • Signature-Input คือฟิลด์ที่มีโครงสร้างแบบพจนานุกรมที่อธิบายคอมโพเนนต์ที่อยู่ในลายเซ็น รวมถึง keyid ที่ใช้ในการลงนาม ซึ่งต้องตรงกับ kid ของคีย์การลงนามที่เลือกจาก signing_keys ในโปรไฟล์ UCP
    • ส่วนหัว Signature มีฐานลายเซ็นที่ลงนามโดยใช้คีย์ส่วนตัวแล้วเข้ารหัส Base64

ดูข้อมูลเพิ่มเติมได้ที่วิธีการลงนามใน ucp.dev

เหตุการณ์คำสั่งซื้อที่สร้างขึ้น

  • ทริกเกอร์: ทันทีหลังจากยืนยันคำสั่งซื้อ (status: processing)

การเปลี่ยนแปลงที่สำคัญในเวอร์ชันนี้:

  • ตอนนี้ต้องระบุช่อง currency ที่ระดับบนสุดของออบเจ็กต์คำสั่งซื้อ
  • ตอนนี้ช่อง type ภายในแต่ละออบเจ็กต์ในอาร์เรย์ totals เป็นสตริงแบบเปิด (เช่น "subtotal", "tax", "fee", "total")
  • ส่วนหัว Webhook-Id ที่ต้องมีมีตัวระบุที่ไม่ซ้ำกันสำหรับเหตุการณ์เว็บฮุค โปรดทราบว่ายังคงต้องระบุช่อง id ในเพย์โหลดที่มีรหัสการยืนยันคำสั่งซื้อ
  • ส่วนหัว Webhook-Timestamp ที่ต้องมีจะระบุเวลาที่สร้างขึ้น ซึ่งจะแทนที่ช่อง created_time เดิมในเพย์โหลด

ตัวอย่าง: ตัวอย่างนี้แสดงคำสั่งซื้อที่สร้างขึ้นหลังจากผู้ซื้อชำระเงินเสร็จสมบูรณ์

ส่วนหัวที่ต้องมี:

  • Webhook-Id: order_01
  • Webhook-Timestamp: 2026-03-23T19:00:00Z
{
  "ucp": {
    "version": "2026-04-08",
    "capabilities": {
      "dev.ucp.shopping.order": [{"version": "2026-04-08"}]
    }
  },
  "id": "order_01",
  "checkout_id": "checkout_01",
  "currency": "USD",
   // Always include all line items, even for single-item checkouts. This ensures any add-ons, gifts, or separate charges are accounted for.
  "line_items": [
    {
      "id": "line_1",
      "item":
        {
          "id": "product_123",
          "title": "Running Shoes",
          "price": 10000
        },
      "quantity": { "total": 1, "fulfilled": 0 },
      "totals": [
        {"type": "subtotal", "display_text": null, "amount": 10000},
        {"type": "total", "display_text": null, "amount": 10000}
      ],
      // The status of a line item must match total and fulfilled quantities (e.g., total 1, fulfilled 0 -> status: processing).
      "status": "processing"
    }
  ],
  "totals": [
    {"type": "subtotal", "display_text": "Subtotal", "amount": 10000}, // Tax-inclusive markets: Set display_text to "Subtotal (including taxes)". Amount must include tax.
    {"type": "fee", "display_text": "Service Fee", "amount": 100},
    {"type": "tax", "display_text": "Tax", "amount": 800}, // Tax-inclusive markets: Omit this entry.
    {"type": "total", "display_text": "Total", "amount": 10900}
  ],
  "fulfillment": {
    "expectations": [
      {
        "id": "exp_1",
        "line_items": [{ "id": "line_1", "quantity": 1 }],
        "method_type": "shipping",
        "destination": {
          "first_name": "Alice",
          "last_name": "Example",
          "street_address": "123 Main St",
          "address_locality": "Austin",
          "address_region": "TX",
          "address_country": "US",
          "postal_code": "78701"
        },
        "description": "Arrives in 2-3 business days",  // Maximum length: 200 characters.
        "fulfillable_on": "now"
      }
    ]
  },
  "permalink_url": "https://merchant.example.com/orders/789"
}

เหตุการณ์การดำเนินการตามคำสั่งซื้อ

ระบบจะส่งเหตุการณ์เหล่านี้เป็นส่วนหนึ่งของอาร์เรย์ fulfillment.events

จัดส่งคำสั่งซื้อแล้ว

เมื่อมีการจัดส่งสินค้าในคำสั่งซื้อแล้ว ช่อง tracking_number และ tracking_url เป็นช่องที่ต้องระบุสำหรับเหตุการณ์ที่จัดส่งแล้ว เนื่องจากจำเป็นต้องใช้เพื่อจัดกลุ่มสินค้าในหน้า "คำสั่งซื้อของฉัน" อย่างถูกต้อง

นำส่งคำสั่งซื้อแล้ว

เมื่อมีการนำส่งสินค้าในคำสั่งซื้อแล้ว

ตัวอย่าง (shipped และ delivered): ตัวอย่างนี้แสดงการอัปเดตคำสั่งซื้อหลังจากมีการจัดส่งและนำส่งสินค้าแล้ว

ส่วนหัวที่ต้องมี:

  • Webhook-Id: fulfill_evt_2
  • Webhook-Timestamp: 2026-02-10T14:00:00Z
{
  "ucp": {
    "version": "2026-04-08",
    "capabilities": {
      "dev.ucp.shopping.order": [{"version": "2026-04-08"}]
    }
  },
  "id": "order_01",
  "checkout_id": "checkout_01",
  "currency": "USD",
  "line_items": [
    {
      "id": "line_1",
      "item":
        {
          "id": "product_123",
          "title": "Running Shoes",
          "price": 10000
        },
      "quantity": { "total": 1, "fulfilled": 1 },
      "totals": [
        {"type": "subtotal", "amount": 10000},
        {"type": "total", "amount": 10000}
      ],
      "status": "fulfilled"
    }
  ],
  "totals": [
    {"type": "subtotal", "amount": 10000},
    {"type": "total", "amount": 10000}
  ],
  // Updated fulfillment details
  "fulfillment": {
    "events": [
      {
        "id": "fulfill_evt_1",
        "occurred_at": "2026-02-08T10:30:00Z",
        "type": "shipped",
        "line_items": [{ "id": "line_1", "quantity": 1 }],
        "tracking_number": "123456789",
        "tracking_url": "https://fedex.com/track/123456789",
        "carrier": "FedEx",
        "description": "Shipping departed from warehouse"
      },
      {
        "id": "fulfill_evt_2",
        "occurred_at": "2026-02-10T14:00:00Z",
        "type": "delivered",
        "line_items": [{ "id": "line_1", "quantity": 1 }],
        "tracking_number": "123456789",
        "tracking_url": "https://fedex.com/track/123456789",
        "carrier": "FedEx",
        "description": "Package delivered"
      }
    ],
    "expectations": [{ "...": "..." }]
  },
  "permalink_url": "https://merchant.example.com/orders/123"
}

ตัวอย่างคำสั่งซื้อที่มีสินค้าหลายรายการ

ตัวอย่างต่อไปนี้แสดงวิธีจัดโครงสร้างการอัปเดตสำหรับคำสั่งซื้อที่มีสินค้าหลายรายการและการจัดส่งแยก ดูข้อมูลเกี่ยวกับกฎในการกำหนดสถานะแพ็กเกจได้ที่ วิธีพิจารณาสถานะแพ็กเกจ

คำสั่งซื้อที่มีสินค้าหลายรายการและการนำส่งแพ็กเกจเดียวกัน

ตัวอย่างนี้แสดงการอัปเดตคำสั่งซื้อสำหรับแพ็กเกจเดียวที่มีสินค้าหลายรายการ ระบบจะจัดกลุ่มรายการโฆษณาทั้งหมดไว้ในเหตุการณ์ shipped เดียวกันซึ่งใช้หมายเลขติดตามพัสดุเดียวกัน

ส่วนหัวที่ต้องมี:

  • Webhook-Id: fulfill_evt_1
  • Webhook-Timestamp: 2026-02-08T10:30:00Z
{
  "ucp": {
    "version": "2026-04-08",
    "capabilities": {
      "dev.ucp.shopping.order": [{"version": "2026-04-08"}]
    }
  },
  "id": "order_multi_01",
  "checkout_id": "checkout_multi_01",
  "currency": "USD",
  "line_items": [
    {
      "id": "line_1",
      "item": { "id": "product_1", "title": "Item 1", "price": 5000 },
      "quantity": { "total": 1, "fulfilled": 1 },
      "totals": [
        {"type": "subtotal", "display_text": null, "amount": 5000},
        {"type": "total", "display_text": null, "amount": 5000}
      ],
      "status": "fulfilled"
    },
    {
      "id": "line_2",
      "item": { "id": "product_2", "title": "Item 2", "price": 5000 },
      "quantity": { "total": 1, "fulfilled": 1 },
      "totals": [
        {"type": "subtotal", "display_text": null, "amount": 5000},
        {"type": "total", "display_text": null, "amount": 5000}
      ],
      "status": "fulfilled"
    }
  ],
  "totals": [
    {"type": "subtotal", "display_text": "Subtotal", "amount": 10000},
    {"type": "fee", "display_text": "Shipping", "amount": 500},
    {"type": "total", "display_text": "Total", "amount": 10500}
  ],
  "fulfillment": {
    "expectations": [
      {
        "id": "exp_1",
        "line_items": [
          { "id": "line_1", "quantity": 1 },
          { "id": "line_2", "quantity": 1 }
        ],
        "method_type": "shipping",
        "destination": {
          "first_name": "Alice",
          "last_name": "Example",
          "street_address": "123 Main St",
          "address_locality": "Austin",
          "address_region": "TX",
          "address_country": "US",
          "postal_code": "78701"
        },
        "description": "Standard Shipping",
        "fulfillable_on": "now"
      }
    ],
    "events": [
      {
        "id": "fulfill_evt_1",
        "occurred_at": "2026-02-08T10:30:00Z",
        "type": "shipped",
        "line_items": [
          { "id": "line_1", "quantity": 1 },
          { "id": "line_2", "quantity": 1 }
        ],
        "tracking_number": "123456789",
        "tracking_url": "https://fedex.com/track/123456789",
        "carrier": "FedEx",
        "description": "Both items shipped together"
      }
    ]
  },
  "permalink_url": "https://merchant.example.com/orders/456"
}

คำสั่งซื้อที่มีสินค้าหลายรายการและการจัดส่งแยก

ตัวอย่างนี้แสดงการอัปเดตคำสั่งซื้อสำหรับการจัดส่งแยก มีเหตุการณ์ shipped หลายรายการ ซึ่งแต่ละรายการอ้างอิง line_items ที่เฉพาะเจาะจงภายในแพ็กเกจนั้นๆ โดยใช้หมายเลขติดตามที่ไม่ซ้ำกัน เนื่องจากแต่ละแพ็กเกจแสดงถึงข้อผูกมัดในการดำเนินการตามคำสั่งซื้อที่แตกต่างกัน (เช่น ความเร็วและค่าใช้จ่ายที่แตกต่างกันจากการชำระเงินแบบหลายกลุ่ม) ระบบจึงแยก expectations ด้วย

ส่วนหัวที่ต้องมี:

  • Webhook-Id: fulfill_evt_2
  • Webhook-Timestamp: 2026-02-09T14:00:00Z
{
  "ucp": {
    "version": "2026-04-08",
    "capabilities": {
      "dev.ucp.shopping.order": [{"version": "2026-04-08"}]
    }
  },
  "id": "order_multi_02",
  "checkout_id": "checkout_multi_02",
  "currency": "USD",
  "line_items": [
    {
      "id": "line_1",
      "item": { "id": "product_1", "title": "Item 1", "price": 5000 },
      "quantity": { "total": 1, "fulfilled": 1 },
      "totals": [
        {"type": "subtotal", "display_text": null, "amount": 5000},
        {"type": "total", "display_text": null, "amount": 5000}
      ],
      "status": "fulfilled"
    },
    {
      "id": "line_2",
      "item": { "id": "product_2", "title": "Item 2", "price": 5000 },
      "quantity": { "total": 1, "fulfilled": 1 },
      "totals": [
        {"type": "subtotal", "display_text": null, "amount": 5000},
        {"type": "total", "display_text": null, "amount": 5000}
      ],
      "status": "fulfilled"
    }
  ],
  "totals": [
    {"type": "subtotal", "display_text": "Subtotal", "amount": 10000},
    {"type": "fee", "display_text": "Shipping", "amount": 1500},
    {"type": "total", "display_text": "Total", "amount": 11500}
  ],
  "fulfillment": {
    "expectations": [
      {
        "id": "exp_1",
        "line_items": [{ "id": "line_1", "quantity": 1 }],
        "method_type": "shipping",
        "destination": {
          "first_name": "Alice",
          "last_name": "Example",
          "street_address": "123 Main St",
          "address_locality": "Austin",
          "address_region": "TX",
          "address_country": "US",
          "postal_code": "78701"
        },
        "description": "Standard Shipping",
        "fulfillable_on": "now"
      },
      {
        "id": "exp_2",
        "line_items": [{ "id": "line_2", "quantity": 1 }],
        "method_type": "shipping",
        "destination": {
          "first_name": "Alice",
          "last_name": "Example",
          "street_address": "123 Main St",
          "address_locality": "Austin",
          "address_region": "TX",
          "address_country": "US",
          "postal_code": "78701"
        },
        "description": "Express Shipping",
        "fulfillable_on": "now"
      }
    ],
    "events": [
      {
        "id": "fulfill_evt_1",
        "occurred_at": "2026-02-08T10:30:00Z",
        "type": "shipped",
        "line_items": [{ "id": "line_1", "quantity": 1 }],
        "tracking_number": "PKG1_TRACKING",
        "tracking_url": "https://fedex.com/track/PKG1_TRACKING",
        "carrier": "FedEx",
        "description": "First item shipped in package 1"
      },
      {
        "id": "fulfill_evt_2",
        "occurred_at": "2026-02-09T14:00:00Z",
        "type": "shipped",
        "line_items": [{ "id": "line_2", "quantity": 1 }],
        "tracking_number": "PKG2_TRACKING",
        "tracking_url": "https://fedex.com/track/PKG2_TRACKING",
        "carrier": "FedEx",
        "description": "Second item shipped in package 2"
      }
    ]
  },
  "permalink_url": "https://merchant.example.com/orders/457"
}

ตัวอย่างเหตุการณ์การปรับ

ตัวอย่างต่อไปนี้แสดงวิธีจัดโครงสร้างการอัปเดตสำหรับการคืนเงิน การคืนสินค้า และการยกเลิก ดูรายการเหตุการณ์ที่รองรับและ คำจำกัดความของเหตุการณ์เหล่านั้นได้ที่เหตุการณ์การปรับใน ภาพรวมของวงจรชีวิตของคำสั่งซื้อ

การยกเลิกและการคืนเงินตามคำสั่งซื้อ

ตัวอย่างนี้แสดงคำสั่งซื้อที่ยกเลิกและคืนเงินสินค้าหลังจากสั่งซื้อได้ไม่นาน

การเปลี่ยนแปลงที่สำคัญในเวอร์ชันนี้ในตัวอย่างนี้:

  • ตอนนี้รายการโฆษณาที่ได้รับผลกระทบจาก cancellation จะใช้ "status": "removed" ใน อาร์เรย์ line_items หลัก
  • เมื่อ line_items.status เป็น removed ระบบจะดำเนินการดังนี้
    • ตั้งค่า line_items.quantity.total เป็น 0
    • จัดเก็บจำนวนเดิมไว้ในช่อง line_items.quantity.original ใหม่

ส่วนหัวที่ต้องมี:

  • Webhook-Id: adj_refund_1
  • Webhook-Timestamp: 2026-02-09T11:05:00Z
{
  "ucp": {
    "version": "2026-04-08",
    "capabilities": {
      "dev.ucp.shopping.order": [{"version": "2026-04-08"}]
    }
  },
  "id": "order_02",
  "checkout_id": "checkout_02",
  "currency": "USD",
  "line_items": [
    {
      "id": "line_2",
      "item": {
        "id": "product_456",
        "title": "Smart Watch",
        "price": 29900
      },
      "quantity": {
        "total": 0,  // Item removed from order total.
        "original": 1,  // Original quantity before removal.
        "fulfilled": 0 //  Item was not fulfilled before cancellation.
      },
      "totals": [
        {"type": "subtotal", "amount": 29900},
        {"type": "tax", "amount": 2400},
        {"type": "total", "amount": 32300}
      ],
      "status": "removed" // Item is cancelled, returned, or refunded.
    }
  ],
  "totals": [
    {"type": "subtotal", "amount": 29900},
    {"type": "tax", "amount": 2400},
    {"type": "total", "amount": 32300}
  ],
    // Fulfillment expectations should still be present even if cancelled early.
  "fulfillment": {
    "expectations": [
      {
        "id": "exp_1",
        "line_items": [{ "id": "line_2", "quantity": 1 }],
        "method_type": "shipping",
        "destination": {
          "first_name": "Bob",
          "last_name": "Consumer",
          "street_address": "456 Oak Ave",
          "address_locality": "Anytown",
          "address_region": "CA",
          "address_country": "US",
          "postal_code": "90210"
        },
        "description": "Standard Shipping",
        "fulfillable_on": "now"
      }
    ]
    // "events": [] // No fulfillment events occurred before cancellation.
    },
  "adjustments": [
    {
      "id": "adj_cancel_1",
      "type": "cancellation",
      "description": "Customer changed mind",
      "line_items": [{ "id": "line_2", "quantity": -1 }],
      "occurred_at": "2026-02-09T11:00:00Z",
      "status": "completed"
    },
    {
      "id": "adj_refund_1",
      "type": "refund",
      "description": "Refund for cancelled item",
      "line_items": [{ "id": "line_2", "quantity": -1 }],
      "totals": [
        {"type": "subtotal", "display_text": "Subtotal", "amount": -29900}, // Negative amounts indicate money returned to the buyer. Tax-inclusive markets: Set display_text to "Subtotal (including taxes)". Amount must include tax.
        {"type": "tax", "amount": -2400}, // Tax-inclusive markets: Omit this entry.
        {"type": "total", "display_text": "Total", "amount": -32300}
      ],
      "occurred_at": "2026-02-09T11:05:00Z",
      "status": "completed"
    }
  ],
  "permalink_url": "https://merchant.example.com/orders/12345"
}

การคืนสินค้าและการคืนเงินตามคำสั่งซื้อ

ตัวอย่างนี้แสดงคำสั่งซื้อที่จัดส่ง นำส่ง คืนสินค้า และคืนเงินแล้ว

การเปลี่ยนแปลงที่สำคัญในเวอร์ชันนี้ในตัวอย่างนี้:

  • ตอนนี้รายการโฆษณาที่ได้รับผลกระทบจาก return จะใช้ "status": "removed" ในอาร์เรย์ line_items หลัก
  • เมื่อ line_items.status เป็น removed ระบบจะดำเนินการดังนี้
    • ตั้งค่า line_items.quantity.total เป็น 0
    • จัดเก็บจำนวนเดิมไว้ในช่อง line_items.quantity.original ใหม่
  • ใน adjustments ประเภท return ช่อง line_items.quantity ภายในรายการปรับจะใช้ค่าติดลบ (เช่น -1) เพื่อระบุสินค้าที่นำกลับ

ส่วนหัวที่ต้องมี:

  • Webhook-Id: adj_refund_2
  • Webhook-Timestamp: 2026-02-10T10:00:00Z
{
  "ucp": {
    "version": "2026-04-08",
    "capabilities": {
      "dev.ucp.shopping.order": [{"version": "2026-04-08"}]
    }
  },
  "id": "order_03",
  "checkout_id": "checkout_03",
  "currency": "USD",
  "line_items": [
    {
      "id": "line_3",
      "item": {
        "id": "product_789",
        "title": "Wireless Earbuds",
        "price": 14900
      },
      "quantity": {
        "total": 0,  // Item removed from order total.
        "original": 1,  // Original quantity before removal.
        "fulfilled": 1 // Was fulfilled before return.
      },
      "totals": [
        {"type": "subtotal", "amount": 14900},
        {"type": "tax", "amount": 1200},
        {"type": "total", "amount": 16100}
      ],
      "status": "removed" // Item is cancelled, returned, or refunded.
    }
  ],
  "totals": [
    {"type": "subtotal", "amount": 14900},
    {"type": "tax", "amount": 1200},
    {"type": "total", "amount": 16100}
  ],
  "fulfillment": {
    "events": [
      {
        "id": "fulfill_evt_1",
        "occurred_at": "2026-02-05T09:00:00Z",
        "type": "shipped",
        "line_items": [{ "id": "line_3", "quantity": 1 }],
        "tracking_number": "987654321",
        "tracking_url": "https://fedex.com/track/987654321",
        "carrier": "FedEx",
        "description": "Item shipped"
      },
      {
        "id": "fulfill_evt_2",
        "occurred_at": "2026-02-07T16:00:00Z",
        "type": "delivered",
        "line_items": [{ "id": "line_3", "quantity": 1 }],
        "tracking_number": "987654321",
        "tracking_url": "https://fedex.com/track/987654321",
        "carrier": "FedEx",
        "description": "Item delivered"
      },
      {
        "id": "fulfill_evt_3",
        "occurred_at": "2026-02-09T09:00:00Z",
        "type": "returned",
        "line_items": [{ "id": "line_3", "quantity": 1 }],
        "tracking_number": "987654321",
        "tracking_url": "https://fedex.com/track/987654321",
        "carrier": "FedEx",
        "description": "Item returned"
      }
    ],
    "expectations": [{ "...": "..." }]
  },
  "adjustments": [
    {
      "id": "adj_return_1",
      "type": "return", // a matching fulfillment event is also added to represent return shipping.
      "description": "Item not compatible",
      "line_items": [{ "id": "line_3", "quantity": -1 }],  // Uses a negative value (such as -1) to indicate a return.
      "occurred_at": "2026-02-09T09:00:00Z",
      "status": "completed"
    },
    {
      "id": "adj_refund_2",
      "type": "refund",
      "description": "Refund for returned item",
      "line_items": [{ "id": "line_3", "quantity": -1 }],
      "totals": [
        {"type": "subtotal", "amount": -14900}, // Negative amounts indicate money returned to the buyer.
        {"type": "tax", "amount": -1200},
        {"type": "total", "amount": -16100}
      ],
      "occurred_at": "2026-02-10T10:00:00Z",
      "status": "completed"
    }
  ],
  "permalink_url": "https://merchant.example.com/orders/67890"
}