Skip to content

feat: add win-back offers, product status, and JWS promotional offers#66

Merged
hyochan merged 11 commits into
mainfrom
feat/api-feature-gap-analysis
Jan 18, 2026
Merged

feat: add win-back offers, product status, and JWS promotional offers#66
hyochan merged 11 commits into
mainfrom
feat/api-feature-gap-analysis

Conversation

@hyochan

@hyochan hyochan commented Jan 18, 2026

Copy link
Copy Markdown
Member

Summary

  • Add Win-Back offers support for iOS 18+
  • Add ProductStatusAndroid for Billing Library 8.0+ status codes
  • Add JWS promotional offers for WWDC 2025

Changes

GraphQL Schema (packages/gql)

  • WinBackOfferInputIOS - Win-back offer input type
  • ProductStatusAndroid - Product fetch status enum
  • PromotionalOfferJWSInputIOS - JWS format promotional offers
  • SubscriptionOfferTypeIOS.WinBack - New offer type enum value

iOS (packages/apple)

  • Implement win-back offer handling in purchase flow
  • Add JWS promotional offer support (back-deployed to iOS 15)
  • Add introductory offer eligibility override

Android (packages/google)

  • Map ProductStatusAndroid from BillingResult
  • Return status in fetchProducts response

Documentation (packages/docs)

  • Release notes for gql 1.3.13, google 1.3.24, apple 1.3.11
  • Type documentation updates for new APIs

Skills & Knowledge

  • Enhanced sync and audit workflows
  • Updated external API references

Test plan

  • Type check passes
  • Tests pass
  • Build succeeds

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • iOS: Win‑Back offers, JWS promotional offers, and introductory‑offer eligibility override
    • Android: per‑product status codes, sub‑response codes, suspended‑subscription support, and auto service reconnection
    • Added option to include suspended Android subscriptions when restoring purchases
  • Documentation

    • Expanded docs, checklists, example‑app guidance, and release notes workflows to cover Billing 8.x and iOS 18+ features

✏️ Tip: You can customize this high-level summary in your review settings.

hyochan and others added 7 commits January 18, 2026 22:10
iOS (StoreKit 2):
- WinBackOfferInputIOS for iOS 18+ win-back offers
- PromotionalOfferJWSInputIOS for WWDC 2025 JWS format (iOS 15+)
- introductoryOfferEligibility override option
- SubscriptionOfferTypeIOS.WinBack enum value

Android (Billing 8.0+):
- ProductStatusAndroid enum (OK, NOT_FOUND, NO_OFFERS_AVAILABLE, UNKNOWN)
- productStatusAndroid field on ProductAndroid and ProductSubscriptionAndroid

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Regenerate platform-specific types after schema changes in 8023a84.
Includes new types for Billing 8.0+ (ProductStatusAndroid, BillingResultAndroid,
SubResponseCodeAndroid) and iOS WWDC 2025 APIs (WinBackOfferInputIOS,
PromotionalOfferJwsInputIOS, introductoryOfferEligibility).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add winBackOffer support in purchase options (iOS 18+)
- Add promotionalOfferJWS for new JWS signature format (WWDC 2025)
- Add introductoryOfferEligibility override option (WWDC 2025)
- Update purchaseOptions to accept product for win-back offer lookup
- Regenerate Types.swift from GQL schema

Note: JWS and eligibility override APIs require Xcode 16.4+ to compile.
Implementation includes TODOs for when tooling is available.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ProductStatusAndroid field to product types
- Implement getProductStatus() using reflection for 8.0+ compatibility
- Maps status codes: OK, NOT_FOUND, NO_OFFERS_AVAILABLE
- Gracefully returns null for older billing library versions
- Regenerate Types.kt from GQL schema

This enables better error handling when products fail to fetch,
as 8.0+ now returns status codes instead of silently omitting products.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add release notes for gql 1.3.14, apple 1.3.12, google 1.3.24
- Document ProductStatusAndroid enum in product types page
- Document WinBackOfferInputIOS in offer types page
- Update llms.txt with new API information for AI assistants

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Google Play Billing Library 8.0 API details (ProductStatus, SubResponseCode)
- Add StoreKit 2 WWDC 2025 APIs (win-back offers, JWS promotional offers)
- Update Horizon API documentation
- Regenerate Claude context file with latest API information

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add documentation checklist to all sync-*.md files
- Add example code requirements to audit-code.md
- Add local dev testing section to sync-expo-iap.md
- Create commit.md skill for structured commit workflows
- Improve sync workflow instructions with verification steps

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

coderabbitai Bot commented Jan 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@hyochan has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 7 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 4f2e67a and ad840a8.

📒 Files selected for processing (3)
  • .claude/commands/review-pr.md
  • knowledge/_claude-context/context.md
  • packages/apple/Sources/Helpers/StoreKitTypesBridge.swift
📝 Walkthrough

Walkthrough

Adds iOS Win‑Back offers, JWS promotional offers, and introductory‑eligibility flags; Android product status, sub‑response codes, and includeSuspended handling; updates cross‑platform models, GraphQL schema, billing converters/helpers, StoreKit bridge signatures, CLI docs, and extensive documentation/examples sync workflows.

Changes

Cohort / File(s) Change Summary
Docs & Commands
.claude/commands/*, knowledge/..., packages/docs/public/..., packages/docs/src/pages/docs/*
Large additions and restructures: new commit/audit/sync command docs, expanded knowledge/API references for Google Billing 8.x and StoreKit 2 (iOS 18+), llms.txt updates, required doc/example checklists, and release notes entries.
iOS models & bridge
packages/apple/Sources/Models/Types.swift, packages/apple/Sources/Helpers/StoreKitTypesBridge.swift, packages/apple/Sources/OpenIapModule.swift
New types for WinBack and PromotionalOfferJWS, added fields on RequestPurchase props, purchaseOptions signature now accepts optional product, win‑back/JWS/eligibility handling and validation, and propagation of new iOS purchase fields.
Android models & runtime
packages/google/openiap/src/main/java/.../Types.kt, .../OpenIapModule.kt, .../helpers/Helpers.kt, .../utils/BillingConverters.kt
Added ProductStatusAndroid, SubResponseCodeAndroid, BillingResultAndroid; includeSuspendedAndroid flag; restore/queryPurchases now accept includeSuspended and use reflection to call setIncludeSuspended; product status extraction via reflection and propagation.
GraphQL / API schema
packages/gql/src/type-android.graphql, packages/gql/src/type-ios.graphql, packages/gql/src/type.graphql
Added enums/types: ProductStatusAndroid, SubResponseCodeAndroid, BillingResultAndroid; WinBack/PromotionalOfferJWS input types; extended RequestPurchase/RequestSubscription iOS inputs; added includeSuspendedAndroid to PurchaseOptions.
Docs site types & examples
packages/docs/src/pages/docs/types/offer.tsx, packages/docs/src/pages/docs/types/product.tsx, packages/docs/src/pages/docs/updates/notes.tsx
Added DiscountOfferType.WinBack across language examples; documented productStatusAndroid; added release notes entry summarizing platform changes.
Auxiliary tooling/text
packages/docs/public/llms-full.txt, packages/docs/public/llms.txt, .claude/commands/*.md
Updated generated docs, timestamps, and expanded guidance/checklists for sync and example updates across platforms.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant iOS_Module as "iOS Module"
    participant StoreKit_Bridge as "StoreKit Bridge"
    participant Product as "Product Catalog"
    participant StoreKit as StoreKit

    Client->>iOS_Module: requestPurchase(props with winBackOffer)
    iOS_Module->>iOS_Module: resolveIosPurchaseProps(props)
    iOS_Module->>StoreKit_Bridge: purchaseOptions(iosProps, product)
    StoreKit_Bridge->>Product: fetch product.promotionalOffers
    alt win-back offer found
        Product-->>StoreKit_Bridge: offer details
        StoreKit_Bridge->>StoreKit_Bridge: insert winBackOffer option
    else not found
        StoreKit_Bridge-->>iOS_Module: throw error (offer not found)
    end
    StoreKit_Bridge-->>iOS_Module: return PurchaseOptions
    iOS_Module->>StoreKit: launch purchase with options
    StoreKit-->>iOS_Module: purchase result
    iOS_Module-->>Client: deliver purchase confirmation/result
Loading
sequenceDiagram
    participant Client
    participant Android_Module as "Android Module"
    participant Helpers as Helpers
    participant BillingClient as "BillingClient"
    participant Converters as Converters

    Client->>Android_Module: getAvailablePurchases(options includeSuspendedAndroid)
    Android_Module->>Helpers: restorePurchases(includeSuspended = options.includeSuspendedAndroid)
    Helpers->>Helpers: build QueryPurchasesParams
    alt includeSuspended = true
        Helpers->>BillingClient: queryPurchasesAsync(params with setIncludeSuspended via reflection)
    else
        Helpers->>BillingClient: queryPurchasesAsync(params standard)
    end
    BillingClient-->>Helpers: purchases list
    Helpers->>Converters: toInAppProduct/toSubscriptionProduct(purchase)
    Converters->>Converters: ProductDetails.getProductStatus() via reflection (8.x)
    Converters-->>Helpers: product models with productStatusAndroid
    Helpers-->>Android_Module: returned purchases with status
    Android_Module-->>Client: available purchases response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

📱 iOS, 🤖 android, ❄️ types, 📖 documentation

Poem

🐰 I hop with joy, code carrots in tow,
Win‑back whispers where offers grow,
JWS seals the promotional tune,
Android status hums a new moon—
Cross‑platform treats, let purchases flow! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.13% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main changes: adding win-back offers for iOS, product status codes for Android, and JWS promotional offers across platforms.

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


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 and usage tips.

@hyochan hyochan added the 🎯 feature New feature label Jan 18, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @hyochan, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request expands the in-app purchase capabilities across iOS and Android platforms. It introduces advanced subscription offer types for iOS, such as win-back and JWS-based promotional offers, alongside an option to override introductory offer eligibility. For Android, it enhances product fetching by providing detailed status codes and allows querying suspended subscriptions. Additionally, the PR includes comprehensive updates to internal development workflows and documentation, ensuring better consistency and clarity for future feature development and maintenance.

Highlights

  • iOS Win-Back Offers (iOS 18+): Added support for re-engaging churned subscribers with a new winBackOffer input and WinBack offer type in iOS purchase flows.
  • iOS JWS Promotional Offers (WWDC 2025, iOS 15+): Implemented support for a new JWS-based promotional offer format (promotionalOfferJWS), back-deployed to iOS 15.
  • iOS Introductory Offer Eligibility Override (WWDC 2025, iOS 15+): Introduced an option (introductoryOfferEligibility) to explicitly set introductory offer eligibility for iOS purchases.
  • Android Product Status Codes (Billing Library 8.0+): Added ProductStatusAndroid enum and productStatusAndroid field to products, providing granular status for fetched products (e.g., OK, NOT_FOUND, NO_OFFERS_AVAILABLE).
  • Android Suspended Subscriptions (Billing Library 8.1+): Enabled querying suspended subscriptions via the includeSuspendedAndroid option in PurchaseOptions.
  • Enhanced Documentation & Workflows: Significantly updated internal documentation for audit, commit, and platform sync processes, including detailed checklists and example code guidelines.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This is an excellent and comprehensive pull request. You've not only added significant new features like Win-Back offers for iOS and Product Status for Android, but you've also done an outstanding job updating all related documentation across the board. The changes to the internal process documents, such as audit-code.md and commit.md, are particularly valuable for maintaining project quality and consistency. The implementation details are solid, with thoughtful considerations for backward compatibility in the Android package and future API availability in the iOS package. The GraphQL schema updates are clear and serve as a strong foundation for these new features. Overall, this is a high-quality contribution that significantly enhances the library's capabilities and maintainability.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
.claude/commands/sync-flutter-iap.md (1)

262-351: Markdownlint violations in new example fences.

The added example blocks need blank lines around fences and one fence is missing a language identifier (MD031/MD040). Please adjust to keep lint clean.

.claude/commands/sync-react-native-iap.md (1)

224-302: Address markdownlint MD031/MD040 for the new example fences.

The new TSX/MDX fences need blank lines around them, and at least one fence is missing a language tag. This will fail markdownlint.

.claude/commands/sync-kmp-iap.md (1)

236-313: Fix markdownlint MD031/MD040 in the newly added fenced blocks.

Please add blank lines around the new fences and specify a language on the fence flagged by MD040 to keep linting green.

🤖 Fix all issues with AI agents
In @.claude/commands/audit-code.md:
- Around line 173-327: The markdown lint errors come from fenced code blocks in
the new examples (e.g., the notes.tsx example, the Swift snippet in
SubscriptionFlowScreen.swift, the Kotlin example in AllProductsScreen.kt and the
MDX examples); fix by ensuring there is a blank line before and after every
triple-backtick fence and that each opening fence includes a language identifier
(e.g., ```typescript, ```swift, ```kotlin, ```mdx) for all added examples and
docs (notes.tsx, API/type MDX blocks, and example snippets) so MD031/MD040 are
satisfied.

In @.claude/commands/sync-expo-iap.md:
- Around line 270-307: Fix markdown fenced-block spacing and nested fences:
ensure there is a blank line before and after every triple-fenced block (e.g.,
the ```tsx blocks under "Example for new iOS feature" and "Example for new
Android feature" and the ```mdx block), and remove any nested triple-fence
sequences inside code examples (replace inner ```typescript in the
"requestSubscription" example with an alternate fence like ~~~ or inline code),
keeping the examples for handleWinBackOffer and products.forEach intact and
properly separated so markdownlint rules MD031/MD040 are satisfied.
- Around line 205-224: Replace the hard-coded, user-specific paths in
LOCAL_OPENIAP_PATHS and the pluginEntries localPath config with portable
placeholders or environment variables: update LOCAL_OPENIAP_PATHS to reference
process.env (e.g., process.env.LOCAL_OPENIAP_IOS / LOCAL_OPENIAP_ANDROID) or
generic relative paths, and ensure the object passed to the app.plugin.js entry
(the localPath field and enableLocalDev flag usage) reads from those
placeholders so no absolute user directory is committed.

In @.claude/commands/sync-godot-iap.md:
- Around line 190-257: Add blank lines before and after every fenced code block
in the document and ensure each fence has a language identifier; specifically,
edit the example fences under "Example Code Guidelines", the iOS example
(gdscript), the Android example (gdscript), and the "Example Documentation
Entry" so each fenced block is preceded and followed by an empty line and the
unlabeled fence in the Documentation Entry is changed to ```gdscript (or
appropriate language) to satisfy markdownlint MD031/MD040; look for the exact
blocks containing RequestSubscriptionIosProps, ProductStatusAndroid match, and
the request_subscription_ios documentation example to locate the fences to
update.

In `@knowledge/_claude-context/context.md`:
- Around line 1493-1503: Update the "Version History" table entries to match
official Google Play Billing Library notes: for Version 8.0 (row with "8.0")
remove "auto-reconnect" and "sub-response codes" and instead describe one-time
product improvements, multiple purchase options/offers, and product-level status
improvements; for Version 8.1 (row with "8.1") revert the detailed features to a
brief "minor release" entry (remove mentions of suspended subscriptions,
includeSuspended, pre-order details, and KEEP_EXISTING) unless you can confirm
those items from an official source; for Version 8.3 (row with "8.3") remove the
"Japan only" restriction from the External Payments program description; keep
the release dates and the "Current Version: 8.3.0" line as-is.
- Around line 2564-2580: Update the table row for originalPlatform to remove the
incorrect "back-deployed to iOS 15" claim (leave it as introduced in iOS 18.4)
or, if you can verify official Apple docs confirm back-deployment, replace the
note with a sourced statement; keep appTransactionID marked as back-deployed to
iOS 15 and ensure the two symbols originalPlatform and appTransactionID are
clearly distinguished in the table.

In `@packages/apple/Sources/Helpers/StoreKitTypesBridge.swift`:
- Around line 410-436: The code silently ignores props.promotionalOfferJWS and
props.introductoryOfferEligibility; update StoreKitTypesBridge.swift to either
apply the new purchase options under a compile-time gate or surface a clear
error: wrap the logic that inserts the options (the options variable where
promotionalOffer and introductoryOfferEligibility would be inserted) in `#if`
swift(>=6.1) and call options.insert(.promotionalOffer(jwsOffer.jws)) and
options.insert(.introductoryOfferEligibility(eligibility)) for
props.promotionalOfferJWS and props.introductoryOfferEligibility respectively;
in the `#else` branch return or throw a PurchaseError.make(...) (matching the
existing pattern used elsewhere in StoreKitTypesBridge.swift) so callers get a
clear unsupported-toolchain error—refer to props.promotionalOfferJWS,
props.introductoryOfferEligibility, options, and mirror the gating/error pattern
used in OpenIapModule.swift and the advancedCommerceData handling.
- Around line 380-408: The code currently uses a compound if-let for
props.winBackOffer and product which silently skips when product is nil; change
this to first guard that if props.winBackOffer (winBackInput) is present but
product is nil then log an error and throw a PurchaseError.make (matching the
pattern used for withOffer) so callers fail fast; otherwise, when product
exists, find the winBackOffer in product.subscription.promotionalOffers, insert
it into options with options.insert(.winBackOffer(offer)) on success or log +
throw PurchaseError.make if the offer isn’t found, and retain the existing log
messages and error codes.

In `@packages/docs/src/pages/docs/updates/notes.tsx`:
- Around line 84-94: The docs list uses wrong enum casing and the example when
expression is non‑exhaustive; update the enum list for ProductStatusAndroid to
use the generated variants Ok, NotFound, NoOffersAvailable, Unknown (references:
ProductStatusAndroid, ProductAndroid, ProductSubscriptionAndroid) and change the
example that evaluates product?.productStatusAndroid (from fetchProducts) to
handle both the null case (product or status is null) and the Unknown case in
the when expression so all branches are covered.
🧹 Nitpick comments (4)
packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt (1)

35-52: Cache reflection lookup and verify Billing 8.0 status codes.

getMethod() is executed per product; consider caching the Method to avoid repeated reflection. Also please confirm the 0/1/2 mappings match the Billing Library 8.0 ProductDetails.ProductStatus constants.

♻️ Proposed refactor (cache reflection lookup)
 internal object BillingConverters {
+    private val productStatusMethod by lazy {
+        runCatching { ProductDetails::class.java.getMethod("getProductStatus") }.getOrNull()
+    }
+
     /**
      * Gets the product status from ProductDetails (Billing Library 8.0+).
      * Returns null for older billing library versions.
      */
     private fun ProductDetails.getProductStatus(): ProductStatusAndroid? {
-        return runCatching {
-            // ProductDetails.productStatus is available in Billing Library 8.0+
-            val statusMethod = this::class.java.getMethod("getProductStatus")
-            val status = statusMethod.invoke(this) as? Int
+        val statusMethod = productStatusMethod ?: return null
+        return runCatching {
+            val status = statusMethod.invoke(this) as? Int
             when (status) {
                 0 -> ProductStatusAndroid.Ok           // ProductDetails.ProductStatus.OK
                 1 -> ProductStatusAndroid.NotFound     // ProductDetails.ProductStatus.NOT_FOUND
                 2 -> ProductStatusAndroid.NoOffersAvailable // ProductDetails.ProductStatus.NO_OFFERS_AVAILABLE
                 else -> ProductStatusAndroid.Unknown
             }
         }.getOrNull()
     }
packages/google/openiap/src/play/java/dev/hyo/openiap/helpers/Helpers.kt (1)

49-85: Confirm Billing 8.1 setIncludeSuspended behavior and fallback expectations.

The reflection-based guard is reasonable, but please confirm from Billing 8.1 docs that setIncludeSuspended(true) is valid only for SUBS and that silently ignoring it on older libs is the intended behavior. If you want stronger observability, consider logging when the method isn’t available.

packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (2)

611-650: Prefer tolerant parsing for ProductStatusAndroid.
Since an Unknown enum value exists, falling back to it avoids crashes if Play adds new status strings in the future.

♻️ Proposed change
-            else -> throw IllegalArgumentException("Unknown ProductStatusAndroid value: $value")
+            else -> ProductStatusAndroid.Unknown

Based on learnings, update the generator rather than hand-editing this file.


3530-3587: Guard against conflicting offer inputs.
Consider validating that promotionalOfferJWS and legacy withOffer aren’t both set, and that win‑back/intro eligibility are only used for subscriptions, to avoid invalid StoreKit option combinations.

Comment thread .claude/commands/audit-code.md
Comment thread .claude/commands/sync-expo-iap.md
Comment thread .claude/commands/sync-expo-iap.md
Comment thread .claude/commands/sync-godot-iap.md
Comment thread knowledge/_claude-context/context.md Outdated
Comment thread knowledge/_claude-context/context.md
Comment thread packages/apple/Sources/Helpers/StoreKitTypesBridge.swift
Comment thread packages/apple/Sources/Helpers/StoreKitTypesBridge.swift
Comment thread packages/docs/src/pages/docs/updates/notes.tsx Outdated
- Fix markdown lint issues (MD031/MD040) in audit-code.md
- Fix markdown fenced-block spacing in sync-expo-iap.md, sync-godot-iap.md
- Replace hard-coded paths with placeholders in sync-expo-iap.md
- Update Google Play Billing version history (correct 8.0, 8.1, 8.3 descriptions)
- Fix originalPlatform back-deployment claim in context.md
- Add guard for winBackOffer product context in StoreKitTypesBridge.swift
- Use #if swift(>=6.1) for JWS promo offers and intro eligibility override
- Fix ProductStatusAndroid enum casing (Ok, NotFound, etc.) in notes.tsx
- Add missing Unknown and null cases to when expression

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
knowledge/_claude-context/context.md (1)

1511-1532: Correct the OpenIAP Note — auto-reconnection is opt-in, not always enabled.

The first claim is accurate: enableAutoServiceReconnection() was introduced in Google Play Billing Library 8.0.0. However, the OpenIAP Note is misleading. According to official documentation, auto-reconnection is opt-in and requires an explicit call to enableAutoServiceReconnection() on the BillingClient builder—it is not enabled by default. Update the note to clarify that developers (or OpenIAP's integration) must explicitly enable it if they want auto-reconnect behavior; claiming "no configuration needed" contradicts the official API design.

🤖 Fix all issues with AI agents
In `@knowledge/_claude-context/context.md`:
- Around line 1493-1499: Rename the duplicate section heading "## Version
History" to a unique title (for example "## Google Play Billing Version
History") to resolve the MD024 duplicate-heading lint error; update the heading
text in the knowledge/_claude-context/context.md file where the "## Version
History" heading appears so any internal links or references that point to that
header are adjusted accordingly.
- Around line 2765-2768: Split the combined availability note into two clear
annotations: mark appTransactionID with "New in iOS 18.4 (back-deployed to iOS
15)" and mark originalPlatform with "New in iOS 18.4 (iOS 18.4+ only)" so the
comment for appTransaction.appTransactionID and appTransaction.originalPlatform
accurately reflect that only appTransactionID is back‑deployed.

In `@packages/apple/Sources/Helpers/StoreKitTypesBridge.swift`:
- Around line 380-417: The code silently ignores props.winBackOffer on
unsupported OS versions; update the `#available` block by adding an else branch
that logs and throws a PurchaseError when props.winBackOffer is non-nil but the
platform is unsupported. Specifically, check props.winBackOffer outside or at
the top of the availability check and if the platform is unsupported call
OpenIapLog.error (include the offerId from props.winBackOffer) and throw
PurchaseError.make (use code: .developerError and productId: props.sku) so
callers fail fast instead of proceeding to purchase without the win-back offer;
keep the existing handling inside the available block (product.subscription,
finding promotionalOffers, options.insert(.winBackOffer(offer))) unchanged.
🧹 Nitpick comments (2)
packages/apple/Sources/Helpers/StoreKitTypesBridge.swift (1)

362-362: Rename this iOS-specific helper to include the IOS suffix.

purchaseOptions is Apple/iOS-specific but doesn’t follow the ...IOS naming rule for packages/apple/Sources/**/*.swift. Consider renaming to purchaseOptionsIOS and updating call sites for consistency. As per coding guidelines, ...

.claude/commands/sync-godot-iap.md (1)

91-97: Prefer placeholders for version examples to prevent drift.

This block is meant as a template; hard-coded versions will go stale quickly. Consider using x.y.z placeholders to keep it evergreen.

📝 Suggested update
-{
-  "gql": "1.3.11",
-  "apple": "1.3.9",
-  "google": "1.3.21"
-}
+{
+  "gql": "x.y.z",
+  "apple": "x.y.z",
+  "google": "x.y.z"
+}

Comment thread knowledge/_claude-context/context.md Outdated
Comment thread knowledge/_claude-context/context.md Outdated
Comment thread packages/apple/Sources/Helpers/StoreKitTypesBridge.swift
hyochan and others added 3 commits January 18, 2026 22:42
- Add reply with commit link before resolving fixed threads
- Add reply templates for different fix scenarios
- Update decision tree with reply requirements
- Add Thread Resolution Rules table
- Clarify that invalid reviews get replies but NOT resolved
- Add important notes about never silent resolving

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Project-specific review-pr.md now only contains:
- Project-specific build commands table
- Project conventions reference
- Links to CLAUDE.md and knowledge/internal/

Global command at ~/.claude/commands/review-pr.md handles:
- Full workflow documentation
- GraphQL API calls
- Decision tree
- Thread resolution rules
- Reply templates

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename "Version History" to "Google Play Billing Version History" (MD024)
- Clarify appTransactionID vs originalPlatform back-deployment in context.md
- Add else branch to fail fast when winBackOffer used on unsupported OS

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

Labels

🎯 feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant