【Laravel】App Store Connect API からiOSアプリの売上レポートを取得する

# はじめに

自分で作ったiOSアプリのダウンロード数などが知りたい場合、以下の2つの方法で確認することができます。

1. App Store Connectにアクセスし、ブラウザ上で可視化された情報を確認する
1. App Store Connect API を叩き、データとして取得する

本記事では、iOSアプリの新規ダウンロード数を算出することを想定し、2番のApp Store Connect API経由でSales and Trendsレポートを取得する方法を紹介します。

# 準備

※ Laravelのプロジェクトが立ち上げ済みであることを前提に話を進めます

## JWT生成ライブラリのインストール
App Store Connect API の認証には、__JWT__(Json Web Token)が必要となります。
今回はLaravelでの実装のため、PHPでJWTを生成できるライブラリである`PHP-JWT`を使用します。(https://github.com/firebase/php-jwt)

上記リンクのREADMEにもあるように、composerでインストールしておきます。

```php
composer require firebase/php-jwt
```

## App Store Connect の認証情報の準備

API認証で必要となる情報は、以下の4つです。

- 秘密鍵
- key ID
- issuer ID
- ベンダー番号

順に説明します。

まず秘密鍵とkey IDです。
[こちら](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api)のドキュメントに従い、秘密鍵(p8形式)とkey IDを作成します。
※ アクセスを選択する箇所は、`Finance`を選択してください。

次に、issuer IDです。
issuer IDはApp Store Connectの「ユーザとアクセス」 > 「キー」に記載があります。

最後に、ベンダー番号です。
[こちら](https://help.apple.com/app-store-connect/#/dev3a16f3fe0)のドキュメントの「ベンダー番号の表示」に従って取得します。

# Sales and Trends レポートを取得

それでは実際にJWTを生成し、認証、レポート取得までのコードを記します。
HTTPクライアントにはGuzzleを使用します。

```php
use Firebase\JWT\JWT;

〜〜略〜〜

public function getReport(string $keyId, string $privateKey, string $issuerId, string $vendorId)
{
    // JWTを生成
    $jwt = $this->createJWT($keyId, $privateKey, $issuerId);

    // クエリパラメータ指定
    $params = [
        'filter[frequency]'     => "DAILY", // レポートの頻度
        'filter[reportDate]'    => "2021-11-01", // 取得したいレポートの日付
        'filter[reportSubType]' => "SUMMARY", // レポートサブタイプ
        'filter[reportType]'    => "SALES", // レポートタイプ
        'filter[vendorNumber]'  => $vendorId,
        'filter[version]'       => "1_0", // レポートのバージョン
    ];

    // ヘッダー指定
    $header = [
        'Authorization' => 'Bearer ' . $jwt,
        'Accept' => 'application/json, application/a-gzip',
    ];

    // Sales and Trends レポート取得APIのエンドポイント
    $endPointUrl = "https://api.appstoreconnect.apple.com/v1/salesReports";
    // クエリパラメータも含めたURLを生成
    $url = sprintf("%s?%s", $endPointUrl, http_build_query($params));

    // リクエスト
    $client = new GuzzleHttp\Client();
    $res = $client->request(
        'GET',
        $url,
        [
            'headers' => $header,
            'decode_content' => false,
        ]
    );

    return gzdecode($res->getBody()->getContents());
}

// JWT生成用関数
private function createJWT(string $keyId, string $privateKey, string $issuerId): string
{
    $payload = [
        'iss' => $issuerId,
        'iat' => 1528407600, // トークン作成時間(UNIXタイムスタンプ)
        'exp' => 1528408800, // トークン有効期限(UNIXタイムスタンプ)
        'aud' => "appstoreconnect-v1" // 固定値
    ];

    // JWTを生成、return
    return JWT::encode($payload, $privateKey, "ES256", $keyId);
}
```

JWT生成時の暗号化アルゴリズムは`ES256`を指定します。
その他、JWT生成に関するドキュメントは[こちら](https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests)。

変数はそれぞれ、先ほど取得した認証情報に対応します。
`$keyId` => key ID
`$privateKey` => 秘密鍵
`$issuerId` => issuer ID
`$vendorId` => ベンダー番号


指定できるクエリパラメータの詳細等は[こちら](https://developer.apple.com/documentation/appstoreconnectapi/download_sales_and_trends_reports)のドキュメントをご覧ください。

これらの変数を引数とし、上記の`getReport`関数を呼び出すことで、レポートを取得することができます。

## 注意点
このAPIの成功時のレスポンスは__gzip__形式のTSVファイルです。
そのため、ヘッダーに`application/a-gzip`を指定する必要があります。

また、現在のGuzzleの仕様でしょうか、レスポンスのgzipをdecodeしようとすると、
`cURL error 61: Unrecognized transfer encoding`
というエラーが出ます。
そのため、リクエストのオプションに

```php
'decode_content' => false,
```

を指定し、一旦decodeせずにレスポンスを受けます。
そして、PHPの標準関数の`gzdecode()`を使用してdecodeしています。

```php
gzdecode($res->getBody()->getContents());
```
これで最終的にTSV形式のレポートを取得することができました！

※ [売上概要レポートのサンプル](https://help.apple.com/app-store-connect/#/dev3d5e6f05e)

# 補足
## 新規ダウンロード数の抽出

新規ダウンロード数を算出したい場合どうするか、簡単に説明しておきます。

まずは、先ほど取得したTSV形式の文字列をLaravelのコレクションに変換します。(コレクションは何かと扱いやすいため)
詳細なコードは割愛しますが、方針としては以下の通りです。

1. PHPの__SplTempFileObject__クラスを用い、TSVをファイルオブジェクトとして扱う
1. TSVのヘッダーをキーとする連想配列として値を格納する
1. コレクションに変換する

次に、作成したコレクションを`each`等のメソッドで回して、条件に合うレコードを抽出します。
具体的には、`Product Type Identifier`キーが新規DLのプロダクトタイプ識別子(`1, 1F, 1T`)に合致するレコードの`Units`の数を足し合わせたものが、その日の新規ダウンロード数となります。

プロトタイプ識別子に関しては[こちら](https://help.apple.com/app-store-connect/#/dev63c6f4502)を参考にしてください。

# おわりに

App Store Connectからのレポート取得やJWT生成に関する記事(特にPHP, Laravel)が少なかったので、実装ついでに記事にしてみました。

参考にしていただけたら幸いです。

# 参考記事

- [App Store Connect APIから新規DL数を取得](https://tech.pepabo.com/2021/03/29/app-store-connect/)
- [App Store Connect APIからアプリのダウンロード数を取得する時にハマったポイントとその解決法](https://zenn.dev/tatsumi0000/articles/570395ae258dc0)