Skip to content
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
9 changes: 9 additions & 0 deletions content/docs/flutter/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ description: "Release notes for the Superwall Flutter SDK"

The changelog for `Superwall`. Also see the [releases](https://github.com/superwall/Superwall-Flutter/releases) on GitHub.

## 2.4.12

### Enhancements
- Updates Android SDK to 2.7.11 [View Android SDK release notes](https://github.com/superwall/Superwall-Android/releases/tag/2.7.11).
- Updates iOS SDK to 4.14.2 [View iOS SDK release notes](https://github.com/superwall/Superwall-iOS/releases/tag/4.14.2).
- Adds `preloadDeviceOverrides` option to `PaywallOptions` to override `shouldPreload` per device tier (Android only)
- Exports `PresentationResult`, `StoreTransaction`, `TransactionProduct` from the public package
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add SDK reference docs for newly exported Flutter types

The 2.4.12 entry says StoreTransaction and TransactionProduct are now publicly exported, but this update only changes version markers and PaywallOptions; there are still no corresponding docs under content/docs/flutter/sdk-reference (repo search for those symbols in that directory returns no matches). This leaves new public API surface undocumented and makes the Flutter SDK reference incomplete for this release; please add reference pages (and navigation entries) for the newly exported types or document where they are intentionally covered.

Useful? React with 👍 / 👎.

- Fixes `PurchaserInfo.storeIdentifiers` leaking the internal Pigeon type instead of the public `StoreIdentifiers` sealed class

## 2.4.11

### Enhancements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ If you're using a custom PurchaseController (with either iOS StoreKit or Android
Here's an example of how you might do this:

```dart
import 'package:superwall_flutter/superwall_flutter.dart';
import 'package:superwallkit_flutter/superwallkit_flutter.dart';

Future<void> syncSubscriptionStatus() async {
// Get the device entitlements from your purchase controller
Expand Down Expand Up @@ -69,7 +69,7 @@ Future<List<String>> getDeviceEntitlements() async {
In addition to syncing the subscription status when purchasing and restoring, you'll need to sync it whenever `didRedeemLink(result)` is called:

```dart
import 'package:superwall_flutter/superwall_flutter.dart';
import 'package:superwallkit_flutter/superwallkit_flutter.dart';

class MySuperwallDelegate extends SuperwallDelegate {
@override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ When your app opens via the deep link, we will call the delegate method `willRed
At this point, you might wish to display a loading indicator in your app so the user knows that the purchase is being redeemed.

```dart
import 'package:superwall_flutter/superwall_flutter.dart';
import 'package:superwallkit_flutter/superwallkit_flutter.dart';
import 'package:flutter/material.dart';

class MySuperwallDelegate extends SuperwallDelegate {
Expand All @@ -88,20 +88,20 @@ You can manually dismiss the paywall at this point if needed, but note that the

### didRedeemLink

After receiving a response from the network, we will call `didRedeemLink(result)` with the result of redeeming the code. The result is a `RedemptionResult` which can be one of:
After receiving a response from the network, we will call `didRedeemLink(result)` with the result of redeeming the code. The result is a `RedemptionResult` subclass, which can be one of:

- `RedemptionResult` with `type: RedemptionResultType.success`: The redemption succeeded and contains information about the redeemed code.
- `RedemptionResult` with `type: RedemptionResultType.error`: An error occurred while redeeming. You can check the error message via the error parameter.
- `RedemptionResult` with `type: RedemptionResultType.expiredCode`: The code expired and contains information about whether a redemption email has been resent and an optional obfuscated email address.
- `RedemptionResult` with `type: RedemptionResultType.invalidCode`: The code that was redeemed was invalid.
- `RedemptionResult` with `type: RedemptionResultType.expiredSubscription`: The subscription that the code redeemed has expired.
- `RedemptionResultSuccess`: The redemption succeeded and contains information about the redeemed code.
- `RedemptionResultError`: An error occurred while redeeming. You can check the error message via the error parameter.
- `RedemptionResultExpiredCode`: The code expired and contains information about whether a redemption email has been resent and an optional obfuscated email address.
- `RedemptionResultInvalidCode`: The code that was redeemed was invalid.
- `RedemptionResultExpiredSubscription`: The subscription that the code redeemed has expired.

On network failure, the SDK will retry up to 6 times before returning an `error` `RedemptionResult` in `didRedeemLink(result)`.
On network failure, the SDK will retry up to 6 times before returning a `RedemptionResultError` in `didRedeemLink(result)`.

Here, you should remove any loading UI you added in `willRedeemLink` and show a message to the user based on the result. If a paywall is presented, it will be dismissed automatically.

```dart
import 'package:superwall_flutter/superwall_flutter.dart';
import 'package:superwallkit_flutter/superwallkit_flutter.dart';
import 'package:flutter/material.dart';

class MySuperwallDelegate extends SuperwallDelegate {
Expand All @@ -111,35 +111,45 @@ class MySuperwallDelegate extends SuperwallDelegate {

@override
void didRedeemLink(RedemptionResult result) {
switch (result.type) {
case RedemptionResultType.expiredCode:
switch (result) {
case RedemptionResultExpiredCode():
_showMessage('Expired Link');
print('[!] code expired: ${result.code}, ${result.expiredInfo}');
print('[!] code expired: ${result.code}, ${result.info}');
break;

case RedemptionResultType.error:
_showMessage(result.error?.message ?? 'An error occurred');
case RedemptionResultError():
_showMessage(result.error.message);
print('[!] error: ${result.code}, ${result.error}');
break;

case RedemptionResultType.expiredSubscription:
case RedemptionResultExpiredSubscription():
_showMessage('Expired Subscription');
print('[!] expired subscription: ${result.code}, ${result.redemptionInfo}');
break;

case RedemptionResultType.invalidCode:
case RedemptionResultInvalidCode():
_showMessage('Invalid Link');
print('[!] invalid code: ${result.code}');
break;

case RedemptionResultType.success:
final email = result.redemptionInfo?.purchaserInfo?.email;
case RedemptionResultSuccess():
final purchaserInfo = result.redemptionInfo.purchaserInfo;
final email = purchaserInfo.email;
if (email != null) {
Superwall.shared.setUserAttributes({'email': email});
_showMessage('Welcome, $email!');
} else {
_showMessage('Welcome!');
}

switch (purchaserInfo.storeIdentifiers) {
case StripeStoreIdentifiers(subscriptionIds: final ids):
print('[!] redeemed Stripe subscriptions: $ids');
case PaddleStoreIdentifiers(subscriptionIds: final ids):
print('[!] redeemed Paddle subscriptions: $ids');
case UnknownStoreIdentifiers(store: final store):
print('[!] redeemed purchase from $store');
}
break;
}
}
Expand Down
34 changes: 27 additions & 7 deletions content/docs/flutter/guides/web-checkout/using-revenuecat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ associate the RevenueCat customer with the Stripe subscription IDs returned from
by using the `didRedeemLink()` delegate method:

```dart
import 'package:superwall_flutter/superwall_flutter.dart';
import 'package:superwallkit_flutter/superwallkit_flutter.dart';
import 'package:purchases_flutter/purchases_flutter.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
Expand All @@ -39,9 +39,18 @@ class MySuperwallDelegate extends SuperwallDelegate {
print('[!] didRedeemLink: $result');
// Send Stripe IDs to RevenueCat to link purchases to the customer

// Get a list of subscription ids tied to the customer
final stripeSubscriptionIds = result.stripeSubscriptionIds;
if (stripeSubscriptionIds == null || stripeSubscriptionIds.isEmpty) {
if (result is! RedemptionResultSuccess) {
return;
}

final storeIdentifiers = result.redemptionInfo.purchaserInfo.storeIdentifiers;
if (storeIdentifiers is! StripeStoreIdentifiers) {
return;
}

// Get a list of Stripe subscription ids tied to the customer
final stripeSubscriptionIds = storeIdentifiers.subscriptionIds;
if (stripeSubscriptionIds.isEmpty) {
return;
}

Expand Down Expand Up @@ -152,8 +161,19 @@ class MySuperwallDelegate extends SuperwallDelegate {
}

Future<void> _handleRedemption(RedemptionResult result) async {
final stripeSubscriptionIds = result.stripeSubscriptionIds;
if (stripeSubscriptionIds == null || stripeSubscriptionIds.isEmpty) {
if (result is! RedemptionResultSuccess) {
print('[!] Redemption did not succeed');
return;
}

final storeIdentifiers = result.redemptionInfo.purchaserInfo.storeIdentifiers;
if (storeIdentifiers is! StripeStoreIdentifiers) {
print('[!] Redemption did not come from Stripe');
return;
}

final stripeSubscriptionIds = storeIdentifiers.subscriptionIds;
if (stripeSubscriptionIds.isEmpty) {
print('[!] No Stripe subscription IDs found');
return;
}
Expand Down Expand Up @@ -222,5 +242,5 @@ Remember to add the `http` package to your `pubspec.yaml`:
dependencies:
http: ^1.1.0
purchases_flutter: ^6.0.0
superwall_flutter: ^1.0.0
superwallkit_flutter: ^2.4.12
```
2 changes: 1 addition & 1 deletion content/docs/flutter/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ If you have feedback on any of our docs, please leave a rating and message at th

If you have any issues with the SDK, please [open an issue on GitHub](https://github.com/superwall/superwall-flutter/issues).

<SdkLatestVersion version="2.4.11" repoUrl="https://github.com/superwall/Superwall-Flutter" />
<SdkLatestVersion version="2.4.12" repoUrl="https://github.com/superwall/Superwall-Flutter" />
6 changes: 5 additions & 1 deletion content/docs/flutter/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
"sdk-reference/PaywallOptions",
"sdk-reference/PurchaseController",
"sdk-reference/PresentationResult",
"sdk-reference/RedemptionResult",
"sdk-reference/StoreIdentifiers",
"sdk-reference/StoreTransaction",
"sdk-reference/TransactionProduct",
"sdk-reference/CustomerInfo",
"sdk-reference/SubscriptionTransaction",
"sdk-reference/NonSubscriptionTransaction",
Expand All @@ -65,4 +69,4 @@
"[Troubleshooting](https://support.superwall.com/articles/6999598520-troubleshooting-flutter-sdk)",
"[Example App](https://github.com/superwall/Superwall-Flutter/tree/main/example)"
]
}
}
4 changes: 2 additions & 2 deletions content/docs/flutter/quickstart/install.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ To use Superwall in your Flutter project, add `superwallkit_flutter` as a depend

```yaml
dependencies:
superwallkit_flutter: ^2.0.5
superwallkit_flutter: ^2.4.12
```

After adding the dependency, run `dart pub get` in your terminal to fetch the package.
Expand Down Expand Up @@ -83,4 +83,4 @@ plugins {

To find the latest compatible versions, you can always check the [Gradle Plugin Release Notes](https://developer.android.com/build/releases/gradle-plugin).

<Check>**And you're done!** Now you're ready to configure the SDK 👇</Check>
<Check>**And you're done!** Now you're ready to configure the SDK 👇</Check>
8 changes: 8 additions & 0 deletions content/docs/flutter/sdk-reference/PaywallOptions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class PaywallOptions {
RestoreFailed restoreFailed = RestoreFailed();
bool shouldShowPurchaseFailureAlert = true;
bool shouldPreload = true;
Map<DeviceTier, bool> preloadDeviceOverrides = const {};
bool automaticallyDismiss = true;
TransactionBackgroundView transactionBackgroundView = TransactionBackgroundView.spinner;
bool shouldShowWebRestorationAlert = true;
Expand All @@ -32,6 +33,8 @@ class RestoreFailed {
}

enum TransactionBackgroundView { spinner, none }

enum DeviceTier { ultraLow, low, mid, high, ultraHigh, unknown }
```

## Parameters
Expand Down Expand Up @@ -72,6 +75,11 @@ enum TransactionBackgroundView { spinner, none }
description: "Preloads and caches trigger paywalls and products during SDK initialization.",
default: "true",
},
preloadDeviceOverrides: {
type: "Map<DeviceTier, bool>",
description: "Android only. Per-device-tier overrides for `shouldPreload`. Only the tiers you specify are overridden; the rest fall back to `shouldPreload`. Use this to disable preloading on low-end devices while keeping it enabled on mid/high-end devices.",
default: "const {}",
},
automaticallyDismiss: {
type: "bool",
description: "Automatically dismisses the paywall on successful purchase or restore.",
Expand Down
137 changes: 137 additions & 0 deletions content/docs/flutter/sdk-reference/RedemptionResult.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
title: "RedemptionResult"
description: "Result types returned when the Flutter SDK redeems a web checkout link."
---

## Purpose

Represents the result passed to [`didRedeemLink()`](/flutter/sdk-reference/SuperwallDelegate) after the SDK handles a web checkout redemption link.

## Signature

```dart
sealed class RedemptionResult {}

class RedemptionResultSuccess extends RedemptionResult {
final String code;
final RedemptionInfo redemptionInfo;
}

class RedemptionResultError extends RedemptionResult {
final String code;
final ErrorInfo error;
}

class RedemptionResultExpiredCode extends RedemptionResult {
final String code;
final ExpiredCodeInfo info;
}

class RedemptionResultInvalidCode extends RedemptionResult {
final String code;
}

class RedemptionResultExpiredSubscription extends RedemptionResult {
final String code;
final RedemptionInfo redemptionInfo;
}
```

## Types

<TypeTable
type={{
RedemptionResultSuccess: {
type: "RedemptionResult",
description: "The redemption succeeded. Inspect `redemptionInfo` for purchaser, entitlement, ownership, and paywall details.",
},
RedemptionResultError: {
type: "RedemptionResult",
description: "The SDK could not redeem the code. Inspect `error.message` for the failure reason.",
},
RedemptionResultExpiredCode: {
type: "RedemptionResult",
description: "The redemption code expired. Inspect `info.resent` and `info.obfuscatedEmail` for email resend details.",
},
RedemptionResultInvalidCode: {
type: "RedemptionResult",
description: "The redemption code was invalid.",
},
RedemptionResultExpiredSubscription: {
type: "RedemptionResult",
description: "The subscription associated with the redemption code expired.",
},
}}
/>

## Success Data

<TypeTable
type={{
"RedemptionInfo.ownership": {
type: "Ownership",
description: "Whether the redeemed code belongs to an app user or device.",
required: true,
},
"RedemptionInfo.purchaserInfo": {
type: "PurchaserInfo",
description: "Purchaser details, including store identifiers for Stripe, Paddle, or another store.",
required: true,
},
"RedemptionInfo.paywallInfo": {
type: "RedemptionPaywallInfo?",
description: "Paywall, placement, variant, and experiment details for the redeemed purchase.",
},
"RedemptionInfo.entitlements": {
type: "Set<Entitlement>",
description: "Entitlements granted by the redeemed purchase.",
required: true,
},
"PurchaserInfo.appUserId": {
type: "String",
description: "The app user ID of the purchaser.",
required: true,
},
"PurchaserInfo.email": {
type: "String?",
description: "The purchaser email address, when available.",
},
"PurchaserInfo.storeIdentifiers": {
type: "StoreIdentifiers",
typeDescriptionLink: "/flutter/sdk-reference/StoreIdentifiers",
description: "Public store identifier details for the redeemed purchase.",
required: true,
},
}}
/>

## Usage

```dart
void didRedeemLink(RedemptionResult result) {
switch (result) {
case RedemptionResultSuccess(redemptionInfo: final info):
final purchaser = info.purchaserInfo;
print('Redeemed for ${purchaser.appUserId}');
break;
case RedemptionResultError(error: final error):
print('Redemption failed: ${error.message}');
break;
case RedemptionResultExpiredCode(info: final info):
print('Code expired; resent email: ${info.resent}');
break;
case RedemptionResultInvalidCode():
print('Invalid code');
break;
case RedemptionResultExpiredSubscription():
print('Expired subscription');
break;
}
}
```

## Related

- [`StoreIdentifiers`](/flutter/sdk-reference/StoreIdentifiers)
- [`SuperwallDelegate`](/flutter/sdk-reference/SuperwallDelegate)
- [Post-Checkout Redirecting](/flutter/guides/web-checkout/post-checkout-redirecting)
Loading
Loading