サイトアイコン てっくぼっと!

UnityIAP subscription 対応時の備忘メモ

はじめに

リトルチャンピオンズ では、定期購読型(subscription)課金実装にUnityIAPを使用しました。

今回はその実装を行った際の知見を紹介したいと思います。

subscription以外の実装例については前回の記事 を参照ください。

また、サーバー側の実装についてはこちらの記事 を参照ください。

対象環境

ドキュメント

定期購読型課金 (Auto-Renewing subscription)とは

こうしたサービスやコンテンツは所定の期間で定期購読することができ、満期になると自動的に更新されます。購読期間は、週単位、月単位、四半期単位、年単位など、App によってさまざまです。定期購読によっては、トライアルを割引料金や無料で提供しているものがあります。

定期購読型の仕様

ストアで設定できる項目

iOS Android
価格 価格テーブルから選択(tierと異なる) 税抜きで入力
期間 1週間/1ヶ月/2ヶ月/3ヶ月/6ヶ月/1年 1週間/1ヶ月/3ヶ月/6ヶ月/1年/”季節”
お試し

(1度のみ無料や割引)

期間

価格

都度払い/前払い/無料トライアル

N日無料(3以上)

価格

グループ 同グループ内の優先度設定

アップグレードやダウングレード、クロスグレードがある

なし

※ UnityIAPではお試しは未サポート

消費型と違うこと

実装

購入

購入動線の制御

リトルチャンピオンズでは1つしか定期購読型のプロダクトが存在しなかったため

アップグレード等を考慮した実装まではしませんでしたが、

購入中は、購入動線があってもユーザが混乱するだけなので

アプリ側の購読中判定で購入動線を制御しました。

購入処理自体は消費型と変わりません。(参考:前回の記事)

更新

更新タイミング(決済後の反映)

  1. サーババッチ更新
    • 1日1回、サーバで管理している購入時レシートを再度検証APIでプラットフォーム側に問い合わせ反映します
    • 主にこの契機で反映を考えていたためゲーム内の有効期限は実際の期限より24時間ほどバッファを設けてました
    • この契機で反映した場合、アプリ側から購入リクエストの際に反映済みのステータスコードを返却し裏でトランザクションを閉じています
  2. アプリ側からサーバに購入リクエスト
    • 決済時にアプリ起動中などで1よりも先に反映する場合
    • 通常の購入APIリクエストと同じくレシート情報付きでリクエスト
  3. ユーザがアプリ上から手動リストア
    • 稀なケースだが1, 2にも引っかからなかった場合

トランザクションのリストア

以下呼び出すと未処理のトランザクションがIStoreListener経由で流れ始めます

すべてのトランザクションが流れ終った後、引数のActionが呼ばれます

_appleExtensions.RestoreTransactions(result =>
{
    // リストア完了処理
});

検証環境(Sandbox)

Sandbox環境での自動購読型の注意点

  1. iOSは購読キャンセルができない
    • 普段はAppStoreから購読のキャンセルを行いますが課金テストアカウントではAppStoreに入れないためキャンセルできません
    • Androidは普通にGooglePlayからキャンセル可能
  2. 自動更新
    • 6回の自動更新でその後キャンセルされます
    • テストしていると購入後1回も更新されずキャンセルされているときがあります
      • レシート内のauto_renew_statusで更新されるか確認できます
  3. 更新期間短縮
    • iOS, Androidともに更新期間は短縮されてます
      • 1週間は3分, 1ヶ月は5分など
      • iOS, Androidでほぼ同じですが微妙に違います
      • (少し前まではAndroidは1日と更新テストが面倒でした…)

おまけ

レシート検証

レシート検証はよく使うので書いておきます

iOS

curl -H 'Content-Type:application/json' -i -X POST -d '{"receipt-data":"${Receipt}", "password":"${Secret}"}' https://sandbox.itunes.apple.com/verifyReceipt

Android

リフレッシュトークンを取得(1度のみ)

アクセストークンの取得

curl --data "refresh_token=${RefreshToken}" --data "client_id=${ClientId}" --data "client_secret=${ClientSecret}" --data "grant_type=refresh_token" https://www.googleapis.com/oauth2/v4/token
------------------
{
 "access_token": "xxxxxxxxxxxx",
 "token_type": "Bearer",
 "expires_in": 3600
}

レシート検証

curl https://www.googleapis.com/androidpublisher/v2/applications/${AppPackage}/purchases/subscriptions/${StoreProductId}/tokens/${Token}?access_token=${AccessToken}

リンク

CA EnginnerBlog(iOS)

CA EnginnerBlog(Android)

サブスクリプションのサーバサイド開発で得た知見

Google Developerアプリ内定期購入

モバイルバージョンを終了