Skip to content

Change result of purchase methods to PurchaseResult #1408

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 18, 2025

Conversation

tonidero
Copy link
Contributor

This changes the result type of the purchase methods. Instead of returning just a CustomerInfo, now we will return a PurchaseResult, which includes a CustomerInfo and a StoreTransaction

@tonidero tonidero added pr:breaking Changes that are breaking pr:feat A new feature labels Jul 17, 2025
/// RevenueCat's servers.
final CustomerInfo customerInfo;
/// The [StoreTransaction] for this purchase.
final StoreTransaction storeTransaction;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that in the web platforms (RN and capacitor) we also have the productIdentifier here... but seemed redundant since it's already part of the PurchaseResult.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing you mean that it's part of the StoreTransaction, right? So yeah, totally not needed to add the redundancy 👍

@@ -12,7 +12,7 @@ import '../purchases_flutter.dart';
class PurchasesFlutterPlugin {
static final _unknownErrorCode = '${PurchasesErrorCode.unknownError.index}';
static final _configurationErrorCode = '${PurchasesErrorCode.configurationError.index}';
static const _purchasesHybridMappingsVersion = '13.29.0';
static const _purchasesHybridMappingsVersion = '15.0.0';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was super outdated 😬

final appUserId = arguments['appUserId'] as String?;

if(appUserId != null && appUserId.isNotEmpty) {
options['appUserId'] = appUserId;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to change this to be added like this since some changes in the underlying JS libraries made it break without this because we were passing null as the appUserId. A proper fix might be better in purchases-js-hybrid-mappings... but I thought to fix it here for now to avoid another release.

@tonidero tonidero marked this pull request as ready for review July 17, 2025 16:08
@tonidero tonidero requested a review from a team July 17, 2025 16:08
Comment on lines +11 to +16
void _checkConstructor(
CustomerInfo customerInfo,
StoreTransaction storeTransaction) {
PurchaseResult purchaseResult =
PurchaseResult(customerInfo, storeTransaction);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand why we need to test the constructor's API.
Is the constructor public? If so, if we ever need to add an extra property to PurchaseResult, it would be a breaking change since the constructor would also change. Or am I missing something? (note that I know null about Dart 😅)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question! So the constructors for all our classes are currently public in dart (they were public before removing freezed as well). If we need to add new fields in the future, we would need to add multiple constructors, falling back to some default when no value is passed. This can indeed be problematic if no good default can be set though...

We could try to make the constructors package private... we haven't really been doing that until now, but I guess that now that we have removed freezed, we have more control. I might just want to leave it like this for now so we dedicate more time to study our options moving forward. Wdyt @RevenueCat/coresdk?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation! Exactly, it might be problematic if we need to add new fields in the future.

We could try to make the constructors package private...

Yes, I think this should be our end goal. But I agree that, for now, we can leave it like this and think about this in the future 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I like non-public constructors, but doing that now would be a breaking change right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup... though having said that, it would be "ok" since we're releasing this on a major anyway. My main concern is over delaying the release for this, since I'm not sure of the best way to do this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hah I'm not sure either. It's not just adding visibility modifiers? 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for package private you need to define each file as part of a library, and prepend any method with an _: https://medium.com/@simplemanderson/dart-libraries-and-visibility-and-how-they-caught-me-by-surprise-d98ba7868fde.

I just need to test that it works 😅 (I'm focusing on returning the StoreTransaction in purchases-unity after a purchase but I can test this in a bit)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just testing this for a bit and it's not straightforward... We would need to define a library and make all models a part of that library... I wouldn't make that change right now without more testing, so I would prefer to keep it as is for this major. Wdyt @JayShortway @ajpallares ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explaining and testing! 🙏 In that case I'd be okay keeping it as is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the investigation Toni! Sure, let's go with what we have right now, as it involves way deeper changes 👍

Copy link
Member

@ajpallares ajpallares left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good so far but I have one question about some methods that could potentially be needed to migrate as well

/// RevenueCat's servers.
final CustomerInfo customerInfo;
/// The [StoreTransaction] for this purchase.
final StoreTransaction storeTransaction;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing you mean that it's part of the StoreTransaction, right? So yeah, totally not needed to add the redundancy 👍

Copy link
Member

@ajpallares ajpallares left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job Toni!!!

final purchaseResult = await Purchases.purchasePackage(package);
final isPro = purchaseResult.customerInfo.entitlements
.active.containsKey(entitlementKey);
print("StoreTransaction: ${purchaseResult.storeTransaction}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm. Is this print intended to be commited?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to leave it as I think it can be useful... This is the test app in any case so I think it's ok

@tonidero tonidero force-pushed the change-purchase-result-type-to-purchase-result branch from 0e0a1a7 to ac72a62 Compare July 18, 2025 11:03
@tonidero tonidero enabled auto-merge (squash) July 18, 2025 11:13
@tonidero tonidero merged commit 2a5a081 into main Jul 18, 2025
9 checks passed
@tonidero tonidero deleted the change-purchase-result-type-to-purchase-result branch July 18, 2025 11:17
@tonidero tonidero mentioned this pull request Jul 18, 2025
tonidero added a commit that referenced this pull request Jul 18, 2025
## RevenueCat SDK
### 💥 Breaking Changes
* Play Billing Library 8 update

This release updates the SDK to use Google Play Billing Library 8. This
version of the Billing Library removed APIs to query for expired
subscriptions and consumed one-time products, aside from other
improvements. You can check the full list of changes here:
https://developer.android.com/google/play/billing/release-notes#8-0-0

#### No expired subscriptions or consumed one-time products
Note: the following is only relevant if you recently integrated
RevenueCat, and do not (yet) have all your transactions imported.

Play Billing Library 8 removed functionality to query expired
subscriptions or consumed one-time products. This means that, for users
migrating from a non-RevenueCat implementation of the Play Billing
Library, the SDK will not be able to send purchase information from
these purchases. We can still ingest historical data from these
purchases through a backend historical import. See
[docs](https://www.revenuecat.com/docs/migrating-to-revenuecat/migrating-existing-subscriptions).
This doesn't affect developers that have all transactions in RevenueCat,
which is true for the vast majority.

#### Using the SDK with your own IAP code (previously Observer Mode)
Using the SDK with your own IAP code is still supported in v9. Other
than updating the SDK version, there are no changes required. Just make
sure the version of the Play Billing Library is also version 8.0.0+.

* Increased min Flutter SDK to 3.22.0 and Dart min SDK to 3.4.0

* Remove freezed in favor of manually parsed models (#1368) via Toni
Rico (@tonidero)
* This removed some APIs like `toJson`,
`StoreTransaction.revenueCatIdentifier`
* This also modified our APIs to use WebPurchaseRedemptionResult. Now
you will need to do:
```
WebPurchaseRedemptionResult result;
switch (result) {
  case WebPurchaseRedemptionSuccess(:final customerInfo):
    // Handle successful redemption
    break;
  case WebPurchaseRedemptionError(:final error):
    // Handle error in redemption
    break;
  case WebPurchaseRedemptionPurchaseBelongsToOtherUser():
    // Handle case where the purchase belongs to another user
    break;
  case WebPurchaseRedemptionInvalidToken():
    // Handle case where the token is invalid
    break;
  case WebPurchaseRedemptionExpired(:final obfuscatedEmail):
    // Handle case where the redemption link has expired
    break;
};
```

* Change result of purchase methods to PurchaseResult (#1408) via Toni
Rico (@tonidero)
* The result of the purchase methods is now a `PurchaseResult` object
instead of a `CustomerInfo` object. The `PurchaseResult` object contains
a `CustomerInfo` object updated with the latest customer information
after the purchase is made, and the `StoreTransaction` object created by
the purchase.

### New features

* Flutter Web (#1407) via Toni Rico (@tonidero)
* Now, the SDK supports Flutter Web! This means you can create an app
that works on iOS, Android, and Web with the same codebase. The web
support makes use of RevenueCat's [Web
Billing](https://www.revenuecat.com/docs/web/web-billing/overview)
engine, so please report any issues you find.

### List of changes
* [AUTOMATIC BUMP] Updates purchases-hybrid-common to 15.0.0 (#1406) via
RevenueCat Git Bot (@RCGitBot)
* [Android
9.1.0](https://github.com/RevenueCat/purchases-android/releases/tag/9.1.0)
* [Android
9.0.1](https://github.com/RevenueCat/purchases-android/releases/tag/9.0.1)
* [Android
9.0.0](https://github.com/RevenueCat/purchases-android/releases/tag/9.0.0)
* [iOS
5.33.0](https://github.com/RevenueCat/purchases-ios/releases/tag/5.33.0)
* Flutter Web + Remove freezed dependency (#1407) via Toni Rico
(@tonidero)
### 📦 Dependency Updates
* [RENOVATE] Update build-dependencies to v8.11.1 (#1402) via RevenueCat
Git Bot (@RCGitBot)

### 🔄 Other Changes
* Bump fastlane from 2.227.2 to 2.228.0 (#1360) via dependabot[bot]
(@dependabot[bot])
* Bump fastlane-plugin-revenuecat_internal from `05ef095` to `7d97553`
(#1381) via dependabot[bot] (@dependabot[bot])

---------

Co-authored-by: Antonio Pallares <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr:breaking Changes that are breaking pr:feat A new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants