메뉴 피드 및 처리 장바구니 항목 매핑
고객이 메뉴 피드의 상품을 장바구니에 추가하고 결제하면 Google에서 해당 상품을 처리 엔드포인트로 전송하여 가격과 재고를 확인합니다. 가격 및 재고가 확인되면 고객이 주문할 수 있습니다. 이 섹션에서는 메뉴 피드 항목을 처리 장바구니 항목에 매핑하는 방법을 보여줍니다.
이 섹션의 샘플은 메뉴 피드 및 장바구니 스키마의 축소된 버전입니다. 메뉴 피드와 장바구니 객체 간의 매핑을 보여주는 데 관련된 필드만 표시됩니다. 전체 스키마는 Menu
및 Cart
를 참고하세요.
장바구니에 추가된 Menu
피드의 상품은 결제 및 주문 제출 모두에 대해 Cart
객체로 전송됩니다.
- 단순한
MenuItem
는lineItems
배열에서LineItem
로 표시되며offerId
는 메뉴 피드에서 선택된 메뉴 항목의offer.id
입니다. - 필수
MenuItemOption
가 있는MenuItem
는lineItems
배열에서LineItem
로 표시되며offerId
는 메뉴 피드에서 선택된 메뉴 항목 옵션의offer.id
입니다. LineItem
의AddOnMenuItem
는FoodItemExtension
의options
배열에서FoodItemOption
로 표시됩니다. 각 옵션에는 메뉴 피드에서 선택한 부가기능 메뉴 항목의offer.id
에 해당하는offerId
가 있습니다. AddOnMenuItem에는 각 옵션 내에subOptions
로 표시되는 중첩된 AddOnMenuItem이 있을 수도 있습니다.
다음 예에서는 메뉴 피드와 처리 장바구니 간에 메뉴 항목을 매핑합니다.
이 예에는 간단한 메뉴 항목 목록이 포함되어 있습니다.
메뉴 피드의 메뉴 항목:
{ "@type": "Menu", "@id": "menu_id", "hasMenuItem": [ { "@type": "MenuItem", "@id": "menuitem_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ] }, { "@type": "MenuItem", "@id": "menuitem_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_2", "price": "p_2", "priceCurrency": "USD" } ] } ] }
주문 처리 장바구니에 매핑된 메뉴 항목:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*p_1)", "nanos": "cent(q_1*p_1)" } }, "quantity": "q_1" }, { "offerId": "menuitem_offer_id_2", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_2*p_2)", "nanos": "cent(q_2*p_2)" } }, "quantity": "q_2" } ] }
이 예에는 AddOnMenuItem이 하나 이상 있는 메뉴 항목이 포함되어 있습니다.
메뉴 피드의 메뉴 항목:
{ "@type": "Menu", "@id": "menu_id", "hasMenuItem": [ { "@type": "MenuItem", "@id": "menuitem_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "MenuAddOnSection", "@id": "menuaddon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_addon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_addon_offer_id_1", "price": "addon_p_1", "priceCurrency": "USD" } ] }, { "@type": "AddOnMenuItem", "@id": "menuitem_addon_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_addon_offer_id_2", "price": "addon_p_2", "priceCurrency": "USD" } ] } ] } ] }, { "@type": "MenuItem", "@id": "menuitem_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_2", "price": "p_2", "priceCurrency": "USD" } ] } ] }
주문 처리 장바구니에 매핑된 메뉴 항목:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*addon_p_2))", "nanos": "cent(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*addon_p_2))" } }, "quantity": "q_1", "extension": { "@type": "FoodItemExtension", "options": [ { "offerId": "menuitem_addon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(addon_q_1*addon_p_1)", "nanos": "cent(addon_q_1*addon_p_1)" }, "quantity": "addon_q_1" }, { "offerId": "menuitem_addon_offer_id_2", "price": { "currencyCode": "USD", "units": "dollar(addon_q_2*addon_p_2)", "nanos": "cent(addon_q_2*addon_p_2)" }, "quantity": "addon_q_2" } ] } }, { "offerId": "menuitem_offer_id_2", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_2*p_2)", "nanos": "cent(q_2*p_2)" } }, "quantity": "q_2" } ] }
이 예에는 메뉴 항목 옵션, AddOnMenuItems, 중첩된 AddOnMenuItems가 포함된 메뉴 항목이 있습니다.
메뉴 피드의 메뉴 항목:
{ "@type": "MenuItem", "@id": "menuitem_id_1", "hasMenuItemOptions": [ { "@type": "MenuItemOption", "value": { "@type": "PropertyValue", "name": "OPTION", "value": "Large", "offers": [ { "@type": "Offer", "@id": "menuitem_option_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "AddOnMenuSection", "@id": "menuitem_option_addon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_option_addon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_option_addon_offer_id_1", "price": "addon_p_1", "priceCurrency": "USD" } ] }, { "@type": "AddOnMenuItem", "@id": "menuitem_option_addon_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_option_addon_offer_id_2", "price": "addon_p_2", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "AddOnMenuSection", "@id": "menuitem_option_subaddon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_option_subaddon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_option_subaddon_offer_id_1", "price": "subaddon_p_1", "priceCurrency": "USD" } ] } ] } ] } ] } ] } } ] }
주문 처리 장바구니에 매핑된 메뉴 항목:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_option_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1)))", "nanos": "cent(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1)))" } }, "quantity": "q_1", "extension": { "@type": "FoodItemExtension", "options": [ { "offerId": "menuitem_option_addon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(addon_q_1*addon_p_1)", "nanos": "cent(addon_q_1*addon_p_1)" }, "quantity": "addon_q_1" }, { "offerId": "menuitem_option_addon_offer_id_2", "price": { "currencyCode": "USD", "units": "dollar(addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1))", "nanos": "cent(addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1))" }, "quantity": "addon_q_2", "subOptions": [ { "offerId": "menuitem_option_subaddon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(subaddon_q_1*subaddon_p_1)", "nanos": "cent(subaddon_q_1*subaddon_p_1)" }, "quantity": "subaddon_q_1" } ] } ] } } ] }
오류 처리
CheckoutRequestMessage
를 처리하는 중에 문제가 발생하면 CheckoutResponse 대신 FoodErrorExtension
가 포함된 CheckoutResponseMessage
로 응답할 수 있습니다. 이 응답을 사용하여 처리 중에 발생한 하나 이상의 오류를 식별할 수 있습니다.
오류를 처리하는 방법에는 두 가지가 있습니다.
- 복구 가능한 오류: 사용자가 장바구니를 수정하지 않고도 주문을 제출할 수 있습니다. 예를 들어
Cart
의 상품에 가격 변경이 있다고 판단되면correctedProposedOrder
및paymentOptions
과 함께 오류 유형PRICE_CHANGED
의FoodOrderError
로 응답할 수 있습니다. Google은 사용자에게 변경사항을 알리지만 사용자가correctedProposedOrder
를 사용하여 제출하도록 허용합니다. 원하는 경우 사용자는 돌아가 장바구니를 수정할 수도 있습니다. 새CheckoutRequestMessage
또는SubmitOrderRequestMessage
를 받게 됩니다. - 복구 불가능한 오류: 사용자가 주문을 제출하기 전에 장바구니를 수정해야 합니다. 예를 들어 레스토랑이 문을 닫았다고 판단되면 오류 유형
CLOSED
의FoodOrderError
로 응답할 수 있습니다. Google은 사용자에게 알리고 상호작용을 관리하여 새 식당으로 업데이트합니다. 새 장바구니에 대한 새CheckoutRequestMessage
가 표시됩니다.
일반적으로 장바구니 수준 오류는 복구할 수 없게 하고 상품 수준 오류는 복구할 수 있도록 합니다. 오류 유형 및 의미의 전체 목록은 FoodOrderError
을 참고하세요.
가격 변경 처리
결제 중 가격 변경
고객의 결제 요청을 처리하는 중에 가격 문제가 발생하면 다음 단계를 따르세요.
- 오류 처리에 설명된 대로
CheckoutRequestMessage
에FoodErrorExtension
가 포함된CheckoutResponseMessage
로 응답합니다. - 오류 응답에서
correctedProposedOrder.cart
를 사용하여 가격을 올바른 값으로 업데이트합니다. Google에서 수정된 주문을 수신하고 새CheckoutRequestMessage
를 발행할 수 있습니다.
결제 후 Google에서는 ProposedOrder
가 변경되었는지 여부와 관계없이 최종 사용자에게 주문 확인 페이지를 표시합니다.
ProposedOrder가 수정된 경우 Google은 사용자에게 변경사항을 알리기 위해 추가 경고를 표시할 수 있습니다. 사용자가 주문에 동의하면 더 이상 결제 요청이 표시되지 않습니다. 수정된 ProposedOrder
를 사용하여 주문 제출 절차가 계속됩니다.
하지만 사용자는 언제든지 마음을 바꿔 장바구니를 다시 수정할 수 있습니다. 이렇게 장바구니가 업데이트되면 Google에서 새 CheckoutRequestMessage
를 전송합니다.
주문 제출 중 가격 변경
주문 제출을 처리하는 중에 가격 문제가 발생하면(actions.intent.TRANSACTION_DECISION
인텐트가 트리거됨) 오류로 응답하거나 응답에서 가격을 업데이트하지 마세요. SubmitOrderRequestMessage
의 가격, 수량 또는 기타 세부정보가 데이터와 일치하지 않으면 orderState
을 REJECTED
로 설정하여 요청한 대로 주문을 처리할 수 없음을 나타냅니다.
그런 다음 주문 및 결제 세부정보가 유효하면 orderState
를 CREATED
또는 CONFIRMED
로 설정합니다. 또한 시스템에서 주문 ID를 나타내는 actionOrderId
를 포함합니다. 후속 업데이트를 전송할 때 이 ID를 사용해야 합니다.
결제를 처리할 수 없고 이미 SubmitOrderRequestMessage
를 보낸 경우 orderState
를 REJECTED
로 설정하여 AsyncOrderUpdateRequestMessage
를 보내 사용자에게 주문이 처리되지 않음을 알릴 수 있습니다.
주문 제출 후 가격 변경
고객이 주문을 제출할 때 사용된 가격에서 가격이 변경되었다고 판단되면 비동기 주문 업데이트 구현에 설명된 대로 새 가격으로 AsyncOrderUpdateRequestMessage
를 발행할 수 있습니다.
비동기 주문 업데이트를 사용하여 가격을 업데이트하려면 다음 단계를 따르세요.
lineItemUpdates[x].price
에서 가격을 변경합니다. 이 값은 부가기능을 포함한 상품의 총비용에 수량을 곱한 값입니다. 자세한 내용은LineItem
의price
필드 설명을 참고하세요.lineItemUpdates[x].reason
에 설명을 입력합니다.lineItemUpdates[x].orderState
을CONFIRMED
로 설정합니다.
개발자는 재량에 따라 AsyncOrderUpdateRequestMessage를 전송하기 전이나 후에 결제 수단에 청구를 시도할 수 있습니다. 가격 차이가 너무 커서 트랜잭션이 실패한 경우 OrderUpdate
에 다음 설정을 사용하여 AsyncOrderUpdateRequestMessage를 전송하여 Google에 실패를 알립니다.
orderState
를REJECTED
로 설정합니다.label
필드에 오류를 설명합니다.
결제 유효성 검사
4단계: 결제 구현에서 설명한 대로 처리 엔드포인트는 들어오는 모든 CheckoutRequestMessage
에 대해 유효성 검사를 실행하고 CheckoutResponseMessage
으로 응답해야 합니다.
다음은 유효성 검사에 성공한 CheckoutResponseMessage
의 예입니다.
사용 사례 | 구현 방법 |
---|---|
사용 사례 1: 검증 성공 | CheckoutResponse 를 반환합니다. ProposedOrder 및 PaymentOptions 가 있어야 합니다.
ProposedOrder 에는 세금, 수수료, 장바구니의 총 가격이 포함됩니다. |
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "checkoutResponse": { "proposedOrder": { "id": "sample_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Pita Chips", "type": "REGULAR", "id": "sample_item_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/offer/id1", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } }, "subLines": [ { "note": "Notes for this item." } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension", "options": [ { "id": "sample_addon_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1", "name": "Honey Mustard", "price": { "currencyCode": "USD" }, "quantity": 1 }, { "id": "sample_addon_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id2", "name": "BBQ Sauce", "price": { "currencyCode": "USD", "nanos": 500000000 }, "quantity": 1 } ] } }, { "name": "Chicken Shwarma Wrap", "type": "REGULAR", "id": "sample_item_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/offer/id2", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "8" } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { // Represents $36.73 "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
배송지 주소 확인
처리 엔드포인트는 각 CheckoutRequestMessage
에 포함된 배송지 주소를 확인해야 합니다.
배송 서비스 범위를 벗어나는 등 배송지 주소에 문제가 있는 경우 처리에서 반환한 CheckoutResponseMessage
에 적절한 유형의 FoodOrderError
가 포함되어야 합니다.
사용 사례 | 구현 방법 |
---|---|
사용 사례 1: 배송지 주소가 범위를 벗어나거나 배송지 주소에 문제가 있어 유효성 검사가 실패함 | 오류 유형 OUT_OF_SERVICE_AREA 의 FoodOrderError 이 있는 FoodErrorExtension 를 반환합니다. |
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "OUT_OF_SERVICE_AREA", "description": "Sorry, the restaurant cannot deliver to your address." } ] } } } ] } } }
최소 주문 금액 유효성 검사
처리 엔드포인트는 각 CheckoutRequestMessage
의 최소 주문 금액을 확인해야 합니다.
최소 주문 금액이 충족되지 않으면 처리에서 반환한 CheckoutResponseMessage
에 오류 유형 REQUIREMENTS_NOT_MET
의 FoodOrderError
이 포함되어야 합니다.
사용 사례 | 구현 방법 |
---|---|
사용 사례 1: 최소 주문 금액이 충족되지 않아 유효성 검사가 실패함 | 오류 유형 REQUIREMENTS_NOT_MET 의 FoodOrderError 이 있는 FoodErrorExtension 를 반환합니다. |
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." } ] } } } ] } } }
주문 기간 유효성 검사
처리 엔드포인트는 각 CheckoutRequestMessage
의 주문 기간에 영향을 줄 수 있는 모든 요소를 확인해야 합니다.
예를 들어 음식점이 문을 닫았거나 현재 주문을 받지 않는 경우 처리에서 반환한 CheckoutResponseMessage
에는 각각 오류 유형 CLOSED
또는 NO_CAPACITY
의 FoodOrderError
가 포함되어야 합니다.
사용 사례 | 구현 방법 |
---|---|
사용 사례 1: 음식점이 폐쇄되었거나 더 이상 지원되지 않아 유효성 검사가 실패함 | 오류 유형 CLOSED 의 FoodOrderError 이 있는 FoodErrorExtension 를 반환합니다. |
사용 사례 2: 음식점이 바빠서 현재 주문을 받지 않고 있어 유효성 검사가 실패함 | 오류 유형 NO_CAPACITY 의 FoodOrderError 이 있는 FoodErrorExtension 를 반환합니다. |
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "CLOSED", "description": "The restaurant is closed." } ] } } } ] } } }
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "NO_CAPACITY", "description": "Sorry, the restaurant is busy at the moment." } ] } } } ] } } }
장바구니 항목 유효성 검사
처리 엔드포인트는 CheckoutRequestMessage
에 포함된 각 장바구니 항목의 가격 및 재고를 확인해야 합니다.
재고 또는 가격이 변경된 경우 처리에서 반환한 CheckoutResponseMessage
에 각각 오류 유형 AVAILABILITY_CHANGED
또는 PRICE_CHANGED
의 FoodOrderError
가 포함되어야 합니다.
사용 사례 | 구현 방법 |
---|---|
사용 사례 1: 일부 메뉴 항목 또는 맞춤설정이 유효하지 않거나 재고가 없어 검증에 실패함 | 오류 유형 AVAILABILITY_CHANGED 의 correctedProposedOrder , PaymentOptions , FoodOrderError 가 포함된 FoodErrorExtension 를 반환합니다. 잘못된 항목은 CorrectedProposedOrder 에서 삭제해야 합니다. |
사용 사례 2: 일부 메뉴 항목 또는 맞춤설정이 유효하지 않거나 재고가 없어서 유효성 검사가 실패했습니다. 수정된 장바구니가 더 이상 최소 주문 금액 요구사항을 충족하지 않습니다. | 오류 유형 AVAILABILITY_CHANGED 및 REQUIREMENTS_NOT_MET 의 FoodOrderError 가 있는 FoodErrorExtension 를 반환합니다. |
사용 사례 3: 일부 메뉴 항목 또는 맞춤설정 가격이 변경되어 검증에 실패함 | 오류 유형 PRICE_CHANGED 의 correctedProposedOrder , PaymentOptions , FoodOrderError 가 포함된 FoodErrorExtension 를 반환합니다. 오래된 가격은 CorrectedProposedOrder 에서 업데이트해야 합니다. |
사용 사례 4: 일부 메뉴 항목 또는 맞춤설정 가격이 변경되어 유효성 검사가 실패했습니다. 수정된 장바구니가 더 이상 최소 주문 금액 요구사항을 충족하지 않음 | 오류 유형 PRICE_CHANGED 및 REQUIREMENTS_NOT_MET 의 FoodOrderError 가 있는 FoodErrorExtension 를 반환합니다. |
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "AVAILABILITY_CHANGED", "id": "sample_item_offer_id_1", "description": "The item is no longer available." }, { "error": "AVAILABILITY_CHANGED", "id": "sample_item_offer_id_2", "description": "The item is no longer available." } ], "correctedProposedOrder": { "id": "sample_corrected_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." }, { "error": "AVAILABILITY_CHANGED", "id": "cart_lineitem_id" "description": "cart_lineitem_id is no longer available." } ] } } } ] } } }
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "PRICE_CHANGED", "id": "sample_item_offer_id_1", "description": "The price has changed.", "updatedPrice": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } }, { "error": "PRICE_CHANGED", "id": "sample_item_offer_id_2", "description": "The price has changed.", "updatedPrice": { "currencyCode": "USD", "units": "8" } } ], "correctedProposedOrder": { "id": "sample_corrected_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Pita Chips", "type": "REGULAR", "id": "sample_item_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/offer/id1", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } }, "subLines": [ { "note": "Notes for this item." } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension", "options": [ { "id": "sample_addon_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1", "name": "Honey Mustard", "price": { "currencyCode": "USD" }, "quantity": 1 }, { "id": "sample_addon_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id2", "name": "BBQ Sauce", "price": { "currencyCode": "USD", "nanos": 500000000 }, "quantity": 1 } ] } }, { "name": "Chicken Shwarma Wrap", "type": "REGULAR", "id": "sample_item_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/offer/id2", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "8" } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." }, { "error": "PRICE_CHANGED", "id": "cart_lineitem_id" "description": "cart_lineitem_id price has been updated." "updatedPrice": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } } ] } } } ] } } }
주문 검증 제출
7단계: 주문 제출 구현에서 설명한 대로 처리 엔드포인트는 들어오는 모든 SubmitOrderRequestMessage
에 대해 유효성 검사를 실행하고 SubmitOrderResponseMessage
으로 응답해야 합니다.
다음은 유효성 검사에 성공한 SubmitOrderResponseMessage
의 예입니다.
사용 사례 | 구현 방법 |
---|---|
사용 사례 1: 주문이 생성됨 | CREATED 주문 상태가 있는 SubmitOrderResponseMessage actionOrderId , userVisibleId , orderManagementActions , estimatedFulfillmentTime 가 있어야 합니다. |
사용 사례 2: 결제 문제로 인해 주문이 거부됨 | REJECTED 주문 상태가 있는 SubmitOrderResponseMessage PAYMENT_DECLINED 유형의 actionOrderId , userVisibleId , orderManagementActions , rejectionInfo 가 있어야 합니다. |
사용 사례 3: 사용자가 차단된 것으로 신고되어 주문이 거부됨 | REJECTED 주문 상태가 있는 SubmitOrderResponseMessage INELIGIBLE 유형의 actionOrderId , userVisibleId , orderManagementActions , rejectionInfo 가 있어야 합니다. |
사용 사례 4: 사용자 정보가 불완전하거나 잘못되어 주문이 거부됨 | REJECTED 주문 상태가 있는 SubmitOrderResponseMessage INELIGIBLE 유형의 actionOrderId , userVisibleId , orderManagementActions , rejectionInfo 가 있어야 합니다. |
사용 사례 5: 알 수 없는 이유로 주문이 거부됨 | REJECTED 주문 상태가 있는 SubmitOrderResponseMessage UNKNOWN 유형의 actionOrderId , userVisibleId , orderManagementActions , rejectionInfo 가 있어야 합니다. |
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "CREATED", "label": "Order received" }, "updateTime": "2017-05-10T02:30:00.000Z", "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "PAYMENT_DECLINED", "reason": "Insufficient funds" }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "INELIGIBLE", "reason": "Sorry, we are not able to take orders from this user" }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "INELIGIBLE", "reason": "Sorry, the phone number must not be blank" }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "UNKNOWN", "reason": "Sorry, there is something wrong with this order." }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }