Skip to content
This repository was archived by the owner on Apr 26, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions docs/blog/2026-02-11-14.7.10-openiap-1.3.17.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
slug: 14.7.10
title: 14.7.10 - OpenIAP 1.3.17 Sync
authors: [hyochan]
tags: [release, openiap, android, billing-library]
date: 2026-02-11
Comment thread
hyochan marked this conversation as resolved.
---

# 14.7.10 Release Notes

This release syncs with [OpenIAP v1.3.17](https://www.openiap.dev/docs/updates/notes#gql-1317), adding new types for Google Play Billing Library 5.0+ and 7.0+ features.

<!-- truncate -->

## New Types

### InstallmentPlanDetailsAndroid (Billing Library 7.0+)

Subscription installment plan details for plans that allow users to pay in installments.

```typescript
interface InstallmentPlanDetailsAndroid {
/** Committed payments count after signup (e.g., 12 monthly payments) */
commitmentPaymentsCount: number;
/** Subsequent commitment payments when plan renews (0 if reverts to normal) */
subsequentCommitmentPaymentsCount: number;
}
```

This is available on `ProductSubscriptionAndroidOfferDetails.installmentPlanDetails`.

### PendingPurchaseUpdateAndroid (Billing Library 5.0+)

Details about pending subscription upgrades/downgrades.

```typescript
interface PendingPurchaseUpdateAndroid {
/** Product IDs for the pending purchase update */
products: string[];
/** Purchase token for the pending transaction */
purchaseToken: string;
}
```

This is available on `PurchaseAndroid.pendingPurchaseUpdateAndroid`.

### purchaseOptionIdAndroid (Billing Library 7.0+)

New field on `DiscountOffer` and `ProductAndroidOneTimePurchaseOfferDetail` to identify which purchase option the user selected.

```typescript
// Available on DiscountOffer
discountOffer.purchaseOptionIdAndroid // string | null
```

## OpenIAP Versions

| Package | Version |
|---------|---------|
| openiap-gql | 1.3.17 |
| openiap-google | 1.3.28 |
| openiap-apple | 1.3.14 |

## Installation

```bash
yarn add react-native-iap@14.7.10
```

## Related

- [OpenIAP Release Notes](https://www.openiap.dev/docs/updates/notes#gql-1317)
1 change: 1 addition & 0 deletions docs/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions docs/static/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,36 @@ interface DiscountOffer {
offerTokenAndroid?: string;
discountAmountMicrosAndroid?: string;
formattedDiscountAmountAndroid?: string;
purchaseOptionIdAndroid?: string; // v14.7.10+, Billing Library 7.0+
}
```

### InstallmentPlanDetailsAndroid (v14.7.10+, Billing Library 7.0+)

Subscription installment plan details:

```tsx
interface InstallmentPlanDetailsAndroid {
commitmentPaymentsCount: number; // Initial commitment (e.g., 12 months)
subsequentCommitmentPaymentsCount: number; // Renewal commitment (0 if reverts to normal)
}
```

Available on `ProductSubscriptionAndroidOfferDetails.installmentPlanDetails`.

### PendingPurchaseUpdateAndroid (v14.7.10+, Billing Library 5.0+)

Pending subscription upgrade/downgrade details:

```tsx
interface PendingPurchaseUpdateAndroid {
products: string[]; // New products being switched to
purchaseToken: string; // Pending transaction token
}
```

Available on `PurchaseAndroid.pendingPurchaseUpdateAndroid`.

### Purchase

```tsx
Expand All @@ -231,6 +258,7 @@ interface Purchase {
autoRenewingAndroid?: boolean;
packageNameAndroid?: string;
isSuspendedAndroid?: boolean; // v14.7.3+, Billing Library 8.1+
pendingPurchaseUpdateAndroid?: PendingPurchaseUpdateAndroid; // v14.7.10+, Billing Library 5.0+
}
```

Expand Down
6 changes: 3 additions & 3 deletions openiap-versions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"apple": "1.3.14",
"google": "1.3.27",
"gql": "1.3.16",
"docs": "1.3.16"
"google": "1.3.28",
"gql": "1.3.17",
"docs": "1.3.17"
}
135 changes: 135 additions & 0 deletions src/__tests__/standardized-offer-types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import type {
ProductAndroid,
ProductSubscriptionAndroid,
RequestPurchaseAndroidProps,
InstallmentPlanDetailsAndroid,
PendingPurchaseUpdateAndroid,
PurchaseAndroid,
} from '../types';

describe('Standardized Offer Types', () => {
Expand Down Expand Up @@ -466,4 +469,136 @@ describe('Standardized Offer Types', () => {
expect(purchaseRequest.offerToken).toBe('summer_sale_offer_token');
});
});

describe('InstallmentPlanDetailsAndroid', () => {
it('should have correct structure for subscription installment plans', () => {
// Installment plan details are available in Google Play Billing Library 7.0+
const installmentDetails: InstallmentPlanDetailsAndroid = {
commitmentPaymentsCount: 12,
subsequentCommitmentPaymentsCount: 12,
};

expect(installmentDetails.commitmentPaymentsCount).toBe(12);
expect(installmentDetails.subsequentCommitmentPaymentsCount).toBe(12);
});

it('should support zero subsequentCommitmentPaymentsCount for non-recurring installments', () => {
// When subsequentCommitmentPaymentsCount is 0, plan reverts to normal after initial commitment
const nonRecurringInstallment: InstallmentPlanDetailsAndroid = {
commitmentPaymentsCount: 6,
subsequentCommitmentPaymentsCount: 0,
};

expect(nonRecurringInstallment.commitmentPaymentsCount).toBe(6);
expect(nonRecurringInstallment.subsequentCommitmentPaymentsCount).toBe(0);
});
});

describe('PendingPurchaseUpdateAndroid', () => {
it('should have correct structure for pending subscription changes', () => {
// Pending purchase updates are available in Google Play Billing Library 5.0+
const pendingUpdate: PendingPurchaseUpdateAndroid = {
products: ['premium_yearly'],
purchaseToken: 'pending_token_abc123',
};

expect(pendingUpdate.products).toEqual(['premium_yearly']);
expect(pendingUpdate.purchaseToken).toBe('pending_token_abc123');
});

it('should support multiple products in pending update', () => {
// When upgrading to a bundle or combined subscription
const bundlePendingUpdate: PendingPurchaseUpdateAndroid = {
products: ['premium_yearly', 'addon_storage'],
purchaseToken: 'bundle_pending_token_xyz',
};

expect(bundlePendingUpdate.products).toHaveLength(2);
expect(bundlePendingUpdate.products).toContain('premium_yearly');
expect(bundlePendingUpdate.products).toContain('addon_storage');
});
});

describe('purchaseOptionIdAndroid on DiscountOffer', () => {
it('should support purchaseOptionIdAndroid field', () => {
// purchaseOptionIdAndroid is available in Google Play Billing Library 7.0+
const discountOfferWithPurchaseOption: DiscountOffer = {
id: 'limited_offer',
displayPrice: '$3.99',
price: 3.99,
currency: 'USD',
type: 'one-time',
offerTokenAndroid: 'offer_token_123',
purchaseOptionIdAndroid: 'purchase_option_456',
};

expect(discountOfferWithPurchaseOption.purchaseOptionIdAndroid).toBe(
'purchase_option_456',
);
});

it('should allow purchaseOptionIdAndroid to be optional', () => {
const offerWithoutPurchaseOption: DiscountOffer = {
displayPrice: '$4.99',
price: 4.99,
currency: 'USD',
type: 'promotional',
// No purchaseOptionIdAndroid - not all offers have this
};
Comment thread
hyochan marked this conversation as resolved.

expect(
offerWithoutPurchaseOption.purchaseOptionIdAndroid,
).toBeUndefined();
});
});

describe('PurchaseAndroid with pendingPurchaseUpdateAndroid', () => {
it('should include pendingPurchaseUpdateAndroid for subscription upgrades', () => {
// When a subscription upgrade/downgrade is pending
const purchaseWithPendingUpdate: PurchaseAndroid = {
id: 'purchase_id_abc',
productId: 'premium_monthly',
platform: 'android',
transactionDate: 1704067200000,
purchaseToken: 'current_token',
purchaseState: 'purchased',
packageNameAndroid: 'com.example.app',
isAutoRenewing: true,
quantity: 1,
store: 'google',
pendingPurchaseUpdateAndroid: {
products: ['premium_yearly'],
purchaseToken: 'pending_upgrade_token',
},
};

expect(
purchaseWithPendingUpdate.pendingPurchaseUpdateAndroid,
).toBeDefined();
expect(
purchaseWithPendingUpdate.pendingPurchaseUpdateAndroid?.products,
).toEqual(['premium_yearly']);
expect(
purchaseWithPendingUpdate.pendingPurchaseUpdateAndroid?.purchaseToken,
).toBe('pending_upgrade_token');
});

it('should allow pendingPurchaseUpdateAndroid to be undefined for regular purchases', () => {
const regularPurchase: PurchaseAndroid = {
id: 'purchase_id_xyz',
productId: 'consumable_coins',
platform: 'android',
transactionDate: 1704067200000,
purchaseToken: 'regular_token',
purchaseState: 'purchased',
packageNameAndroid: 'com.example.app',
isAutoRenewing: false,
quantity: 1,
store: 'google',
// No pending update - this is a regular purchase
};

expect(regularPurchase.pendingPurchaseUpdateAndroid).toBeUndefined();
});
});
});
Loading