Amazon Alexa
ask-utils

Alexaでのスキル課金の実装を楽にするライブラリ`@ask-utils/isp`をリリースしました。

ついに公式アナウンスがありましたね。

5月31日、スキル内課金を使ったスキルを日本のAmazon Alexaユーザー向けにも開発できるようになります。スキル内課金を使うと、開発者やコンテンツクリエーターは、プレミアムコンテンツにスキル内で課金して収益につなげることができます。

https://developer.amazon.com/ja/blogs/alexa/post/65d66ff2-29bb-468b-b732-75748903f73a/developers-in-japan-can-now-monetize-alexa-skills-with-in-skill-purchasing

日本がアメリカに次いで2番目の解禁とのことで、Alexa熱がシアトルに伝わったのかなと勝手に思っていたりします。

コードを楽に書きたい

で、スキル課金対応のスキルを作るにあたって難関になってくるのが「どうやって実装すればいいの」というところです。

素直に実装しようとすると、おおよそこんな感じのコードになります。

なお、サンプルということで例外処理系を横着して全部throwしちゃってます。必要に応じてレスポンスを作ってあげてください。

import { getLocale } from 'ask-sdk-core'
 
const beforeISPUtils = {
  handle() {
    return true
  },
  async handler(handlerInput) {
  if (!handlerInput.serviceClientFactory) throw new Error('No service client')
  const client = handlerInput.serviceClientFactory.getMonetizationServiceClient()
  const { inSkillProducts } = await client.getInSkillProducts(getLocale(handlerInput.requestEnvelope))
  if (!inSkillProducts) throw new Error('No product')
  const product = inSkillProducts.find(product => product.name === 'YOUR_PPRODUCT_NAME')
  if (!product) throw new Error('no product')
  return handlerInput.responseBuilder
      .addDirective({
        'type': 'Connections.SendRequest',
        'name': 'Buy',
        'payload': {
          'InSkillProduct': {
            'productId': product.productId
          }
        },
        'token': 'correlationToken'
      })
      .getResponse()
  }
}

毎回書くにはちょっと長いのでラップした

上のコードを購入処理で毎回書くとなると、スキルの数が増えてきた時にちょっと面倒です。あとfindとかfilterで絞り込むのもできればラップしてしまいたいところ。

ということで、@ask-utils/ispというライブラリを作りました。

使い方

npmで公開しているので、いつもどおりnpm i -S @ask-utils/ispでインストールしてください。

上で紹介しているサンプルを書き換えると、以下のようになります。

import { ISPProductClient, getBuyProductDirective } from '@ask-utils/isp'

const afterISPUtils = {
  handle() {
    return true
  },
  async handler(handlerInput) {
    const client = new ISPProductClient(handlerInput)
    const product = await client.searchProduct({
      productName: 'YOUR_PPRODUCT_NAME'
    })
    if (!product) throw new Error('no product')
    return handlerInput.responseBuilder
      .addDirective(getBuyProductDirective(product.productId))
      .getResponse()
  }
}

クラスの中で絞り込みまでやってしまいますので、かなりシンプルになりました。

しれっとキャッシュします

実装を見てもらうとわかるのですが、一度APIをコールすると取得したアイテム一覧をsessionAttributesにキャッシュします。

デフォルトではオンになっていますので、常にAPIから最新情報を取りたいときはオフにしてください。


    const client = new ISPProductClient(handlerInput)
    const product = await client.disabledCache().searchProduct({
      productName: 'YOUR_PPRODUCT_NAME'
    })

ドキュメント整備中

公式のアナウンスをみて慌ててリリースしたため、まだドキュメントが作れていません。

が、TypeScriptでなるべくDocコメントも入れていますので、ソースをみればなんとなくはわかるかなと思います。

準備ができ次第npm / GitHubのREADME.mdにリンクが付くと思いますので、今しばらくお待ち下さい。


Random posts

GitHubHomeEnglish