ドキュメント
Appendix
モバイルアプリのアプリ内課金 実装・運用設計ドキュメント の「Appendix」をまとめたページです。
Appendix
Appendix A. 一次情報一覧
Apple
- Validating receipts with the App Store
- verifyReceipt
- App Store Server API
- Get Transaction Info
- Get All Subscription Statuses
- App Store Server Notifications
- Receiving App Store Server Notifications
- Responding to App Store Server Notifications
- Transaction
- Get Notification History
- Request a Test Notification
- Get Test Notification Status
- Testing In-App Purchases with sandbox
- Testing subscriptions and In-App Purchases in TestFlight
- Set up introductory offers for auto-renewable subscriptions
- isEligibleForIntroOffer
- displayPrice
- sync()
- Enable billing grace period for auto-renewable subscriptions
- Handling Subscriptions Billing
- appAccountToken
- appAccountToken(_:)
- Google Play’s billing system
- Getting ready
- Integrate the Google Play Billing Library into your app
- Subscription lifecycle
- About subscriptions
- Real-time developer notifications reference guide
- Voided Purchases API
- purchases.voidedpurchases.list
- Test your Google Play Billing Library integration
- purchases.subscriptionsv2.get
- purchases.subscriptions.acknowledge
- setObfuscatedAccountId
- setObfuscatedProfileId
- Test in-app billing with application licensing
- ProductDetails.PricingPhase
- Create and manage subscriptions
- Understand product types and catalog considerations
- Push subscriptions
- Pull subscriptions
- Choose a subscription type
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/api と bff/batch を並列に持つほうが現実的です。
B-2. 推奨する考え方
bff/apiを API 用 NestJS project root とするbff/batchを batch 用 NestJS project root とするbff/を BFF workspace root として扱う- API と batch の両方で使うコードは
bff/libs/に段階的に切り出す - 最初から大規模なディレクトリ移動は行わず、共有が必要になった箇所だけ整理する
このドキュメントの文脈では、同一コードベースに統合しつつ、API と batch は別プロセス・別デプロイ単位で実行する構成を推奨します。
B-3. 推奨ディレクトリ構成
最もわかりやすい形は、bff/ の下に api と batch を並べ、必要に応じて 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.jsonbff/api/package.jsonbff/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 したほうがよいケース
apiとbatchの両方で同じ Prisma repository を使いたいapiとbatchの両方で同じ Apple / Google 連携 client を使いたい- 同じ projection や reconciliation use case を共有したい
この場合は、その部分だけを bff/libs に移し、git mv で履歴を保ちながら整理するとよいです。
B-7. 移行の進め方
最初の移行は、次の 2 段階に分けるのが安全です。
段階 1. bff/batch を追加する
bff/batch/src/main.tsbff/batch/src/app.module.tsbff/batch/src/jobs/bff/batch/src/commands/
まずは batch 専用の入口だけを作ります。
段階 2. 共有コードを bff/libs に切り出す
次のようなコードが共有候補です。
api/src/integration/appleapi/src/integration/googleapi/src/infrastructure/prismaapi/src/shared/billingapi/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 の公式ドキュメントとして、次を参照すると理解しやすいです。
- NestJS CLI / Monorepo: https://docs.nestjs.com/cli/monorepo
- NestJS Standalone applications: https://docs.nestjs.com/standalone-applications
- NestJS Task scheduling: https://docs.nestjs.com/techniques/task-scheduling
特に今回の構成では、既存の独自モノレポに api と batch の 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-library | App Store Server API 呼び出しと signed data 検証の主経路 |
| Google Play Developer API | googleapis | purchases.subscriptionsv2.get や acknowledgement 呼び出し |
| Google 認証 / Pub/Sub push JWT 検証 | google-auth-library | サービスアカウント認証、push JWT 検証 |
| DTO バリデーション | class-validator / class-transformer | ingest / Webhook 入力の構造検証 |
| 定期ジョブ | @nestjs/schedule | 救済ジョブや補正ジョブの土台 |
本ドキュメントは特定ライブラリの API リファレンスを目的にしないため、ここでは採用候補と責務の整理にとどめます。
重要なのは、Apple / Google の公式 SDK や公式クライアントを主経路にし、独自実装を増やしすぎないことです。
C-3. バックエンド設定はカテゴリで整理する
環境変数や設定値は、個別名を先に増やすより、次のカテゴリで整理すると運用しやすくなります。
C-3-1. Apple API 呼び出し用設定
issuerIdkeyIdprivateKeybundleId- 実行環境の既定値
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 に加えて、次のルールを先に決めておくと迷いにくくなります。
actionはpurchase/restoreのみ受ける- Apple ingest では
transactionIdを必須にする - Google ingest では
purchaseTokenを最小必須とし、packageName/productIdは任意補助項目として扱う - Webhook / RTDN では「署名検証前に業務処理へ進まない」
- 管理 API では
providerごとに必要識別子を分岐検証する
DTO は入力検証の責務に留め、最終的な真正性確認は必ずストア再照会で行うという原則は崩さないようにします。