이 가이드에서는 사이트에서 관리하는 결제 수단을 사용하여 실제 상품에 대한 거래를 통합하는 작업 프로젝트를 개발하는 과정을 안내합니다.
거래 흐름
작업 프로젝트가 판매자 관리 결제를 사용해 실제 거래를 처리하는 경우 다음과 같은 흐름을 사용합니다.
- 정보 수집 (선택사항) - 정보의 성격에 따라
거래를 시작할 때 사용자로부터 다음과 같은 정보를 수집하는 것이 좋습니다.
대화:
<ph type="x-smartling-placeholder">
- </ph>
- 거래 요구사항 확인 - 거래 요구사항 도우미를 사용합니다. 대화를 시작할 때 사용자의 결제 정보가 사용자가 장바구니를 만들기 전에 올바르게 구성되어 사용할 수 있습니다
- 배송지 주소 요청: 배송이 필요한 거래인 경우 주소, 배송지 주소 도우미 인텐트의 처리 요청 사용자로부터 하나를 수집합니다.
- 주문하기 - 사용자에게 '장바구니 조립' 여기에서 구매할 상품을 선택할 수 있습니다.
- 계정 연결 수행: 사용자가 결제를 사용하도록 합니다. 방법인 경우 계정 연결을 사용하세요 Google 계정을 서비스상의 계정과 연결할 수 있습니다.
- 주문 제안 - 장바구니가 완료되면 다음을 위해 주문을 제안합니다. 정답을 확인할 수 있습니다. 주문이 확인되면 주문 세부정보와 결제 토큰이 포함된 응답을 받습니다.
- 주문 완료 및 영수증 보내기 - 주문이 확인되면 인벤토리 추적 또는 기타 처리 서비스에서 영수증을 보내면 표시됩니다.
- 주문 업데이트 전송: 주문 처리 수명 동안 주문에 PATCH 요청을 전송하여 사용자에게 주문 업데이트 제공 API에 액세스할 수 있습니다.
제한사항 및 검토 가이드라인
거래가 있는 작업에는 추가 정책이 적용됩니다. 거래가 포함된 작업을 검토하는 데 최대 6주가 걸릴 수 있으므로 출시 일정을 계획할 때 이 시간을 고려하세요. 원활한 검토 절차를 위해 작업을 제출하여 검토받기 전에 거래 정책 및 가이드라인을 준수하는지 확인하세요.
다음 국가에서만 실제 상품을 판매하는 작업을 배포할 수 있습니다.
오스트레일리아 브라질 캐나다 인도네시아 |
일본 멕시코 카타르 러시아 |
싱가포르 스위스 태국 터키 영국 미국 |
프로젝트 빌드
트랜잭션 대화의 광범위한 예는 Node.js 및 자바의 트랜잭션 샘플을 참고하세요.
프로젝트 설정
작업을 만들 때 트랜잭션 수행 여부를 지정해야 합니다. Actions 콘솔을 참조하세요. 또한 Node.JS 클라이언트 라이브러리를 사용하여 최신 API v2를 사용하도록 처리를 Orders API 버전입니다.
프로젝트 및 처리를 설정하려면 다음을 수행합니다.
- 새 프로젝트를 만들거나 기존 프로젝트를 가져옵니다.
- 배포 > 디렉터리 정보.
추가 정보 > 거래 > '작업 수행' 체크박스를 선택합니다. Transactions API를 사용하여 실제 상품의 거래를 할 수 있나요?"라는 질문이 포함됩니다.
Node.JS 클라이언트 라이브러리를 사용하여 작업의 처리를 빌드하는 경우 처리 코드를 열고 앱 선언을 업데이트하여
ordersv3
플래그를true
로 설정합니다. 다음 코드 스니펫은 예시 앱을 보여줍니다. 선언을 사용하세요.
Node.js
const {dialogflow} = require('actions-on-google'); let app = dialogflow({ clientId, // If using account linking debug: true, ordersv3: true, });
Node.js
const {actionssdk} = require('actions-on-google'); let app = actionssdk({ clientId, // If using account linking debug: true, ordersv3: true, });
로그인 설정
자체 결제 수단을 사용하여 사용자에게 청구하는 경우 Google 계정을 자체 서비스에 연결된 계정과 연결하여 저장된 결제 수단을 검색하고 제시하고 청구할 수 있습니다.
Google에서는 OAuth 2.0 계정 연결을 제공합니다. 이를 충족하려면 요구사항을 충족해야 합니다 OAuth 2.0 어설션 흐름을 사용하도록 설정하는 것이 좋습니다. 매우 간소화된 사용자 환경을 제공합니다.
Google에서 제공하는 actions.intent.SIGN_IN
인텐트를 사용하면 사용자가
대화 중에 사용자 계정을 연결하는 것입니다. 다음을 수행해야 합니다.
계정 연결 사용 설정
Actions 콘솔에서
actions.intent.SIGN_IN
인텐트에 대한 응답입니다.
accessToken
를 찾을 수 없는 경우 이 인텐트를 사용해야 합니다.
User
객체를 반환합니다. 사용자가 아직
계정을 연결했습니다.
actions.intent.SIGN_IN
인텐트를 요청한 후 다음을 수신합니다.
값이 "OK"
인 SignInStatus
를 포함하는 Argument
"CANCELLED"
또는 "ERROR"
입니다. 상태가 "OK"
인 경우 다음을 찾을 수 있습니다.
User
객체의 accessToken
처리
로그인 요청
Node.js
app.intent('Sign In', (conv) => { conv.ask(new SignIn('To get your account details')); });
Node.js
conv.ask(new SignIn('To get your account details'));
자바
@ForIntent("Sign In") public ActionResponse signIn(ActionRequest request) { return getResponseBuilder(request).add( new SignIn() .setContext("To get your account details")) .build(); }
자바
return getResponseBuilder(request).add( new SignIn() .setContext("To get your account details")) .build();
JSON
{ "payload": { "google": { "expectUserResponse": true, "systemIntent": { "intent": "actions.intent.SIGN_IN", "data": { "@type": "type.googleapis.com/google.actions.v2.SignInValueSpec", "optContext": "To get your account details" } } } } }
JSON
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.SIGN_IN", "inputValueData": { "@type": "type.googleapis.com/google.actions.v2.SignInValueSpec", "optContext": "To get your account details" } } ] } ], "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\"}}" }
로그인 결과 수신
Node.js
app.intent('Sign In Complete', (conv, params, signin) => { if (signin.status !== 'OK') { conv.ask('You need to sign in before making a transaction.'); } else { const accessToken = conv.user.access.token; // possibly do something with access token conv.ask('You must meet all the requirements necessary ' + 'to make a transaction. Try saying ' + '"check transaction requirements".'); conv.ask(new Suggestions(`check requirements`)); } });
Node.js
app.intent('actions.intent.SIGN_IN', (conv, params, signin) => { if (signin.status !== 'OK') { conv.ask('You need to sign in before making a transaction.'); } else { const accessToken = conv.user.access.token; // possibly do something with access token conv.ask('You must meet all the requirements necessary ' + 'to make a transaction. Try saying ' + '"check transaction requirements".'); conv.ask(new Suggestions(`check requirements`)); } });
자바
@ForIntent("Sign In Complete") public ActionResponse signInComplete(ActionRequest request) { ResponseBuilder responseBuilder = getResponseBuilder(request); if (request.isSignInGranted()) { responseBuilder .add("You must meet all the requirements necessary to make a " + "transaction. Try saying \"check transaction requirements\".") .addSuggestions(new String[] { "check requirements" }); } else { responseBuilder.add("You need to sign in before making a transaction."); } return responseBuilder.build(); }
자바
@ForIntent("actions.intent.SIGN_IN") public ActionResponse signInComplete(ActionRequest request) { ResponseBuilder responseBuilder = getResponseBuilder(request); if (request.isSignInGranted()) { responseBuilder .add("You must meet all the requirements necessary to make a " + "transaction. Try saying \"check transaction requirements\".") .addSuggestions(new String[] { "check requirements" }); } else { responseBuilder.add("You need to sign in before making a transaction."); } return responseBuilder.build(); }
JSON
{ "responseId": "d2123d8d-3f00-466e-b5a9-1a4ed53a7cb7-594de0a7", "queryResult": { "queryText": "actions_intent_SIGN_IN", "parameters": {}, "allRequiredParamsPresent": true, "fulfillmentMessages": [ { "text": { "text": [ "" ] } } ], "outputContexts": [ { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/merchant_payment", "lifespanCount": 2 }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_web_browser" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_screen_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_account_linking" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_media_response_audio" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_audio_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/google_assistant_input_type_keyboard" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_intent_sign_in", "parameters": { "SIGN_IN": { "@type": "type.googleapis.com/google.actions.v2.SignInValue", "status": "OK" }, "text": "" } } ], "intent": { "name": "projects/df-transactions/agent/intents/105b925b-b186-4f5d-8bde-a9a782a0fa9f", "displayName": "Sign In Complete" }, "intentDetectionConfidence": 1, "languageCode": "en" }, "originalDetectIntentRequest": { "source": "google", "version": "2", "payload": { "user": { "locale": "en-US", "lastSeen": "2019-09-23T19:49:18Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy", "type": "ACTIVE", "conversationToken": "[]" }, "inputs": [ { "intent": "actions.intent.SIGN_IN", "rawInputs": [ { "inputType": "KEYBOARD" } ], "arguments": [ { "name": "SIGN_IN", "extension": { "@type": "type.googleapis.com/google.actions.v2.SignInValue", "status": "OK" } }, { "name": "text" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" }, { "name": "actions.capability.AUDIO_OUTPUT" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.SCREEN_OUTPUT" } ] } ] } }, "session": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy" }
JSON
{ "user": { "locale": "en-US", "lastSeen": "2019-11-11T23:55:52Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGFwStZlYaQ9YT8rg9t_idVsxZrku1pUDrEbGSJmSUMatVdPwPEEQSCe1IwIBoN4sS4Weyn9pmgetEgbsWgw3JSvQmw", "type": "ACTIVE", "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\"}}" }, "inputs": [ { "intent": "actions.intent.SIGN_IN", "rawInputs": [ {} ], "arguments": [ { "name": "SIGN_IN", "extension": { "@type": "type.googleapis.com/google.actions.v2.SignInValue", "status": "OK" } }, { "name": "text" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" } ] } ] }
1. 정보 수집(선택사항)
1a. 거래 요건 확인(선택사항)
사용자 환경
다음과 같이 actions.intent.TRANSACTION_REQUIREMENTS_CHECK
인텐트를 트리거합니다.
사용자가 거래를 수행할 수 있는지 여부를 신속하게 확인할 수 있습니다. 이
단계를 따르면 사용자가 계속 진행할 수 있으며 설정을 수정할 수 있습니다.
거래를 처리하는 것을 막을 수 있습니다.
예를 들어 작업이 호출되면 "신발을 주문하시겠어요, 아니면 계정 잔액을 확인하시겠어요?" 사용자가 다음과 같이 말하는 경우 '신발 주문'을 찾는 경우 이 인텐트를 즉시 요청해야 합니다. 이렇게 하면 계속 진행하여 설정을 수정할 기회를 줄 수 있습니다. 거래를 계속할 수 없게 합니다.
거래 요청 다음 결과 중 하나가 발생합니다.
- 요구사항이 충족되면 인텐트는 다음 예와 함께 처리로 다시 전송됩니다. 성공 조건이 충족되면 사용자의 주문 작성을 진행할 수 있습니다.
- 하나 이상의 요구사항을 충족할 수 없는 경우 인텐트가 다시 전송됩니다.
추가해야 합니다 이 경우
대화에서 벗어나거나 대화를 종료하는 것이 중요합니다.
- 장애 상태를 초래하는 오류를 사용자가 수정할 수 있는 경우 기기에서 이러한 문제를 해결하라는 메시지가 표시됩니다. 만약 음성 전용 공간에서만 이루어지는 대화입니다. 사용자의 휴대전화로 핸드오프가 시작됩니다.
처리
사용자가
거래 요구사항,
actions.intent.TRANSACTION_REQUIREMENTS_CHECK
인텐트에
TransactionRequirementsCheckSpec 객체를 확인합니다.
요건 확인하기
다음을 사용하여 사용자가 거래 요구사항을 충족하는지 확인할 수 있습니다. 클라이언트 라이브러리:
Node.js
conv.ask(new TransactionRequirements());
Node.js
conv.ask(new TransactionRequirements());
자바
return getResponseBuilder(request) .add(new TransactionRequirements()) .build();
자바
return getResponseBuilder(request) .add(new TransactionRequirements()) .build();
JSON
{ "payload": { "google": { "expectUserResponse": true, "systemIntent": { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "data": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec" } } } } }
JSON
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.TEXT" } ], "inputPrompt": { "richInitialPrompt": { "items": [ { "simpleResponse": { "textToSpeech": "Looks like you're good to go! Next I'll need your delivery address.Try saying \"get delivery address\"." } } ], "suggestions": [ { "title": "get delivery address" } ] } } } ], "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\"}}" }
요구사항 확인 결과 수신
어시스턴트는 인텐트를 처리한 후 처리를 통해 요청을 보냅니다.
actions.intent.TRANSACTION_REQUIREMENTS_CHECK
인텐트와 결과 포함
확인합니다. 이 요청을 올바르게 처리하려면 Dialogflow 인텐트를 선언합니다.
actions_intent_TRANSACTION_REQUIREMENTS_CHECK
이벤트에 의해 트리거됩니다. 트리거되면
처리에서 처리해야 합니다.
Node.js
app.intent('Transaction Check Complete', (conv) => { const arg = conv.arguments.get('TRANSACTION_REQUIREMENTS_CHECK_RESULT'); if (arg && arg.resultType === 'CAN_TRANSACT') { // Normally take the user through cart building flow conv.ask(`Looks like you're good to go! ` + `Next I'll need your delivery address.` + `Try saying "get delivery address".`); conv.ask(new Suggestions('get delivery address')); } else { // Exit conversation conv.close('Transaction failed.'); } });
Node.js
app.intent('actions.intent.TRANSACTION_REQUIREMENTS_CHECK', (conv) => { const arg = conv.arguments.get('TRANSACTION_REQUIREMENTS_CHECK_RESULT'); if (arg && arg.resultType === 'CAN_TRANSACT') { // Normally take the user through cart building flow conv.ask(`Looks like you're good to go! ` + `Next I'll need your delivery address.` + `Try saying "get delivery address".`); conv.ask(new Suggestions('get delivery address')); } else { // Exit conversation conv.close('Transaction failed.'); } });
자바
@ForIntent("Transaction Check Complete") public ActionResponse transactionCheckComplete(ActionRequest request) { LOGGER.info("Checking Transaction Requirements Result."); // Check result of transaction requirements check Argument transactionCheckResult = request .getArgument("TRANSACTION_REQUIREMENTS_CHECK_RESULT"); boolean result = false; if (transactionCheckResult != null) { Map<String, Object> map = transactionCheckResult.getExtension(); if (map != null) { String resultType = (String) map.get("resultType"); result = resultType != null && resultType.equals("CAN_TRANSACT"); } } ResponseBuilder responseBuilder = getResponseBuilder(request); if (result) { // Normally take the user through cart building flow responseBuilder .add("Looks like you're good to go! Next " + "I'll need your delivery address. Try saying " + "\"get delivery address\".") .addSuggestions(new String[]{"get delivery address"}); } else { // Exit conversation responseBuilder.add("Transaction failed."); } return responseBuilder.build(); }
자바
@ForIntent("actions.intent.TRANSACTION_REQUIREMENTS_CHECK") public ActionResponse transactionCheckComplete(ActionRequest request) { LOGGER.info("Checking Transaction Requirements Result."); // Check result of transaction requirements check Argument transactionCheckResult = request .getArgument("TRANSACTION_REQUIREMENTS_CHECK_RESULT"); boolean result = false; if (transactionCheckResult != null) { Map<String, Object> map = transactionCheckResult.getExtension(); if (map != null) { String resultType = (String) map.get("resultType"); result = resultType != null && resultType.equals("CAN_TRANSACT"); } } ResponseBuilder responseBuilder = getResponseBuilder(request); if (result) { // Normally take the user through cart building flow responseBuilder .add("Looks like you're good to go! Next " + "I'll need your delivery address. Try saying " + "\"get delivery address\".") .addSuggestions(new String[]{"get delivery address"}); } else { // Exit conversation responseBuilder.add("Transaction failed."); } return responseBuilder.build(); }
JSON
{ "responseId": "db1a333c-2781-41e3-84b1-cc0cc37643d7-594de0a7", "queryResult": { "queryText": "actions_intent_TRANSACTION_REQUIREMENTS_CHECK", "action": "transaction.check.complete", "parameters": {}, "allRequiredParamsPresent": true, "fulfillmentText": "Failed to get transaction check results", "fulfillmentMessages": [ { "text": { "text": [ "Failed to get transaction check results" ] } } ], "outputContexts": [ { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_media_response_audio" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_audio_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_account_linking" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_screen_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_web_browser" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/google_assistant_input_type_keyboard" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/merchant_payment", "lifespanCount": 1 }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_intent_transaction_requirements_check", "parameters": { "TRANSACTION_REQUIREMENTS_CHECK_RESULT": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckResult", "resultType": "CAN_TRANSACT" }, "text": "" } } ], "intent": { "name": "projects/df-transactions/agent/intents/fd16d86b-60db-4d19-a683-5b52a22f4795", "displayName": "Transaction Check Complete" }, "intentDetectionConfidence": 1, "languageCode": "en" }, "originalDetectIntentRequest": { "source": "google", "version": "2", "payload": { "user": { "locale": "en-US", "lastSeen": "2019-09-23T19:49:32Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy", "type": "ACTIVE", "conversationToken": "[\"merchant_payment\"]" }, "inputs": [ { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "rawInputs": [ { "inputType": "KEYBOARD" } ], "arguments": [ { "name": "TRANSACTION_REQUIREMENTS_CHECK_RESULT", "extension": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckResult", "resultType": "CAN_TRANSACT" } }, { "name": "text" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.SCREEN_OUTPUT" } ] } ] } }, "session": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy" }
JSON
{ "user": { "locale": "en-US", "lastSeen": "2019-11-11T23:56:03Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGFwStZlYaQ9YT8rg9t_idVsxZrku1pUDrEbGSJmSUMatVdPwPEEQSCe1IwIBoN4sS4Weyn9pmgetEgbsWgw3JSvQmw", "type": "ACTIVE", "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\"}}" }, "inputs": [ { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "rawInputs": [ {} ], "arguments": [ { "name": "TRANSACTION_REQUIREMENTS_CHECK_RESULT", "extension": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckResult", "resultType": "CAN_TRANSACT" } }, { "name": "text" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" } ] } ] }
1b. 배송지 주소 요청(선택사항)
거래에 사용자의 배송지 주소가 필요한 경우
actions.intent.DELIVERY_ADDRESS
인텐트 처리를 통해 구현됩니다. 이는
총가격, 배달/픽업 위치 또는
사용자가 서비스 리전 내에 있는지 확인합니다
이 인텐트의 처리를 요청할 때 reason
옵션을 전달합니다.
를 사용하면 주소를 가져오라는 어시스턴트의 요청을 문자열로 시작할 수 있습니다.
예를 들어 '주문을 보낼 위치를 알기'라고 지정하면 어시스턴트가
는 사용자에게 다음과 같이 질문할 수 있습니다.
"주문 상품을 어디로 배송해야 할지 확인하려면 배송지 주소를 확인해야 합니다."
사용자 환경
화면이 있는 표시 경로에서는 사용자가 사용할 주소를 선택함 입니다. 이전에 주소를 제공한 적이 없는 경우 새로운 주소를 입력할 수 있습니다.
음성 전용 표시 경로에서는 어시스턴트가 사용자에게 공유 권한을 요청합니다. 기본 주소가 표시됩니다. 이전에 액세스 권한을 부여한 적이 없다면 대화는 입력할 수 있도록 휴대전화로 전달됩니다.
주소 요청
Node.js
app.intent('Delivery Address', (conv) => { conv.ask(new DeliveryAddress({ addressOptions: { reason: 'To know where to send the order', }, })); });
Node.js
conv.ask(new DeliveryAddress({ addressOptions: { reason: 'To know where to send the order', }, }));
자바
@ForIntent("Delivery Address") public ActionResponse deliveryAddress(ActionRequest request) { DeliveryAddressValueSpecAddressOptions addressOptions = new DeliveryAddressValueSpecAddressOptions() .setReason("To know where to send the order"); return getResponseBuilder(request) .add(new DeliveryAddress() .setAddressOptions(addressOptions)) .build(); }
자바
DeliveryAddressValueSpecAddressOptions addressOptions = new DeliveryAddressValueSpecAddressOptions() .setReason("To know where to send the order"); return getResponseBuilder(request) .add(new DeliveryAddress() .setAddressOptions(addressOptions)) .build();
JSON
{ "payload": { "google": { "expectUserResponse": true, "systemIntent": { "intent": "actions.intent.DELIVERY_ADDRESS", "data": { "@type": "type.googleapis.com/google.actions.v2.DeliveryAddressValueSpec", "addressOptions": { "reason": "To know where to send the order" } } } } } }
JSON
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.DELIVERY_ADDRESS", "inputValueData": { "@type": "type.googleapis.com/google.actions.v2.DeliveryAddressValueSpec", "addressOptions": { "reason": "To know where to send the order" } } } ] } ], "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\"}}" }
주소 수신
어시스턴트는 인텐트를 처리한 후 처리를 통해 요청을 보냅니다.
actions.intent.DELIVERY_ADDRESS
인텐트로 요청을 처리합니다.
이 요청을 올바르게 처리하려면 다음에서 트리거한 Dialogflow 인텐트를 선언합니다.
actions_intent_DELIVERY_ADDRESS
이벤트를 전달합니다. 트리거되면
fulfillment를 사용할 수 있습니다.
Node.js
app.intent('Delivery Address Complete', (conv) => { const arg = conv.arguments.get('DELIVERY_ADDRESS_VALUE'); if (arg && arg.userDecision ==='ACCEPTED') { conv.data.location = arg.location; conv.ask('Great, got your address! Now say "confirm transaction".'); conv.ask(new Suggestions('confirm transaction')); } else { conv.close('Transaction failed.'); } });
Node.js
app.intent('actions.intent.DELIVERY_ADDRESS', (conv) => { const arg = conv.arguments.get('DELIVERY_ADDRESS_VALUE'); if (arg && arg.userDecision ==='ACCEPTED') { conv.data.location = arg.location; conv.ask('Great, got your address! Now say "confirm transaction".'); conv.ask(new Suggestions('confirm transaction')); } else { conv.close('Transaction failed.'); } });
자바
@ForIntent("Delivery Address Complete") public ActionResponse deliveryAddressComplete(ActionRequest request) { Argument deliveryAddressValue = request.getArgument("DELIVERY_ADDRESS_VALUE"); Location deliveryAddress = null; if (deliveryAddressValue != null) { Map<String, Object> map = deliveryAddressValue.getExtension(); if (map != null) { String userDecision = (String) map.get("userDecision"); Location location = (Location) map.get("location"); deliveryAddress = userDecision != null && userDecision.equals("ACCEPTED") ? location : null; } } ResponseBuilder responseBuilder = getResponseBuilder(request); if (deliveryAddress != null) { // Cache delivery address in conversation data for later use Map<String, Object> conversationData = request.getConversationData(); conversationData.put("location", GSON_BUILDER.create().toJson(deliveryAddress, Location.class)); responseBuilder .add("Great, got your address! Now say \"confirm transaction\".") .addSuggestions(new String[] { "confirm transaction" }); } else { responseBuilder.add("Transaction failed.").endConversation(); } return responseBuilder.build(); }
자바
@ForIntent("actions.intent.DELIVERY_ADDRESS") public ActionResponse deliveryAddressComplete(ActionRequest request) { Argument deliveryAddressValue = request.getArgument("DELIVERY_ADDRESS_VALUE"); Location deliveryAddress = null; if (deliveryAddressValue != null) { Map<String, Object> map = deliveryAddressValue.getExtension(); if (map != null) { String userDecision = (String) map.get("userDecision"); Location location = (Location) map.get("location"); deliveryAddress = userDecision != null && userDecision.equals("ACCEPTED") ? location : null; } } ResponseBuilder responseBuilder = getResponseBuilder(request); if (deliveryAddress != null) { // Cache delivery address in conversation data for later use Map<String, Object> conversationData = request.getConversationData(); conversationData.put("location", GSON_BUILDER.create().toJson(deliveryAddress, Location.class)); responseBuilder .add("Great, got your address! Now say \"confirm transaction\".") .addSuggestions(new String[] { "confirm transaction" }); } else { responseBuilder.add("Transaction failed.").endConversation(); } return responseBuilder.build(); }
JSON
{ "responseId": "58b0c305-b437-47ac-8593-4fb0122a19e6-594de0a7", "queryResult": { "queryText": "actions_intent_DELIVERY_ADDRESS", "parameters": {}, "allRequiredParamsPresent": true, "fulfillmentMessages": [ { "text": { "text": [ "" ] } } ], "outputContexts": [ { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_media_response_audio" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_audio_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_account_linking" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_screen_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_web_browser" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_intent_delivery_address", "parameters": { "DELIVERY_ADDRESS_VALUE": { "@type": "type.googleapis.com/google.actions.v2.DeliveryAddressValue", "userDecision": "ACCEPTED", "location": { "coordinates": { "latitude": 37.432524, "longitude": -122.098545 }, "zipCode": "94043-1351", "city": "MOUNTAIN VIEW", "postalAddress": { "regionCode": "US", "postalCode": "94043-1351", "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "recipients": [ "John Doe" ] }, "phoneNumber": "+1 123-456-7890" } }, "text": "1600 AMPHITHEATRE PKWY" } } ], "intent": { "name": "projects/df-transactions/agent/intents/0be5d130-1760-4355-85e9-4dc01da8bf3c", "displayName": "Delivery Address Complete" }, "intentDetectionConfidence": 1, "languageCode": "en" }, "originalDetectIntentRequest": { "source": "google", "version": "2", "payload": { "user": { "locale": "en-US", "lastSeen": "2019-09-23T19:49:55Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy", "type": "ACTIVE", "conversationToken": "[]" }, "inputs": [ { "intent": "actions.intent.DELIVERY_ADDRESS", "rawInputs": [ { "query": "1600 AMPHITHEATRE PKWY" } ], "arguments": [ { "name": "DELIVERY_ADDRESS_VALUE", "extension": { "@type": "type.googleapis.com/google.actions.v2.DeliveryAddressValue", "userDecision": "ACCEPTED", "location": { "coordinates": { "latitude": 37.432524, "longitude": -122.098545 }, "zipCode": "94043-1351", "city": "MOUNTAIN VIEW", "postalAddress": { "regionCode": "US", "postalCode": "94043-1351", "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "recipients": [ "John Doe" ] }, "phoneNumber": "+1 123-456-7890" } } } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" }, { "name": "actions.capability.AUDIO_OUTPUT" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.SCREEN_OUTPUT" } ] } ] } }, "session": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy" }
JSON
{ "user": { "locale": "en-US", "lastSeen": "2019-11-11T23:57:20Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGFwStZlYaQ9YT8rg9t_idVsxZrku1pUDrEbGSJmSUMatVdPwPEEQSCe1IwIBoN4sS4Weyn9pmgetEgbsWgw3JSvQmw", "type": "ACTIVE", "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\"}}" }, "inputs": [ { "intent": "actions.intent.DELIVERY_ADDRESS", "rawInputs": [ { "inputType": "VOICE", "query": "1600 AMPHITHEATRE PKWY" } ], "arguments": [ { "name": "DELIVERY_ADDRESS_VALUE", "extension": { "@type": "type.googleapis.com/google.actions.v2.DeliveryAddressValue", "userDecision": "ACCEPTED", "location": { "coordinates": { "latitude": 37.421578499999995, "longitude": -122.0837816 }, "zipCode": "94043-1351", "city": "MOUNTAIN VIEW", "postalAddress": { "regionCode": "US", "postalCode": "94043-1351", "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "recipients": [ "John Doe" ] }, "phoneNumber": "+1 123-456-7890" } } }, { "name": "text", "rawText": "1600 AMPHITHEATRE PKWY", "textValue": "1600 AMPHITHEATRE PKWY" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" }, { "name": "actions.capability.ACCOUNT_LINKING" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.SCREEN_OUTPUT" } ] } ] }
2. 주문 만들기
사용자 환경
필요한 사용자 정보를 얻었으면 '장바구니'를 구축합니다. assembly" 맞춤 환경을 제공합니다. 모든 작업은 상황에 따라 장바구니 조립 플로우가 약간 다릅니다. 제품 또는 서비스
가장 기본적인 장바구니 조립 환경은 사용자가 목록에서 추가할 항목을 선택하는 것입니다. 다만, 대화 내용을 쉽게 이해할 수 있도록 설계하여 있습니다. 이 광고주는 다양한 장바구니 제품을 통해 사용자가 간단한 예 또는 아니요 질문을 통해 최근 구매 항목을 재주문할 수 있습니다. 사용자에게 상위 '추천' 목록의 캐러셀 또는 목록 카드를 제시할 수도 있습니다. 또는 '권장' 개의 항목이 있습니다.
리치 미디어 응답을 사용하여 사용자의 옵션을 표시합니다. 시각적으로도 유용하지만, 사용자가 원하는 내용을 구축할 수 있도록 대화를 설계하세요. 장바구니를 이용할 수 있습니다. 사용 사례에 대한 권장사항과 예시를 장바구니를 조립하는 과정에 대해 자세히 알아보려면 트랜잭션 설계 가이드라인을 참고하세요.
처리
대화를 진행하는 동안 사용자가 원하는 항목을 수집해야 합니다.
그런 다음 Order
객체를 구성합니다.
Order
에는 최소한 다음이 포함되어야 합니다.
buyerInfo
- 구매하는 사용자에 관한 정보입니다.transactionMerchant
- 이 문제에 도움을 준 판매자에 대한 정보 있습니다.contents
-lineItems
로 표시되는 주문의 실제 콘텐츠입니다.priceAttributes
- 총액을 포함한 주문의 가격 세부정보 할인 및 세금이 포함된 주문 비용입니다.
Order
응답 문서 참고하기
장바구니를 구성합니다. 다른 필드를 포함해야 할 수도 있습니다.
다르기 때문입니다.
아래의 샘플 코드는 선택 필드를 포함하여 전체 주문을 보여줍니다.
Node.js
const order = { createTime: '2019-09-24T18:00:00.877Z', lastUpdateTime: '2019-09-24T18:00:00.877Z', merchantOrderId: orderId, // A unique ID String for the order userVisibleOrderId: orderId, transactionMerchant: { id: 'http://www.example.com', name: 'Example Merchant', }, contents: { lineItems: [ { id: 'LINE_ITEM_ID', name: 'Pizza', description: 'A four cheese pizza.', priceAttributes: [ { type: 'REGULAR', name: 'Item Price', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 8990000, }, taxIncluded: true, }, { type: 'TOTAL', name: 'Total Price', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 9990000, }, taxIncluded: true, }, ], notes: [ 'Extra cheese.', ], purchase: { quantity: 1, unitMeasure: { measure: 1, unit: 'POUND', }, itemOptions: [ { id: 'ITEM_OPTION_ID', name: 'Pepperoni', prices: [ { type: 'REGULAR', state: 'ACTUAL', name: 'Item Price', amount: { currencyCode: 'USD', amountInMicros: 1000000, }, taxIncluded: true, }, { type: 'TOTAL', name: 'Total Price', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 1000000, }, taxIncluded: true, }, ], note: 'Extra pepperoni', quantity: 1, subOptions: [], }, ], }, }, ], }, buyerInfo: { email: 'janedoe@gmail.com', firstName: 'Jane', lastName: 'Doe', displayName: 'Jane Doe', }, priceAttributes: [ { type: 'SUBTOTAL', name: 'Subtotal', state: 'ESTIMATE', amount: { currencyCode: 'USD', amountInMicros: 9990000, }, taxIncluded: true, }, { type: 'DELIVERY', name: 'Delivery', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 2000000, }, taxIncluded: true, }, { type: 'TAX', name: 'Tax', state: 'ESTIMATE', amount: { currencyCode: 'USD', amountInMicros: 3780000, }, taxIncluded: true, }, { type: 'TOTAL', name: 'Total Price', state: 'ESTIMATE', amount: { currencyCode: 'USD', amountInMicros: 15770000, }, taxIncluded: true, }, ], followUpActions: [ { type: 'VIEW_DETAILS', title: 'View details', openUrlAction: { url: 'http://example.com', }, }, { type: 'CALL', title: 'Call us', openUrlAction: { url: 'tel:+16501112222', }, }, { type: 'EMAIL', title: 'Email us', openUrlAction: { url: 'mailto:person@example.com', }, }, ], termsOfServiceUrl: 'http://www.example.com', note: 'Sale event', promotions: [ { coupon: 'COUPON_CODE', }, ], purchase: { status: 'CREATED', userVisibleStatusLabel: 'CREATED', type: 'FOOD', returnsInfo: { isReturnable: false, daysToReturn: 1, policyUrl: 'http://www.example.com', }, fulfillmentInfo: { id: 'FULFILLMENT_SERVICE_ID', fulfillmentType: 'DELIVERY', expectedFulfillmentTime: { timeIso8601: '2019-09-25T18:00:00.877Z', }, location: location, price: { type: 'REGULAR', name: 'Delivery Price', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 2000000, }, taxIncluded: true, }, fulfillmentContact: { email: 'johnjohnson@gmail.com', firstName: 'John', lastName: 'Johnson', displayName: 'John Johnson', }, }, purchaseLocationType: 'ONLINE_PURCHASE', }, };
Node.js
const order = { createTime: '2019-09-24T18:00:00.877Z', lastUpdateTime: '2019-09-24T18:00:00.877Z', merchantOrderId: orderId, // A unique ID String for the order userVisibleOrderId: orderId, transactionMerchant: { id: 'http://www.example.com', name: 'Example Merchant', }, contents: { lineItems: [ { id: 'LINE_ITEM_ID', name: 'Pizza', description: 'A four cheese pizza.', priceAttributes: [ { type: 'REGULAR', name: 'Item Price', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 8990000, }, taxIncluded: true, }, { type: 'TOTAL', name: 'Total Price', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 9990000, }, taxIncluded: true, }, ], notes: [ 'Extra cheese.', ], purchase: { quantity: 1, unitMeasure: { measure: 1, unit: 'POUND', }, itemOptions: [ { id: 'ITEM_OPTION_ID', name: 'Pepperoni', prices: [ { type: 'REGULAR', state: 'ACTUAL', name: 'Item Price', amount: { currencyCode: 'USD', amountInMicros: 1000000, }, taxIncluded: true, }, { type: 'TOTAL', name: 'Total Price', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 1000000, }, taxIncluded: true, }, ], note: 'Extra pepperoni', quantity: 1, subOptions: [], }, ], }, }, ], }, buyerInfo: { email: 'janedoe@gmail.com', firstName: 'Jane', lastName: 'Doe', displayName: 'Jane Doe', }, priceAttributes: [ { type: 'SUBTOTAL', name: 'Subtotal', state: 'ESTIMATE', amount: { currencyCode: 'USD', amountInMicros: 9990000, }, taxIncluded: true, }, { type: 'DELIVERY', name: 'Delivery', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 2000000, }, taxIncluded: true, }, { type: 'TAX', name: 'Tax', state: 'ESTIMATE', amount: { currencyCode: 'USD', amountInMicros: 3780000, }, taxIncluded: true, }, { type: 'TOTAL', name: 'Total Price', state: 'ESTIMATE', amount: { currencyCode: 'USD', amountInMicros: 15770000, }, taxIncluded: true, }, ], followUpActions: [ { type: 'VIEW_DETAILS', title: 'View details', openUrlAction: { url: 'http://example.com', }, }, { type: 'CALL', title: 'Call us', openUrlAction: { url: 'tel:+16501112222', }, }, { type: 'EMAIL', title: 'Email us', openUrlAction: { url: 'mailto:person@example.com', }, }, ], termsOfServiceUrl: 'http://www.example.com', note: 'Sale event', promotions: [ { coupon: 'COUPON_CODE', }, ], purchase: { status: 'CREATED', userVisibleStatusLabel: 'CREATED', type: 'FOOD', returnsInfo: { isReturnable: false, daysToReturn: 1, policyUrl: 'http://www.example.com', }, fulfillmentInfo: { id: 'FULFILLMENT_SERVICE_ID', fulfillmentType: 'DELIVERY', expectedFulfillmentTime: { timeIso8601: '2019-09-25T18:00:00.877Z', }, location: location, price: { type: 'REGULAR', name: 'Delivery Price', state: 'ACTUAL', amount: { currencyCode: 'USD', amountInMicros: 2000000, }, taxIncluded: true, }, fulfillmentContact: { email: 'johnjohnson@gmail.com', firstName: 'John', lastName: 'Johnson', displayName: 'John Johnson', }, }, purchaseLocationType: 'ONLINE_PURCHASE', }, };
자바
// Transaction Merchant MerchantV3 transactionMerchant = new MerchantV3() .setId("http://www.example.com") .setName("Example Merchant"); // Line Item PriceAttribute itemPrice = new PriceAttribute() .setType("REGULAR") .setName("Item Price") .setState("ACTUAL") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(8990000L) ) .setTaxIncluded(true); PriceAttribute totalItemPrice = new PriceAttribute() .setType("TOTAL") .setName("Total Price") .setState("ACTUAL") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(9990000L) ) .setTaxIncluded(true); // Purchase Item Extension PurchaseItemExtension purchaseItemExtension = new PurchaseItemExtension() .setQuantity(1) .setUnitMeasure(new MerchantUnitMeasure() .setMeasure(1.0) .setUnit("POUND")) .setItemOptions(Arrays.asList(new PurchaseItemExtensionItemOption() .setId("ITEM_OPTION_ID") .setName("Pepperoni") .setPrices(Arrays.asList( new PriceAttribute() .setType("REGULAR") .setState("ACTUAL") .setName("Item Price") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(1000000L)) .setTaxIncluded(true), new PriceAttribute() .setType("TOTAL") .setState("ACTUAL") .setName("Total Price") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(1000000L)) .setTaxIncluded(true) )) .setNote("Extra pepperoni") .setQuantity(1))); LineItemV3 lineItem = new LineItemV3() .setId("LINE_ITEM_ID") .setName("Pizza") .setDescription("A four cheese pizza.") .setPriceAttributes(Arrays.asList(itemPrice, totalItemPrice)) .setNotes(Collections.singletonList("Extra cheese.")) .setPurchase(purchaseItemExtension); // Order Contents OrderContents contents = new OrderContents() .setLineItems(Collections.singletonList(lineItem)); // User Info UserInfo buyerInfo = new UserInfo() .setEmail("janedoe@gmail.com") .setFirstName("Jane") .setLastName("Doe") .setDisplayName("Jane Doe"); // Price Attributes PriceAttribute subTotal = new PriceAttribute() .setType("SUBTOTAL") .setName("Subtotal") .setState("ESTIMATE") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(9990000L) ) .setTaxIncluded(true); PriceAttribute deliveryFee = new PriceAttribute() .setType("DELIVERY") .setName("Delivery") .setState("ACTUAL") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(2000000L) ) .setTaxIncluded(true); PriceAttribute tax = new PriceAttribute() .setType("TAX") .setName("Tax") .setState("ESTIMATE") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(3780000L) ) .setTaxIncluded(true); PriceAttribute totalPrice = new PriceAttribute() .setType("TOTAL") .setName("Total Price") .setState("ESTIMATE") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(15770000L) ) .setTaxIncluded(true); // Follow up actions Action viewDetails = new Action() .setType("VIEW_DETAILS") .setTitle("View details") .setOpenUrlAction(new OpenUrlAction() .setUrl("https://example.com")); Action call = new Action() .setType("CALL") .setTitle("Call us") .setOpenUrlAction(new OpenUrlAction() .setUrl("tel:+16501112222")); Action email = new Action() .setType("EMAIL") .setTitle("Email us") .setOpenUrlAction(new OpenUrlAction() .setUrl("mailto:person@example.com")); // Terms of service and order note String termsOfServiceUrl = "http://example.com"; String orderNote = "Sale event"; // Promotions PromotionV3 promotion = new PromotionV3() .setCoupon("COUPON_CODE"); // Purchase Order Extension Location location = GSON_BUILDER.create().fromJson( (String) conversationData.get("location"), Location.class); PurchaseOrderExtension purchaseOrderExtension = new PurchaseOrderExtension() .setStatus("CREATED") .setUserVisibleStatusLabel("CREATED") .setType("FOOD") .setReturnsInfo(new PurchaseReturnsInfo() .setIsReturnable(false) .setDaysToReturn(1) .setPolicyUrl("https://example.com")) .setFulfillmentInfo(new PurchaseFulfillmentInfo() .setId("FULFILLMENT_SERVICE_ID") .setFulfillmentType("DELIVERY") .setExpectedFulfillmentTime(new TimeV3() .setTimeIso8601("2019-09-25T18:00:00.877Z")) .setLocation(location) .setPrice(new PriceAttribute() .setType("REGULAR") .setName("Delivery price") .setState("ACTUAL") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(2000000L)) .setTaxIncluded(true)) .setFulfillmentContact(new UserInfo() .setEmail("johnjohnson@gmail.com") .setFirstName("John") .setLastName("Johnson") .setDisplayName("John Johnson"))) .setPurchaseLocationType("ONLINE_PURCHASE"); OrderV3 order = new OrderV3() .setCreateTime("2019-09-24T18:00:00.877Z") .setLastUpdateTime("2019-09-24T18:00:00.877Z") .setMerchantOrderId(orderId) .setUserVisibleOrderId(orderId) .setTransactionMerchant(transactionMerchant) .setContents(contents) .setBuyerInfo(buyerInfo) .setPriceAttributes(Arrays.asList( subTotal, deliveryFee, tax, totalPrice )) .setFollowUpActions(Arrays.asList( viewDetails, call, email )) .setTermsOfServiceUrl(termsOfServiceUrl) .setNote(orderNote) .setPromotions(Collections.singletonList(promotion)) .setPurchase(purchaseOrderExtension);
자바
// Transaction Merchant MerchantV3 transactionMerchant = new MerchantV3() .setId("http://www.example.com") .setName("Example Merchant"); // Line Item PriceAttribute itemPrice = new PriceAttribute() .setType("REGULAR") .setName("Item Price") .setState("ACTUAL") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(8990000L) ) .setTaxIncluded(true); PriceAttribute totalItemPrice = new PriceAttribute() .setType("TOTAL") .setName("Total Price") .setState("ACTUAL") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(9990000L) ) .setTaxIncluded(true); // Purchase Item Extension PurchaseItemExtension purchaseItemExtension = new PurchaseItemExtension() .setUnitMeasure(new MerchantUnitMeasure() .setMeasure(1.0) .setUnit("POUND")) .setItemOptions(Arrays.asList(new PurchaseItemExtensionItemOption() .setId("ITEM_OPTION_ID") .setName("Pepperoni") .setPrices(Arrays.asList( new PriceAttribute() .setType("REGULAR") .setState("ACTUAL") .setName("Item Price") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(1000000L)) .setTaxIncluded(true), new PriceAttribute() .setType("TOTAL") .setState("ACTUAL") .setName("Total Price") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(1000000L)) .setTaxIncluded(true) )) .setNote("Extra pepperoni"))); LineItemV3 lineItem = new LineItemV3() .setId("LINE_ITEM_ID") .setName("Pizza") .setDescription("A four cheese pizza.") .setPriceAttributes(Arrays.asList(itemPrice, totalItemPrice)) .setNotes(Collections.singletonList("Extra cheese.")) .setPurchase(purchaseItemExtension); // Order Contents OrderContents contents = new OrderContents() .setLineItems(Collections.singletonList(lineItem)); // User Info UserInfo buyerInfo = new UserInfo() .setEmail("janedoe@gmail.com") .setFirstName("Jane") .setLastName("Doe") .setDisplayName("Jane Doe"); // Price Attributes PriceAttribute subTotal = new PriceAttribute() .setType("SUBTOTAL") .setName("Subtotal") .setState("ESTIMATE") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(9990000L) ) .setTaxIncluded(true); PriceAttribute deliveryFee = new PriceAttribute() .setType("DELIVERY") .setName("Delivery") .setState("ACTUAL") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(2000000L) ) .setTaxIncluded(true); PriceAttribute tax = new PriceAttribute() .setType("TAX") .setName("Tax") .setState("ESTIMATE") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(3780000L) ) .setTaxIncluded(true); PriceAttribute totalPrice = new PriceAttribute() .setType("TOTAL") .setName("Total Price") .setState("ESTIMATE") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(15770000L) ) .setTaxIncluded(true); // Follow up actions Action viewDetails = new Action() .setType("VIEW_DETAILS") .setTitle("View details") .setOpenUrlAction(new OpenUrlAction() .setUrl("https://example.com")); Action call = new Action() .setType("CALL") .setTitle("Call us") .setOpenUrlAction(new OpenUrlAction() .setUrl("tel:+16501112222")); Action email = new Action() .setType("EMAIL") .setTitle("Email us") .setOpenUrlAction(new OpenUrlAction() .setUrl("mailto:person@example.com")); // Terms of service and order note String termsOfServiceUrl = "http://example.com"; String orderNote = "Sale event"; // Promotions PromotionV3 promotion = new PromotionV3() .setCoupon("COUPON_CODE"); // Purchase Order Extension Location location = GSON_BUILDER.create().fromJson( (String) conversationData.get("location"), Location.class); PurchaseOrderExtension purchaseOrderExtension = new PurchaseOrderExtension() .setStatus("CREATED") .setUserVisibleStatusLabel("CREATED") .setType("FOOD") .setReturnsInfo(new PurchaseReturnsInfo() .setIsReturnable(false) .setDaysToReturn(1) .setPolicyUrl("https://example.com")) .setFulfillmentInfo(new PurchaseFulfillmentInfo() .setId("FULFILLMENT_SERVICE_ID") .setFulfillmentType("DELIVERY") .setExpectedFulfillmentTime(new TimeV3() .setTimeIso8601("2019-09-25T18:00:00.877Z")) .setLocation(location) .setPrice(new PriceAttribute() .setType("REGULAR") .setName("Delivery price") .setState("ACTUAL") .setAmount(new MoneyV3() .setCurrencyCode("USD") .setAmountInMicros(2000000L)) .setTaxIncluded(true)) .setFulfillmentContact(new UserInfo() .setEmail("johnjohnson@gmail.com") .setFirstName("John") .setLastName("Johnson") .setDisplayName("John Johnson"))) .setPurchaseLocationType("ONLINE_PURCHASE"); OrderV3 order = new OrderV3() .setCreateTime("2019-09-24T18:00:00.877Z") .setLastUpdateTime("2019-09-24T18:00:00.877Z") .setMerchantOrderId(orderId) .setUserVisibleOrderId(orderId) .setTransactionMerchant(transactionMerchant) .setContents(contents) .setBuyerInfo(buyerInfo) .setPriceAttributes(Arrays.asList( subTotal, deliveryFee, tax, totalPrice )) .setFollowUpActions(Arrays.asList( viewDetails, call, email )) .setTermsOfServiceUrl(termsOfServiceUrl) .setNote(orderNote) .setPromotions(Collections.singletonList(promotion)) .setPurchase(purchaseOrderExtension);
3. 계정 연결 수행
자체 결제 수단을 사용하여 사용자에게 청구하는 경우 Google 계정을 자체 서비스에 연결된 계정과 연결하여 저장된 결제 수단을 검색하고 제시하고 청구할 수 있습니다.
Google에서는 OAuth 2.0 계정 연결을 제공합니다. 이를 충족하려면 요구사항을 충족해야 합니다 OAuth 2.0 어설션 흐름을 사용하도록 설정하는 것이 좋습니다. 매우 간소화된 사용자 환경을 제공합니다.
Google에서 제공하는 actions.intent.SIGN_IN
인텐트를 사용하면 사용자가
대화 중에 사용자 계정을 연결하는 것입니다. 다음을 수행해야 합니다.
계정 연결 사용 설정
Actions 콘솔에서
actions.intent.SIGN_IN
인텐트에 대한 응답입니다.
accessToken
를 찾을 수 없는 경우 이 인텐트를 사용해야 합니다.
User
객체를 반환합니다. 사용자가 아직
계정을 연결했습니다.
actions.intent.SIGN_IN
인텐트를 요청한 후 다음을 수신합니다.
값이 "OK"
인 SignInStatus
를 포함하는 Argument
"CANCELLED"
또는 "ERROR"
입니다. 상태가 "OK"
인 경우 다음을 찾을 수 있습니다.
User
객체의 accessToken
처리
로그인 요청
Node.js
app.intent('Sign In', (conv) => { conv.ask(new SignIn('To get your account details')); });
Node.js
conv.ask(new SignIn('To get your account details'));
자바
@ForIntent("Sign In") public ActionResponse signIn(ActionRequest request) { return getResponseBuilder(request).add( new SignIn() .setContext("To get your account details")) .build(); }
자바
return getResponseBuilder(request).add( new SignIn() .setContext("To get your account details")) .build();
JSON
{ "payload": { "google": { "expectUserResponse": true, "systemIntent": { "intent": "actions.intent.SIGN_IN", "data": { "@type": "type.googleapis.com/google.actions.v2.SignInValueSpec", "optContext": "To get your account details" } } } } }
JSON
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.SIGN_IN", "inputValueData": { "@type": "type.googleapis.com/google.actions.v2.SignInValueSpec", "optContext": "To get your account details" } } ] } ], "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\"}}" }
로그인 결과 수신
Node.js
app.intent('Sign In Complete', (conv, params, signin) => { if (signin.status !== 'OK') { conv.ask('You need to sign in before making a transaction.'); } else { const accessToken = conv.user.access.token; // possibly do something with access token conv.ask('You must meet all the requirements necessary ' + 'to make a transaction. Try saying ' + '"check transaction requirements".'); conv.ask(new Suggestions(`check requirements`)); } });
Node.js
app.intent('actions.intent.SIGN_IN', (conv, params, signin) => { if (signin.status !== 'OK') { conv.ask('You need to sign in before making a transaction.'); } else { const accessToken = conv.user.access.token; // possibly do something with access token conv.ask('You must meet all the requirements necessary ' + 'to make a transaction. Try saying ' + '"check transaction requirements".'); conv.ask(new Suggestions(`check requirements`)); } });
자바
@ForIntent("Sign In Complete") public ActionResponse signInComplete(ActionRequest request) { ResponseBuilder responseBuilder = getResponseBuilder(request); if (request.isSignInGranted()) { responseBuilder .add("You must meet all the requirements necessary to make a " + "transaction. Try saying \"check transaction requirements\".") .addSuggestions(new String[] { "check requirements" }); } else { responseBuilder.add("You need to sign in before making a transaction."); } return responseBuilder.build(); }
자바
@ForIntent("actions.intent.SIGN_IN") public ActionResponse signInComplete(ActionRequest request) { ResponseBuilder responseBuilder = getResponseBuilder(request); if (request.isSignInGranted()) { responseBuilder .add("You must meet all the requirements necessary to make a " + "transaction. Try saying \"check transaction requirements\".") .addSuggestions(new String[] { "check requirements" }); } else { responseBuilder.add("You need to sign in before making a transaction."); } return responseBuilder.build(); }
JSON
{ "responseId": "d2123d8d-3f00-466e-b5a9-1a4ed53a7cb7-594de0a7", "queryResult": { "queryText": "actions_intent_SIGN_IN", "parameters": {}, "allRequiredParamsPresent": true, "fulfillmentMessages": [ { "text": { "text": [ "" ] } } ], "outputContexts": [ { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/merchant_payment", "lifespanCount": 2 }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_web_browser" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_screen_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_account_linking" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_media_response_audio" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_audio_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/google_assistant_input_type_keyboard" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_intent_sign_in", "parameters": { "SIGN_IN": { "@type": "type.googleapis.com/google.actions.v2.SignInValue", "status": "OK" }, "text": "" } } ], "intent": { "name": "projects/df-transactions/agent/intents/105b925b-b186-4f5d-8bde-a9a782a0fa9f", "displayName": "Sign In Complete" }, "intentDetectionConfidence": 1, "languageCode": "en" }, "originalDetectIntentRequest": { "source": "google", "version": "2", "payload": { "user": { "locale": "en-US", "lastSeen": "2019-09-23T19:49:18Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy", "type": "ACTIVE", "conversationToken": "[]" }, "inputs": [ { "intent": "actions.intent.SIGN_IN", "rawInputs": [ { "inputType": "KEYBOARD" } ], "arguments": [ { "name": "SIGN_IN", "extension": { "@type": "type.googleapis.com/google.actions.v2.SignInValue", "status": "OK" } }, { "name": "text" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" }, { "name": "actions.capability.AUDIO_OUTPUT" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.SCREEN_OUTPUT" } ] } ] } }, "session": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy" }
JSON
{ "user": { "locale": "en-US", "lastSeen": "2019-11-11T23:55:52Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGFwStZlYaQ9YT8rg9t_idVsxZrku1pUDrEbGSJmSUMatVdPwPEEQSCe1IwIBoN4sS4Weyn9pmgetEgbsWgw3JSvQmw", "type": "ACTIVE", "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\"}}" }, "inputs": [ { "intent": "actions.intent.SIGN_IN", "rawInputs": [ {} ], "arguments": [ { "name": "SIGN_IN", "extension": { "@type": "type.googleapis.com/google.actions.v2.SignInValue", "status": "OK" } }, { "name": "text" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" } ] } ] }
4. 주문 제안
주문을 작성한 후에는 주문을 확인하거나 판매하기 위해 사용자에게 제시해야 합니다.
거부합니다. actions.intent.TRANSACTION_DECISION
요청
인텐트를 전달하고 사용자의 저장된 결제로 빌드한 주문을 제공합니다.
확인할 수 있습니다
사용자 환경
actions.intent.TRANSACTION_DECISION
인텐트를 요청하면 어시스턴트는
전달한 Order
가
'장바구니 미리보기 카드'에 직접 렌더링됩니다. 사용자가 "주문하기"라고 말하면
거래를 거부하거나 신용카드와 같은 결제 옵션을 변경하거나
주문 내용 변경을 요청할 수 있습니다.
이 시점에서 사용자가 주문 변경을 요청할 수도 있습니다. 이 경우 처리에서 주문 변경 요청을 처리할 수 있는지 마무리하는 단계입니다
처리
귀하가
actions.intent.TRANSACTION_DECISION
인텐트를 사용하여
Order
가 포함된 TransactionDecision
orderOptions
, paymentParameters
도 포함됩니다. 내 paymentParameters
객체에는 사용자의 동작을 설명하는 merchantPaymentOption
결제 수단에 액세스할 수 있게 됩니다.
다음 코드는 결제된 주문의 TransactionsDecision
예를 보여줍니다.
Visa 신용카드 사용:
Node.js
conv.ask(new TransactionDecision({ orderOptions: { userInfoOptions: { userInfoProperties: [ 'EMAIL', ], }, }, paymentParameters: { merchantPaymentOption: { defaultMerchantPaymentMethodId: '12345678', managePaymentMethodUrl: 'https://example.com/managePayment', merchantPaymentMethod: [ { paymentMethodDisplayInfo: { paymentMethodDisplayName: 'VISA **** 1234', paymentType: 'PAYMENT_CARD', }, paymentMethodGroup: 'Payment method group', paymentMethodId: '12345678', paymentMethodStatus: { status: 'STATUS_OK', statusMessage: 'Status message', }, }, ], }, }, presentationOptions: { actionDisplayName: 'PLACE_ORDER', }, order: order, }));
Node.js
conv.ask(new TransactionDecision({ orderOptions: { userInfoOptions: { userInfoProperties: [ 'EMAIL', ], }, }, paymentParameters: { merchantPaymentOption: { defaultMerchantPaymentMethodId: '12345678', managePaymentMethodUrl: 'https://example.com/managePayment', merchantPaymentMethod: [ { paymentMethodDisplayInfo: { paymentMethodDisplayName: 'VISA **** 1234', paymentType: 'PAYMENT_CARD', }, paymentMethodGroup: 'Payment method group', paymentMethodId: '12345678', paymentMethodStatus: { status: 'STATUS_OK', statusMessage: 'Status message', }, }, ], }, }, presentationOptions: { actionDisplayName: 'PLACE_ORDER', }, order: order, }));
자바
// Create order options OrderOptionsV3 orderOptions = new OrderOptionsV3() .setUserInfoOptions(new UserInfoOptions() .setUserInfoProperties(Collections.singletonList("EMAIL"))); // Create presentation options PresentationOptionsV3 presentationOptions = new PresentationOptionsV3() .setActionDisplayName("PLACE_ORDER"); // Create payment parameters MerchantPaymentMethod merchantPaymentMethod = new MerchantPaymentMethod() .setPaymentMethodDisplayInfo(new PaymentMethodDisplayInfo() .setPaymentMethodDisplayName("VISA **** 1234") .setPaymentType("PAYMENT_CARD")) .setPaymentMethodGroup("Payment method group") .setPaymentMethodId("12345678") .setPaymentMethodStatus(new PaymentMethodStatus() .setStatus("STATUS_OK") .setStatusMessage("Status message")); MerchantPaymentOption merchantPaymentOption = new MerchantPaymentOption() .setDefaultMerchantPaymentMethodId("12345678") .setManagePaymentMethodUrl("https://example.com/managePayment") .setMerchantPaymentMethod(Collections.singletonList(merchantPaymentMethod)); paymentParameters.setMerchantPaymentOption(merchantPaymentOption); return getResponseBuilder(request) .add(new TransactionDecision() .setOrder(order) .setOrderOptions(orderOptions) .setPresentationOptions(presentationOptions) .setPaymentParameters(paymentParameters) ) .build();
자바
// Create order options OrderOptionsV3 orderOptions = new OrderOptionsV3() .setUserInfoOptions(new UserInfoOptions() .setUserInfoProperties(Collections.singletonList("EMAIL"))); // Create presentation options PresentationOptionsV3 presentationOptions = new PresentationOptionsV3() .setActionDisplayName("PLACE_ORDER"); // Create payment parameters MerchantPaymentMethod merchantPaymentMethod = new MerchantPaymentMethod() .setPaymentMethodDisplayInfo(new PaymentMethodDisplayInfo() .setPaymentMethodDisplayName("VISA **** 1234") .setPaymentType("PAYMENT_CARD")) .setPaymentMethodGroup("Payment method group") .setPaymentMethodId("12345678") .setPaymentMethodStatus(new PaymentMethodStatus() .setStatus("STATUS_OK") .setStatusMessage("Status message")); MerchantPaymentOption merchantPaymentOption = new MerchantPaymentOption() .setDefaultMerchantPaymentMethodId("12345678") .setManagePaymentMethodUrl("https://example.com/managePayment") .setMerchantPaymentMethod(Collections.singletonList(merchantPaymentMethod)); paymentParameters.setMerchantPaymentOption(merchantPaymentOption); return getResponseBuilder(request) .add(new TransactionDecision() .setOrder(order) .setOrderOptions(orderOptions) .setPresentationOptions(presentationOptions) .setPaymentParameters(paymentParameters) ) .build();
JSON
{ "payload": { "google": { "expectUserResponse": true, "richResponse": { "items": [ { "simpleResponse": { "textToSpeech": "Transaction Decision Placeholder." } } ] }, "systemIntent": { "intent": "actions.intent.TRANSACTION_DECISION", "data": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec", "orderOptions": { "userInfoOptions": { "userInfoProperties": [ "EMAIL" ] } }, "paymentParameters": { "merchantPaymentOption": { "defaultMerchantPaymentMethodId": "12345678", "managePaymentMethodUrl": "https://example.com/managePayment", "merchantPaymentMethod": [ { "paymentMethodDisplayInfo": { "paymentMethodDisplayName": "VISA **** 1234", "paymentType": "PAYMENT_CARD" }, "paymentMethodGroup": "Payment method group", "paymentMethodId": "12345678", "paymentMethodStatus": { "status": "STATUS_OK", "statusMessage": "Status message" } } ] } }, "presentationOptions": { "actionDisplayName": "PLACE_ORDER" }, "order": { "createTime": "2019-09-24T18:00:00.877Z", "lastUpdateTime": "2019-09-24T18:00:00.877Z", "merchantOrderId": "ORDER_ID", "userVisibleOrderId": "ORDER_ID", "transactionMerchant": { "id": "http://www.example.com", "name": "Example Merchant" }, "contents": { "lineItems": [ { "id": "LINE_ITEM_ID", "name": "Pizza", "description": "A four cheese pizza.", "priceAttributes": [ { "type": "REGULAR", "name": "Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 8990000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 9990000 }, "taxIncluded": true } ], "notes": [ "Extra cheese." ], "purchase": { "quantity": 1, "unitMeasure": { "measure": 1, "unit": "POUND" }, "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "prices": [ { "type": "REGULAR", "state": "ACTUAL", "name": "Item Price", "amount": { "currencyCode": "USD", "amountInMicros": 1000000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 1000000 }, "taxIncluded": true } ], "note": "Extra pepperoni", "quantity": 1, "subOptions": [] } ] } } ] }, "buyerInfo": { "email": "janedoe@gmail.com", "firstName": "Jane", "lastName": "Doe", "displayName": "Jane Doe" }, "priceAttributes": [ { "type": "SUBTOTAL", "name": "Subtotal", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 9990000 }, "taxIncluded": true }, { "type": "DELIVERY", "name": "Delivery", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 2000000 }, "taxIncluded": true }, { "type": "TAX", "name": "Tax", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 3780000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 15770000 }, "taxIncluded": true } ], "followUpActions": [ { "type": "VIEW_DETAILS", "title": "View details", "openUrlAction": { "url": "http://example.com" } }, { "type": "CALL", "title": "Call us", "openUrlAction": { "url": "tel:+16501112222" } }, { "type": "EMAIL", "title": "Email us", "openUrlAction": { "url": "mailto:person@example.com" } } ], "termsOfServiceUrl": "http://www.example.com", "note": "Sale event", "promotions": [ { "coupon": "COUPON_CODE" } ], "purchase": { "status": "CREATED", "userVisibleStatusLabel": "CREATED", "type": "FOOD", "returnsInfo": { "isReturnable": false, "daysToReturn": 1, "policyUrl": "http://www.example.com" }, "fulfillmentInfo": { "id": "FULFILLMENT_SERVICE_ID", "fulfillmentType": "DELIVERY", "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "location": { "city": "MOUNTAIN VIEW", "coordinates": { "latitude": 37.432524, "longitude": -122.098545 }, "phoneNumber": "+1 123-456-7890", "postalAddress": { "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "postalCode": "94043-1351", "recipients": [ "John Doe" ], "regionCode": "US" }, "zipCode": "94043-1351" }, "price": { "type": "REGULAR", "name": "Delivery Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 2000000 }, "taxIncluded": true }, "fulfillmentContact": { "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson", "displayName": "John Johnson" } }, "purchaseLocationType": "ONLINE_PURCHASE" } } } } } }, "outputContexts": [ { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/_actions_on_google", "lifespanCount": 99, "parameters": { "data": "{\"location\":{\"coordinates\":{\"latitude\":37.432524,\"longitude\":-122.098545},\"zipCode\":\"94043-1351\",\"city\":\"MOUNTAIN VIEW\",\"postalAddress\":{\"regionCode\":\"US\",\"postalCode\":\"94043-1351\",\"administrativeArea\":\"CA\",\"locality\":\"MOUNTAIN VIEW\",\"addressLines\":[\"1600 AMPHITHEATRE PKWY\"],\"recipients\":[\"John Doe\"]},\"phoneNumber\":\"+1 123-456-7890\"},\"latestOrderId\":\"ORDER_ID\"}" } } ] }
JSON
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.TRANSACTION_DECISION", "inputValueData": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec", "orderOptions": { "userInfoOptions": { "userInfoProperties": [ "EMAIL" ] } }, "paymentParameters": { "merchantPaymentOption": { "defaultMerchantPaymentMethodId": "12345678", "managePaymentMethodUrl": "https://example.com/managePayment", "merchantPaymentMethod": [ { "paymentMethodDisplayInfo": { "paymentMethodDisplayName": "VISA **** 1234", "paymentType": "PAYMENT_CARD" }, "paymentMethodGroup": "Payment method group", "paymentMethodId": "12345678", "paymentMethodStatus": { "status": "STATUS_OK", "statusMessage": "Status message" } } ] } }, "presentationOptions": { "actionDisplayName": "PLACE_ORDER" }, "order": { "createTime": "2019-09-24T18:00:00.877Z", "lastUpdateTime": "2019-09-24T18:00:00.877Z", "merchantOrderId": "ORDER_ID", "userVisibleOrderId": "ORDER_ID", "transactionMerchant": { "id": "http://www.example.com", "name": "Example Merchant" }, "contents": { "lineItems": [ { "id": "LINE_ITEM_ID", "name": "Pizza", "description": "A four cheese pizza.", "priceAttributes": [ { "type": "REGULAR", "name": "Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 8990000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 9990000 }, "taxIncluded": true } ], "notes": [ "Extra cheese." ], "purchase": { "quantity": 1, "unitMeasure": { "measure": 1, "unit": "POUND" }, "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "prices": [ { "type": "REGULAR", "state": "ACTUAL", "name": "Item Price", "amount": { "currencyCode": "USD", "amountInMicros": 1000000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 1000000 }, "taxIncluded": true } ], "note": "Extra pepperoni", "quantity": 1, "subOptions": [] } ] } } ] }, "buyerInfo": { "email": "janedoe@gmail.com", "firstName": "Jane", "lastName": "Doe", "displayName": "Jane Doe" }, "priceAttributes": [ { "type": "SUBTOTAL", "name": "Subtotal", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 9990000 }, "taxIncluded": true }, { "type": "DELIVERY", "name": "Delivery", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 2000000 }, "taxIncluded": true }, { "type": "TAX", "name": "Tax", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 3780000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 15770000 }, "taxIncluded": true } ], "followUpActions": [ { "type": "VIEW_DETAILS", "title": "View details", "openUrlAction": { "url": "http://example.com" } }, { "type": "CALL", "title": "Call us", "openUrlAction": { "url": "tel:+16501112222" } }, { "type": "EMAIL", "title": "Email us", "openUrlAction": { "url": "mailto:person@example.com" } } ], "termsOfServiceUrl": "http://www.example.com", "note": "Sale event", "promotions": [ { "coupon": "COUPON_CODE" } ], "purchase": { "status": "CREATED", "userVisibleStatusLabel": "CREATED", "type": "FOOD", "returnsInfo": { "isReturnable": false, "daysToReturn": 1, "policyUrl": "http://www.example.com" }, "fulfillmentInfo": { "id": "FULFILLMENT_SERVICE_ID", "fulfillmentType": "DELIVERY", "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "location": { "coordinates": { "latitude": 37.421578499999995, "longitude": -122.0837816 }, "zipCode": "94043-1351", "city": "MOUNTAIN VIEW", "postalAddress": { "regionCode": "US", "postalCode": "94043-1351", "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "recipients": [ "John Doe" ] }, "phoneNumber": "+1 123-456-7890" }, "price": { "type": "REGULAR", "name": "Delivery Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 2000000 }, "taxIncluded": true }, "fulfillmentContact": { "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson", "displayName": "John Johnson" } }, "purchaseLocationType": "ONLINE_PURCHASE" } } } } ], "inputPrompt": { "richInitialPrompt": { "items": [ { "simpleResponse": { "textToSpeech": "Transaction Decision Placeholder." } } ] } } } ], "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\",\"location\":{\"coordinates\":{\"latitude\":37.421578499999995,\"longitude\":-122.0837816},\"zipCode\":\"94043-1351\",\"city\":\"MOUNTAIN VIEW\",\"postalAddress\":{\"regionCode\":\"US\",\"postalCode\":\"94043-1351\",\"administrativeArea\":\"CA\",\"locality\":\"MOUNTAIN VIEW\",\"addressLines\":[\"1600 AMPHITHEATRE PKWY\"],\"recipients\":[\"John Doe\"]},\"phoneNumber\":\"+1 123-456-7890\"},\"latestOrderId\":\"ORDER_ID\"}}" }
사용자 결정 처리
어시스턴트는 인텐트를 처리한 후 처리를 통해 요청을 보냅니다.
사용자의 답변이 포함된 actions_intent_TRANSACTION_DECISION
인텐트 사용
거래 결정입니다. 여러분은
TransactionDecisionValue가 포함된 Argument
입니다. 이 값에는 다음이 포함됩니다.
transactionDecision
- 제안된 광고에 대한 사용자의 결정 있습니다. 가능한 값은ORDER_ACCEPTED
,ORDER_REJECTED
,DELIVERY_ADDRESS_UPDATED
,CART_CHANGE_REQUESTED
,USER_CANNOT_TRANSACT
입니다.deliveryAddress
- 업데이트된 배송지 주소 사용자가 배송지 주소를 변경함 이 경우transactionDecision
은DELIVERY_ADDRESS_UPDATED
입니다.
이 요청을 올바르게 처리하려면 다음에서 트리거한 Dialogflow 인텐트를 선언합니다.
actions_intent_TRANSACTION_DECISION
이벤트 트리거되면 다음 위치에서 처리합니다.
다음과 같습니다.
Node.js
const arg = conv.arguments.get('TRANSACTION_DECISION_VALUE'); if (arg && arg.transactionDecision === 'ORDER_ACCEPTED') { console.log('Order accepted.'); const order = arg.order; }
Node.js
const arg = conv.arguments.get('TRANSACTION_DECISION_VALUE'); if (arg && arg.transactionDecision === 'ORDER_ACCEPTED') { console.log('Order accepted.'); const order = arg.order; }
자바
// Check transaction decision value Argument transactionDecisionValue = request .getArgument("TRANSACTION_DECISION_VALUE"); Map<String, Object> extension = null; if (transactionDecisionValue != null) { extension = transactionDecisionValue.getExtension(); } String transactionDecision = null; if (extension != null) { transactionDecision = (String) extension.get("transactionDecision"); } ResponseBuilder responseBuilder = getResponseBuilder(request); if ((transactionDecision != null && transactionDecision.equals("ORDER_ACCEPTED"))) { OrderV3 order = ((OrderV3) extension.get("order")); }
자바
// Check transaction decision value Argument transactionDecisionValue = request .getArgument("TRANSACTION_DECISION_VALUE"); Map<String, Object> extension = null; if (transactionDecisionValue != null) { extension = transactionDecisionValue.getExtension(); } String transactionDecision = null; if (extension != null) { transactionDecision = (String) extension.get("transactionDecision"); } ResponseBuilder responseBuilder = getResponseBuilder(request); if ((transactionDecision != null && transactionDecision.equals("ORDER_ACCEPTED"))) { OrderV3 order = ((OrderV3) extension.get("order")); }
JSON
{ "responseId": "aba44717-4236-4602-af55-e5ae1fc2d97a-594de0a7", "queryResult": { "queryText": "actions_intent_TRANSACTION_DECISION", "action": "transaction.decision.complete", "parameters": {}, "allRequiredParamsPresent": true, "fulfillmentText": "Failed to get transaction decision", "fulfillmentMessages": [ { "text": { "text": [ "Failed to get transaction decision" ] } } ], "outputContexts": [ { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_media_response_audio" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_audio_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_account_linking" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_screen_output" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_capability_web_browser" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/google_assistant_input_type_voice" }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/_actions_on_google", "lifespanCount": 97, "parameters": { "data": "{\"location\":{\"coordinates\":{\"latitude\":37.432524,\"longitude\":-122.098545},\"zipCode\":\"94043-1351\",\"city\":\"MOUNTAIN VIEW\",\"postalAddress\":{\"regionCode\":\"US\",\"postalCode\":\"94043-1351\",\"administrativeArea\":\"CA\",\"locality\":\"MOUNTAIN VIEW\",\"addressLines\":[\"1600 AMPHITHEATRE PKWY\"],\"recipients\":[\"John Doe\"]},\"phoneNumber\":\"+1 123-456-7890\"}}" } }, { "name": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy/contexts/actions_intent_transaction_decision", "parameters": { "TRANSACTION_DECISION_VALUE": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValue", "transactionDecision": "ORDER_ACCEPTED", "order": { "createTime": "2019-09-24T18:00:00.877Z", "lastUpdateTime": "2019-09-24T18:00:00.877Z", "merchantOrderId": "ORDER_ID", "userVisibleOrderId": "ORDER_ID", "transactionMerchant": { "id": "http://www.example.com", "name": "Example Merchant" }, "contents": { "lineItems": [ { "id": "LINE_ITEM_ID", "name": "Pizza", "description": "A four cheese pizza.", "priceAttributes": [ { "type": "REGULAR", "name": "Line Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 8990000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 9990000 }, "taxIncluded": true } ], "notes": [ "Extra cheese." ], "purchase": { "quantity": 1, "unitMeasure": { "measure": 1, "unit": "POUND" }, "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "prices": [ { "type": "REGULAR", "state": "ACTUAL", "name": "Item Price", "amount": { "currencyCode": "USD", "amountInMicros": 1000000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 1000000 }, "taxIncluded": true } ], "note": "Extra pepperoni", "quantity": 1, "subOptions": [] } ] } } ] }, "buyerInfo": { "email": "janedoe@gmail.com", "firstName": "Jane", "lastName": "Doe", "displayName": "Jane Doe" }, "priceAttributes": [ { "type": "SUBTOTAL", "name": "Subtotal", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 9990000 }, "taxIncluded": true }, { "type": "DELIVERY", "name": "Delivery", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 2000000 }, "taxIncluded": true }, { "type": "TAX", "name": "Tax", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 3780000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 15770000 }, "taxIncluded": true } ], "followUpActions": [ { "type": "VIEW_DETAILS", "title": "View details", "openUrlAction": { "url": "http://example.com" } }, { "type": "CALL", "title": "Call us", "openUrlAction": { "url": "tel:+16501112222" } }, { "type": "EMAIL", "title": "Email us", "openUrlAction": { "url": "mailto:person@example.com" } } ], "termsOfServiceUrl": "www.example.com", "note": "Sale event", "promotions": [ { "coupon": "COUPON_CODE" } ], "purchase": { "status": "CREATED", "userVisibleStatusLabel": "CREATED", "type": "FOOD", "returnsInfo": { "isReturnable": false, "daysToReturn": 1, "policyUrl": "http://www.example.com" }, "fulfillmentInfo": { "id": "FULFILLMENT_SERVICE_ID", "fulfillmentType": "DELIVERY", "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "location": {}, "price": { "type": "REGULAR", "name": "Delivery Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 2000000 }, "taxIncluded": true }, "fulfillmentContact": { "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson", "displayName": "John Johnson" } }, "purchaseLocationType": "ONLINE_PURCHASE" } } }, "text": "" } } ], "intent": { "name": "projects/df-transactions/agent/intents/fd16d86b-60db-4d19-a683-5b52a22f4795", "displayName": "Transaction Decision Complete" }, "intentDetectionConfidence": 1, "languageCode": "en" }, "originalDetectIntentRequest": { "source": "google", "version": "2", "payload": { "user": { "locale": "en-US", "lastSeen": "2019-09-23T19:49:32Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy", "type": "ACTIVE", "conversationToken": "[\"merchant_payment\"]" }, "inputs": [ { "intent": "actions.intent.TRANSACTION_DECISION", "rawInputs": [ { "inputType": "KEYBOARD" } ], "arguments": [ { "name": "TRANSACTION_DECISION_VALUE", "extension": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValue", "transactionDecision": "ORDER_ACCEPTED", "order": { "createTime": "2019-09-24T18:00:00.877Z", "lastUpdateTime": "2019-09-24T19:00:00.877Z", "merchantOrderId": "ORDER_ID", "userVisibleOrderId": "ORDER_ID", "transactionMerchant": { "id": "http://www.example.com", "name": "Example Merchant" }, "contents": { "lineItems": [ { "id": "LINE_ITEM_ID", "name": "Pizza", "description": "A four cheese pizza.", "priceAttributes": [ { "type": "REGULAR", "name": "Line Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 8990000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 9990000 }, "taxIncluded": true } ], "notes": [ "Extra cheese." ], "purchase": { "quantity": 1, "unitMeasure": { "measure": 1, "unit": "POUND" }, "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "prices": [ { "type": "REGULAR", "state": "ACTUAL", "name": "Item Price", "amount": { "currencyCode": "USD", "amountInMicros": 1000000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 1000000 }, "taxIncluded": true } ], "note": "Extra pepperoni", "quantity": 1, "subOptions": [] } ] } } ] }, "buyerInfo": { "email": "janedoe@gmail.com", "firstName": "Jane", "lastName": "Doe", "displayName": "Jane Doe" }, "priceAttributes": [ { "type": "SUBTOTAL", "name": "Subtotal", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 9990000 }, "taxIncluded": true }, { "type": "DELIVERY", "name": "Delivery", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 2000000 }, "taxIncluded": true }, { "type": "TAX", "name": "Tax", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 3780000 }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": 15770000 }, "taxIncluded": true } ], "followUpActions": [ { "type": "VIEW_DETAILS", "title": "View details", "openUrlAction": { "url": "http://example.com" } }, { "type": "CALL", "title": "Call us", "openUrlAction": { "url": "tel:+16501112222" } }, { "type": "EMAIL", "title": "Email us", "openUrlAction": { "url": "mailto:person@example.com" } } ], "termsOfServiceUrl": "www.example.com", "note": "Sale event", "promotions": [ { "coupon": "COUPON_CODE" } ], "purchase": { "status": "CREATED", "userVisibleStatusLabel": "CREATED", "type": "FOOD", "returnsInfo": { "isReturnable": false, "daysToReturn": 1, "policyUrl": "http://www.example.com" }, "fulfillmentInfo": { "id": "FULFILLMENT_SERVICE_ID", "fulfillmentType": "DELIVERY", "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "location": {}, "price": { "type": "REGULAR", "name": "Delivery Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": 2000000 }, "taxIncluded": true }, "fulfillmentContact": { "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson", "displayName": "John Johnson" } }, "purchaseLocationType": "ONLINE_PURCHASE" } } } }, { "name": "text" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.SCREEN_OUTPUT" } ] } ] } }, "session": "projects/df-transactions/agent/sessions/ABwppHGYEP2Fj7tJBxoaKMevL6lZ2rs063lOEWhSW5etZWVOoJe7Dzm_bLejRTYIYXL3D78ER7YvA5aN9Wpy" }
JSON
{ "user": { "locale": "en-US", "lastSeen": "2019-11-11T23:57:31Z", "userVerificationStatus": "VERIFIED" }, "conversation": { "conversationId": "ABwppHGFwStZlYaQ9YT8rg9t_idVsxZrku1pUDrEbGSJmSUMatVdPwPEEQSCe1IwIBoN4sS4Weyn9pmgetEgbsWgw3JSvQmw", "type": "ACTIVE", "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\",\"location\":{\"coordinates\":{\"latitude\":37.421578499999995,\"longitude\":-122.0837816},\"zipCode\":\"94043-1351\",\"city\":\"MOUNTAIN VIEW\",\"postalAddress\":{\"regionCode\":\"US\",\"postalCode\":\"94043-1351\",\"administrativeArea\":\"CA\",\"locality\":\"MOUNTAIN VIEW\",\"addressLines\":[\"1600 AMPHITHEATRE PKWY\"],\"recipients\":[\"John Doe\"]},\"phoneNumber\":\"+1 123-456-7890\"},\"latestOrderId\":\"ORDER_ID\"}}" }, "inputs": [ { "intent": "actions.intent.TRANSACTION_DECISION", "rawInputs": [ {} ], "arguments": [ { "name": "TRANSACTION_DECISION_VALUE", "extension": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValue", "transactionDecision": "ORDER_ACCEPTED", "order": { "googleOrderId": "05528125187071048269", "merchantOrderId": "ORDER_ID", "userVisibleOrderId": "ORDER_ID", "buyerInfo": { "email": "janedoe@example.com", "firstName": "Jane", "lastName": "Doe", "displayName": "Jane Doe" }, "createTime": "2019-09-24T18:00:00.877Z", "lastUpdateTime": "2019-09-24T18:00:00.877Z", "transactionMerchant": { "id": "http://www.example.com", "name": "Example Merchant" }, "contents": { "lineItems": [ { "id": "LINE_ITEM_ID", "name": "Pizza", "priceAttributes": [ { "type": "REGULAR", "name": "Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "8990000" }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "9990000" }, "taxIncluded": true } ], "description": "A four cheese pizza.", "notes": [ "Extra cheese." ], "purchase": { "quantity": 1, "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "prices": [ { "type": "REGULAR", "name": "Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "1000000" }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "1000000" }, "taxIncluded": true } ], "note": "Extra pepperoni", "quantity": 1 } ], "unitMeasure": { "measure": 1, "unit": "POUND" } }, "vertical": { "@type": "type.googleapis.com/google.actions.orders.v3.verticals.purchase.PurchaseItemExtension", "quantity": 1, "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "prices": [ { "type": "REGULAR", "name": "Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "1000000" }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "1000000" }, "taxIncluded": true } ], "note": "Extra pepperoni", "quantity": 1 } ], "unitMeasure": { "measure": 1, "unit": "POUND" } } } ] }, "priceAttributes": [ { "type": "SUBTOTAL", "name": "Subtotal", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": "9990000" }, "taxIncluded": true }, { "type": "DELIVERY", "name": "Delivery", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "2000000" }, "taxIncluded": true }, { "type": "TAX", "name": "Tax", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": "3780000" }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": "15770000" }, "taxIncluded": true } ], "followUpActions": [ { "type": "VIEW_DETAILS", "title": "View details", "openUrlAction": { "url": "http://example.com" } }, { "type": "CALL", "title": "Call us", "openUrlAction": { "url": "tel:+16501112222" } }, { "type": "EMAIL", "title": "Email us", "openUrlAction": { "url": "mailto:person@example.com" } } ], "termsOfServiceUrl": "http://www.example.com", "note": "Sale event", "paymentData": { "paymentResult": { "merchantPaymentMethodId": "12345678" }, "paymentInfo": { "paymentMethodDisplayInfo": { "paymentType": "PAYMENT_CARD", "paymentMethodDisplayName": "VISA **** 1234" }, "paymentMethodProvenance": "PAYMENT_METHOD_PROVENANCE_MERCHANT" } }, "promotions": [ { "coupon": "COUPON_CODE" } ], "purchase": { "status": "CREATED", "type": "FOOD", "returnsInfo": { "daysToReturn": 1, "policyUrl": "http://www.example.com" }, "fulfillmentInfo": { "id": "FULFILLMENT_SERVICE_ID", "fulfillmentType": "DELIVERY", "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "location": { "coordinates": { "latitude": 37.421578499999995, "longitude": -122.0837816 }, "zipCode": "94043-1351", "city": "MOUNTAIN VIEW", "postalAddress": { "regionCode": "US", "postalCode": "94043-1351", "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "recipients": [ "John Doe" ] }, "phoneNumber": "+1 123-456-7890" }, "price": { "type": "REGULAR", "name": "Delivery Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "2000000" }, "taxIncluded": true }, "fulfillmentContact": { "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson", "displayName": "John Johnson" } }, "purchaseLocationType": "ONLINE_PURCHASE", "userVisibleStatusLabel": "CREATED" }, "vertical": { "@type": "type.googleapis.com/google.actions.orders.v3.verticals.purchase.PurchaseOrderExtension", "status": "CREATED", "type": "FOOD", "returnsInfo": { "daysToReturn": 1, "policyUrl": "http://www.example.com" }, "fulfillmentInfo": { "id": "FULFILLMENT_SERVICE_ID", "fulfillmentType": "DELIVERY", "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "location": { "coordinates": { "latitude": 37.421578499999995, "longitude": -122.0837816 }, "zipCode": "94043-1351", "city": "MOUNTAIN VIEW", "postalAddress": { "regionCode": "US", "postalCode": "94043-1351", "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "recipients": [ "John Doe" ] }, "phoneNumber": "+1 123-456-7890" }, "price": { "type": "REGULAR", "name": "Delivery Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "2000000" }, "taxIncluded": true }, "fulfillmentContact": { "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson", "displayName": "John Johnson" } }, "purchaseLocationType": "ONLINE_PURCHASE", "userVisibleStatusLabel": "CREATED" } } } }, { "name": "text" } ] } ], "surface": { "capabilities": [ { "name": "actions.capability.AUDIO_OUTPUT" }, { "name": "actions.capability.MEDIA_RESPONSE_AUDIO" }, { "name": "actions.capability.ACCOUNT_LINKING" }, { "name": "actions.capability.SCREEN_OUTPUT" } ] }, "availableSurfaces": [ { "capabilities": [ { "name": "actions.capability.SCREEN_OUTPUT" }, { "name": "actions.capability.WEB_BROWSER" }, { "name": "actions.capability.AUDIO_OUTPUT" } ] } ] }
5. 주문 완료 및 영수증 보내기
actions.intent.TRANSACTION_DECISION
인텐트가
transactionDecision
인코더에 전달하여 해당 작업을 즉시 수행해야 하는 경우ORDER_ACCEPTED
'확인'을 하려면 (예:
사용자에게 요금을 부과할 수 있음).
이 대답으로 대화를 종료할 수 있지만 간단한 대답을 포함해야 합니다.
대화를 이어 나가세요 이 초기 orderUpdate
를 제공하면
사용자에게 '접힌 영수증 카드'가 표시됩니다. 나머지 응답도 함께 표시됩니다
이 카드에는 사용자의 주문 내역에 있는 영수증이 반영됩니다.
주문 확인 중에는 주문 객체에 userVisibleOrderId
가 포함될 수 있습니다.
는 주문에 대해 사용자에게 표시되는 ID입니다. merchantOrderId
를 재사용할 수 있습니다.
이 필드에 입력하겠습니다.
OrderUpdate의 일부 객체에는 후속 조치 개체를 반환하며, 이 개체는 주문 세부정보의 하단에 URL 버튼으로 나타납니다. 어시스턴트 주문 내역에서 찾을 수 있습니다.
처리
Node.js
// Set lastUpdateTime and update status of order const order = arg.order; order.lastUpdateTime = '2019-09-24T19:00:00.877Z'; order.purchase.status = 'CONFIRMED'; order.purchase.userVisibleStatusLabel = 'Order confirmed'; // Send synchronous order update conv.ask(`Transaction completed! Your order` + ` ${conv.data.latestOrderId} is all set!`); conv.ask(new Suggestions('send order update')); conv.ask(new OrderUpdate({ type: 'SNAPSHOT', reason: 'Reason string', order: order, }));
Node.js
// Set lastUpdateTime and update status of order const order = arg.order; order.lastUpdateTime = '2019-09-24T19:00:00.877Z'; order.purchase.status = 'CONFIRMED'; order.purchase.userVisibleStatusLabel = 'Order confirmed'; // Send synchronous order update conv.ask(`Transaction completed! Your order ` + `${conv.data.latestOrderId} is all set!`); conv.ask(new Suggestions('send order update')); conv.ask(new OrderUpdate({ type: 'SNAPSHOT', reason: 'Reason string', order: order, }));
자바
OrderV3 order = ((OrderV3) extension.get("order")); order.setLastUpdateTime("2019-09-24T19:00:00.877Z"); // Update order status PurchaseOrderExtension purchaseOrderExtension = order.getPurchase(); purchaseOrderExtension.setStatus("CONFIRMED"); purchaseOrderExtension.setUserVisibleStatusLabel("Order confirmed"); order.setPurchase(purchaseOrderExtension); // Order update OrderUpdateV3 orderUpdate = new OrderUpdateV3() .setType("SNAPSHOT") .setReason("Reason string") .setOrder(order); Map<String, Object> conversationData = request.getConversationData(); String orderId = (String) conversationData.get("latestOrderId"); responseBuilder .add("Transaction completed! Your order " + orderId + " is all set!") .addSuggestions(new String[] {"send order update"}) .add(new StructuredResponse().setOrderUpdateV3(orderUpdate));
자바
OrderV3 order = ((OrderV3) extension.get("order")); order.setLastUpdateTime("2019-09-24T19:00:00.877Z"); // Update order status PurchaseOrderExtension purchaseOrderExtension = order.getPurchase(); purchaseOrderExtension.setStatus("CONFIRMED"); purchaseOrderExtension.setUserVisibleStatusLabel("Order confirmed"); order.setPurchase(purchaseOrderExtension); // Order update OrderUpdateV3 orderUpdate = new OrderUpdateV3() .setType("SNAPSHOT") .setReason("Reason string") .setOrder(order); Map<String, Object> conversationData = request.getConversationData(); String orderId = (String) conversationData.get("latestOrderId"); responseBuilder .add("Transaction completed! Your order " + orderId + " is all set!") .addSuggestions(new String[] {"send order update"}) .add(new StructuredResponse().setOrderUpdateV3(orderUpdate));
JSON
{ "payload": { "google": { "expectUserResponse": true, "richResponse": { "items": [ { "simpleResponse": { "textToSpeech": "Transaction completed! Your order undefined is all set!" } }, { "structuredResponse": { "orderUpdateV3": { "order": { "buyerInfo": { "displayName": "Jane Doe", "email": "janedoe@gmail.com", "firstName": "Jane", "lastName": "Doe" }, "contents": { "lineItems": [ { "description": "A four cheese pizza.", "id": "LINE_ITEM_ID", "name": "Pizza", "notes": [ "Extra cheese." ], "priceAttributes": [ { "amount": { "amountInMicros": 8990000, "currencyCode": "USD" }, "name": "Line Item Price", "state": "ACTUAL", "taxIncluded": true, "type": "REGULAR" }, { "amount": { "amountInMicros": 9990000, "currencyCode": "USD" }, "name": "Total Price", "state": "ACTUAL", "taxIncluded": true, "type": "TOTAL" } ], "purchase": { "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "note": "Extra pepperoni", "prices": [ { "amount": { "amountInMicros": 1000000, "currencyCode": "USD" }, "name": "Item Price", "state": "ACTUAL", "taxIncluded": true, "type": "REGULAR" }, { "amount": { "amountInMicros": 1000000, "currencyCode": "USD" }, "name": "Total Price", "state": "ACTUAL", "taxIncluded": true, "type": "TOTAL" } ], "quantity": 1, "subOptions": [] } ], "quantity": 1, "unitMeasure": { "measure": 1, "unit": "POUND" } } } ] }, "createTime": "2019-09-24T18:00:00.877Z", "followUpActions": [ { "openUrlAction": { "url": "http://example.com" }, "title": "View details", "type": "VIEW_DETAILS" }, { "openUrlAction": { "url": "tel:+16501112222" }, "title": "Call us", "type": "CALL" }, { "openUrlAction": { "url": "mailto:person@example.com" }, "title": "Email us", "type": "EMAIL" } ], "lastUpdateTime": "2019-09-24T19:00:00.877Z", "merchantOrderId": "ORDER_ID", "note": "Sale event", "priceAttributes": [ { "amount": { "amountInMicros": 9990000, "currencyCode": "USD" }, "name": "Subtotal", "state": "ESTIMATE", "taxIncluded": true, "type": "SUBTOTAL" }, { "amount": { "amountInMicros": 2000000, "currencyCode": "USD" }, "name": "Delivery", "state": "ACTUAL", "taxIncluded": true, "type": "DELIVERY" }, { "amount": { "amountInMicros": 3780000, "currencyCode": "USD" }, "name": "Tax", "state": "ESTIMATE", "taxIncluded": true, "type": "TAX" }, { "amount": { "amountInMicros": 15770000, "currencyCode": "USD" }, "name": "Total Price", "state": "ESTIMATE", "taxIncluded": true, "type": "TOTAL" } ], "promotions": [ { "coupon": "COUPON_CODE" } ], "purchase": { "fulfillmentInfo": { "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "fulfillmentContact": { "displayName": "John Johnson", "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson" }, "fulfillmentType": "DELIVERY", "id": "FULFILLMENT_SERVICE_ID", "location": {}, "price": { "amount": { "amountInMicros": 2000000, "currencyCode": "USD" }, "name": "Delivery Price", "state": "ACTUAL", "taxIncluded": true, "type": "REGULAR" } }, "purchaseLocationType": "ONLINE_PURCHASE", "returnsInfo": { "daysToReturn": 1, "isReturnable": false, "policyUrl": "http://www.example.com" }, "status": "CONFIRMED", "type": "FOOD", "userVisibleStatusLabel": "Order confirmed" }, "termsOfServiceUrl": "www.example.com", "transactionMerchant": { "id": "http://www.example.com", "name": "Example Merchant" }, "userVisibleOrderId": "ORDER_ID" }, "reason": "Reason string", "type": "SNAPSHOT" } } } ], "suggestions": [ { "title": "send order update" } ] } } } }
JSON
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.TEXT" } ], "inputPrompt": { "richInitialPrompt": { "items": [ { "simpleResponse": { "textToSpeech": "Transaction completed! Your order ORDER_ID is all set!" } }, { "structuredResponse": { "orderUpdateV3": { "type": "SNAPSHOT", "reason": "Reason string", "order": { "googleOrderId": "05528125187071048269", "merchantOrderId": "ORDER_ID", "userVisibleOrderId": "ORDER_ID", "buyerInfo": { "email": "janedoe@example.com", "firstName": "Jane", "lastName": "Doe", "displayName": "Jane Doe" }, "createTime": "2019-09-24T18:00:00.877Z", "lastUpdateTime": "2019-09-24T19:00:00.877Z", "transactionMerchant": { "id": "http://www.example.com", "name": "Example Merchant" }, "contents": { "lineItems": [ { "id": "LINE_ITEM_ID", "name": "Pizza", "priceAttributes": [ { "type": "REGULAR", "name": "Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "8990000" }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "9990000" }, "taxIncluded": true } ], "description": "A four cheese pizza.", "notes": [ "Extra cheese." ], "purchase": { "quantity": 1, "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "prices": [ { "type": "REGULAR", "name": "Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "1000000" }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "1000000" }, "taxIncluded": true } ], "note": "Extra pepperoni", "quantity": 1 } ], "unitMeasure": { "measure": 1, "unit": "POUND" } }, "vertical": { "@type": "type.googleapis.com/google.actions.orders.v3.verticals.purchase.PurchaseItemExtension", "quantity": 1, "itemOptions": [ { "id": "ITEM_OPTION_ID", "name": "Pepperoni", "prices": [ { "type": "REGULAR", "name": "Item Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "1000000" }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "1000000" }, "taxIncluded": true } ], "note": "Extra pepperoni", "quantity": 1 } ], "unitMeasure": { "measure": 1, "unit": "POUND" } } } ] }, "priceAttributes": [ { "type": "SUBTOTAL", "name": "Subtotal", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": "9990000" }, "taxIncluded": true }, { "type": "DELIVERY", "name": "Delivery", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "2000000" }, "taxIncluded": true }, { "type": "TAX", "name": "Tax", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": "3780000" }, "taxIncluded": true }, { "type": "TOTAL", "name": "Total Price", "state": "ESTIMATE", "amount": { "currencyCode": "USD", "amountInMicros": "15770000" }, "taxIncluded": true } ], "followUpActions": [ { "type": "VIEW_DETAILS", "title": "View details", "openUrlAction": { "url": "http://example.com" } }, { "type": "CALL", "title": "Call us", "openUrlAction": { "url": "tel:+16501112222" } }, { "type": "EMAIL", "title": "Email us", "openUrlAction": { "url": "mailto:person@example.com" } } ], "termsOfServiceUrl": "http://www.example.com", "note": "Sale event", "paymentData": { "paymentResult": { "merchantPaymentMethodId": "12345678" }, "paymentInfo": { "paymentMethodDisplayInfo": { "paymentType": "PAYMENT_CARD", "paymentMethodDisplayName": "VISA **** 1234" }, "paymentMethodProvenance": "PAYMENT_METHOD_PROVENANCE_MERCHANT" } }, "promotions": [ { "coupon": "COUPON_CODE" } ], "purchase": { "status": "CONFIRMED", "type": "FOOD", "returnsInfo": { "daysToReturn": 1, "policyUrl": "http://www.example.com" }, "fulfillmentInfo": { "id": "FULFILLMENT_SERVICE_ID", "fulfillmentType": "DELIVERY", "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "location": { "coordinates": { "latitude": 37.421578499999995, "longitude": -122.0837816 }, "zipCode": "94043-1351", "city": "MOUNTAIN VIEW", "postalAddress": { "regionCode": "US", "postalCode": "94043-1351", "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "recipients": [ "John Doe" ] }, "phoneNumber": "+1 123-456-7890" }, "price": { "type": "REGULAR", "name": "Delivery Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "2000000" }, "taxIncluded": true }, "fulfillmentContact": { "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson", "displayName": "John Johnson" } }, "purchaseLocationType": "ONLINE_PURCHASE", "userVisibleStatusLabel": "Order confirmed" }, "vertical": { "@type": "type.googleapis.com/google.actions.orders.v3.verticals.purchase.PurchaseOrderExtension", "status": "CREATED", "type": "FOOD", "returnsInfo": { "daysToReturn": 1, "policyUrl": "http://www.example.com" }, "fulfillmentInfo": { "id": "FULFILLMENT_SERVICE_ID", "fulfillmentType": "DELIVERY", "expectedFulfillmentTime": { "timeIso8601": "2019-09-25T18:00:00.877Z" }, "location": { "coordinates": { "latitude": 37.421578499999995, "longitude": -122.0837816 }, "zipCode": "94043-1351", "city": "MOUNTAIN VIEW", "postalAddress": { "regionCode": "US", "postalCode": "94043-1351", "administrativeArea": "CA", "locality": "MOUNTAIN VIEW", "addressLines": [ "1600 AMPHITHEATRE PKWY" ], "recipients": [ "John Doe" ] }, "phoneNumber": "+1 123-456-7890" }, "price": { "type": "REGULAR", "name": "Delivery Price", "state": "ACTUAL", "amount": { "currencyCode": "USD", "amountInMicros": "2000000" }, "taxIncluded": true }, "fulfillmentContact": { "email": "johnjohnson@gmail.com", "firstName": "John", "lastName": "Johnson", "displayName": "John Johnson" } }, "purchaseLocationType": "ONLINE_PURCHASE", "userVisibleStatusLabel": "CREATED" } } } } } ], "suggestions": [ { "title": "send order update" } ] } } } ], "conversationToken": "{\"data\":{\"paymentType\":\"merchant_payment\",\"location\":{\"coordinates\":{\"latitude\":37.421578499999995,\"longitude\":-122.0837816},\"zipCode\":\"94043-1351\",\"city\":\"MOUNTAIN VIEW\",\"postalAddress\":{\"regionCode\":\"US\",\"postalCode\":\"94043-1351\",\"administrativeArea\":\"CA\",\"locality\":\"MOUNTAIN VIEW\",\"addressLines\":[\"1600 AMPHITHEATRE PKWY\"],\"recipients\":[\"John Doe\"]},\"phoneNumber\":\"+1 123-456-7890\"},\"latestOrderId\":\"ORDER_ID\"}}" }
6. 주문 업데이트 보내기
판매자는 주문 상태를 처리하는 동안 사용자에게 주문 상태를 계속 알려야 합니다. 변경할 수 있습니다 HTTP를 전송하여 사용자 주문 업데이트 전송 주문 상태 및 세부정보와 함께 Orders API에 대한 PATCH 요청입니다.
Orders API에 대한 비동기 요청 설정
Orders API에 대한 주문 업데이트 요청이 액세스 권한으로 승인됨
토큰입니다. Orders API로 주문 업데이트를 PATCH하려면 JSON을 다운로드하세요.
Actions Console 프로젝트와 연결된 서비스 계정 키를 생성한 다음 교환합니다.
여기에 전달할 수 있는 Bearer 토큰의 서비스 계정 키입니다.
HTTP 요청의 Authorization
헤더입니다.
서비스 계정 키를 가져오려면 다음 단계를 수행합니다.
- Google Cloud 콘솔에서 메뉴 ❯ >로 이동합니다. API 및 서비스 > 사용자 인증 정보 > 사용자 인증 정보 만들기 > 서비스 계정 키.
- 서비스 계정에서 새 서비스 계정을 선택합니다.
- 서비스 계정을
service-account
로 설정합니다. - 역할을 프로젝트 > 소유자를 선택합니다.
- 키 유형을 JSON으로 설정합니다.
- 만들기를 선택합니다.
- 비공개 JSON 서비스 계정 키가 로컬 머신에 다운로드됩니다.
주문 업데이트 코드에서 서비스 키를 Bearer 토큰으로 교환할 수 있습니다. Google API 클라이언트 라이브러리 및 "https://www.googleapis.com/auth/actions.order.developer" 범위 사용 설치 단계와 API 클라이언트 라이브러리 GitHub 페이지의 예시
또한 Node.js 및 Java 샘플에서 order-update.js
를 참조할 수 있습니다.
예시 키 교환입니다
주문 업데이트 보내기
서비스 계정 키를 OAuth Bearer 토큰으로 교환한 후에는 주문 업데이트를 승인된 PATCH 요청으로 Orders API에 보낼 수 있습니다.
Orders API URL:
<ph type="x-smartling-placeholder">
PATCH https://actions.googleapis.com/v3/orders/${orderId}
</ph>
요청에 다음 헤더를 제공합니다.
"Authorization: Bearer token"
를 OAuth Bearer 토큰으로 바꿉니다. 서비스 계정 키를 교환한 경우에 사용합니다"Content-Type: application/json"
.
PATCH 요청은 다음 형식의 JSON 본문을 사용해야 합니다.
{ "orderUpdate": OrderUpdate }
OrderUpdate
객체는 다음과 같은 최상위 필드로 구성됩니다.
updateMask
- 업데이트할 주문의 필드입니다. 주문 상태, 값을purchase.status, purchase.userVisibleStatusLabel
로 설정합니다.order
- 업데이트 콘텐츠입니다. 주문의 콘텐츠가 반환되면 값을 업데이트된Order
객체로 설정합니다. 주문 상태를 업데이트하는 경우 (예:"CONFIRMED"
에서"SHIPPED"
까지) 다음 필드를 포함해야 합니다.merchantOrderId
-Order
객체에 설정한 것과 동일한 ID입니다.lastUpdateTime
- 이 업데이트의 타임스탬프입니다.purchase
- 다음을 포함하는 객체입니다. <ph type="x-smartling-placeholder">- </ph>
status
- 주문의 상태(PurchaseStatus
)입니다. 예: 'SHIPPED
' 또는 'DELIVERED
'.userVisibleStatusLabel
- 다음에 관한 세부정보를 제공하는 사용자 대상 라벨입니다. 주문 상태(예: '주문 상품 배송이 시작되었으며 있습니다.
userNotification
(선택사항) -userNotification
이 업데이트가 전송될 때 사용자 기기에 표시될 수 있는 객체입니다. 참고 이 객체를 포함한다고 해서 알림이 반드시 사용자의 기기에서 시작됩니다.
다음 샘플 코드는 OrderUpdate
DELIVERED
에게 주문 상태를 전송합니다.
Node.js
// Import the 'googleapis' module for authorizing the request. const {google} = require('googleapis'); // Import the 'request-promise' module for sending an HTTP POST request. const request = require('request-promise'); // Import the OrderUpdate class from the Actions on Google client library. const {OrderUpdate} = require('actions-on-google'); // Import the service account key used to authorize the request. // Replacing the string path with a path to your service account key. // i.e. const serviceAccountKey = require('./service-account.json') // Create a new JWT client for the Actions API using credentials // from the service account key. let jwtClient = new google.auth.JWT( serviceAccountKey.client_email, null, serviceAccountKey.private_key, ['https://www.googleapis.com/auth/actions.order.developer'], null, ); // Authorize the client let tokens = await jwtClient.authorize(); // Declare order update const orderUpdate = new OrderUpdate({ updateMask: [ 'lastUpdateTime', 'purchase.status', 'purchase.userVisibleStatusLabel', ].join(','), order: { merchantOrderId: orderId, // Specify the ID of the order to update lastUpdateTime: new Date().toISOString(), purchase: { status: 'DELIVERED', userVisibleStatusLabel: 'Order delivered', }, }, reason: 'Order status updated to delivered.', }); // Set up the PATCH request header and body, // including the authorized token and order update. let options = { method: 'PATCH', uri: `https://actions.googleapis.com/v3/orders/${orderId}`, auth: { bearer: tokens.access_token, }, body: { header: { isInSandbox: true, }, orderUpdate, }, json: true, }; // Send the PATCH request to the Orders API. try { await request(options); conv.close(`The order has been updated.`); } catch (e) { console.log(`Error: ${e}`); conv.close(`There was an error sending an order update.`); }
Node.js
// Import the 'googleapis' module for authorizing the request. const {google} = require('googleapis'); // Import the 'request-promise' module for sending an HTTP POST request. const request = require('request-promise'); // Import the OrderUpdate class from the Actions on Google client library. const {OrderUpdate} = require('actions-on-google'); // Import the service account key used to authorize the request. // Replacing the string path with a path to your service account key. // i.e. const serviceAccountKey = require('./service-account.json') // Create a new JWT client for the Actions API using credentials // from the service account key. let jwtClient = new google.auth.JWT( serviceAccountKey.client_email, null, serviceAccountKey.private_key, ['https://www.googleapis.com/auth/actions.order.developer'], null, ); // Authorize the client let tokens = await jwtClient.authorize(); // Declare order update const orderUpdate = new OrderUpdate({ updateMask: [ 'lastUpdateTime', 'purchase.status', 'purchase.userVisibleStatusLabel', ].join(','), order: { merchantOrderId: orderId, // Specify the ID of the order to update lastUpdateTime: new Date().toISOString(), purchase: { status: 'DELIVERED', userVisibleStatusLabel: 'Order delivered', }, }, reason: 'Order status updated to delivered.', }); // Set up the PATCH request header and body, // including the authorized token and order update. let options = { method: 'PATCH', uri: `https://actions.googleapis.com/v3/orders/${orderId}`, auth: { bearer: tokens.access_token, }, body: { header: { isInSandbox: true, }, orderUpdate, }, json: true, }; // Send the PATCH request to the Orders API. try { await request(options); conv.close(`The order has been updated.`); } catch (e) { console.log(`Error: ${e}`); conv.close(`There was an error sending an order update.`); }
자바
// Setup service account credentials String serviceAccountFile = MyActionsApp.class.getClassLoader() .getResource(SERVICE_ACCOUNT_KEY_FILE_NAME) .getFile(); InputStream actionsApiServiceAccount = new FileInputStream( serviceAccountFile); ServiceAccountCredentials serviceAccountCredentials = (ServiceAccountCredentials) ServiceAccountCredentials.fromStream(actionsApiServiceAccount) .createScoped(Collections.singleton( "https://www.googleapis.com/auth/actions.order.developer")); AccessToken token = serviceAccountCredentials.refreshAccessToken(); // Setup request with headers HttpPatch patchRequest = new HttpPatch( "https://actions.googleapis.com/v3/orders/" + orderId); patchRequest.setHeader("Content-type", "application/json"); patchRequest.setHeader("Authorization", "Bearer " + token.getTokenValue()); // Create order update FieldMask fieldMask = FieldMask.newBuilder().addAllPaths(Arrays.asList( "lastUpdateTime", "purchase.status", "purchase.userVisibleStatusLabel")) .build(); OrderUpdateV3 orderUpdate = new OrderUpdateV3() .setOrder(new OrderV3() .setMerchantOrderId(orderId) .setLastUpdateTime(Instant.now().toString()) .setPurchase(new PurchaseOrderExtension() .setStatus("DELIVERED") .setUserVisibleStatusLabel("Order delivered."))) .setUpdateMask(FieldMaskUtil.toString(fieldMask)) .setReason("Order status was updated to delivered."); // Setup JSON body containing order update JsonParser parser = new JsonParser(); JsonObject orderUpdateJson = parser.parse(new Gson().toJson(orderUpdate)).getAsJsonObject(); JsonObject body = new JsonObject(); body.add("orderUpdate", orderUpdateJson); JsonObject header = new JsonObject(); header.addProperty("isInSandbox", true); body.add("header", header); StringEntity entity = new StringEntity(body.toString()); entity.setContentType(ContentType.APPLICATION_JSON.getMimeType()); patchRequest.setEntity(entity); // Make request HttpClient httpClient = HttpClientBuilder.create().build(); HttpResponse response = httpClient.execute(patchRequest); LOGGER.info(response.getStatusLine().getStatusCode() + " " + response .getStatusLine().getReasonPhrase()); return getResponseBuilder(request) .add("The order has been updated.") .build();
자바
// Setup service account credentials String serviceAccountFile = MyActionsApp.class.getClassLoader() .getResource(SERVICE_ACCOUNT_KEY_FILE_NAME) .getFile(); InputStream actionsApiServiceAccount = new FileInputStream( serviceAccountFile); ServiceAccountCredentials serviceAccountCredentials = (ServiceAccountCredentials) ServiceAccountCredentials.fromStream(actionsApiServiceAccount) .createScoped(Collections.singleton( "https://www.googleapis.com/auth/actions.order.developer")); AccessToken token = serviceAccountCredentials.refreshAccessToken(); // Setup request with headers HttpPatch patchRequest = new HttpPatch( "https://actions.googleapis.com/v3/orders/" + orderId); patchRequest.setHeader("Content-type", "application/json"); patchRequest.setHeader("Authorization", "Bearer " + token.getTokenValue()); // Create order update FieldMask fieldMask = FieldMask.newBuilder().addAllPaths(Arrays.asList( "lastUpdateTime", "purchase.status", "purchase.userVisibleStatusLabel")) .build(); OrderUpdateV3 orderUpdate = new OrderUpdateV3() .setOrder(new OrderV3() .setMerchantOrderId(orderId) .setLastUpdateTime(Instant.now().toString()) .setPurchase(new PurchaseOrderExtension() .setStatus("DELIVERED") .setUserVisibleStatusLabel("Order delivered."))) .setUpdateMask(FieldMaskUtil.toString(fieldMask)) .setReason("Order status was updated to delivered."); // Setup JSON body containing order update JsonParser parser = new JsonParser(); JsonObject orderUpdateJson = parser.parse(new Gson().toJson(orderUpdate)).getAsJsonObject(); JsonObject body = new JsonObject(); body.add("orderUpdate", orderUpdateJson); JsonObject header = new JsonObject(); header.addProperty("isInSandbox", true); body.add("header", header); StringEntity entity = new StringEntity(body.toString()); entity.setContentType(ContentType.APPLICATION_JSON.getMimeType()); patchRequest.setEntity(entity); // Make request HttpClient httpClient = HttpClientBuilder.create().build(); HttpResponse response = httpClient.execute(patchRequest); LOGGER.info(response.getStatusLine().getStatusCode() + " " + response .getStatusLine().getReasonPhrase()); return getResponseBuilder(request) .add("The order has been updated.") .build();
구매 상태 설정
주문 업데이트 status
주문의 현재 상태를 설명해야 합니다. 업데이트의 order.purchase.status
필드에서 다음 값 중 하나를 사용합니다.
CREATED
- 사용자가 주문을 수락하고 '생성됨' 살펴봤습니다 백엔드에서 수동 처리가 필요합니다.CONFIRMED
- 주문이 활성 상태이며 처리를 위해 처리 중입니다.IN_PREPARATION
- 음식 등 주문 제품의 배송/배송 준비 중 조리 중인 음식 또는 포장된 품목 등이 있습니다.READY_FOR_PICKUP
- 수령인이 주문 상품을 수령할 수 있습니다.DELIVERED
- 주문이 수취인에게 배송됨OUT_OF_STOCK
- 주문에 포함된 하나 이상의 상품의 재고가 없습니다.CHANGE_REQUESTED
- 사용자가 주문 변경을 요청했으며 변경사항이 있습니다.RETURNED
- 배송 후 사용자가 주문 상품을 반품했습니다.REJECTED
- 처리, 청구 또는 기타 방법으로 처리할 수 없는 경우 "활성화" 확인할 수 있습니다.CANCELLED
- 사용자가 주문을 취소했습니다.
판매자와 관련된 각 상태에 대해 주문 업데이트를 전송해야 합니다.
발생합니다 예를 들어 다음 작업을 위해 트랜잭션에 수동 처리가 필요한 경우
주문을 완료한 후 기록하고 다음 시점까지 CREATED
주문 업데이트를 전송합니다.
추가 처리가 이루어집니다. 모든 주문에 모든 상태 값이 필요한 것은 아닙니다.
문제 해결
테스트 중에 문제가 발생하면 문제 해결 단계를 참고하세요. 100% 업타임 체크를 제공합니다