【Android】PlayBillingLibrary を3.0→4.0にバージョンアップ&課金処理実装してみた

# はじめに
こんにちは。
PlayBillingLibraryのバージョンを上げる機会があったので備忘録として残しておきます。

[RevenueCat](https://www.revenuecat.com/)というツールを使ってアプリにサブスクリプションを実装しようとした際にPlayBillingLibraryがVer4.0に上がるみたく今まではVer3.0を使っていたがビルドすら通らなくなったのでもれなくPlayBillingLibraryもVer4.0に上げなきゃいけない事になりました。

## PlayBillingLibraryとは
Androidアプリ内で課金を実装するライブラリです。
簡単に言うと課金処理の際にGoogle Playとアプリを繋げてくれるやつ。

このライブラリは毎年バージョンアップが行われサポート期間は2年です。
2年経つと足切りされ課金ができなくなる？のでチェックしてみてください。

ちなみに2022年6月現在Ver5.0までリリースされています。

以下参考

https://developer.android.com/google/play/billing/release-notes?hl=ja

## PlayBillingLibrary4.0の変更点
### 機能編
- **Multi-quantity Purchase**
    - アプリ内で一度に複数個のアイテムを購入できる。
- **Multi-line Subscription**
    - 単一の課金アイテムをいくつかまとめてサブスクリプションとして販売できるようになった。

### コード編
- 今後のリリースで削除される予定の **BillingClient.queryPurchases()** の代わりとして**BillingClient.queryPurchasesAsync()**  を追加
- 定期購入の新しい交換モードである **IMMEDIATE_AND_CHARGE_FULL_PRICE** を追加
- Play Billing Library の接続状態を取得する **BillingClient.getConnectionState()** メソッドを追加
- **Purchase#getSkus()** と **PurchaseHistoryRecord#getSkus()** を追加

#### 注意点
PlayBillingLibrary5.0で **BillingClient.queryPurchases()** が削除されたので早めに**BillingClient.queryPurchasesAsync()** に変更しておきましょう。

また、PlayBillingLibrary3.0では1回の購入で一つのアイテムしか購入できなかったためアイテムを取得するのに **getSku()** を使用できました。
```java
// ver3.0
purchase.getSku("課金アイテムのProductID");
```

PlayBillingLibrary4.0では課金アイテムを取得するメソッド **getSku()** が削除され **getProducts()** に変更されました。
```java
// ver4.0
 purchase.getProducts().get(0)
```
PlayBillingLibrary4.0では一度に複数個のアイテムを購入ができるため **getProducts()** では戻り値がArrayList<String>となっていますが、単一の課金アイテムを取得したい場合は **getProduct().get(0)** などで取得できます。

## Androidアプリの課金処理の流れ
1.アプリからGoogle Playに接続
2.ユーザーが課金アイテム購入しGoogle Playに支払い
3.Google Playからのレシートを元にレシート検証
4.レシート検証結果を元にGoogle Playに承認リクエスト
5.アプリで購入成功・失敗

この記事がとても参考になったので処理の流れを知りたい方はこの記事を見てみてください。

https://speakerdeck.com/syarihu/re-zero-starting-uses-of-play-billing-library

### 課金承諾について
PlayBillingLibraryでは3日以内に課金承諾を行わないとユーザーに返金されます。
上の手順の4番ですね。

レシート検証はアプリ側で行うこともできますがサーバーサイドで行うのが一般的です。

# 実装方法
ここから実際に実装していきます。
間違ってたらコメントください。優しめで。

## PlayBillingLibraryを3.0から4.0に変更
```diff:build.gradle
- implementation "com.android.billingclient:billing:3.0.0"
+ implementation "com.android.billingclient:billing:4.0.0"
```

## アプリからGoogle Playに接続。

### Billing Clientインスタンスを生成。

```java:PurchaseActivity.java
public void onCreate() {
    private BillingClient billingClient = BillingClient.newBuilder(context)
        .setListener(purchasesUpdatedListener)
        .enablePendingPurchases()
        .build();
}
```

### BillingClientインスタンスをGooglePlayに接続。
Google Play接続前に購入処理を行っても購入できないので購入画面のActivityなどの早めのライフサイクル関数onCreate等で行うのが一般的。

```java:PurchaseActivity.java
billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                // billingResultを元に接続が成功or失敗の処理
            }

            @Override
            public void onBillingServiceDisconnected() {
                // GooglePlayとの接続切断時処理
            }
        });
```

## 購入可能なアイテム表示
課金アイテムのProductIDはGooglePlayConsoleにて確認してください。

```java:PurchaseActivity.java
QueryProductDetailsParams queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("課金アイテムのProductID")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build();
```

## 購入後のハンドリング
ユーザーが購入またはキャンセルを行うと、GooglePlayから **onPurchasesUpdated()** がコールされます。

**onPurchasesUpdated()** 内でレスポンスコードを処理します。

```java:PurchaseActivity.java
@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // ユーザーが購入キャンセルした場合のハンドリング
    } else {
        // その他のエラーハンドリング
    }
}
```

## 最後に
今回はPlayBillingLibraryのバージョンを上げてアプリ内課金を実装してみました。

一年に一回新バージョンがリリースされるのでその度変更を加えるのは大変ですね。。

ではまたっ！！