Today `js_play_billing_purchase(productId)` only supports buying a brand-new subscription — there's no way to upgrade an existing subscription to a higher tier or downgrade to a lower one. Apps that offer multiple subscription tiers will hit this immediately.
Play Billing API
`BillingFlowParams` has a `SubscriptionUpdateParams` builder that takes the current subscription's purchase token plus a `replacementMode` enum:
- `CHARGE_FULL_PRICE` — charge the new price, no proration
- `CHARGE_PRORATED_PRICE` — charge the difference for the rest of the period
- `WITHOUT_PRORATION` — effective on next renewal
- `WITH_TIME_PRORATION` — extend the period based on credit
- `DEFERRED` — change applies on next renewal, no charge now
Each mode has different UX implications. Apps need to expose the choice, or pick one based on policy.
Proposed API
Add a third optional parameter to `purchase`:
```typescript
export type ReplacementMode =
| "chargeFullPrice"
| "chargeProratedPrice"
| "withoutProration"
| "withTimeProration"
| "deferred";
export type PurchaseOptions = {
offerToken?: string;
/** Pass to upgrade / downgrade an existing subscription. */
replaceFor?: {
oldPurchaseToken: string;
replacementMode: ReplacementMode;
};
};
export declare function js_play_billing_purchase(
productId: string,
options?: PurchaseOptions,
): Promise;
```
(This obsoletes the simpler signature in #2 — they should land together. Or land #2 first as a milestone.)
Why deferred
Subscription upgrades have non-trivial UX implications (the user is changing their plan, not buying a new one). v0.1.x intentionally only covers the new-purchase case so apps can ship one-tier subscriptions. Multi-tier apps need this, but they're a smaller subset.
Work
Today `js_play_billing_purchase(productId)` only supports buying a brand-new subscription — there's no way to upgrade an existing subscription to a higher tier or downgrade to a lower one. Apps that offer multiple subscription tiers will hit this immediately.
Play Billing API
`BillingFlowParams` has a `SubscriptionUpdateParams` builder that takes the current subscription's purchase token plus a `replacementMode` enum:
Each mode has different UX implications. Apps need to expose the choice, or pick one based on policy.
Proposed API
Add a third optional parameter to `purchase`:
```typescript
export type ReplacementMode =
| "chargeFullPrice"
| "chargeProratedPrice"
| "withoutProration"
| "withTimeProration"
| "deferred";
export type PurchaseOptions = {
offerToken?: string;
/** Pass to upgrade / downgrade an existing subscription. */
replaceFor?: {
oldPurchaseToken: string;
replacementMode: ReplacementMode;
};
};
export declare function js_play_billing_purchase(
productId: string,
options?: PurchaseOptions,
): Promise;
```
(This obsoletes the simpler signature in #2 — they should land together. Or land #2 first as a milestone.)
Why deferred
Subscription upgrades have non-trivial UX implications (the user is changing their plan, not buying a new one). v0.1.x intentionally only covers the new-purchase case so apps can ship one-tier subscriptions. Multi-tier apps need this, but they're a smaller subset.
Work
Product.subscription.subscriptionGroupID+Transaction.upgrade); the cross-platform README example needs updating to show both flows side by side.