|
| 1 | +--- |
| 2 | +slug: 14.7.4-android-input-naming |
| 3 | +title: 14.7.4 - Android Input Type Field Naming Simplification |
| 4 | +authors: [hyochan] |
| 5 | +tags: [release, openiap, android, billing, breaking-change] |
| 6 | +date: 2026-01-20 |
| 7 | +--- |
| 8 | + |
| 9 | +# 14.7.4 Release Notes |
| 10 | + |
| 11 | +This release simplifies field naming in Android input types (`RequestPurchaseAndroidProps` and `RequestSubscriptionAndroidProps`). Since these types are already Android-specific, their fields no longer need the `Android` suffix. |
| 12 | + |
| 13 | +## Breaking Changes |
| 14 | + |
| 15 | +### Simplified Field Names in Android Input Types |
| 16 | + |
| 17 | +Fields inside platform-specific input types no longer require the platform suffix. The parent type name already indicates the platform context. |
| 18 | + |
| 19 | +**Why this change?** |
| 20 | + |
| 21 | +When you write `google: { offerToken: "..." }`, the `google` key already tells you this is Android-specific. Adding `Android` suffix to fields inside is redundant: |
| 22 | + |
| 23 | +```typescript |
| 24 | +// Redundant - we know it's Android from the parent |
| 25 | +google: { offerTokenAndroid: "..." } |
| 26 | + |
| 27 | +// Cleaner - parent context is sufficient |
| 28 | +google: { offerToken: "..." } |
| 29 | +``` |
| 30 | + |
| 31 | +### Migration Guide |
| 32 | + |
| 33 | +| Old Name (v14.7.3) | New Name (v14.7.4) | |
| 34 | +|-------------------|-------------------| |
| 35 | +| `obfuscatedAccountIdAndroid` | `obfuscatedAccountId` | |
| 36 | +| `obfuscatedProfileIdAndroid` | `obfuscatedProfileId` | |
| 37 | +| `purchaseTokenAndroid` | `purchaseToken` | |
| 38 | +| `replacementModeAndroid` | `replacementMode` | |
| 39 | + |
| 40 | +**Before (v14.7.3):** |
| 41 | + |
| 42 | +```typescript |
| 43 | +await requestPurchase({ |
| 44 | + request: { |
| 45 | + google: { |
| 46 | + skus: ['subscription_id'], |
| 47 | + subscriptionOffers: [{sku: 'subscription_id', offerToken: 'token'}], |
| 48 | + purchaseTokenAndroid: currentPurchaseToken, |
| 49 | + replacementModeAndroid: 1, |
| 50 | + obfuscatedAccountIdAndroid: 'user_123', |
| 51 | + }, |
| 52 | + }, |
| 53 | + type: 'subs', |
| 54 | +}); |
| 55 | +``` |
| 56 | + |
| 57 | +**After (v14.7.4):** |
| 58 | + |
| 59 | +```typescript |
| 60 | +await requestPurchase({ |
| 61 | + request: { |
| 62 | + google: { |
| 63 | + skus: ['subscription_id'], |
| 64 | + subscriptionOffers: [{sku: 'subscription_id', offerToken: 'token'}], |
| 65 | + purchaseToken: currentPurchaseToken, |
| 66 | + replacementMode: 1, |
| 67 | + obfuscatedAccountId: 'user_123', |
| 68 | + }, |
| 69 | + }, |
| 70 | + type: 'subs', |
| 71 | +}); |
| 72 | +``` |
| 73 | + |
| 74 | +### Important: Response Types Keep Suffixes |
| 75 | + |
| 76 | +The suffix removal only applies to **input types** (request parameters). Response types still use suffixes because they're cross-platform: |
| 77 | + |
| 78 | +```typescript |
| 79 | +// Response fields KEEP the Android suffix |
| 80 | +const purchase = purchases[0] as PurchaseAndroid; |
| 81 | + |
| 82 | +// These response fields still have Android suffix |
| 83 | +console.log(purchase.purchaseTokenAndroid); // Keep suffix |
| 84 | +console.log(purchase.obfuscatedAccountIdAndroid); // Keep suffix |
| 85 | + |
| 86 | +// But input fields don't need it anymore |
| 87 | +await requestPurchase({ |
| 88 | + request: { |
| 89 | + google: { |
| 90 | + skus: [product.id], |
| 91 | + obfuscatedAccountId: 'user_123', // Input: no suffix |
| 92 | + }, |
| 93 | + }, |
| 94 | + type: 'in-app', |
| 95 | +}); |
| 96 | +``` |
| 97 | + |
| 98 | +## New Features |
| 99 | + |
| 100 | +### One-Time Purchase Discount Offers (Android 7.0+) |
| 101 | + |
| 102 | +This release adds support for discount offers on one-time (in-app) purchases: |
| 103 | + |
| 104 | +```typescript |
| 105 | +import {fetchProducts, requestPurchase} from 'react-native-iap'; |
| 106 | +import type {ProductAndroid} from 'react-native-iap'; |
| 107 | + |
| 108 | +// 1. Fetch products with discount offers |
| 109 | +const products = await fetchProducts({ |
| 110 | + skus: ['premium_upgrade'], |
| 111 | + type: 'in-app', |
| 112 | +}); |
| 113 | + |
| 114 | +const product = products[0] as ProductAndroid; |
| 115 | + |
| 116 | +// 2. Get the discount offer |
| 117 | +const discountOffer = product.discountOffers?.[0]; |
| 118 | + |
| 119 | +// 3. Purchase with the discount |
| 120 | +if (discountOffer?.offerTokenAndroid) { |
| 121 | + await requestPurchase({ |
| 122 | + request: { |
| 123 | + google: { |
| 124 | + skus: [product.id], |
| 125 | + offerToken: discountOffer.offerTokenAndroid, // Use simplified input field |
| 126 | + }, |
| 127 | + }, |
| 128 | + type: 'in-app', |
| 129 | + }); |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | +## Naming Convention Summary |
| 134 | + |
| 135 | +| Field Location | Suffix Required? | Example | |
| 136 | +|----------------|------------------|---------| |
| 137 | +| Inside `RequestPurchaseAndroidProps` | **NO** | `offerToken` | |
| 138 | +| Inside `RequestSubscriptionAndroidProps` | **NO** | `purchaseToken` | |
| 139 | +| Cross-platform response type | **YES** | `PurchaseAndroid.purchaseTokenAndroid` | |
| 140 | + |
| 141 | +## OpenIAP Versions |
| 142 | + |
| 143 | +| Package | Version | |
| 144 | +|---------|---------| |
| 145 | +| openiap-gql | 1.3.15 | |
| 146 | +| openiap-google | 1.3.26 | |
| 147 | +| openiap-apple | 1.3.13 | |
| 148 | + |
| 149 | +For detailed changes, see the [OpenIAP Release Notes](https://www.openiap.dev/docs/updates/notes). |
0 commit comments