| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:ui' show hashValues; |
| import 'package:collection/collection.dart'; |
| import 'package:json_annotation/json_annotation.dart'; |
| import 'enum_converters.dart'; |
| |
| // WARNING: Changes to `@JsonSerializable` classes need to be reflected in the |
| // below generated file. Run `flutter packages pub run build_runner watch` to |
| // rebuild and watch for further changes. |
| part 'sk_product_wrapper.g.dart'; |
| |
| /// Dart wrapper around StoreKit's [SKProductsResponse](https://developer.apple.com/documentation/storekit/skproductsresponse?language=objc). |
| /// |
| /// Represents the response object returned by [SKRequestMaker.startProductRequest]. |
| /// Contains information about a list of products and a list of invalid product identifiers. |
| @JsonSerializable() |
| class SkProductResponseWrapper { |
| /// Creates an [SkProductResponseWrapper] with the given product details. |
| SkProductResponseWrapper( |
| {required this.products, required this.invalidProductIdentifiers}); |
| |
| /// Constructing an instance from a map from the Objective-C layer. |
| /// |
| /// This method should only be used with `map` values returned by [SKRequestMaker.startProductRequest]. |
| factory SkProductResponseWrapper.fromJson(Map<String, dynamic> map) { |
| return _$SkProductResponseWrapperFromJson(map); |
| } |
| |
| /// Stores all matching successfully found products. |
| /// |
| /// One product in this list matches one valid product identifier passed to the [SKRequestMaker.startProductRequest]. |
| /// Will be empty if the [SKRequestMaker.startProductRequest] method does not pass any correct product identifier. |
| @JsonKey(defaultValue: <SKProductWrapper>[]) |
| final List<SKProductWrapper> products; |
| |
| /// Stores product identifiers in the `productIdentifiers` from [SKRequestMaker.startProductRequest] that are not recognized by the App Store. |
| /// |
| /// The App Store will not recognize a product identifier unless certain criteria are met. A detailed list of the criteria can be |
| /// found here https://developer.apple.com/documentation/storekit/skproductsresponse/1505985-invalidproductidentifiers?language=objc. |
| /// Will be empty if all the product identifiers are valid. |
| @JsonKey(defaultValue: <String>[]) |
| final List<String> invalidProductIdentifiers; |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(other, this)) { |
| return true; |
| } |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| final SkProductResponseWrapper typedOther = |
| other as SkProductResponseWrapper; |
| return DeepCollectionEquality().equals(typedOther.products, products) && |
| DeepCollectionEquality().equals( |
| typedOther.invalidProductIdentifiers, invalidProductIdentifiers); |
| } |
| |
| @override |
| int get hashCode => hashValues(this.products, this.invalidProductIdentifiers); |
| } |
| |
| /// Dart wrapper around StoreKit's [SKProductPeriodUnit](https://developer.apple.com/documentation/storekit/skproductperiodunit?language=objc). |
| /// |
| /// Used as a property in the [SKProductSubscriptionPeriodWrapper]. Minimum is a day and maximum is a year. |
| // The values of the enum options are matching the [SKProductPeriodUnit]'s values. Should there be an update or addition |
| // in the [SKProductPeriodUnit], this need to be updated to match. |
| enum SKSubscriptionPeriodUnit { |
| /// An interval lasting one day. |
| @JsonValue(0) |
| day, |
| |
| /// An interval lasting one month. |
| @JsonValue(1) |
| |
| /// An interval lasting one week. |
| week, |
| @JsonValue(2) |
| |
| /// An interval lasting one month. |
| month, |
| |
| /// An interval lasting one year. |
| @JsonValue(3) |
| year, |
| } |
| |
| /// Dart wrapper around StoreKit's [SKProductSubscriptionPeriod](https://developer.apple.com/documentation/storekit/skproductsubscriptionperiod?language=objc). |
| /// |
| /// A period is defined by a [numberOfUnits] and a [unit], e.g for a 3 months period [numberOfUnits] is 3 and [unit] is a month. |
| /// It is used as a property in [SKProductDiscountWrapper] and [SKProductWrapper]. |
| @JsonSerializable() |
| class SKProductSubscriptionPeriodWrapper { |
| /// Creates an [SKProductSubscriptionPeriodWrapper] for a `numberOfUnits`x`unit` period. |
| SKProductSubscriptionPeriodWrapper( |
| {required this.numberOfUnits, required this.unit}); |
| |
| /// Constructing an instance from a map from the Objective-C layer. |
| /// |
| /// This method should only be used with `map` values returned by [SKProductDiscountWrapper.fromJson] or [SKProductWrapper.fromJson]. |
| factory SKProductSubscriptionPeriodWrapper.fromJson( |
| Map<String, dynamic>? map) { |
| if (map == null) { |
| return SKProductSubscriptionPeriodWrapper( |
| numberOfUnits: 0, unit: SKSubscriptionPeriodUnit.day); |
| } |
| return _$SKProductSubscriptionPeriodWrapperFromJson(map); |
| } |
| |
| /// The number of [unit] units in this period. |
| /// |
| /// Must be greater than 0 if the object is valid. |
| @JsonKey(defaultValue: 0) |
| final int numberOfUnits; |
| |
| /// The time unit used to specify the length of this period. |
| @SKSubscriptionPeriodUnitConverter() |
| final SKSubscriptionPeriodUnit unit; |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(other, this)) { |
| return true; |
| } |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| final SKProductSubscriptionPeriodWrapper typedOther = |
| other as SKProductSubscriptionPeriodWrapper; |
| return typedOther.numberOfUnits == numberOfUnits && typedOther.unit == unit; |
| } |
| |
| @override |
| int get hashCode => hashValues(this.numberOfUnits, this.unit); |
| } |
| |
| /// Dart wrapper around StoreKit's [SKProductDiscountPaymentMode](https://developer.apple.com/documentation/storekit/skproductdiscountpaymentmode?language=objc). |
| /// |
| /// This is used as a property in the [SKProductDiscountWrapper]. |
| // The values of the enum options are matching the [SKProductDiscountPaymentMode]'s values. Should there be an update or addition |
| // in the [SKProductDiscountPaymentMode], this need to be updated to match. |
| enum SKProductDiscountPaymentMode { |
| /// Allows user to pay the discounted price at each payment period. |
| @JsonValue(0) |
| payAsYouGo, |
| |
| /// Allows user to pay the discounted price upfront and receive the product for the rest of time that was paid for. |
| @JsonValue(1) |
| payUpFront, |
| |
| /// User pays nothing during the discounted period. |
| @JsonValue(2) |
| freeTrail, |
| |
| /// Unspecified mode. |
| @JsonValue(-1) |
| unspecified, |
| } |
| |
| /// Dart wrapper around StoreKit's [SKProductDiscount](https://developer.apple.com/documentation/storekit/skproductdiscount?language=objc). |
| /// |
| /// It is used as a property in [SKProductWrapper]. |
| @JsonSerializable() |
| class SKProductDiscountWrapper { |
| /// Creates an [SKProductDiscountWrapper] with the given discount details. |
| SKProductDiscountWrapper( |
| {required this.price, |
| required this.priceLocale, |
| required this.numberOfPeriods, |
| required this.paymentMode, |
| required this.subscriptionPeriod}); |
| |
| /// Constructing an instance from a map from the Objective-C layer. |
| /// |
| /// This method should only be used with `map` values returned by [SKProductWrapper.fromJson]. |
| factory SKProductDiscountWrapper.fromJson(Map<String, dynamic> map) { |
| return _$SKProductDiscountWrapperFromJson(map); |
| } |
| |
| /// The discounted price, in the currency that is defined in [priceLocale]. |
| @JsonKey(defaultValue: '') |
| final String price; |
| |
| /// Includes locale information about the price, e.g. `$` as the currency symbol for US locale. |
| final SKPriceLocaleWrapper priceLocale; |
| |
| /// The object represent the discount period length. |
| /// |
| /// The value must be >= 0 if the object is valid. |
| @JsonKey(defaultValue: 0) |
| final int numberOfPeriods; |
| |
| /// The object indicates how the discount price is charged. |
| @SKProductDiscountPaymentModeConverter() |
| final SKProductDiscountPaymentMode paymentMode; |
| |
| /// The object represents the duration of single subscription period for the discount. |
| /// |
| /// The [subscriptionPeriod] of the discount is independent of the product's [subscriptionPeriod], |
| /// and their units and duration do not have to be matched. |
| final SKProductSubscriptionPeriodWrapper subscriptionPeriod; |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(other, this)) { |
| return true; |
| } |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| final SKProductDiscountWrapper typedOther = |
| other as SKProductDiscountWrapper; |
| return typedOther.price == price && |
| typedOther.priceLocale == priceLocale && |
| typedOther.numberOfPeriods == numberOfPeriods && |
| typedOther.paymentMode == paymentMode && |
| typedOther.subscriptionPeriod == subscriptionPeriod; |
| } |
| |
| @override |
| int get hashCode => hashValues(this.price, this.priceLocale, |
| this.numberOfPeriods, this.paymentMode, this.subscriptionPeriod); |
| } |
| |
| /// Dart wrapper around StoreKit's [SKProduct](https://developer.apple.com/documentation/storekit/skproduct?language=objc). |
| /// |
| /// A list of [SKProductWrapper] is returned in the [SKRequestMaker.startProductRequest] method, and |
| /// should be stored for use when making a payment. |
| @JsonSerializable() |
| class SKProductWrapper { |
| /// Creates an [SKProductWrapper] with the given product details. |
| SKProductWrapper({ |
| required this.productIdentifier, |
| required this.localizedTitle, |
| required this.localizedDescription, |
| required this.priceLocale, |
| this.subscriptionGroupIdentifier, |
| required this.price, |
| this.subscriptionPeriod, |
| this.introductoryPrice, |
| }); |
| |
| /// Constructing an instance from a map from the Objective-C layer. |
| /// |
| /// This method should only be used with `map` values returned by [SkProductResponseWrapper.fromJson]. |
| factory SKProductWrapper.fromJson(Map<String, dynamic> map) { |
| return _$SKProductWrapperFromJson(map); |
| } |
| |
| /// The unique identifier of the product. |
| @JsonKey(defaultValue: '') |
| final String productIdentifier; |
| |
| /// The localizedTitle of the product. |
| /// |
| /// It is localized based on the current locale. |
| @JsonKey(defaultValue: '') |
| final String localizedTitle; |
| |
| /// The localized description of the product. |
| /// |
| /// It is localized based on the current locale. |
| @JsonKey(defaultValue: '') |
| final String localizedDescription; |
| |
| /// Includes locale information about the price, e.g. `$` as the currency symbol for US locale. |
| final SKPriceLocaleWrapper priceLocale; |
| |
| /// The subscription group identifier. |
| /// |
| /// If the product is not a subscription, the value is `null`. |
| /// |
| /// A subscription group is a collection of subscription products. |
| /// Check [SubscriptionGroup](https://developer.apple.com/app-store/subscriptions/) for more details about subscription group. |
| final String? subscriptionGroupIdentifier; |
| |
| /// The price of the product, in the currency that is defined in [priceLocale]. |
| @JsonKey(defaultValue: '') |
| final String price; |
| |
| /// The object represents the subscription period of the product. |
| /// |
| /// Can be [null] is the product is not a subscription. |
| final SKProductSubscriptionPeriodWrapper? subscriptionPeriod; |
| |
| /// The object represents the duration of single subscription period. |
| /// |
| /// This is only available if you set up the introductory price in the App Store Connect, otherwise the value is `null`. |
| /// Programmer is also responsible to determine if the user is eligible to receive it. See https://developer.apple.com/documentation/storekit/in-app_purchase/offering_introductory_pricing_in_your_app?language=objc |
| /// for more details. |
| /// The [subscriptionPeriod] of the discount is independent of the product's [subscriptionPeriod], |
| /// and their units and duration do not have to be matched. |
| final SKProductDiscountWrapper? introductoryPrice; |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(other, this)) { |
| return true; |
| } |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| final SKProductWrapper typedOther = other as SKProductWrapper; |
| return typedOther.productIdentifier == productIdentifier && |
| typedOther.localizedTitle == localizedTitle && |
| typedOther.localizedDescription == localizedDescription && |
| typedOther.priceLocale == priceLocale && |
| typedOther.subscriptionGroupIdentifier == subscriptionGroupIdentifier && |
| typedOther.price == price && |
| typedOther.subscriptionPeriod == subscriptionPeriod && |
| typedOther.introductoryPrice == introductoryPrice; |
| } |
| |
| @override |
| int get hashCode => hashValues( |
| this.productIdentifier, |
| this.localizedTitle, |
| this.localizedDescription, |
| this.priceLocale, |
| this.subscriptionGroupIdentifier, |
| this.price, |
| this.subscriptionPeriod, |
| this.introductoryPrice); |
| } |
| |
| /// Object that indicates the locale of the price |
| /// |
| /// It is a thin wrapper of [NSLocale](https://developer.apple.com/documentation/foundation/nslocale?language=objc). |
| // TODO(cyanglaz): NSLocale is a complex object, want to see the actual need of getting this expanded. |
| // Matching android to only get the currencySymbol for now. |
| // https://github.com/flutter/flutter/issues/26610 |
| @JsonSerializable() |
| class SKPriceLocaleWrapper { |
| /// Creates a new price locale for `currencySymbol` and `currencyCode`. |
| SKPriceLocaleWrapper( |
| {required this.currencySymbol, required this.currencyCode}); |
| |
| /// Constructing an instance from a map from the Objective-C layer. |
| /// |
| /// This method should only be used with `map` values returned by [SKProductWrapper.fromJson] and [SKProductDiscountWrapper.fromJson]. |
| factory SKPriceLocaleWrapper.fromJson(Map<String, dynamic>? map) { |
| if (map == null) { |
| return SKPriceLocaleWrapper(currencyCode: '', currencySymbol: ''); |
| } |
| return _$SKPriceLocaleWrapperFromJson(map); |
| } |
| |
| ///The currency symbol for the locale, e.g. $ for US locale. |
| @JsonKey(defaultValue: '') |
| final String currencySymbol; |
| |
| ///The currency code for the locale, e.g. USD for US locale. |
| @JsonKey(defaultValue: '') |
| final String currencyCode; |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(other, this)) { |
| return true; |
| } |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| final SKPriceLocaleWrapper typedOther = other as SKPriceLocaleWrapper; |
| return typedOther.currencySymbol == currencySymbol && |
| typedOther.currencyCode == currencyCode; |
| } |
| |
| @override |
| int get hashCode => hashValues(this.currencySymbol, this.currencyCode); |
| } |