إنشاء حجوزات (Dialogflow)

سيرشدك هذا الدليل خلال عملية تطوير مشروع "المهام" الذي يستخدم واجهة برمجة تطبيقات الطلبات لإجراء الحجوزات.

مسار المعاملة

عندما يعالج مشروع Actions الحجوزات، فإنه يستخدم التدفق التالي:

  1. التحقّق من متطلبات المعاملة (اختياري): استخدِم مساعد متطلبات المعاملات في بداية المحادثة للتأكّد من أنّ المستخدم قادر على إجراء معاملة.
  2. إنشاء الطلب - قدِّم للمستخدم إرشادات حول "تجميع عربة التسوق" حيث يبني تفاصيل حجزه.
  3. اقتراح الترتيب - بمجرد اكتمال "سلة التسوق"، اقترح "طلب" الحجز على المستخدم، حتى يمكنه تأكيد صحته. إذا تم تأكيد الحجز، ستتلقى ردًا بتفاصيل الحجز.
  4. إنهاء الطلب وإرسال إيصال - بعد تأكيد الطلب، يمكنك تحديث نظام الحجز وإرسال إيصال إلى المستخدم.
  5. إرسال إشعارات بالطلب: خلال مدة الحجز، أدخِل تعديلات حالة الحجز على المستخدم من خلال إرسال طلبات PATCH إلى واجهة برمجة التطبيقات Orders API.

القيود وإرشادات المراجعة

ملاحظة: تنطبق سياسات إضافية على الإجراءات التي تستخدم المعاملات وOrders API. قد تستغرق مراجعة الإجراءات ذات المعاملات مدة تصل إلى ستة أسابيع، لذلك يُرجى أخذ هذا الوقت في الاعتبار عند التخطيط للجدول الزمني للإصدار. لتيسير عملية المراجعة، احرص على الالتزام بسياسات وإرشادات المعاملات قبل إرسال الإجراء الخاص بك للمراجعة.

يمكنك نشر الإجراءات التي تستخدم Orders API في البلدان التالية فقط:

أستراليا
البرازيل
كندا
إندونيسيا
اليابان
المكسيك
قطر
روسيا
سنغافورة
سويسرا
تايلاند
تركيا
المملكة المتحدة
الولايات المتحدة

إنشاء مشروعك

للحصول على أمثلة شاملة لمحادثات المعاملات، يمكنك الاطّلاع على نماذج المعاملات في Node.js وJava.

إعداد المشروع

عند إنشاء الإجراء الخاص بك، يجب تحديد أنّك تريد تنفيذ معاملات في وحدة تحكّم الإجراءات. بالإضافة إلى ذلك، إذا كنت تستخدم مكتبة عميل Node.JS، عليك إعداد عملية التنفيذ لاستخدام أحدث إصدار من Orders API.

لإعداد مشروعك وتنفيذه، قم بما يلي:

  1. أنشئ مشروعًا جديدًا أو استورِد مشروعًا حاليًا.
  2. انتقِل إلى نشر > معلومات الدليل.
  3. ضمن معلومات إضافية > المعاملات > ضَع علامة في المربّع "هل تستخدم إجراءاتك واجهة برمجة تطبيقات المعاملات لتنفيذ معاملات سلع مادية؟".

  4. إذا كنت تستخدم مكتبة عملاء Node.JS لإنشاء عملية تنفيذ الإجراء الخاص بك، افتح رمز التنفيذ وعدِّل اختيار تطبيقك لضبط العلامة ordersv3 على true. يعرض مقتطف الرمز التالي مثالاً لإعلان التطبيق لإصدار الطلبات 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- التحقّق من متطلبات المعاملة (اختياري)

تجربة المستخدم

عندما يُظهر المستخدم رغبته في إجراء حجز، ننصحك بتفعيل هدف actions.intent.TRANSACTION_REQUIREMENTS_CHECK للتأكّد من أنّه يمكنه طلب إجراء حجز. على سبيل المثال، عند استدعاء الإجراء الخاص بك، قد يطرح السؤال التالي: "هل تود حجز مقعد؟" إذا قال المستخدم "نعم"، يجب عليك طلب هذا الهدف على الفور. سيضمن ذلك إمكانية المتابعة وأن يمنحهم فرصة لإصلاح أي إعدادات تمنعهم من مواصلة المعاملة.

يؤدي طلب الغرض من التحقق من متطلبات المعاملات إلى إحدى النتائج التالية:

  • في حال استيفاء المتطلبات، يتلقى تنفيذك نية الشراء بشرط نجاح، ويمكنك متابعة عملية إنشاء طلب المستخدم.
  • في حال عدم استيفاء متطلب واحد أو أكثر من المتطلبات، يتلقى تنفيذك القصد بحالة الفشل. في هذه الحالة، قم بإنهاء المحادثة أو الابتعاد عن تدفق الحجز.

    إذا استطاع المستخدم إصلاح الخطأ، سيُطلب منه تلقائيًا حل هذه المشاكل على جهازه. إذا كانت المحادثة تتم على سطح مخصّص للصوت فقط مثل مكبّر صوت ذكي، يتم تسليمها إلى هاتف المستخدم.

توصيل الطلبات

لضمان استيفاء المستخدم لمتطلبات المعاملات، اطلب تنفيذ الغرض من 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 أدناه يصف الردّ التلقائي على الويب.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
        }
      }
    }
  }
}
حزمة SDK الخاصة بالإجراءات بتنسيق JSON

تجدر الإشارة إلى أنّ تنسيق JSON أدناه يصف الردّ التلقائي على الويب.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
          }
        }
      ]
    }
  ]
}
تلقّي نتيجة التحقّق من المتطلبات

بعد أن يستوفي "مساعد Google" الهدف، يرسل طلب التنفيذ بهدف actions.intent.TRANSACTION_REQUIREMENTS_CHECK بنتيجة الفحص.

لمعالجة هذا الطلب بشكل صحيح، يجب تحديد هدف 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 أدناه يصف طلب الردّ التلقائي على الويب.

{
  "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": ""
}
حزمة SDK الخاصة بالإجراءات بتنسيق JSON

تجدر الإشارة إلى أنّ ملف JSON أدناه يصف طلب الردّ التلقائي على الويب.

{
  "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 أدناه يصف الردّ التلقائي على الويب.

{
  "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 التي أنشأتها.

انطباع المستخدم

عند طلب هدف actions.intent.TRANSACTION_DECISION، يبدأ "مساعد Google" تجربة مضمّنة يتم فيها عرض Order مباشرةً على "بطاقة معاينة سلة التسوّق". يمكن للمستخدم قول "جدولة الحجز" أو رفض المعاملة أو طلب تغيير تفاصيل الحجز.

ويمكن للمستخدم أيضًا طلب إجراء تغييرات على الطلب في هذه المرحلة. في هذه الحالة، يجب عليك التأكد من أن عملية التنفيذ يمكنها التعامل مع طلبات تغيير الطلبات بعد إنهاء تجربة تجميع عربة التسوق.

توصيل الطلبات

عندما تطلب هدف 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 أدناه يصف الردّ التلقائي على الويب.

{
  "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"
          }
        }
      }
    }
  }
}
حزمة SDK الخاصة بالإجراءات بتنسيق JSON

تجدر الإشارة إلى أنّ تنسيق JSON أدناه يصف الردّ التلقائي على الويب.

{
  "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"
            }
          }
        }
      ]
    }
  ]
}
التعامل مع قرار المستخدم

بعد أن يردّ المستخدم على الطلب المُقترَح، يتلقّى تنفيذك الغرض actions_intent_TRANSACTION_DECISION من خلال وسيطة تحتوي على TransactionDecisionValue. تحتوي هذه القيمة على ما يلي:

  • transactionDecision - قرار المستخدم بشأن الطلب المقترَح. والقيم المحتملة هي ORDER_ACCEPTED وORDER_REJECTED وCART_CHANGE_REQUESTED وUSER_CANNOT_TRANSACT.

لمعالجة هذا الطلب، يجب تحديد هدف 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 أدناه يصف طلب الردّ التلقائي على الويب.

{
  "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": ""
}
حزمة SDK الخاصة بالإجراءات بتنسيق JSON

تجدر الإشارة إلى أنّ ملف JSON أدناه يصف طلب الردّ التلقائي على الويب.

{
  "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. إنهاء الحجز وإرسال إيصال

عندما يعود الغرض من 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 أدناه يصف الردّ التلقائي على الويب.

{
  "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"
                }
              }
            }
          }
        ]
      }
    }
  }
}
حزمة SDK الخاصة بالإجراءات بتنسيق JSON

تجدر الإشارة إلى أنّ تنسيق JSON أدناه يصف الردّ التلقائي على الويب.

{
  "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 مرتبط بمشروعك على "وحدة تحكّم المهام"، ثم استبدال مفتاح حساب الخدمة برمز حامل مميّز يمكن تمريره إلى رأس Authorization لطلب HTTP.

لاسترداد مفتاح حساب الخدمة، عليك اتّباع الخطوات التالية:

  1. في Google Cloud Console، انتقِل إلى القائمة أعضاء > واجهات برمجة التطبيقات والخدمات > بيانات الاعتماد > إنشاء بيانات اعتماد > مفتاح حساب الخدمة.
  2. ضمن حساب الخدمة، اختَر حساب خدمة جديد.
  3. اضبط حساب الخدمة على service-account.
  4. اضبط الدور على المشروع > المالك.
  5. اضبط نوع المفتاح على JSON.
  6. اختَر إنشاء.
  7. سيتم تنزيل مفتاح حساب خدمة JSON خاص على جهازك المحلي.

في رمز تعديلات الطلب، استبدِل مفتاح الخدمة برمز مميز للحامل باستخدام مكتبة عملاء Google APIs ونطاق "https://www.googleapis.com/auth/actions.order.developer". يمكنك العثور على خطوات التثبيت وأمثلة عليه في صفحة GitHub في مكتبة عميل واجهة برمجة التطبيقات.

يمكنك الرجوع إلى order-update.js في نماذج Node.js وJava للحصول على مثال لتبادل المفاتيح.

إرسال آخر أخبار الطلب

بعد استبدال مفتاح حساب الخدمة برمز مميز لحامل OAuth، أرسِل آخر المعلومات حول الطلب كطلبات تصحيح معتمدة إلى واجهة برمجة التطبيقات Orders API.

عنوان URL لواجهة برمجة تطبيقات الطلبات: 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 إلى حين الانتهاء من المعالجة الإضافية. لا يتطلب كل حجز كل قيمة للحالة.

تحديد المشاكل وحلّها

إذا واجهت أي مشاكل أثناء الاختبار، يمكنك الاطّلاع على خطوات تحديد المشاكل وحلّها للمعاملات.