feat(google): support one-time purchase discount offers#51
Conversation
BREAKING CHANGE: `oneTimePurchaseOfferDetailsAndroid` type changed from single object to array to support multiple discount offers. Migration: - Before: `product.oneTimePurchaseOfferDetailsAndroid?.formattedPrice` - After: `product.oneTimePurchaseOfferDetailsAndroid?.firstOrNull()?.formattedPrice` Changes: - Add discount-related types: DiscountDisplayInfoAndroid, DiscountAmountAndroid, ValidTimeWindowAndroid, LimitedQuantityInfoAndroid, PreorderDetailsAndroid, RentalDetailsAndroid - Update BillingConverters to use oneTimePurchaseOfferDetailsList API (Google Play Billing Library 7.0+) - Add discount display support in Example app (ProductCard, Modals) - Add discount feature documentation - Fix verifyPurchaseWithIapkit return type usage in Example screens Requires: Google Play Billing Library 7.0+
WalkthroughThis PR adds Android-specific discount, rental, limited-quantity, and validity models and changes one-time purchase offer details from a single object to an array across product and subscription types. Converters, GraphQL schema, docs, example UI, and serialization/parsing were updated to support multiple enriched offers. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 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.
Actionable comments posted: 2
🧹 Nitpick comments (5)
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (1)
1270-1286: IAPKit result handling is correct; consider redacting token in logsReturning
result.isValiddirectly fromverifyPurchaseWithIapkitmatches the helper’s contract and simplifies the call. The only tweak I’d suggest is to align withPurchaseFlowScreenby logging a truncatedpurchaseToken(e.g.,token.take(6)) instead of the full value, to avoid leaking full tokens in logs even in example code.packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt (1)
560-577: Direct use ofresult.isValidlooks good; watch for blocking network workThe switch to
val result = verifyPurchaseWithIapkit(...); return result.isValidis correct and clearer than handling a collection of results. SinceverifyPurchaseWithIapkitis a synchronous HTTP call, consider wrapping it inwithContext(Dispatchers.IO)inside thissuspendhelper (or updating the util) so thatLaunchedEffecton the main dispatcher doesn’t end up doing network I/O on the UI thread.packages/google/Example/src/main/java/dev/hyo/martie/screens/uis/ProductCard.kt (1)
114-158: Discount UI is solid; consider safer handling for malformedfullPriceMicrosUsing the first one-time offer and
discountDisplayInfoto drive the strikethrough price and badge is well-structured and null-safe. One small improvement: instead of defaulting to0LwhenfullPriceMicros?.toLongOrNull()fails, you could skip rendering the original-price line in that case to avoid ever showing a bogus “0.00” crossed-out price.packages/google/Example/src/main/java/dev/hyo/martie/screens/uis/Modals.kt (1)
139-216: Multi-offer rendering correctly reflects new one-time offer modelThe new block that handles
oneTimePurchaseOfferDetailsAndroidas a list is well-structured: it safely iterates offers, surfaces discount, time-window, limited-quantity, preorder, and rental metadata, and uses visual cues (card tint) when discounts are present. As a future enhancement, you might format the millis fields (valid window, preorder times) into human-readable dates, but as a diagnostics-focused modal the current raw values are acceptable.packages/docs/src/pages/docs/features/discount.tsx (1)
268-369: Minor: prefer formatted price source over currency code math in UI exampleIn
ProductCard, the original price is reconstructed from micros and shown as{currency} {fullPrice.toFixed(2)}, wherecurrencyis a currency code (e.g.,USD). For clearer UX and localization, consider either:
- Using a formatted price string from Billing (if available), or
- Formatting via
Intl.NumberFormatusing the currency code.Also, in the earlier fetch example you always log
discount?.percentageDiscount + '% OFF'; you might mirror the logic fromgetDiscountText()(handlingdiscountAmountor falling back toSALE) to avoid loggingnull% OFF.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (4)
packages/gql/src/generated/Types.ktis excluded by!**/generated/**packages/gql/src/generated/Types.swiftis excluded by!**/generated/**packages/gql/src/generated/types.dartis excluded by!**/generated/**packages/gql/src/generated/types.tsis excluded by!**/generated/**
📒 Files selected for processing (13)
packages/apple/Sources/Models/Types.swift(6 hunks)packages/docs/src/pages/docs.tsx(3 hunks)packages/docs/src/pages/docs/features/discount.tsx(1 hunks)packages/docs/src/pages/docs/types.tsx(1 hunks)packages/docs/src/pages/docs/updates/notes.tsx(1 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt(1 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt(1 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/uis/Modals.kt(1 hunks)packages/google/Example/src/main/java/dev/hyo/martie/screens/uis/ProductCard.kt(1 hunks)packages/google/openiap/src/horizon/java/dev/hyo/openiap/utils/BillingConverters.kt(4 hunks)packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt(11 hunks)packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt(5 hunks)packages/gql/src/type-android.graphql(3 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: iOS-specific functions must end withIOSsuffix (e.g.,clearTransactionIOS,getAppTransactionIOS)
Android-specific functions must end withAndroidsuffix (e.g.,acknowledgePurchaseAndroid,consumePurchaseAndroid)
Use action prefixes for function naming:getfor retrieval,requestfor async operations,clearfor removal,is/hasfor boolean checks,show/presentfor UI display,begin/finish/endfor process controlPrefer interface for defining object shapes in TypeScript
Files:
packages/docs/src/pages/docs/types.tsxpackages/docs/src/pages/docs/features/discount.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs.tsx
packages/docs/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/docs/**/*.{ts,tsx}: Use kebab-case for search modal IDs (e.g.,id: 'request-products')
Modal state should be defined once at the app root level using Preact Signals (signal from '@preact/signals-react'), not instantiated multiple times
ALL components must fit within parent boundaries and never overflow outside parent containers; useoverflow-hidden,break-words, andwhitespace-nowrapas needed
Delete unused components, functions, and imports immediately; do not keep commented-out code or unused variables
ANY function that returns a Promise must be wrapped withvoidoperator when used where a void return is expected (e.g., event handlers)
Files:
packages/docs/src/pages/docs/types.tsxpackages/docs/src/pages/docs/features/discount.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs.tsx
packages/docs/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
packages/docs/**/*: Before committing, runnpx prettier --write,npm run lint,bun run tscornpm run typecheck, andnpm run buildto verify formatting, linting, types, and build success
Use conventional commit format with lowercase type prefix and lowercase description (e.g.,feat: add user authentication)
Files:
packages/docs/src/pages/docs/types.tsxpackages/docs/src/pages/docs/features/discount.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (GEMINI.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for variable and function names
Use PascalCase for class and component names
Always use async/await for handling promises instead of .then() chains
Add JSDoc comments for public functions and exported APIs
Use const by default, let if reassignment is needed, avoid var
Files:
packages/docs/src/pages/docs/types.tsxpackages/docs/src/pages/docs/features/discount.tsxpackages/docs/src/pages/docs/updates/notes.tsxpackages/docs/src/pages/docs.tsx
packages/google/**/*.kt
📄 CodeRabbit inference engine (CLAUDE.md)
DO NOT add
Androidsuffix to function names in the Android-only package, even for Android-specific APIs (e.g., useacknowledgePurchase()notacknowledgePurchaseAndroid())
Files:
packages/google/Example/src/main/java/dev/hyo/martie/screens/uis/Modals.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.ktpackages/google/openiap/src/horizon/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/uis/ProductCard.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt
packages/gql/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
Run
bun run generateto regenerate types for all platforms (TypeScript, Swift, Kotlin, Dart) from the GraphQL schema
Files:
packages/gql/src/type-android.graphql
packages/apple/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
packages/apple/**/*.swift: iOS-specific functions MUST haveIOSsuffix (e.g.,presentCodeRedemptionSheetIOS(),showManageSubscriptionsIOS())
Use Pascal case for acronyms at the beginning or middle of names (e.g.,IapManager,IapPurchase), but ALL CAPS only when appearing as a suffix (e.g.,ProductIAP,OpenIAP)
Files:
packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/Types.swift
📄 CodeRabbit inference engine (CLAUDE.md)
DO NOT edit
Types.swiftinSources/Models/as it is auto-generated from the OpenIAP GraphQL schema; regenerate using./scripts/generate-types.sh
Files:
packages/apple/Sources/Models/Types.swift
packages/apple/Sources/Models/**/*.swift
📄 CodeRabbit inference engine (CLAUDE.md)
OpenIAP official types in
Sources/Models/must match types defined at openiap.dev/docs/types
Files:
packages/apple/Sources/Models/Types.swift
🧠 Learnings (8)
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to **/*.{ts,tsx} : Android-specific functions must end with `Android` suffix (e.g., `acknowledgePurchaseAndroid`, `consumePurchaseAndroid`)
Applied to files:
packages/docs/src/pages/docs/types.tsxpackages/docs/src/pages/docs/updates/notes.tsx
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)
Applied to files:
packages/google/Example/src/main/java/dev/hyo/martie/screens/uis/Modals.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/uis/ProductCard.ktpackages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/Types.kt : DO NOT edit `openiap/src/main/Types.kt` as it is auto-generated from the GraphQL schema; regenerate it using `./scripts/generate-types.sh`
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/gql/src/type-android.graphqlpackages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap/src/main/java/dev/hyo/openiap/utils/**/*.kt : Place reusable Kotlin helper functions in `openiap/src/main/java/dev/hyo/openiap/utils/`
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.ktpackages/google/Example/src/main/java/dev/hyo/martie/screens/PurchaseFlowScreen.kt
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/openiap-versions.json : To update OpenIAP GraphQL types in the Android library, edit the `gql` field in `openiap-versions.json`, then run `./scripts/generate-types.sh` and compile to verify
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/gql/src/type-android.graphql
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : OpenIAP official types in `Sources/Models/` must match types defined at [openiap.dev/docs/types](https://www.openiap.dev/docs/types)
Applied to files:
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.ktpackages/apple/Sources/Models/Types.swift
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/openiap-versions.json : To update OpenIAP GraphQL types in the iOS/macOS library, edit the `gql` field in `openiap-versions.json`, run `./scripts/generate-types.sh`, and run `swift test` to verify compatibility
Applied to files:
packages/gql/src/type-android.graphql
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/Types.swift : DO NOT edit `Types.swift` in `Sources/Models/` as it is auto-generated from the OpenIAP GraphQL schema; regenerate using `./scripts/generate-types.sh`
Applied to files:
packages/gql/src/type-android.graphqlpackages/apple/Sources/Models/Types.swift
🧬 Code graph analysis (5)
packages/docs/src/pages/docs/features/discount.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
packages/google/Example/src/main/java/dev/hyo/martie/screens/SubscriptionFlowScreen.kt (2)
packages/apple/Sources/OpenIapModule.swift (1)
verifyPurchaseWithIapkit(648-722)packages/google/openiap/src/main/java/dev/hyo/openiap/utils/PurchaseVerificationValidator.kt (1)
verifyPurchaseWithIapkit(83-167)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (1)
packages/gql/src/generated/types.ts (6)
DiscountAmountAndroid(79-84)DiscountDisplayInfoAndroid(90-101)LimitedQuantityInfoAndroid(229-234)ProductAndroidOneTimePurchaseOfferDetail(408-439)RentalDetailsAndroid(788-796)ValidTimeWindowAndroid(975-980)
packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt (1)
packages/gql/src/generated/types.ts (7)
DiscountDisplayInfoAndroid(90-101)DiscountAmountAndroid(79-84)ValidTimeWindowAndroid(975-980)LimitedQuantityInfoAndroid(229-234)PreorderDetailsAndroid(356-367)RentalDetailsAndroid(788-796)ProductAndroidOneTimePurchaseOfferDetail(408-439)
packages/apple/Sources/Models/Types.swift (1)
packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (7)
discountAmountMicros(677-702)discountAmount(708-735)maximumQuantity(917-942)discountDisplayInfo(1090-1169)offerToken(2161-2184)rentalExpirationPeriod(1767-1793)endTimeMillis(1971-1996)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test Android
🔇 Additional comments (14)
packages/docs/src/pages/docs.tsx (1)
13-13: Discount docs page wiring matches existing routing patternImporting
Discountand adding both the sidebar NavLink andfeatures/discountroute are consistent with the existing feature pages (purchase, subscription, etc.). Paths and labels line up, so navigation to the new Discounts (Android) docs should work as expected.Also applies to: 174-182, 258-258
packages/docs/src/pages/docs/updates/notes.tsx (1)
95-100: Kotlin snippet correctly reflects array-based one-time offer detailsUpdating the example to treat
oneTimePurchaseOfferDetailsAndroidas an array and using.firstOrNull()?.preorderDetailsAndroidmatches the new list-based API and keeps the sample idiomatic and null-safe.packages/docs/src/pages/docs/types.tsx (1)
367-389: Android one-time offer docs now aligned with new type shapeThe updated description correctly documents the field as an array and enumerates the new offer metadata fields, linking to the dedicated Discounts page and calling out the Billing Library 7.0+ requirement. This matches the generated types and Kotlin models.
packages/docs/src/pages/docs/features/discount.tsx (1)
7-777: Comprehensive Discounts page is well-structured and consistent with new Android typesThe page ties together data structures, fetching, UI patterns, time/quantity logic, and purchase flow in a way that matches the new one-time offer types and Kotlin/Swift/TS models. The sectioning, anchors, and useScrollToHash/SEO match existing docs conventions.
packages/apple/Sources/Models/Types.swift (1)
327-346: Android discount offer Swift types look consistent; confirm they’re generator-syncedThe new Android structs (
DiscountAmountAndroid,DiscountDisplayInfoAndroid,LimitedQuantityInfoAndroid,RentalDetailsAndroid,ValidTimeWindowAndroid) and the expandedProductAndroidOneTimePurchaseOfferDetail(now used as an array on bothProductAndroidandProductSubscriptionAndroid) line up with the documented TS/GQL shapes and the docs updates for discount offers.Given this file is auto-generated, please ensure these changes come from regenerating Types via the GraphQL schema (
./scripts/generate-types.sh/npm run generate), not from manual edits, so Swift, Kotlin, and TS types all stay in lockstep with the docs.Based on learnings, this file should only change via the generator.
Also applies to: 399-407, 432-448, 450-477, 505-513, 665-673, 724-731
packages/google/openiap/src/horizon/java/dev/hyo/openiap/utils/BillingConverters.kt (1)
26-55: Horizon converters correctly adapt to list-based one-time offersThe conversions now wrap Horizon’s single
oneTimePurchaseOfferDetailsinto a singletonProductAndroidOneTimePurchaseOfferDetaillist for both in-app and subscription products, leaving all discount/time/quantity/preorder/rental fields null (as Horizon doesn’t expose them) and wiring into the newoneTimePurchaseOfferDetailsAndroidlist fields.Using an empty string for
offerTokenis reasonable for Horizon as long as downstream code treats Horizon offers as non-purchasable via offer tokens and only uses tokens for Google Play.Also applies to: 90-119
packages/google/openiap/src/play/java/dev/hyo/openiap/utils/BillingConverters.kt (1)
4-24: All accessed API properties are available in BillingClient 8.xThe new
toOfferDetail()extension correctly mapsProductDetails.OneTimePurchaseOfferDetailsintoProductAndroidOneTimePurchaseOfferDetail, including all accessed properties:discountDisplayInfo,validTimeWindow,limitedQuantityInfo,preorderDetails,rentalDetails,fullPriceMicros,offerId,offerToken, andofferTags. The use ofoneTimePurchaseOfferDetailsListonProductDetailswith defensiverunCatchingis appropriate. BothtoInAppProduct()andtoSubscriptionProduct()correctly use this list while maintaining backward compatibility with the legacy single offer.packages/google/openiap/src/main/java/dev/hyo/openiap/Types.kt (7)
673-735: New discount-related Android types are consistent with the GraphQL schema.
DiscountAmountAndroidandDiscountDisplayInfoAndroidcorrectly mirror their TypeScript counterparts inpackages/gql/src/generated/types.ts. ThefromJson/toJsonmethods follow the established patterns in this auto-generated file.
913-942:LimitedQuantityInfoAndroidmatches the schema definition.Fields correctly map TypeScript
numberto KotlinIntwith properNumber.toInt()conversions infromJson.
1036-1084: One-time purchase offers now correctly support multiple offers as a list.The change from a single object to
List<ProductAndroidOneTimePurchaseOfferDetail>?aligns with the PR objective to support multiple discount offers. The nullable list handling follows the same pattern used forsubscriptionOfferDetailsAndroid.
1086-1169:ProductAndroidOneTimePurchaseOfferDetailfully matches the GraphQL schema.All 12 fields align with the TypeScript definition in
packages/gql/src/generated/types.ts(lines 407-438). ThefromJson/toJsonmethods correctly handle nested objects (DiscountDisplayInfoAndroid,LimitedQuantityInfoAndroid,RentalDetailsAndroid,ValidTimeWindowAndroid) and the list ofofferTags.
1239-1287:ProductSubscriptionAndroidconsistently updated to support one-time purchase offer lists.The same list-based pattern is applied here as in
ProductAndroid, maintaining consistency across product types.
1763-1793:RentalDetailsAndroidandValidTimeWindowAndroidcorrectly implement their schema definitions.Both new data classes match their TypeScript counterparts in field names, types, and nullability. The ISO 8601 period format for rental details and millisecond epoch timestamps for valid time windows are appropriately typed as
Stringto preserve precision.Also applies to: 1967-1996
1-7: Auto-generated file verified against GraphQL schema.Based on learnings, this file is auto-generated and should not be edited directly. All changes correctly reflect the updated GraphQL schema for one-time purchase discount offers. The new types (
DiscountAmountAndroid,DiscountDisplayInfoAndroid,LimitedQuantityInfoAndroid,RentalDetailsAndroid,ValidTimeWindowAndroid) and the conversion ofoneTimePurchaseOfferDetailsAndroidto a list are consistent with the TypeScript definitions inpackages/gql/src/generated/types.ts.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/docs/src/pages/docs/features/discount.tsx (1)
642-694: VerifyrequestPurchasetypediscriminator value in TS & Kotlin examplesThe
requestPurchaseexamples now correctly include the requiredtypediscriminator, but they use the literal'inapp'/"inapp". Previous review feedback referencedtype: 'in-app'for in‑app purchases, so there may still be a mismatch between these examples and the actualRequestPurchaseProps.typeunion.Please double‑check the canonical value in your shared types (e.g.,
RequestPurchaseProps/RequestPurchaseAndroidProps) and ensure:
- The TypeScript example’s
typevalue matches the allowed union.- The Kotlin example passes the same canonical string into
RequestPurchaseProps.If the union expects
'in-app', both examples should be updated accordingly; if'inapp'is now the official value, you’re good and the earlier guidance can be ignored.You can verify consistency with a quick scan of the codebase:
#!/bin/bash # Inspect RequestPurchase-related types and usages of the type discriminator # Find the RequestPurchaseProps definition rg "interface RequestPurchaseProps|type RequestPurchaseProps" -n packages -S # Check what string literals are used for the discriminator rg "type\s*[:=]\s*['\"]in-app['\"]|['\"]inapp['\"]" -n packages -S
🧹 Nitpick comments (1)
packages/docs/src/pages/docs/features/discount.tsx (1)
7-18: Add JSDoc for exportedDiscountpage component
Discountis the default export and part of the public docs surface, but it lacks a JSDoc block. Add a brief JSDoc describing the page’s purpose to align with the guideline that public functions/exports should be documented.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/docs/src/pages/docs/features/discount.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: iOS-specific functions must end withIOSsuffix (e.g.,clearTransactionIOS,getAppTransactionIOS)
Android-specific functions must end withAndroidsuffix (e.g.,acknowledgePurchaseAndroid,consumePurchaseAndroid)
Use action prefixes for function naming:getfor retrieval,requestfor async operations,clearfor removal,is/hasfor boolean checks,show/presentfor UI display,begin/finish/endfor process controlPrefer interface for defining object shapes in TypeScript
Files:
packages/docs/src/pages/docs/features/discount.tsx
packages/docs/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
packages/docs/**/*.{ts,tsx}: Use kebab-case for search modal IDs (e.g.,id: 'request-products')
Modal state should be defined once at the app root level using Preact Signals (signal from '@preact/signals-react'), not instantiated multiple times
ALL components must fit within parent boundaries and never overflow outside parent containers; useoverflow-hidden,break-words, andwhitespace-nowrapas needed
Delete unused components, functions, and imports immediately; do not keep commented-out code or unused variables
ANY function that returns a Promise must be wrapped withvoidoperator when used where a void return is expected (e.g., event handlers)
Files:
packages/docs/src/pages/docs/features/discount.tsx
packages/docs/**/*
📄 CodeRabbit inference engine (CLAUDE.md)
packages/docs/**/*: Before committing, runnpx prettier --write,npm run lint,bun run tscornpm run typecheck, andnpm run buildto verify formatting, linting, types, and build success
Use conventional commit format with lowercase type prefix and lowercase description (e.g.,feat: add user authentication)
Files:
packages/docs/src/pages/docs/features/discount.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (GEMINI.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for variable and function names
Use PascalCase for class and component names
Always use async/await for handling promises instead of .then() chains
Add JSDoc comments for public functions and exported APIs
Use const by default, let if reassignment is needed, avoid var
Files:
packages/docs/src/pages/docs/features/discount.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to **/*.{ts,tsx} : Android-specific functions must end with `Android` suffix (e.g., `acknowledgePurchaseAndroid`, `consumePurchaseAndroid`)
Applied to files:
packages/docs/src/pages/docs/features/discount.tsx
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/apple/Sources/Models/**/*.swift : OpenIAP official types in `Sources/Models/` must match types defined at [openiap.dev/docs/types](https://www.openiap.dev/docs/types)
Applied to files:
packages/docs/src/pages/docs/features/discount.tsx
📚 Learning: 2025-12-06T20:15:59.223Z
Learnt from: CR
Repo: hyodotdev/openiap PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-06T20:15:59.223Z
Learning: Applies to packages/google/**/*.kt : DO NOT add `Android` suffix to function names in the Android-only package, even for Android-specific APIs (e.g., use `acknowledgePurchase()` not `acknowledgePurchaseAndroid()`)
Applied to files:
packages/docs/src/pages/docs/features/discount.tsx
🧬 Code graph analysis (1)
packages/docs/src/pages/docs/features/discount.tsx (1)
packages/docs/src/hooks/useScrollToHash.ts (1)
useScrollToHash(4-28)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test Android
BREAKING CHANGE:
oneTimePurchaseOfferDetailsAndroidtype changed fromsingle object to array to support multiple discount offers.
Migration:
product.oneTimePurchaseOfferDetailsAndroid?.formattedPriceproduct.oneTimePurchaseOfferDetailsAndroid?.firstOrNull()?.formattedPriceChanges:
ValidTimeWindowAndroid, LimitedQuantityInfoAndroid, PreorderDetailsAndroid,
RentalDetailsAndroid
(Google Play Billing Library 7.0+)
Related issue: hyochan/react-native-iap#3102
Summary by CodeRabbit
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.