[iOS] check eligibility for trial

https://jira.mail.ru/browse/MAPSME-14171
This commit is contained in:
Alexander Boriskov 2020-07-23 22:49:01 +03:00 committed by Arsentiy Milchakov
parent c885b11710
commit d09ed36a23
16 changed files with 292 additions and 99 deletions

View file

@ -134,6 +134,12 @@
9974CA2D23DF197B003FE824 /* ElevationProfileData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 9974CA2B23DF197B003FE824 /* ElevationProfileData+Core.h */; };
999D3A64237B097C00C5F7A8 /* DeepLinkSubscriptionData.h in Headers */ = {isa = PBXBuildFile; fileRef = 999D3A62237B097C00C5F7A8 /* DeepLinkSubscriptionData.h */; settings = {ATTRIBUTES = (Public, ); }; };
999D3A65237B097C00C5F7A8 /* DeepLinkSubscriptionData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 999D3A63237B097C00C5F7A8 /* DeepLinkSubscriptionData.mm */; };
99D934A624C9FFE7002E4802 /* IMWMTrialEligibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 99D934A424C9FFE6002E4802 /* IMWMTrialEligibility.h */; settings = {ATTRIBUTES = (Public, ); }; };
99D934A724C9FFE7002E4802 /* IMWMPurchaseValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = 99D934A524C9FFE7002E4802 /* IMWMPurchaseValidation.h */; settings = {ATTRIBUTES = (Public, ); }; };
99D934B224CA0052002E4802 /* MWMTrialEligibility.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99D934AE24CA0051002E4802 /* MWMTrialEligibility.mm */; };
99D934B324CA0052002E4802 /* MWMTrialEligibility.h in Headers */ = {isa = PBXBuildFile; fileRef = 99D934AF24CA0051002E4802 /* MWMTrialEligibility.h */; settings = {ATTRIBUTES = (Public, ); }; };
99D934B424CA0052002E4802 /* MWMPurchaseValidation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99D934B024CA0052002E4802 /* MWMPurchaseValidation.mm */; };
99D934B524CA0052002E4802 /* MWMPurchaseValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = 99D934B124CA0052002E4802 /* MWMPurchaseValidation.h */; settings = {ATTRIBUTES = (Public, ); }; };
99F31EB823D5DD9000CE2CE1 /* PromoAfterBookingData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 99F31EB523D5DD8F00CE2CE1 /* PromoAfterBookingData.mm */; };
99F31EB923D5DD9000CE2CE1 /* PromoAfterBookingData+Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 99F31EB623D5DD8F00CE2CE1 /* PromoAfterBookingData+Core.h */; };
99F31EBA23D5DD9000CE2CE1 /* PromoAfterBookingData.h in Headers */ = {isa = PBXBuildFile; fileRef = 99F31EB723D5DD9000CE2CE1 /* PromoAfterBookingData.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -276,6 +282,12 @@
9974CA2B23DF197B003FE824 /* ElevationProfileData+Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElevationProfileData+Core.h"; sourceTree = "<group>"; };
999D3A62237B097C00C5F7A8 /* DeepLinkSubscriptionData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepLinkSubscriptionData.h; sourceTree = "<group>"; };
999D3A63237B097C00C5F7A8 /* DeepLinkSubscriptionData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DeepLinkSubscriptionData.mm; sourceTree = "<group>"; };
99D934A424C9FFE6002E4802 /* IMWMTrialEligibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IMWMTrialEligibility.h; sourceTree = "<group>"; };
99D934A524C9FFE7002E4802 /* IMWMPurchaseValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IMWMPurchaseValidation.h; sourceTree = "<group>"; };
99D934AE24CA0051002E4802 /* MWMTrialEligibility.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMTrialEligibility.mm; sourceTree = "<group>"; };
99D934AF24CA0051002E4802 /* MWMTrialEligibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMTrialEligibility.h; sourceTree = "<group>"; };
99D934B024CA0052002E4802 /* MWMPurchaseValidation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMPurchaseValidation.mm; sourceTree = "<group>"; };
99D934B124CA0052002E4802 /* MWMPurchaseValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMPurchaseValidation.h; sourceTree = "<group>"; };
99F31EB523D5DD8F00CE2CE1 /* PromoAfterBookingData.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PromoAfterBookingData.mm; sourceTree = "<group>"; };
99F31EB623D5DD8F00CE2CE1 /* PromoAfterBookingData+Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "PromoAfterBookingData+Core.h"; sourceTree = "<group>"; };
99F31EB723D5DD9000CE2CE1 /* PromoAfterBookingData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PromoAfterBookingData.h; sourceTree = "<group>"; };
@ -325,6 +337,7 @@
470015F12342509C00EBF03D /* CoreApi */ = {
isa = PBXGroup;
children = (
99D934A324C9FFCE002E4802 /* InappPurchase */,
47EF73F6246E031E00D32AB8 /* GuidesGallery */,
47E8163D23B2B97A008FD836 /* User */,
47F4F1F623A333280022FD56 /* Storage */,
@ -677,6 +690,27 @@
path = ElevationProfile;
sourceTree = "<group>";
};
99D934A324C9FFCE002E4802 /* InappPurchase */ = {
isa = PBXGroup;
children = (
99D934B624CA0066002E4802 /* Impl */,
99D934A524C9FFE7002E4802 /* IMWMPurchaseValidation.h */,
99D934A424C9FFE6002E4802 /* IMWMTrialEligibility.h */,
);
path = InappPurchase;
sourceTree = "<group>";
};
99D934B624CA0066002E4802 /* Impl */ = {
isa = PBXGroup;
children = (
99D934B124CA0052002E4802 /* MWMPurchaseValidation.h */,
99D934B024CA0052002E4802 /* MWMPurchaseValidation.mm */,
99D934AF24CA0051002E4802 /* MWMTrialEligibility.h */,
99D934AE24CA0051002E4802 /* MWMTrialEligibility.mm */,
);
path = Impl;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -693,6 +727,7 @@
471AB99123AB931000F56D49 /* MWMMapSearchResult+Core.h in Headers */,
4738A8E0239FACE7007C0F43 /* CoreBanner.h in Headers */,
47942D6D237CC3E300DEFAE3 /* PlacePagePreviewData.h in Headers */,
99D934B324CA0052002E4802 /* MWMTrialEligibility.h in Headers */,
9940622023EAC57900493D1A /* ElevationHeightPoint.h in Headers */,
3D40DED523ED9C7F00A0153A /* WebApi.h in Headers */,
47A65CAF235008E100DCD85F /* CoreApi-swift.h in Headers */,
@ -725,6 +760,7 @@
479834F323426CD200724D1E /* MWMTagGroup+Convenience.h in Headers */,
47D9019923AC236100D9364C /* MWMMapUpdateInfo+Core.h in Headers */,
999D3A64237B097C00C5F7A8 /* DeepLinkSubscriptionData.h in Headers */,
99D934A624C9FFE7002E4802 /* IMWMTrialEligibility.h in Headers */,
47942D7E237CC43000DEFAE3 /* UgcData+Core.h in Headers */,
47942D6E237CC3E800DEFAE3 /* PlacePagePreviewData+Core.h in Headers */,
4738A8E4239FB46E007C0F43 /* CoreBanner+Core.h in Headers */,
@ -737,10 +773,12 @@
47E8163723B188D3008FD836 /* MWMStorage.h in Headers */,
47EEAFF62350CF48005CF316 /* AppInfo.h in Headers */,
47F701F3238C877C00D18E95 /* PlacePageButtonsData+Core.h in Headers */,
99D934B524CA0052002E4802 /* MWMPurchaseValidation.h in Headers */,
471527392491EDAA00E91BBA /* MWMBookmarkColor.h in Headers */,
479F7053234FB7BC00011E2E /* MWMCatalogCommon.h in Headers */,
47942D7A237CC41A00DEFAE3 /* HotelRoom.h in Headers */,
4700160F2342579000EBF03D /* MWMTag.h in Headers */,
99D934A724C9FFE7002E4802 /* IMWMPurchaseValidation.h in Headers */,
47F4F1F923A3336C0022FD56 /* MWMMapNodeAttributes.h in Headers */,
479F705B234FBB1100011E2E /* MWMUTM.h in Headers */,
47942D98237D675400DEFAE3 /* CatalogPromoItem+Core.h in Headers */,
@ -872,12 +910,14 @@
47E8164123B2B98F008FD836 /* MWMUser.mm in Sources */,
471AB98E23AB925D00F56D49 /* MWMMapSearchResult.mm in Sources */,
479834EA2342697400724D1E /* MWMTag+Convenience.mm in Sources */,
99D934B224CA0052002E4802 /* MWMTrialEligibility.mm in Sources */,
47C637D62354AEBE00E12DE0 /* MWMMapOverlayManager.mm in Sources */,
475784C32344B422008291A4 /* Framework.cpp in Sources */,
47942D8D237D634300DEFAE3 /* CatalogPromoData.mm in Sources */,
4718C4332355FC3C00640DF1 /* MWMNetworkPolicy.mm in Sources */,
472602A924092C5B00731135 /* MWMGeoUtil.mm in Sources */,
993F54F3237C5D1100545511 /* PromoAfterBookingCampaignAdapter.mm in Sources */,
99D934B424CA0052002E4802 /* MWMPurchaseValidation.mm in Sources */,
47F701F0238C86F000D18E95 /* PlacePageButtonsData.mm in Sources */,
9940622123EAC57900493D1A /* ElevationHeightPoint.m in Sources */,
47EEAFF42350CEDB005CF316 /* AppInfo.mm in Sources */,

View file

@ -37,6 +37,7 @@ FOUNDATION_EXPORT const unsigned char CoreApiVersionString[];
#import <CoreApi/MWMMapNodeAttributes.h>
#import <CoreApi/MWMMapSearchResult.h>
#import <CoreApi/MWMMapUpdateInfo.h>
#import <CoreApi/MWMPurchaseValidation.h>
#pragma mark - Place Page

View file

@ -23,3 +23,5 @@ FOUNDATION_EXPORT const unsigned char CoreApiVersionString[];
#import <CoreApi/MWMTypes.h>
#import <CoreApi/MWMUser.h>
#import <CoreApi/MWMUTM.h>
#import <CoreApi/MWMTrialEligibility.h>
#import <CoreApi/MWMPurchaseValidation.h>

View file

@ -1,3 +1,5 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, MWMPurchaseValidationResult) {

View file

@ -0,0 +1,20 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, MWMCheckTrialEligibilityResult) {
MWMCheckTrialEligibilityResultEligible,
MWMCheckTrialEligibilityResultNotEligible,
MWMCheckTrialEligibilityResultServerError,
MWMCheckTrialEligibilityResultNoReceipt
};
typedef void (^CheckTrialEligibilityCallback)(MWMCheckTrialEligibilityResult result);
@protocol IMWMTrialEligibility <NSObject>
- (void)checkTrialEligibility:(NSString *)serverId callback:(CheckTrialEligibilityCallback)callback;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,12 @@
#import "IMWMTrialEligibility.h"
NS_ASSUME_NONNULL_BEGIN
@interface MWMTrialEligibility : NSObject <IMWMTrialEligibility>
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithVendorId:(NSString *)vendorId;
@end
NS_ASSUME_NONNULL_END

View file

@ -0,0 +1,67 @@
#import "MWMTrialEligibility.h"
#include <CoreApi/Framework.h>
static NSMutableDictionary<NSString *, NSMutableArray<CheckTrialEligibilityCallback> *> *callbacks = [NSMutableDictionary dictionary];
@interface MWMTrialEligibility ()
@property (nonatomic, copy) NSString *vendorId;
@end
@implementation MWMTrialEligibility
- (instancetype)initWithVendorId:(NSString *)vendorId {
self = [super init];
if (self) {
_vendorId = vendorId;
}
return self;
}
-(void)checkTrialEligibility:(NSString *)serverId callback:(CheckTrialEligibilityCallback)callback {
NSURL *receiptUrl = [NSBundle mainBundle].appStoreReceiptURL;
NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
if (!receiptData) {
if (callback)
callback(MWMCheckTrialEligibilityResultNoReceipt);
return;
}
GetFramework().GetPurchase()->SetTrialEligibilityCallback([serverId](auto trialEligibilityCode){
MWMCheckTrialEligibilityResult result;
switch (trialEligibilityCode) {
case Purchase::TrialEligibilityCode::Eligible:
result = MWMCheckTrialEligibilityResultEligible;
case Purchase::TrialEligibilityCode::NotEligible:
result = MWMCheckTrialEligibilityResultNotEligible;
case Purchase::TrialEligibilityCode::ServerError:
result = MWMCheckTrialEligibilityResultServerError;
}
NSMutableArray<CheckTrialEligibilityCallback> *callbackArray = callbacks[serverId];
[callbackArray enumerateObjectsUsingBlock:^(CheckTrialEligibilityCallback _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj(result);
}];
[callbacks removeObjectForKey:serverId];
});
NSMutableArray<CheckTrialEligibilityCallback> *callbackArray = callbacks[serverId];
if (!callbackArray) {
callbackArray = [NSMutableArray arrayWithObject:[callback copy]];
callbacks[serverId] = callbackArray;
Purchase::ValidationInfo vi;
vi.m_receiptData = [receiptData base64EncodedStringWithOptions:0].UTF8String;
vi.m_serverId = serverId.UTF8String;
vi.m_vendorId = self.vendorId.UTF8String;
GetFramework().GetPurchase()->CheckTrialEligibility(vi);
} else {
[callbackArray addObject:[callback copy]];
}
}
@end

View file

@ -62,7 +62,6 @@
#import "MWMNoteCell.h"
#import "MWMPlacePageManagerHelper.h"
#import "MWMPurchaseManager.h"
#import "MWMPurchaseValidation.h"
#import "MWMPushNotifications.h"
#import "MWMRouteManagerPointType.h"
#import "MWMRoutePreviewTaxiCellType.h"

View file

@ -10,7 +10,15 @@ typedef NS_ENUM(NSUInteger, MWMValidationResult)
MWMValidationResultAuthError
};
typedef NS_ENUM(NSUInteger, MWMTrialEligibilityResult)
{
MWMTrialEligibilityResultEligible,
MWMTrialEligibilityResultNotEligible,
MWMTrialEligibilityResultServerError
};
typedef void (^ValidateReceiptCallback)(NSString * serverId, MWMValidationResult validationResult);
typedef void (^TrialEligibilityCallback)(NSString * serverId, MWMTrialEligibilityResult result);
typedef void (^StartTransactionCallback)(BOOL success, NSString * serverId);
@ -35,6 +43,9 @@ typedef void (^StartTransactionCallback)(BOOL success, NSString * serverId);
- (void)validateReceipt:(NSString *)serverId
refreshReceipt:(BOOL)refresh
callback:(ValidateReceiptCallback)callback;
- (void)checkTrialEligibility:(NSString *)serverId
refreshReceipt:(BOOL)refresh
callback:(TrialEligibilityCallback)callback;
- (void)startTransaction:(NSString *)serverId callback:(StartTransactionCallback)callback;
- (void)refreshReceipt;

View file

@ -1,17 +1,17 @@
#import "MWMPurchaseManager.h"
#import "MWMPurchaseValidation.h"
#include <CoreApi/Framework.h>
#include <CoreApi/CoreApi.h>
#import <StoreKit/StoreKit.h>
@interface MWMPurchaseManager() <SKRequestDelegate>
@property(nonatomic, copy) ValidateReceiptCallback callback;
@property(nonatomic) NSMutableDictionary<NSString *, NSMutableArray<ValidateReceiptCallback> *> *validationCallbacks;
@property(nonatomic) NSMutableDictionary<NSString *, NSMutableArray<TrialEligibilityCallback> *> *trialCallbacks;
@property(nonatomic) SKReceiptRefreshRequest *receiptRequest;
@property(nonatomic, copy) NSString *serverId;
@property(nonatomic, copy) NSString *vendorId;
@property(nonatomic) id<IMWMPurchaseValidation> purchaseValidation;
@property(nonatomic) id<IMWMTrialEligibility> trialEligibility;
@end
@ -91,6 +91,9 @@
if (self) {
_vendorId = vendorId;
_purchaseValidation = [[MWMPurchaseValidation alloc] initWithVendorId:vendorId];
_trialEligibility = [[MWMTrialEligibility alloc] initWithVendorId:vendorId];
_validationCallbacks = [NSMutableDictionary dictionary];
_trialCallbacks = [NSMutableDictionary dictionary];
}
return self;
}
@ -106,39 +109,81 @@
refreshReceipt:(BOOL)refresh
callback:(ValidateReceiptCallback)callback
{
self.callback = callback;
self.serverId = serverId;
[self validateReceipt:refresh];
NSMutableArray<ValidateReceiptCallback> *callbackArray = self.validationCallbacks[serverId];
if (callbackArray) {
[callbackArray addObject:[callback copy]];
} else {
self.validationCallbacks[serverId] = [NSMutableArray arrayWithObject:[callback copy]];
}
[self validateReceipt:serverId refreshReceipt:refresh];
}
- (void)validateReceipt:(BOOL)refresh {
- (void)validateReceipt:(NSString *)serverId
refreshReceipt:(BOOL)refresh {
__weak __typeof(self) ws = self;
[self.purchaseValidation validateReceipt:self.serverId callback:^(MWMPurchaseValidationResult validationResult) {
[self.purchaseValidation validateReceipt:serverId callback:^(MWMPurchaseValidationResult validationResult) {
__strong __typeof(self) self = ws;
switch (validationResult) {
case MWMPurchaseValidationResultValid:
[self validReceipt];
[self notifyValidation:serverId result:MWMValidationResultValid];
break;
case MWMPurchaseValidationResultNotValid:
[self invalidReceipt];
[self notifyValidation:serverId result:MWMValidationResultNotValid];
break;
case MWMPurchaseValidationResultError:
[self serverError];
[self notifyValidation:serverId result:MWMValidationResultServerError];
break;
case MWMPurchaseValidationResultAuthError:
[self authError];
[self notifyValidation:serverId result:MWMValidationResultAuthError];
break;
case MWMPurchaseValidationResultNoReceipt:
if (refresh) {
[self refreshReceipt];
} else {
[self noReceipt];
[self notifyValidation:serverId result:MWMValidationResultNotValid];
}
break;
}
}];
}
- (void)checkTrialEligibility:(NSString *)serverId
refreshReceipt:(BOOL)refresh
callback:(TrialEligibilityCallback)callback {
NSMutableArray<TrialEligibilityCallback> *callbackArray = self.trialCallbacks[serverId];
if (callbackArray) {
[callbackArray addObject:[callback copy]];
} else {
self.trialCallbacks[serverId] = [NSMutableArray arrayWithObject:[callback copy]];
}
[self checkTrialEligibility:serverId refreshReceipt:refresh];
}
- (void)checkTrialEligibility:(NSString *)serverId
refreshReceipt:(BOOL)refresh {
__weak __typeof(self) ws = self;
[self.trialEligibility checkTrialEligibility:serverId callback:^(MWMCheckTrialEligibilityResult result) {
__strong __typeof(self) self = ws;
switch (result) {
case MWMCheckTrialEligibilityResultEligible:
[self notifyTrialEligibility:serverId result:MWMTrialEligibilityResultEligible];
break;
case MWMCheckTrialEligibilityResultNotEligible:
[self notifyTrialEligibility:serverId result:MWMTrialEligibilityResultNotEligible];
break;
case MWMCheckTrialEligibilityResultServerError:
[self notifyTrialEligibility:serverId result:MWMTrialEligibilityResultServerError];
break;
case MWMCheckTrialEligibilityResultNoReceipt:
if (refresh) {
[self refreshReceipt];
} else {
[self notifyTrialEligibility:serverId result:MWMTrialEligibilityResultNotEligible];
}
}
}];
}
- (void)startTransaction:(NSString *)serverId callback:(StartTransactionCallback)callback {
GetFramework().GetPurchase()->SetStartTransactionCallback([callback](bool success,
std::string const & serverId,
@ -151,40 +196,20 @@
GetFramework().GetUser().GetAccessToken());
}
- (void)validReceipt
{
if (self.callback)
self.callback(self.serverId, MWMValidationResultValid);
- (void)notifyValidation:(NSString *)serverId result:(MWMValidationResult)result {
NSMutableArray<ValidateReceiptCallback> *callbackArray = self.validationCallbacks[serverId];
[callbackArray enumerateObjectsUsingBlock:^(ValidateReceiptCallback _Nonnull callback, NSUInteger idx, BOOL * _Nonnull stop) {
callback(serverId, result);
}];
[self.validationCallbacks removeObjectForKey:serverId];
}
- (void)noReceipt
{
if (self.callback)
self.callback(self.serverId, MWMValidationResultNotValid);
}
- (void)invalidReceipt
{
if (self.callback)
self.callback(self.serverId, MWMValidationResultNotValid);
}
- (void)serverError
{
if (self.callback)
self.callback(self.serverId, MWMValidationResultServerError);
}
- (void)authError
{
if (self.callback)
self.callback(self.serverId, MWMValidationResultAuthError);
}
- (void)appstoreError:(NSError *)error
{
if (self.callback)
self.callback(self.serverId, MWMValidationResultServerError);
-(void)notifyTrialEligibility:(NSString *)serverId result:(MWMTrialEligibilityResult)result {
NSMutableArray<TrialEligibilityCallback> *callbackArray = self.trialCallbacks[serverId];
[callbackArray enumerateObjectsUsingBlock:^(TrialEligibilityCallback _Nonnull callback, NSUInteger idx, BOOL * _Nonnull stop) {
callback(serverId, result);
}];
[self.trialCallbacks removeObjectForKey:serverId];
}
+ (void)setAdsDisabled:(BOOL)disabled
@ -204,12 +229,22 @@
- (void)requestDidFinish:(SKRequest *)request
{
[self validateReceipt:NO];
[self.validationCallbacks enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableArray<ValidateReceiptCallback> * _Nonnull obj, BOOL * _Nonnull stop) {
[self validateReceipt:key refreshReceipt:NO];
}];
[self.trialCallbacks enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableArray<TrialEligibilityCallback> * _Nonnull obj, BOOL * _Nonnull stop) {
[self checkTrialEligibility:key refreshReceipt:NO];
}];
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
[self appstoreError:error];
[self.trialCallbacks enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableArray<TrialEligibilityCallback> * _Nonnull obj, BOOL * _Nonnull stop) {
[self notifyValidation:key result:MWMValidationResultServerError];
}];
[self.trialCallbacks enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableArray<TrialEligibilityCallback> * _Nonnull obj, BOOL * _Nonnull stop) {
[self notifyTrialEligibility:key result:MWMTrialEligibilityResultServerError];
}];
}
@end

View file

@ -1,10 +1,12 @@
@objc protocol ISubscriptionManager: class{
typealias SuscriptionsCompletion = ([ISubscription]?, Error?) -> Void
typealias ValidationCompletion = (MWMValidationResult) -> Void
typealias TrialEligibilityCompletion = (MWMTrialEligibilityResult) -> Void
var productIds: [String] { get }
var serverId: String { get }
var vendorId: String { get }
var hasTrial: Bool { get }
@objc static func canMakePayments() -> Bool
@objc func getAvailableSubscriptions(_ completion: @escaping SuscriptionsCompletion)
@ -12,6 +14,7 @@
@objc func addListener(_ listener: SubscriptionManagerListener)
@objc func removeListener(_ listener: SubscriptionManagerListener)
@objc func validate(completion: ValidationCompletion?)
@objc func checkTrialEligibility(completion: TrialEligibilityCompletion?)
@objc func restore(_ callback: @escaping ValidationCompletion)
@objc func setSubscriptionActive(_ value: Bool)
}
@ -36,12 +39,14 @@ class SubscriptionManager: NSObject, ISubscriptionManager {
let productIds: [String]
let serverId: String
let vendorId: String
let hasTrial: Bool
private var purchaseManager: MWMPurchaseManager?
init(productIds: [String], serverId: String, vendorId: String) {
self.productIds = productIds
self.serverId = serverId
self.vendorId = vendorId
self.hasTrial = serverId == MWMPurchaseManager.allPassSubscriptionServerId()
super.init()
paymentQueue.add(self)
self.purchaseManager = MWMPurchaseManager(vendorId: vendorId)
@ -115,6 +120,12 @@ class SubscriptionManager: NSObject, ISubscriptionManager {
}
}
@objc func checkTrialEligibility(completion: TrialEligibilityCompletion?) {
purchaseManager?.checkTrialEligibility(serverId, refreshReceipt: true, callback: { (_, result) in
completion?(result)
})
}
private func logEvents(_ validationResult: MWMValidationResult) {
switch validationResult {
case .valid:

View file

@ -343,7 +343,6 @@
472E3F472146BCD30020E412 /* SubscriptionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472E3F462146BCD30020E412 /* SubscriptionManager.swift */; };
472E3F4A2146C4CD0020E412 /* MWMPurchaseManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 472E3F492146C4CD0020E412 /* MWMPurchaseManager.mm */; };
472E3F4C2147D5700020E412 /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472E3F4B2147D5700020E412 /* Subscription.swift */; };
473464A7218B0BC000D6AF5B /* MWMPurchaseValidation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 473464A6218B0BC000D6AF5B /* MWMPurchaseValidation.mm */; };
4735008A23A83CF700661A95 /* DownloadedMapsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4735008923A83CF700661A95 /* DownloadedMapsDataSource.swift */; };
4738A8E7239FC513007C0F43 /* AdBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4738A8E6239FC513007C0F43 /* AdBannerView.swift */; };
4738A8E9239FC526007C0F43 /* AdBannerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4738A8E8239FC526007C0F43 /* AdBannerView.xib */; };
@ -1472,8 +1471,6 @@
472E3F482146C4CD0020E412 /* MWMPurchaseManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMPurchaseManager.h; sourceTree = "<group>"; };
472E3F492146C4CD0020E412 /* MWMPurchaseManager.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMPurchaseManager.mm; sourceTree = "<group>"; };
472E3F4B2147D5700020E412 /* Subscription.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subscription.swift; sourceTree = "<group>"; };
473464A5218B0BC000D6AF5B /* MWMPurchaseValidation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMPurchaseValidation.h; sourceTree = "<group>"; };
473464A6218B0BC000D6AF5B /* MWMPurchaseValidation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMPurchaseValidation.mm; sourceTree = "<group>"; };
4735008923A83CF700661A95 /* DownloadedMapsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadedMapsDataSource.swift; sourceTree = "<group>"; };
473500C023A8F81800661A95 /* MWMFrameworkObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMFrameworkObserver.h; sourceTree = "<group>"; };
47375E562420ECA800FFCC49 /* Chart.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Chart.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1548,7 +1545,6 @@
47B9065121C7FA400079C85E /* IMWMImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IMWMImageCache.h; sourceTree = "<group>"; };
47C7F9722191E15A00C2760C /* InAppBilling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppBilling.swift; sourceTree = "<group>"; };
47C7F97421930F5300C2760C /* IInAppBilling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IInAppBilling.swift; sourceTree = "<group>"; };
47C7F976219310D800C2760C /* IMWMPurchaseValidation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IMWMPurchaseValidation.h; sourceTree = "<group>"; };
47C8788E22DF525A00A772DA /* SubscriptionSuccessViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionSuccessViewController.swift; sourceTree = "<group>"; };
47C8788F22DF525A00A772DA /* SubscriptionSuccessViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SubscriptionSuccessViewController.xib; sourceTree = "<group>"; };
47C8789722DF622400A772DA /* SubscriptionFailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionFailViewController.swift; sourceTree = "<group>"; };
@ -3463,7 +3459,6 @@
47C7F97421930F5300C2760C /* IInAppBilling.swift */,
477D7AC6218F1515007EE2CB /* IPaidRoutePurchase.swift */,
4719A644219CBD65009F9AA7 /* IPendingTransactionsHandler.swift */,
47C7F976219310D800C2760C /* IMWMPurchaseValidation.h */,
4716EAB921A325310029B886 /* IPaidRouteStatistics.swift */,
);
path = InappPurchase;
@ -3476,8 +3471,6 @@
47C7F9722191E15A00C2760C /* InAppBilling.swift */,
470F5A7C2189BB2F00754295 /* PaidRoutePurchase.swift */,
47D0026621999DA900F651A2 /* PendingTransactionsHandler.swift */,
473464A5218B0BC000D6AF5B /* MWMPurchaseValidation.h */,
473464A6218B0BC000D6AF5B /* MWMPurchaseValidation.mm */,
4719A64D21A30C3B009F9AA7 /* PaidRouteStatistics.swift */,
);
path = Impl;
@ -5446,7 +5439,6 @@
F63AF50F1EA6215100A1DB98 /* FilterPriceCategoryCell.swift in Sources */,
993DF12123F6BDB100AC231A /* UIViewControllerRenderer.swift in Sources */,
34D3AFF61E37A36A004100F9 /* UICollectionView+Cells.swift in Sources */,
473464A7218B0BC000D6AF5B /* MWMPurchaseValidation.mm in Sources */,
4767CDA420AAF66B00BD8166 /* NSAttributedString+HTML.swift in Sources */,
47B06DFE21B965950094CCAD /* Geo.swift in Sources */,
6741A9A91BF340DE002C974C /* MWMDefaultAlert.mm in Sources */,

View file

@ -1,7 +1,6 @@
protocol SubscriptionInteractorProtocol: AnyObject {
func purchase(anchor: UIView, subscription: ISubscription)
func restore(anchor: UIView)
func trial(anchor: UIView)
}
class SubscriptionInteractor {
@ -42,16 +41,10 @@ extension SubscriptionInteractor: SubscriptionInteractorProtocol {
self?.subscriptionManager.subscribe(to: subscription)
}
}
Statistics.logEvent(kStatInappSelect, withParameters: [kStatPurchase: subscriptionManager.serverId,
kStatProduct: subscription.productId],
with: .realtime)
Statistics.logEvent(kStatInappPay, withParameters: [kStatPurchase: subscriptionManager.serverId],
with: .realtime)
}
func restore(anchor: UIView) {
subscriptionManager.addListener(self)
Statistics.logEvent(kStatInappRestore, withParameters: [kStatPurchase: subscriptionManager.serverId])
viewController?.signup(anchor: anchor, source: .subscription) { [weak self] success in
guard success else { return }
self?.presenter.isLoadingHidden = false
@ -72,20 +65,6 @@ extension SubscriptionInteractor: SubscriptionInteractorProtocol {
}
}
}
func trial(anchor: UIView) {
subscriptionManager.addListener(self)
viewController?.signup(anchor: anchor, source: .subscription) { [weak self] success in
guard success else { return }
MWMAlertViewController.activeAlert().presentDefaultAlert(withTitle: L("trial_error_dialog"),
message: nil,
rightButtonTitle: L("ok"),
leftButtonTitle: nil) {
self?.presenter.debugTrial = false
self?.presenter.configure()
}
}
}
}
extension SubscriptionInteractor: SubscriptionManagerListener {

View file

@ -1,7 +1,5 @@
protocol SubscriptionPresenterProtocol: AnyObject {
var isLoadingHidden: Bool { get set }
// TODO: (boriskov) remove stub
var debugTrial: Bool { get set }
func configure()
func purchase(anchor: UIView, period: SubscriptionPeriod)
func restore(anchor: UIView)
@ -35,6 +33,28 @@ class SubscriptionPresenter {
self.source = source
debugTrial = subscriptionManager === InAppPurchase.allPassSubscriptionManager
}
private func configureTrial() {
guard let trialSubscriptionItem = self.subscriptionGroup?[.year] else {
fatalError()
}
view?.setModel(SubscriptionViewModel.trial(SubscriptionViewModel.TrialData(price: trialSubscriptionItem.formattedPrice)))
}
private func configureSubscriptions() {
var data: [SubscriptionViewModel.SubscriptionData] = []
for period in [SubscriptionPeriod.month, SubscriptionPeriod.year] {
guard let subscriptionItem = self.subscriptionGroup?[period] else {
fatalError()
}
data.append(SubscriptionViewModel.SubscriptionData(price: subscriptionItem.formattedPrice,
title: subscriptionItem.title,
period: period,
hasDiscount: subscriptionItem.hasDiscount,
discount: L("all_pass_screen_best_value")))
}
view?.setModel(SubscriptionViewModel.subsctiption(data))
}
}
extension SubscriptionPresenter: SubscriptionPresenterProtocol {
@ -69,26 +89,24 @@ extension SubscriptionPresenter: SubscriptionPresenterProtocol {
let group = SubscriptionGroup(subscriptions: subscriptions)
self?.subscriptionGroup = group
if self!.debugTrial {
guard let trialSubscriptionItem = group[.year] else {
return
}
self?.view?.setModel(SubscriptionViewModel.trial(SubscriptionViewModel.TrialData(price: trialSubscriptionItem.formattedPrice)))
} else {
var data: [SubscriptionViewModel.SubscriptionData] = []
for period in [SubscriptionPeriod.month, SubscriptionPeriod.year] {
guard let subscriptionItem = group[period] else {
assertionFailure()
return
if self?.subscriptionManager.hasTrial == true {
self?.subscriptionManager.checkTrialEligibility { (result) in
switch result {
case .eligible:
self?.configureTrial()
case .notEligible:
self?.configureSubscriptions()
case .serverError:
MWMAlertViewController.activeAlert().presentInfoAlert(L("error_server_title"),
text: L("error_server_message"))
self?.onCancel()
@unknown default:
fatalError()
}
data.append(SubscriptionViewModel.SubscriptionData(price: subscriptionItem.formattedPrice,
title: subscriptionItem.title,
period: period,
hasDiscount: subscriptionItem.hasDiscount,
discount: L("all_pass_screen_best_value")))
}
self?.view?.setModel(SubscriptionViewModel.subsctiption(data))
} else {
self?.configureSubscriptions()
}
}
@ -125,10 +143,14 @@ extension SubscriptionPresenter: SubscriptionPresenterProtocol {
func restore(anchor: UIView) {
interactor.restore(anchor: anchor)
Statistics.logEvent(kStatInappRestore, withParameters: [kStatPurchase: subscriptionManager.serverId])
}
func trial(anchor: UIView) {
interactor.trial(anchor: anchor)
guard let subscription = subscriptionGroup?[.year]?.subscription else {
return
}
interactor.purchase(anchor: anchor, subscription: subscription)
}
func onSubscribe() {