Wdrażanie cyklu życia zamówienia

Ten przewodnik zawiera techniczną dokumentację API i schematy ładunków służące do przesyłania do Google pełnych aktualizacji stanu zamówienia, zdarzeń realizacji i korekt za pomocą webhooków w wersji 2026-04-08 protokołu Universal Commerce Protocol (UCP).

Zanim zaczniesz tworzyć punkty końcowe, zapoznaj się z omówieniem cyklu życia zamówienia, aby poznać ogólne koncepcje, obowiązkowe zdarzenia i szczegóły punktu końcowego webhooka.

Uwierzytelnianie i podpisywanie żądań

Najważniejsze zmiany w wersji 2026-04-08 to wprowadzenie nowych obowiązkowych nagłówków webhooka i konkretnych procedur podpisywania żądań.

Wymagane nagłówki webhooka

Wszystkie żądania webhooka muszą zawierać te nagłówki HTTP:

  • Webhook-Id: unikalny identyfikator tego konkretnego zdarzenia webhooka. Ten identyfikator powinien być zgodny z polem id wysyłanego zdarzenia głównego (np. identyfikatorem zdarzenia realizacji lub identyfikatorem zdarzenia korekty).
  • Webhook-Timestamp: sygnatura czasowa wskazująca, kiedy wystąpiło zdarzenie.

Te nagłówki zastępują pola id i created_time, które były wcześniej oczekiwane w ładunku zamówienia.

Podpisywanie żądań

  1. Oblicz skrót SHA-256 nieprzetworzonej treści żądania i ustaw nagłówek Content-Digest.
  2. Wybierz klucz podpisywania z pola signing_keys w profilu UCP.
  3. Utwórz bazę podpisu zgodnie z RFC 9421.
    • Zobacz specyfikację podpisywanych komponentów
  4. Ustaw nagłówki UCP-Agent, Signature-Input i Signature.
    • UCP-Agent to link do Twojego profilu UCP w formacie profile="https://merchant.example.com/.well-known/ucp".
    • Signature-Input to pole strukturalne w postaci słownika opisujące komponenty zawarte w podpisie oraz keyid użyty do podpisania, który musi być zgodny z polem kid wybranego klucza podpisywania z pola signing_keys w profilu UCP.
    • Nagłówek Signature zawiera bazę podpisu, która jest podpisywana za pomocą klucza prywatnego, a następnie kodowana w formacie base64.

Więcej informacji znajdziesz w instrukcjach podpisywania na stronie ucp.dev.

Zdarzenie utworzenia zamówienia

  • Aktywator: natychmiast po potwierdzeniu zamówienia (status: processing).

Najważniejsze zmiany w tej wersji:

  • Pole currency jest teraz wymagane na najwyższym poziomie obiektu Order.
  • Pole type w każdym obiekcie w tablicy totals jest teraz otwartym ciągiem znaków (np. „subtotal”, „tax”, „fee”, „total”).
  • Obowiązkowy nagłówek Webhook-Id zawiera unikalny identyfikator zdarzenia webhooka. Pamiętaj, że pole id w ładunku zawierającym identyfikator potwierdzenia zamówienia jest nadal wymagane.
  • Obowiązkowy nagłówek Webhook-Timestamp zawiera czas utworzenia, zastępując poprzednie pole created_time w ładunku.

Przykład: ten przykład pokazuje zamówienie utworzone po tym, jak kupujący sfinalizował zakupy.

Wymagane nagłówki:

  • 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"
}

Zdarzenia realizacji

Te zdarzenia są wysyłane w ramach tablicy fulfillment.events.

Wysłano zamówienie

Gdy produkty w zamówieniu zostały wysłane. W przypadku zdarzeń wysyłki wymagane są pola tracking_number i tracking_url, ponieważ są one potrzebne do prawidłowego grupowania produktów na stronie „Moje zamówienia”.

Dostarczono zamówienie

Gdy produkty w zamówieniu zostały dostarczone.

Przykład (shipped i delivered): ten przykład pokazuje aktualizację zamówienia po wysłaniu i dostarczeniu produktu.

Wymagane nagłówki:

  • 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"
}

Przykłady zamówień z wieloma produktami

Z poniższych przykładów dowiesz się, jak strukturyzować aktualizacje zamówień z wieloma produktami i dzielić przesyłki. Informacje o regułach określających stan paczki znajdziesz w artykule Jak określa się stan paczki.

Zamówienie z wieloma produktami, dostawa w tej samej paczce

Ten przykład pokazuje aktualizację zamówienia dla jednej paczki zawierającej wiele produktów. Wszystkie elementy zamówienia są pogrupowane w ramach jednego zdarzenia shipped z tym samym numerem śledzenia.

Wymagane nagłówki:

  • 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"
}

Zamówienie z wieloma produktami, podzielona przesyłka

Ten przykład pokazuje aktualizację zamówienia w przypadku podzielonych przesyłek. Występuje kilka zdarzeń shipped, z których każde odwołuje się do konkretnych elementów zamówienia line_items w danej paczce i ma inne numery śledzenia. Ponieważ każda paczka stanowi odrębne zobowiązanie do realizacji (np. różne prędkości i koszty w przypadku płatności w wielu grupach), obiekt expectations jest również podzielony.

Wymagane nagłówki:

  • 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"
}

Przykłady zdarzeń korekty

Z poniższych przykładów dowiesz się, jak strukturyzować aktualizacje dotyczące zwrotów środków, zwrotów produktów i anulowania. Listę obsługiwanych zdarzeń i ich definicje znajdziesz w artykule Zdarzenia korekty w omówieniu cyklu życia zamówienia.

Anulowanie zamówienia i zwrot środków

Ten przykład pokazuje zamówienie, w którym produkt został anulowany i zwrócono za niego środki krótko po złożeniu zamówienia.

Najważniejsze zmiany w tej wersji w tym przykładzie:

  • Elementy zamówienia, których dotyczy cancellation, używają teraz "status": "removed" w głównej tablicy line_items.
  • Gdy line_items.status ma wartość removed:
    • Pole line_items.quantity.total jest ustawione na 0.
    • Oryginalna ilość jest przechowywana w nowym polu line_items.quantity.original.

Wymagane nagłówki:

  • 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"
}

Zwrot zamówienia i zwrot środków

Ten przykład pokazuje zamówienie, w którym produkt został wysłany, dostarczony, a następnie zwrócony i zwrócono za niego środki.

Najważniejsze zmiany w tej wersji w tym przykładzie:

  • Elementy zamówienia, których dotyczy return, używają teraz "status": "removed" w głównej line_items tablicy.
  • Gdy line_items.status ma wartość removed:
    • Pole line_items.quantity.total jest ustawione na 0.
    • Oryginalna ilość jest przechowywana w nowym polu line_items.quantity.original.
  • W adjustments typu return, pole line_items.quantity w korekcie używa wartości ujemnej (np. -1) do wskazania produktów, które są zwracane.

Wymagane nagłówki:

  • 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"
}