T

Titanium Mobileでアプリ内課金(iOS編)

アプリ内課金の実装方法について知りたいという声があったので、参考になればと書いておきます。アプリ開発者の方は権利の15%で取引に応じますの苦労がこれで少しでも減るなら喜ばしいことです。とはいえ、世知辛い世の中ですから、このコードで生じた金銭的問題の責任は放棄しますので、ご利用の際はあくまでも参考程度にとどめ、十分検証した上で実装されるようお願いします。

一応、動作はTiStorekit1.4、Titanium Mobile SDK 1.8.1、非消費型コンテンツで確認しています。

iOSの場合、課金の手続きは

  • In App Purchaseが有効になっているかを確認する
  • 正しいProduct IDかを確認する
  • 購入開始
  • 成功ならレシートを受信してチェック、キャンセルやエラーならエラー処理

となります。ここではAppcelerator社から公開されているTiStorekitを使った実装の例で説明します。以前に実装したものを参考にざっと書いた関係で、細かい条件は検証していませんのであしからず。CoffeeScriptだけどいいよね?

事前にiTunes ConnectでIn App Purchaseの設定が完了していることが前提になります。iTunes Connectの使い方は割愛しますが、支払い関連の設定が完了していないとIn App Purchaseは有効になりません。またProvisioning Portalで設定を確認して、正しいProvisioning Profileでビルドしましょう。iOS5からシミュレータからも利用できるようになったようですが、必ず実機で動作確認してください。

そうそう、下のコードではコンストラクタで@SANDBOXをtrueにしていますが、リリース時には変更しましょう!


class InAppPurchase
  constructor:()->
    @ERROR_PRODUCT_ID = {type:'product_id', message:L('inapp_error_product_id')}
    @ERROR_CANMAKEPAYMENT = {type:'canmakepayment', message:L('inapp_error_settings')}
    @ERROR_UNKNOWN = {type:'unknown', message:L('inapp_error_unknown')}
    @SANDBOX = true

  start:(data, success, error)->
    ###
    args  dataは課金対象の商品オブジェクト({product_id:プロダクトID})
          sccess、errorはそれぞれcallback関数
    ###

    storekit = require 'ti.storekit'
    if data.product_id == '' || data.product_id == null || data.product_id == undefined
      # なんて防衛的なコード! # 
      error(@ERROR_PRODUCT_ID)
    else if !storekit.canMakePayments
      # AppStoreが有効になっていない #
      error(@ERROR_CANMAKEPAYMENT)
    else
      # 正しいProduct IDかどうかチェック #
      storekit.requestProducts([data.product_id], (e)=>
        if e.success
          product = e.products[0]
          if product != null
            _success = success
            _error = error
            storekit.purchase(product, (elem)->
              if elem.state == storekit.FAILED
                error(@ERROR_PRODUCT_ID)
              else if elem.state == storekit.PURCHASED
                callback = (response)=>
                  if response.success && response.valid
                    _success()
                  else
                    _error(@ERROR_UNKNOWN)

                # subscriptionの場合は下のオブジェクトにsharedSecretプロパティを追加 #
                args = {
                  receipt:elem.receipt
                  sandbox:@SANDBOX
                  callback:callback
                }
                storekit.verifyReceipt(args)
              else if elem.state == storekit.RESTORED
                storekit.restoreCompletedTransactions()
                success()
              else
                Ti.API.info "purchasing..."
            )
          else
            error(@ERROR_UNKNOWN)
        else
          error(@ERROR_PRODUCT_ID)
      )
TiStorekit = new InAppPurchase()
TiStorekit.start(product, success, error)

canMakePaymentsは定数でiOSの設定画面でAppStoreが有効になっていないのでそもそもアプリ内で課金ができない場合はfalseになります。requestProductsでProduct IDが正しいかどうかを確認し、purchaseで購入処理を開始します。successが返ればveryfyReceiptで確認して完了処理を実行します。

レシートの確認まで実装しましたが、ここは迷うところですね。通信に時間がかかったり、またはこのタイミングで処理が中断してしまった場合、利用者にとってはお金を払ったのに購入が正常に完了しないように見えてしまいます。時間がかかるようならキューに入れてレシートは後で確認し、いったん完了処理をしてから、不正があったらそれなりの処理をする、といったような手段で対応するしかないでしょう。かといってレシートを確認しないと、不正な利用を完全に防ぐことができなくなります。

Posted by on 2月 4, 2012 in Apple, Titanium

コメントを残す