2
3

More than 1 year has passed since last update.

iOSでアプリ内課金を実装する(サーバーサイド)

Last updated at Posted at 2022-07-23

はじめに

個人的な備忘として、iOSアプリでアプリ内課金を実装する際のHowToやノウハウを残しておく。

動いていることは確認しているが、あまり洗練されている気がしない。。。
App Store Server Notifications を利用すれば、多分、もう少し良い具合に実装できそう。
ベストプラクティスだとは思わないで頂けると。。。

処理

以下、時系列に従って実装の概要・勘所を説明する。

1. アプリ内課金を実施する

割愛(サーバーから商品情報取得したり、App Storeで決済したり)

2. アプリでレシートを取得する

アプリ側の実装として、以下のコードを参考にApp Storeからレシートを取得する。
取得したレシートは、サーバーへ送信し、検証・商品提供を行う。

// エラーハンドリングは割愛
let url = Bundle.main.appStoreReceiptURL
let data = Data(contentsOf: url, options: .alwaysMapped)
let receipt = data.base64EncodedString()

3. サーバーでレシート検証を行う

App Store verifyReceipt を実行して、処理項番2で取得したレシートの検証を行う。

参考: Validating Receipts with the App Store | Apple Developer Documentation

以下、URLにPOSTすると、検証結果、及びトランザクション情報が返却される。

環境 URL
Production https://buy.itunes.apple.com/verifyReceipt
Sandbox https://sandbox.itunes.apple.com/verifyReceipt

パラメーター

キー データ 備考
receipt-data 処理項番2で取得したレシート -
password iTunes Connectのキー サブスクのみ
exclude-old-transactions true
(最新のトランザクション情報のみ取得)
サブスクのみ

レスポンス

公式ドキュメント参照。

Appleレビュアー対策

Appleレビュアーは、Sandbox環境のレシートを送信してくる。
Productionに対して、Sandboxレシートを送信するとstatus21007 (環境不整合)になり、処理できない。

課金処理が正常終了しないと、不具合ありとしてレビュー通過できないため、考慮が必要。

考えられる解決策は、以下3つ。

1. User-Agentを利用したバージョンチェック

User-Agentのアプリバージョンが公開前バージョンであれば、Appleレビュアーの検証によるものとみなし、SandboxでverifyReceiptを行う。

  • メリット
    • 直感的な実装になる(と思う)
  • デメリット
    • 開発側で公開前バージョンアプリを利用した、決済テストができない
    • アプリバージョンの管理が必要

2. verifyReceiptの実行結果に応じてハンドリング

一旦、ProductionでverifyReceiptを行う。status = 21007だった場合はSandboxで再実行する。

  • メリット
    • 案1のデメリットが解消できる
    • 比較的、少ない手間で対応可能
  • デメリット
    • 実装がスッキリしない
    • Appleレビュアー以外からの不正なリクエストだった場合、検出しにくい

3. テストユーザーとして管理する

Appleへアプリ審査を依頼する際に、テストユーザーの認証情報を指定することができる。
サービスの本番環境向けテストユーザー管理の仕組みに組み込む。

  • メリット
    • 案1, 2のデメリットを解消できる
    • テスト容易性が良い
    • あるべき論的にも良いかも
  • デメリット
    • 途中から導入すると、かなり手間がかかる

4. トランザクション検証を行う

処理項番3で取得したトランザクション情報を検証し、商品の提供可否を判定する。

サブスクリプションの場合

verifyReceiptの結果(以降verifyReceiptとする)のlatest_receipt_info[]にて評価する。

以下、verifyReceipt.latest_receipt_info[n]の主な評価ポイント。

キー 概要 チェック方法
product_id 商品ID サービス側で定義している
商品IDと一致するか、チェックする
original_transaction_id 購入処理トランザクションID
新たに課金する都度、付与されるID
既に管理されている(DBに存在する)IDの場合、
新規購入ではない可能性がある
transaction_id トランザクションID
新規・継続など支払い都度付与されるID
既に管理されている(DBに存在する)IDの場合、
商品提供が済んでいる可能性がある
expires_date 期限日時 期限切れかチェックする
cancellation_date キャンセル日時 有効な日時が設定されている場合
キャンセルされている

latest_receipt_infoの取り扱い
新しい順・古い順が変わることがあるらしい。順不同と思って扱うべき。
また、同一商品の通常価格品・セール品が有効になってるとか、不整合アラート目的で全てのレコードをチェックすると安心。

ポイントなど消費系商品の場合

verifyReceiptのreceipt.in_app[]にて評価する。

以下、verifyReceipt.receipt.in_app[n]の主な評価ポイント。

キー 概要 チェック方法
product_id 商品ID サービス側で定義している
商品IDと一致するか、チェックする
transaction_id トランザクションID
支払い都度付与されるID
既に管理されている(DBに存在する)IDの場合、
商品提供が済んでいる可能性がある
cancellation_date キャンセル日時 有効な日時が設定されている場合
キャンセルされている

5. レシート情報を保管

後述のサブスク継続処理用にレシート情報を保管する。

verifyReceipt.latest_receiptをDBなどに保管しておき、後の継続処理で利用する。

6. 商品提供を行う

サービスの要件に応じた商品提供を行う。

7. サブスクを継続する

サブスク期限の到来時に、処理項番5で保管しておいたレシート情報に基づいて、以下を実行する。

ノウハウ

課金の復元

  • 復元機能を用意する
    ユーザーが任意のタイミングで、課金状態を反映する機能を用意しないと、Appleアプリ審査通過できない。
    前述の流れに従って、ユーザーに提供できていない商品をチェック・提供する機能が必要。

商品提供関連

  • 事前チェック
    ユーザーの状態(性別や年齢層など)に応じて、提供商品が異なる場合、早めにチェックする。
    6. 商品提供を行う でエラーを返しても、決済が取り消されることはない。
    返金対応などの業務が発生するため、アプリ側で課金する前に防ぐ。

  • 返金の考慮
    iOSアプリでの課金はユーザーが直接Appleに対して返金をリクエストできる。
    サービスとApple側でユーザーの課金状態にズレが生じる可能性があるため、考慮が必要。
    例えば、以下の方法で対応する。
     ・アプリ起動時に必ず課金状態をチェックするようにして、同期を取る
     ・バッチを用意して全Apple決済サブスクのチェックを定期的に行う
     ・App Store Server Notifications を利用する?

  • 決済保留の考慮
    カード有効期限切れなどが原因で、Apple側で決済が保留になる可能性がある。
    ユーザーが決済情報を更新した場合、引き続きサブスクが有効になるため、一定期間監視が必要。
    支払いが認められた場合、トランザクション情報が更新されるため、商品提供を行う必要がある。

  • 商品の多重提供対策
    商品提供を行う前に、トランザクションIDを利用した提供済みチェックは慎重に行う。
    同一AppleIDを決済アカウントとして、複数のアカウント登録しているユーザーがいる可能性がある。
    ユーザーID×トランザクションIDで当該チェックを実施すると、同一決済の商品を多重に提供しかねない。
    トランザクションIDで一意とする方が良いと思う。

Sandbox関連

  • Sandboxのサブスク期限
    Sandboxのサクブス期限は5〜60分程度と短い。
    サービス側でサブスク期限を独自に定義している場合(※)は、検証方法の考慮が必要。
    ※トランザクション情報の期限の当日23:59までなど

  • 売上計上関連
    前述の通り、AppleレビュアーはSandboxレシートを送信してくる。
    課金処理で売上関連テーブルに登録している場合は、注意が必要。(売り上げとして計上すべきでない)

本番検証関連

  • 本番検証用のカード
    本番検証を行う際に、AppleIDに法人カードを紐付けると課金できない可能性がある。
    iTunesカード(Apple Gift Card)などを利用するか、個人のカードを利用して後で請求するとか、考慮が必要。
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3