🍎

App Store Connect APIを使ってゲーム内課金アイテム一覧を取得する

2023/10/26に公開

概要

https://developer.apple.com/documentation/appstoreconnectapi

App Store Connect APIを使えばiOSアプリリリースの際の作業あれこれを自動化できます。そのなかで当然ストア情報も取れます。ストア情報が取れたら自動更新だったり、自動チェックだったり色々できそうなんじゃないの?ということで実装した知見を記載します。

https://developer.apple.com/documentation/appstoreconnectapi/app_store/in-app_purchase/managing_in-app_purchases

ゲーム内アイテム情報を取得するにはin-app purchasesのAPIを使うことになりますが、少しコツがいることがわかりました。具体的にいうと、「ゲーム内課金アイテム一覧を取得」→「各アイテムのIDを指定してアイテム価格を取得」と2段階にAPIを叩いています。

癖があるAPIであまり情報がなかったので、こちらにやり方やコードを記載しておこうと思います。

環境構築

実行環境はこちらです。Pythonまわりの実行環境立ち上げについては省略します。

  • Python v3.11
  • MacOS 12.6.3

実装手順

アクセスキーの発行

https://developer.apple.com/jp/help/app-store-connect/get-started/app-store-connect-api/

こちらのページを参考にAppStoreConnectからキーを発行します。ページ内にもありますが、キーは一度しかダウンロードできず、権限によっては破壊的な命令(アプリや課金アイテムを消したり増やしたり)もできてしまうので取り扱いはくれぐれも注意しましょう。

APIリクエスト用のトークンの生成

https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests

APIを叩くためにはキーを元にJWTトークンを生成する必要があります。生成方法については公式リンクにも記載されていますがややこしかったのでこちらにも記載しておきます。

事前にこちらのライブラリをpipにてインストールします。JWTやRequestはライブラリから操作したほうが楽です。

JWTトークンを生成して、APIアクセスのためのトークンを生成するコードです。ところどころ公式ドキュメントから補足コメントを記載しています。

# AppStoreConnectのアクセスキーを読み取る
CERTIFICATE_PATH=f"{SELF_PATH}/.authkey.p8"
def LoadCertificateFile():
    with open(CERTIFICATE_PATH, "r") as f:
        return f.read()

# Token仕様
# https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests
def GenerateToken():
    # Your issuer ID from the API Keys page in App Store Connect; for example, 57246542-96fe-1a63-e053-0824d011072a.
    issuerId = "YOUR ISSUE ID"
    
    # Your private key ID from App Store Connect; for example 2X9R4HXF34.
    keyId = "YOUR KEY ID"
    
    # The token’s expiration time in Unix epoch time. Tokens that expire more than 20 minutes into the future are not valid except for resources listed in Determine the Appropriate Token Lifetime.
    expirationInTenMinutes = int(time.time() + 600)
    
    # All JWTs for App Store Connect API must be signed with ES256 encryption.
    alg = "ES256"
    
    # payloadとヘッダのJsonを作成
    payload = {
        "iss": issuerId,
        "exp": expirationInTenMinutes,
        "aud": "appstoreconnect-v1"
    }
    headers = {
        "alg": alg,
        "kid": keyId,
        "typ": "JWT"
    }
    
    #  アクセスキーも合わせてJWTのエンコードを実施
    privateKey=LoadCertificateFile()
    token = jwt.encode(payload=payload, key=privateKey, algorithm=alg, headers=headers)
    return token

ゲーム内課金アイテム情報取得のAPIを叩く

https://developer.apple.com/documentation/appstoreconnectapi/app_store/in-app_purchase/in-app_purchases

in-app purchasesのAPI一覧はこのページから見れます。今回はそのなかのすべてのゲーム内課金情報を取得するAPIを叩きます。

def GetInappPurchases(token: str):
    id = "YOUR APP ID"
    r = requests.get(f"https://api.appstoreconnect.apple.com/v1/apps/{id}/inAppPurchasesV2?limit=200", headers={"Authorization": f"Bearer {token}"})
    
    if (r.status_code == 200):
        return json.loads(r.text)
    else:
        print(f"inAppPurchasesV2 api was failed. status_code: {r.status_code}")
        sys.exit()

ポイント

これでJson形式のゲーム内課金アイテム一覧が取得できます。

ゲーム内課金アイテム価格まで問い合わせる

https://developer.apple.com/forums/thread/711031
ここまでの手順で落とし穴があり、これだけだとアイテムの値段詳細までが取得できていません。AppleのForumにも同様の問い合わせるがあります。2023/10/26現在では一覧から各アイテムのIDを使って追加の問い合わせを実施する必要があります。

私はこんなコードで実行することにしました

# Note: inAppPurchasesV2 のAPIのみでは価格を取得できない
# inAppPurchasePriceSchedulesのAPI を追加で実施し、価格をjsonに追記する
def GetInappPurchasePrice(token: str, inappPurchasesJson: json):
    for i, item in enumerate(inappPurchasesJson["data"]):
        id = item["id"]
        r = requests.get(f"https://api.appstoreconnect.apple.com/v1/inAppPurchasePriceSchedules/{id}/manualPrices?include=inAppPurchasePricePoint&filter[territory]=JPN",headers={"Authorization": f"Bearer {token}"})

        if (r.status_code != 200):
            print(f"inAppPurchasePriceSchedules api was failed. status_code: {r.status_code}")
            sys.exit()
        
        itemJson = json.loads(r.text)
        customerPrice = itemJson["included"][0]["attributes"]["customerPrice"] # FIXME: もしアイテムに価格がない等の例外処理が存在するかも
        
        inappPurchasesJson["data"][i]["attributes"]["customerPrice"] = customerPrice
    return inappPurchasesJson

ポイント

  • inAppPurchasePriceSchedulesのAPIを叩きます
  • パラメータとしてinclude=inAppPurchasePricePoint&filter[territory]=JPNを指定して、日本円だけ取得するようにフィルタ、フィルタしないとJsonサイズが大きくなるので。
  • customerPriceの属性を取得して、もともとのjsonに追記するようにしています

まとめ

App Store Connect APIを使ってゲーム内課金アイテムを取得しました。こういった情報が取れれば色々な自動化が実現できそうです。(例. ゲーム内課金アイテムとマスターデータを突き合わせて価格チェックをする)
inAppPurchasesV2だけでは価格まで取れないので一工夫が必要です。これも公式ドキュメントには案内がないので今回ご紹介した内容を参考にすると良いと思います。

Discussion