スキルコードにスキル内課金の機能を追加する
スキル内課金(ISP)をカスタムスキルに追加すると、購入フローをAlexaに制御を移すスキルロジックに追加し、ユーザーに購入手順を案内できます。
以下の手順を実行して、商品のインベントリー管理、購入フローの呼び出し、購入フロー完了後のスキルの再開を行います。消費型商品を購入する場合、ユーザーのアイテムのインベントリーを永続ストレージに保存し、ユーザーがアイテムを使用するごとにインベントリーを管理します。
前提条件
スキル内課金を追加する前に、次の前提条件を満たす必要があります。
- カスタム対話モデルを使用し、Lambda関数に関連付けられているスキル。詳細については、カスタムスキルのビルド手順を参照してください。ウェブサービスとしてホストされるスキルにもスキル内商品を追加できますが、このトピックのコードでは、AWS Lambda関数にスキル内商品を追加する方法を説明しています。
- 少なくとも1つのスキル内商品がカスタムスキルに追加されていること。商品の設定は開発者コンソールまたはASK CLIを使用して行うことができます。
- スキル内課金のフローをデザインする。詳細については、スキル内課金の優れたカスタマーエクスペリエンスのデザインを参照してください。
スキル内商品のリストを取得する
各スキルセッションの開始時に、ユーザーが購入可能なスキル内商品のリストを取得します。商品のインベントリーを使って、ユーザーが購入済みの商品と提供する商品を次のように判断します。
- 各商品の商品IDを調べます。商品に対するユーザーリクエストを商品IDにマッピングします。
- このユーザーに提供されている商品を調べます。
PURCHASABLE
とマークされた商品を探します。 - このユーザーが既に購入済みの商品を調べます。
ENTITLED
とマークされた商品を探します。 - 消費型商品の場合は、このユーザーが今日までに購入済みの商品のユニット数を調べます。
activeEntitlementCount
を確認します。
利用可能なスキル内商品のリストを取得するには、List in-skill products APIを使用します。この呼び出しを起動リクエストハンドラーに追加し、スキルセッション中はこの商品のリストを保存します。リクエストが成功すると、メッセージ結果には、フィルター条件に該当する商品のリストが含まれます。
例
このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。
このLaunchRequest
ハンドラーは、SDKで提供されているMonetizationServiceClientを使用して、InSkillProducts
APIを呼び出し、商品のリストを取得します。ウェルカムメッセージで、既に購入済みの商品がユーザーに示されます。
/*
この関数はinSkillProductリストをフィルタリングしてすべての購入済み商品の
リストを取得し、それに従ってSkill CXをレンダリングする方法を示します
*/
function getAllEntitledProducts(inSkillProductList) {
const entitledProductList = inSkillProductList.filter(record => record.entitled === 'ENTITLED');
return entitledProductList;
}
/*
買い切り型商品のリストから、商品名の発話可能リストを返すヘルパー関数
です。
*/
function getSpeakableListOfProducts(entitleProductsList) {
const productNameList = entitleProductsList.map(item => item.name);
let productListSpeech = productNameList.join(', '); // 商品名をカンマで区切った文字列を1つ生成します
productListSpeech = productListSpeech.replace(/_([^_]*)$/, 'and $1'); // 最後のカンマを'と'に置き換えます
return productListSpeech;
}
/*
Requestハンドラーです。このハンドラーは、ユーザーが特定のインテントを指定せずに
スキルを開始した場合に使用されます。
*/
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
handle(handlerInput) {
const locale = handlerInput.requestEnvelope.request.locale;
const ms = handlerInput.serviceClientFactory.getMonetizationServiceClient();
return ms.getInSkillProducts(locale).then(
function reportPurchasedProducts(result) {
const entitledProducts = getAllEntitledProducts(result.inSkillProducts);
if (entitledProducts && entitledProducts.length > 0) {
// ユーザーが商品を1つ以上所持している
return handlerInput.responseBuilder
.speak(`${skillName}へようこそ。現在${getSpeakableListOfProducts(entitledProducts)}` +
'個の商品を所持しています。ランダムに豆知識を聞く場合は、\'豆知識を教えて\'と言ってください。' +
'購入済みのカテゴリーを指定するには、たとえば\'科学の豆知識を教えて\'と言ってください。' +
'ほかに購入できるものを知りたい場合は、\'買えるものを教えて\'と言ってください。それでは何を' +
'しましょうか?')
.reprompt('すみません、わかりませんでした。何をしましょうか?')
.getResponse();
}
// これまで購入した商品がない場合。
console.log('No entitledProducts');
return handlerInput.responseBuilder
.speak(`${skillName}へようこそ。ランダムに豆知識を聞く場合は、'豆知識を教えて'と言ってください。' +
'購入できるプレミアムカテゴリーを知りたい場合は、\'買えるものを教えて\'と言ってください。' +
'ヘルプが欲しい場合は、\'使い方を教えて\'と言ってください...それでは何をしましょうか?')
.reprompt('すみません、わかりませんでした。何をしましょうか?')
.getResponse();
},
function reportPurchasedProductsError(err) {
console.log(`Error calling InSkillProducts API: ${err}`);
return handlerInput.responseBuilder
.speak('購入履歴のロード中に問題が発生しました。')
.getResponse();
},
);
},
}; // LaunchRequestHandlerを終了します
このサンプルコードはAlexa Skills Kit SDK for Node.js(v1)を使用しています。
このLaunchRequest
ハンドラーはHTTP GETリクエストを構築し、InSkillProducts
APIを呼び出して商品のリストを取得します。getProductsAndEntitlements
関数はコールバック関数のパラメーターを取り、非同期にHTTP呼び出しを行ってから、コールバック関数を呼び出します。HTTP呼び出しが完了すると、コールバック関数によって結果が処理されます。
function onLaunch(launchRequest, session, callback) {
console.log(`onLaunch requestId=${launchRequest.requestId}, sessionId=${session.sessionId}`);
...
// 利用可能な商品を取得する関数を呼び出します
getProductsAndEntitlements(this, functionToProcessProductListOnceItIsLoaded);
...
}
function getProductsAndEntitlements(self, callback) {
// 購入APIを呼び出し、商品がまだキャッシュされていない場合にのみ商品をロードします
if (!self.attributes.areProductsLoaded) {
self.attributes.inSkillProducts = [];
var returnData = [];
// APIを呼び出すために必要な情報がこのセッションで利用可能です
const https = require('https');
const apiEndpoint = "api.amazonalexa.com";
const token = "bearer " + self.event.context.System.apiAccessToken;
const language = self.event.request.locale;
// APIパス
const apiPath = "/v1/users/~current/skills/~current/inSkillProducts";
const options = {
host: apiEndpoint,
path: apiPath,
method: 'GET',
headers: {
"Content-Type" : 'application/json',
"Accept-Language" : language,
"Authorization" : token
}
};
// APIを呼び出します
const req = https.get(options, (res) => {
res.setEncoding("utf8");
if(res.statusCode != 200) {
console.log("InSkillProducts returned status code " + res.statusCode);
self.emit(":tell", "購入履歴のロード中に問題が発生しました。エラーコードは" + res.code );
}
res.on('data', (chunk) => {
console.log("Chunk:" + chunk);
returnData += chunk;
});
res.on('end', () => {
var inSkillProductInfo = JSON.parse(returnData);
if(Array.isArray(inSkillProductInfo.inSkillProducts))
self.attributes.InSkillProducts = inSkillProductInfo.inSkillProducts;
else
self.attributes.InSkillProducts=[];
console.log("Product list loaded:" + JSON.stringify(self.attributes.InSkillProducts));
callback(self, self.attributes.InSkillProducts);
});
});
req.on('error', (e) => {
console.log('Error calling InSkillProducts API: ' + e.message);
self.emit(":tell", "製品リストのロード中に問題が発生しました。エラーコードは" + e.code + "、メッセージは" + e.message);
});
} // (!self.attributes.areProductsLoaded) {}であれば終了
else {
console.log("製品情報は既にロードされています。");
callback(self, self.attributes.InSkillProducts);
return;
}
}
// 商品リストを処理します。
var functionToProcessProductListOnceItIsLoaded = function(self, inSkillProductList) {
if (!inSkillProductList) {
console.log("製品リストのロード中に問題が発生しました。");
}
// 取得した商品リストに対するアクション
for (var idx = 0; idx < inSkillProductList.length; idx ++) {
console.log("inSkillProductList[" + idx + "] is:" + JSON.stringify(inSkillProductList[idx]));
}
}
購入リクエストに対応するコードを追加する
ユーザーがスキル内で商品を参照し、名前を指定していずれかを購入するためのコードを追加します。たとえば、次のようなリクエストをサポートできます。
- 何を買えるの?
- どんなものを買えるの?
- 買えるものを教えて
- <商品名>を買いたい
- <商品名>が欲しい
- アレクサ、<スキル呼び出し名>に<商品名>を頼んで
購入リクエストに対応するには、次を実行します。
- 購入リクエストに対応するカスタムインテントを作成します
- カスタムインテントを処理するコードを追加し、ディレクティブを送信して購入フローを開始します
インテントを作成する
以下は、商品を名前指定で購入するか、購入する商品を見つけるユーザーリクエストをサポートするためのカスタムインテントをモデリングするJSONの例です。
例
{
"intents": [
{
"name": "WhatCanIBuyIntent",
"samples": [
"何を買えるの",
"どんなものを買えるの",
"買えるものを教えて",
"買う",
"買い物",
"購入する"
],
"slots": []
},
{
"name": "BuyIntent",
"samples": [
"{ProductName}を買います",
"{ProductName}を購入",
"{ProductName}が欲しい",
"{ProductName}をお願い"
],
"slots": [
{
"name": "ProductName",
"type": "LIST_OF_PRODUCT_NAMES"
}
]
}
],
"types": [
{
"name": "LIST_OF_PRODUCT_NAMES",
"values": [
{
"id": "reference_name",
"name": {
"value": "商品A",
"synonyms": [
"Aという商品"
]
}
}
]
}
]
}
インテントを処理する
購入インテントを実装する場合、コードでは次の2つのシナリオを処理する必要があります。
ユーザーが商品を指定しなかった場合
提供されている商品をユーザーがたずねてきた場合、ユーザーに商品のいずれかを指定するよう案内します。つまり、コードでは一般的な買い物または購入リクエストを検出し、ユーザーが選択して購入できる商品のリストを提供して応答を返す必要があります。リストを提供する場合は次のようにします。
- 同時にリストとして提供する数を2~3個にして選択肢を覚えやすくします。
- 読点ではなく句点やSSMLのブレークタグを使用して、選択肢を区別しやすくします。
例
ユーザー: 何を買えるの?
Alexa: 利用できる拡張パックが二つあります。洞窟探検。または深海探査です。どちらにしますか?
ユーザーが商品を指定した場合
ユーザーが商品を直接指定する場合もあれば、商品を選択するようにこちらから指示する場合もあります。いずれにしても、ユーザーが購入する商品を指定したら、Buy
ディレクティブを送信して購入フローを開始します。
購入フローが開始されると、スキルセッションは終了します。Amazon側で、音声対話モデルと購入のすべてのメカニズムを処理し、商品のスキーマから商品説明と標準価格を取得します。購入が完了するとスキルが新しいセッションで再度起動し、購入結果がスキルに提供されます。
Buy
リクエストを送信すると、スキルセッションは終了します。関連するユーザーデータは、必ず永続的なデータストアに保存してください。購入フローの完了後、Alexaがスキルを再開します。保存されたデータを使用してスキルを続行できます。永続アトリビュートの詳細については、Node.js、Java、PythonのASK SDKドキュメントを参照してください。例
Lambda関数にコードを追加して購入リクエストのConnections.SendRequest
ディレクティブを送信する方法の例を以下に示します。
このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。
return handlerInput.responseBuilder
.addDirective({
type: "Connections.SendRequest",
name: "Buy",
payload: {
InSkillProduct: {
productId: "amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
}
},
token: "correlationToken"
})
.getResponse();
このサンプルコードはAlexa Skills Kit SDK for Node.js(v1)を使用しています。
this.handler.response = {
'version': '1.0',
'response': {
'directives': [
{
'type': 'Connections.SendRequest',
'name': 'Buy',
'payload': {
'InSkillProduct': {
'productId': 'amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
}
},
'token': 'correlationToken'
}
],
'shouldEndSession': true
}
};
this.emit(":responseReady");
購入リクエストのConnections.SendRequest
ディレクティブのJSON構文です。この場合、name
はBuy
です。
{
"directives": [
{
"type": "Connections.SendRequest",
"name": "Buy",
"payload": {
"InSkillProduct": {
"productId": "amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}
},
"token": "correlationToken"
}
]
}
ディレクティブのフィールド
フィールド | 型 | 説明 | 必須 |
---|---|---|---|
type |
文字列 | ディレクティブのタイプです。購入フローでは常にSendRequest です。 |
◯ |
name |
文字列 | SendRequest メッセージのターゲットです。購入リクエストでは常にBuy です。 |
◯ |
payload |
オブジェクト | 指定されたアクションの詳細を含みます。購入リクエストの場合は、InSkillProduct プロパティと商品IDが含まれます。 |
◯ |
token |
文字列 | このメッセージの交換を識別しスキル情報を保存するトークンです。このトークンはAlexaでは使用しませんが、Connections.Response の結果として返されます。このトークンはスキルに合わせた形式で提供します。 |
◯ |
shouldEndSession |
ブール値 | trueを送信してセッションが終了することを示します。ただし、SendRequest メッセージが送信された後は常にセッションは終了します。 |
◯ |
お勧めを行う
ユーザーがスキル内商品を購入するもう1つの方法は、お勧めを利用することです。つまり、ユーザーが現在スキルと行っている対話に関連した商品を事前に提案します。保存されたリストを参照してユーザーが商品を所有しているかどうかを確認し、お勧めする商品と、商品に関連したメッセージを、Amazonの購入フローに渡します。購入の提案を行うには、ディレクティブで購入フローを開始し、アップセルメッセージを含むコードを追加します。
例
ユーザーがコンテンツを終了した後、スキルはアップセルリクエストを提示するディレクティブを送信します。スキルセッションが終了し、Alexaはディレクティブに含まれるupsellMessageを話します。
Alexa: 洞窟探検拡張パックを入手すると、冒険をもっと楽しめます。詳しく知りたいですか?
スキルはUpsell
リクエストを提示するディレクティブを送信します。これにより現在のスキルセッションが終了します。
ユーザー: はい
買い切り型の場合、購入を確定する前にAmazonからpurchasePromptDescription
と価格が提供されます。
サブスクリプション型の場合は、購入の確定前に価格が提供されます。
お勧めを開始するディレクティブを送信する
ユーザーインテントに対してお勧めの提案で応答するには、Connections.SendRequest
ディレクティブをAlexaに送信します。以下のようなものです。
Upsell
タスクを指定してお勧めを提示します。- 提案する商品のIDを提供します。
- お勧めのメッセージプロンプト(
upsellMessage
)を提供します。 - スキルの現在の状態やその他の関連情報を保存するトークンが含まれます。Alexaは、
SendRequest
呼び出しからの応答で、スキルにトークンを戻します。
アップセルメッセージに何を含めるかについて詳しくは、お勧めをデザインするを参照してください。
購入フローが開始されると、スキルセッションは終了します。Amazonは、音声対話モデルと、購入のあらゆるしくみをサポートしています。Alexaはアップセルメッセージを再生し、ユーザーが確認したら、商品スキーマから取得した商品説明と標準価格を再生します。購入が完了すると、Alexaはスキルを再起動し、購入結果を提示します。
Upsell
リクエストを送信すると、スキルセッションは終了します。関連するユーザーデータは、必ず永続的なデータストアに保存してください。購入フローの完了後、Alexaがスキルを再開します。保存されたデータを使用してスキルを続行できます。永続アトリビュートの詳細については、Node.js、Java、PythonのASK SDKドキュメントを参照してください。例
Lambda関数にコードを追加してお勧めのConnections.SendRequest
ディレクティブを送信する方法の例を以下に示します。
このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。
return handlerInput.responseBuilder
.addDirective({
type: "Connections.SendRequest",
name: "Upsell",
payload: {
InSkillProduct: {
productId: "amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
},
upsellMessage: "こちらの商品はいかがでしょう。詳しく知りたいですか?",
},
token: "correlationToken",
})
.getResponse();
このサンプルコードはAlexa Skills Kit SDK for Node.js(v1)を使用しています。
this.handler.response = {
'version': '1.0',
'response': {
'directives': [
{
'type': 'Connections.SendRequest',
'name': 'Upsell',
'payload': {
'InSkillProduct': {
'amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
},
'upsellMessage': 'こちらの商品はいかがでしょう。詳しく知りたいですか?'
},
'token': 'correlationToken'
}
],
'shouldEndSession': true
}
};
this.emit(":responseReady");
アップセルのConnections.SendRequest
ディレクティブのJSON構文です。この場合、name
はUpsell
であり、payload
にはupsellMessage
が含まれます。
{
"directives": [
{
"type": "Connections.SendRequest",
"name": "Upsell",
"payload": {
"InSkillProduct": {
"productId": "amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
},
"upsellMessage": "こちらの商品はいかがでしょう。詳しく知りたいですか?"
},
"token": "correlationToken"
}
]
}
ディレクティブのフィールド
フィールド | 型 | 説明 | 必須 |
---|---|---|---|
type |
文字列 | ディレクティブのタイプです。購入フローでは常にSendRequest です。 |
◯ |
name |
文字列 | SendRequest メッセージのターゲットです。お勧めでは常にUpsell です |
◯ |
payload |
オブジェクト | 指定されたアクションの詳細を含みます。お勧めの場合、これにはInSkillProduct プロパティと商品ID、およびupsellMessage プロパティとカスタムメッセージが含まれます。 |
◯ |
upsellMessage |
文字列 | 現在のユーザーコンテキストに沿った商品のお勧めです。常に明示的な確認の質問で終わる必要があります。 | ◯ |
token |
文字列 | このメッセージの交換を識別しスキル情報を保存するトークンです。このトークンはAlexaでは使用しませんが、Connections.Response の結果として返されます。このトークンはスキルに合わせた形式で提供します。 |
◯ |
shouldEndSession |
ブール値 | trueを送信してセッションが終了することを示します。ただし、SendRequest メッセージが送信された後は常にセッションは終了します。 |
◯ |
購入フロー後にスキルを再開する
購入フローの完了後、Alexaがスキルを再開します。購入結果を処理するコードを追加し、シームレスにスキルを続行します。
スキルは、購入リクエストの結果をConnections.Response
リクエストとして受け取ります。ペイロードに含まれるpurchaseResult
に基づいてスキルを再開します。Alexaは、リクエストに渡したtoken
も返します。トークンを使って、ユーザーが中断したところからスキルを再開できます。
各結果タイプを処理する方法の詳細については、ガイドラインを参照してください。
購入結果の例
以下のJSONは、商品オファーからのConnections.Response
リクエストを示しています。
{
"type": "Connections.Response",
"requestId": "string",
"timestamp": "string",
"name": "Upsell",
"status": {
"code": "string",
"message": "string"
},
"payload": {
"purchaseResult":"ACCEPTED",
"productId":"string",
"message":"任意の追加メッセージ"
},
"token": "string"
}
購入結果の定義
プロパティ | 説明 | 型 |
---|---|---|
|
リクエストのタイプです。 |
文字列 |
|
Connections.Responseリクエストの一意のIDです。 |
文字列 |
|
Connections.Responseリクエストの時間です。 |
文字列 |
|
この応答が適用されるリクエストの名前です。 |
文字列 |
|
HTTPステータスの結果です。 |
オブジェクト |
|
HTTP応答のステータス( |
文字列 |
|
HTTPステータスのメッセージ( |
文字列 |
|
購入またはキャンセルトランザクションの結果を含みます。 |
オブジェクト |
|
購入またはキャンセルリクエストの結果です。 |
文字列 |
|
リクエストで送信されたスキル内商品のIDです。 |
文字列 |
|
(オプション)キャンセルまたは購入トランザクションに関するそのほかの詳細です。 |
文字列 |
|
購入またはキャンセルフローが完了したときにAlexaが応答で返すオプションの文字列です。たとえば、このトークンを使用して、購入後にスキルを再開するための情報を保存できます。 |
文字列 |
購入結果を処理する際のガイドライン
payload.productId
を渡してすべてのスキル内商品を取得するAPIを実行し、応答に返された商品のステータスを確認します。purchaseResult
を処理するには、次のガイドラインを参照してください。
結果がACCEPTED
Alexaのセリフ:
- 買い切り型または消費型: 「はい、 <スキル内商品名>の購入処理が完了しました」
- サブスクリプション型: 「はい、<スキル内商品名>を使用できます」
スキルの動作:
自動的にリクエストされたコンテンツを開始、またはリクエストされた消費型アイテムを使用します。たとえば「それではプレイしましょう…」や「ではヒントです…」などです。ユーザーが購入を承諾したら、元のリクエストを完了するようにしてください。
消費型商品の場合は、リクエストで指定されたuserId
に関連付けられている永続ストレージにあるユーザーのアイテムのインベントリーを更新する必要もあります。消費型購入のインベントリーの管理を参照してください。
例:
ユーザーが購入を完了しました
Alexa: 洞窟探検を入手しました
スキルが起動します
Alexa: それではプレイしましょう
結果がPENDING_PURCHASE
購入フローに想定よりも時間がかかることがあります。たとえば、現地の銀行規制により、ユーザーは銀行の支払い認証をAlexa購入フローの外部で行わなければならない場合があります。スキルに購入が進行中であることを知らせるには、AlexaはPENDING_PURCHASE
を返します。
スキルの動作:
- その商品の購入ステータスを確認します。応答でユーザーが商品を購入済み(
entitled
=ENTITLED
)であることがわかったら、プレミアムコンテンツで続行します。 - 未購入の場合は、ユーザーが中断したところまで戻り、ユーザーに続行する方法を提案します。たとえば、ほかの購入済みコンテンツや無料のコンテンツで続行します。
- 購入が保留中であることには言及しません。
- 次のスキルセッションの開始時に、ユーザーの購入ステータスを確認します。これは、購入がスキルセッションの外で完了している可能性があるためです。
結果がDECLINED
Alexaのセリフ:
- 買い切り型または消費型: 「わかりました。」
- サブスクリプション型: 「はい、わかりました。<スキル内商品名>に申し込みたい時は、いつでも言ってください。」
スキルの動作:
ユーザーが中断したところまで戻り、ユーザーに続行する方法を提案します。
例:
ユーザーが購入を拒否しました
Alexa: わかりました
スキルが起動してからのセリフ
Alexa: コレクションから冒険を選びましょう。どれをプレイしますか?
結果がERROR
Alexaのセリフ:
応答はエラーの原因によって異なります。
- 「支払い方法が正しく設定されていません。Alexaアプリに設定画面へのリンクを送信しましたので、そちらで確認してみてください。」
- 「すみません、うまく処理できません。」
- 「すみません。<コンテンツのタイトル>はご使用の国では利用できません。」
- 「すみません、このデバイスではスキル内課金を行うことができません。」
スキルの動作:
- ユーザーが中断したタイミングまで戻って、続行方法を提供します。
- エラーが発生したことには言及しません。スキルに戻る前に、ユーザーは購入フローから説明を受け取っています。
- ユーザーにもう一度行うかどうかをたずねないでください。
例:
ユーザーは購入に同意しましたが、支払い情報を更新する必要があります
Alexa: 支払い方法が正しく設定されていません。Alexaアプリに設定画面へのリンクを送信しましたので、そちらで確認してみてください。
スキルが起動します
Alexa: コレクションから冒険を選びましょう。どれをプレイしますか?
結果がALREADY_PURCHASED
この結果は、買い切り型およびサブスクリプション型で返されることがあります。
消費型は商品を複数回購入できるため、消費型でこの結果が返されることはありません。
Alexaのセリフ:
<スキル内商品名>はすでにお持ちです
スキルの動作:
- リクエストされたコンテンツを自動的に開始します。
- 別の商品のオファーはしません。
例:
ユーザーがクリスタルキャッチャーを購入するように頼みましたが、ユーザーはすでに持っています。
Alexa: ご存じでしたか?クリスタルキャッチャーはすでにお持ちです。
スキルが起動します
Alexa: コレクションから冒険を選びましょう。どれをプレイしますか?
返金またはキャンセルのリクエストを処理する
購入代金を返金するユーザーリクエストを処理し、リクエストを購入フローに転送できる機能も必要です。
ユーザーは次のように返金を依頼することがあります。
- アレクサ、<スキル呼び出し名>に<商品名>の返品を頼んで
- アレクサ、<商品名>を払い戻して
- <商品名>を返品したい
- <商品名>の返金を受けたい
例:
ユーザー: アレクサ、洞窟探検を返金して
スキルはCancel
リクエストを示すConnections.SendRequest
ディレクティブを送信します。これにより現在のスキルセッションが終了します。
Alexa: 返金については、Alexaアプリにリンクを送信しましたので、そちらで確認してください。
サブスクリプション型のキャンセル依頼は次のように行われることがあります。
- 私の<商品名>のサブスクリプションをキャンセルして
- <商品名>のサブスクリプションをキャンセルして
例:
ユーザー: アレクサ、私の宝探しプラスのサブスクリプションをキャンセルして。
スキルはCancel
リクエストを示すConnections.SendRequest
ディレクティブを送信します。これにより現在のスキルセッションが終了します。
Alexa: わかりました。念のため、宝探しプラスは、ここでしかプレイできない冒険を10個以上集めたもので、毎月新しい冒険が1つ追加されます。ご利用のサービスをキャンセルするんですね?
キャンセルや返金のリクエストをサポートするには、次を実行します。
- 返金/キャンセルリクエストをサポートするカスタムインテントを作成します
- カスタムインテントを処理するコードを追加し、ディレクティブを送信してキャンセルフローを開始します
インテントを作成する
以下は、購入を返金するユーザーリクエストをサポートするためのカスタムインテントをモデリングするJSONの例です。サンプルインテントは商品の返品や返金のバリエーションと、スキル内商品のリストを含むカスタムスロットのバリエーションを一覧表示します。
Amazon.CancelIntent
をトリガーしてスキルセッションを終了しないように、サブスクリプションをキャンセルするインテントには、「サブスクリプション」という単語と商品名が必要です。例
{
"intents": [
{
"name": "RefundSkillItemIntent",
"samples": [
"{ProductName}を返品",
"{ProductName}を返金",
"{ProductName}の返金を受けたい",
"{ProductName}を返品したい"
],
"slots": [
{
"name": "ProductName",
"type": "LIST_OF_PRODUCT_NAMES"
}
]
}
],
"types": [
{
"name": "LIST_OF_PRODUCT_NAMES",
"values": [
{
"id": "reference_name",
"name": {
"value": "商品A",
"synonyms": [
"Aという商品"
]
}
}
]
}
]
}
インテントを処理する
返金/キャンセルインテントのハンドラーで、キャンセルを示すメッセージを購入フローに送信する必要があります。
Cancel
リクエストを送信すると、スキルセッションは終了します。関連するユーザーデータは、必ず永続的なデータストアに保存してください。購入フローの完了後、Alexaがスキルを再開します。保存されたデータを使用してスキルを続行できます。永続アトリビュートの詳細については、Node.js、Java、PythonのASK SDKドキュメントを参照してください。コードを追加して返金リクエストのConnections.SendRequest
ディレクティブを送信する方法の例を以下に示します。
このサンプルコードはAlexa Skills Kit SDK for Node.js(v2)を使用しています。
return handlerInput.responseBuilder
.addDirective({
type: 'Connections.SendRequest',
name: 'Cancel',
payload: {
InSkillProduct: {
productId: "amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
}
},
token: "correlationToken"
})
.getResponse();
このサンプルコードはAlexa Skills Kit SDK for Node.js(v1)を使用しています。
this.handler.response = {
'version': '1.0',
'response': {
'directives': [
{
'type': 'Connections.SendRequest',
'name': 'Cancel',
'payload': {
'InSkillProduct': {
'productId': 'amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
}
},
'token': 'correlationToken'
}
],
'shouldEndSession': true
}
};
this.emit(":responseReady");
キャンセルまたは返金リクエストのConnections.SendRequest
ディレクティブのJSON構文です。この場合、name
はCancel
です。
{
"directives": [
{
"type": "Connections.SendRequest",
"name": "Cancel",
"payload": {
"InSkillProduct": {
"productId": "amzn1.adg.product.aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}
},
"token": "correlationToken"
}
]
}
ディレクティブのフィールド
フィールド | 型 | 説明 | 必須 |
---|---|---|---|
type |
文字列 | ディレクティブのタイプです。購入フローでは常にSendRequest です。 |
◯ |
name |
文字列 | SendRequest メッセージのターゲットです。返金またはキャンセルの場合は常にCancel です。 |
◯ |
payload |
オブジェクト | 指定されたアクションの詳細を含みます。キャンセルリクエストの場合は、InSkillProduct プロパティと商品IDが含まれます。 |
◯ |
token |
文字列 | このメッセージの交換を識別しスキル情報を保存するトークンです。このトークンはAlexaでは使用しませんが、Connections.Response の結果として返されます。このトークンはスキルに合わせた形式で提供します。 |
◯ |
shouldEndSession |
ブール値 | trueを送信してセッションが終了することを示します。ただし、SendRequest メッセージが送信された後は常にセッションは終了します。 |
◯ |
消費型購入のインベントリーの管理
消費型商品を提供する場合は、ユーザーがアイテムを購入したり使用したりするたびに、そのアイテムのインベントリーを管理する必要があります。Alexaは、ユーザーが特定の商品を購入した合計回数は追跡していますが、この回数にはユーザーがその購入済みユニットを消費したかどうかは反映されません。スキルが消費型購入でACCEPTED
応答を受け取った場合、以下を行う必要があります。
- 永続ストレージのユーザーインベントリーを更新して、アイテムの正しい数を反映させます。たとえば、ユーザーが「ヒント5個パック」を購入した場合、ヒントインベントリーに5個のアイテムを追加します。
- 該当する場合はアイテムを使用し、インベントリーの数を減らします。
- インベントリーの更新を永続ストレージに保存します。
ASK SDKs for Node.js、Java、およびPythonにはすべて、アトリビュートを保存するPersistenceAdapter
があります。詳細については、ASK SDKドキュメントの永続アトリビュートを参照してください。
ユーザーのインベントリーを永続ストレージに保存するときは、常にインベントリーをリクエストに含まれるuserId
にマッピングします。userId
は、リクエストのcontext
オブジェクトで提供されます(context.System.User.userId
)。SDKで提供されるデフォルトの永続レイヤーは、自動的にuserId
を使用します。
消費型商品の合計課金数を取得する
ユーザーが特定の消費型商品を購入した合計回数を取得するには、Get customer entitlementAPIを呼び出し、応答のactiveEntitlementCount
プロパティを確認します。activeEntitlementCount
の値は、ユーザーが行った購入の合計回数です。商品を購入するたびにactiveEntitlementCount
が1つずつ増えます。キャンセル(支払いエラーや返金リクエストなどのため)のたびにactiveEntitlementCount
が1つずつ減ります。
複数のアイテムのパックを提供している場合、購入の合計回数とユーザーがスキルで使えるアイテムの数が合わない場合があります。たとえば、スキルで「ヒント5個パック」を提供しているとします。ユーザーがこのパックを3回購入します。購入1回ごとに、インベントリーを5ずつ増やします。この例では、以下のようになります。
- ユーザーの購入ステータスの取得から返される
activeEntitlementCount
は3
です。 - このユーザー用に保有しているインベントリーの数は15です。
その後、ユーザーは15個のヒントのうち、14個を使います。この時点では、以下のようになります。
- ユーザーの購入ステータスの取得から返される
activeEntitlementCount
はこの時点でも3
です。 - このユーザー用に保有しているインベントリーの数は
1
となります。
リクエストごとにインベントリーを検証する
ユーザーの購入済み消費型アイテムの数は、通常のスキルセッション外で変更される可能性があります。例
- 購入フローに想定より時間がかかったが、スキルセッション終了後に正常に完了した場合。ユーザーが次回スキルを開いたとき、
activeEntitlementCount
はユーザーのインベントリーを反映した数よりも大きい値を返します。 - ユーザーが間違って購入した商品の返金をリクエストした場合。ユーザーが次回スキルを開いたとき、
activeEntitlementCount
は前回より小さい値を返します。永続ストレージのユーザーのインベントリーには、返金前のユーザーの購入が反映されています。 - ユーザーテスト中に、
reset-isp-entitlement
を使用して自分の購入を消去し、やり直した場合。次にスキルを開くと、activeEntitlementCount
は0に戻っていますが、永続ストレージの自分のインベントリーには以前のテスト購入が反映されています。
このような場合は、リクエストを受信するたびにインベントリーを確認して更新します。ASK SDK for Node.js、Java、Pythonには、リクエストインターセプターが含まれます。これらを使用して、通常のリクエストハンドラーの前にコードを実行します。ここで、ユーザーの購入ステータスのチェックを実装します。詳細については、でリクエストインターセプターを参照してください。
ユーザーがスキルを無効化してから再有効化した場合にユーザーインベントリーを管理する
スキルが消費型商品を提供する場合、ユーザーがスキルを無効にしてから再度有効にしても、userId
は変わりません。これにより、このシナリオで保有しているアイテムのインベントリーが失われることはありません。
このシナリオでuserId
にこのような特別な動作をさせるために、何かする必要はありません。ただし、AlexaSkillEvent.SkillDisabled
イベントを使用して、ユーザーがスキルを無効にした後に何らかのクリーンアップを実行する場合、ユーザーの消費型購入のインベントリーを維持するようにしてください。イベントオブジェクトでuserInformationPersistenceStatus
プロパティを確認できます。
PERSISTED
: ユーザーがスキルを再度有効にする場合に同じuserId
を使用するために、データを消去しません。NOT_PERSISTED
: ユーザーがスキルを再度有効にした場合、新しいuserId
が取得されます。そのため、userId
に伴って保存されたデータを消去しても安全です。
userId
の動作は、消費型商品を提供するスキルにのみ適用されることに注意してください。消費型商品を提供しないスキルが無効にされた場合は、AlexaはuserId
をリセットします。サンプルコード
次のサンプルコードでは、さまざまな支払いモデルの商品にスキル内課金を使用する方法を示しています。
- Build An Alexa Skill with In-Skill Purchases - Premium Fact (Node.js)(英語)
- Build An Alexa Skill with In-Skill Purchases using ASK Python SDK(英語)
- Build An Alexa Skill with In-Skill Purchases - Premium Hello World (Node.js)(英語)
- Build An Alexa Skill with In-Skill Purchases - Premium Hello World (Python)(英語)
関連トピック
最終更新日: 2024 年 04 月 10 日