InAppProductsManager.m 20.3 KB
Newer Older
DanmeiChen's avatar
DanmeiChen committed
1 2
/*
 * Copyright (c) 2010-2019 Belledonne Communications SARL.
3
 *
DanmeiChen's avatar
DanmeiChen committed
4
 * This file is part of linphone-iphone
5
 *
DanmeiChen's avatar
DanmeiChen committed
6 7 8 9
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
10
 *
DanmeiChen's avatar
DanmeiChen committed
11 12 13 14
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
15
 *
DanmeiChen's avatar
DanmeiChen committed
16 17
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 19 20
 */

#import "InAppProductsManager.h"
21
#import "ShopView.h"
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
22

23
// In app purchase are not supported by the Simulator
Brieuc's avatar
Brieuc committed
24
#import "XMLRPCHelper.h"
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
25
#import "LinphoneManager.h"
26
#import "PhoneMainView.h"
27
#import "StoreKit/StoreKit.h"
28

29 30 31
@interface InAppProductsManager ()
@property(strong, nonatomic) NSDate *expirationDate;
@property(strong, nonatomic) NSDictionary *accountCreationData;
Brieuc's avatar
Brieuc committed
32

33 34
@end

35 36


37
@implementation InAppProductsManager
38

39 40 41 42
@synthesize checkPeriod;
@synthesize warnBeforeExpiryPeriod;
@synthesize notificationCategory;

43
// LINPHONE_CAPABILITY_INAPP_PURCHASE must be defined in Linphone Build Settings
44
#if 1
45

46 47
- (instancetype)init {
	if ((self = [super init]) != nil) {
48
		_enabled = (([SKPaymentQueue canMakePayments]) &&
49
					([LinphoneManager.instance lpConfigBoolForKey:@"enabled" inSection:@"in_app_purchase"]));
50 51
		_initialized = false;
		_available = false;
52
		_accountActivationInProgress = false;
53 54 55
		checkPeriod = [LinphoneManager.instance lpConfigIntForKey:@"expiry_check_period" inSection:@"in_app_purchase"];
		warnBeforeExpiryPeriod = [LinphoneManager.instance lpConfigIntForKey:@"warn_before_expiry_period" inSection:@"in_app_purchase"];
		lastCheck = 0;
56

57
		[XMLRPCHelper.self initArray];
58 59 60 61 62 63 64
		//========// for test only
		// int testExpiry = [LinphoneManager.instance lpConfigIntForKey:@"expiry_time_test"
		// inSection:@"in_app_purchase"];
		// if (testExpiry > 0){
		//	expiryTime = time(NULL) + testExpiry;
		//}else expiryTime = 0;
		//========//
65
		if (_enabled) {
Brieuc's avatar
Brieuc committed
66
			// self.xmlrpc = [[InAppProductsXMLRPCDelegate alloc] init];
67
			_status = kIAPNotReady;
68 69
			[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
			[self loadProducts];
70 71 72
			[self checkAccountExpirationDate];
			[self checkAccountTrial];
			[self checkAccountExpired];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
73
		}
74
		//[self check];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
75
	}
76
	return self;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
77 78
}

79
#pragma mark Public API
80

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
81
- (BOOL)isPurchasedWithID:(NSString *)productID {
82 83
	if (!_enabled)
		return FALSE;
84

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
85
	for (NSString *prod in _productsIDPurchased) {
86
		NSDate *now = [[NSDate alloc] init];
87
		// since multiple ID represent the same product, we must not check it
88
		if (/*[prod isEqual: productID] &&*/ [self.expirationDate earlierDate:now] == now) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
89
			bool isBought = true;
90
			LOGE(@"%@ is %s bought.", prod, isBought ? "" : "NOT");
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
91 92
			return isBought;
		}
93
	}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
94 95
	return false;
}
96

97
- (BOOL)purchaseWithID:(NSString *)productID {
98 99 100 101 102
	if (!_enabled || !_initialized || !_available) {
		NSDictionary *dict = @{
			@"product_id" : productID,
			@"error_msg" : NSLocalizedString(@"Cannot purchase anything yet, please try again later.", nil)
		};
103 104 105
		[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
		return FALSE;
	}
106 107
	SKProduct *prod = [self productIDAvailable:productID];
	if (prod) {
108 109 110 111 112 113
		// Display waitview on click
		UIWindow *window = [[UIApplication sharedApplication] keyWindow];
		UIView *topView = window.rootViewController.view;
		UIView *waitview = (UIView *)[topView viewWithTag:288];
		[waitview setHidden:FALSE];

114
		NSDictionary *dict = @{ @"product_id" : productID };
115
		[self postNotificationforStatus:kIAPPurchaseTrying withDict:dict];
116
		SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:prod];
117
		_available = false;
118 119
		[[SKPaymentQueue defaultQueue] addPayment:payment];
		[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
120
		return TRUE;
121
	} else {
122
		NSDictionary *dict = @{ @"product_id" : productID, @"error_msg" : @"Product not available" };
123
		[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
124 125 126 127
		return FALSE;
	}
}

128 129 130
- (BOOL)restore {
	if (!_enabled || !_initialized || !_available) {
		NSDictionary *dict = @{ @"error_msg" : NSLocalizedString(@"In apps not ready yet", nil) };
131 132 133
		[self postNotificationforStatus:kIAPRestoreFailed withDict:dict];
		return FALSE;
	}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
134
	LOGI(@"Restoring user purchases...");
135
	// force new query of our server
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
136
	latestReceiptMD5 = nil;
137
	_available = false;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
138
	[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
139
	return TRUE;
140 141
}

142
- (BOOL)retrievePurchases {
143 144
	if (!_enabled | !_initialized | !_available) {
		NSDictionary *dict = @{ @"error_msg" : NSLocalizedString(@"In apps not ready yet", nil) };
145 146 147 148 149 150 151 152 153 154
		[self postNotificationforStatus:kIAPRestoreFailed withDict:dict];
		return FALSE;
	} else if ([[self getPhoneNumber] length] == 0) {
		LOGW(@"Not retrieving purchase since not account configured yet");
		return FALSE;
	} else {
		_available = false;
		[self validateReceipt:nil];
		return TRUE;
	}
155 156 157 158 159
}

#pragma mark ProductListLoading

- (void)loadProducts {
160
	NSArray *list = [[[LinphoneManager.instance lpConfigStringForKey:@"products_list" inSection:@"in_app_purchase"]
161 162
		stringByReplacingOccurrencesOfString:@" "
								  withString:@""] componentsSeparatedByString:@","];
163 164 165

	_productsIDPurchased = [[NSMutableArray alloc] initWithCapacity:0];

166 167
	SKProductsRequest *productsRequest =
		[[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithArray:list]];
168 169 170 171
	productsRequest.delegate = self;
	[productsRequest start];
}

172 173
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
	_productsAvailable = [NSMutableArray arrayWithArray:response.products];
174 175

	LOGI(@"Found %lu products available", (unsigned long)_productsAvailable.count);
176
	_initialized = true;
177 178 179 180 181
	if (response.invalidProductIdentifiers.count > 0) {
		for (NSString *invalidIdentifier in response.invalidProductIdentifiers) {
			LOGW(@"Found product Identifier with invalid ID '%@'", invalidIdentifier);
		}
	} else {
182 183
		_available = true;
		[self postNotificationforStatus:kIAPReady withDict:nil];
184 185 186 187
	}
}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
188
	LOGE(@"Impossible to retrieve list of products: %@", [error localizedDescription]);
189
	// well, let's retry...
190 191 192 193 194
	[self loadProducts];
}

#pragma mark Other

195 196 197
- (SKProduct *)productIDAvailable:(NSString *)productID {
	if (!_enabled || !_initialized)
		return nil;
198 199 200 201 202 203 204
	for (SKProduct *product in _productsAvailable) {
		if ([product.productIdentifier compare:productID options:NSLiteralSearch] == NSOrderedSame) {
			return product;
		}
	}
	return nil;
}
205

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
206
- (void)requestDidFinish:(SKRequest *)request {
207
	if ([request isKindOfClass:[SKReceiptRefreshRequest class]]) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
208 209 210
		NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
		if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptUrl path]]) {
			LOGI(@"App Receipt exists");
211
			[self validateReceipt:nil];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
212 213
		} else {
			// This can happen if the user cancels the login screen for the store.
214 215
			// If we get here it means there is no receipt and an attempt to get it failed because the user cancelled
			// the login.
216
			LOGF(@"Receipt request done but there is no receipt");
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
217
		}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
218 219 220
	}
}

221 222
#pragma mark Receipt management

223
- (NSString *)getReceipt {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
224 225
	NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
	// Test whether the receipt is present at the above URL
226
	if (![[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
227
		// We are probably in sandbox environment, trying to retrieve it...
228 229 230 231 232
		return nil;
	}

	NSString *receiptBase64 = [[NSData dataWithContentsOfURL:receiptURL] base64EncodedStringWithOptions:0];
	LOGI(@"Found appstore receipt %@", [receiptBase64 md5]);
233
	[self saveReceiptTemp:receiptBase64];
234 235 236
	return receiptBase64;
}

237
/**
238
 Save Receipt temporarily until xmlrpc server request completed and confirmation sent
239 240
**/
- (void)saveReceiptTemp:(NSString *)receipt {
241
	LOGE(@"===>>> saveReceiptTemp : TmpReceipt");
242
	[LinphoneManager.instance lpConfigSetString:receipt forKey:@"save_tmp_receipt" inSection:@"in_app_purchase"];
243 244 245
}

/**
246
 reset Receipt to empty after xmlrpc request confirmation received
247 248
 **/
- (void)removeTmpReceipt:(NSString *)receipt {
249
	LOGE(@"===>>> removeReceiptTemp : TmpReceipt");
250 251
	if ([LinphoneManager.instance lpConfigStringForKey:@"save_tmp_receipt" inSection:@"in_app_purchase"])
		[LinphoneManager.instance lpConfigSetString:@"0" forKey:@"save_tmp_receipt" inSection:@"in_app_purchase"];
252 253
}

254 255 256
/**
 get temp Receipt to retry xmlrpc request
 **/
257
- (NSString *)getTmpReceipt {
258
	LOGE(@"===>>> getReceiptTemp : TmpReceipt");
259
	return [LinphoneManager.instance lpConfigStringForKey:@"save_tmp_receipt" inSection:@"in_app_purchase"];
260 261
}

262
- (void)validateReceipt:(SKPaymentTransaction *)transaction {
263 264
	NSString *receiptBase64 = [self getReceipt];
	if (receiptBase64 == nil) {
265
		SKRequest *req = [[SKReceiptRefreshRequest alloc] init];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
266 267 268
		LOGI(@"Receipt not found yet, trying to retrieve it...");
		req.delegate = self;
		[req start];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
269
		return;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
270
	}
271 272 273 274 275
	// Hide waiting view
	UIWindow *window = [[UIApplication sharedApplication] keyWindow];
	UIView *topView = window.rootViewController.view;
	UIView *waitview = (UIView *)[topView viewWithTag:288];
	[waitview setHidden:TRUE];
276
	// only check the receipt if it has changed
277
	if (latestReceiptMD5 == nil || ![latestReceiptMD5 isEqualToString:[receiptBase64 md5]]) {
278
		[self updateAccountExpirationDate:receiptBase64];
279
		latestReceiptMD5 = [receiptBase64 md5];
Brieuc's avatar
Brieuc committed
280
		LOGI(@"XMLRPC query ");
281
	} else {
282
		LOGW(@"Not checking receipt since it has already been done!");
283
		_available = true;
284
	}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
285 286
}

287 288
#pragma mark Getters

289 290
- (NSString *)getPhoneNumber {
	NSString *phoneNumber = @"";
291
	LinphoneProxyConfig *config = linphone_core_get_default_proxy_config(LC);
292
	if (config) {
293
		const LinphoneAddress *identity = linphone_proxy_config_get_identity_address(config);
294
		if (identity) {
295
			phoneNumber = [NSString stringWithUTF8String:linphone_address_get_username(identity)];
296 297 298 299 300
		}
	}
	return phoneNumber;
}

301 302 303
- (NSString *)getPassword {
	NSString *pass;
	LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(LC);
Benjamin REIS's avatar
Benjamin REIS committed
304 305 306 307 308 309
	if (cfg &&
		strcmp([LinphoneManager.instance lpConfigStringForKey:@"domain_name"
													inSection:@"app"
												  withDefault:@"sip.linphone.org"]
				   .UTF8String,
			   linphone_proxy_config_get_domain(cfg)) == 0) {
310 311 312 313 314 315 316 317 318 319 320 321
		const LinphoneAuthInfo *info = linphone_proxy_config_find_auth_info(cfg);
		const char *tmpPass;
		if (linphone_auth_info_get_passwd(info)) {
			tmpPass = linphone_auth_info_get_passwd(info);
		} else {
			tmpPass = linphone_auth_info_get_ha1(info);
		}
		pass = [NSString stringWithFormat:@"%s", tmpPass];
	}
	return pass;
}

322 323
#pragma mark Payment management

324
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
325
	for (SKPaymentTransaction *transaction in transactions) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
326
		switch (transaction.transactionState) {
327 328 329 330 331 332 333 334 335 336 337
			case SKPaymentTransactionStatePurchasing:
				break;
			case SKPaymentTransactionStatePurchased:
			case SKPaymentTransactionStateRestored: {
				if (!_initialized) {
					LOGW(@"Pending transactions before end of initialization, not verifying receipt");
				} else {
					[self validateReceipt:transaction];
				}
				[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
				break;
338
			}
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
			case SKPaymentTransactionStateDeferred:
				LOGI(@"Waiting for parent approval...");
				// could do some UI stuff
				break;
			case SKPaymentTransactionStateFailed: {
				_available = true;
				[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
				if (transaction.error.code == SKErrorPaymentCancelled) {
					LOGI(@"SKPaymentTransactionStateFailed: cancelled");
					NSDictionary *dict = @{ @"product_id" : transaction.payment.productIdentifier };
					[self postNotificationforStatus:kIAPPurchaseCancelled withDict:dict];
				} else {
					NSString *errlast =
						[NSString stringWithFormat:@"Purchase failed: %@.", transaction.error.localizedDescription];
					LOGE(@"SKPaymentTransactionStateFailed: %@", errlast);
					NSDictionary *dict =
						@{ @"product_id" : transaction.payment.productIdentifier,
						   @"error_msg" : errlast };
					[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
				}
				break;
360
			}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
361 362 363
		}
	}
}
364

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
365
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
366
	for (SKPaymentTransaction *transaction in transactions) {
367
		LOGD(@"%@ was removed from the payment queue.", transaction.payment.productIdentifier);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
368
	}
369 370
}

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
371 372
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
	if (error.code != SKErrorPaymentCancelled) {
373
		NSDictionary *dict = @{ @"error_msg" : [error localizedDescription] };
374
		[self postNotificationforStatus:kIAPRestoreFailed withDict:dict];
375 376 377
	}
}

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
378
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
379 380 381
	LOGI(@"All restorable transactions have been processed by the payment queue.");
}

382
- (void)postNotificationforStatus:(IAPPurchaseNotificationStatus)status withDict:(NSDictionary *)dict {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
383 384
	_status = status;
	LOGI(@"Triggering notification for status %@", status);
385
	[NSNotificationCenter.defaultCenter postNotificationName:status object:self userInfo:dict];
386 387 388 389 390 391
	if ([status isEqual:kIAPPurchaseFailed] || [status isEqual:kIAPPurchaseCancelled]) {
		// Hide waiting view
		UIWindow *window = [[UIApplication sharedApplication] keyWindow];
		UIView *topView = window.rootViewController.view;
		UIView *waitview = (UIView *)[topView viewWithTag:288];
		[waitview setHidden:TRUE];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
392
	}
393
}
394

395
#pragma mark expiration notif
396

397 398 399 400 401 402 403 404 405 406
- (void) presentNotification:(int64_t) remaining{
	if (notificationCategory == nil) return;
	int days = (int)remaining / (3600 * 24);
	NSString * expireText;
	if (remaining >= 0){
		expireText = [NSString stringWithFormat:NSLocalizedString(@"Your account will expire in %i days.", nil), days];
	}else{
		expireText = [NSString stringWithFormat:NSLocalizedString(@"Your account has expired.", nil), days];
	}

407
	if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
408 409
		UILocalNotification *notification = [[UILocalNotification alloc] init];
		if (notification) {
410

411 412 413 414
			notification.category = notificationCategory;
			notification.repeatInterval = 0;
			notification.applicationIconBadgeNumber = 1;
			notification.alertBody = expireText;
415

416 417
			[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
		}
418

419
	}else{
420
		UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Account expiring", nil)
Brieuc's avatar
Brieuc committed
421 422
																	   message:expireText
																preferredStyle:UIAlertControllerStyleAlert];
423

424 425
		UIAlertAction* buyAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Buy", nil)
															style:UIAlertActionStyleDefault
426 427 428
														  handler:^(UIAlertAction * action) {
															  [PhoneMainView.instance changeCurrentView:ShopView.compositeViewDescription];
														  }];
429

430
		UIAlertAction *laterAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Later", nil)
Brieuc's avatar
Brieuc committed
431 432 433 434
															  style:UIAlertActionStyleCancel
															handler:^(UIAlertAction *action){
																// [alert dismissViewControllerAnimated:FALSE];
															}];
435

436 437 438 439 440 441 442 443 444
		[alert addAction:buyAction];
		[alert addAction:laterAction];
		[PhoneMainView.instance presentViewController:alert animated:YES completion:nil];
	}
}

- (void) check{
	if (!_available) return;
	if (expiryTime == 0 || checkPeriod == 0) return;
445

446 447 448 449 450 451 452 453
	time_t now = time(NULL);

	if (now < lastCheck + checkPeriod) return;
	if (now >= expiryTime - warnBeforeExpiryPeriod){
		lastCheck = now;
		int64_t remaining = (int64_t)expiryTime - (int64_t)now;
		[self presentNotification: remaining];
	}
454 455 456 457 458

	if (![[self getTmpReceipt] isEqualToString:@""]) {
		LOGE(@"===>>> Check : getTmpReceipt != ''");
		[self updateAccountExpirationDate:[self getReceipt]];
	}
459 460
}

461 462
#pragma mark Intermediate XMLRPC call method

463 464 465 466 467 468 469 470 471 472 473 474 475
// Intermediate method to check XMLRPC account expiration date
- (BOOL)updateAccountExpirationDate:(NSString *)receiptBase64 {
	return [self
		callXmlrpcRequestWithParams:@"update_expiration_date"
						  onSuccess:^(NSString *response) {
							if (response) {
								// NSString *productID = [LinphoneManager.instance
								// lpConfigStringForKey:@"paid_account_id" inSection:@"in_app_purchase"];
								LOGI(@"update_expiration_date callback - response: %@", response);
								if ([response containsString:@"ERROR"]) {
									LOGE(@"Failed with error %@", response);
									NSString *errorMsg;
									if ([response isEqualToString:@"ERROR_ACCOUNT_ALREADY_EXISTS"]) {
476
										errorMsg = NSLocalizedString(@"This account is already connected.", nil);
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
									} else if ([response isEqualToString:@"ERROR_UID_ALREADY_IN_USE"]) {
										errorMsg = NSLocalizedString(@"You already own an account.", nil);
									} else if ([response isEqualToString:@"ERROR_ACCOUNT_DOESNT_EXIST"]) {
										errorMsg = NSLocalizedString(@"You have already purchased an account "
																	 @"but it does not exist anymore.",
																	 nil);
									} else if ([response isEqualToString:@"ERROR_PURCHASE_CANCELLED"]) {
										errorMsg = NSLocalizedString(@"You cancelled your account.", nil);
									} else {
										errorMsg = [NSString
											stringWithFormat:NSLocalizedString(@"Unknown error (%@).", nil), response];
									}
									// NSDictionary *dict = @{ @"product_id" : productID, @"error_msg" :
									// errorMsg };
									//[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
492 493 494

								} else // remove temporarily receipt
									[self removeTmpReceipt:receiptBase64];
495 496 497 498 499 500 501
							}
						  }
							onError:NULL
							  extra:receiptBase64];
}

// Intermediate method to check XMLRPC account expiration date
502
- (BOOL)checkAccountExpirationDate {
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
	return [self callXmlrpcRequestWithParams:@"get_account_expiration"
								   onSuccess:^(NSString *response) {
									 if (response) {
										 LOGI(@"get_account_expiration callback - response: %@", response);
										 if ([response containsString:@"ERROR_NO_EXPIRATION"]) {
											 expiryTime = 0;
										 }
									 }
								   }
									 onError:NULL
									   extra:NULL];
}

// Intermediate method to check XMLRPC account trial
- (BOOL)checkAccountTrial {
	return [self callXmlrpcRequestWithParams:@"is_account_trial"
								   onSuccess:^(NSString *response) {
									 if (response) {
										 LOGI(@"is_account_trial callback - response: %@", response);
									 }
								   }
									 onError:NULL
									   extra:NULL];
}

// Intermediate method to check XMLRPC account expired
- (BOOL)checkAccountExpired {
	return [self callXmlrpcRequestWithParams:@"is_account_expired"
								   onSuccess:^(NSString *response) {
									 if (response) {
										 LOGI(@"is_account_expired callback - response: %@", response);
									 }
								   }
									 onError:NULL
									   extra:NULL];
}

540 541 542 543 544 545 546 547 548 549 550 551
// Intermediate method to check check payload signature
- (BOOL)checkPayloadSignature {
	return [self callXmlrpcRequestWithParams:@"check_payload_signature"
								   onSuccess:^(NSString *response) {
									 if (response) {
										 LOGI(@"check_payload_signature callback - response: %@", response);
									 }
								   }
									 onError:NULL
									   extra:NULL];
}

552 553 554 555 556
// Generic function to call sendXMLRPCRequestWithParams
- (BOOL)callXmlrpcRequestWithParams:(NSString *)method
						  onSuccess:(void (^)(NSString *))successBk
							onError:(void (^)(NSString *req))errorBk
							  extra:(NSString *)extra {
557 558 559
	if ([[self getPhoneNumber] length] > 0) {
		NSString *phoneNumber = [self getPhoneNumber];
		NSString *password = [self getPassword];
560 561 562 563 564 565 566 567 568 569 570
		NSArray *args;
		if (extra != NULL)
			args = @[ phoneNumber, password, @"", extra ];
		else
			args = @[ phoneNumber, password, @"" ];
		if (successBk && errorBk)
			[XMLRPCHelper.self sendXMLRPCRequestWithParams:method withParams:args onSuccess:successBk onError:errorBk];
		else if (successBk)
			[XMLRPCHelper.self sendXMLRPCRequestWithParams:method withParams:args onSuccess:successBk];
		else
			[XMLRPCHelper.self sendXMLRPCRequestWithParams:method withParams:args];
571
		return TRUE;
572

573 574 575 576 577 578
	} else {
		LOGW(@"No SIP URI configured, can't get account expiration date.");
		return FALSE;
	}
}

579
#pragma mark Other
580
#else
581
- (void)postNotificationforStatus:(IAPPurchaseNotificationStatus)status {
582
	_status = status;
583
	[NSNotificationCenter.defaultCenter postNotificationName:status object:self userInfo:nil];
584 585
	LOGE(@"Not supported, triggering %@", status);
}
586

587
#endif
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
588
@end