Просмотр исходного кода

【综合】年订阅代码同步

tangchao 6 месяцев назад
Родитель
Сommit
f5ea7f2402

+ 13 - 0
PDF Office/PDF Master/Class/Purchase/IAPProductsManager.h

@@ -32,18 +32,27 @@ extern NSString * const KMIAPSubscriptionLoadedNotification;
 @property (nonatomic,retain) SKProduct *product;
 #endif
 
+@property (nonatomic, assign) NSInteger mouthNumber;
+
 - (NSString *)price;
 - (NSString *)averagePrice;
 
+- (NSString *)discountPrice;
+- (NSString *)discountAveragePrice;
+
 - (NSString *)offersPrice;
 - (NSString *)offersAveragePrice;
 
+// 是否取消订阅
+- (BOOL)isCancelAutoRenew;
+
 @end
 
 @interface IAPProductsManager : NSObject
 
 @property (nonatomic,readonly) IAPProduct *monthProduct;
 @property (nonatomic,readonly) IAPProduct *newlyMonthProduct;
+@property (nonatomic,retain, readonly) IAPProduct *yearProduct;
 @property (nonatomic,readonly) IAPProduct *allAccessProduct;
 @property (nonatomic,readonly) IAPProduct *PDFToOfficeProduct;
 
@@ -65,7 +74,11 @@ extern NSString * const KMIAPSubscriptionLoadedNotification;
 - (void)makeProduct:(IAPProduct *)iapProduct;
 - (void)restoreSubscriptions;
 
+- (void)makeSubProduct:(IAPProduct *)iapProduct discount:(BOOL)discount;
+
 - (BOOL)isAvailableAllFunction;
 - (BOOL)isAvailableAdvancedPDFToOffice;
 
+- (BOOL)isCancelAutoRenew;
+
 @end

+ 327 - 8
PDF Office/PDF Master/Class/Purchase/IAPProductsManager.m

@@ -17,6 +17,7 @@
 #import "NSNULL+Filtration.h"
 #import "ASIFormDataRequest.h"
 #import "JSONKit.h"
+#import "NSObject+DeviceInfo.h"
 
 NSString *const KMStoreLiteKitSecret = @"d8286a8909884e64b84d522305f07463";
 NSString *const KMStoreKitSecret = @"b88b5d687d1547c2ad3c4dd882dec9db";
@@ -28,6 +29,7 @@ NSString *const KMItunesServer = @"https://store.filmagepro.com:3018/api/subscri
 NSString *const KMStoreReceipt = @"KMStoreReceiptKey";
 
 NSString * const KMMonthLicenseProductIdentifier = @"com.pdfreaderpro.mac_free.member.all_access_pack_6months.001";
+NSString * const KMYearLicenseProductIdentifier = @"com.pdfreaderpro.mac_free.member.all_access_pack_12months.001";
 NSString * const KMNewMonthLicenseProductIdentifier = @"com.pdfreaderpro.mac_free.member.all_access_pack_new_6months.001";
 //NSString * const KMNewMonthLicenseProductIdentifier = @"com.pdftechnologies.pdfreader.mac.yearly.002.Test";
 NSString * const KMAllLicenseProductIdentifier = @"com.pdfreaderpro.mac_free.member.all_access_pack_permanent_license.001";
@@ -58,6 +60,8 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
 
 @property (nonatomic, assign) BOOL isTrialPeriod;//判断当前套餐是否试用过
 
+@property (nonatomic, copy) NSString *autoRenewStatus;
+
 @end
 
 @implementation IAPProduct
@@ -94,6 +98,28 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
     return price;
 }
 
+- (NSString *)discountPrice {
+    NSString *price = nil;
+    if (@available(macOS 10.14.4, *)) {
+        SKProductDiscount *flagDis = self.product.discounts.firstObject;
+#if !VERSION_DMG
+        if (flagDis.price) {
+            price = [self discountFormattedPrice:false price:flagDis.price];
+        } else {
+            price = [NSString stringWithFormat:@"USD $%0.2f", [self.offersPrice doubleValue]];
+#ifndef VERSION_DMG
+            // 获取产品价格信息
+            [[IAPProductsManager defaultManager] loadAllProducts];
+#endif
+        }
+    } else {
+        
+    }
+#endif
+    price = price ? : @"";
+    return price;
+}
+
 - (NSString *)averagePrice {
     NSString *price = nil;
 #if !VERSION_DMG
@@ -101,7 +127,11 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
         price = [self formattedPrice:YES];
     } else {
         if (self.priceNumber) {
-            price = [NSString stringWithFormat:@"USD $%0.2f", [self.priceNumber doubleValue]/6.0];
+            CGFloat mouths = self.mouthNumber;
+            if (mouths <= 0) {
+                mouths = 1.0;
+            }
+            price = [NSString stringWithFormat:@"USD $%0.2f", [self.priceNumber doubleValue]/mouths];
         }
 #ifndef VERSION_DMG
         // 获取产品价格信息
@@ -113,6 +143,35 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
     return price;
 }
 
+- (NSString *)discountAveragePrice {
+    NSString *price = nil;
+    if (@available(macOS 10.14.4, *)) {
+        SKProductDiscount *flagDis = self.product.discounts.firstObject;
+#if !VERSION_DMG
+    if (flagDis.price) {
+        price = [self discountFormattedPrice:true price:flagDis.price];
+    } else {
+        if (self.offersAveragePrice) {
+            CGFloat mouths = self.mouthNumber;
+            if (mouths <= 0) {
+                mouths = 1.0;
+            }
+            price = [NSString stringWithFormat:@"USD $%0.2f", [self.offersAveragePrice doubleValue]/mouths];
+        }
+#ifndef VERSION_DMG
+        // 获取产品价格信息
+        [[IAPProductsManager defaultManager] loadAllProducts];
+#endif
+    }
+#endif
+    } else {
+        // Fallback on earlier versions
+    }
+
+    price = price ? : @"";
+    return price;
+}
+
 - (NSString *)offersPrice {
     NSString *price = nil;
     if (self.offersPriceNumber) {
@@ -125,7 +184,11 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
 - (NSString *)offersAveragePrice {
     NSString *price = nil;
     if (self.offersPriceNumber) {
-        price = [NSString stringWithFormat:@"USD $%0.2f", [self.offersPriceNumber doubleValue]/6.0];
+        CGFloat mouths = self.mouthNumber;
+        if (mouths <= 0) {
+            mouths = 1.0;
+        }
+        price = [NSString stringWithFormat:@"USD $%0.2f", [self.offersPriceNumber doubleValue]/mouths];
     }
     price = price ? : @"";
     return price;
@@ -135,6 +198,14 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
     return [self.offersPriceNumber doubleValue]/[self.priceNumber doubleValue];
 }
 
+- (BOOL)isCancelAutoRenew {
+    if (self.autoRenewStatus == nil) {
+        // 没有订阅
+        return false;
+    }
+    return [self.autoRenewStatus boolValue] == false;
+}
+
 #pragma mark - Private Methods
 
 - (NSString *)formattedPrice:(BOOL)isAverage {
@@ -147,7 +218,11 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
         [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
         [numberFormatter setLocale:product.priceLocale];
         if (isAverage) {
-            NSNumber *number = [NSNumber numberWithDouble:[product.price doubleValue]/6.0];
+            CGFloat mouths = self.mouthNumber;
+            if (mouths <= 0) {
+                mouths = 1.0;
+            }
+            NSNumber *number = [NSNumber numberWithDouble:[product.price doubleValue]/mouths];
             formattedString = [numberFormatter stringFromNumber:number];
         } else {
             formattedString = [numberFormatter stringFromNumber:product.price];
@@ -157,6 +232,30 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
     return formattedString;
 }
 
+- (NSString *)discountFormattedPrice:(BOOL)isAverage price:(NSDecimalNumber *)priceNum {
+    NSString *formattedString = nil;
+#if !VERSION_DMG
+    SKProduct *product = self.product;
+    if (product && [product.productIdentifier isEqualToString:self.productIdentifier]) {
+        NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
+        [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
+        [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
+        [numberFormatter setLocale:product.priceLocale];
+        if (isAverage) {
+            CGFloat mouths = self.mouthNumber;
+            if (mouths <= 0) {
+                mouths = 1.0;
+            }
+            NSNumber *number = [NSNumber numberWithDouble:[priceNum doubleValue]/mouths];
+            formattedString = [numberFormatter stringFromNumber:number];
+        } else {
+            formattedString = [numberFormatter stringFromNumber:priceNum];
+        }
+    }
+#endif
+    return formattedString;
+}
+
 @end
 
 @interface IAPProductsManager ()
@@ -166,6 +265,7 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
 
 @property (nonatomic,retain) IAPProduct *monthProduct;
 @property (nonatomic,retain) IAPProduct *newlyMonthProduct;
+@property (nonatomic,retain) IAPProduct *yearProduct;
 @property (nonatomic,retain) IAPProduct *allAccessProduct;
 @property (nonatomic,retain) IAPProduct *PDFToOfficeProduct;
 
@@ -215,12 +315,21 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
         self.monthProduct = [[[IAPProduct alloc] init] autorelease];
         self.monthProduct.productIdentifier = KMMonthLicenseProductIdentifier;
         self.monthProduct.priceNumber = @(29.99);
+        self.monthProduct.mouthNumber = 1;
         
         self.newlyMonthProduct = [[[IAPProduct alloc] init] autorelease];
         self.newlyMonthProduct.productIdentifier = KMNewMonthLicenseProductIdentifier;
         self.newlyMonthProduct.priceNumber = @(39.99);
         self.newlyMonthProduct.offersPriceNumber = @(29.99);
         self.newlyMonthProduct.isOffers = isOffers;
+        self.newlyMonthProduct.mouthNumber = 6;
+        
+        self.yearProduct = [[[IAPProduct alloc] init] autorelease];
+        self.yearProduct.productIdentifier = KMYearLicenseProductIdentifier;
+        self.yearProduct.priceNumber = @(59.99);
+        self.yearProduct.offersPriceNumber = @(29.99);
+        self.yearProduct.isOffers = isOffers;
+        self.yearProduct.mouthNumber = 12;
         
         self.allAccessProduct = [[[IAPProduct alloc] init] autorelease];
         self.allAccessProduct.productIdentifier = KMAllLicenseProductIdentifier;
@@ -240,6 +349,7 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
         
         self.productIdentifiers = @[KMMonthLicenseProductIdentifier,
                                     KMNewMonthLicenseProductIdentifier,
+                                    KMYearLicenseProductIdentifier,
                                     KMAllLicenseProductIdentifier,
                                     KMAILicenseProductIdentifier];
 #else
@@ -276,6 +386,7 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
 #endif
     [_monthProduct release];
     [_newlyMonthProduct release];
+    [_yearProduct release];
     [_allAccessProduct release];
     [_PDFToOfficeProduct release];
     [_liteAIProduct release];
@@ -332,11 +443,84 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
                                                             object:iapProduct.productIdentifier];
         return;
     }
+    
     SKPayment *payment = [SKPayment paymentWithProduct:iapProduct.product];
     [[SKPaymentQueue defaultQueue] addPayment:payment];
 #endif
 }
 
+- (void)makeSubProduct:(IAPProduct *)iapProduct discount:(BOOL)discount {
+#if !VERSION_DMG
+    if (![SKPaymentQueue canMakePayments]) {
+        [[NSNotificationCenter defaultCenter] postNotificationName:KMIAPProductFailedNotification
+                                                            object:iapProduct.productIdentifier];
+        return;
+    } else if (!iapProduct.product) {
+        [self loadAllProducts];
+        [[NSNotificationCenter defaultCenter] postNotificationName:KMIAPProductFailedNotification
+                                                            object:iapProduct.productIdentifier];
+        return;
+    }
+    
+    if (@available(macOS 10.14.4, *)) {
+        if (discount == false) {
+            SKPayment *payment = [SKPayment paymentWithProduct:iapProduct.product];
+            [[SKPaymentQueue defaultQueue] addPayment:payment];
+            return;
+        }
+        
+        NSString *discountId = @"all_access_pack_12months_202407260";
+        NSString *sixDiscountId = @"all_access_pack_new_6months_202407260";
+//        SKProductDiscount *discount = iapProduct.product.discounts.firstObject;
+        SKProductDiscount *flagDiscount = nil;
+        for (SKProductDiscount *dis in iapProduct.product.discounts) {
+            if ([dis.identifier isEqualToString:discountId]) {
+                flagDiscount = dis;
+                break;
+            } else if ([dis.identifier isEqualToString:sixDiscountId]) {
+                flagDiscount = dis;
+                break;
+            }
+        }
+        if (flagDiscount == nil) {
+            SKPayment *payment = [SKPayment paymentWithProduct:iapProduct.product];
+            [[SKPaymentQueue defaultQueue] addPayment:payment];
+            return;
+        }
+        
+        // python3 -m pip install ecdsa
+        NSString *uuid = GetHardwareUUID().lowercaseString;
+        [self fetchOfferDetails:uuid productIdentifier:iapProduct.productIdentifier offerIdentifier:flagDiscount.identifier handler:^(NSString *nonce, CGFloat timestamp, NSString *sign, NSString *keyId, NSError *error) {
+            if (error == nil && sign != nil) {
+                SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:iapProduct.product];
+                NSUUID *uid = [[NSUUID alloc] initWithUUIDString:nonce];
+                NSNumber *time = [NSNumber numberWithDouble:timestamp];
+                SKPaymentDiscount *paymentDiscount = [[SKPaymentDiscount alloc] initWithIdentifier:flagDiscount.identifier keyIdentifier:keyId nonce:uid signature:sign timestamp:time];
+                payment.applicationUsername = @"user_name";
+                payment.paymentDiscount = paymentDiscount;
+
+                [[SKPaymentQueue defaultQueue] addPayment:payment];
+                
+//                SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:iapProduct.product];
+//                NSUUID *uid = [[NSUUID alloc] initWithUUIDString:@"198b80fe-181f-53f4-9c4e-5d6e73f290eb"];
+//                NSNumber *time = [NSNumber numberWithDouble:1721983323123];
+//                SKPaymentDiscount *paymentDiscount = [[SKPaymentDiscount alloc] initWithIdentifier:flagDiscount.identifier keyIdentifier:@"7ZM85LHZYB" nonce:uid signature:@"MEUCIQCa3ZbKANYgJFsr71tCfPQeUyMXXMHKRFhzIDzxaJX8TAIgTN29r1bYCxwResW7gfos1DuWE9ni2MpcacOla+aUCTQ=" timestamp:time];
+//                payment.applicationUsername = @"user_name";
+//                payment.paymentDiscount = paymentDiscount;
+//
+//                [[SKPaymentQueue defaultQueue] addPayment:payment];
+            } else {
+                [[NSNotificationCenter defaultCenter] postNotificationName:KMIAPProductFailedNotification
+                                                                                   object:iapProduct.productIdentifier];
+            }
+        }];
+    } else {
+        SKPayment *payment = [SKPayment paymentWithProduct:iapProduct.product];
+        [[SKPaymentQueue defaultQueue] addPayment:payment];
+    }
+#endif
+}
+
 - (void)restoreSubscriptions {
 #if !VERSION_DMG
     [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
@@ -365,7 +549,8 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
     // 免费版
     if (self.monthProduct.isSubscribed ||
         self.newlyMonthProduct.isSubscribed ||
-        self.allAccessProduct.isSubscribed) {
+        self.allAccessProduct.isSubscribed ||
+        self.yearProduct.isSubscribed) {
         return YES;
     }
     return NO;
@@ -406,7 +591,8 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
     // 免费版
     if (self.monthProduct.isSubscribed ||
         self.newlyMonthProduct.isSubscribed ||
-        self.allAccessProduct.isSubscribed) {
+        self.allAccessProduct.isSubscribed ||
+        self.yearProduct.isSubscribed) {
         return YES;
     }
     return NO;
@@ -421,6 +607,21 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
 #endif
 }
 
+- (BOOL)isCancelAutoRenew {
+    // 是否取消订阅
+    BOOL result = IAPProductsManager.defaultManager.monthProduct.isCancelAutoRenew;
+    if (result == false) {
+        result = IAPProductsManager.defaultManager.newlyMonthProduct.isCancelAutoRenew;
+    }
+    if (result == false) {
+        result = IAPProductsManager.defaultManager.allAccessProduct.isCancelAutoRenew;
+    }
+    if (result == false) {
+        result = IAPProductsManager.defaultManager.yearProduct.isCancelAutoRenew;
+    }
+    return result;
+}
+
 #if !VERSION_DMG
 #pragma mark - Private Methods
 
@@ -546,7 +747,14 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
                     self.newlyMonthProduct.isTrialPeriod = YES;
                 }
                 self.newlyMonthProduct.originalPurchased = YES;
-                
+            } else if ([product_id isEqualToString:self.yearProduct.productIdentifier]) {
+                if (!self.yearProduct.isSubscribed) {
+                    self.yearProduct.isSubscribed = isActive;
+                }
+                if ([receipt[@"is_trial_period"] boolValue]) {
+                    self.yearProduct.isTrialPeriod = YES;
+                }
+                self.yearProduct.originalPurchased = YES;
             } else if ([product_id isEqualToString:self.allAccessProduct.productIdentifier]) {
                 if (!self.allAccessProduct.isSubscribed) {
                     self.allAccessProduct.isSubscribed = quantity > 0 ? YES : NO;
@@ -594,6 +802,14 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
                     self.newlyMonthProduct.isTrialPeriod = YES;
                 }
                 self.newlyMonthProduct.originalPurchased = YES;
+            } else if ([product_id isEqualToString:self.yearProduct.productIdentifier]) {
+                if (!self.yearProduct.isSubscribed) {
+                    self.yearProduct.isSubscribed = isActive;
+                }
+                if ([receipt[@"is_trial_period"] boolValue]) {
+                    self.yearProduct.isTrialPeriod = YES;
+                }
+                self.yearProduct.originalPurchased = YES;
             } else if ([product_id isEqualToString:self.allAccessProduct.productIdentifier]) {
                 if (!self.allAccessProduct.isSubscribed) {
                     self.allAccessProduct.isSubscribed = quantity > 0 ? YES : NO;
@@ -618,6 +834,23 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
         }
     }
     
+    if ([jsonResponse objectForKey:@"pending_renewal_info"]) {
+        NSArray *receipts =[jsonResponse objectForKey:@"pending_renewal_info"];
+        for (NSDictionary *receipt in receipts) {
+            NSString *product_id = receipt[@"product_id"];
+            NSString *status = receipt[@"auto_renew_status"];
+            if ([product_id isEqualToString:self.monthProduct.productIdentifier]) {
+                self.monthProduct.autoRenewStatus = status;
+            } else if([product_id isEqualToString:self.newlyMonthProduct.productIdentifier]) {
+                self.newlyMonthProduct.autoRenewStatus = status;
+            } else if([product_id isEqualToString:self.yearProduct.productIdentifier]) {
+                self.yearProduct.autoRenewStatus = status;
+            } else if ([product_id isEqualToString:self.allAccessProduct.productIdentifier]) {
+                self.allAccessProduct.autoRenewStatus = status;
+            }
+        }
+    }
+    
     [[NSUserDefaults standardUserDefaults] setObject:self.allAccessProduct.isSubscribed ? @"YES":@"NO" forKey:@"allAccessProduct.isSubscribed"];    
     [[NSUserDefaults standardUserDefaults] setObject:self.PDFToOfficeProduct.isSubscribed ? @"YES":@"NO" forKey:@"PDFToOfficeProduct.isSubscribed"];
     [[NSUserDefaults standardUserDefaults] synchronize];
@@ -625,6 +858,80 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
     return YES;
 }
 
+- (void)prepareOffer:(NSString *)usernameHash productIdentifier:(NSString *)productIdentifier offerIdentifier:(NSString *)offerIdentifier handler:(void (^)(NSString *nonce, CGFloat timestamp, NSString *sign, NSString *keyId, NSError *error))handler {
+    [self fetchOfferDetails:usernameHash productIdentifier:productIdentifier offerIdentifier:offerIdentifier handler:^(NSString *nonce, CGFloat timestamp, NSString *sign, NSString *keyId, NSError *error) {
+        if (handler != nil) {
+            handler(nonce, timestamp, sign, keyId, error);
+        }
+    }];
+}
+
+- (void)fetchOfferDetails:(NSString *)usernameHash productIdentifier:(NSString *)productIdentifier offerIdentifier:(NSString *)offerIdentifier handler:(void (^)(NSString *nonce, CGFloat timestamp, NSString *sign, NSString *keyId, NSError *error))handler {
+    NSString *bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]?:@"";
+    
+    NSMutableDictionary *params = [NSMutableDictionary dictionary];
+    [params setObject:bundleID forKey:@"appBundleID"];
+    [params setObject:productIdentifier forKey:@"productIdentifier"];
+    [params setObject:offerIdentifier forKey:@"offerIdentifier"];
+    [params setObject:@"user_name" forKey:@"applicationUsername"];
+    [params setObject:usernameHash forKey:@"nonce"];
+    
+    NSString *urlString = [NSString stringWithFormat:@"%@/api/orders/getDiscountSign", kVerificationServer];
+    NSString *linkString = urlString;
+    if(params.allKeys.count) {
+        if ([urlString rangeOfString:@"?"].location != NSNotFound) {
+            linkString = [urlString stringByAppendingFormat:@"&%@", [IAPProductsManager linkStringWithParams:params]];
+        } else {
+            linkString = [urlString stringByAppendingFormat:@"?%@", [IAPProductsManager linkStringWithParams:params]];
+        }
+    }
+    NSURL *url = [NSURL URLWithString:linkString];
+    
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        ASIFormDataRequest *request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];
+        request.defaultResponseEncoding = NSUTF8StringEncoding;
+        request.timeOutSeconds = 20;
+        request.numberOfTimesToRetryOnTimeout = 3;
+        [request setRequestMethod:@"GET"];
+        [request setRequestHeaders:[NSMutableDictionary dictionaryWithObject:@"application/json" forKey:@"Content-Type"]];
+        [request startSynchronous];
+        NSError *error = request.error;
+        BOOL isSuccessful = NO;
+        NSString *nonce = nil;
+        CGFloat timestamp = 0;
+        NSString *sign = nil;
+        NSString *keyId = nil;
+        if (!error) {
+            NSDictionary *jsonResponse = [[request.responseString objectFromJSONString] filterNullObject];
+            NSDictionary *dict = jsonResponse[@"data"];
+            if ([dict isKindOfClass:[NSDictionary class]] && dict.count > 0) {
+                nonce = dict[@"nonce"];
+                timestamp = [dict[@"timestamp"] doubleValue];
+                sign = dict[@"sign"];
+                keyId = dict[@"keyIdentifier"];
+            }
+        }
+        dispatch_async(dispatch_get_main_queue(), ^{
+            if (handler) {
+                handler(nonce, timestamp, sign, keyId, error);
+            }
+        });
+    });
+}
+
++ (NSString*)linkStringWithParams:(NSDictionary*)params {
+    NSString *link = @"";
+    
+    for (NSString *key in params.allKeys) {
+        if ([link rangeOfString:@"="].location != NSNotFound)
+            link = [link stringByAppendingString:@"&"];
+        
+        link = [link stringByAppendingFormat:@"%@=%@", key, params[key]];
+    }
+    
+    return link;
+}
+
 - (void)loadAllProducts {
     NSSet *identifiers = [NSSet setWithArray:self.productIdentifiers];
     SKProductsRequest *productRequest = [[[SKProductsRequest alloc] initWithProductIdentifiers:identifiers] autorelease];
@@ -660,6 +967,16 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
         self.monthProduct.isSubscribed = YES;
     } else if ([productIdentifier isEqualToString:self.newlyMonthProduct.productIdentifier]) {
         self.newlyMonthProduct.isSubscribed = YES;
+        [self loadReceipt:^(NSError *error) {
+            [[NSNotificationCenter defaultCenter] postNotificationName:KMIAPSubscriptionLoadedNotification
+                                                                object:nil];
+        }];
+    } else if ([productIdentifier isEqualToString:self.yearProduct.productIdentifier]) {
+        [self loadReceipt:^(NSError *error) {
+            [[NSNotificationCenter defaultCenter] postNotificationName:KMIAPSubscriptionLoadedNotification
+                                                                object:nil];
+        }];
+        self.yearProduct.isSubscribed = YES;
     } else if ([productIdentifier isEqualToString:self.allAccessProduct.productIdentifier]) {
         self.allAccessProduct.isSubscribed = YES;
         [[NSUserDefaults standardUserDefaults] setObject:self.allAccessProduct.isSubscribed ? @"YES":@"NO" forKey:@"allAccessProduct.isSubscribed"];
@@ -759,12 +1076,12 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
         [alert setMessageText:@""];
 #if VERSION_FREE
         if (self.monthProduct.isSubscribed ||
-            self.newlyMonthProduct.isSubscribed) {
+            self.newlyMonthProduct.isSubscribed || self.yearProduct.isSubscribed) {
             informative = NSLocalizedString(@"Restore successfully", nil);
         } else if (self.allAccessProduct.isSubscribed) {
             informative = NSLocalizedString(@"Restore successfully. You can start using the advanced tools, including PDF to Office, PDF Watermark, Page Editor, and more.", nil);
         } else if (self.monthProduct.originalPurchased ||
-                   self.newlyMonthProduct.originalPurchased) {
+                   self.newlyMonthProduct.originalPurchased || self.yearProduct.originalPurchased) {
             informative = [NSString stringWithFormat:@"\n%@\n\n%@\n\n%@\n\n",
                            NSLocalizedString(@"Your subscription has expired, please repurchase", nil),
                            NSLocalizedString(@"—》If you already completed the payment, simply click the \"Restore Previous Purchase\" button directly from the upgrade page to enable all the advanced features at no cost to you.", nil),
@@ -885,6 +1202,8 @@ NSString * const KMIAPSubscriptionLoadedNotification = @"KMIAPSubscriptionLoaded
             self.monthProduct.product = product;
         } else if ([product.productIdentifier isEqualToString:self.newlyMonthProduct.productIdentifier]) {
             self.newlyMonthProduct.product = product;
+        } else if ([product.productIdentifier isEqualToString:self.yearProduct.productIdentifier]) {
+            self.yearProduct.product = product;
         } else if ([product.productIdentifier isEqualToString:self.allAccessProduct.productIdentifier]) {
             self.allAccessProduct.product = product;
         } else if ([product.productIdentifier isEqualToString:self.PDFToOfficeProduct.productIdentifier]) {