[iOS] refactor purchase validation, fix bugs

This commit is contained in:
Aleksey Belouosv 2019-07-26 21:03:22 +03:00 committed by Aleksey Belousov
parent 3026187949
commit a6fd07037a
17 changed files with 204 additions and 188 deletions

View file

@ -4,6 +4,7 @@ class BookmarksSubscriptionViewController: MWMViewController {
@IBOutlet private var gradientView: GradientView!
@IBOutlet private var scrollView: UIScrollView!
@IBOutlet private var continueButton: UIButton!
@IBOutlet var loadingView: UIView!
private let annualViewController = BookmarksSubscriptionCellViewController()
private let monthlyViewController = BookmarksSubscriptionCellViewController()
@ -102,9 +103,10 @@ class BookmarksSubscriptionViewController: MWMViewController {
}
@IBAction func onContinue(_ sender: UIButton) {
loadingView.isHidden = false
MWMBookmarksManager.shared().ping { [weak self] (success) in
guard success else {
// self?.loadingView.isHidden = true
self?.loadingView.isHidden = true
let errorDialog = BookmarksSubscriptionFailViewController { [weak self] in
self?.dismiss(animated: true)
}
@ -126,23 +128,36 @@ class BookmarksSubscriptionViewController: MWMViewController {
}
extension BookmarksSubscriptionViewController: SubscriptionManagerListener {
func didFailToSubscribe(_ subscription: ISubscription, error: Error?) {
func didFailToValidate() {
loadingView.isHidden = true
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didValidate(_ isValid: Bool) {
loadingView.isHidden = true
if (isValid) {
onSubscribe?()
let successDialog = BookmarksSubscriptionSuccessViewController { [weak self] in
self?.dismiss(animated: true)
}
present(successDialog, animated: true)
} else {
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
}
func didFailToSubscribe(_ subscription: ISubscription, error: Error?) {
loadingView.isHidden = true
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didSubsribe(_ subscription: ISubscription) {
onSubscribe?()
}
func didFailToValidate(_ subscription: ISubscription, error: Error?) {
}
func didDefer(_ subscription: ISubscription) {
}
func validationError() {
}
}

View file

@ -15,6 +15,7 @@
<outlet property="annualView" destination="Bae-oL-ekX" id="l9n-u0-LNW"/>
<outlet property="continueButton" destination="neX-0h-hs4" id="kQw-7y-Igs"/>
<outlet property="gradientView" destination="BcI-3g-iCI" id="Rmd-LB-Rl5"/>
<outlet property="loadingView" destination="BHb-cU-Ze3" id="bfe-6a-BJf"/>
<outlet property="monthlyView" destination="Jpy-cA-wCv" id="6yi-OC-MrD"/>
<outlet property="scrollView" destination="q5w-jW-Chn" id="dMR-TM-gMC"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
@ -261,6 +262,24 @@
<constraint firstItem="Eiz-QQ-h0b" firstAttribute="width" secondItem="0eE-hs-sId" secondAttribute="width" id="ke9-ke-QPG"/>
</constraints>
</scrollView>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="BHb-cU-Ze3">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="XZg-yN-Jtv">
<rect key="frame" x="188.66666666666666" y="349.66666666666669" width="37" height="37"/>
<color key="color" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" white="1" alpha="0.89998929790000004" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="XZg-yN-Jtv" firstAttribute="centerX" secondItem="BHb-cU-Ze3" secondAttribute="centerX" id="Ade-4z-Spa"/>
<constraint firstItem="XZg-yN-Jtv" firstAttribute="centerY" secondItem="BHb-cU-Ze3" secondAttribute="centerY" id="CQX-i5-ZcY"/>
</constraints>
<viewLayoutGuide key="safeArea" id="Nh0-0H-Zgm"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="backgroundColorName" value="toastBackground"/>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
@ -269,9 +288,13 @@
<constraint firstItem="0eE-hs-sId" firstAttribute="top" secondItem="fnl-2z-Ty3" secondAttribute="top" id="Avd-W3-cXU"/>
<constraint firstAttribute="trailing" secondItem="BcI-3g-iCI" secondAttribute="trailing" id="IKZ-OT-jxC"/>
<constraint firstAttribute="bottom" secondItem="BcI-3g-iCI" secondAttribute="bottom" id="SHg-Ke-dap"/>
<constraint firstAttribute="bottom" secondItem="BHb-cU-Ze3" secondAttribute="bottom" id="X6W-ia-1Mi"/>
<constraint firstAttribute="trailing" secondItem="BHb-cU-Ze3" secondAttribute="trailing" id="dNr-7E-k98"/>
<constraint firstItem="BcI-3g-iCI" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" id="doL-Dw-bDS"/>
<constraint firstItem="BHb-cU-Ze3" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" id="ipn-ug-1wp"/>
<constraint firstItem="fnl-2z-Ty3" firstAttribute="bottom" secondItem="0eE-hs-sId" secondAttribute="bottom" id="ku8-zI-AI4"/>
<constraint firstItem="0eE-hs-sId" firstAttribute="leading" secondItem="fnl-2z-Ty3" secondAttribute="leading" id="sv4-ek-sAP"/>
<constraint firstAttribute="top" secondItem="BHb-cU-Ze3" secondAttribute="top" id="vTu-oZ-veb"/>
</constraints>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
<userDefinedRuntimeAttributes>

View file

@ -3,6 +3,16 @@ class BookmarksLoadedViewController: UIViewController {
@objc var onViewBlock: MWMVoidBlock?
@objc var onCancelBlock: MWMVoidBlock?
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
transitioningDelegate = transitioning
modalPresentationStyle = .custom
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@IBAction func onViewMap(_ sender: UIButton) {
onViewBlock?()
}
@ -10,14 +20,4 @@ class BookmarksLoadedViewController: UIViewController {
@IBAction func onNotNow(_ sender: UIButton) {
onCancelBlock?()
}
override var transitioningDelegate: UIViewControllerTransitioningDelegate? {
get { return transitioning }
set { }
}
override var modalPresentationStyle: UIModalPresentationStyle {
get { return .custom }
set { }
}
}

View file

@ -7,6 +7,8 @@ class BookmarksSubscriptionExpiredViewController: UIViewController {
self.onSubscribe = onSubscribe
self.onDelete = onDelete
super.init(nibName: nil, bundle: nil)
transitioningDelegate = transitioning
modalPresentationStyle = .custom
}
required init?(coder aDecoder: NSCoder) {
@ -24,14 +26,4 @@ class BookmarksSubscriptionExpiredViewController: UIViewController {
@IBAction func onDelete(_ sender: UIButton) {
onDelete()
}
override var transitioningDelegate: UIViewControllerTransitioningDelegate? {
get { return transitioning }
set { }
}
override var modalPresentationStyle: UIModalPresentationStyle {
get { return .custom }
set { }
}
}

View file

@ -5,6 +5,8 @@ class BookmarksSubscriptionFailViewController: UIViewController {
init(onOk: @escaping MWMVoidBlock) {
self.onOk = onOk
super.init(nibName: nil, bundle: nil)
transitioningDelegate = transitioning
modalPresentationStyle = .custom
}
required init?(coder aDecoder: NSCoder) {
@ -18,14 +20,4 @@ class BookmarksSubscriptionFailViewController: UIViewController {
@IBAction func onOk(_ sender: UIButton) {
onOk()
}
override var transitioningDelegate: UIViewControllerTransitioningDelegate? {
get { return transitioning }
set { }
}
override var modalPresentationStyle: UIModalPresentationStyle {
get { return .custom }
set { }
}
}

View file

@ -5,6 +5,8 @@ class BookmarksSubscriptionSuccessViewController: UIViewController {
init(onOk: @escaping MWMVoidBlock) {
self.onOk = onOk
super.init(nibName: nil, bundle: nil)
transitioningDelegate = transitioning
modalPresentationStyle = .custom
}
required init?(coder aDecoder: NSCoder) {
@ -18,14 +20,4 @@ class BookmarksSubscriptionSuccessViewController: UIViewController {
@IBAction func onOk(_ sender: UIButton) {
onOk()
}
override var transitioningDelegate: UIViewControllerTransitioningDelegate? {
get { return transitioning }
set { }
}
override var modalPresentationStyle: UIModalPresentationStyle {
get { return .custom }
set { }
}
}

View file

@ -204,28 +204,36 @@ class PaidRouteViewController: MWMViewController {
}
extension PaidRouteViewController : SubscriptionManagerListener {
func didFailToValidate() {
loadingView.isHidden = true
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didValidate(_ isValid: Bool) {
loadingView.isHidden = true
if (isValid) {
delegate?.didCompleteSubscription(self)
let successDialog = BookmarksSubscriptionSuccessViewController { [weak self] in
self?.dismiss(animated: true)
}
present(successDialog, animated: true)
} else {
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
}
func didFailToSubscribe(_ subscription: ISubscription, error: Error?) {
loadingView.isHidden = true
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didSubsribe(_ subscription: ISubscription) {
loadingView.isHidden = true
delegate?.didCompleteSubscription(self)
let successDialog = BookmarksSubscriptionSuccessViewController { [weak self] in
self?.dismiss(animated: true)
}
present(successDialog, animated: true)
}
func didFailToValidate(_ subscription: ISubscription, error: Error?) {
loadingView.isHidden = true
}
func didDefer(_ subscription: ISubscription) {
loadingView.isHidden = true
}
func validationError() {
loadingView.isHidden = true
}
}

View file

@ -248,7 +248,10 @@ using namespace osm_auth_ios;
[UNUserNotificationCenter currentNotificationCenter].delegate = self.notificationManager;
if ([MWMFrameworkHelper isWiFiConnected]) {
[[InAppPurchase adsRemovalSubscriptionManager] validate];
[[InAppPurchase bookmarksSubscriptionManager] validateWithCompletion:nil];
[[InAppPurchase adsRemovalSubscriptionManager] validateWithCompletion:^(MWMValidationResult result) {
[MWMPurchaseManager setAdsDisabled:result != MWMValidationResultNotValid];
}];
self.pendingTransactionHandler = [InAppPurchase pendingTransactionsHandler];
__weak __typeof(self) ws = self;
[self.pendingTransactionHandler handlePendingTransactions:^(PendingTransactionsStatus) {

View file

@ -5,6 +5,7 @@ typedef NS_ENUM(NSUInteger, MWMPurchaseValidationResult) {
MWMPurchaseValidationResultNotValid,
MWMPurchaseValidationResultError,
MWMPurchaseValidationResultAuthError,
MWMPurchaseValidationResultNoReceipt
};
typedef void (^ValidatePurchaseCallback)(MWMPurchaseValidationResult validationResult);

View file

@ -3,10 +3,11 @@
#include "Framework.h"
#include "private.h"
static NSMutableDictionary<NSString *, NSMutableArray<ValidatePurchaseCallback> *> *callbacks = [NSMutableDictionary dictionary];
@interface MWMPurchaseValidation ()
@property (nonatomic, copy) NSString *vendorId;
@property (nonatomic, copy) ValidatePurchaseCallback callback;
@end
@ -22,51 +23,54 @@
}
- (void)validateReceipt:(NSString *)serverId callback:(ValidatePurchaseCallback)callback {
self.callback = callback;
NSURL * receiptUrl = [NSBundle mainBundle].appStoreReceiptURL;
NSData * receiptData = [NSData dataWithContentsOfURL:receiptUrl];
if (!receiptData)
{
[self validationComplete:MWMPurchaseValidationResultNotValid];
NSURL *receiptUrl = [NSBundle mainBundle].appStoreReceiptURL;
NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
if (!receiptData) {
if (callback)
callback(MWMPurchaseValidationResultNoReceipt);
return;
}
GetFramework().GetPurchase()->SetValidationCallback([self](auto validationCode, auto const & validationInfo) {
GetFramework().GetPurchase()->SetValidationCallback([](auto validationCode, auto const &validationInfo) {
MWMPurchaseValidationResult result;
switch (validationCode) {
case Purchase::ValidationCode::Verified:
[self validationComplete:MWMPurchaseValidationResultValid];
result = MWMPurchaseValidationResultValid;
break;
case Purchase::ValidationCode::NotVerified:
[self validationComplete:MWMPurchaseValidationResultNotValid];
result = MWMPurchaseValidationResultNotValid;
break;
case Purchase::ValidationCode::ServerError: {
[self validationComplete:MWMPurchaseValidationResultError];
result = MWMPurchaseValidationResultError;
break;
case Purchase::ValidationCode::AuthError:
[self validationComplete:MWMPurchaseValidationResultAuthError];
result = MWMPurchaseValidationResultAuthError;
break;
}
}
GetFramework().GetPurchase()->SetValidationCallback(nullptr);
NSString *serverId = @(validationInfo.m_serverId.c_str());
NSMutableArray<ValidatePurchaseCallback> *callbackArray = callbacks[serverId];
[callbackArray enumerateObjectsUsingBlock:^(ValidatePurchaseCallback _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
obj(result);
}];
[callbacks removeObjectForKey:serverId];
});
Purchase::ValidationInfo vi;
vi.m_receiptData = [receiptData base64EncodedStringWithOptions:0].UTF8String;
vi.m_serverId = serverId.UTF8String;
vi.m_vendorId = self.vendorId.UTF8String;
auto const accessToken = GetFramework().GetUser().GetAccessToken();
GetFramework().GetPurchase()->Validate(vi, accessToken);
}
#pragma mark - Private
- (void)validationComplete:(MWMPurchaseValidationResult)result {
if (self.callback)
self.callback(result);
self.callback = nil;
NSMutableArray<ValidatePurchaseCallback> *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;
auto const accessToken = GetFramework().GetUser().GetAccessToken();
GetFramework().GetPurchase()->Validate(vi, accessToken);
} else {
[callbackArray addObject:[callback copy]];
}
}
@end

View file

@ -20,8 +20,10 @@ final class PaidRoutePurchase: NSObject, IPaidRoutePurchase {
private var storeProductCompletion: StoreProductCompletion?
private var storePaymentCompletion: StorePaymentCompletion?
private var billingProduct: IBillingProduct?
private var purchaseManager: MWMPurchaseManager
init(serverId: String,
vendorId: String,
productId: String,
purchaseValidation: IMWMPurchaseValidation,
billing: IInAppBilling) {
@ -29,6 +31,7 @@ final class PaidRoutePurchase: NSObject, IPaidRoutePurchase {
self.productId = productId
self.purchaseValidation = purchaseValidation
self.billing = billing
self.purchaseManager = MWMPurchaseManager(vendorId: vendorId)
super.init()
}
@ -50,7 +53,7 @@ final class PaidRoutePurchase: NSObject, IPaidRoutePurchase {
}
storePaymentCompletion = completion
MWMPurchaseManager.shared().startTransaction(serverId) { [weak self] (success, serverId) in
purchaseManager.startTransaction(serverId) { [weak self] (success, serverId) in
if !success {
self?.storePaymentCompletion?(.error, RoutePurchaseError.paymentError)
self?.storePaymentCompletion = nil
@ -77,7 +80,7 @@ final class PaidRoutePurchase: NSObject, IPaidRoutePurchase {
case .valid:
self?.billing.finishTransaction()
self?.storePaymentCompletion?(.success, nil)
case .notValid:
case .notValid, .noReceipt:
self?.storePaymentCompletion?(.error, RoutePurchaseError.validationFailed)
case .error:
self?.storePaymentCompletion?(.error, RoutePurchaseError.validationError)

View file

@ -15,7 +15,7 @@ final class PendingTransactionsHandler: IPendingTransactionsHandler {
case .paid:
purchaseValidation.validateReceipt("") { [weak self] in
switch $0 {
case .valid, .notValid:
case .valid, .notValid, .noReceipt:
completion(.success)
self?.pendingTransaction.finishTransaction()
case .error:

View file

@ -5,6 +5,7 @@ final class InAppPurchase: NSObject {
let validation = MWMPurchaseValidation(vendorId: BOOKMARKS_VENDOR)
let billing = InAppBilling()
return PaidRoutePurchase(serverId: serverId,
vendorId: BOOKMARKS_VENDOR,
productId: productId,
purchaseValidation: validation,
billing: billing)

View file

@ -24,14 +24,13 @@ typedef void (^StartTransactionCallback)(BOOL success, NSString * serverId);
+ (NSArray<NSString *> *)productIds;
+ (NSArray<NSString *> *)legacyProductIds;
+ (NSArray<NSString *> *)bookmarkInappIds;
+ (MWMPurchaseManager *)sharedManager;
+ (void)setAdsDisabled:(BOOL)disabled;
- (instancetype)initWithVendorId:(NSString *)vendorId;
- (void)validateReceipt:(NSString *)serverId
vendorId:(NSString *)vendorId
refreshReceipt:(BOOL)refresh
callback:(ValidateReceiptCallback)callback;
- (void)startTransaction:(NSString *)serverId callback:(StartTransactionCallback)callback;
- (void)setAdsDisabled:(BOOL)disabled;
- (void)refreshReceipt;
@end

View file

@ -1,4 +1,5 @@
#import "MWMPurchaseManager.h"
#import "MWMPurchaseValidation.h"
#include "Framework.h"
#include "private.h"
@ -9,8 +10,9 @@
@property(nonatomic, copy) ValidateReceiptCallback callback;
@property(nonatomic) SKReceiptRefreshRequest *receiptRequest;
@property(nonatomic, copy) NSString * serverId;
@property(nonatomic, copy) NSString * vendorId;
@property(nonatomic, copy) NSString *serverId;
@property(nonatomic, copy) NSString *vendorId;
@property(nonatomic) id<IMWMPurchaseValidation> purchaseValidation;
@end
@ -69,14 +71,13 @@
return [result copy];
}
+ (MWMPurchaseManager *)sharedManager
{
static MWMPurchaseManager *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[MWMPurchaseManager alloc] init];
});
return instance;
- (instancetype)initWithVendorId:(NSString *)vendorId {
self = [super init];
if (self) {
_vendorId = vendorId;
_purchaseValidation = [[MWMPurchaseValidation alloc] initWithVendorId:vendorId];
}
return self;
}
- (void)refreshReceipt
@ -87,53 +88,40 @@
}
- (void)validateReceipt:(NSString *)serverId
vendorId:(NSString *)vendorId
refreshReceipt:(BOOL)refresh
callback:(ValidateReceiptCallback)callback
{
self.callback = callback;
self.serverId = serverId;
self.vendorId = vendorId;
[self validateReceipt:refresh];
}
- (void)validateReceipt:(BOOL)refresh
{
NSURL * receiptUrl = [NSBundle mainBundle].appStoreReceiptURL;
NSData * receiptData = [NSData dataWithContentsOfURL:receiptUrl];
if (!receiptData)
{
if (refresh)
[self refreshReceipt];
else
[self noReceipt];
return;
}
GetFramework().GetPurchase()->SetValidationCallback([self](auto validationCode, auto const & validationInfo)
{
switch (validationCode)
{
case Purchase::ValidationCode::Verified:
[self validReceipt];
break;
case Purchase::ValidationCode::NotVerified:
[self invalidReceipt];
break;
case Purchase::ValidationCode::ServerError:
case Purchase::ValidationCode::AuthError:
[self serverError];
break;
- (void)validateReceipt:(BOOL)refresh {
__weak __typeof(self) ws = self;
[self.purchaseValidation validateReceipt:self.serverId callback:^(MWMPurchaseValidationResult validationResult) {
__strong __typeof(self) self = ws;
switch (validationResult) {
case MWMPurchaseValidationResultValid:
[self validReceipt];
break;
case MWMPurchaseValidationResultNotValid:
[self invalidReceipt];
break;
case MWMPurchaseValidationResultError:
[self serverError];
break;
case MWMPurchaseValidationResultAuthError:
[self authError];
break;
case MWMPurchaseValidationResultNoReceipt:
if (refresh) {
[self refreshReceipt];
} else {
[self noReceipt];
}
break;
}
GetFramework().GetPurchase()->SetValidationCallback(nullptr);
});
Purchase::ValidationInfo vi;
vi.m_receiptData = [receiptData base64EncodedStringWithOptions:0].UTF8String;
vi.m_serverId = self.serverId.UTF8String;
vi.m_vendorId = self.vendorId.UTF8String;
auto const accessToken = GetFramework().GetUser().GetAccessToken();
GetFramework().GetPurchase()->Validate(vi, accessToken);
}];
}
- (void)startTransaction:(NSString *)serverId callback:(StartTransactionCallback)callback {
@ -184,7 +172,7 @@
self.callback(self.serverId, MWMValidationResultServerError);
}
- (void)setAdsDisabled:(BOOL)disabled
+ (void)setAdsDisabled:(BOOL)disabled
{
GetFramework().GetPurchase()->SetSubscriptionEnabled(SubscriptionType::RemoveAds, disabled);
}

View file

@ -1,35 +1,34 @@
@objc protocol SubscriptionManagerListener: AnyObject {
func didFailToSubscribe(_ subscription: ISubscription, error: Error?)
func didSubsribe(_ subscription: ISubscription)
func didFailToValidate(_ subscription: ISubscription, error: Error?)
func didDefer(_ subscription: ISubscription)
func validationError()
func didFailToValidate()
func didValidate(_ isValid: Bool)
}
class SubscriptionManager: NSObject {
typealias SuscriptionsCompletion = ([ISubscription]?, Error?) -> Void
typealias RestorationCompletion = (MWMValidationResult) -> Void
// @objc static var shared: SubscriptionManager = { return SubscriptionManager() }()
typealias ValidationCompletion = (MWMValidationResult) -> Void
private let paymentQueue = SKPaymentQueue.default()
private var productsRequest: SKProductsRequest?
private var subscriptionsComplection: SuscriptionsCompletion?
private var products: [String: SKProduct]?
private var pendingSubscription: ISubscription?
private var listeners = NSHashTable<SubscriptionManagerListener>.weakObjects()
private var restorationCallback: RestorationCompletion?
private var restorationCallback: ValidationCompletion?
private var productIds: [String] = []
private var serverId: String = ""
private var vendorId: String = ""
private var purchaseManager: MWMPurchaseManager?
convenience init(productIds: [String], serverId: String, vendorId: String) {
self.init()
self.productIds = productIds
self.serverId = serverId
self.vendorId = vendorId
self.purchaseManager = MWMPurchaseManager(vendorId: vendorId)
}
override private init() {
@ -66,29 +65,29 @@ class SubscriptionManager: NSObject {
listeners.remove(listener)
}
@objc func validate() {
validate(false)
@objc func validate(completion: ValidationCompletion? = nil) {
validate(false, completion: completion)
}
@objc func restore(_ callback: @escaping RestorationCompletion) {
restorationCallback = callback
validate(true)
@objc func restore(_ callback: @escaping ValidationCompletion) {
validate(true) {
callback($0)
}
}
private func validate(_ refreshReceipt: Bool) {
MWMPurchaseManager.shared()
.validateReceipt(serverId, vendorId: vendorId, refreshReceipt: refreshReceipt) { [weak self] (_, validationResult) in
self?.logEvents(validationResult)
if validationResult == .valid || validationResult == .notValid {
MWMPurchaseManager.shared().setAdsDisabled(validationResult == .valid)
self?.paymentQueue.transactions
.filter { self?.productIds.contains($0.payment.productIdentifier) ?? false &&
($0.transactionState == .purchased || $0.transactionState == .restored) }
.forEach { self?.paymentQueue.finishTransaction($0) }
}
self?.restorationCallback?(validationResult)
self?.restorationCallback = nil
private func validate(_ refreshReceipt: Bool, completion: ValidationCompletion? = nil) {
purchaseManager?.validateReceipt(serverId, refreshReceipt: refreshReceipt) { [weak self] (_, validationResult) in
self?.logEvents(validationResult)
if validationResult == .valid || validationResult == .notValid {
self?.listeners.allObjects.forEach { $0.didValidate(validationResult == .valid) }
self?.paymentQueue.transactions
.filter { self?.productIds.contains($0.payment.productIdentifier) ?? false &&
($0.transactionState == .purchased || $0.transactionState == .restored) }
.forEach { self?.paymentQueue.finishTransaction($0) }
} else {
self?.listeners.allObjects.forEach { $0.didFailToValidate() }
}
completion?(validationResult)
}
}
@ -166,7 +165,6 @@ extension SubscriptionManager: SKPaymentTransactionObserver {
}
private func processPurchased(_ transaction: SKPaymentTransaction) {
MWMPurchaseManager.shared().setAdsDisabled(true)
paymentQueue.finishTransaction(transaction)
if let ps = pendingSubscription, transaction.payment.productIdentifier == ps.productId {
Statistics.logEvent(kStatInappPaymentSuccess)
@ -175,7 +173,6 @@ extension SubscriptionManager: SKPaymentTransactionObserver {
}
private func processRestored(_ transaction: SKPaymentTransaction) {
MWMPurchaseManager.shared().setAdsDisabled(true)
paymentQueue.finishTransaction(transaction)
if let ps = pendingSubscription, transaction.payment.productIdentifier == ps.productId {
listeners.allObjects.forEach { $0.didSubsribe(ps) }

View file

@ -219,22 +219,20 @@ import SafariServices
}
extension RemoveAdsViewController: SubscriptionManagerListener {
func validationError() {
hidePurchaseProgress()
delegate?.didCompleteSubscribtion(self)
func didFailToValidate() {
}
func didValidate(_ isValid: Bool) {
}
func didSubsribe(_ subscription: ISubscription) {
MWMPurchaseManager.setAdsDisabled(true)
hidePurchaseProgress()
delegate?.didCompleteSubscribtion(self)
}
func didFailToValidate(_ subscription: ISubscription, error: Error?) {
hidePurchaseProgress()
MWMAlertViewController.activeAlert().presentInfoAlert(L("bookmarks_convert_error_title"),
text: L("purchase_error_subtitle"))
}
func didDefer(_ subscription: ISubscription) {
hidePurchaseProgress()
delegate?.didCompleteSubscribtion(self)