Today `js_play_billing_purchase(productId)` automatically picks the first `SubscriptionOfferDetails` returned by Play, which is typically "the cheapest available offer." That's the right default for the simple case but loses information for apps that want to show users a choice between offers — intro pricing, free trials, developer-defined promotional codes.
Current behavior
`PlayBillingBridge.purchase` (Kotlin):
```kotlin
val offer = product.subscriptionOfferDetails?.firstOrNull()
if (offer != null) builder.setOfferToken(offer.offerToken)
```
The TS surface already exposes `subscriptionOffers: SubscriptionOffer[]` per Product, including each offer's `offerToken`, `basePlanId`, `offerId`, `offerTags`, and `pricingPhases`. The data is there; the purchase entry point just doesn't accept it.
Proposed API
Add an optional second parameter:
```typescript
export declare function js_play_billing_purchase(
productId: string,
offerToken?: string,
): Promise;
```
When omitted, current behavior (cheapest auto-selected). When passed, the Kotlin side calls `setOfferToken(offerToken)` directly without inspecting the offer list.
Why deferred
The v0.1.x surface is a faithful port of issue #537's sketch (`purchase(productId): Promise`) and matches what the apps that triggered the issue actually need today. Offer selection is a real-world need but most apps don't expose it in their UI for v1; this can land independently when there's a real consumer.
Work
Notes
- Compatible with Apple's StoreKit 2 sibling (`@perryts/storekit`): StoreKit doesn't have a comparable offer concept, so the cross-platform API stays the same on the iOS side.
Today `js_play_billing_purchase(productId)` automatically picks the first `SubscriptionOfferDetails` returned by Play, which is typically "the cheapest available offer." That's the right default for the simple case but loses information for apps that want to show users a choice between offers — intro pricing, free trials, developer-defined promotional codes.
Current behavior
`PlayBillingBridge.purchase` (Kotlin):
```kotlin
val offer = product.subscriptionOfferDetails?.firstOrNull()
if (offer != null) builder.setOfferToken(offer.offerToken)
```
The TS surface already exposes `subscriptionOffers: SubscriptionOffer[]` per Product, including each offer's `offerToken`, `basePlanId`, `offerId`, `offerTags`, and `pricingPhases`. The data is there; the purchase entry point just doesn't accept it.
Proposed API
Add an optional second parameter:
```typescript
export declare function js_play_billing_purchase(
productId: string,
offerToken?: string,
): Promise;
```
When omitted, current behavior (cheapest auto-selected). When passed, the Kotlin side calls `setOfferToken(offerToken)` directly without inspecting the offer list.
Why deferred
The v0.1.x surface is a faithful port of issue #537's sketch (`purchase(productId): Promise`) and matches what the apps that triggered the issue actually need today. Offer selection is a real-world need but most apps don't expose it in their UI for v1; this can land independently when there's a real consumer.
Work
Notes