Skip to content

feat(mobile): スマホアプリのネイティブ化 — Capacitor 8 + 買い切りIAP実配線 + iOS/Android CI#9

Open
1llum1n4t1s wants to merge 4 commits into
mainfrom
feature/mobile-native
Open

feat(mobile): スマホアプリのネイティブ化 — Capacitor 8 + 買い切りIAP実配線 + iOS/Android CI#9
1llum1n4t1s wants to merge 4 commits into
mainfrom
feature/mobile-native

Conversation

@1llum1n4t1s

Copy link
Copy Markdown
Owner

概要

スマホアプリ(mobile/)のネイティブ化を前進。Capacitor 6→8 アップグレード、買い切り IAP の実配線、iOS/Android ビルド CI 整備。実機ビルド・署名・ストア申請(ゆろ君の環境依存)以外を完了。

変更

Capacitor 8 アップグレード(chore)

  • @capacitor/* を 8.4.1 系へ。保守される IAP プラグインが Cap8 必須@Capgo も RevenueCat も peer >=8.0.0)かつ mobile はまだネイティブ未ビルド=アップグレードの好機。
  • Android: minSdk24 / compileSdk36 / Gradle8.14.3 / JDK21、iOS: target15 / Xcode26+ / SPM

買い切り IAP 実配線(src/iap.js

  • @capgo/native-purchases(StoreKit2 / Google Play Billing 直叩き・外部 SaaS 不要=サーバーレス志向に合致)。
  • 所有判定: iOS=currentEntitlements(別 Apple ID 漏洩防止)/ Android=purchaseState"1"&acknowledged。ストア照会結果を Preferences にキャッシュ(オフライン即時表示)。
  • web(dev) は従来の localStorage 解錠。本番は import.meta.env.DEV で dead code 化=無課金バイパス防止。
  • onBuy を未解錠時に「解禁」と誤表示しないよう堅牢化。

iOS カメラ(調査で方針確定)

  • 「iOS 15.5+ で getUserMedia 不可」という当初の調査主張を敵対的に再検証→誤りと判明。実際は iOS 14.3+ の WKWebView で getUserMedia 動作(NSCameraUsageDescription + allowsInlineMediaPlayback で)。barcode-scanner 差し替えは不要
  • CI で Info.plist に NSCameraUsageDescription を注入(Android の CAMERA 注入と対称)。

CI

  • mobile-android.yml: JDK17→21(Cap8 が JavaVersion.VERSION_21)、AndroidManifest へ CAMERA + BILLING 注入。
  • mobile-ios.yml(新規): macOS・SPM。cap add ios→Info.plist 注入→署名なし simulator コンパイル検証。

検証

  • _sync_repro 223 / _mobile_crud_repro 15 / _mobile_sync_repro 9(実 relay e2e・暗号化往復)すべて緑。
  • vite build(Cap8)緑、cap add android(Cap8・minSdk24/compileSdk36/@Capgo 検出)緑。

残(ゆろ君の環境/手作業)

  • ストア product 登録: jp.nephilim.petarin.sync(non-consumable・¥500)を App Store Connect / Play Console に登録。
  • ネイティブ実機の目視確認(付箋 CRUD・ペアリング・カメラ QR・購入フロー)。
  • iOS 署名証明書(App Store Connect API key / provisioning)を Secrets 投入して署名ビルド/ストア申請。
  • Android release 署名(keystore)、課金 enforcement 強化(relay 側)。

🤖 Generated with Claude Code

1llum1n4t1s and others added 2 commits June 23, 2026 20:45
- Capacitor 6→8 アップグレード(@capacitor/* 8.4.1・app8.1/preferences8.0)。保守される
  IAP プラグインが Cap8 必須のため。mobile はネイティブ未ビルド=アップグレードの好機。
- iap.js: @capgo/native-purchases(StoreKit2/Play Billing 直叩き・外部SaaS不要)で
  initIap/purchase/restore を実配線。所有判定は iOS=currentEntitlements・Android=
  purchaseState"1"&acknowledged。ストア照会結果を Preferences にキャッシュ(オフライン即時表示)。
  web(dev)は従来の localStorage 解錠(本番は import.meta.env.DEV で dead code=バイパス防止)。
- main.js: onBuy を未解錠時に「解禁」と誤表示しないよう堅牢化。QRカメラのコメントを
  「iOS 14.3+ で getUserMedia 動作・barcode-scanner 差し替え不要」と確認した内容へ更新。
- README: Cap8/IAP配線/CI を実態に更新。

回帰: _sync_repro 223 / _mobile_crud_repro 15 / _mobile_sync_repro 9(実relay e2e)緑。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- mobile-android.yml: Cap8 テンプレが JavaVersion.VERSION_21 を使うため JDK 17→21。
  AndroidManifest へ CAMERA(QRスキャナ)に加え BILLING(@Capgo課金)も注入。
- mobile-ios.yml(新規): macOS・SPM。cap add ios→Info.plist へ NSCameraUsageDescription
  注入→署名なし simulator コンパイル検証。実機.ipa/署名/申請は証明書投入後。
  Cap8 は Xcode26+ 要求=runner の Xcode 次第。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 47311c0e-e8b4-475f-b9a7-9e71d6a811d7

📥 Commits

Reviewing files that changed from the base of the PR and between 48b6973 and ab0010b.

📒 Files selected for processing (2)
  • .github/workflows/mobile-android.yml
  • .github/workflows/mobile-ios.yml
✅ Files skipped from review due to trivial changes (1)
  • .github/workflows/mobile-ios.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/mobile-android.yml

📝 Walkthrough

Summary by CodeRabbit

リリースノート

  • 新機能
    • iOS向けビルド検証ワークフローを追加しました。
  • 改善
    • アプリ内課金の解錠判定をネイティブのストア情報ベースへ刷新しました。
    • 購入確認が取れない場合などの表示を改善しました。
  • Chores
    • Capacitor/CLIや課金連携の依存関係を更新し、Androidのビルド要件(Java 21)と必要権限注入(CAMERA/BILLING)を調整しました。
    • pnpmのビルド許可設定を最適化しました。
  • ドキュメント
    • モバイルREADMEの課金・ビルド手順を最新化しました。

ウォークスルー

Capacitor 6から8へ依存を更新し、@capgo/native-purchasesを追加してネイティブIAPを実装。ネイティブ環境ではStoreKit2(iOS)とPlay Billing(Android)経由でストア照会・購入・復元を行い、キャッシュはPreferencesで保持。Android CIはJava 21化とBILLING権限注入に対応し、iOS CI新規ワークフローを追加した。pnpmワークスペース設定で許可ビルド対象を厳格化。

変更内容

Capacitor 8アップグレード&ネイティブIAP&CI/CD対応

レイヤー / ファイル 概要
Capacitor 8依存更新&allowBuilds設定
mobile/package.json, mobile/pnpm-workspace.yaml
Capacitorパッケージ群を^6.xから^8.xへ更新し、@capgo/native-purchasesを追加。@capacitor/cli^8.4.1へ更新。pnpm-workspace.yamlにallowBuilds設定を追加し、esbuildのみビルド許可に制限。
ネイティブIAP: queryOwned / initIap / purchase / restore実装
mobile/src/iap.js
Capacitor.isNativePlatform()でネイティブ判定し、@capgo/native-purchasesを遅延ロード。getPurchasesによるストア照会・所有判定(queryOwned)、Preferencesキャッシュ優先のinitIappurchaseProduct呼び出し+所有確定のpurchaserestorePurchases後にinitIapへ委譲するrestoreを実装。web側はIS_DEV時のみdev解錠フラグで動作。
onBuy購入フロー戻り値チェック&コメント更新
mobile/src/main.js
purchase()戻り値okがfalseの場合に「購入が確認できませんでした。」を表示して早期リターン。catch文言を更新。QRカメラスキャンのコメントをiOS WKWebView対応条件・Info.plist・Android権限へ差し替え。
Android CI: Java 21化&BILLING権限注入
.github/workflows/mobile-android.yml
setup-javaをTemurin Java 21へ更新。pnpm installをワークスペース前提の単純形式へ変更。AndroidManifest権限注入をCAMERA単体からandroid.permission.CAMERAcom.android.vending.BILLINGの両権限注入へ拡張。
iOS CI新規ワークフロー追加
.github/workflows/mobile-ios.yml
mobile-v*タグと手動実行で起動するiOS CIを新規追加。macos-15上でNode.js 22セットアップ、pnpm install --frozen-lockfile→viteビルド→Capacitor ios追加・同期→Info.plistへNSCameraUsageDescription冪等注入→署名なしiOS Simulatorコンパイル検証を実行。
READMEドキュメント更新
mobile/README.md
src/iap.jsの説明をネイティブ課金実装の具体内容へ更新。残TODOをCapacitor 8環境詳細(JDK21/Gradle 8.14.3、権限注入、Xcode 26+/SPM)、ストア製品登録、実機目視確認、iOS署名申請、課金enforcement強化へ再構成。

シーケンス図

sequenceDiagram
  participant UI as main.js (onBuy)
  participant IAP as iap.js
  participant Plugin as `@capgo/native-purchases`
  participant Prefs as Capacitor Preferences

  UI->>IAP: initIap()
  IAP->>Prefs: キャッシュ読み込み → _unlocked即時反映
  IAP->>Plugin: getPurchases({onlyCurrentEntitlements: true})
  Plugin-->>IAP: purchases[]
  IAP->>IAP: queryOwned() で PRODUCT_ID 所有判定
  IAP->>Prefs: 解錠キャッシュ書き込み

  UI->>IAP: purchase()
  IAP->>Plugin: purchaseProduct(PRODUCT_ID)
  Plugin-->>IAP: result
  IAP->>IAP: 成立確認、不確実時はqueryOwned()で再確定
  IAP->>Prefs: 解錠キャッシュ書き込み
  IAP-->>UI: ok: true / false

  alt ok === false
    UI->>UI: 「購入が確認できませんでした。」表示して早期リターン
  else ok === true
    UI->>UI: renderPairing() + 解禁メッセージ表示
  end
Loading

関連する可能性のあるPR

  • 1llum1n4t1s/Petarin#8: 本PRがAndroidManifest権限注入ロジック(android.permission.CAMERAsed挿入)をCAMERA + BILLINGの両権限へ拡張しており、retrieved PRのCAMERA注入ステップと直接コードレベルで連続する。
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR タイトルは「スマホアプリのネイティブ化 — Capacitor 8 + 買い切りIAP実配線 + iOS/Android CI」であり、実際のChangeset(Capacitor 6→8 アップグレード、IAP 実配線、iOS/Android CI 構築)と完全に一致している。
Description check ✅ Passed PR説明は、Capacitor8 アップグレード、買い切り IAP 実配線(@capgo/native-purchases)、iOS カメラ検証、CI 構築など、実際の変更内容を詳細かつ正確に記述しており、Changeset との関連性が高い。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/mobile-native

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request upgrades Capacitor to version 8 and implements native In-App Purchases (IAP) for a non-consumable product using @capgo/native-purchases, complete with offline caching via Capacitor Preferences. The review feedback highlights three critical issues in the IAP implementation: a potential type mismatch when validating the Android purchase state (comparing numeric 1 with string "1"), a security risk where pending or deferred transactions (such as "Ask to Buy") could bypass validation, and an issue where temporary billing support check failures would overwrite the offline cache and lock out valid users.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread mobile/src/iap.js Outdated
Comment thread mobile/src/iap.js Outdated
Comment thread mobile/src/iap.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/mobile-ios.yml:
- Line 20: The runs-on field in the mobile-ios.yml workflow uses macos-latest
which can change Xcode versions unpredictably when GitHub updates the runner
image, making it impossible to guarantee the Xcode 26+ requirement mentioned
elsewhere in the workflow. Replace the runs-on: macos-latest value with a
specific macOS version runner (such as macos-14 or macos-13) that consistently
provides the required Xcode 26+ version, or alternatively add an explicit Xcode
version pinning step in the workflow to ensure reproducibility and meet the
stated Xcode requirements.
- Line 38: Replace the `dangerouslyAllowAllBuilds=true` configuration in the
pnpm install command (line 38 of mobile-ios.yml) with the
`onlyBuiltDependencies` based allowlist approach that is already implemented in
the pnpm-workspace.yaml configuration file. Apply the same pattern fix to
mobile-android.yml to ensure both mobile workflows use the consistent, more
secure dependency build script execution policy instead of allowing all builds.

In `@mobile/src/iap.js`:
- Around line 113-119: The purchase success determination on line 115 uses a
ternary operator that bypasses the strict ownership verification in queryOwned()
when the tx exists and productIdentifier matches PRODUCT_ID. Instead of the
conditional ok ? true : await queryOwned() logic, always call queryOwned() to
ensure the final ownership confirmation is performed regardless of the initial
productIdentifier check. This ensures that strict verification conditions (such
as PENDING status and acknowledgement completion on Android) are always
validated before setting _unlocked to true and saving to the cache.
- Around line 61-64: The Android ownership check in the Capacitor platform
branch uses a loose validation for the isAcknowledged property that allows
undefined values to pass through, violating Google Play Billing requirements. In
the condition that checks both purchaseState and isAcknowledged, change the
`p.isAcknowledged !== false` check to strictly require the acknowledged status
by checking for `p.isAcknowledged === true` instead. This ensures that only
explicitly acknowledged purchases are considered valid, preventing
unacknowledged purchases from being treated as owned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bdf24111-915d-4fa1-a129-141583f9550c

📥 Commits

Reviewing files that changed from the base of the PR and between 6987a26 and d5a17b9.

⛔ Files ignored due to path filters (1)
  • mobile/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • .github/workflows/mobile-android.yml
  • .github/workflows/mobile-ios.yml
  • mobile/README.md
  • mobile/package.json
  • mobile/src/iap.js
  • mobile/src/main.js

Comment thread .github/workflows/mobile-ios.yml Outdated
Comment thread .github/workflows/mobile-ios.yml Outdated
Comment thread mobile/src/iap.js
Comment thread mobile/src/iap.js Outdated
iap.js(課金):
- Android 所有判定: purchaseState は数値 1 / 文字列 "1" / "PURCHASED" を許容、isAcknowledged は厳密 true を
  要求(!==false は undefined を通し、3日で自動払戻しされる未承認購入を所有扱いにする穴)。
- purchase: productIdentifier 一致では解錠せず必ず queryOwned() で確定(iOS Ask to Buy 等の deferred を
  未承認のまま解禁しない)。
- isBillingSupported の throw を握り潰さず伝播=initIap が catch して Preferences キャッシュを維持
  (一時障害/オフラインで購入者をロックアウトしない)。

CI:
- mobile-ios.yml: runs-on を macos-latest→macos-15 固定(イメージ更新による Xcode 非決定性を排除)。
- 両 mobile CI: --config.dangerouslyAllowAllBuilds=true を廃し mobile/pnpm-workspace.yaml の
  allowBuilds(esbuild のみ)で許可。--ignore-workspace も外す(付けると allowBuilds が読まれず ignored builds)。

検証: vite build / _sync_repro 223 / _mobile_crud_repro 15 緑。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
.github/workflows/mobile-android.yml (1)

45-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

pnpm install--frozen-lockfile を付けて再現性を固定してください。

Line 46 はロックファイル差分をCIで見逃す可能性があるため、依存解決の非決定性を避ける設定にした方が安全です。

差分案
       - name: Install deps
-        run: pnpm install
+        run: pnpm install --frozen-lockfile
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/mobile-android.yml around lines 45 - 46, In the Install
deps step where pnpm install is executed, add the --frozen-lockfile flag to the
run command to ensure dependency resolution is deterministic and prevent
lockfile drift in CI. Update the pnpm install command to include
--frozen-lockfile as an argument to guarantee reproducible builds and catch any
unintended lockfile changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/mobile-android.yml:
- Around line 45-46: In the Install deps step where pnpm install is executed,
add the --frozen-lockfile flag to the run command to ensure dependency
resolution is deterministic and prevent lockfile drift in CI. Update the pnpm
install command to include --frozen-lockfile as an argument to guarantee
reproducible builds and catch any unintended lockfile changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60dc6477-835f-44ea-8663-4298cff0ea6f

📥 Commits

Reviewing files that changed from the base of the PR and between d5a17b9 and 48b6973.

📒 Files selected for processing (4)
  • .github/workflows/mobile-android.yml
  • .github/workflows/mobile-ios.yml
  • mobile/pnpm-workspace.yaml
  • mobile/src/iap.js
✅ Files skipped from review due to trivial changes (1)
  • mobile/pnpm-workspace.yaml
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/mobile-ios.yml
  • mobile/src/iap.js

…性固定)

CodeRabbit nitpick 対応。両 mobile CI の install を frozen-lockfile 化=lockfile と
package.json の不整合を CI で検出し、依存解決を再現可能にする。ローカルで通過確認済み。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant