주문 이행(Fulfillment) 확장¶
개요¶
fulfillment 확장은 business가 실물 상품 이행(배송, 매장 수령 등) 지원 여부를 광고할 수 있게 합니다.
이 확장은 Checkout에 fulfillment 필드를 추가하며, 다음을 포함합니다.
methods[]- 장바구니 아이템에 적용 가능한 fulfillment 방법(배송, 픽업 등)line_item_ids- 이 방법으로 처리되는 아이템destinations[]- 이행 대상 위치(주소, 매장 위치)groups[]- business가 생성한 패키지 단위, 각 그룹은 선택 가능한options[]포함available_methods[]- 아이템별 재고 기반 이행 가능성(선택)
멘탈 모델:
methods[0]Shippingline_item_ids👕👖selected_destination_id=destinations[0].id🔘✅ 123 Fake Stgroups[0]📦👕👖selected_option_id=options[0].id🔘✅ Standard $5options[1]🔘 Express $10
methods[1]Pick Up in Storeline_item_ids👞selected_destination_id=destinations[0].id🔘✅ Uptown Storegroups[0]📦👞selected_option_id=options[0].id🔘✅ In-Store Pickupoptions[1]🔘 Curbside Pickup
스키마¶
fulfillment는 실물 전달이 필요한 아이템에만 적용됩니다. 이행이 필요하지 않은 아이템(예: 디지털 상품)은 method에 할당할 필요가 없습니다.
속성¶
| Name | Type | Required | Description |
|---|---|---|---|
| fulfillment | Fulfillment | No | Fulfillment details. |
엔터티¶
주문 이행(Fulfillment)¶
| Name | Type | Required | Description |
|---|---|---|---|
| methods | Array[Fulfillment Method] | No | Fulfillment methods for cart items. |
| available_methods | Array[Fulfillment Available Method] | No | Inventory availability hints. |
주문 이행 방식 응답¶
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique fulfillment method identifier. |
| type | string | Yes | Fulfillment method type. Enum: shipping, pickup |
| line_item_ids | Array[string] | Yes | Line item IDs fulfilled via this method. |
| destinations | Array[Fulfillment Destination] | No | Available destinations. For shipping: addresses. For pickup: retail locations. |
| selected_destination_id | ['string', 'null'] | No | ID of the selected destination. |
| groups | Array[Fulfillment Group] | No | Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method. |
주문 이행 목적지 응답¶
This object MUST be one of the following types: Shipping Destination, Retail Location.
배송 목적지 응답¶
| Name | Type | Required | Description |
|---|---|---|---|
| extended_address | string | No | An address extension such as an apartment number, C/O or alternative name. |
| street_address | string | No | The street address. |
| address_locality | string | No | The locality in which the street address is, and which is in the region. For example, Mountain View. |
| address_region | string | No | The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division. |
| address_country | string | No | The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example "US". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as "SGP" or a full country name such as "Singapore" can also be used. |
| postal_code | string | No | The postal code. For example, 94043. |
| first_name | string | No | Optional. First name of the contact associated with the address. |
| last_name | string | No | Optional. Last name of the contact associated with the address. |
| phone_number | string | No | Optional. Phone number of the contact associated with the address. |
| id | string | Yes | ID specific to this shipping destination. |
오프라인 매장 위치 응답¶
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique location identifier. |
| name | string | Yes | Location name (e.g., store name). |
| address | Postal Address | No | Physical address of the location. |
주문 이행 그룹 응답¶
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Group identifier for referencing merchant-generated groups in updates. |
| line_item_ids | Array[string] | Yes | Line item IDs included in this group/package. |
| options | Array[Fulfillment Option] | No | Available fulfillment options for this group. |
| selected_option_id | ['string', 'null'] | No | ID of the selected fulfillment option for this group. |
주문 이행 옵션 응답¶
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique fulfillment option identifier. |
| title | string | Yes | Short label (e.g., 'Express Shipping', 'Curbside Pickup'). |
| description | string | No | Complete context for buyer decision (e.g., 'Arrives Dec 12-15 via FedEx'). |
| carrier | string | No | Carrier name (for shipping). |
| earliest_fulfillment_time | string | No | Earliest fulfillment date. |
| latest_fulfillment_time | string | No | Latest fulfillment date. |
| totals | Array[Total] | Yes | Fulfillment option totals breakdown. |
주문 이행 가능 방식 응답¶
| Name | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Fulfillment method type this availability applies to. Enum: shipping, pickup |
| line_item_ids | Array[string] | Yes | Line items available for this fulfillment method. |
| fulfillable_on | ['string', 'null'] | No | 'now' for immediate availability, or ISO 8601 date for future (preorders, transfers). |
| description | string | No | Human-readable availability info (e.g., 'Available for pickup at Downtown Store today'). |
합계 응답¶
| Name | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Type of total categorization. Enum: items_discount, subtotal, discount, fulfillment, tax, fee, total |
| display_text | string | No | Text to display against the amount. Should reflect appropriate method (e.g., 'Shipping', 'Delivery'). |
| amount | integer | Yes | If type == total, sums subtotal - discount + fulfillment + tax + fee. Should be >= 0. Amount in minor (cents) currency units. |
우편 주소(Postal Address)¶
| Name | Type | Required | Description |
|---|---|---|---|
| extended_address | string | No | An address extension such as an apartment number, C/O or alternative name. |
| street_address | string | No | The street address. |
| address_locality | string | No | The locality in which the street address is, and which is in the region. For example, Mountain View. |
| address_region | string | No | The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division. |
| address_country | string | No | The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example "US". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as "SGP" or a full country name such as "Singapore" can also be used. |
| postal_code | string | No | The postal code. For example, 94043. |
| first_name | string | No | Optional. First name of the contact associated with the address. |
| last_name | string | No | Optional. Last name of the contact associated with the address. |
| phone_number | string | No | Optional. Phone number of the contact associated with the address. |
예시¶
{
"fulfillment": {
"methods": [
{
"id": "method_1",
"type": "shipping",
"line_item_ids": ["shirt", "pants"],
"selected_destination_id": "dest_1",
"destinations": [
{
"id": "dest_1",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["shirt", "pants"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard Shipping",
"description": "Arrives Dec 12-15 via USPS",
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express Shipping",
"description": "Arrives Dec 10-11 via FedEx",
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
}
]
}
}
렌더링¶
fulfillment 옵션은 method-agnostic 렌더링을 목표로 설계되었습니다. platform은 method 타입(배송/픽업 등)을 구체적으로 이해하지 않아도 옵션을 의미 있게 표시할 수 있습니다. business가 사전 계산된 사람이 읽기 쉬운 필드를 제공하고, platform은 이를 그대로 렌더링하면 됩니다.
사람이 읽기 쉬운 필드¶
| 위치 | 필드 | 필수 | 목적 |
|---|---|---|---|
groups[].options[] |
title |
Yes | 같은 그룹 내 옵션을 구분하는 기본 라벨 |
groups[].options[] |
description |
No | title을 보조하는 추가 설명 |
groups[].options[] |
total |
Yes | 소수 단위 가격(아직 확정 전이면 null 가능) |
available_methods[] |
description |
No | 대체 가능 방법에 대한 독립 설명 |
비즈니스 책임¶
options[].title에 대해:
- 같은 그룹 내 형제 옵션과 구분되도록 MUST 작성
- 방법/속도를 포함하는 것이 SHOULD (예: "Express Shipping", "Curbside Pickup")
description이 없어도 구매자 의사결정에 충분해야 MUST 함
options[].description에 대해:
title또는total을 반복하면 안 되며(MUST NOT), 보조 맥락만 제공- 도착 시점/택배사/의사결정 관련 정보를 포함하는 것이 SHOULD
- 완전한 문장 또는 구로 작성하는 것이 SHOULD (예: "Arrives Dec 12-15 via FedEx")
- title만으로 충분하면 생략 가능(MAY)
available_methods[].description에 대해:
- 무엇을/언제/어디서 처리 가능한지 설명하는 독립 문장이어야 MUST 함
- platform 대화 UI에 그대로 사용할 수 있어야 SHOULD 함 (예: "Pants available for pickup at Downtown Store today at 2pm")
정렬 순서에 대해:
- business는
options[]를 의미 있는 순서(예: 최저가 우선, 최단시간 우선)로 반환하는 것이 SHOULD - platform은 제공된 순서 그대로 렌더링하는 것이 SHOULD
플랫폼 책임¶
platform은 fulfillment를 범용 렌더링 가능한 구조로 취급하는 것이 SHOULD 합니다.
- 각 옵션을
title,description,total기반 카드 형태로 렌더링 - business가 제공한 순서대로 옵션 제시
- 반환된 모든 method를 제시 (method 선택은 구매자 결정)
available_methods[].description을 사용해 대체 옵션 노출
platform은 인식 가능한 method 타입에 대해 향상 UX를 제공할 수 있습니다(MAY).
(예: pickup 매장 선택기, shipping 운송사 로고)
단, 이는 선택 사항입니다. 기본 계약은
title + description + total만으로 모든 옵션을 렌더링 가능해야 한다는 점입니다.
구매자가 플랫폼이 완전 처리할 수 없는 옵션을 선택한 경우,
platform은 business checkout으로 핸드오프하기 위해 continue_url을 사용하는 것이
SHOULD 됩니다.
사용 가능한 방식(Available Methods)¶
available methods는 특정 아이템이 특정 method로 이행 가능한지, 그리고 언제 가능한지를 나타냅니다. 사용 사례 예시:
- 대체 방법 안내: "이 바지는 Downtown Store 매장 수령도 가능합니다"
- 나중 이행: 예약 주문, 원거리 창고 출고, 매장 재고 입고 후 픽업
{
"fulfillment": {
"methods": [
{
"id": "shipping",
"type": "shipping",
"line_item_ids": ["shirt", "pants"]
},
{
"id": "pickup",
"type": "pickup",
"line_item_ids": []
}
],
"available_methods": [
{
"type": "shipping",
"line_item_ids": ["shirt", "pants"],
"fulfillable_on": "now"
},
{
"type": "pickup",
"line_item_ids": ["pants"],
"fulfillable_on": "2026-12-01T10:00:00Z",
"description": "Available for pickup at Downtown Store today at 2pm"
}
]
}
}
description 필드는 platform이 구매자에게 대체 옵션을 안내할 수 있게 합니다.
🤖 The shirt and pants ship for $5, arriving in 5-8 days. Or the pants can be picked up at Downtown Store in 4 hours.
구매자가 pickup을 선택했지만 platform이 분할 fulfillment를 지원하지 않으면,
platform은 business checkout으로 핸드오프하기 위해 continue_url을 사용하는 것이
SHOULD 됩니다.
구성(Configuration)¶
business와 platform은 각자의 프로필에서 fulfillment 제약을 선언합니다. business는 platform 프로필을 조회해 이에 맞는 응답을 생성합니다.
플랫폼 프로필¶
platform은 platform_schema를 사용해 렌더링 capability를 선언합니다.
| Name | Type | Required | Description |
|---|---|---|---|
| supports_multi_group | boolean | No | Enables multiple groups per method. |
config를 생략하거나 supports_multi_group: false로 설정한 platform은
단일 그룹 응답을 받습니다. 응답 구조는 항상 methods[].groups[]이며,
차이는 각 method 내 groups.length가 1을 초과할 수 있는지 여부입니다.
// Default: single group per method
{ "dev.ucp.shopping.fulfillment": [{"version": "2026-01-11"}] }
// Opt-in: business MAY return multiple groups per method
{ "dev.ucp.shopping.fulfillment": [{"version": "2026-01-11", "config": { "supports_multi_group": true }}] }
비즈니스 프로필¶
business는 merchant_config를 사용해 자신이 지원하는 fulfillment 구성을 선언합니다.
| Name | Type | Required | Description |
|---|---|---|---|
| allows_multi_destination | object | No | Permits multiple destinations per method type. |
| allows_method_combinations | Array[array] | No | Allowed method type combinations. |
{
"capabilities": {
"dev.ucp.shopping.fulfillment": [
{
"version": "2026-01-11",
"config": {
"allows_multi_destination": {
"shipping": true
},
"allows_method_combinations": [["shipping", "pickup"]]
}
}
]
}
}
위 예시는 shipping이 다중 주소를 지원하고, 장바구니에서 shipping+pickup 혼합이 가능함을 의미합니다.
비즈니스 응답 동작¶
supports_multi_group: false(기본값)일 때:
- business는 모든 아이템을 method당 단일 그룹으로 통합해야 합니다(MUST).
- 응답은 배열 구조를 유지합니다:
methods[].groups[]에서groups.length === 1 - 장바구니 요구에 따라 business는 여러 method(예: shipping + pickup)를 여전히 반환할 수 있습니다(MAY).
supports_multi_group: true일 때:
- business는 재고/패키징/창고 로직에 따라 method당 다중 그룹을 반환할 수 있습니다(MAY).
- platform은 그룹 선택 UI(예: 패키지별 배송 속도 선택)를 렌더링해야 합니다.
신규 Method 추가¶
fulfillment를 신규 method 타입(예: local_delivery)으로 확장하는 확장은,
아래를 포함하는 extension schema를 반드시(MUST) 추가해야 합니다.
fulfillment_method의typeenum에 신규 method 추가- 대응 business config 옵션 추가:
allows_multi_destination.local_delivery: booleanallows_method_combinations항목 enum (예:"local_delivery"포함)
참고: platform의 supports_multi_group는 method-agnostic(단일 boolean)이므로
추가 확장이 필요하지 않습니다.
예시¶
기본¶
Config: 별도 요구 없음(기본 동작)
{
"fulfillment": {
"methods": [
{
"id": "method_1",
"type": "shipping",
"line_item_ids": ["shirt", "pants"],
"selected_destination_id": "dest_1",
"destinations": [
{
"id": "dest_1",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["shirt", "pants"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard Shipping",
"description": "Arrives Dec 12-15 via USPS",
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express Shipping",
"description": "Arrives Dec 10-11 via FedEx",
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
}
]
}
}
그룹 분할(Split Groups)¶
Config: platform 프로필에 config.supports_multi_group: true 필요
business가 아이템을 여러 패키지로 나누고, 구매자가 패키지별 배송 옵션을 선택합니다.
{
"fulfillment": {
"methods": [
{
"id": "method_1",
"type": "shipping",
"line_item_ids": ["shirt", "pants"],
"selected_destination_id": "dest_1",
"destinations": [
{
"id": "dest_1",
"street_address": "123 Main St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["shirt"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard",
"totals": [ {"type": "total", "amount": 500} ]
},
{
"id": "express",
"title": "Express",
"totals": [ {"type": "total", "amount": 1000} ]
}
]
},
{
"id": "package_2",
"line_item_ids": ["pants"],
"selected_option_id": "express",
"options": [
{
"id": "standard",
"title": "Standard",
"totals": [ {"type": "total", "amount": 500} ]
},
{
"id": "express",
"title": "Express",
"totals": [ {"type": "total", "amount": 1000} ]
}
]
}
]
}
]
}
}
목적지 분할(Split Destinations)¶
Config: business 프로필에
config.allows_multi_destination.shipping: true 필요
셔츠는 엄마(미국), 바지는 할머니(홍콩)에게 배송되는 예시입니다. 동일 타입(shipping)의 method를 2개 두고 각각 독립 목적지를 가집니다.
{
"fulfillment": {
"methods": [
{
"id": "method_1",
"type": "shipping",
"line_item_ids": ["shirt"],
"selected_destination_id": "dest_mom",
"destinations": [
{
"id": "dest_mom",
"street_address": "123 Mom St",
"address_locality": "Springfield",
"address_region": "IL",
"postal_code": "62701",
"address_country": "US"
}
],
"groups": [
{
"id": "package_1",
"line_item_ids": ["shirt"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard",
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express",
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
},
{
"id": "method_2",
"type": "shipping",
"line_item_ids": ["pants"],
"selected_destination_id": "dest_grandma",
"destinations": [
{
"id": "dest_grandma",
"street_address": "88 Queensway",
"address_locality": "Hong Kong",
"address_country": "HK"
}
],
"groups": [
{
"id": "package_2",
"line_item_ids": ["pants"],
"selected_option_id": "standard",
"options": [
{
"id": "standard",
"title": "Standard",
"totals": [
{
"type": "total",
"amount": 500
}
]
},
{
"id": "express",
"title": "Express",
"totals": [
{
"type": "total",
"amount": 1000
}
]
}
]
}
]
}
]
}
}