fix(google): recover already-owned purchases#189
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds a new ChangesITEM_ALREADY_OWNED Recovery Flow
Release Workflow Version Metadata Integration
Sequence DiagramsequenceDiagram
participant User
participant requestPurchase
participant BillingClient
participant queryAlreadyOwnedPurchases
participant purchaseUpdateListeners
participant purchaseErrorListeners
User->>requestPurchase: Request purchase (SKU already owned)
requestPurchase->>BillingClient: launchBillingFlow
BillingClient-->>requestPurchase: ITEM_ALREADY_OWNED response
requestPurchase->>queryAlreadyOwnedPurchases: Recover owned purchases<br/>(with base-plan mapping)
queryAlreadyOwnedPurchases-->>requestPurchase: List of recovered purchases
alt Purchases found
requestPurchase->>purchaseUpdateListeners: Notify suspended-subscription issue
requestPurchase->>purchaseUpdateListeners: Dispatch recovered purchases
requestPurchase-->>User: Resolve with purchases
else No purchases found
requestPurchase->>purchaseErrorListeners: Notify error
requestPurchase-->>User: Resolve with empty list
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces recovery logic for ITEM_ALREADY_OWNED billing responses in the Google Play Billing implementation by tracking purchase request contexts and querying already owned purchases. The review feedback highlights a critical thread-safety issue where purchaseUpdateListeners and purchaseErrorListeners should use CopyOnWriteArraySet to prevent concurrent modification exceptions. Additionally, it recommends handling the fallback case for ITEM_ALREADY_OWNED recovery failures to avoid mapping to a generic error, and suggests using purchaseList.orEmpty() to safely handle potential null platform types from the billing library.
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.
4554753 to
ef32272
Compare
When Play Billing reports ITEM_ALREADY_OWNED during a purchase flow, query the current owned purchases for the requested SKU and publish matching purchases to update listeners instead of only surfacing an already-owned error. Closes #166
ef32272 to
a083093
Compare
There was a problem hiding this comment.
Code Review
This pull request introduces a recovery mechanism for ITEM_ALREADY_OWNED responses during billing flows by querying and filtering already owned purchases. It also updates listener sets to thread-safe CopyOnWriteArraySet and adds corresponding unit tests. The review feedback suggests updating the mock Purchase JSON in tests to include the productIds array for compatibility with Google Play Billing Library v5.0+.
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.
There was a problem hiding this comment.
Code Review
This pull request adds support for handling the ITEM_ALREADY_OWNED billing response code by querying already owned purchases and notifying listeners. It also enhances thread safety by converting purchaseUpdateListeners and purchaseErrorListeners to CopyOnWriteArraySet, and adds corresponding unit tests. The reviewer recommended extending this thread-safety improvement to userChoiceBillingListeners and developerProvidedBillingListeners to prevent potential ConcurrentModificationExceptions.
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.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt (1)
111-114: 💤 Low valueThread-safe listener sets look good; consider applying to remaining sets for consistency.
The switch to
CopyOnWriteArraySetforpurchaseUpdateListenersandpurchaseErrorListenerscorrectly addresses concurrent iteration from callback threads. Note thatuserChoiceBillingListenersanddeveloperProvidedBillingListenersat lines 113-114 remainmutableSetOfand are also iterated in BillingClient proxy callbacks—if those could race with add/remove operations, consider making them thread-safe in a follow-up.🤖 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 `@packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt` around lines 111 - 114, Replace the non-thread-safe mutableSetOf implementations for userChoiceBillingListeners and developerProvidedBillingListeners with java.util.concurrent.CopyOnWriteArraySet, matching the pattern already used for purchaseUpdateListeners and purchaseErrorListeners. This ensures thread-safe iteration when these sets are accessed from BillingClient proxy callbacks that may race with add/remove operations.
🤖 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 `@packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.kt`:
- Around line 111-114: Replace the non-thread-safe mutableSetOf implementations
for userChoiceBillingListeners and developerProvidedBillingListeners with
java.util.concurrent.CopyOnWriteArraySet, matching the pattern already used for
purchaseUpdateListeners and purchaseErrorListeners. This ensures thread-safe
iteration when these sets are accessed from BillingClient proxy callbacks that
may race with add/remove operations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8a87dfdb-fa23-4d0a-b3cc-7208bfa5672a
📒 Files selected for processing (3)
packages/google/openiap/src/play/java/dev/hyo/openiap/OpenIapModule.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.ktpackages/google/openiap/src/testPlay/java/dev/hyo/openiap/QueryPurchasesRaceTest.kt
There was a problem hiding this comment.
Code Review
This pull request introduces handling for the ITEM_ALREADY_OWNED billing response code by querying already owned purchases and recovering them. It updates listener sets to thread-safe CopyOnWriteArraySet collections, adds the queryAlreadyOwnedPurchases helper function, and includes corresponding unit tests. Feedback suggests resolving the correct basePlanId by matching the requested offer token instead of using firstOrNull(), and wrapping the asynchronous query in a try-catch block to prevent coroutines from hanging in case of unexpected IPC failures.
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.
There was a problem hiding this comment.
Code Review
This pull request introduces handling for the ITEM_ALREADY_OWNED billing response code in the Android OpenIapModule. When this response is received, the module now queries already owned purchases to recover and notify listeners of matching active purchases. To support this, thread-safe CopyOnWriteArraySet listeners and new helper functions (queryAlreadyOwnedPurchases, resolveBasePlanIdForOfferToken) were introduced, along with corresponding unit tests. Additionally, the release and deployment scripts were updated to track packages/docs/src/generated/version-metadata.json. I have no feedback to provide as there are no review comments to address.
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.
Summary
ITEM_ALREADY_OWNEDresults returned immediately fromlaunchBillingFlowby querying Play Billing owned purchases for the requested SKU/type.onPurchaseSuccess/purchaseUpdatedListenercan receive the purchase instead of only seeingalready-owned.Closes #166
Test plan
./gradlew :openiap:testPlayDebugUnitTest --tests dev.hyo.openiap.QueryPurchasesRaceTest./gradlew :openiap:compilePlayDebugKotlin :openiap:compileHorizonDebugKotlinSummary by CodeRabbit
Release Notes
Bug Fixes
Tests
Chores