在 Alexa 技能開發中,整合產品內購買(ISP)功能,能提供更豐富的使用者經驗和商業模式。本文將深入探討如何運用 in_skill_product_response 判斷使用者購買狀態,並引導開發者設計與實作 Alexa 技能的購買流程,包含購買意圖的建立、購買請求與回應的處理,以及購買後回應的客製化設計。理解 in_skill_product_response 的結構,例如 entitledentitlement_reason 欄位,能幫助開發者判斷使用者是否已購買特定產品,並根據購買狀態提供不同的服務或內容。透過 Python 程式碼範例,示範如何修改啟動請求(LaunchRequest),根據 in_skill_product_response 的結果,提供已購買或未購買產品的使用者不同的歡迎訊息和操作指引。此外,本文也詳細說明如何設計購買意圖(Intent),並使用 SendRequestDirective 傳送購買請求,以及如何處理購買成功、失敗等不同狀態的回應。最後,也提供客製化購買後回應處理的程式碼範例,讓開發者能根據自身需求打造更完善的使用者經驗。

Alexa 技能產品整合:使用 in_skill_product_response

在開發 Alexa 技能時,瞭解如何整合 in_skill_product_response 是非常重要的。這個 response 提供了有關產品的資訊,例如使用者是否已購買特定產品、產品的名稱、產品 ID 等。

in_skill_product_response 結構

in_skill_product_response 的結構如下:

{
  "in_skill_products": [
    {
      "active_entitlement_count": 0,
      "entitled": "NOT_ENTITLED",
      "entitlement_reason": "NOT_PURCHASED",
      "name": "paid joke",
      "object_type": "ENTITLEMENT",
      "product_id": "amzn1.adg.product.d0aed689-6761-483c-baf9-788",
      "purchasable": "PURCHASABLE",
      "purchase_mode": "TEST",
      "reference_name": "paidforjoke",
      "summary": "This gives you access to more jokes"
    }
  ],
  "is_truncated": None,
  "next_token": None
}

這個 response 中的 entitledentitlement_reason 欄位非常重要,因為它們指示使用者是否已購買特定產品。

處理不同的使用者場景

根據使用者的購買狀態和行為,會有不同的場景出現。以下是幾種可能的場景:

  1. 使用者尚未購買付費笑話:在這種情況下,需要告知使用者可用的選擇,並詢問是否要購買。
  2. 使用者已購買付費技能:如果使用者已購買付費技能,則需要提供付費笑話給使用者。
  3. 使用者要求購買付費會員:如果使用者要求購買付費會員,則需要確認使用者的購買意願。
  4. 使用者已購買付費會員並要求取消:如果使用者已購買付費會員並要求取消,則需要處理取消的邏輯。

修改啟動請求

為了處理這些場景,需要修改啟動請求(LaunchRequest)以告知使用者可用的選擇。以下是修改後的 LaunchRequestHandler 類別:

class LaunchRequestHandler(AbstractRequestHandler):
    """Handler for Skill Launch."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speak_output = "Welcome, you can ask for a joke or say hello"
        in_skill_response = in_skill_product_response(handler_input)
        if isinstance(in_skill_response, InSkillProductsResponse):
            entitled_prods = get_all_entitled_products(in_skill_response.in_skill_products)
            if entitled_prods:
                speak_output = (
                    "Welcome to {}. You currently own {} products. "
                    "To hear a paid joke, say, 'Tell me a joke.'").format(
                    skill_name,
                    get_speakable_list_of_products(entitled_prods))
            else:
                logger.info("No entitled products")
                speak_output = (
                    "Welcome to {}. You don't own any products. "
                    "To hear a paid joke, say, 'Tell me a joke.'").format(skill_name)
        return Response(builder=speak_output)

在這個修改後的 LaunchRequestHandler 中,會先檢查使用者是否已購買任何產品,如果有,則會告知使用者可用的選擇。如果沒有,則會提示使用者可以購買產品。

圖表翻譯:

  graph LR
    A[Launch Request] -->|in_skill_product_response|> B{Entitled Products}
    B -->|Yes|> C[Speak Output: Welcome and owned products]
    B -->|No|> D[Speak Output: Welcome and no owned products]
    C --> E[Handle User Input]
    D --> E

這個圖表展示了 LaunchRequestHandler 的邏輯流程,根據使用者的購買狀態和行為,會有不同的 speak output 和後續處理。

Alexa 技能購買功能實作

在 Alexa 技能開發中,實作購買功能是一個重要的步驟。以下是實作購買功能的步驟:

1. 建立購買意圖

首先,需要建立一個購買意圖(Intent),用於處理使用者的購買請求。可以透過 Alexa Developer Console 或編輯 JSON 檔案來建立意圖。

{
  "name": "TellMeMoreIntent",
  "slots": [],
  "samples": [
    "What can I purchase",
    "What can I get",
    "Tell me more",
    "What can I buy"
  ]
}

2. 處理購買請求

接下來,需要建立一個處理器(Handler)來處理購買請求。這個處理器需要繼承 AbstractRequestHandler 類別,並實作 can_handle 方法來判斷是否可以處理購買請求。

class TellMeMoreHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        # 判斷是否可以處理購買請求
        return handler_input.request_object.request_type == "IntentRequest" and \
               handler_input.request_object.intent.name == "TellMeMoreIntent"

3. 回應購買請求

當處理器可以處理購買請求時,需要回應使用者的請求。可以使用 speak_output 變數來設定回應內容。

speak_output = "Something went wrong in loading your purchase history."
reprompt = speak_output

return handler_input.response_builder.speak(speak_output).ask(speak_output).response

4. 測試購買功能

最後,需要測試購買功能是否正常工作。可以使用 Alexa Simulator 或實際裝置來測試購買功能。

5. 儲存和佈署

完成所有步驟後,需要儲存和佈署 Alexa 技能。這樣就可以讓使用者使用購買功能了。

圖表翻譯:

以下是購買功能的流程圖:

  flowchart TD
    A[使用者請求] --> B[建立購買意圖]
    B --> C[處理購買請求]
    C --> D[回應購買請求]
    D --> E[測試購買功能]
    E --> F[儲存和佈署]

這個流程圖展示了購買功能的實作步驟,從建立購買意圖到儲存和佈署 Alexa 技能。

Alexa 技術深入:In-skill Purchasing (ISP) 實作

10.6 Purchase Intent 處理

在 Alexa 的 In-skill Purchasing (ISP) 中,實作購買功能是非常重要的。為了避免語音辨識的誤會,例如 “buy” 被誤認為 “bye”,我們需要小心設計購買的意圖(Intent)。

設計 Purchase Intent

首先,我們需要定義購買的意圖,包括其名稱、插槽(Slots)和示例陳述式(Samples)。以下是 JSON 格式的定義:

{
  "name": "BuyIntent",
  "slots": [],
  "samples": [
    "let's buy it",
    "upgrade",
    "Can I buy it",
    "purchase",
    "I want to buy it"
  ]
}

如果你有多個產品,則需要在示例陳述式中包含產品類別,例如 “let’s buy {productCategory} pack”。

實作 Purchase Intent Handler

購買意圖的處理需要兩個步驟:一是處理購買請求,二是處理使用者對購買請求的回應(例如 “Yes” 或 “No”)。我們將分兩步實作這兩個 handler。

首先,需要取得產品 ID。這可以在 Alexa Developer Console 中的 “Monetize” 部分找到,或者透過檢視產品詳情頁面來取得。產品 ID 的格式通常為 “amzn1.adg.product.{product_id}"。

然後,新增標準技能建構器(StandardSkillBuilder)並將購買 handler 新增進去:

sb = StandardSkillBuilder()
sb.add_request_handler(TellMeMoreHandler())

實作 Purchase Logic

購買邏輯的實作需要使用 Alexa 的 In-skill Purchasing API。這涉及到向 Amazon 傳送購買請求並處理使用者的回應。

以下是 purchase handler 的基本結構:

def handle(self, handler_input):
    # type: (HandlerInput) -> Response
    logger.info("In BuyHandler")
    
    # 取得產品 ID
    product_id = "amzn1.adg.product.{product_id}"
    
    # 傳送購買請求
    speak_output = "You can upgrade to hear more jokes. Just say, 'Upgrade' to purchase this product."
    reprompt = "I didn’t catch that. What can I help you with?"
    return handler_input.response_builder.speak(speak_output).ask(reprompt).response

處理使用者回應

使用者對購買請求的回應(例如 “Yes” 或 “No”)需要被處理以完成購買流程。這部分的實作涉及到處理使用者的語音輸入並根據使用者的意圖進行相應的動作。

以下是使用者回應 handler 的基本結構:

def handle(self, handler_input):
    # type: (HandlerInput) -> Response
    logger.info("In BuyResponseHandler")
    
    # 處理使用者回應
    if handler_input.request_envelope.request.object.intent.name == "AMAZON.YesIntent":
        # 完成購買
        speak_output = "Purchase completed successfully."
    elif handler_input.request_envelope.request.object.intent.name == "AMAZON.NoIntent":
        # 取消購買
        speak_output = "Purchase cancelled."
    else:
        speak_output = "I didn’t catch that. What can I help you with?"
    
    return handler_input.response_builder.speak(speak_output).response

處理購買請求的程式碼

class BuyHandler(AbstractRequestHandler):
    """處理使用者購買產品的請求。
    使用者說:Alexa, 升級
    """
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("BuyIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        logger.info("在 BuyHandler 中")
        product_id = "PASTE YOUR PRODUCT ID HERE"
        return handler_input.response_builder.add_directive(
            SendRequestDirective(
                name="Buy",
                payload={
                    "InSkillProduct": {
                        "productId": product_id
                    }
                },
                token="buyToken"
            )
        ).response

處理購買回應的程式碼

class BuyResponseHandler(AbstractRequestHandler):
    """處理購買回應的請求。
    """
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return handler_input.request_object.request.type == "Connections.Response"

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        logger.info("在 BuyResponseHandler 中")
        payload = handler_input.request_object.payload
        purchase_result = payload.get("purchaseResult")
        
        if purchase_result == "ACCEPTED":
            # 處理購買成功的情況
            pass
        elif purchase_result == "DECLINED":
            # 處理購買被拒絕的情況
            pass
        elif purchase_result == "ALREADY_PURCHASED":
            # 處理已經購買的情況
            pass
        elif purchase_result == "ERROR":
            # 處理發生錯誤的情況
            pass
        
        return handler_input.response_builder.response

新增請求處理器

sb.add_request_handler(BuyHandler())
sb.add_request_handler(BuyResponseHandler())
sb.add_request_handler(LaunchRequestHandler())

內容解密:

上述程式碼定義了兩個請求處理器:BuyHandlerBuyResponseHandlerBuyHandler 處理使用者購買產品的請求,而 BuyResponseHandler 處理購買回應。

BuyHandler 中,我們使用 SendRequestDirective 來傳送一個購買請求給 Amazon。這個請求包含了產品 ID 和一個 token。

BuyResponseHandler 中,我們處理購買回應。根據購買結果的不同,我們可以進行不同的處理。例如,如果購買成功,我們可以顯示一個成功的訊息;如果購買被拒絕,我們可以顯示一個錯誤訊息。

最後,我們增加了這兩個請求處理器到技能中。

圖表翻譯:

  flowchart TD
    A[使用者傳送購買請求] --> B[BuyHandler]
    B --> C[傳送購買請求給 Amazon]
    C --> D[Amazon 處理購買請求]
    D --> E[傳回購買回應]
    E --> F[BuyResponseHandler]
    F --> G[處理購買回應]

這個圖表顯示了購買請求的流程。使用者傳送購買請求,然後 BuyHandler 處理這個請求並傳送一個購買請求給 Amazon。Amazon 處理購買請求並傳回購買回應,然後 BuyResponseHandler 處理購買回應。

實作購買後的回應處理

為了處理使用者購買後的回應,我們需要實作一個自定義的請求處理器。這個處理器將負責接收和處理購買後的回應,根據購買結果提供相應的反饋給使用者。

從使用者經驗、商業價值和技術架構三個維度來看,整合 in_skill_product_response 和建構 In-Skill Purchasing (ISP) 功能,對於提升 Alexa 技能的商業潛力至關重要。 分析段落中提供的程式碼範例,清楚地展現瞭如何利用 SendRequestDirective 傳送購買請求,以及如何根據 purchaseResult 處理不同的購買狀態(ACCEPTED, DECLINED, ALREADY_PURCHASED, ERROR)。 這有效地解決了技能變現的核心問題,並透過客製化的互動流程,引導使用者完成購買,提升轉換率。技術限制方面,程式碼範例中缺少對錯誤情況的具體處理邏輯,例如網路連線失敗或 Amazon 服務異常,這在實際佈署中需要特別關注。此外,程式碼的彈性設計也需考量,例如支援多個產品的購買和管理。展望未來,隨著語音互動技術的成熟,更簡潔、更智慧的 ISP 流程將成為趨勢。 玄貓認為,開發者應深入理解 Alexa ISP 的運作機制,並結合使用者行為分析,持續最佳化購買流程和產品設計,才能最大化技能的商業價值。