建立非消耗性數位交易 (Dialogflow)

本指南說明如何將數位交易新增至對話動作,讓使用者能夠購買非消耗性數位商品。

重要字詞:「非消耗性」數位商品是指只能購買一次的庫存單位 (SKU),例如在動作或 Android 應用程式中付費存取額外內容。這類產品與可購買、使用及重新購買的消耗性數位商品不同。

如要進一步瞭解非消耗性一次性產品,請參閱一次性產品專屬功能的 Android 說明文件。

限制和查看規範

「透過交易執行的操作」適用額外政策。我們可能需要數週的時間審查包含交易的動作,因此在規劃發布時間表時,請將這個時間納入考量。為簡化審查程序,請確保您遵守交易政策與規範,再將動作送審。

銷售數位商品的動作只能在下列國家/地區部署:

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

交易流程

本指南概述數位商品交易流程中發生的每個開發步驟。動作處理數位商品的交易時,會使用下列流程:

  1. 設定 Digital Purchase API 用戶端:您的動作會使用 Digital Purchases API 與您的 Google Play 廣告空間進行通訊,並進行交易。在您的動作執行其他操作之前,系統會建立含有服務金鑰的 JWT 用戶端來與數位購買交易 API 通訊。
  2. 收集資訊:您的動作會收集使用者和 Google Play 清查的基本資訊,以便為交易準備。
    1. 驗證交易規定:您的動作會在購買流程開始時使用數位交易規定輔助程式,確保使用者可以進行交易。
    2. 收集可用廣告空間:您的動作會檢查 Google Play 商品目錄,並找出目前可購買的商品。
  3. 建立訂單:您的動作會向使用者顯示可用的數位商品,讓使用者選擇想要購買。
  4. 完成購買程序:您的動作會使用數位購買 API,在使用者選擇至 Google Play 商店後啟動購買程序。
  5. 處理結果:您的動作會收到交易的狀態碼,並通知使用者購買成功 (或需要採取其他步驟)。

必要條件

將數位交易整合至動作之前,您必須具備下列必要條件:

  • 擁有開發人員帳戶和 Google Play 的商家帳戶,以便在 Google Play 管理中心管理數位商品。

  • 在 Google Search Console 中驗證的網域。 網域不需要與公開發布網站建立關聯,只需要參照您的網域即可。

  • 在 Google Play 管理中心內具備 com.android.vending.BILLING 權限的 Android 應用程式。您的數位商品將在 Google Play 管理中心與這個應用程式建立關聯的「應用程式內購」。

    此外,您還必須在 Play 管理中心使用這個應用程式建立版本,但如果您不想發布版本,可以建立封閉的 Alpha 版

    如果您尚未安裝 Android 應用程式,請按照這篇文章的「連結 Android 應用程式」指示操作。

  • Google Play 管理中心中的一或多項受管理產品,這些產品是您透過動作販售的數位商品。請注意,您必須先設定 Android 應用程式必要條件,才能在 Play 管理中心建立納入管理的產品。

    如果您尚未建立受管理的產品,請按照建立數位商品的操作說明完成。

與 Android 應用程式建立關聯

如果 Google Play 管理中心目前還沒有具備帳單權限的 Android 應用程式,請按照下列步驟操作:

  1. Android Studio 或您選擇的 Android IDE 中,建立新專案。請在專案設定提示中選擇選項,來建立非常基本的應用程式。
  2. 為專案設定套件名稱,例如 com.mycompany.myapp。請勿將這個名稱保留預設值,因為您無法將包含 com.example 的套件上傳至 Play 管理中心。
  3. 開啟應用程式的 AndroidManifest.xml 檔案。
  4. manifest 元素中加入以下這行程式碼:

    <uses-permission android:name="com.android.vending.BILLING" />

    您的 AndroidManifest.xml 檔案應類似於以下程式碼區塊:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.mycompany.myapp">
        <uses-permission android:name="com.android.vending.BILLING" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" />
    </manifest>
    
  5. 以已簽署的 APK 建構應用程式。在 Android Studio 中按照下列步驟操作:

    1. 前往「Build」,「Generate Signed Bundle / APK」
    2. 按一下「Next」
    3. 在「Key store path」下方,按一下「Create new」
    4. 填妥每個欄位,然後按一下「OK」。請記下您的「Key store Password」和「Key password」,並妥善保存這些資料,因為稍後會用到。
    5. 按一下「Next」
    6. 選取「release」
    7. 選取「V1 (JAR Signature)」
    8. 按一下「Finish」
    9. 幾秒後,Android Studio 會產生 app-release.apk 檔案。找出這個檔案以便日後使用。
  6. Google Play 管理中心建立新的應用程式。

  7. 前往應用程式版本頁面。

  8. 在「封閉測試群組」下方,依序前往「管理」和「Alpha 版」

  9. 按一下「Create Release」按鈕。

  10. 在「允許 Google 管理及保護您的簽署金鑰」下方,輸入您的簽署金鑰資訊。

  11. 上傳 APK 檔案。

  12. 點按「儲存」

建立數位商品

如果您在 Play 管理中心目前還沒有任何數位商品,請按照下列步驟操作:

  1. Google Play 管理中心中,依序前往「應用程式內產品」和「受管理的產品」。如果看到警告,請按照先前的操作說明建立 Android 應用程式,或按一下連結來建立商家檔案。
  2. 按一下「建立受管理的產品」
  3. 填寫數位產品的欄位。請記下產品 ID,這個 ID 方便您在動作中參照這項產品。
  4. 點按「儲存」
  5. 針對您要銷售的每個產品重複執行步驟 2 到 4。

Google Play 管理中心的非消耗性商品示例。

準備動作專案

在 Google Play 管理中心設定數位商品後,您必須啟用數位交易,並將動作專案與 Play 應用程式建立關聯。

如要在動作專案中啟用數位商品交易,請按照下列步驟操作:

  1. Actions 主控台中,開啟專案或建立新專案。
  2. 依序前往「部署」和「目錄資訊」
  3. 在「額外資訊」和「交易」下方,勾選「Do your Actions use the Digital Purchase API to 執行數位商品的交易」底下的「Yes」方塊。
  4. 點按「儲存」

建立數位商品 API 金鑰

如要將要求傳送至數位商品 API,您必須下載與 Actions 主控台專案相關聯的 JSON 服務帳戶金鑰。

如要擷取服務帳戶金鑰,請按照下列步驟操作:

  1. 在「Actions 主控台」中,按一下右上角的三點圖示,然後點選「Project settings」
  2. 找出動作的專案 ID
  3. 請點選這個連結,並將「<project_id>」替換成您的專案 ID:https://console.developers.google.com/apis/credentials?project=project_id
  4. 在主要導覽選單中,前往「憑證」
  5. 在顯示的頁面中,依序按一下「建立憑證」和「服務帳戶金鑰」
  6. 前往「Service Account」,然後按一下「New Service Account」
  7. 為服務帳戶命名,例如數位交易。
  8. 點選「建立」
  9. 將「Role」(角色) 設為 [Project] (專案) > [Owner] (擁有者)
  10. 按一下 [繼續]。
  11. 按一下 [Create Key] (建立金鑰)
  12. 選取「JSON」金鑰類型。
  13. 按一下「建立金鑰」,並下載 JSON 服務帳戶金鑰。

請將這個服務帳戶金鑰存放在安全的地方。您會在執行要求中使用這組金鑰,為數位購買交易 API 建立用戶端。

連結至您的 Play 廣告空間

如要從 Actions 專案存取數位商品,請將您的網站網域和應用程式與專案建立關聯,做為已連結的屬性

注意:我們驗證資源時,連結步驟最多可能需要一週才能完成。如果您的網站或應用程式在 24 小時後仍未連結,請與支援團隊聯絡

如要將 Play 管理中心網域和應用程式連結至動作專案,請按照下列步驟操作:

  1. 動作控制台中,依序前往「部署」和「品牌驗證」
  2. 如果您尚未連結任何資源,請先連結網站:

    1. 按一下 [網站資源] 按鈕。</>
    2. 輸入您網域的網址,然後按一下「Connect」

    Google 會透過電子郵件,將進一步操作說明傳送給在 Google Search Console 中驗證該網域的使用者。收到這封電子郵件的收件者完成這些步驟後,網站應會顯示在「品牌驗證」下方。

  3. 擁有至少一個已連結的網站後,請執行下列步驟以連結 Android 應用程式:

    1. 動作控制台中,依序前往「部署」和「品牌驗證」
    2. 按一下 [連結應用程式]
    3. 在隨即顯示的頁面中,按照操作說明驗證您在 Play 管理中心的網域。選取含有您數位商品的 Play 應用程式,然後輸入「品牌驗證」頁面上顯示的網域網址。

      再一次,Google 會傳送驗證電子郵件給網域的已驗證擁有者。核准驗證後,您的 Play 應用程式應會顯示在「品牌驗證」下方。

    4. 啟用「存取 Play 購買項目」

此圖片顯示已連結至 Actions 專案的網站和應用程式。

建立購買流程

備妥 Actions 專案和數位商品庫存後,請在對話執行要求 Webhook 中建立數位商品購買流程。

1. 設定數位購物 API 用戶端

在對話執行要求 Webhook 中,使用服務帳戶 JSON 金鑰和 https://www.googleapis.com/auth/actions.purchases.digital 範圍建立 JWT 用戶端。

下列 Node.js 程式碼會為數位購買交易 API 建立 JWT 用戶端:

  const serviceAccount = {'my-file.json'};
  const request = require('request');
  const {google} = require('googleapis');

  const jwtClient = new google.auth.JWT(
    serviceAccount.client_email, null, serviceAccount.private_key,
    ['https://www.googleapis.com/auth/actions.purchases.digital'],
    null
  );

2. 收集資訊

使用者購買商品之前,您的動作會收集有關使用者進行購買交易的功能,以及您的商品目錄中有哪些商品的相關資訊。

2. a. 驗證交易規定

建議先確認使用者帳戶已設為可執行交易,再讓他們選擇購買商品。這個步驟包括檢查使用者是否已設定付款方式,以及他們位於支援數位交易的語言代碼。請在交易流程開始時,使用 DIGITAL_PURCHASE_CHECK 輔助程式透過 Google 助理驗證使用者的交易設定。

下列 Node.js 程式碼會在對話開始時使用 DIGITAL_PURCHASE_CHECK

app.intent('Default Welcome Intent', async (conv, { SKU }) => {
  // Immediately invoke digital purchase check intent to confirm
  // purchase eligibility.
  conv.ask(new DigitalPurchaseCheck());
});

在對話引數中,以 DIGITAL_PURCHASE_CHECK_RESULT 的形式找出這項檢查的結果。您可以根據上述結果,繼續交易流程或退出交易,並提示他們檢查 Google Pay 設定。

下列 Node.js 程式碼會處理需求檢查結果:

app.intent('Digital Purchase Check', async (conv) => {
  const arg = conv.arguments.get('DIGITAL_PURCHASE_CHECK_RESULT');
  if (!arg || !arg.resultType) {
    conv.close('Digital Purchase check failed. Please check logs.');
    return;
  }
  // User does not meet necessary conditions for completing a digital purchase
  if (arg.resultType === 'CANNOT_PURCHASE' || arg.resultType === 'RESULT_TYPE_UNSPECIFIED') {
    conv.close(`It looks like you aren't able to make digital purchases. Please check your Google Pay configuration and try again.`);
    return;
  }
  conv.ask('Welcome to the Digital Goods Sample. Would you like to see what I have for sale?');
});

2. b. 收集可用廣告空間

使用數位購買項目 API 要求目前可用的 Play 商店庫存,然後將該庫存建構到各個產品的 JSON 物件陣列中。您稍後可以參考這個陣列,向使用者顯示哪些購買選項。

您的每項數位商品都會以 JSON 格式表示。下列 Node.js 程式碼概述每個 SKU 的預期格式:

body = {
  skus: [
    skuId: {
      skuType: one of "APP" or "UNSPECIFIED"
      id: string,
      packageName: string
    }
    formattedPrice: string,
    title: string,
    description: string
  ]
}

將 POST 要求傳送至 https://actions.googleapis.com/v3/packages/{packageName}/skus:batchGet 端點,其中 {packageName} 是 Google Play 管理中心內的應用程式的套件名稱 (例如 com.myapp.digitalgoods),然後將結果格式化為 SKU 物件陣列。

如果只要在結果陣列中擷取特定數位商品,請列出您想在 body.ids 中購買的數位商品 (如 Google Play 管理中心各項應用程式內商品下方所示) 的產品 ID。

下列 Node.js 程式碼會要求數位購買交易 API 提供的可用商品清單,並將結果格式化為 SKU 陣列:

return jwtClient.authorize((err, tokens) => {
    if (err) {
      throw new Error(`Auth error: ${err}`);
    }

    const packageName = 'com.example.projectname';

    request.post(`https://actions.googleapis.com/v3/packages/${packageName}/skus:batchGet`, {
      'auth': {
        'bearer': tokens.access_token,
      },
      'json': true,
      'body': {
        'conversationId': conversationId,
        'skuType': 'APP',
        // This request is filtered to only retrieve SKUs for the following product IDs
        'ids': ['nonconsumable.1']
      },
    }, (err, httpResponse, body) => {
      if (err) {
        throw new Error(`API request error: ${err}`);
      }
      console.log(`${httpResponse.statusCode}: ${httpResponse.statusMessage}`);
      console.log(JSON.stringify(body));
    });
  });
});

3. 建立訂單

如要開始使用者的數位購買交易,請顯示可供購買的數位商品清單。您可以使用各種豐富的回應類型來代表庫存,並提示使用者做出選擇。

下列 Node.js 程式碼會讀取 SKU 物件的庫存陣列,並建立清單回應,以及每個清單項目:

skus.forEach((sku) => {
  const key = `${sku.skuId.skuType},${sku.skuId.id}`
  list.items[key] = {
    title: sku.title,
    description: `${sku.description} | ${sku.formattedPrice}`,
  };
});

4. 完成購買程序

如要完成購買程序,請搭配使用者選取的項目使用 COMPLETE_PURCHASE 輔助意圖。

下列 Node.js 程式碼會從清單回應處理使用者選取的 SKU,並使用該資訊要求 COMPLETE_PURCHASE 意圖:

app.intent('Send Purchase', (conv, params, option) => {
  let [skuType, id] = option.split(',');

  conv.ask(new CompletePurchase({
    skuId: {
      skuType: skuType,
      id: id,
      packageName: <PACKAGE_NAME>,
    },
  }));
});

5. 處理結果

購買交易完成後,系統會使用 COMPLETE_PURCHASE_VALUE 引數描述結果,觸發 actions_intent_COMPLETE_PURCHASE Dialogflow 事件 (或 actions.intent.COMPLETE_PURCHASE Actions SDK 意圖)。建構由此事件觸發的意圖,並向使用者傳達結果。

請處理下列可能的購買結果:

  • PURCHASE_STATUS_OK:購買成功。此時交易已完成,因此請結束交易流程,並回到對話。
  • PURCHASE_STATUS_ALREADY_OWNED:使用者擁有該項目,因此交易失敗。如要避免發生這項錯誤,請檢查使用者先前的購買交易,並調整顯示的商品,以免使用者無法選擇重新購買他們原本擁有的商品。
  • PURCHASE_STATUS_ITEM_UNAVAILABLE:要求的項目無法使用,導致交易失敗。如要避免發生這項錯誤,請檢查靠近購買時間的可用 SKU。
  • PURCHASE_STATUS_ITEM_CHANGE_REQUESTED:交易失敗,因為使用者決定購買其他商品。再次提醒建立訂單,讓使用者可以立即做出其他決定。
  • PURCHASE_STATUS_USER_CANCELLED:由於使用者取消購買流程,導致交易失敗。由於使用者提早退出流程,因此請詢問使用者是否要重試交易或完全結束交易。
  • PURCHASE_STATUS_ERROR:交易失敗,原因不明。請告知使用者交易失敗,並詢問是否要再試一次。
  • PURCHASE_STATUS_UNSPECIFIED:交易因不明原因失敗,導致狀態不明。如要處理此錯誤狀態,請讓使用者知道交易失敗,並詢問是否要再試一次。

以下 Node.js 程式碼會讀取 COMPLETE_PURCHASE_VALUE 引數並處理每個結果:

app.intent('Purchase Result', (conv) => {
  const arg = conv.arguments.get('COMPLETE_PURCHASE_VALUE');
  console.log('User Decision: ' + JSON.stringify(arg));
  if (!arg || !arg.purchaseStatus) {
    conv.close('Purchase failed. Please check logs.');
    return;
  }
  if (arg.purchaseStatus === 'PURCHASE_STATUS_OK') {
    conv.close(`Purchase completed! You're all set!`);
  } else if (arg.purchaseStatus === 'PURCHASE_STATUS_ALREADY_OWNED') {
    conv.close('Purchase failed. You already own this item.');
  } else if (arg.purchaseStatus === 'PURCHASE_STATUS_ITEM_UNAVAILABLE') {
    conv.close('Purchase failed. Item is not available.');
  } else if (arg.purchaseStatus === 'PURCHASE_STATUS_ITEM_CHANGE_REQUESTED') {
    // Reprompt with your item selection dialog
  }  else {
    conv.close('Purchase Failed:' + arg.purchaseStatus);
  }
});

反映使用者的購買交易

當使用者查詢您的動作時,要求的 JSON 的 user 物件會包含他們的購買交易清單。請查看這項資訊,並根據使用者付費的內容變更動作的回應。

以下程式碼範例顯示要求的 user 物件,其中包含了他們為 com.digitalgoods.application 套件所進行應用程式內購的 packageEntitlements

  "user": {
    "userId": "xxxx",
    "locale": "en-US",
    "lastSeen": "2018-02-09T01:49:23Z",
    "packageEntitlements": [
      {
        "packageName": "com.digitalgoods.application",
        "entitlements": [
          {
            "sku": "non-consumable.1",
            "skuType": "APP"
          }
          {
            "sku": "consumable.2",
            "skuType": "APP"
          }
        ]
      },
      {
        "packageName": "com.digitalgoods.application",
        "entitlements": [
          {
            "sku": "annual.subscription",
            "skuType": "SUBSCRIPTION",
            "inAppDetails": {
              "inAppPurchaseData": {
                "autoRenewing": true,
                "purchaseState": 0,
                "productId": "annual.subscription",
                "purchaseToken": "12345",
                "developerPayload": "HSUSER_IW82",
                "packageName": "com.digitalgoods.application",
                "orderId": "GPA.233.2.32.3300783",
                "purchaseTime": 1517385876421
              },
              "inAppDataSignature": "V+Q=="
            }
          }
        ]
      }
    ]
  },
  "conversation": {
    "conversationId": "1518141160297",
    "type": "NEW"
  },
  "inputs": [
    {
      "intent": "actions.intent.MAIN",
      "rawInputs": [
        {
          "inputType": "VOICE",
          "query": "Talk to My Test App"
        }
      ]
    }
  ],
  ...
}