Skip to content

Commit 163cbf1

Browse files
Merge pull request #286 from qonversion/release/7.3.0
Release 7.3.0
2 parents 9d4c69a + cdd38a9 commit 163cbf1

21 files changed

+400
-54
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 7.3.0
2+
* Updated entitlements fields
3+
14
## 7.2.0
25
* Added attach/detach remote configuration functions
36

README.md

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
Qonversion
33
</h1>
44

5-
Qonversion is the data platform to power in-app subscription revenue growth.
5+
Qonversion - In-app subscription monetization: implement subscriptions and grow your app’s revenue with A/B experiments
66

7-
* fast in-app subscriptions implementation
8-
* back-end infrastructure to validate user receipts
9-
* manage cross-platform user access to paid content on your app
10-
* comprehensive subscription analytics
11-
* out-of-the-box integrations with the leading marketing, attribution, and product analytics platforms
12-
* push notifications and in-app messaging to win back lapsed subscribers
13-
* A/B Testing for in-app purchases
7+
* In-app subscription management SDK
8+
* API and webhooks to make your subscription data available where you need it
9+
* Seamless Stripe integration to enable cross-platform access management
10+
* Subscribers CRM with user-level transactions
11+
* Instant access to real-time subscription analytics
12+
* Built-in A/B experiments for subscription business model
1413

1514
<p align="center">
1615
<a href="https://qonversion.io"><img width="90%" src="https://qcdn3.sfo3.digitaloceanspaces.com/github/qonversion_platform.png">
@@ -21,50 +20,65 @@ Qonversion is the data platform to power in-app subscription revenue growth.
2120
[![MIT License](https://img.shields.io/cocoapods/l/Qonversion.svg?style=flat)](https://qonversion.io)
2221

2322

24-
## How It Works: Product Center
23+
## In-App Subscription Implementation & Management
2524

2625
<p align="center">
2726
<a href="https://documentation.qonversion.io/docs/integrations-overview"><img width="90%" src="https://user-images.githubusercontent.com/13959241/161107203-8ef3ecee-86be-47a2-ac57-b21d3da19339.png">
2827
</a>
2928
</p>
3029

31-
1. Application calls the purchase method of Qonversion SDK.
32-
2. Qonversion SDK communicates with StoreKit or Google Billing Client to make a purchase.
33-
3. If a purchase is successful, the SDK sends a request to Qonversion API for server-to-server purchase validation. Qonversion server receives accurate information on the in-app purchase status and user entitlements.
34-
4. SDK returns control to the application with a processing state.
30+
1. Qonversion SDK provides three simple methods to manage subscriptions:
31+
* Get in-app product details
32+
* Make purchases
33+
* Check subscription status to manage premium access
34+
2. Qonversion communicates with Apple or Google platforms both through SDK and server-side to process native in-app payments and keep subscription statuses up to date.
35+
3. You can use Qonversion webhooks and API in addition to SDK to get user-level data where you need it.
36+
37+
See the [quick start guide documentation](https://documentation.qonversion.io/docs/quickstart).
3538

3639
## Analytics
3740

38-
Monitor your in-app revenue metrics. Understand your customers and make better decisions with precise subscription revenue data.
41+
Qonversion provides advanced subscription analytics out-of-the-box. You can monitor real-time metrics from new users and trial-to-paid conversions to revenue, MRR, ARR, cohort retention and more. Understand your customers and make better decisions with precise subscription analytics.
3942

4043
<p align="center">
41-
<a href="https://documentation.qonversion.io/docs/analytics"><img width="90%" src="https://qonversion.io/img/screenshots/desktop/mobile_subscription_analytics.jpg">
44+
<a href="https://documentation.qonversion.io/docs/analytics"><img width="90%" src="https://files.readme.io/9a4fdf6-Analytics.png">
4245
</a>
4346
</p>
4447

45-
## Integrations
4648

47-
Send subscription data to your favorite platforms. Share your mobile and web subscription data using our powerful integrations.
49+
## A/B Experiments
50+
51+
Qonversion's A/B Experiments feature provides everything required to quickly launch paywall and other monetization experiments, analyze results and roll out winning versions without releasing a new app build. Qonversion A/B Experiments include:
52+
53+
* User segmentation by country, install date, app version, free/paying user
54+
* Traffic allocation
55+
* Advanced subscription analytics
56+
* Visualization of A/B experiments results
57+
* Statistical significance of the results
58+
* Roll out winning versions without app release with remote config
59+
4860

4961
<p align="center">
50-
<a href="https://documentation.qonversion.io/docs/integrations-overview"><img width="90%" src="https://qcdn3.sfo3.digitaloceanspaces.com/github/integrations.png">
62+
<a href="https://documentation.qonversion.io/docs/subscription-ab-testing"><img width="90%" src="https://qcdn3.sfo3.digitaloceanspaces.com/github/ab_tests.png">
5163
</a>
5264
</p>
5365

54-
## Personalized push notifications & in-app messaging
55-
56-
Qonversion allows sending automated, personalized push notifications and in-app messages initiated by in-app purchase events. This feature is designed to increase your app's revenue and retention, provide cancellation insights, reduce subscriber churn, and improve your subscribers' user experience.
66+
See more details [here](https://documentation.qonversion.io/docs/paywall-experiments).
5767

68+
## Integrations
5869

59-
See more in the [documentation](https://documentation.qonversion.io/docs/automations)
60-
![](https://qonversion.io/img/@2x/automation/in-app-constructor.gif)
61-
62-
## A/B Testing for in-app purchases
70+
Send user-level subscription data to your favorite platforms.
6371

64-
Boost conversion rates with paywalls and in-app purchases A/B testing. Find the best pricing and paywall variations. Be flexible to prove hypotheses without app releases.
72+
* Amplitude
73+
* Mixpanel
74+
* Appsflyer
75+
* Adjust
76+
* Singular
77+
* CleverTap
78+
* [All other integrations here](qonversion.io/integrations)
6579

6680
<p align="center">
67-
<a href="https://documentation.qonversion.io/docs/subscription-ab-testing"><img width="90%" src="https://user-images.githubusercontent.com/13959241/161716071-b30311b3-b60f-482d-a5d3-c40c1951253b.png">
81+
<a href="https://documentation.qonversion.io/docs/integrations-overview"><img width="90%", src="https://qcdn3.sfo3.digitaloceanspaces.com/github/integrations.png">
6882
</a>
6983
</p>
7084

@@ -75,18 +89,17 @@ Boost conversion rates with paywalls and in-app purchases A/B testing. Find the
7589
* **Track and increase your revenue.** Qonversion provides detailed real-time revenue analytics including cohort analysis, trial conversion rates, country segmentation, and much more.
7690
* **Integrations with the leading mobile platforms.** Qonversion allows sending data to platforms like AppsFlyer, Adjust, Branch, Tenjin, Facebook Ads, Amplitude, Mixpanel, and many others.
7791
* **Change promoted in-app products.** Change promoted in-app products anytime without app releases.
78-
* **Win back lapsed subscribers.** Qonversion allows sending highly targeted push notifications triggered by server-side subscription events. You can send special offers to users who just canceled a free trial or a subscription. Plus you can deliver in-app messages with a beautiful native design that you create in Qonversion.
7992
* **A/B test** and identify winning in-app purchases, subscriptions or paywals.
8093
* **Cross-device and cross-platform access management.** If you provide user authorization in your app, you can easily set Qonversion to provide premium access to authorized users across devices and operating systems.
81-
* **SDK caches the data.** Qonversion SDK caches purchase data including in-app products and permissions, so the user experience is not affected even with the slow or interrupting network connection.
94+
* **SDK caches the data.** Qonversion SDK caches purchase data including in-app products and entitlements, so the user experience is not affected even with the slow or interrupting network connection.
8295
* **Webhooks.** You can easily send all of the data to your server with Qonversion webhooks.
8396
* **Customer support.** You can always reach out to our customer support and get the help required.
8497

8598
Convinced? Let's go!
8699

87100
## Documentation
88101

89-
Check the [documentation](https://documentation.qonversion.io/docs/quickstart) to learn details on implementing and using Qonversion SDKs.
102+
Check the [full documentation](https://documentation.qonversion.io/docs/quickstart) to learn about implementation details and available features.
90103

91104
#### Help us improve the documentation
92105

@@ -113,4 +126,4 @@ Contact us via [issues on GitHub](https://github.com/qonversion/flutter-sdk/issu
113126

114127
## License
115128

116-
Qonversion SDK is available under the MIT license.
129+
Qonversion SDK is available under the MIT license.

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ android {
4343

4444
dependencies {
4545
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
46-
implementation "io.qonversion.sandwich:sandwich:3.2.0"
46+
implementation "io.qonversion.sandwich:sandwich:3.3.3"
4747
implementation 'com.google.code.gson:gson:2.9.0'
4848
}

android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/QonversionPlugin.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ class QonversionPlugin : MethodCallHandler, FlutterPlugin, ActivityAware {
216216
}
217217

218218
private fun checkEntitlements(result: Result) {
219-
qonversionSandwich.checkEntitlements(result.toResultListener())
219+
qonversionSandwich.checkEntitlements(result.toJsonResultListener())
220220
}
221221

222222
private fun restore(result: Result) {

android/src/main/kotlin/com/qonversion/flutter/sdk/qonversion_flutter_sdk/extenstions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal fun MethodChannel.Result.toPurchaseResultListener(): PurchaseResultList
3737
}
3838

3939
override fun onSuccess(data: Map<String, Any?>) {
40-
success(data)
40+
success(Gson().toJson(data))
4141
}
4242
}
4343
}

example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@
204204
TargetAttributes = {
205205
97C146ED1CF9000F007C117D = {
206206
CreatedOnToolsVersion = 7.3.1;
207-
DevelopmentTeam = MTVL2X9L7N;
207+
DevelopmentTeam = 5ZBNSPDUJ2;
208208
LastSwiftMigration = 1100;
209209
ProvisioningStyle = Automatic;
210210
};
@@ -436,7 +436,7 @@
436436
CODE_SIGN_IDENTITY = "Apple Development";
437437
CODE_SIGN_STYLE = Automatic;
438438
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
439-
DEVELOPMENT_TEAM = MTVL2X9L7N;
439+
DEVELOPMENT_TEAM = 5ZBNSPDUJ2;
440440
ENABLE_BITCODE = NO;
441441
FRAMEWORK_SEARCH_PATHS = (
442442
"$(inherited)",
@@ -449,7 +449,7 @@
449449
"$(inherited)",
450450
"$(PROJECT_DIR)/Flutter",
451451
);
452-
PRODUCT_BUNDLE_IDENTIFIER = com.qonversion.sample;
452+
PRODUCT_BUNDLE_IDENTIFIER = io.qonversion.sample;
453453
PRODUCT_NAME = "$(TARGET_NAME)";
454454
PROVISIONING_PROFILE_SPECIFIER = "";
455455
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -574,7 +574,7 @@
574574
CODE_SIGN_IDENTITY = "Apple Development";
575575
CODE_SIGN_STYLE = Automatic;
576576
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
577-
DEVELOPMENT_TEAM = MTVL2X9L7N;
577+
DEVELOPMENT_TEAM = 5ZBNSPDUJ2;
578578
ENABLE_BITCODE = NO;
579579
FRAMEWORK_SEARCH_PATHS = (
580580
"$(inherited)",
@@ -587,7 +587,7 @@
587587
"$(inherited)",
588588
"$(PROJECT_DIR)/Flutter",
589589
);
590-
PRODUCT_BUNDLE_IDENTIFIER = com.qonversion.sample;
590+
PRODUCT_BUNDLE_IDENTIFIER = io.qonversion.sample;
591591
PRODUCT_NAME = "$(TARGET_NAME)";
592592
PROVISIONING_PROFILE_SPECIFIER = "";
593593
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -607,7 +607,7 @@
607607
CODE_SIGN_IDENTITY = "Apple Development";
608608
CODE_SIGN_STYLE = Automatic;
609609
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
610-
DEVELOPMENT_TEAM = MTVL2X9L7N;
610+
DEVELOPMENT_TEAM = 5ZBNSPDUJ2;
611611
ENABLE_BITCODE = NO;
612612
FRAMEWORK_SEARCH_PATHS = (
613613
"$(inherited)",
@@ -620,7 +620,7 @@
620620
"$(inherited)",
621621
"$(PROJECT_DIR)/Flutter",
622622
);
623-
PRODUCT_BUNDLE_IDENTIFIER = com.qonversion.sample;
623+
PRODUCT_BUNDLE_IDENTIFIER = io.qonversion.sample;
624624
PRODUCT_NAME = "$(TARGET_NAME)";
625625
PROVISIONING_PROFILE_SPECIFIER = "";
626626
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

ios/Classes/SwiftQonversionPlugin.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ public class SwiftQonversionPlugin: NSObject, FlutterPlugin {
228228
}
229229

230230
private func checkEntitlements(_ result: @escaping FlutterResult) {
231-
qonversionSandwich?.checkEntitlements(getDefaultCompletion(result))
231+
qonversionSandwich?.checkEntitlements(getJsonCompletion(result))
232232
}
233233

234234
private func remoteConfig(_ result: @escaping FlutterResult) {
@@ -388,7 +388,15 @@ public class SwiftQonversionPlugin: NSObject, FlutterPlugin {
388388
return result(FlutterError.purchaseError(error))
389389
}
390390

391-
result(data)
391+
guard let data = data else {
392+
return result(nil)
393+
}
394+
395+
guard let jsonData = data.toJson() else {
396+
return result(FlutterError.serializationError)
397+
}
398+
399+
result(jsonData)
392400
}
393401
}
394402
}

ios/qonversion_flutter.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
1616
s.source_files = 'Classes/**/*'
1717
s.dependency 'Flutter'
1818
s.platform = :ios, '9.0'
19-
s.dependency "QonversionSandwich", "3.2.0"
19+
s.dependency "QonversionSandwich", "3.3.3"
2020

2121
# Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
2222
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }

lib/src/dto/entitlement.dart

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import 'package:json_annotation/json_annotation.dart';
22
import 'package:qonversion_flutter/src/dto/entitlement_source.dart';
33
import 'package:qonversion_flutter/src/dto/entitlement_renew_state.dart';
4+
import 'package:qonversion_flutter/src/dto/transaction.dart';
45
import 'package:qonversion_flutter/src/internal/mapper.dart';
56

7+
import 'entitlement_grant_type.dart';
8+
69
part 'entitlement.g.dart';
710

811
@JsonSerializable(createToJson: false)
@@ -47,6 +50,64 @@ class QEntitlement {
4750
)
4851
final DateTime? expirationDate;
4952

53+
/// Renews count for the entitlement. Renews count starts from the second paid subscription.
54+
/// For example, we have 20 transactions. One is the trial, and one is the first paid transaction after the trial.
55+
/// Renews count is equal to 18.
56+
@JsonKey(
57+
name: 'renewsCount',
58+
defaultValue: 0
59+
)
60+
final int renewsCount;
61+
62+
/// Trial start date.
63+
@JsonKey(
64+
name: 'trialStartTimestamp',
65+
fromJson: QMapper.dateTimeFromNullableSecondsTimestamp,
66+
)
67+
final DateTime? trialStartDate;
68+
69+
/// First purchase date.
70+
@JsonKey(
71+
name: 'firstPurchaseTimestamp',
72+
fromJson: QMapper.dateTimeFromNullableSecondsTimestamp,
73+
)
74+
final DateTime? firstPurchaseDate;
75+
76+
/// Last purchase date.
77+
@JsonKey(
78+
name: 'lastPurchaseTimestamp',
79+
fromJson: QMapper.dateTimeFromNullableSecondsTimestamp,
80+
)
81+
final DateTime? lastPurchaseDate;
82+
83+
/// Last activated offer code.
84+
@JsonKey(
85+
name: 'lastActivatedOfferCode'
86+
)
87+
final String? lastActivatedOfferCode;
88+
89+
/// Grant type of the entitlement.
90+
@JsonKey(
91+
name: 'grantType',
92+
unknownEnumValue: QEntitlementGrantType.purchase,
93+
fromJson: QMapper.grantTypeFromNullableValue
94+
)
95+
final QEntitlementGrantType grantType;
96+
97+
/// Auto-renew disable date.
98+
@JsonKey(
99+
name: 'autoRenewDisableTimestamp',
100+
fromJson: QMapper.dateTimeFromNullableSecondsTimestamp,
101+
)
102+
final DateTime? autoRenewDisableDate;
103+
104+
/// Array of the transactions that unlocked current entitlement.
105+
@JsonKey(
106+
name: 'transactions',
107+
fromJson: QMapper.transactionsFromNullableValue
108+
)
109+
final List<QTransaction> transactions;
110+
50111
/// Use for checking entitlement for current user.
51112
/// Pay attention, isActive == true does not mean that subscription is renewable.
52113
/// Subscription could be canceled, but the user could still have a entitlement
@@ -61,8 +122,15 @@ class QEntitlement {
61122
this.startedDate,
62123
this.expirationDate,
63124
this.isActive,
125+
this.renewsCount,
126+
this.trialStartDate,
127+
this.firstPurchaseDate,
128+
this.lastPurchaseDate,
129+
this.lastActivatedOfferCode,
130+
this.grantType,
131+
this.autoRenewDisableDate,
132+
this.transactions
64133
);
65134

66-
factory QEntitlement.fromJson(Map<String, dynamic> json) =>
67-
_$QEntitlementFromJson(json);
135+
factory QEntitlement.fromJson(Map<String, dynamic> json) => _$QEntitlementFromJson(json);
68136
}

lib/src/dto/entitlement.g.dart

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)