고객이 음식 주문을 제출하면 주문 엔드 투 엔드 서비스에 주문 업데이트 메시지를 전송하여 변경사항을 Google에 알릴 수 있습니다.
주문 업데이트를 전송하는 일반적인 이유는 다음과 같습니다.
- 주문이 가능해지거나 변경될 것으로 예상되는 주문 처리 시간입니다.
- 주문 상태가 변경됩니다.
- 주문을 더 이상 처리할 수 없습니다.
- 주문에 포함된 메뉴 항목의 가격이 변경됨
- 고객은 고객 지원팀 또는 음식점 전화번호와 같은 새로운 방식으로 주문을 관리할 수 있습니다.
- 주문 영수증이 제공됩니다.
다음 섹션에서는 주문 업데이트를 사용하여 이러한 다양한 시나리오를 해결하는 방법을 자세히 설명합니다.
주문 상태 전환
주문에는 6가지 상태가 있을 수 있습니다. 이러한 상태와 가능한 전환은 다음 다이어그램에 요약되어 있습니다.
고객이 처음 주문을 제출하면 주문이 CREATED
, CONFIRMED
또는 REJECTED
상태로 시작됩니다. 상태 전환이 유효하면 주문 업데이트 메시지를 전송하여 주문 상태를 업데이트할 수 있습니다. CREATED
상태는 파트너의 플랫폼이 주문을 즉시 확인하거나 거부할 수 없는 경우에
사용됩니다. 사용 사례 예시는 고객이 배송 애그리게이터를 통해 주문하는 경우입니다. 배달 애그리게이터가 Google로부터 배달 음식을 수신하고 이 정보를 식당으로 보냅니다. 음식점에서 주문 가능 여부를
수신하고 확인한 후에는 상태가 CONFIRMED
일 수 있고, 그렇지 않은 경우에는
REJECTED
일 수 있습니다.
CONFIRMED
상태의 주문은 다음으로 IN_PREPARATION
상태로 전환됩니다. 주문 상품이 수령인지 배달인지에 따라 다음으로 READY_FOR_PICKUP
또는 IN_TRANSIT
상태를 사용합니다. 음식이 배달 또는 수령되면 주문이 FULFILLED
상태로 설정됩니다.
고객이 주문을 취소할 수 있도록 허용하는 경우 CANCELLED
상태를 사용할 수 있습니다. CREATED
, CONFIRMED
, IN_PREPARATION
, READY_FOR_PICKUP
또는 IN_TRANSIT
상태에서 주문을 취소할 수 있습니다.
주문 엔드 투 엔드 서비스는 취소 정책 및 취소 시점의 결제 상태에 따라 환불을 처리해야 합니다.
주문 엔드 투 엔드 서비스가 사용 가능한 모든 상태 및 전환을 지원할 필요는 없습니다. 하지만 주문의 최종 상태는 FULFILLED
, REJECTED
또는 CANCELLED
여야 합니다.
예상 처리 시간 제공
주문 상품을 수령 (또는 배달)할 수 있는 예상 기간을 사용자에게 제공할 수 있습니다. FoodOrderUpdateExtension
의 estimatedFulfillmentTimeIso8601
필드를
사용하여 고객의 주문 상품을 수령하거나 배달할 수 있는
예상 시간 범위를 제공합니다.
다음 경우에 estimatedFulfillmentTimeIso8601
를 전송합니다.
- 예상 시간을 사용할 수 있게 되면
CREATED
또는CONFIRMED
상태인 것이 이상적입니다. - 예상 시간이 변경되는 경우(예: 주문이
IN_TRANSIT
일 때 예상 시간을 더 정확하게 업데이트)
사용자 기대치를 효과적으로 관리하려면 추정치에 보수적인 값을 제시하고 고정된 날짜와 시간이 아닌 날짜 및 시간 범위를 제공하세요. 가능하면 교통 상황과 같은 변화를 고려해야 합니다. 예를 들어 예상 배송 시간이 오후 1시인 주문의 경우 오후 12시 45분 (하한값)에서 오후 1시 15분 (상한값)까지의 예상 시간을 전송할 수 있습니다.
주문 관리 작업 제공
주문 업데이트를 전송할 때 OrderManagementAction
형식으로 주문을 관리하는 데 도움이 되는 리소스를 고객에게 제공할 수 있습니다. 고객은
주문 후 진행 상황을 추적하거나, 변경하거나, 주문을 취소하기 위해
판매자 또는 주문을 처리하는 음식점에 문의해야 할 수 있습니다.
OrderManagementAction
를 사용하면 고객이 기기에서 직접 이메일을 보내거나 전화를 걸거나 URL에 연결할 수 있습니다. 사용자에게 전송하는 이메일 주문 확인에 있는 것과 동일한 정보를 OrderManagementAction
에 사용합니다.
주문 관리 작업에는 다음 유형이 포함됩니다.
CUSTOMER_SERVICE
: 고객에게 고객 서비스에 연락할 수 있는 작업을 제공합니다. 이 관리 작업 유형은 주문 업데이트에 필수입니다.EMAIL
: 고객에게 제공된 이메일 주소로 이메일을 보내는 작업을 제공합니다.CALL
: 고객에게 제공된 전화번호로 전화를 걸 수 있는 액션을 제공합니다.VIEW_DETAIL
: 고객에게 주문 세부정보를 볼 수 있는 작업을 제공합니다.
각 주문 업데이트에는 하나 이상의 주문 관리 작업이 포함되어야 합니다. 하지만
제공된 주문 관리 작업은 주문 상태에 따라 다를 수 있습니다.
예를 들어 주문이 CONFIRMED
상태인 경우 CUSTOMER_SERVICE
작업이 고객 서비스 전화번호를 가리킬 수 있습니다. 주문 상태가 IN_TRANSIT
로 업데이트되면 CUSTOMER_SERVICE
작업이 처리 레스토랑의 전화번호를 가리킬 수 있습니다.
주문 업데이트 전송
AsyncOrderUpdateRequestMessage
메시지 유형을 사용하여 주문 엔드 투 엔드 서비스에 주문 업데이트를 보냅니다. Google은 AsyncOrderUpdateResponseMessage
로 응답합니다. 예를 들어 고객에게 주문이 유효하고 수락되었음을 알리려면 AsyncOrderUpdateRequestMessage
를 전송하여 Accepted by restaurant
라벨을 사용하여 주문 상태를 CONFIRMED
로 변경하면 됩니다.
주문 업데이트 메시지 설정
Google에 AsyncOrderUpdateRequestMessage
를 전송할 때 OrderUpdate
필드를 사용하여
주문 상태에 대한 정보를 포함해야 합니다.
다음 예는 각 주문 상태의 AsyncOrderUpdateRequestMessage
샘플을 보여줍니다.
확정됨
이 예시는 영수증과 예상 배송 기간으로 주문이 확인되었음을 사용자에게 알리는 샘플 주문 업데이트 요청을 보여줍니다.
{ "isInSandbox": true, "customPushMessage": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "CONFIRMED", "label": "Provider confirmed" }, "receipt": { "userVisibleOrderId": "userVisibleId1234" }, "updateTime": "2017-07-17T12:00:00Z", "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_RESTAURANT", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "infoExtension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension", "estimatedFulfillmentTimeIso8601": "2017-07-17T13:00:00Z/2017-07-17T13:30:00Z" } } } }
거부됨
이 예시에서는 사용자에게 거부 사유와 함께 주문이 거부되었음을 알리는 주문 업데이트 요청 샘플을 보여줍니다.
{ "isInSandbox": true, "customPushMessage": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "UNKNOWN", "reason": "Sorry, the restaurant cannot take your order right now." }, "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_RESTAURANT", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "infoExtension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension", "foodOrderErrors": [ { "error": "NO_CAPACITY", "description": "Sorry, the restaurant cannot take your order right now." } ] } } } }
CANCELLED
이 예에서는 사용자에게 취소 사유와 함께 주문이 취소되었음을 알리는 샘플 주문 업데이트 요청을 보여줍니다.
{ "isInSandbox": true, "customPushMessage": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "CANCELLED", "label": "Order cancelled" }, "updateTime": "2017-05-10T02:30:00.000Z", "cancellationInfo": { "reason": "Customer requested" }, "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_RESTAURANT", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ] } } }
IN_PREPARATION
이 예는 사용자에게 음식이 현재 준비 중임을 알리는 샘플 주문 업데이트 요청을 보여줍니다.
{ "isInSandbox":true, "customPushMessage":{ "orderUpdate":{ "actionOrderId":"sample_action_order_id", "orderState":{ "state":"IN_PREPARATION", "label":"Order is being prepared" }, "receipt": { "userVisibleOrderId": "userVisibleId1234" }, "updateTime":"2018-04-15T11:30:00Z", "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_RESTAURANT", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "infoExtension":{ "@type":"type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension", "estimatedFulfillmentTimeIso8601":"PT20M" } } } }
READY_FOR_PICKUP
이 예는 사용자에게 음식 수령이 준비되었음을 알리는 샘플 주문 업데이트 요청을 보여줍니다.
{ "isInSandbox": true, "customPushMessage": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "READY_FOR_PICKUP", "label": "Order is ready for pickup" }, "receipt": { "userVisibleOrderId": "userVisibleId1234" }, "updateTime": "2018-04-15T12:00:00Z", "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_RESTAURANT", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "infoExtension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension", "estimatedFulfillmentTimeIso8601": "PT20M" } } } }
IN_TRANSIT
이 예에서는 사용자에게 주문 상품이 예상 배송 시간과 함께 배송 중임을 알리는 샘플 주문 업데이트 요청을 보여줍니다.
{ "isInSandbox": true, "customPushMessage": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "IN_TRANSIT", "label": "Order is on the way" }, "inTransitInfo": { "updatedTime": "2017-07-17T12:00:00Z" }, "updateTime": "2017-07-17T12:00:00Z", "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_RESTAURANT", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "infoExtension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension", "estimatedFulfillmentTimeIso8601": "PT20M" } } } }
처리됨
이 예시는 사용자에게 주문 상품이 수령 또는 배송되었음을 알리는 샘플 주문 업데이트 요청을 보여줍니다.
{ "isInSandbox": true, "customPushMessage": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "FULFILLED", "label": "Order delivered" }, "updateTime": "2017-05-10T02:30:00.000Z", "fulfillmentInfo": { "deliveryTime": "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_RESTAURANT", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ] } } }
다양한 사용 사례의 주문 업데이트 요청에 관한 자세한 예는 고급 주문 업데이트 구현을 참고하세요.
승인 토큰 생성 및 메시지 전송
주문 업데이트에는 주문 엔드 투 엔드 서비스에서 메시지가 주문 엔드 투 엔드 웹 서비스에서 전송된 메시지임을 확인할 수 있도록 승인 토큰이 필요합니다.
프로젝트의 주문 업데이트를 구현하려면 다음 단계를 따르세요.
- 다음 단계에 따라 승인 토큰을 생성합니다.
- Google 인증 라이브러리를 사용하여 서비스 계정 파일에서 사용자 인증 정보를 읽습니다.
- 다음 API 범위를 사용하는 요청 토큰:
https://www.googleapis.com/auth/actions.fulfillment.conversation
- 이 토큰을 사용하여 인증된 HTTP POST 요청을
https://actions.googleapis.com/v2/conversations:send
엔드포인트로 보냅니다. - 요청의 일부로
Content-Type
헤더를application/json
로 설정합니다.
다음 예는 주문 업데이트를 구현하는 방법을 보여줍니다.
Node.js
이 코드는 Node.js용 Google 인증 라이브러리를 사용합니다.
const {auth} = require('google-auth-library') const request = require('request'); // The service account client secret file downloaded from the Google Cloud Console const serviceAccountJson = require('./service-account.json') // order-update.json is a file that contains the payload const jsonBody = require('./order-update.json') /** * Get the authorization token using a service account. */ async function getAuthToken() { let client = auth.fromJSON(serviceAccountJson) client.scopes = ['https://www.googleapis.com/auth/actions.fulfillment.conversation'] const tokens = await client.authorize() return tokens.access_token; } /** * Send an order update request */ async function sendOrderUpdate() { const token = await getAuthToken() request.post({ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, url: 'https://actions.googleapis.com/v2/conversations:send', body: jsonBody, json: true }, (err, res, body) => { if (err) { return console.log(err); } console.log(`Response: ${JSON.stringify(res)}`) }) }
Python
이 코드는 Python용 Google 인증 라이브러리를 사용합니다.
from google.oauth2 import service_account from google.auth.transport.requests import AuthorizedSession import json # service-account.json is the service account client secret file downloaded from the # Google Cloud Console credentials = service_account.Credentials.from_service_account_file( 'service-account.json') scoped_credentials = credentials.with_scopes( ['https://www.googleapis.com/auth/actions.fulfillment.conversation']) authed_session = AuthorizedSession(scoped_credentials) # order-update.json is a file that contains the payload json_payload=json.load(open('order-update.json')) response = authed_session.post( 'https://actions.googleapis.com/v2/conversations:send', json=json_payload)
Java
이 코드는 Java용 Google 인증 라이브러리를 사용합니다.
/** * Get the authorization token using a service account. */ private static String getAuthToken() { InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json"); ServiceAccountCredentials.Builder credentialsSimpleBuilder = ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder(); credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/actions.fulfillment.conversation")); AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken(); return accessToken.getTokenValue(); } /** * Send an order update request */ public void sendOrderUpdate() { String authToken = getAuthToken(); // Execute POST request executePostRequest("https://actions.googleapis.com/v2/conversations:send", authToken, "update_order_example.json",); }
오류 없이 주문 업데이트가 완료되면 Google은 페이로드가 비어 있는 HTTP 200 응답을 반환합니다. 업데이트 형식이 잘못된 경우와 같은 문제가 있으면 Google에서 오류를 반환합니다.