비동기 순서 업데이트

고객이 음식 주문을 제출한 후 Order with Google 서비스로 주문 업데이트 메시지를 보내 변경사항을 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 상태에서 주문을 취소할 수 있습니다. Order with Google 서비스는 취소 정책 및 취소 시점의 결제 상태에 따라 환불을 처리해야 합니다.

Order with Google 서비스는 사용 가능한 모든 상태 및 전환을 지원하지 않아도 됩니다. 하지만 주문의 최종 상태는 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 메시지 유형을 사용하여 Order with Google 서비스로 주문 업데이트를 보냅니다. 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"
            }
          }
        }
      ]
    }
  }
}
    

비준비

이 예시에서는 음식이 현재 준비 중임을 사용자에게 알리는 샘플 주문 업데이트 요청을 보여줍니다.

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

PIPKIP 대비

이 예에서는 음식 수령이 가능하다고 사용자에게 알리는 샘플 주문 업데이트 요청을 보여줍니다.

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

처리됨

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

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

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

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

Order를 업데이트하려면 Google을 통한 주문 서비스가 메시지가 Order with Google 웹 서비스에서 보낸 것인지 확인할 수 있도록 승인 토큰이 필요합니다.

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

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