diff --git a/Framework/Info.plist b/Framework/Info.plist
index b7b3ddc7..8b984030 100644
--- a/Framework/Info.plist
+++ b/Framework/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 5.12.6
+ 5.13.0
CFBundleSignature
????
CFBundleVersion
diff --git a/Qonversion.podspec b/Qonversion.podspec
index 43762146..d5f1f3a0 100644
--- a/Qonversion.podspec
+++ b/Qonversion.podspec
@@ -3,7 +3,7 @@ Pod::Spec.new do |s|
idfa_exclude_files = ['Sources/Qonversion/IDFA']
s.name = 'Qonversion'
s.swift_version = '5.5'
- s.version = '5.12.6'
+ s.version = '5.13.0'
s.summary = 'qonversion.io'
s.description = <<-DESC
Deep Analytics for iOS Subscriptions
diff --git a/Sources/Qonversion/Public/QONConfiguration.m b/Sources/Qonversion/Public/QONConfiguration.m
index 06f6c848..01e5580f 100644
--- a/Sources/Qonversion/Public/QONConfiguration.m
+++ b/Sources/Qonversion/Public/QONConfiguration.m
@@ -9,7 +9,7 @@
#import "QONConfiguration.h"
#import "QNAPIConstants.h"
-static NSString *const kSDKVersion = @"5.12.6";
+static NSString *const kSDKVersion = @"5.13.0";
@interface QONConfiguration ()
diff --git a/Sources/Qonversion/Public/QONPromotionalOffer.h b/Sources/Qonversion/Public/QONPromotionalOffer.h
index d81b8cda..7f0f772d 100644
--- a/Sources/Qonversion/Public/QONPromotionalOffer.h
+++ b/Sources/Qonversion/Public/QONPromotionalOffer.h
@@ -12,7 +12,7 @@
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Qonversion.PromotionalOffer)
-API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), visionos(1.0))
+API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), tvos(12.2), visionos(1.0))
@interface QONPromotionalOffer : NSObject
@property (nonatomic, strong) SKProductDiscount *productDiscount;
diff --git a/Sources/Qonversion/Public/QONPurchaseOptions.h b/Sources/Qonversion/Public/QONPurchaseOptions.h
index 180a7b4d..5a347af4 100644
--- a/Sources/Qonversion/Public/QONPurchaseOptions.h
+++ b/Sources/Qonversion/Public/QONPurchaseOptions.h
@@ -8,6 +8,8 @@
#import
+@class QONPromotionalOffer;
+
NS_ASSUME_NONNULL_BEGIN
NS_SWIFT_NAME(Qonversion.PurchaseOptions)
@@ -22,6 +24,9 @@ NS_SWIFT_NAME(Qonversion.PurchaseOptions)
// Context keys associated with a purchase. Use this field to associate a purchase with a concrete remote config.
@property (nonatomic, copy, nullable) NSArray *contextKeys;
+// Promo offer details. Use to make a purchase with a promo offer.
+@property (nonatomic, strong, nullable) QONPromotionalOffer *promoOffer API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), tvos(12.2), visionos(1.0));
+
/**
Initialize purchase options with quantity.
@param quantity quantity of product purchasing. Use for consumable in-app products.
@@ -44,6 +49,22 @@ NS_SWIFT_NAME(Qonversion.PurchaseOptions)
*/
- (instancetype)initWithContextKeys:(NSArray * _Nullable)contextKeys NS_SWIFT_UNAVAILABLE("Use swift style initializer instead.");
+/**
+ Initialize purchase options with quantity, context keys, and promo offer details.
+ @param quantity quantity of product purchasing. Use for consumable in-app products.
+ @param contextKeys context keys associated with a purchase. Use this field to associate a purchase with a concrete remote config.
+ @param promoOffer promo offer details.
+ @return QONPurchaseOptions instance
+ */
+- (instancetype)initWithQuantity:(NSInteger)quantity contextKeys:(NSArray * _Nullable)contextKeys promoOffer:(QONPromotionalOffer * _Nullable)promoOffer API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), tvos(12.2), visionos(1.0)) NS_SWIFT_UNAVAILABLE("Use swift style initializer instead.");
+
+/**
+ Initialize purchase options with promo offer details.
+ @param promoOffer promo offer details.
+ @return QONPurchaseOptions instance
+ */
+- (instancetype)initWithPromoOffer:(QONPromotionalOffer * _Nullable)promoOffer API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), tvos(12.2), visionos(1.0)) NS_SWIFT_UNAVAILABLE("Use swift style initializer instead.");
+
@end
NS_ASSUME_NONNULL_END
diff --git a/Sources/Qonversion/Public/QONPurchaseOptions.m b/Sources/Qonversion/Public/QONPurchaseOptions.m
index 62199de6..85855a88 100644
--- a/Sources/Qonversion/Public/QONPurchaseOptions.m
+++ b/Sources/Qonversion/Public/QONPurchaseOptions.m
@@ -29,6 +29,22 @@ - (instancetype)initWithQuantity:(NSInteger)quantity contextKeys:(NSArray * _Nullable)contextKeys promoOffer:(QONPromotionalOffer * _Nullable)promoOffer {
+ self = [super init];
+
+ if (self) {
+ _quantity = quantity;
+ _contextKeys = contextKeys;
+ _promoOffer = promoOffer;
+ }
+
+ return self;
+}
+
+- (instancetype)initWithPromoOffer:(QONPromotionalOffer *)promoOffer {
+ return [self initWithQuantity:1 contextKeys:nil promoOffer:promoOffer];
+}
+
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
diff --git a/Sources/Qonversion/Public/QONStoreKit2PurchaseModel.h b/Sources/Qonversion/Public/QONStoreKit2PurchaseModel.h
index c1c315ef..6f8531b3 100644
--- a/Sources/Qonversion/Public/QONStoreKit2PurchaseModel.h
+++ b/Sources/Qonversion/Public/QONStoreKit2PurchaseModel.h
@@ -25,6 +25,12 @@ NS_SWIFT_NAME(Qonversion.StoreKit2PurchaseModel)
@property (nonatomic, copy, nullable) NSString *introductoryPeriodUnit;
@property (nonatomic, copy, nullable) NSString *introductoryPeriodNumberOfUnits;
@property (nonatomic, copy, nullable) NSString *introductoryPaymentMode;
+@property (nonatomic, copy, nullable) NSString *promoOfferId;
+@property (nonatomic, copy, nullable) NSString *promoOfferPrice;
+@property (nonatomic, copy, nullable) NSString *promoOfferNumberOfPeriods;
+@property (nonatomic, copy, nullable) NSString *promoOfferPeriodUnit;
+@property (nonatomic, copy, nullable) NSString *promoOfferPeriodNumberOfUnits;
+@property (nonatomic, copy, nullable) NSString *promoOfferPaymentMode;
@property (nonatomic, copy, nullable) NSString *storefrontCountryCode;
@end
diff --git a/Sources/Qonversion/Public/QONStoreKit2PurchaseModel.m b/Sources/Qonversion/Public/QONStoreKit2PurchaseModel.m
index cf8dc163..15efa688 100644
--- a/Sources/Qonversion/Public/QONStoreKit2PurchaseModel.m
+++ b/Sources/Qonversion/Public/QONStoreKit2PurchaseModel.m
@@ -10,4 +10,31 @@
@implementation QONStoreKit2PurchaseModel
+- (NSString *)description {
+ NSMutableString *description = [NSMutableString stringWithFormat:@"<%@: ", NSStringFromClass([self class])];
+
+ [description appendFormat:@"productId=%@,\n", self.productId];
+ [description appendFormat:@"price=%@,\n", self.price];
+ [description appendFormat:@"currency=%@,\n", self.currency];
+ [description appendFormat:@"transactionId=%@,\n", self.transactionId];
+ [description appendFormat:@"originalTransactionId=%@,\n", self.originalTransactionId];
+ [description appendFormat:@"subscriptionPeriodUnit=%@,\n", self.subscriptionPeriodUnit];
+ [description appendFormat:@"subscriptionPeriodNumberOfUnits=%@,\n", self.subscriptionPeriodNumberOfUnits];
+ [description appendFormat:@"introductoryPrice=%@,\n", self.introductoryPrice];
+ [description appendFormat:@"introductoryNumberOfPeriods=%@,\n", self.introductoryNumberOfPeriods];
+ [description appendFormat:@"introductoryPeriodUnit=%@,\n", self.introductoryPeriodUnit];
+ [description appendFormat:@"introductoryPeriodNumberOfUnits=%@,\n", self.introductoryPeriodNumberOfUnits];
+ [description appendFormat:@"introductoryPaymentMode=%@,\n", self.introductoryPaymentMode];
+ [description appendFormat:@"promoOfferId=%@,\n", self.promoOfferId];
+ [description appendFormat:@"promoOfferPrice=%@,\n", self.promoOfferPrice];
+ [description appendFormat:@"promoOfferNumberOfPeriods=%@,\n", self.promoOfferNumberOfPeriods];
+ [description appendFormat:@"promoOfferPeriodUnit=%@,\n", self.promoOfferPeriodUnit];
+ [description appendFormat:@"promoOfferPeriodNumberOfUnits=%@,\n", self.promoOfferPeriodNumberOfUnits];
+ [description appendFormat:@"promoOfferPaymentMode=%@,\n", self.promoOfferPaymentMode];
+ [description appendFormat:@"storefrontCountryCode=%@,\n", self.storefrontCountryCode];
+ [description appendString:@">"];
+
+ return [description copy];
+}
+
@end
diff --git a/Sources/Qonversion/Public/QONTransaction.h b/Sources/Qonversion/Public/QONTransaction.h
index 6870d90d..8fc18996 100644
--- a/Sources/Qonversion/Public/QONTransaction.h
+++ b/Sources/Qonversion/Public/QONTransaction.h
@@ -63,6 +63,11 @@ NS_SWIFT_NAME(Qonversion.Transaction)
*/
@property (nonatomic, strong, nullable) NSDate *transactionRevocationDate;
+/**
+ The identifier for the promotional offer if this transaction was made using it.
+ */
+@property (nonatomic, copy, nullable) NSString *promoOfferId;
+
/**
Environment of the transaction.
*/
diff --git a/Sources/Qonversion/Public/QONTransaction.m b/Sources/Qonversion/Public/QONTransaction.m
index db881d3f..f02ed947 100644
--- a/Sources/Qonversion/Public/QONTransaction.m
+++ b/Sources/Qonversion/Public/QONTransaction.m
@@ -16,6 +16,7 @@ - (instancetype)initWithOriginalTransactionId:(NSString *)originalTransactionId
transactionDate:(NSDate *)transactionDate
expirationDate:(NSDate *)expirationDate
transactionRevocationDate:(NSDate *)transactionRevocationDate
+ promoOfferId:(NSString *)promoOfferId
environment:(QONTransactionEnvironment)environment
ownershipType:(QONTransactionOwnershipType)ownershipType
type:(QONTransactionType)type {
@@ -28,6 +29,7 @@ - (instancetype)initWithOriginalTransactionId:(NSString *)originalTransactionId
_transactionDate = transactionDate;
_expirationDate = expirationDate;
_transactionRevocationDate = transactionRevocationDate;
+ _promoOfferId = promoOfferId;
_environment = environment;
_ownershipType = ownershipType;
_type = type;
@@ -45,6 +47,7 @@ - (instancetype)initWithCoder:(NSCoder *)coder {
_transactionDate = [coder decodeObjectForKey:NSStringFromSelector(@selector(transactionDate))];
_expirationDate = [coder decodeObjectForKey:NSStringFromSelector(@selector(expirationDate))];
_transactionRevocationDate = [coder decodeObjectForKey:NSStringFromSelector(@selector(transactionRevocationDate))];
+ _promoOfferId = [coder decodeObjectForKey:NSStringFromSelector(@selector(promoOfferId))];
_environment = [coder decodeIntegerForKey:NSStringFromSelector(@selector(environment))];
_ownershipType = [coder decodeIntegerForKey:NSStringFromSelector(@selector(ownershipType))];
_type = [coder decodeIntegerForKey:NSStringFromSelector(@selector(type))];
@@ -59,6 +62,7 @@ - (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:_transactionDate forKey:NSStringFromSelector(@selector(transactionDate))];
[coder encodeObject:_expirationDate forKey:NSStringFromSelector(@selector(expirationDate))];
[coder encodeObject:_transactionRevocationDate forKey:NSStringFromSelector(@selector(transactionRevocationDate))];
+ [coder encodeObject:_promoOfferId forKey:NSStringFromSelector(@selector(promoOfferId))];
[coder encodeInteger:_environment forKey:NSStringFromSelector(@selector(environment))];
[coder encodeInteger:_ownershipType forKey:NSStringFromSelector(@selector(ownershipType))];
[coder encodeInteger:_type forKey:NSStringFromSelector(@selector(type))];
@@ -72,6 +76,7 @@ - (NSString *)description {
[description appendFormat:@"transactionDate=%@,\n", self.transactionDate];
[description appendFormat:@"expirationDate=%@,\n", self.expirationDate];
[description appendFormat:@"transactionRevocationDate=%@,\n", self.transactionRevocationDate];
+ [description appendFormat:@"promoOfferId=%@,\n", self.promoOfferId];
[description appendFormat:@"environment=%@ (enum value = %li),\n", [self prettyEnvironment], (long) self.environment];
[description appendFormat:@"ownershipType=%@ (enum value = %li),\n", [self prettyOwnershipType], (long) self.ownershipType];
[description appendFormat:@"type=%@ (enum value = %li),\n", [self prettyType], (long) self.type];
diff --git a/Sources/Qonversion/Public/Qonversion.h b/Sources/Qonversion/Public/Qonversion.h
index 35c923e4..59ff6a33 100644
--- a/Sources/Qonversion/Public/Qonversion.h
+++ b/Sources/Qonversion/Public/Qonversion.h
@@ -132,6 +132,18 @@ static NSString *const QonversionApiErrorDomain = @"com.qonversion.io.api";
*/
- (void)attribution:(NSDictionary *)data fromProvider:(QONAttributionProvider)provider DEPRECATED_MSG_ATTRIBUTE("This function shouldn't be called anymore. All attribution logic continues to work as usual.");
+/**
+ Retrieve the promotional offer for the product if it exists.
+ Make sure to call this function before displaying product details to the user.
+ The generated signature for the promotional offer is valid for a single transaction.
+ If the purchase fails, you need to call this function again to obtain a new promotional offer signature.
+ Use this signature to complete the purchase through the purchase function, along with the purchase options object.
+ @param product - product you want to purchase.
+ @param discount - discount to create promotional offer signature.
+ @param completion - completion block that will be called when response is received.
+ */
+- (void)getPromotionalOfferForProduct:(QONProduct * _Nonnull)product discount:(SKProductDiscount * _Nonnull)discount completion:(nonnull QONPromotionalOfferCompletionHandler)completion API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), tvos(12.2), visionos(1.0));
+
/**
Check user entitlements
@param completion Completion block that includes entitlements dictionary and error
diff --git a/Sources/Qonversion/Public/Qonversion.m b/Sources/Qonversion/Public/Qonversion.m
index 506afe75..4f436634 100644
--- a/Sources/Qonversion/Public/Qonversion.m
+++ b/Sources/Qonversion/Public/Qonversion.m
@@ -244,13 +244,10 @@ - (void)handlePurchases:(NSArray *)purchasesInfo co
[self.productCenterManager handlePurchases:purchasesInfo completion:completion];
}
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmissing-declarations"
-NS_SWIFT_NAME(getPromotionalOfferForProduct(product: discount: completion:));
-- (void)getPromotionalOfferForProduct:(QONProduct * _Nonnull)product discount:(SKProductDiscount * _Nonnull)discount completion:(nonnull QONPromotionalOfferCompletionHandler)completion API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), visionos(1.0)) {
+- (void)getPromotionalOfferForProduct:(QONProduct * _Nonnull)product discount:(SKProductDiscount * _Nonnull)discount completion:(nonnull QONPromotionalOfferCompletionHandler)completion {
[self.productCenterManager getPromotionalOfferForProduct:product discount:discount completion:completion];
}
-#pragma GCC diagnostic pop
+
- (BOOL)isFallbackFileAccessible {
QONFallbackObject *fallbackData = [self.fallbackService obtainFallbackData];
diff --git a/Sources/Qonversion/Qonversion/Constants/QNAPIConstants/QNAPIConstants.h b/Sources/Qonversion/Qonversion/Constants/QNAPIConstants/QNAPIConstants.h
index 80a8d3ea..3710a570 100644
--- a/Sources/Qonversion/Qonversion/Constants/QNAPIConstants/QNAPIConstants.h
+++ b/Sources/Qonversion/Qonversion/Constants/QNAPIConstants/QNAPIConstants.h
@@ -11,7 +11,7 @@
extern NSString *const kAPIBase;
extern NSString *const kInitEndpoint;
extern NSString *const kPurchaseEndpoint;
-extern NSString *const kGetPromoOfferDetailsEndpoint;
+extern NSString *const kPostPromoOfferDetailsEndpoint;
extern NSString *const kProductsEndpoint;
extern NSString *const kPropertiesEndpoint;
extern NSString *const kActionPointsEndpointFormat;
diff --git a/Sources/Qonversion/Qonversion/Constants/QNAPIConstants/QNAPIConstants.m b/Sources/Qonversion/Qonversion/Constants/QNAPIConstants/QNAPIConstants.m
index e9dcf2ec..98089d35 100644
--- a/Sources/Qonversion/Qonversion/Constants/QNAPIConstants/QNAPIConstants.m
+++ b/Sources/Qonversion/Qonversion/Constants/QNAPIConstants/QNAPIConstants.m
@@ -13,8 +13,7 @@
NSString * const kInitEndpoint = @"v1/user/init";
NSString * const kPurchaseEndpoint = @"v1/user/purchase";
-// TODO: Update endpoint
-NSString * const kGetPromoOfferDetailsEndpoint = @"update_promo_offer_endpoint_here";
+NSString * const kPostPromoOfferDetailsEndpoint = @"v3/users/%@/offers/%@/signatures";
NSString * const kProductsEndpoint = @"v1/products/get";
NSString * const kPropertiesEndpoint = @"v3/users/%@/properties";
NSString * const kRemoteConfigEndpoint = @"v3/remote-config";
diff --git a/Sources/Qonversion/Qonversion/Core/QNRequestBuilder/QNRequestBuilder.h b/Sources/Qonversion/Qonversion/Core/QNRequestBuilder/QNRequestBuilder.h
index 6a3a8823..bbe81f89 100644
--- a/Sources/Qonversion/Qonversion/Core/QNRequestBuilder/QNRequestBuilder.h
+++ b/Sources/Qonversion/Qonversion/Core/QNRequestBuilder/QNRequestBuilder.h
@@ -30,6 +30,6 @@ typedef NS_ENUM(NSInteger, QONRequestType) {
- (NSURLRequest *)makeDetachUserFromExperimentRequest:(NSString *)experimentId userID:(NSString *)userID;
- (NSURLRequest *)makeAttachUserToRemoteConfigurationRequest:(NSString *)remoteConfigurationId userID:(NSString *)userID;
- (NSURLRequest *)makeDetachUserFromRemoteConfigurationRequest:(NSString *)remoteConfigurationId userID:(NSString *)userID;
-- (NSURLRequest *)makeGetPromotionalOfferRequestWithBody:(NSDictionary *)body;
+- (NSURLRequest *)makePostPromotionalOfferRequestWithBody:(NSDictionary *)body userId:(NSString *)userId offerId:(NSString *)offerId;
@end
diff --git a/Sources/Qonversion/Qonversion/Core/QNRequestBuilder/QNRequestBuilder.m b/Sources/Qonversion/Qonversion/Core/QNRequestBuilder/QNRequestBuilder.m
index 736469c3..6c5ea1f7 100644
--- a/Sources/Qonversion/Qonversion/Core/QNRequestBuilder/QNRequestBuilder.m
+++ b/Sources/Qonversion/Qonversion/Core/QNRequestBuilder/QNRequestBuilder.m
@@ -54,8 +54,8 @@ - (NSURLRequest *)makePurchaseRequestWith:(NSDictionary *)parameters {
return [self makeRequestWithDictBody:parameters baseURL:self.baseURL endpoint:kPurchaseEndpoint type:QONRequestTypePost];
}
-- (NSURLRequest *)makeGetPromotionalOfferRequestWithBody:(NSDictionary *)body {
- return [self makeRequestWithDictBody:body baseURL:self.baseURL endpoint:kGetPromoOfferDetailsEndpoint type:QONRequestTypePost];
+- (NSURLRequest *)makePostPromotionalOfferRequestWithBody:(NSDictionary *)body userId:(NSString *)userId offerId:(NSString *)offerId {
+ return [self makeRequestWithDictBody:body baseURL:self.baseURL endpoint:[NSString stringWithFormat:kPostPromoOfferDetailsEndpoint, userId, offerId] type:QONRequestTypePost];
}
- (NSURLRequest *)makeUserActionPointsRequestWith:(NSString *)parameter {
diff --git a/Sources/Qonversion/Qonversion/Core/QNRequestSerializer/QNRequestSerializer.h b/Sources/Qonversion/Qonversion/Core/QNRequestSerializer/QNRequestSerializer.h
index 58d032ec..67a48086 100644
--- a/Sources/Qonversion/Qonversion/Core/QNRequestSerializer/QNRequestSerializer.h
+++ b/Sources/Qonversion/Qonversion/Core/QNRequestSerializer/QNRequestSerializer.h
@@ -19,7 +19,6 @@ NS_ASSUME_NONNULL_BEGIN
receipt:(nullable NSString *)receipt;
- (NSDictionary *)promotionalOfferInfoForProduct:(QONProduct *)product
- discount:(SKProductDiscount *)productDiscount
identityId:(NSString *)identityId
receipt:(nullable NSString *)receipt API_AVAILABLE(ios(11.2), macos(10.13.2), visionos(1.0));
diff --git a/Sources/Qonversion/Qonversion/Core/QNRequestSerializer/QNRequestSerializer.m b/Sources/Qonversion/Qonversion/Core/QNRequestSerializer/QNRequestSerializer.m
index 568a5bc3..f6cad20f 100644
--- a/Sources/Qonversion/Qonversion/Core/QNRequestSerializer/QNRequestSerializer.m
+++ b/Sources/Qonversion/Qonversion/Core/QNRequestSerializer/QNRequestSerializer.m
@@ -65,6 +65,30 @@ - (NSDictionary *)purchaseData:(SKProduct *)product
}
}
+ if (@available(iOS 12.2, macOS 10.14.4, watchOS 6.2, visionOS 1.0, tvOS 12.2, *)) {
+ NSString *offerId = transaction.payment.paymentDiscount.identifier;
+ if (offerId.length > 0) {
+ NSMutableDictionary *promoOffer = [[NSMutableDictionary alloc] init];
+ SKProductDiscount *purchasedDiscount = nil;
+ for (SKProductDiscount *discount in product.discounts) {
+ if ([discount.identifier isEqualToString:offerId]) {
+ purchasedDiscount = discount;
+ }
+ }
+
+ if (purchasedDiscount) {
+ promoOffer[@"id"] = offerId;
+ promoOffer[@"value"] = purchasedDiscount.price.stringValue;
+ promoOffer[@"number_of_periods"] = @(purchasedDiscount.numberOfPeriods).stringValue;
+ promoOffer[@"period_number_of_units"] = @(purchasedDiscount.subscriptionPeriod.numberOfUnits).stringValue;
+ promoOffer[@"period_unit"] = @(purchasedDiscount.subscriptionPeriod.unit).stringValue;
+ promoOffer[@"payment_mode"] = @(purchasedDiscount.paymentMode).stringValue;
+
+ result[@"promo_offer"] = [promoOffer copy];
+ }
+ }
+ }
+
if (purchaseOptions.contextKeys.count > 0) {
purchaseDict[@"context_keys"] = purchaseOptions.contextKeys;
}
@@ -80,17 +104,13 @@ - (NSDictionary *)purchaseData:(SKProduct *)product
}
- (NSDictionary *)promotionalOfferInfoForProduct:(QONProduct *)product
- discount:(SKProductDiscount *)productDiscount
identityId:(NSString *)identityId
receipt:(nullable NSString *)receipt {
NSMutableDictionary *result = [NSMutableDictionary new];
- result[@"productIdentifier"] = product.storeID;
- if (@available(iOS 12.2, macOS 10.14.4, watchOS 6.2, tvOS 12.2, visionOS 1.0, *)) {
- result[@"discountIdentifier"] = productDiscount.identifier;
- }
- result[@"idetntityId"] = identityId;
- result[@"receipt"] = receipt;
+ result[@"product"] = product.storeID;
+ result[@"app_account_token"] = identityId;
+ result[@"app_bundle_id"] = [NSBundle mainBundle].bundleIdentifier;
return [result copy];
}
@@ -122,6 +142,16 @@ - (NSDictionary *)purchaseInfo:(QONStoreKit2PurchaseModel *)purchaseModel
result[@"introductory_offer"] = introOffer.count > 0 ? introOffer : nil;
+ NSMutableDictionary *promoOffer = [[NSMutableDictionary alloc] init];
+ promoOffer[@"id"] = purchaseModel.promoOfferId;
+ promoOffer[@"value"] = purchaseModel.promoOfferPrice;
+ promoOffer[@"number_of_periods"] = purchaseModel.promoOfferNumberOfPeriods;
+ promoOffer[@"period_number_of_units"] = purchaseModel.promoOfferPeriodNumberOfUnits;
+ promoOffer[@"period_unit"] = purchaseModel.promoOfferPeriodUnit;
+ promoOffer[@"payment_mode"] = purchaseModel.promoOfferPaymentMode;
+
+ result[@"promo_offer"] = [promoOffer copy];
+
purchaseDict[@"country"] = purchaseModel.storefrontCountryCode;
result[@"purchase"] = purchaseDict;
diff --git a/Sources/Qonversion/Qonversion/Main/QNProductCenterManager/QNProductCenterManager.h b/Sources/Qonversion/Qonversion/Main/QNProductCenterManager/QNProductCenterManager.h
index 2de1ca52..42a8cb4f 100644
--- a/Sources/Qonversion/Qonversion/Main/QNProductCenterManager/QNProductCenterManager.h
+++ b/Sources/Qonversion/Qonversion/Main/QNProductCenterManager/QNProductCenterManager.h
@@ -42,7 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)launch:(void (^)(QONLaunchResult * _Nullable result, NSError * _Nullable error))completion;
- (void)getPromotionalOfferForProduct:(QONProduct *)product
discount:(SKProductDiscount *)discount
- completion:(QONPromotionalOfferCompletionHandler)completion API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), visionos(1.0));
+ completion:(QONPromotionalOfferCompletionHandler)completion API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), tvos(12.2), visionos(1.0));
@end
diff --git a/Sources/Qonversion/Qonversion/Main/QNProductCenterManager/QNProductCenterManager.m b/Sources/Qonversion/Qonversion/Main/QNProductCenterManager/QNProductCenterManager.m
index 0a443f63..4977a0a2 100644
--- a/Sources/Qonversion/Qonversion/Main/QNProductCenterManager/QNProductCenterManager.m
+++ b/Sources/Qonversion/Qonversion/Main/QNProductCenterManager/QNProductCenterManager.m
@@ -446,8 +446,8 @@ - (void)processProductPurchase:(QONProduct *)product options:(QONPurchaseOptions
QONVERSION_LOG(@"Purchasing in process");
return;
}
-
- if (product && [_storeKitService purchase:product.storeID options:options]) {
+ NSString *identityId = [self.userInfoService obtainCustomIdentityUserID];
+ if (product && [_storeKitService purchase:product.storeID options:options identityId:identityId]) {
[self updatePurchaseOptions:options storeProductId:product.storeID];
self.purchasingBlocks[product.storeID] = completion;
@@ -1053,8 +1053,9 @@ - (void)getPromotionalOfferForProduct:(QONProduct *)product
__block __weak QNProductCenterManager *weakSelf = self;
[self.storeKitService receipt:^(NSString * receipt) {
NSString *identityId = [weakSelf.userInfoService obtainCustomIdentityUserID];
+ NSString *userId = [weakSelf.userInfoService obtainUserID];
- [self.apiClient getPromotionalOfferForProduct:product discount:discount identityId:identityId receipt:receipt completion:^(NSDictionary * _Nullable dict, NSError * _Nullable error) {
+ [self.apiClient getPromotionalOfferForProduct:product discount:discount userId:userId identityId:identityId receipt:receipt completion:^(NSDictionary * _Nullable dict, NSError * _Nullable error) {
if (error) {
run_block_on_main(completion, nil, error);
return;
diff --git a/Sources/Qonversion/Qonversion/Mappers/QNMapper/QNMapper.h b/Sources/Qonversion/Qonversion/Mappers/QNMapper/QNMapper.h
index b3118e72..5fa4bc3c 100644
--- a/Sources/Qonversion/Qonversion/Mappers/QNMapper/QNMapper.h
+++ b/Sources/Qonversion/Qonversion/Mappers/QNMapper/QNMapper.h
@@ -22,6 +22,6 @@
- (NSDictionary * _Nullable)mapProductsEntitlementsRelations:(NSDictionary * _Nullable)dict;
-+ (QONPromotionalOffer * _Nullable)mapPromoOffer:(NSDictionary * _Nullable)rawData productDiscount:(SKProductDiscount * _Nonnull)productDiscount mappingError:(NSError * _Nullable * _Nullable)error API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), visionos(1.0));
++ (QONPromotionalOffer * _Nullable)mapPromoOffer:(NSDictionary * _Nullable)rawData productDiscount:(SKProductDiscount * _Nonnull)productDiscount mappingError:(NSError * _Nullable * _Nullable)error API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), tvos(12.2), visionos(1.0));
@end
diff --git a/Sources/Qonversion/Qonversion/Mappers/QNMapper/QNMapper.m b/Sources/Qonversion/Qonversion/Mappers/QNMapper/QNMapper.m
index 1cdabebc..15460c76 100644
--- a/Sources/Qonversion/Qonversion/Mappers/QNMapper/QNMapper.m
+++ b/Sources/Qonversion/Qonversion/Mappers/QNMapper/QNMapper.m
@@ -72,17 +72,17 @@ + (QONPromotionalOffer * _Nullable)mapPromoOffer:(NSDictionary * _Nullable)rawDa
return nil;
}
- NSString *identifier = rawData[@"identifier"];
- NSString *keyIdentifier = rawData[@"keyIdentifier"];
- NSString *uuidString = rawData[@"uuid"];
- NSUUID *nonce = [[NSUUID alloc] initWithUUIDString:uuidString];
+ NSString *identifier = productDiscount.identifier;
+ NSString *keyIdentifier = rawData[@"key_identifier"];
+ NSString *nonceString = rawData[@"nonce"];
+ NSUUID *nonce = [[NSUUID alloc] initWithUUIDString:nonceString];
NSString *signature = rawData[@"signature"];
NSTimeInterval timestamp = [self mapInteger:rawData[@"timestamp"] orReturn:0];
timestamp = timestamp != 0 ? timestamp : [NSDate date].timeIntervalSince1970;
NSNumber *timestampNumber = [NSNumber numberWithDouble:timestamp];
- if (identifier.length == 0 || keyIdentifier.length == 0 || uuidString.length == 0 || signature.length == 0) {
+ if (identifier.length == 0 || keyIdentifier.length == 0 || nonceString.length == 0 || signature.length == 0) {
*error = [self promoOfferMappingError];
return nil;
@@ -276,6 +276,7 @@ + (QONTransaction *)mapEntitlementTransaction:(NSDictionary *)rawTransaction {
NSString *originalTransactionId = rawTransaction[@"original_transaction_id"];
NSString *transactionId = rawTransaction[@"transaction_id"];
NSString *offerCode = rawTransaction[@"offer_code"];
+ NSString *promoOfferId = rawTransaction[@"promo_offer_id"];
NSDate *transactionDate = [self mapDateFromSource:rawTransaction key:@"transaction_timestamp"];
NSDate *expirationDate = [self mapDateFromSource:rawTransaction key:@"expiration_timestamp"];
@@ -293,7 +294,7 @@ + (QONTransaction *)mapEntitlementTransaction:(NSDictionary *)rawTransaction {
NSNumber *transactionTypeNumber = transactionTypes[typeRaw];
QONTransactionType transactionType = transactionTypes ? transactionTypeNumber.integerValue : QONTransactionTypeUnknown;
- QONTransaction *transaction = [[QONTransaction alloc] initWithOriginalTransactionId:originalTransactionId transactionId:transactionId offerCode:offerCode transactionDate:transactionDate expirationDate:expirationDate transactionRevocationDate:transactionRevocationDate environment:environment ownershipType:ownershipType type:transactionType];
+ QONTransaction *transaction = [[QONTransaction alloc] initWithOriginalTransactionId:originalTransactionId transactionId:transactionId offerCode:offerCode transactionDate:transactionDate expirationDate:expirationDate transactionRevocationDate:transactionRevocationDate promoOfferId:promoOfferId environment:environment ownershipType:ownershipType type:transactionType];
return transaction;
}
diff --git a/Sources/Qonversion/Qonversion/Models/Protected/QONTransaction+Protected.h b/Sources/Qonversion/Qonversion/Models/Protected/QONTransaction+Protected.h
index d5bb56e7..56bb632a 100644
--- a/Sources/Qonversion/Qonversion/Models/Protected/QONTransaction+Protected.h
+++ b/Sources/Qonversion/Qonversion/Models/Protected/QONTransaction+Protected.h
@@ -18,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
transactionDate:(NSDate *)transactionDate
expirationDate:(NSDate *)expirationDate
transactionRevocationDate:(NSDate *)transactionRevocationDate
+ promoOfferId:(NSString *)promoOfferId
environment:(QONTransactionEnvironment)environment
ownershipType:(QONTransactionOwnershipType)ownershipType
type:(QONTransactionType)type;
diff --git a/Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.h b/Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.h
index 410998e4..96c88e76 100644
--- a/Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.h
+++ b/Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.h
@@ -64,9 +64,10 @@ NS_ASSUME_NONNULL_BEGIN
- (void)detachUserFromRemoteConfiguration:(NSString *)remoteConfigurationId completion:(QNAPIClientEmptyCompletionHandler)completion;
- (void)getPromotionalOfferForProduct:(QONProduct *)product
discount:(SKProductDiscount *)discount
+ userId:(NSString *)userId
identityId:(NSString *)identityId
receipt:(nullable NSString *)receipt
- completion:(QNAPIClientDictCompletionHandler)completion API_AVAILABLE(ios(11.2), macos(10.13.2), visionos(1.0));
+ completion:(QNAPIClientDictCompletionHandler)completion API_AVAILABLE(ios(12.2), macos(10.14.4), watchos(6.2), tvos(12.2), visionos(1.0));
- (NSURLRequest *)handlePurchase:(QONStoreKit2PurchaseModel *)purchaseInfo
receipt:(nullable NSString *)receipt
diff --git a/Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.m b/Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.m
index 470036d4..f374cf22 100644
--- a/Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.m
+++ b/Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.m
@@ -363,12 +363,13 @@ - (void)sendCrashReport:(NSDictionary *)data completion:(QNAPIClientEmptyComplet
- (void)getPromotionalOfferForProduct:(QONProduct *)product
discount:(SKProductDiscount *)discount
+ userId:(NSString *)userId
identityId:(NSString *)identityId
receipt:(nullable NSString *)receipt
completion:(QNAPIClientDictCompletionHandler)completion {
- NSDictionary *body = [self.requestSerializer promotionalOfferInfoForProduct:product discount:discount identityId:identityId receipt:receipt];
+ NSDictionary *body = [self.requestSerializer promotionalOfferInfoForProduct:product identityId:identityId receipt:receipt];
- NSURLRequest *request = [self.requestBuilder makeGetPromotionalOfferRequestWithBody:body];
+ NSURLRequest *request = [self.requestBuilder makePostPromotionalOfferRequestWithBody:body userId:userId offerId: discount.identifier];
[self processDictRequest:request completion:completion];
}
diff --git a/Sources/Qonversion/Qonversion/Services/QNStoreKitService/QNStoreKitService.h b/Sources/Qonversion/Qonversion/Services/QNStoreKitService/QNStoreKitService.h
index 02728aac..ae9eaa1e 100644
--- a/Sources/Qonversion/Qonversion/Services/QNStoreKitService/QNStoreKitService.h
+++ b/Sources/Qonversion/Qonversion/Services/QNStoreKitService/QNStoreKitService.h
@@ -15,7 +15,7 @@ typedef void(^QNStoreKitServiceReceiptFetchWithReceiptCompletionHandler)(NSStrin
- (instancetype)initWithDelegate:(id )delegate;
- (void)loadProducts:(NSSet *)products;
-- (SKProduct *)purchase:(NSString *)productID options:(QONPurchaseOptions * _Nullable)options;
+- (SKProduct *)purchase:(NSString *)productID options:(QONPurchaseOptions * _Nullable)options identityId:(NSString *)identityId;
- (void)purchaseProduct:(SKProduct *)product;
- (void)presentCodeRedemptionSheet;
- (void)restore;
diff --git a/Sources/Qonversion/Qonversion/Services/QNStoreKitService/QNStoreKitService.m b/Sources/Qonversion/Qonversion/Services/QNStoreKitService/QNStoreKitService.m
index 004458d1..1f2409ba 100644
--- a/Sources/Qonversion/Qonversion/Services/QNStoreKitService/QNStoreKitService.m
+++ b/Sources/Qonversion/Qonversion/Services/QNStoreKitService/QNStoreKitService.m
@@ -53,12 +53,11 @@ - (instancetype)init {
return self;
}
-- (SKProduct *)purchase:(NSString *)productID options:(QONPurchaseOptions * _Nullable)options {
+- (SKProduct *)purchase:(NSString *)productID options:(QONPurchaseOptions * _Nullable)options identityId:(NSString *)identityId {
SKProduct *skProduct = self->_products[productID];
if (skProduct) {
- // TODO: get promo offer from purchase options
- [self purchaseProduct:skProduct options:options];
+ [self purchaseProduct:skProduct options:options identityId:identityId];
return skProduct;
} else {
@@ -67,10 +66,10 @@ - (SKProduct *)purchase:(NSString *)productID options:(QONPurchaseOptions * _Nul
}
- (void)purchaseProduct:(SKProduct *)product {
- [self purchaseProduct:product options:nil];
+ [self purchaseProduct:product options:nil identityId:nil];
}
-- (void)purchaseProduct:(SKProduct *)product options:(QONPurchaseOptions * _Nullable)options {
+- (void)purchaseProduct:(SKProduct *)product options:(QONPurchaseOptions * _Nullable)options identityId:(NSString *)identityId {
@synchronized (self) {
self->_purchasingCurrently = product.productIdentifier;
}
@@ -81,6 +80,13 @@ - (void)purchaseProduct:(SKProduct *)product options:(QONPurchaseOptions * _Null
payment.quantity = options.quantity;
}
+ if (@available(iOS 12.2, macOS 10.14.4, watchOS 6.2, visionOS 1.0, tvOS 12.2, *)) {
+ if (options.promoOffer) {
+ payment.paymentDiscount = options.promoOffer.paymentDiscount;
+ payment.applicationUsername = identityId;
+ }
+ }
+
[[SKPaymentQueue defaultQueue] addPayment:[payment copy]];
}
@@ -246,7 +252,12 @@ - (void)paymentQueue:(nonnull SKPaymentQueue *)queue
for (SKPaymentTransaction *transaction in groupedTransactions) {
BOOL isTheSameProductId = [previousHandledProductId isEqualToString:transaction.payment.productIdentifier];
- if (!isTheSameProductId) {
+ if (@available(iOS 12.2, macOS 10.14.4, watchOS 6.2, visionOS 1.0, tvOS 12.2, *)) {
+ if (!isTheSameProductId || transaction.payment.paymentDiscount) {
+ [resultTransactions addObject:transaction];
+ previousHandledProductId = transaction.payment.productIdentifier;
+ }
+ } else if (!isTheSameProductId) {
[resultTransactions addObject:transaction];
previousHandledProductId = transaction.payment.productIdentifier;
}
diff --git a/Sources/Swift/Extensions.swift b/Sources/Swift/Extensions.swift
index b75bd4a4..fdd4afc8 100644
--- a/Sources/Swift/Extensions.swift
+++ b/Sources/Swift/Extensions.swift
@@ -16,4 +16,12 @@ extension Qonversion.PurchaseOptions {
self.contextKeys = contextKeys
}
+ @available(iOS 12.2, macOS 10.14.4, watchOS 6.2, visionOS 1.0, tvOS 12.2, *)
+ public convenience init(quantity: Int = 1, contextKeys: [String]? = nil, promoOffer: Qonversion.PromotionalOffer? = nil) {
+ self.init()
+ self.quantity = quantity
+ self.contextKeys = contextKeys
+ self.promoOffer = promoOffer
+ }
+
}
diff --git a/Sources/Swift/PurchasesMapper.swift b/Sources/Swift/PurchasesMapper.swift
index 56451dd7..62a4289f 100644
--- a/Sources/Swift/PurchasesMapper.swift
+++ b/Sources/Swift/PurchasesMapper.swift
@@ -10,7 +10,7 @@ import Foundation
import StoreKit
@_exported import Qonversion
-@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, *)
+@available(iOS 15.0, macOS 12.0, watchOS 8.0, tvOS 15.0, visionOS 1.0, *)
final class PurchasesMapper {
func map(transactions: [Transaction], with products:[Product]) async -> [Qonversion.StoreKit2PurchaseModel] {
var result: [Qonversion.StoreKit2PurchaseModel] = []
@@ -50,10 +50,23 @@ final class PurchasesMapper {
purchaseInfo.introductoryPeriodUnit = convert(periodUnit: introductoryOffer.period.unit)
purchaseInfo.introductoryPeriodNumberOfUnits = String(introductoryOffer.period.value)
purchaseInfo.introductoryPaymentMode = convert(paymentMode: introductoryOffer.paymentMode)
- purchaseInfo.storefrontCountryCode = await Storefront.current?.countryCode
}
}
+ if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) {
+ if let offer: Transaction.Offer = transaction.offer, offer.type == .promotional, let offerId = offer.id, let promoOffer: Product.SubscriptionOffer = product.subscription?.promotionalOffers.first(where: {$0.id == offerId}) {
+ purchaseInfo.promoOfferId = offerId
+ purchaseInfo.promoOfferPrice = "\(promoOffer.price)"
+ purchaseInfo.promoOfferNumberOfPeriods = String(promoOffer.periodCount)
+ purchaseInfo.promoOfferPeriodUnit = convert(periodUnit: promoOffer.period.unit)
+ purchaseInfo.promoOfferPeriodNumberOfUnits = String(promoOffer.period.value)
+ purchaseInfo.promoOfferPaymentMode = convert(paymentMode: promoOffer.paymentMode)
+
+ }
+ }
+
+ purchaseInfo.storefrontCountryCode = await Storefront.current?.countryCode
+
return purchaseInfo
}
diff --git a/Sources/Swift/QonversionSwift.swift b/Sources/Swift/QonversionSwift.swift
index 37bac26c..063d4611 100644
--- a/Sources/Swift/QonversionSwift.swift
+++ b/Sources/Swift/QonversionSwift.swift
@@ -22,7 +22,7 @@ public class QonversionSwift {
/// Contact us before you start using this function.
/// Call this function to sync purchases if you are using StoreKit2.
public func syncStoreKit2Purchases() {
- if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
+ if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) {
Task.init {
try? await storeKitService?.syncTransactions()
}
@@ -31,7 +31,7 @@ public class QonversionSwift {
/// Contact us before you start using this function.
/// Call this function to sync purchases if you are using StoreKit2.
- @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+ @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
public func syncStoreKit2Transactions() async {
try? await storeKitService?.syncTransactions()
}
@@ -39,7 +39,7 @@ public class QonversionSwift {
/// Call this function to sync StoreKit2 transaction with Qonversion.
/// - Parameters:
/// - transaction: StoreKit2 transaction
- @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+ @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
public func handleTransaction(_ transaction: Transaction) async {
try? await storeKitService?.handleTransaction(transaction)
}
@@ -47,7 +47,7 @@ public class QonversionSwift {
/// Call this function to sync StoreKit2 transactions with Qonversion.
/// - Parameters:
/// - transactions: an array of StoreKit2 transactions
- @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+ @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
public func handleTransactions(_ transactions: [Transaction]) async {
try? await storeKitService?.handleTransactions(transactions)
}
diff --git a/Sources/Swift/StoreKit2Service.swift b/Sources/Swift/StoreKit2Service.swift
index 3e80e624..fb97c55b 100644
--- a/Sources/Swift/StoreKit2Service.swift
+++ b/Sources/Swift/StoreKit2Service.swift
@@ -12,17 +12,17 @@ import StoreKit
protocol StoreKit2ServiceInterface {
- @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+ @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
func syncTransactions() async throws
- @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+ @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
func handleTransaction(_ transaction: Transaction) async throws
- @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+ @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
func handleTransactions(_ transactions: [Transaction]) async throws
}
-@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
+@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
public class StoreKit2Service: StoreKit2ServiceInterface {
let mapper = PurchasesMapper()
@@ -112,10 +112,18 @@ public class StoreKit2Service: StoreKit2ServiceInterface {
var previousHandledProductId = ""
for transaction in transactions {
- // here we detect another product purchase
- if previousHandledProductId != transaction.productID {
- result.append(transaction)
- previousHandledProductId = transaction.productID
+ if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) {
+ // here we detect another product purchase or offer available
+ if transaction.offer != nil || previousHandledProductId != transaction.productID {
+ result.append(transaction)
+ previousHandledProductId = transaction.productID
+ }
+ } else {
+ // here we detect another product purchase
+ if previousHandledProductId != transaction.productID {
+ result.append(transaction)
+ previousHandledProductId = transaction.productID
+ }
}
}
}
diff --git a/fastlane/report.xml b/fastlane/report.xml
index 921e0214..3ae26ea4 100644
--- a/fastlane/report.xml
+++ b/fastlane/report.xml
@@ -5,12 +5,12 @@
-
+
-
+