비동기 주문 업데이트

고객이 음식 주문을 제출한 후 주문 업데이트 메시지를 주문 엔드 투 엔드 서비스로 전송하여 변경사항을 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여야 합니다.

예상 처리 시간 제공

사용자에게 주문 제품을 수령 (또는 배송)할 수 있는 예상 기간을 제공할 수 있습니다. FoodOrderUpdateExtensionestimatedFulfillmentTimeIso8601 필드를 사용하여 고객의 주문 제품을 수령하거나 배송할 수 있는 예상 시간 범위를 제공합니다.

다음 시간에 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"
      }
    }
  }
}
    

배송 중

이 예에서는 사용자에게 예상 배송일과 함께 주문이 배송 중임을 알리는 샘플 주문 업데이트 요청을 보여줍니다.

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

FULFILLED

이 예에서는 주문 제품이 수령 또는 배송되었음을 사용자에게 알리는 샘플 주문 업데이트 요청을 보여줍니다.

{
  "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"
            }
          }
        }
      ]
    }
  }
}
    

다양한 사용 사례에서 주문 업데이트 요청의 예시를 보려면 고급 주문 업데이트 구현을 참고하세요.

승인 토큰 생성 및 메시지 전송

주문 업데이트에는 엔드 투 엔드 순서 지정 서비스에서 메시지가 엔드 투 엔드 순서 지정 웹 서비스에서 전송된 것인지 확인할 수 있도록 승인 토큰이 필요합니다.

프로젝트에 순서 업데이트를 구현하려면 다음 단계를 따르세요.

  1. 다음 단계에 따라 승인 토큰을 생성합니다.
    1. Google 인증 라이브러리를 사용하여 서비스 계정 파일에서 사용자 인증 정보를 읽습니다.
    2. 다음 API 범위를 사용하여 토큰을 요청합니다. https://www.googleapis.com/auth/actions.fulfillment.conversation
  2. 이 토큰을 사용하여 다음 엔드포인트에 인증된 HTTP POST 요청을 보냅니다. https://actions.googleapis.com/v2/conversations:send
  3. 요청의 일부로 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용 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에서 오류를 반환합니다.