forked from organicmaps/organicmaps
[iOS] check eligibility for trial
https://jira.mail.ru/browse/MAPSME-14171
This commit is contained in:
parent
c885b11710
commit
d09ed36a23
16 changed files with 292 additions and 99 deletions
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, MWMPurchaseValidationResult) {
|
20
iphone/CoreApi/CoreApi/InappPurchase/IMWMTrialEligibility.h
Normal file
20
iphone/CoreApi/CoreApi/InappPurchase/IMWMTrialEligibility.h
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 */,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Add table
Reference in a new issue