【Android】 における Google play billing 課金処理の実装方法

今回Android Studioで初のアプリを開発するにあたり、Google Play の課金の処理を実装しました。情報が少ないというか、バージョン違いのせいか、全く別物に感じる記事が多かった気がしたので、備忘録的に記載してみます。コードが非常に汚く、理解の浅い素人によるものです。また、何も保障できるものではありませんので、ご了承下さい。

こちらの公式を参考に実装しました。

言語はkotlinです。Goolge play consoleでアイテム登録済み、有効化済みであることが前提です。ちなみに課金のテストは、Goolge play consoleにAPKをアップロードして(内部テストでOKだったと思いますが、内部アルファ版テスト公開したほうが確実そうです)、そのリンクからテスターとしてアプリをインストールを1度以上行わないと、エラーになったと思います。知らずにハマった記憶があります。また、アイテムをGoolge playに登録して1時間くらい skuDetailsList が取得できない(nullが返る)ことがありました。登録直後から取得できたときもあり、まちまちなようです。

追記)課金アイテムを追加したとき、いままで取得できていたskuDetailsが Android Studio上で、全て取得できなくなることがありました。その際には、端末上のアプリを一旦削除し(端末をつないでデバッグしていました)、Google Play から既存のアプリをダウンロードして再インストールした後、Android Studioで実行すると新たな課金アイテムを含めて取得できるようになりました。PCの再起動等、アプリの削除等を行ってもダメだったので、Google Play 経由でインスト―ルないとダメだったように感じました。どうもGoogle Play 絡みになると、コードのミスではない原因による不具合がたくさん発生して、すんなりいかないことが多いですね。

 

build.gradleに、下記を追加して、右上の「sync」をクリックして反映させます。

dependencies {
    def billing_version = "4.0.0"
    implementation "com.android.billingclient:billing:$billing_version"
}

次に実際に実装したコードです。

課金の処理は、1.BillingClientを作成、2.GooglePlayに接続、3.アイテムの情報を取得、4購入リクエスト、のような流れになります。

※スコープ、名前空間等、理解できてないため、書くべき場所がわからず、MainActivityに全て書いてます。適切な場所に記述して下さい。また、Contextを理解してません。

package *

import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.BillingFlowParams


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var billingClient: BillingClient? = null
        var billingFlowParams: BillingFlowParams? = null
        context = applicationContext
        
        //購入を承認したあとのコールバック 特に何もせずに課金フラグをたてたが・・・
        val acknowledgePurchaseResponseListener = AcknowledgePurchaseResponseListener {
       //購入が完了したときの処理
        }

        //消費不可アイテムの購入を承認するには、Google Play Billing Library の BillingClient.acknowledgePurchase() または Google Play Developer API の Product.Purchases.Acknowledge を使用します。
        fun handlePurchase(purchase: Purchase) {
            if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
                //購入がまだ承認されていないならば購入を承認する
                if (!purchase.isAcknowledged) {
                    val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(purchase.purchaseToken)
                        .build()
                    billingClient?.acknowledgePurchase(acknowledgePurchaseParams,
                        acknowledgePurchaseResponseListener)
                }
            }
        }

        //BillingClient を初期化する このとき、購入時にコールバックされるonPurchasesUpdatedのリスナーを作って登録する
        // ※Android公式ページでは、onPurchasesUpdatedをオーバーライドするように書かれていたが、kotlinではオーバーライドできなかった
        // 試行錯誤の末、ラムダ式で実現できた
        val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases ->
            // To be implemented in a later section.
            if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
                for (purchase in purchases) {
                    handlePurchase(purchase)//購入するとここを通る
                }
            } else if (billingResult.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
                // Handle an error caused by a user cancelling the purchase flow.
            } else {
                // Handle any other error codes.
                //既に購入済みの場合ここを通る
                if(billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {

                }
            }
        }

        billingClient = BillingClient.newBuilder(context)
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .build()


        //Google Play との接続を確立する
        billingClient.startConnection(object : BillingClientStateListener {
            override fun onBillingSetupFinished(billingResult: BillingResult) {
                if (billingResult.responseCode ==  BillingClient.BillingResponseCode.OK) {
                    // The BillingClient is ready. You can query purchases here.
                    //購入可能なアイテムの情報を取得するためのパラメータparamsを作成

                    val skuList = ArrayList<String>()
                    skuList.add("****************")//googleに登録したアイテムID
                    val params = SkuDetailsParams.newBuilder()
                    params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP)

                    //googleに商品が登録されて有効になっていて、上記の設定が正しいと以下の処理でskuDetailsListに情報が取れるようになる
                    billingClient.querySkuDetailsAsync(params.build()) { billingResult, skuDetailsList ->
                        //情報が取得できるとonSkuDetailsResponseが呼ばれて、billingFlowParamsを作成完了(購入時にポップアップを表示するとき使う情報)
                        // Process the result.
                        val skuDetails = skuDetailsList!![0]
                        //購入フローを起動する
                        // Retrieve a value for "skuDetails" by calling querySkuDetailsAsync().
                        billingFlowParams = BillingFlowParams.newBuilder()
                            .setSkuDetails(skuDetails)
                            .build()

                        //購入準備ができたときに実行する処理
                    }
                }
            }
            override fun onBillingServiceDisconnected() {
                // Try to restart the connection on the next request to
                // Google Play by calling the startConnection() method.
            }
        })

        //購入ボタン押下時に実行
        fun TapPurchase() {
            //購入処理開始、ウィンドウをポップアップ
            //!! = Null の場合 Null Pointer Exception が発生します。
            val responseCode = billingClient!!.launchBillingFlow(this, billingFlowParams!!)?.responseCode
        }

        //購入ボタン押下時に呼ぶ関数を登録
        button.setOnClickListener {
            TapPurchase()
        }
    }
}

以上です。

※ 購入の準備ができたときの処理のタイミングが間違っていたので修正しました

comment

タイトルとURLをコピーしました