透過 Google Pay 建立實體交易

本指南將逐步引導您開發 Actions 專案,其中包含實體商品交易,以及使用 Google Pay 支付款項。

交易流程

Actions 專案使用商家管理的付款處理實體交易時,會使用下列流程:

  1. 收集資訊 (選填):視交易性質而定,您可能想要在對話開始時向使用者收集下列資訊:
    1. 驗證交易需求 - 在對話開始時,驗證使用者是否符合交易的需求,例如:正確設定付款資訊且可供使用,以便進行購物車。
    2. 要求寄送地址:如果交易需要寄送地址,請向使用者收集地址。
  2. 建立訂單:引導使用者完成「購物車組裝」流程,從中挑選要購買的商品。
  3. 建議訂單 - 購物車完成後,請向使用者顯示訂單,以便確認訂單正確。如果訂單已確認,您會收到回應,內含訂單詳細資料和付款權杖。
  4. 完成訂單並傳送收據:在訂單確認後,更新庫存追蹤或其他出貨服務,然後傳送收據給使用者。
  5. 傳送訂單更新 - 在訂單執行期限的效期內,傳送 PATCH 要求至 Orders API,掌握使用者訂單更新情形。

限制和查看規範

請注意,「用於交易的動作」須遵守額外政策。我們最多需要六週的時間審查含有交易的動作,因此在規劃發布時間表時,請將這段時間納入考量。為簡化審查程序,在將動作送交審查之前,請務必遵循交易政策與規範

您只能在下列國家/地區部署販售實體商品的動作:

澳洲
巴西
加拿大
印尼
日本
墨西哥
俄羅斯
新加坡
泰國
土耳其
英國
美國

建構您的專案

如需完整的交易對話範例,請參閱 Node.js 交易範例

設定

建立動作時,您必須在動作主控台中指定要執行交易。

如要設定專案和執行要求,請按照以下步驟操作:

  1. 建立新專案或匯入現有專案。
  2. 依序前往「Deploy」(部署) >「Directory information」(目錄資訊)
  3. 在「其他資訊」>「交易」下方,勾選「您的動作是否使用 Transactions API 執行實體商品交易?」方塊。

1. 收集資訊 (選填)

1a. 驗證交易規定 (選用)

使用者表示要購買後,您應立即進行檢查,以確保對方能順利進行交易。舉例來說,系統叫用您的動作時,可能會詢問「Do you need to order to order to order or check your account account?」如果使用者說「訂購鞋子」,您應確保他們可以繼續操作,並讓使用者有機會修正任何設定,防止他們繼續進行交易。如要這麼做,您應轉換至執行交易需求檢查的情境。

「建立交易要求」檢查場景
  1. 在「場景」分頁中,新增名為 TransactionRequirementsCheck 的場景。
  2. 在「運算單元填充」下方,按一下「+」來新增版位。
  3. 在「Select type」(選取類型) 下方,選取 actions.type.TransactionRequirementsCheckResult 做為運算單元類型。
  4. 在運算單元名稱欄位中,為版位提供名稱 TransactionRequirementsCheck
  5. 勾選「Customize slot value writeback」核取方塊 (預設為啟用)。
  6. 點按「儲存」

如果檢查交易需求條件,就會產生下列其中一項結果:

  • 如果符合要求,工作階段參數就會設定成功條件,您就可以繼續建立使用者訂單。
  • 如果無法滿足一或多項要求,工作階段參數會設為失敗條件。在這種情況下,您應該停止對話體驗,或是結束對話。
    • 如果使用者可以修正任何導致失敗狀態的錯誤,系統會提示使用者在裝置上解決這些問題。如果對話在純語音的途徑上進行,系統會轉接到使用者的手機。

處理交易需求檢查結果

  1. 在「Scenes」分頁中,選取新建立的 TransactionRequirementsCheck 場景。
  2. 在「條件」下方,按一下「+」新增條件。
  3. 在文字欄位中,輸入下列條件語法來檢查成功條件:

    scene.slots.status == "FINAL" && session.params.TransactionRequirementsCheck.resultType == "CAN_TRANSACT"
    
  4. 將遊標懸停在剛剛新增的條件上,然後按一下向上箭頭,將遊標放在 if scene.slots.status == "FINAL" 之前。

  5. 啟用「Send 提示」,並提供簡單的提示,讓使用者知道他們已準備好進行交易:

    candidates:
      - first_simple:
          variants:
            - speech: >-
                You are ready to purchase physical goods.
    
  6. 在「Transition」下方選取另一個場景,讓使用者可以繼續進行對話並繼續進行交易。

  7. 選取條件圖示 else if scene.slots.status == "FINAL"

  8. 啟用「Send 提示」,並透過簡單提示,讓使用者瞭解他們無法完成交易:

    candidates:
      - first_simple:
          variants:
            - speech: Transaction requirements check failed.
    
  9. 如果使用者無法進行交易,請選取「Transition」下方的「End 對話」以結束對話。

索取寄送地址

如果交易需要使用者的寄送地址,則應要求使用者提供地址。這有助於判斷總價、運送/取貨地點,或確保使用者位於您的服務區域內。為此,您應轉換至提示使用者輸入寄送地址的場景。

建立寄送地址場景

  1. 在「Scenes」分頁中新增名稱為 DeliveryAddress 的新場景。
  2. 在「運算單元填充」下方,按一下「+」來新增版位。
  3. 在「選取類型」下方,選取 actions.type.DeliveryAddressValue 做為運算單元類型。
  4. 在運算單元名稱欄位中,為版位提供名稱 TransactionDeliveryAddress
  5. 勾選「CustomizeSlot value writeback」(自訂運算單元值寫入) 核取方塊 (預設為啟用)。
  6. 按一下「Save」

設定運算單元時,您可以提供 reason,讓 Google 助理的要求能夠預先取得含有字串的位址。預設原因字串為「知道該在哪裡傳送訂單」。因此,Google 助理可能會詢問使用者:「想知道訂單送件在哪裡,我需要取得你的寄送地址」

  • 在顯示螢幕的介面上,使用者可以選擇要用於交易的位址。如果他們之前沒有提供地址,可以輸入新地址。
  • 在純語音介面上,Google 助理會要求使用者授予交易預設地址的權限。如果他們之前並未提供地址,系統會將對話交由手機進入。

如要處理寄送地址的結果,請按照下列步驟操作:

  1. 在「Scenes」分頁中,選取新建立的 DeliveryAddress 場景。
  2. 在「條件」下方,按一下「+」新增條件。
  3. 在文字欄位中,輸入下列條件語法來檢查成功條件:

    scene.slots.status == "FINAL" && session.params.TransactionDeliveryAddress.userDecision == "ACCEPTED"
    
  4. 將遊標懸停在剛剛新增的條件上,然後按一下向上箭頭,將遊標放在 if scene.slots.status == "FINAL" 之前。

  5. 啟用「Send 提示」,並透過簡單提示,讓使用者知道您已收到他們的地址:

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Great! Your order will be delivered to
                $session.params.TransactionDeliveryAddress.location.postalAddress.locality
                $session.params.TransactionDeliveryAddress.location.postalAddress.administrativeArea
                $session.params.TransactionDeliveryAddress.location.postalAddress.regionCode
                $session.params.TransactionDeliveryAddress.location.postalAddress.postalCode
    
  6. 在「Transition」下方,選取其他場景,以便使用者繼續進行對話。

  7. 選取條件 else if scene.slots.status == "FINAL"

  8. 啟用「Send 提示」,並透過簡單提示,讓使用者瞭解他們無法完成交易:

    candidates:
      - first_simple:
          variants:
            - speech: I failed to get your delivery address.
    
  9. 如果使用者無法進行交易,請選取「Transition」下方的「End 對話」以結束對話。

建立訂單

取得所需的使用者資訊後,您就可以建構「購物車組合」體驗,引導使用者建立訂單。每項動作的購物車組件流程,會因其產品或服務而異。

最基本的購物車組合體驗可讓使用者從清單中挑選要加入順序的商品,但您可以設計對話來簡化使用者體驗。您可以打造購物車組合體驗,讓使用者透過簡單的是非題,重新訂購最近的購買交易。您也可以向使用者展示熱門「精選」或「推薦」項目的輪轉介面或清單資訊卡。

我們建議使用豐富的回應以視覺化方式呈現使用者選項,同時設計對話,讓使用者僅使用語音建構購物車。如需優質購物車組合體驗的最佳做法和範例,請參閱設計指南

建立一筆訂單

在對話過程中,您需要收集使用者想購買的項目,然後建構 Order 物件。

您的 Order 至少必須包含下列項目:

  • buyerInfo - 購買使用者的相關資訊。
  • transactionMerchant - 處理訂單的商家相關資訊。
  • contents - 訂單的實際內容 (顯示為 lineItems)。
  • priceAttributes - 訂單的價格詳細資料,包括含折扣和稅金的訂單總費用。

請參閱 Order 回應說明文件,瞭解如何建構購物車。請注意,視順序而定,您可能需要加入不同的欄位。

以下程式碼範例顯示完整訂單,包括選填欄位:

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

建立訂單和簡報選項

使用者確認訂單前,系統會向他們顯示系統提議的訂單卡片。您可以設定各種順序和呈現方式選項,藉此自訂這張資訊卡向使用者顯示的方式。

以下是訂購需要寄送地址的訂單和呈現方式,包括使用者在訂單確認資訊卡中提供的電子郵件地址:

const orderOptions = {
      'requestDeliveryAddress': true,
      'userInfoOptions': {
        'userInfoProperties': ['EMAIL']
      }
    };

const presentationOptions = {
      'actionDisplayName': 'PLACE_ORDER'
    };

建立付款參數

paymentParameters 物件會包含根據您打算使用的 Google Pay 處理方 (例如 Stripe、Brintree、ACI 等) 而變更的權杖化參數。

const paymentParamenters = {
      'googlePaymentOption': {
        // facilitationSpec is expected to be a serialized JSON string
        'facilitationSpec': JSON.stringify({
          'apiVersion': 2,
          'apiVersionMinor': 0,
          'merchantInfo': {
            'merchantName': 'Example Merchant',
          },
          'allowedPaymentMethods': [
            {
              'type': 'CARD',
              'parameters': {
                'allowedAuthMethods': ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
                'allowedCardNetworks': [
                  'AMEX', 'DISCOVER', 'JCB', 'MASTERCARD', 'VISA'],
              },
              'tokenizationSpecification': {
                'type': 'PAYMENT_GATEWAY',
                'parameters': {
                  'gateway': 'example',
                  'gatewayMerchantId': 'exampleGatewayMerchantId',
                },
              },
            },
          ],
          'transactionInfo': {
            'totalPriceStatus': 'FINAL',
            'totalPrice': '15.77',
            'currencyCode': 'USD',
          },
        }),
      },
    };

每個付款閘道的 tokenizationSpecification 物件內容都不同。下表列出每個閘道使用的參數:

範例
"parameters": {
  "gateway": "example",
  "gatewayMerchantId": "exampleGatewayMerchantId"
}
ACI
"parameters": {
  "gateway": "aciworldwide",
  "gatewayMerchantId": "YOUR_ENTITY_ID"
}
輪胎
"parameters": {
  "gateway": "adyen",
  "gatewayMerchantId": "YOUR_MERCHANT_ACCOUNT_NAME"
}
ALFA-BANK
"parameters": {
  "gateway": "alfabank",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
BLUE_MEDIA
"parameters": {
  "gateway": "bluemedia",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
藍色
"parameters": {
  "gateway": "bluesnap",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
快速說明
"parameters": {
  "gateway": "braintree",
  "braintree:apiVersion": "v1",
  "braintree:sdkVersion": braintree.client.VERSION,
  "braintree:merchantId": "YOUR_BRAINTREE_MERCHANT_ID",
  "braintree:clientKey": "YOUR_BRAINTREE_TOKENIZATION_KEY"
}
CHASE_PAYMENTECH
"parameters": {
  "gateway": "chase",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ACCOUNT_NUMBER"
}
CheckOUT
"parameters": {
  "gateway": "checkoutltd",
  "gatewayMerchantId": "YOUR_PUBLIC_KEY"
}
CloudPAYMENTS
"parameters": {
  "gateway": "cloudpayments",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
CYBERSOURCE
"parameters": {
  "gateway": "cybersource",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
資料傳輸
"parameters": {
  "gateway": "datatrans",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
EBANX
"parameters": {
  "gateway": "ebanx",
  "gatewayMerchantId": "YOUR_PUBLIC_INTEGRATION_KEY"
}
FIRST_DATA
"parameters": {
  "gateway": "firstdata",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
GLOBAL_PAYMENTS
"parameters": {
  "gateway": "globalpayments",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
GOPAY
"parameters": {
  "gateway": "gopay",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
HITRUST
"parameters": {
  "gateway": "hitrustpay",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
匯入工具
"parameters": {
  "gateway": "imsolutions",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
LyRA
"parameters": {
  "gateway": "lyra",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
MPGS
"parameters": {
  "gateway": "mpgs",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
個幣_EMAIL_RU
"parameters": {
  "gateway": "moneymailru",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
NEWEBPAY
"parameters": {
  "gateway": "newebpay",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
NEXI
"parameters": {
  "gateway": "nexi",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
NMI
"parameters": {
  "gateway": "creditcall",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
PAYSAFE
"parameters": {
  "gateway": "paysafe",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
PAYTURE
"parameters": {
  "gateway": "payture",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
PAYU
"parameters": {
  "gateway": "payu",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
PRZELEWY24
"parameters": {
  "gateway": "przelewy24",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
韓元
"parameters": {
  "gateway": "rbkmoney",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
SBERBANK
"parameters": {
  "gateway": "sberbank",
  "gatewayMerchantId": "YOUR_ORGANIZATION_NAME"
}
個半形字元
"parameters": {
  "gateway": "square",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
標準
"parameters": {
  "gateway": "stripe",
  "stripe:version": "2018-10-31",
  "stripe:publishableKey": "YOUR_PUBLIC_STRIPE_KEY"
}
TAPPAY
"parameters": {
  "gateway": "tappay",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
TINKOFF
"parameters": {
  "gateway": "tinkoff",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
供應商
"parameters": {
  "gateway": "uniteller",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
VANTIV
"parameters": {
  "gateway": "vantiv",
  "vantiv:merchantPayPageId": "YOUR_PAY_PAGE_ID",
  "vantiv:merchantOrderId": "YOUR_ORDER_ID",
  "vantiv:merchantTransactionId": "YOUR_TRANSACTION_ID",
  "vantiv:merchantReportGroup": "*web"
}
WORLDPAY
"parameters": {
  "gateway": "worldpay",
  "gatewayMerchantId": "YOUR_WORLDPAY_MERCHANT_ID"
}
YANDEX
"parameters": {
  "gateway": "yandexcheckout",
  "gatewayMerchantId": "YOUR_SHOP_ID"
}

將訂單資料儲存在工作階段參數中

透過執行要求,將訂單資料儲存至工作階段參數。訂單物件會用於同一個工作階段的不同場景。

conv.session.params.order = {
    '@type': 'type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec',
    order: order,
    orderOptions: orderOptions,
    presentationOptions: presentationOptions,
    paymentParameters: paymentParameters
};

提出訂單

建立訂單後,您必須向使用者顯示該訂單,才能進行確認或拒絕。為此,您應該轉換至執行交易決策的場景。

建立交易決策情境

  1. 在「Scenes」分頁中新增名稱為 TransactionDecision 的新場景。
  2. 在「運算單元填充」下方,按一下「+」來新增版位。
  3. 在「Select type」(選取類型) 下方,選取 actions.type.TransactionDecisionValue 做為運算單元類型。
  4. 在運算單元名稱欄位中,為版位提供名稱 TransactionDecision
  5. 勾選「CustomizeSlot value writeback」(自訂運算單元值寫入) 核取方塊 (預設為啟用)。
  6. 在「設定版位」下方的下拉式選單中,選取「使用工作階段參數」
  7. 在「ConfigureSlot」(設定運算單元) 下方,輸入用於將訂單儲存至文字欄位的工作階段參數名稱 (即 $session.params.order)。
  8. 按一下「Save」

為了嘗試填入 TransactionDecisionValue 位置,Google 助理會啟動內建體驗,而您傳遞的 Order 會直接轉譯至「購物車預覽資訊卡」。使用者可以說出「下單」、拒絕交易、變更信用卡或地址等付款方式,或是要求變更訂單內容。

使用者在這個時候也可以要求變更訂單。在這種情況下,您應確保完成購物車組件體驗後,您的執行要求可以處理訂單變更要求。

處理交易決策結果

填補 TransactionDecisionValue 運算單元後,使用者對交易決策的答案會儲存在工作階段參數中。這個值包含以下內容:

  • ORDER_ACCEPTED,
  • ORDER_REJECTED,
  • DELIVERY_ADDRESS_UPDATED,
  • CART_CHANGE_REQUESTED
  • USER_CANNOT_TRANSACT.

處理交易決策結果的方法如下:

  1. 在「Scenes」分頁中,選取新建立的 TransactionDecision 場景。
  2. 在「條件」下方,按一下「+」新增條件。
  3. 在文字欄位中輸入下列條件語法來檢查成功條件:

    scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_ACCEPTED"
    
  4. 將遊標懸停在剛剛新增的條件上,然後按一下向上箭頭,將遊標放在 if scene.slots.status == "FINAL" 之前。

  5. 啟用「Send 提示」,並提供簡單的提示,讓使用者瞭解訂單已完成:

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Transaction completed! Your order
                $session.params.TransactionDecision.order.merchantOrderId is all
                set!
    
  6. 選取「轉換」下方的「結束對話」即可結束對話。

  7. 在「條件」下方,按一下「+」新增條件。

  8. 在文字欄位中,輸入下列條件語法來檢查失敗狀況:

      scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_REJECTED"
    
  9. 將遊標懸停在剛剛新增的條件上,然後按一下向上箭頭,將遊標放在 if scene.slots.status == "FINAL" 之前。

  10. 啟用「Send 提示」,並提供簡單提示,讓使用者瞭解訂單已遭拒:

    candidates:
      - first_simple:
          variants:
            - speech: Look like you don't want to order anything. Goodbye.
    
  11. 在「轉換」下方,選取「結束對話」即可結束對話。

  12. 選取條件 else if scene.slots.status == "FINAL"

  13. 啟用「Send 提示」,並提供簡單的提示,讓使用者瞭解他們無法進行交易:

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Transaction failed with status
                $session.params.TransactionDecision.transactionDecision
    
  14. 如果使用者無法進行交易,請選取「Transition」下方的「End 對話」以結束對話。

完成訂單並傳送收據

TransactionDecisionValue 運算單元傳回 ORDER_ACCEPTED 的結果時,您必須立即執行「確認」訂單所需的任何處理作業 (例如將訂單保存在自己的資料庫中,並向使用者收費)。

您可以透過這個回應結束對話,但您必須加入簡單的回應,才能讓對話順暢運作。當您提供此初始 orderUpdate 時,使用者會看到「收合的收據資訊卡」以及您的其餘回應。這張卡片會反映使用者在「訂單記錄」中找到的收據。

在訂單確認期間,訂單物件可以包含 userVisibleOrderId,這是使用者看到的訂單 ID。您可以在這個欄位重複使用 merchantOrderId

OrderUpdate 物件的部分需要包含後續動作物件,這類物件會在訂單詳細資料底部以網址按鈕的形式呈現,供使用者在 Google 助理訂單記錄中找到。

  • 您必須為每個訂單提供至少 VIEW_DETAILS 後續行動。這項屬性應包含指向行動應用程式/網站上訂單的深層連結。
  • 除了動作對話中的收據卡以外,您也必須透過電子郵件傳送符合所有交易法律要求的正式收據。

如要傳送初始訂單更新,請按照下列指示操作:

  1. 在「Scenes」分頁中選取 TransactionDecision 場景。
  2. 在「Condition」下方,選取檢查成功結果的條件 ORDER_ACCEPTED

      scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_ACCEPTED"
    
  3. 針對這個條件,請啟用「Call your Webhook」並提供意圖處理常式名稱,例如 update_order

  4. 在 Webhook 程式碼中,新增用於傳送初始訂單更新的意圖處理常式:

    app.handle('update_order', conv => {
      const currentTime = new Date().toISOString();
      let order = conv.session.params.TransactionDecision.order;
      conv.add(new OrderUpdate({
        'updateMask': {
          'paths': [
            'purchase.status',
            'purchase.user_visible_status_label'
          ]
        },
        'order': {
          'merchantOrderId': order.merchantOrderId,
          'lastUpdateTime': currentTime,
          'purchase': {
            'status': 'CONFIRMED',
            'userVisibleStatusLabel': 'Order confirmed'
          },
        },
        'reason': 'Reason string
      }));
    });
    

傳送訂單最新狀態

您必須在生命週期內讓使用者瞭解訂單狀態。傳送 HTTP PATCH 要求給 Orders API,其中包含訂單狀態和詳細資料,即可傳送使用者訂單更新內容。

設定對 Orders API 的非同步要求

系統會透過存取權杖授權向 Orders API 發出訂單更新要求。如要對 Orders API 更新訂單,請下載與 Actions Console 專案相關聯的 JSON 服務帳戶金鑰,然後交換服務帳戶金鑰,取得可傳遞至 HTTP 要求 Authorization 標頭的不記名憑證。

如要擷取服務帳戶金鑰,請執行下列步驟:

  1. Google Cloud 控制台中,依序前往「選單」圖示 ⋮ >「API 和服務」>「憑證」>「建立憑證」>「服務帳戶金鑰」
  2. 在「服務帳戶」下方,選取「新增服務帳戶」
  3. 將服務帳戶設為 service-account
  4. 將「Role」(角色) 設為 [Project] (專案) > [Owner] (擁有者)
  5. 將金鑰類型設為「JSON」
  6. 選取「Create」(建立)
  7. 系統會將私人 JSON 服務帳戶金鑰下載到本機電腦。

在訂單更新程式碼中,您可以使用 Google API 用戶端程式庫和 "https://www.googleapis.com/auth/actions.order.developer" 範圍,交換服務金鑰並取得不記名憑證。如需安裝步驟和範例,請參閱 API 用戶端程式庫的 GitHub 頁面

您也可以參考 Node.js 範例中的 order-update.js,取得金鑰交換範例。

傳送訂單最新狀態

將服務帳戶金鑰交換為 OAuth 不記名權杖後,您就能以經過授權的 PATCH 要求來將訂單更新傳送至 Orders API。

Orders API 網址: PATCH https://actions.googleapis.com/v3/orders/${orderId}

在要求中提供下列標頭:

  • "Authorization: Bearer token" 改成您交換服務帳戶金鑰的 OAuth 不記名憑證。
  • "Content-Type: application/json".

PATCH 要求應採用下列格式的 JSON 主體:

{ "orderUpdate": OrderUpdate }

OrderUpdate 物件包含下列頂層欄位:

  • updateMask - 要更新的訂單欄位。如要更新訂單狀態,請將值設為 purchase.status, purchase.userVisibleStatusLabel
  • order:更新的內容。如果您要更新訂單內容,請將值設為更新後的 Order 物件。如果您要更新訂單狀態 (例如從 "CONFIRMED""SHIPPED"),物件包含下列欄位:

    • merchantOrderId:您在 Order 物件中設定的 ID。
    • lastUpdateTime:本次更新的時間戳記。
    • purchase - 包含下列項目的物件:
      • status:訂單狀態,為 PurchaseStatus,例如「SHIPPED」或「DELIVERED」。
      • userVisibleStatusLabel - 面向使用者的標籤,提供訂單狀態詳細資料,例如「您的訂單已出貨,且正在路上」。
  • userNotification 物件。請注意,加入這個物件並不保證會在使用者的裝置上收到通知。

以下程式碼範例顯示 OrderUpdate 範例,可將訂單狀態更新為 DELIVERED

// 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 client library.
const {OrderUpdate} = require('@assistant/conversation');

// 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: {
      paths: [
        'purchase.status',
        'purchase.user_visible_status_label'
      ]
    },
    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);
} catch (e) {
  console.log(`Error: ${e}`);
}
設定購買狀態

訂單更新的 status 必須描述訂單的目前狀態。在更新的 order.purchase.status 欄位中,使用下列其中一個值:

  • CREATED - 使用者接受訂單,並且從動作的角度「建立」訂單,但需要您後端手動處理。
  • CONFIRMED - 訂單有效且正在處理履行要求。
  • IN_PREPARATION - 目前正在準備運送/配送訂單,例如正在烹調的食物或包裝的商品。
  • READY_FOR_PICKUP - 訂單可取貨,
  • DELIVERED - 訂購商品已送達接收者
  • OUT_OF_STOCK - 訂單中的一或多項商品缺貨中。
  • CHANGE_REQUESTED - 使用者要求變更訂單,系統正在處理變更。
  • RETURNED - 訂單商品已在送達後由使用者退回。
  • REJECTED - 如果您無法處理、收費或以其他方式「啟用」訂單。
  • CANCELLED - 使用者已取消訂單。

您應針對與交易相關的每個狀態傳送訂單更新。舉例來說,如果您的交易需要以手動處理的方式在下單後記錄訂單,請傳送 CREATED 訂單更新,直到該額外處理作業完成為止。並非所有訂單都需要所有狀態值。

測試專案

測試專案時,您可以在動作主控台中啟用沙箱模式,以便在不透過付款方式收費的情況下測試動作。如要啟用沙箱模式,請按照下列步驟操作:

  1. 在 Actions 控制台的導覽面板中,按一下「測試」
  2. 按一下「設定」
  3. 啟用「Development Sandbox」選項。

如果是實體交易,您也可以在樣本中將 isInSandbox 欄位設為 true。這項操作相當於在動作主控台中啟用沙箱模式設定。如要查看使用 isInSandbox 的程式碼片段,請參閱「傳送訂單更新」一節。

疑難排解

如果您在測試期間遇到任何問題,請參閱交易的疑難排解步驟