ドキュメント

Appendix

モバイルアプリのアプリ内課金 実装・運用設計ドキュメント の「Appendix」をまとめたページです。

Appendix

Appendix A. 一次情報一覧

Apple

Google

Appendix B. モノレポ構成時のディレクトリ構成のヒント

本付録は、既にモノレポで運用されているプロジェクトに、NestJS の API アプリNestJS の batch アプリを追加・共存させる場合の構成例を示すものです。
本編の必須要件ではなく、既存構成を大きく崩さずに導入するための実装ヒントとして扱います。

B-1. 前提

ここでは、次のような既存構成を前提にします。

bff/
  api/
    prisma/
    src/
      common/
      infrastructure/
      integration/
      shared/
      modules/
      utils/
    test/
  xxxx/
docs/
mobile/

このような構成では、Nest CLI の標準 monorepo mode に無理に寄せるより、既存のリポジトリ構成を維持したまま bff/apibff/batch を並列に持つほうが現実的です。

B-2. 推奨する考え方

  • bff/apiAPI 用 NestJS project root とする
  • bff/batchbatch 用 NestJS project root とする
  • bff/BFF workspace root として扱う
  • API と batch の両方で使うコードは bff/libs/ に段階的に切り出す
  • 最初から大規模なディレクトリ移動は行わず、共有が必要になった箇所だけ整理する

このドキュメントの文脈では、同一コードベースに統合しつつ、API と batch は別プロセス・別デプロイ単位で実行する構成を推奨します。

B-3. 推奨ディレクトリ構成

最もわかりやすい形は、bff/ の下に apibatch を並べ、必要に応じて libs を追加する構成です。

bff/
  package.json
  tsconfig.base.json

  api/
    package.json
    nest-cli.json
    prisma/
    src/
      common/
      infrastructure/
      integration/
      shared/
      modules/
      utils/
      app.module.ts
      main.ts
    test/

  batch/
    package.json
    nest-cli.json
    src/
      common/
      infrastructure/
      integration/
      shared/
      jobs/
      commands/
      utils/
      app.module.ts
      main.ts
    test/

  libs/
    billing/
      src/
        domain/
        application/
        infrastructure/
        query/
    platform/
      src/
        config/
        logging/
        time/

B-4. 役割の分け方

bff/api

bff/api には、HTTP で受ける責務を置きます。

  • 購入情報反映 API
  • Apple 通知受信 API
  • Google 通知受信 API
  • 利用可否取得 API
  • 契約詳細取得 API

ここには、controller、guard、interceptor、request / response の整形など、HTTP サーバとしての責務を置きます。

bff/batch

bff/batch には、定期実行や手動実行の責務を置きます。

  • 失敗イベント再処理ジョブ
  • 通知欠落補正ジョブ
  • Google acknowledgement 救済ジョブ
  • 期限近辺の再照合ジョブ
  • バックフィルコマンド
  • 通知再生コマンド

bff/batch は HTTP サーバである必要はありません。
HTTP listener を持たない NestJS の standalone application として起動しても構いません。

bff/libs

bff/libs には、API と batch の両方で使うコードを置きます。

  • 課金ドメインの状態判定
  • 再照合 use case
  • projection
  • repository interface
  • Prisma repository 実装
  • Apple / Google 連携 client
  • lock / queue / config などの共通基盤

B-5. package.json の置き方

この構成では、package.json を 3 つ置くと理解しやすくなります。

  • bff/package.json
  • bff/api/package.json
  • bff/batch/package.json

B-5-1. bff/package.json

bff/ 全体の workspace root として使います。

{
  "name": "@your-org/bff-workspace",
  "private": true,
  "workspaces": [
    "api",
    "batch"
  ],
  "scripts": {
    "build": "npm run build -w api && npm run build -w batch",
    "build:api": "npm run build -w api",
    "build:batch": "npm run build -w batch",
    "start:dev:api": "npm run start:dev -w api",
    "start:dev:batch": "npm run start:dev -w batch",
    "lint": "npm run lint -w api && npm run lint -w batch",
    "test": "npm run test -w api && npm run test -w batch",
    "prisma:generate": "npm run prisma:generate -w api",
    "prisma:migrate:dev": "npm run prisma:migrate:dev -w api"
  },
  "devDependencies": {
    "@nestjs/cli": "^11.0.0",
    "typescript": "^5.7.0"
  }
}

B-5-2. bff/api/package.json

bff/api の project root として使います。

{
  "name": "@your-org/bff-api",
  "private": true,
  "scripts": {
    "build": "nest build",
    "start": "node dist/main",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
    "test": "jest",
    "test:e2e": "jest --config ./test/jest-e2e.json",
    "prisma:generate": "prisma generate",
    "prisma:migrate:dev": "prisma migrate dev"
  },
  "dependencies": {
    "@nestjs/common": "^11.0.0",
    "@nestjs/core": "^11.0.0",
    "@nestjs/platform-express": "^11.0.0",
    "@nestjs/config": "^4.0.0",
    "@prisma/client": "^6.0.0",
    "reflect-metadata": "^0.2.2",
    "rxjs": "^7.8.1"
  },
  "devDependencies": {
    "@nestjs/testing": "^11.0.0",
    "@types/jest": "^29.5.12",
    "@types/node": "^22.10.1",
    "eslint": "^9.0.0",
    "jest": "^29.7.0",
    "prisma": "^6.0.0",
    "ts-jest": "^29.2.5",
    "ts-node": "^10.9.2",
    "typescript": "^5.7.0"
  }
}

B-5-3. bff/batch/package.json

bff/batch の project root として使います。

{
  "name": "@your-org/bff-batch",
  "private": true,
  "scripts": {
    "build": "nest build",
    "start": "node dist/main",
    "start:dev": "nest start --watch",
    "start:job:reconcile": "node dist/commands/reconcile-subscriptions.command.js",
    "start:job:retry-failed-events": "node dist/commands/retry-failed-events.command.js",
    "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
    "test": "jest"
  },
  "dependencies": {
    "@nestjs/common": "^11.0.0",
    "@nestjs/core": "^11.0.0",
    "@nestjs/config": "^4.0.0",
    "@nestjs/schedule": "^5.0.0",
    "@prisma/client": "^6.0.0",
    "reflect-metadata": "^0.2.2",
    "rxjs": "^7.8.1"
  },
  "devDependencies": {
    "@nestjs/testing": "^11.0.0",
    "@types/jest": "^29.5.12",
    "@types/node": "^22.10.1",
    "eslint": "^9.0.0",
    "jest": "^29.7.0",
    "ts-jest": "^29.2.5",
    "ts-node": "^10.9.2",
    "typescript": "^5.7.0"
  }
}

B-6. git mv は必要か

結論として、最初から必須ではありません。

すぐに git mv しなくてよいケース

  • まず bff/batch を新設したい
  • batch 側のジョブをゼロから実装する
  • 共有したいコードがまだ少ない

この段階では、bff/api は大きく動かさず、bff/batch を追加するだけで始められます。

段階的に git mv したほうがよいケース

  • apibatch の両方で同じ Prisma repository を使いたい
  • apibatch の両方で同じ Apple / Google 連携 client を使いたい
  • 同じ projection や reconciliation use case を共有したい

この場合は、その部分だけを bff/libs に移し、git mv で履歴を保ちながら整理するとよいです。

B-7. 移行の進め方

最初の移行は、次の 2 段階に分けるのが安全です。

段階 1. bff/batch を追加する

  • bff/batch/src/main.ts
  • bff/batch/src/app.module.ts
  • bff/batch/src/jobs/
  • bff/batch/src/commands/

まずは batch 専用の入口だけを作ります。

段階 2. 共有コードを bff/libs に切り出す

次のようなコードが共有候補です。

  • api/src/integration/apple
  • api/src/integration/google
  • api/src/infrastructure/prisma
  • api/src/shared/billing
  • api/src/modules/billing のうち HTTP 依存を持たない service / use case

このとき、controller や guard まで一緒に移さないことが重要です。
共有するのは、あくまで API と batch の両方で使う ドメイン・ユースケース・基盤実装です。

B-8. 最小変更で始めるなら

初回は、次のような簡略構成でも十分です。

bff/
  api/
    prisma/
    src/
      common/
      infrastructure/
      integration/
      shared/
      modules/
      utils/
    test/

  batch/
    src/
      common/
      infrastructure/
      integration/
      shared/
      jobs/
      commands/
      utils/
    test/

この構成で運用しながら、共有したい部分が増えた段階で bff/libs を足す形でも問題ありません。

B-9. 補足

NestJS には monorepo mode がありますが、既に独自のモノレポ構成で運用している場合、Nest CLI の標準的な monorepo へ無理に寄せる必要はありません
この付録で示した構成は、既存のリポジトリ構成を尊重しつつ、API と batch を NestJS で共存させるための実務的な一例です。

B-10. 参考リンク

NestJS の公式ドキュメントとして、次を参照すると理解しやすいです。

特に今回の構成では、既存の独自モノレポに apibatch の 2 つの NestJS application を追加する考え方HTTP listener を持たない batch app の作り方cron / interval による定期ジョブの実装を確認しておくと役立ちます。

Appendix C. 実装に入る前のバックエンド設定メモ

C-1. この付録の位置づけ

本編 3 章では、App Store Connect / Play Console 側の準備を中心に説明しました。
一方、実装着手時には、バックエンド側で何を設定として持つか を先に整理しておくと手戻りを減らせます。
ここでは、実装依存が強すぎない範囲で、最低限そろえておきたい設定観点だけをまとめます。

C-2. 採用ライブラリの目安

用途代表例役割
Apple Server API / JWS 検証@apple/app-store-server-libraryApp Store Server API 呼び出しと signed data 検証の主経路
Google Play Developer APIgoogleapispurchases.subscriptionsv2.get や acknowledgement 呼び出し
Google 認証 / Pub/Sub push JWT 検証google-auth-libraryサービスアカウント認証、push JWT 検証
DTO バリデーションclass-validator / class-transformeringest / Webhook 入力の構造検証
定期ジョブ@nestjs/schedule救済ジョブや補正ジョブの土台

本ドキュメントは特定ライブラリの API リファレンスを目的にしないため、ここでは採用候補と責務の整理にとどめます。
重要なのは、Apple / Google の公式 SDK や公式クライアントを主経路にし、独自実装を増やしすぎないことです。

C-3. バックエンド設定はカテゴリで整理する

環境変数や設定値は、個別名を先に増やすより、次のカテゴリで整理すると運用しやすくなります。

C-3-1. Apple API 呼び出し用設定

  • issuerId
  • keyId
  • privateKey
  • bundleId
  • 実行環境の既定値

C-3-2. Apple signed data 検証用設定

  • Apple Root 証明書群
  • bundleId
  • 検証対象環境
  • 必要に応じて appAppleId
  • オンライン証明書確認の有無

C-3-3. Google Play API 用設定

  • packageName
  • サービスアカウント email / private key
  • OAuth scope
  • 必要に応じて token URI などの上書き設定

C-3-4. Google RTDN 受信認証用設定

  • Pub/Sub push audience
  • 許可する service account
  • 公開鍵取得先
  • 必要に応じた追加 bearer token

C-3-5. 共通運用設定

  • 既定 provider / environment
  • 管理 API の認証情報
  • DB 接続情報
  • ポート、ログレベル、queue 設定

このように責務で分けておくと、API 呼び出しに使う秘密鍵署名検証に使う材料 を混同しにくくなります。

C-4. Config レイヤーで吸収するとよいこと

設定読み出しは、単に process.env を各所で読むより、Config レイヤーで次を吸収すると実装が安定します。

  • 改行を含む秘密鍵の整形
  • boolean 文字列の安全なパース
  • CSV 形式の複数値パース
  • Apple Root 証明書のファイル入力 / Base64 入力の吸収
  • 必須設定の fail-fast 検証

ここで重要なのは helper 名ではなく、アプリ起動時に設定不備を早く検出できる構成にしておくことです。

C-5. DTO バリデーションで先に決めておくとよいこと

API 実装サンプルの DTO に加えて、次のルールを先に決めておくと迷いにくくなります。

  • actionpurchase / restore のみ受ける
  • Apple ingest では transactionId を必須にする
  • Google ingest では purchaseToken を最小必須とし、packageName / productId は任意補助項目として扱う
  • Webhook / RTDN では「署名検証前に業務処理へ進まない」
  • 管理 API では provider ごとに必要識別子を分岐検証する

DTO は入力検証の責務に留め、最終的な真正性確認は必ずストア再照会で行うという原則は崩さないようにします。