Skip to content

fix(google): fall back to token for blank horizon order ids - #278

Merged
hyochan merged 1 commit into
mainfrom
fix/horizon-blank-order-id
Aug 3, 2026
Merged

hyochan merged 1 commit into
mainfrom
fix/horizon-blank-order-id

Conversation

@hyochan

@hyochan hyochan commented Aug 3, 2026 •

Copy link
Copy Markdown
Member

Found by running the Horizon device row that #276 reported as blocked — and it
is exactly the class of bug #276's strict decoders exist to catch.

The bug

The Horizon billing-compatibility Purchase.orderId is a non-null String
(confirmed from the class file) and arrives blank on device, so the
converter's null-only orderId ?: token fallback never fired. Every Horizon
purchase reached the SDKs with an empty id/transactionId, and the strict
purchase decoders correctly rejected the whole batch:

getAvailablePurchases result: purchaseCount=2
NitroPurchase has invalid required field: id
PurchaseError: billing-response-json-parse-error

Under the pre-#276 lossy decoders this would have silently dropped or shipped
malformed purchases; now it failed loudly and pointed at the defective bridge.

The fix

Blank-aware fallbacks in the Horizon converters — toPurchase (id,
transactionId) and toActiveSubscription (transactionId) — pinned by
regression tests that build the Meta Purchase with a blank and a present
orderId. The payload-parity audit's pinned source expression for the Horizon
transactionId is updated to the new canonical form (it rejected the first
draft of this change, which is that audit working as intended).

Also enables the commented-out HORIZON_APP_ID meta-data in the React Native
example manifest: without it the Horizon platform SDK cannot initialize, so
initConnection failed before any store call and the example was not runnable
on the device at all. The meta-data is inert on Play and FireOS builds.

Device verification (Quest 3, real Horizon store data)

Step Before After
initConnection fails (no App ID) true
getAvailablePurchases batch rejected at index 0 4 purchases decoded
getActiveSubscriptions — 2 active subscriptions

With this, every store in the matrix has now decoded real payloads through the
strict pipeline on hardware: Play, App Store, Amazon, Vega runtime, and
Horizon.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved Horizon purchase handling when order IDs are blank by reliably using the purchase token instead.
    • Ensured purchase and active subscription transaction identifiers remain consistent and usable.
    • Enabled the required Horizon OS application metadata for Android builds.
  • Tests

    • Added coverage for Horizon purchases and subscriptions with blank, missing, and populated order IDs.
    • Added validation to help maintain consistent purchase payloads across supported platforms.

The Horizon billing-compatibility Purchase.orderId is a non-null String and
arrives blank on device, so the converter's null-only `orderId ?: token`
fallback never fired and produced purchases with an empty id and
transactionId. The strict cross-SDK purchase decoders introduced in #276
correctly reject an empty id, which failed the whole available-purchases
batch on a Quest 3 with two real store purchases:

  getAvailablePurchases result: purchaseCount=2
  NitroPurchase has invalid required field: id
  PurchaseError: billing-response-json-parse-error

Blank-aware fallbacks now cover toPurchase (id, transactionId) and
toActiveSubscription (transactionId), pinned by regression tests that build
the Meta Purchase with a blank and a present orderId, and the payload-parity
audit's pinned source expression for the Horizon transactionId is updated to
the new canonical form. Verified on the same Quest 3: the batch decodes as
4 available purchases and 2 active subscriptions with no decode errors.

Also enable the commented-out HORIZON_APP_ID meta-data in the React Native
example manifest. Without it the Horizon platform SDK cannot initialize, so
initConnection failed before any store call and the example was not runnable
on the device at all. The meta-data is inert on Play and FireOS builds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hyochan hyochan added react-native-iap react-native-iap library 👀 example 🛠 bugfix All kinds of bug fixes 🤖 android Related to android labels Aug 3, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e6c2b189-3882-4fcd-95d0-4e2fa7d9806c

📥 Commits

Reviewing files that changed from the base of the PR and between 9fd4384 and 66f3e81.

📒 Files selected for processing (4)
  • libraries/react-native-iap/example/android/app/src/main/AndroidManifest.xml
  • packages/google/openiap/src/horizon/java/dev/hyo/openiap/utils/BillingConverters.kt
  • packages/google/openiap/src/testHorizon/java/dev/hyo/openiap/HorizonBlankOrderIdTest.kt
  • scripts/audit-purchase-payload-parity.mjs

📝 Walkthrough

Walkthrough

The example app enables Horizon application metadata. Horizon purchase and active subscription conversion now use the purchase token when orderId is null or blank. Tests and the payload parity audit cover this fallback.

Changes

Horizon purchase identity handling

Layer / File(s) Summary
Enable Horizon application metadata
libraries/react-native-iap/example/android/app/src/main/AndroidManifest.xml
The example manifest activates the HORIZON_APP_ID metadata entry.
Apply nonblank order ID fallback
packages/google/openiap/src/horizon/java/dev/hyo/openiap/utils/BillingConverters.kt, packages/google/openiap/src/testHorizon/java/dev/hyo/openiap/HorizonBlankOrderIdTest.kt, scripts/audit-purchase-payload-parity.mjs
Purchase and active subscription conversion use the purchase token when orderId is null or blank. Tests and the parity audit validate the fallback and preserve nonblank order IDs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • hyodotdev/openiap#39: Updates the same Horizon purchase conversion methods for base-plan ID extraction.
  • hyodotdev/openiap#162: Introduces related Horizon purchase conversion and manifest metadata behavior.

Suggested labels: ⌚️ regression, 🧪 test

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main Horizon conversion fix and specifies the fallback from blank order IDs to purchase tokens.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/horizon-blank-order-id

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@hyochan
hyochan merged commit 08059f4 into main Aug 3, 2026
20 checks passed
@hyochan
hyochan deleted the fix/horizon-blank-order-id branch August 3, 2026 19:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🤖 android Related to android 🛠 bugfix All kinds of bug fixes 👀 example react-native-iap react-native-iap library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant