יצירת הזמנות (Dialogflow)

במדריך הזה תלמדו איך מפתחים פרויקט Actions שמשתמש ב-Orders API כדי לבצע הזמנות.

תהליך העסקה

כשפרויקט Actions עובד על הזמנות, מתבצע התהליך הבא:

  1. אימות דרישות לגבי עסקאות (אופציונלי) – משתמשים בכלי העזר לעזרה לגבי דרישות טרנזקציות בתחילת השיחה כדי לוודא שהמשתמש יכול לבצע עסקה.
  2. יצירת ההזמנה – מדריכים את המשתמש ב"רכבת עגלה", שבה הוא בונה את פרטי ההזמנה.
  3. מציעים את ההזמנה – בסיום ה"עגלת קניות", מציעים למשתמש את ההזמנה "הזמנה" כדי שהוא יוכל לוודא שהיא נכונה. אם ההזמנה תאושר, תקבלו תשובה עם פרטי ההזמנה.
  4. השלמת ההזמנה ושליחת קבלה – לאחר אישור ההזמנה, מעדכנים את מערכת ההזמנות ושולחים למשתמש קבלה.
  5. שליחת עדכונים לגבי הזמנות – במהלך משך החיים של ההזמנה, שולחים בקשות PATCH ל-Orders API כדי לעדכן את סטטוס ההזמנה למשתמש.

הגבלות והנחיות לבדיקה

חשוב לזכור שכללי מדיניות נוספים חלים על פעולות שמשתמשות ב-Orders API ובטרנזקציות. תהליך הבדיקה של פעולות עם עסקאות עשוי להימשך עד שישה שבועות, לכן כדאי לקחת בחשבון את הזמן הזה כשמתכננים את לוח הזמנים להפצה. כדי להקל על תהליך הבדיקה, חשוב להקפיד לפעול בהתאם למדיניות ולהנחיות בנושא עסקאות לפני ששולחים את הפעולה לבדיקה.

אפשר לפרוס את הפעולות שמשתמשות ב-Orders API רק במדינות הבאות:

אוסטרליה
ברזיל
קנדה
אינדונזיה
יפן
מקסיקו
קטאר
רוסיה
סינגפור
שווייץ
תאילנד
טורקיה
בריטניה
ארצות הברית

בניית הפרויקט

לקבלת דוגמאות מקיפות של שיחות עסקאות, עיין בדוגמאות של העסקאות שלנו ב-Node.js וב-Java.

הגדרת הפרויקט

כשיוצרים את הפעולה, צריך לציין שרוצים לבצע טרנזקציות בקונסולה ל-Actions. בנוסף, אם אתם משתמשים בספריית הלקוח של Node.JS, צריך להגדיר את מילוי ההזמנה כך שישתמש בגרסה האחרונה של ה-Orders API.

כדי להגדיר את הפרויקט ומילוי ההזמנות:

  1. אפשר ליצור פרויקט חדש או לייבא פרויקט קיים.
  2. מנווטים אל פריסה > פרטי הספרייה.
  3. בקטע מידע נוסף > עסקאות > מסמנים את התיבה שליד המשפט 'האם הפעולות משתמשות ב-Transactions API לביצוע עסקאות של מוצרים פיזיים?'.

  4. אם אתם משתמשים בספריית הלקוח של Node.JS כדי ליצור את מילוי הבקשה ב-Action, יש לפתוח את קוד מילוי הבקשה ולעדכן את הצגת האפליקציה כך שהדגל ordersv3 יוגדר ל-true. קטע הקוד הבא מציג דוגמה להצהרה של האפליקציה עבור Orders גרסה 3.

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,
});

1. אימות הדרישות לגבי עסקאות (אופציונלי)

חוויית משתמש

ברגע שהמשתמש מציין שהוא רוצה להגדיר הזמנה, מומלץ להפעיל את Intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK כדי להבטיח שהוא יוכל לבקש הזמנה. לדוגמה, כשמפעילים את הפעולה, עשויה להופיע השאלה "האם ברצונך לשמור מקום?" אם המשתמש אומר "כן", עליכם לבקש את הכוונה הזו מיד. כך אפשר לוודא שהם יוכלו להמשיך, ולתת להם הזדמנות לתקן הגדרות שמונעות מהם להמשיך בביצוע העסקה.

אם שולחים בקשה לגבי הדרישות לגבי עסקאות, בודקים את כוונת הרכישה באחת מהתוצאות הבאות:

  • אם הדרישות מתקיימות, מילוי הבקשה שלכם כולל Intent עם תנאי להצלחה, ותוכלו להמשיך בבניית ההזמנה של המשתמש.
  • אם אחת או יותר מהדרישות לא תקינות, מילוי הבקשה מקבל את הכוונה עם תנאי של כשל. במקרה כזה, תוכלו לסיים את השיחה או לעזוב את תהליך ההזמנה.

    אם המשתמשים יכולים לתקן את השגיאה, הם יתבקשו באופן אוטומטי לפתור את הבעיות במכשיר שלהם. אם השיחה מתנהלת במשטח קולי בלבד, כמו רמקול חכם, השיחה מועברת לטלפון של המשתמש.

טיפול בהזמנות

כדי לוודא שהמשתמש עומד בדרישות של הטרנזקציה, צריך לבקש למלא את ה-Intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK באמצעות אובייקט TransactionRequirementsCheckSpec.

לבדיקת הדרישות

צריך לבדוק אם המשתמש עונה על דרישות ההזמנה בספריית הלקוח:

Node.js
conv.ask(new TransactionRequirements());
Java
return getResponseBuilder(request)
    .add("Placeholder for transaction requirements text")
    .add(new TransactionRequirements())
    .build();
Dialogflow JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר תגובה של תגובה לפעולה מאתר אחר (webhook).

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
        }
      }
    }
  }
}
Actions SDK JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר תגובה של תגובה לפעולה מאתר אחר (webhook).

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
          }
        }
      ]
    }
  ]
}
קבלת התוצאה של בדיקת הדרישות

אחרי ש-Assistant ממלאת את הכוונה, היא שולחת בקשה למימוש עם הכוונה של actions.intent.TRANSACTION_REQUIREMENTS_CHECK עם תוצאת הבדיקה.

כדי לטפל בבקשה הזו כראוי, צריך להצהיר על Intent של Dialogflow שהופעל על ידי האירוע actions_intent_TRANSACTION_REQUIREMENTS_CHECK. אם מפעילים את האפשרות הזו, צריך לטפל בכוונה הזו במילוי הבקשה:

Node.js
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!`);
} else {
  conv.close('Transaction failed.');
}
Java
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) {
  responseBuilder.add("Looks like you're good to go! Now say 'confirm transaction'");
} else {
  responseBuilder.add("Transaction failed");
}
return responseBuilder.build();
Dialogflow JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר בקשת webhook.

{
  "responseId": "",
  "queryResult": {
    "queryText": "",
    "action": "",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "",
    "fulfillmentMessages": [],
    "outputContexts": [],
    "intent": {
      "name": "reservation_transaction_check_complete_df",
      "displayName": "reservation_transaction_check_complete_df"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": ""
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "isInSandbox": true,
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          }
        ]
      },
      "inputs": [
        {
          "rawInputs": [],
          "intent": "",
          "arguments": [
            {
              "extension": {
                "@type": "type.googleapis.com/google.transactions.v3.TransactionRequirementsCheckResult",
                "resultType": "CAN_TRANSACT"
              },
              "name": "TRANSACTION_REQUIREMENTS_CHECK_RESULT"
            }
          ]
        }
      ],
      "user": {},
      "conversation": {},
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
  },
  "session": ""
}
Actions SDK JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר בקשת webhook.

{
  "user": {},
  "device": {},
  "surface": {
    "capabilities": [
      {
        "name": "actions.capability.SCREEN_OUTPUT"
      },
      {
        "name": "actions.capability.AUDIO_OUTPUT"
      },
      {
        "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
      },
      {
        "name": "actions.capability.WEB_BROWSER"
      }
    ]
  },
  "conversation": {},
  "inputs": [
    {
      "rawInputs": [],
      "intent": "reservation_transaction_check_complete_asdk",
      "arguments": [
        {
          "extension": {
            "@type": "type.googleapis.com/google.transactions.v3.TransactionRequirementsCheckResult",
            "resultType": "CAN_TRANSACT"
          },
          "name": "TRANSACTION_REQUIREMENTS_CHECK_RESULT"
        }
      ]
    }
  ],
  "availableSurfaces": [
    {
      "capabilities": [
        {
          "name": "actions.capability.SCREEN_OUTPUT"
        },
        {
          "name": "actions.capability.AUDIO_OUTPUT"
        },
        {
          "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
        },
        {
          "name": "actions.capability.WEB_BROWSER"
        }
      ]
    }
  ]
}

2. יצירת ההזמנה

חוויית משתמש

אחרי שמקבלים את פרטי המשתמש הדרושים, אפשר ליצור חוויית 'הרכבת עגלת קניות' שמנחה את המשתמשים לבנות את ההזמנה. לכל פעולה יהיה תהליך הרכבת עגלת הקניות שונה מעט, בהתאם לשירות.

בחוויה הבסיסית של הרכבת עגלת קניות, המשתמשים בוחרים אפשרויות מתוך רשימה להוסיף להזמנה שלהם, אבל אפשר לעצב את השיחה כך שחוויית המשתמש תהיה פשוטה יותר. לדוגמה, תוכלו ליצור חוויה של הרכבה של עגלת קניות שמאפשרת למשתמש לקבוע הזמנה חודשית עם שאלה פשוטה של 'כן' או 'לא'. אפשר גם להציג למשתמש קרוסלה או כרטיס רשימה עם הזמנות "מומלצות".

מומלץ להשתמש בתגובות עשירות כדי להציג את האפשרויות של המשתמש באופן חזותי, אבל גם לעצב את השיחה כך שהמשתמש יוכל לבנות את עגלת הקניות באמצעות הקול בלבד. לקבלת שיטות מומלצות ודוגמאות של חוויות הרכבת עגלת הקניות, עיינו בהנחיות לעיצוב עסקאות.

טיפול בהזמנות

במהלך השיחה, עליכם לאסוף את פרטי ההזמנה שהמשתמש רוצה לרכוש, וליצור אובייקט Order.

Order חייב להכיל לפחות את הפרטים הבאים:

  • buyerInfo - מידע על המשתמש שקבע את ההזמנה.
  • transactionMerchant – מידע על המוכר שמסייע לביצוע ההזמנה.
  • contents - פרטי ההזמנה בפועל הרשומים כ-lineItems.

כדי לבנות את עגלת הקניות, עיינו במסמכי התגובה של Order. שימו לב שייתכן שתצטרכו לכלול שדות שונים בהתאם להזמנה.

הקוד לדוגמה הבא מציג הזמנת הזמנה מלאה, כולל שדות אופציונליים:

Node.js
app.intent('build_reservation_df', (conv) => {
  const now = new Date().toISOString();
  const order = {
    createTime: now,
    lastUpdateTime: now,
    merchantOrderId: 'UNIQUE_ORDER_ID',
    userVisibleOrderId: 'USER_VISIBLE_ORDER_ID',
    transactionMerchant: {
      id: 'https://www.example.com',
      name: 'Example Merchant',
    },
    contents: {
      lineItems: [
        {
          id: 'LINE_ITEM_ID',
          name: 'Dinner reservation',
          description: 'A world of flavors all in one destination.',
          reservation: {
            status: 'PENDING',
            userVisibleStatusLabel: 'Reservation is pending.',
            type: 'RESTAURANT',
            reservationTime: {
              timeIso8601: '2020-01-16T01:30:15.01Z',
            },
            userAcceptableTimeRange: {
              timeIso8601: '2020-01-15/2020-01-17',
            },
            partySize: 6,
            staffFacilitators: [
              {
                name: 'John Smith',
              },
            ],
            location: {
              zipCode: '94086',
              city: 'Sunnyvale',
              postalAddress: {
                regionCode: 'US',
                postalCode: '94086',
                administrativeArea: 'CA',
                locality: 'Sunnyvale',
                addressLines: [
                  '222, Some other Street',
                ],
              },
            },
          },
        },
      ],
    },
    buyerInfo: {
      email: 'janedoe@gmail.com',
      firstName: 'Jane',
      lastName: 'Doe',
      displayName: 'Jane Doe',
    },
    followUpActions: [
      {
        type: 'VIEW_DETAILS',
        title: 'View details',
        openUrlAction: {
          url: 'https://example.com',
        },
      },
      {
        type: 'CALL',
        title: 'Call us',
        openUrlAction: {
          url: 'tel:+16501112222',
        },
      },
      {
        type: 'EMAIL',
        title: 'Email us',
        openUrlAction: {
          url: 'mailto:person@example.com',
        },
      },
    ],
    termsOfServiceUrl: 'https://www.example.com',
  };

Java
private static OrderV3 createOrder() {
  // Transaction Merchant
  MerchantV3 transactionMerchant = new MerchantV3()
      .setId("http://www.example.com")
      .setName("Example Merchant");

  // Line Item

  // Reservation Item Extension
  ReservationItemExtension reservationItemExtension = new ReservationItemExtension()
      .setStatus("PENDING")
      .setUserVisibleStatusLabel("Reservation pending.")
      .setType("RESTAURANT")
      .setReservationTime(new TimeV3()
          .setTimeIso8601("2020-01-16T01:30:15.01Z"))
      .setUserAcceptableTimeRange(new TimeV3()
          .setTimeIso8601("2020-01-15/2020-01-17"))
      .setPartySize(6)
      .setStaffFacilitators(Collections.singletonList(new StaffFacilitator()
          .setName("John Smith")))
      .setLocation(new Location()
          .setZipCode("94086")
          .setCity("Sunnyvale")
          .setPostalAddress(new PostalAddress()
              .setRegionCode("US")
              .setPostalCode("94086")
              .setAdministrativeArea("CA")
              .setLocality("Sunnyvale")
              .setAddressLines(
                  Collections.singletonList("222, Some other Street"))));

  LineItemV3 lineItem = new LineItemV3()
      .setId("LINE_ITEM_ID")
      .setName("Dinner reservation")
      .setDescription("A world of flavors all in one destination.")
      .setReservation(reservationItemExtension);

  // 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");

  // 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 = "https://example.com";

  String now = Instant.now().toString();

  OrderV3 order = new OrderV3()
      .setCreateTime(now)
      .setLastUpdateTime(now)
      .setMerchantOrderId("UNIQUE_ORDER_ID")
      .setUserVisibleOrderId("UNIQUE_USER_VISIBLE_ORDER_ID")
      .setTransactionMerchant(transactionMerchant)
      .setContents(contents)
      .setBuyerInfo(buyerInfo)
      .setFollowUpActions(Arrays.asList(
          viewDetails,
          call,
          email
      ))
      .setTermsOfServiceUrl(termsOfServiceUrl);

  return order;
}
JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר תגובה של תגובה לפעולה מאתר אחר (webhook).

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_DECISION",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
          "order": {
            "createTime": "2019-07-17T18:25:30.182Z",
            "lastUpdateTime": "2019-07-17T18:25:30.182Z",
            "merchantOrderId": "UNIQUE_ORDER_ID",
            "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
            "transactionMerchant": {
              "id": "https://www.example.com",
              "name": "Example Merchant"
            },
            "contents": {
              "lineItems": [
                {
                  "id": "LINE_ITEM_ID",
                  "name": "Dinner reservation",
                  "description": "A world of flavors all in one destination.",
                  "reservation": {
                    "status": "PENDING",
                    "userVisibleStatusLabel": "Reservation is pending.",
                    "type": "RESTAURANT",
                    "reservationTime": {
                      "timeIso8601": "2020-01-16T01:30:15.01Z"
                    },
                    "userAcceptableTimeRange": {
                      "timeIso8601": "2020-01-15/2020-01-17"
                    },
                    "partySize": 6,
                    "staffFacilitators": [
                      {
                        "name": "John Smith"
                      }
                    ],
                    "location": {
                      "zipCode": "94086",
                      "city": "Sunnyvale",
                      "postalAddress": {
                        "regionCode": "US",
                        "postalCode": "94086",
                        "administrativeArea": "CA",
                        "locality": "Sunnyvale",
                        "addressLines": [
                          "222, Some other Street"
                        ]
                      }
                    }
                  }
                }
              ]
            },
            "buyerInfo": {
              "email": "janedoe@gmail.com",
              "firstName": "Jane",
              "lastName": "Doe",
              "displayName": "Jane Doe"
            },
            "followUpActions": [
              {
                "type": "VIEW_DETAILS",
                "title": "View details",
                "openUrlAction": {
                  "url": "https://example.com"
                }
              },
              {
                "type": "CALL",
                "title": "Call us",
                "openUrlAction": {
                  "url": "tel:+16501112222"
                }
              },
              {
                "type": "EMAIL",
                "title": "Email us",
                "openUrlAction": {
                  "url": "mailto:person@example.com"
                }
              }
            ],
            "termsOfServiceUrl": "https://www.example.com"
          },
          "orderOptions": {
            "requestDeliveryAddress": false,
            "userInfoOptions": {
              "userInfoProperties": [
                "EMAIL"
              ]
            }
          },
          "presentationOptions": {
            "actionDisplayName": "RESERVE"
          }
        }
      }
    }
  }
}

3. הצעת ההזמנה

מציגים את הזמנת ההזמנה למשתמש כדי שהוא יוכל לאשר או לדחות אותה. מבקשים מכוונת actions.intent.TRANSACTION_DECISION ומציינים את ה-Order שיצרתם.

חווית המשתמש

כשמבקשים את Intent של actions.intent.TRANSACTION_DECISION, Assistant מפעילה חוויה מובנית, שבה Order מרונדר ישירות ל "כרטיס תצוגה מקדימה של עגלת קניות". המשתמש יכול לומר "schedule order", לדחות את העסקה או לבקש לשנות את פרטי ההזמנה.

המשתמש יוכל גם לבקש לבצע שינויים בהזמנה בשלב הזה. במקרה כזה, עליכם לוודא שמילוי ההזמנות יכול לטפל בבקשות לשינוי הזמנה אחרי שמסיימים את תהליך הרכבת העגלה.

טיפול בהזמנות

כשמבקשים את ה-Intent actions.intent.TRANSACTION_DECISION, צריך ליצור TransactionDecision שמכיל את ה-Order ו-orderOptions

הקוד הבא הוא דוגמה לTransactionsDecision של הזמנה:

Node.js
conv.ask(new TransactionDecision({
  orderOptions: {
    requestDeliveryAddress: 'false',
  },
  presentationOptions: {
    actionDisplayName: 'RESERVE',
  },
  order: order,
}));
Java
// Create order options
OrderOptionsV3 orderOptions = new OrderOptionsV3()
    .setRequestDeliveryAddress(false)
    .setUserInfoOptions(new UserInfoOptions()
        .setUserInfoProperties(Collections.singletonList("EMAIL")));

// Create presentation options
PresentationOptionsV3 presentationOptions = new PresentationOptionsV3()
    .setActionDisplayName("RESERVE");

// Ask for transaction decision
return getResponseBuilder(request)
    .add("Placeholder for transaction decision text")
    .add(new TransactionDecision()
        .setOrder(order)
        .setOrderOptions(orderOptions)
        .setPresentationOptions(presentationOptions)
    )
    .build();
Dialogflow JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר תגובה של תגובה לפעולה מאתר אחר (webhook).

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_DECISION",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
          "orderOptions": {
            "requestDeliveryAddress": "false"
          },
          "presentationOptions": {
            "actionDisplayName": "RESERVE"
          },
          "order": {
            "createTime": "2019-07-17T18:25:30.184Z",
            "lastUpdateTime": "2019-07-17T18:25:30.184Z",
            "merchantOrderId": "UNIQUE_ORDER_ID",
            "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
            "transactionMerchant": {
              "id": "https://www.example.com",
              "name": "Example Merchant"
            },
            "contents": {
              "lineItems": [
                {
                  "id": "LINE_ITEM_ID",
                  "name": "Dinner reservation",
                  "description": "A world of flavors all in one destination.",
                  "reservation": {
                    "status": "PENDING",
                    "userVisibleStatusLabel": "Reservation is pending.",
                    "type": "RESTAURANT",
                    "reservationTime": {
                      "timeIso8601": "2020-01-16T01:30:15.01Z"
                    },
                    "userAcceptableTimeRange": {
                      "timeIso8601": "2020-01-15/2020-01-17"
                    },
                    "partySize": 6,
                    "staffFacilitators": [
                      {
                        "name": "John Smith"
                      }
                    ],
                    "location": {
                      "zipCode": "94086",
                      "city": "Sunnyvale",
                      "postalAddress": {
                        "regionCode": "US",
                        "postalCode": "94086",
                        "administrativeArea": "CA",
                        "locality": "Sunnyvale",
                        "addressLines": [
                          "222, Some other Street"
                        ]
                      }
                    }
                  }
                }
              ]
            },
            "buyerInfo": {
              "email": "janedoe@gmail.com",
              "firstName": "Jane",
              "lastName": "Doe",
              "displayName": "Jane Doe"
            },
            "followUpActions": [
              {
                "type": "VIEW_DETAILS",
                "title": "View details",
                "openUrlAction": {
                  "url": "https://example.com"
                }
              },
              {
                "type": "CALL",
                "title": "Call us",
                "openUrlAction": {
                  "url": "tel:+16501112222"
                }
              },
              {
                "type": "EMAIL",
                "title": "Email us",
                "openUrlAction": {
                  "url": "mailto:person@example.com"
                }
              }
            ],
            "termsOfServiceUrl": "https://www.example.com"
          }
        }
      }
    }
  }
}
Actions SDK JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר תגובה של תגובה לפעולה מאתר אחר (webhook).

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_DECISION",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
            "orderOptions": {
              "requestDeliveryAddress": "false"
            },
            "presentationOptions": {
              "actionDisplayName": "RESERVE"
            },
            "order": {
              "createTime": "2019-07-17T18:25:30.057Z",
              "lastUpdateTime": "2019-07-17T18:25:30.057Z",
              "merchantOrderId": "UNIQUE_ORDER_ID",
              "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
              "transactionMerchant": {
                "id": "https://www.example.com",
                "name": "Example Merchant"
              },
              "contents": {
                "lineItems": [
                  {
                    "id": "LINE_ITEM_ID",
                    "name": "Dinner reservation",
                    "description": "A world of flavors all in one destination.",
                    "reservation": {
                      "status": "PENDING",
                      "userVisibleStatusLabel": "Reservation is pending.",
                      "type": "RESTAURANT",
                      "reservationTime": {
                        "timeIso8601": "2020-01-16T01:30:15.01Z"
                      },
                      "userAcceptableTimeRange": {
                        "timeIso8601": "2020-01-15/2020-01-17"
                      },
                      "partySize": 6,
                      "staffFacilitators": [
                        {
                          "name": "John Smith"
                        }
                      ],
                      "location": {
                        "zipCode": "94086",
                        "city": "Sunnyvale",
                        "postalAddress": {
                          "regionCode": "US",
                          "postalCode": "94086",
                          "administrativeArea": "CA",
                          "locality": "Sunnyvale",
                          "addressLines": [
                            "222, Some other Street"
                          ]
                        }
                      }
                    }
                  }
                ]
              },
              "buyerInfo": {
                "email": "janedoe@gmail.com",
                "firstName": "Jane",
                "lastName": "Doe",
                "displayName": "Jane Doe"
              },
              "followUpActions": [
                {
                  "type": "VIEW_DETAILS",
                  "title": "View details",
                  "openUrlAction": {
                    "url": "https://example.com"
                  }
                },
                {
                  "type": "CALL",
                  "title": "Call us",
                  "openUrlAction": {
                    "url": "tel:+16501112222"
                  }
                },
                {
                  "type": "EMAIL",
                  "title": "Email us",
                  "openUrlAction": {
                    "url": "mailto:person@example.com"
                  }
                }
              ],
              "termsOfServiceUrl": "https://www.example.com"
            }
          }
        }
      ]
    }
  ]
}
טיפול בהחלטה של המשתמש

אחרי שהמשתמש מגיב להזמנה שהוצעה, מילוי הבקשה מקבל את ה-Intent actions_intent_TRANSACTION_DECISION עם ארגומנט שמכיל TransactionDecisionValue. הערך הזה יכלול את הפרטים הבאים:

  • transactionDecision - החלטת המשתמש לגבי ההזמנה המוצעת. הערכים האפשריים הם ORDER_ACCEPTED, ORDER_REJECTED, CART_CHANGE_REQUESTED ו-USER_CANNOT_TRANSACT.

כדי לטפל בבקשה הזו, צריך להצהיר על Intent של 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;
}
Java
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");
}
if ((transactionDecision != null && transactionDecision.equals("ORDER_ACCEPTED"))) {
  OrderV3 order = ((OrderV3) extension.get("order"));
}
Dialogflow JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר בקשת webhook.

{
  "responseId": "",
  "queryResult": {
    "queryText": "",
    "action": "",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "",
    "fulfillmentMessages": [],
    "outputContexts": [],
    "intent": {
      "name": "reservation_get_transaction_decision_df",
      "displayName": "reservation_get_transaction_decision_df"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": ""
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "isInSandbox": true,
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          }
        ]
      },
      "inputs": [
        {
          "rawInputs": [],
          "intent": "",
          "arguments": []
        }
      ],
      "user": {},
      "conversation": {},
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
  },
  "session": ""
}
Actions SDK JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר בקשת webhook.

{
  "user": {},
  "device": {},
  "surface": {
    "capabilities": [
      {
        "name": "actions.capability.SCREEN_OUTPUT"
      },
      {
        "name": "actions.capability.AUDIO_OUTPUT"
      },
      {
        "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
      },
      {
        "name": "actions.capability.WEB_BROWSER"
      }
    ]
  },
  "conversation": {},
  "inputs": [
    {
      "rawInputs": [],
      "intent": "reservation_get_transaction_decision_asdk",
      "arguments": []
    }
  ],
  "availableSurfaces": [
    {
      "capabilities": [
        {
          "name": "actions.capability.SCREEN_OUTPUT"
        },
        {
          "name": "actions.capability.AUDIO_OUTPUT"
        },
        {
          "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
        },
        {
          "name": "actions.capability.WEB_BROWSER"
        }
      ]
    }
  ]
}

4. השלמת ההזמנה ושליחת קבלה

כשה-Intent actions.intent.TRANSACTION_DECISION חוזר עם transactionDecision מ-ORDER_ACCEPTED, מבצעים את העיבוד שנדרש כדי לתזמן את ההזמנה (למשל לשמור אותו במסד הנתונים שלכם).

לשלוח תגובה פשוטה כדי שהשיחה תמשיך לנוע. המשתמש יקבל "כרטיס קבלה מכווץ" יחד עם התשובה שלכם.

טיפול בהזמנות

Node.js
// Set lastUpdateTime and update status of reservation
order.lastUpdateTime = new Date().toISOString();
order.reservation.status = 'CONFIRMED';
order.reservation.userVisibleStatusLabel = 'Reservation confirmed';
order.reservation.confirmationCode = '123ABCDEFGXYZ';

// Send synchronous order update
conv.ask(`Transaction completed! You're all set!`);
conv.ask(new OrderUpdate({
  type: 'SNAPSHOT',
  reason: 'Reason string',
  order: order,
}));
Java
ResponseBuilder responseBuilder = getResponseBuilder(request);
order.setLastUpdateTime(Instant.now().toString());

// Set reservation status to confirmed and provide confirmation code
LineItemV3 lineItem = order.getContents().getLineItems().get(0);
ReservationItemExtension reservationItemExtension = lineItem.getReservation();
reservationItemExtension.setStatus("CONFIRMED");
reservationItemExtension.setUserVisibleStatusLabel("Reservation confirmed.");
reservationItemExtension.setConfirmationCode("123ABCDEFGXYZ");
lineItem.setReservation(reservationItemExtension);
order.getContents().getLineItems().set(0, lineItem);

// Order update
OrderUpdateV3 orderUpdate = new OrderUpdateV3()
    .setType("SNAPSHOT")
    .setReason("Reason string")
    .setOrder(order);

responseBuilder
    .add("Transaction completed! You're all set! Would you like to do anything else?")
    .add(new StructuredResponse().setOrderUpdateV3(orderUpdate));
return responseBuilder.build();
Dialogflow JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר תגובה של תגובה לפעולה מאתר אחר (webhook).

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Transaction completed! You're all set!"
            }
          },
          {
            "structuredResponse": {
              "orderUpdateV3": {
                "type": "SNAPSHOT",
                "reason": "Reason string",
                "order": {
                  "merchantOrderId": "UNIQUE_ORDER_ID",
                  "reservation": {
                    "status": "CONFIRMED",
                    "userVisibleStatusLabel": "Reservation confirmed",
                    "confirmationCode": "123ABCDEFGXYZ"
                  },
                  "lastUpdateTime": "2019-07-17T18:25:30.187Z"
                }
              }
            }
          }
        ]
      }
    }
  }
}
Actions SDK JSON

שימו לב: קובץ ה-JSON שמוצג בהמשך מתאר תגובה של תגובה לפעולה מאתר אחר (webhook).

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Transaction completed! You're all set!"
              }
            },
            {
              "structuredResponse": {
                "orderUpdateV3": {
                  "type": "SNAPSHOT",
                  "reason": "Reason string",
                  "order": {
                    "merchantOrderId": "UNIQUE_ORDER_ID",
                    "reservation": {
                      "status": "CONFIRMED",
                      "userVisibleStatusLabel": "Reservation confirmed",
                      "confirmationCode": "123ABCDEFGXYZ"
                    },
                    "lastUpdateTime": "2019-07-17T18:25:30.059Z"
                  }
                }
              }
            }
          ]
        }
      }
    }
  ]
}

5. שליחת עדכונים לגבי הזמנות

סטטוס ההזמנה משתנה במהלך משך החיים שלו. שליחת עדכונים של הזמנות של משתמשים עם בקשות HTTP PATCH אל ה-Orders API, ומכילים את סטטוס ההזמנה והפרטים שלה.

הגדרת בקשות אסינכרוניות ל-Orders API

בקשות לעדכון הזמנות ל-Orders API מורשות באמצעות אסימון גישה. כדי לבצע תיקון של עדכון הזמנה ל-Orders API, צריך להוריד מפתח של חשבון שירות JSON שמשויך לפרויקט Actions Console, ואז להחליף את המפתח של חשבון השירות באסימון למוכ"ז שאפשר להעביר אל הכותרת Authorization של בקשת ה-HTTP.

כדי לאחזר את המפתח של חשבון השירות, יש לבצע את השלבים הבאים:

  1. במסוף Google Cloud, נכנסים לתפריט q החלט > ממשקי API ושירותים > פרטי כניסה > יצירת פרטי כניסה > מפתח חשבון שירות.
  2. בקטע חשבון שירות, בוחרים באפשרות חשבון שירות חדש.
  3. מגדירים את חשבון השירות לערך service-account.
  4. מגדירים את התפקיד בתור פרויקט > בעלים.
  5. מגדירים את סוג המפתח כ-JSON.
  6. בוחרים באפשרות יצירה.
  7. תתבצע הורדה של מפתח חשבון שירות פרטי מסוג JSON למחשב המקומי.

בקוד של עדכון ההזמנה, מחליפים את מפתח השירות באסימון למוכ"ז באמצעות ספריית הלקוח של Google APIs וההיקף "https://www.googleapis.com/auth/actions.order.developer". תוכלו למצוא דוגמאות ושלבי ההתקנה בדף של GitHub בספריית הלקוח של ה-API.

אפשר להיעזר ב-order-update.js בדוגמאות של Node.js ו-Java לדוגמה להחלפת מפתחות.

שליחת עדכונים לגבי הזמנות

אחרי שמעבירים את המפתח של חשבון השירות באסימון למוכ"ז OAuth, שולחים ל-Orders API עדכונים של הזמנות כבקשות מורשות ל-PATCH.

כתובת ה-URL של Orders API: PATCH https://actions.googleapis.com/v3/orders/${orderId}

כלול בבקשה שלך את הכותרות הבאות:

  • "Authorization: Bearer token" באסימון למוכ"ז OAuth שעבורו המרתם את המפתח של חשבון השירות.
  • "Content-Type: application/json".

בקשת ה-PATCH צריכה לכלול גוף JSON בפורמט הבא:

{ "orderUpdate": OrderUpdate }

האובייקט OrderUpdate מורכב מהשדות הבאים ברמה העליונה:

  • updateMask - השדות בהזמנה שמעדכנים. כדי לעדכן את סטטוס ההזמנה, צריך להגדיר את הערך reservation.status, reservation.userVisibleStatusLabel.
  • order - תוכן העדכון. אם אתם מעדכנים את התוכן של ההזמנה, צריך להגדיר את הערך לאובייקט Order המעודכן. אם אתם רק מעדכנים את הסטטוס של ההזמנה (לדוגמה, מ-"PENDING" ל-"FULFILLED"), האובייקט מכיל את השדות הבאים:

    • merchantOrderId – אותו מזהה שהגדרתם באובייקט Order.
    • lastUpdateTime - חותמת הזמן של העדכון הזה.
    • purchase – אובייקט שמכיל את הרכיבים הבאים:
      • status - סטטוס ההזמנה כ-ReservationStatus, כגון "CONFIRMED" או "CANCELLED".
      • userVisibleStatusLabel – תווית שגלויה למשתמש עם פרטים על סטטוס ההזמנה, כמו 'ההזמנה אושרה'.
  • userNotification שניתן להציג במכשיר של המשתמש כשהעדכון הזה נשלח. שימו לב שהוספה של האובייקט הזה לא מבטיחה שתופיע התראה במכשיר של המשתמש.

הקוד לדוגמה הבא מציג דוגמה OrderUpdate שמעדכנת את הסטטוס של הזמנת ההזמנה ל-FULFILLED:

Node.js
// Import the 'googleapis' module for authorizing the request.
const {google} = require('googleapis');
// Import the 'request' module for sending an HTTP POST request.
const request = require('request');
// 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. Replace the string path with a path to your service account key.
const key = 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(
    key.client_email,
    null,
    key.private_key,
    ['https://www.googleapis.com/auth/actions.order.developer'],
    null
);
// Authorize the client asynchronously, passing in a callback to run upon authorization.
jwtClient.authorize((err, tokens) => {
    if (err) {
        console.log(err);
        return;
    }
    // Declare the ID of the order to update.
    const orderId = '<UNIQUE_MERCHANT_ORDER_ID>';

    const orderUpdateJson = new OrderUpdate({
        updateMask: [
          'lastUpdateTime',
          'contents.lineItems.reservation.status',
          'contents.lineItems.reservation.userVisibleStatusLabel',
      ].join(','),
        order: {
          merchantOrderId: orderId,
          lastUpdateTime: new Date().toISOString(),
          contents: {
            lineItems: [
              {
                reservation: {
                  status: 'FULFILLED',
                  userVisibleStatusLabel: 'Reservation fulfilled',
                },
              }
            ]
          }
        },
        reason: 'Reservation status was updated to fulfilled.',
    });

    // Set up the PATCH request header and body, including the authorized token
    // and order update.
    const bearer = 'Bearer ' + tokens.access_token;
    const options = {
        method: 'PATCH',
        url: `https://actions.googleapis.com/v3/orders/${orderId}`,
        headers: {
          'Authorization': bearer,
        },
        body: {
          header: {
            'isInSandbox': true,
          },
          orderUpdate: orderUpdateJson,
        },
        json: true,
      };
    // Send the PATCH request to the Orders API.
    request.patch(options, (err, httpResponse, body) => {
        if (err) {
            console.log('There was an error...');
            console.log(err);
            return;
        }
    });
});
Java
// Create order update
FieldMask fieldMask = FieldMask.newBuilder().addAllPaths(Arrays.asList(
    "lastUpdateTime",
    "contents.lineItems.reservation.status",
    "contents.lineItems.reservation.userVisibleStatusLabel"))
    .build();

OrderUpdateV3 orderUpdate = new OrderUpdateV3()
    .setOrder(new OrderV3()
        .setMerchantOrderId(orderId)
        .setLastUpdateTime(Instant.now().toString())
        .setContents(new OrderContents()
        .setLineItems(Collections.singletonList(new LineItemV3()
            .setReservation(new ReservationItemExtension()
                .setStatus("FULFILLED")
                .setUserVisibleStatusLabel("Reservation fulfilled."))))))
    .setUpdateMask(FieldMaskUtil.toString(fieldMask))
    .setReason("Reservation status was updated to fulfilled.");

// 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());
request.setEntity(entity);

// Make request
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse response = httpClient.execute(request);
הגדרת סטטוס ההזמנה

השדה ReservationStatus של עדכון ההזמנה חייב לתאר את המצב הנוכחי של ההזמנה. בשדה order.ReservationStatus של העדכון, משתמשים באחד מהערכים הבאים:

  • PENDING – ההזמנה 'נוצרה' על ידי הפעולה, אבל נדרש עיבוד נוסף בצד העורפי.
  • CONFIRMED - ההזמנה אושרה בקצה העורפי של התזמון.
  • CANCELLED - המשתמש ביטל את ההזמנה.
  • FULFILLED - השירות מילא את ההזמנה של המשתמש.
  • CHANGE_REQUESTED – המשתמש ביקש לבצע שינוי בהזמנה, והשינוי נמצא בעיבוד.
  • REJECTED - אם לא הצלחתם לעבד את ההזמנה או לאשר אותה בדרך אחרת.

צריך לשלוח עדכונים בקשר להזמנות לכל סטטוס שרלוונטי להזמנה. לדוגמה, אם ההזמנה שלכם דורשת עיבוד ידני כדי לאשר את ההזמנה אחרי שהיא התבקשה, עליכם לשלוח עדכון של ההזמנה מ-PENDING עד לסיום העיבוד הנוסף. לא בכל הזמנה נדרש כל ערך סטטוס.

פתרון בעיות

נתקלתם בבעיות במהלך הבדיקה? תוכלו לקרוא את השלבים לפתרון בעיות בעסקאות.