mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
InApp: handle error cases and standardize notifications name
This commit is contained in:
parent
6826e0112a
commit
993e062ccf
5 changed files with 213 additions and 141 deletions
|
|
@ -26,16 +26,17 @@
|
|||
|
||||
@end
|
||||
|
||||
#define IAPNotReadyYet @"IAPNotReadyYet" // startup status, manager is not ready yet
|
||||
#define IAPAvailableSucceeded @"IAPAvailableSucceeded" //no data
|
||||
#define IAPAvailableFailed @"IAPAvailableFailed" //data: error_msg
|
||||
#define IAPPurchaseTrying @"IAPPurchaseTrying" //data: product_id
|
||||
#define IAPPurchaseFailed @"IAPPurchaseFailed" //data: product_id, error_msg
|
||||
#define IAPPurchaseSucceeded @"IAPPurchaseSucceeded" //data: product_id, expires_date
|
||||
#define IAPRestoreFailed @"IAPRestoreFailed" //data: error_msg
|
||||
#define IAPRestoreSucceeded @"IAPRestoreSucceeded" //no data
|
||||
#define IAPReceiptFailed @"IAPReceiptFailed" //data: error_msg
|
||||
#define IAPReceiptSucceeded @"IAPReceiptSucceeded" //no data
|
||||
#define kIAPNotReady @"IAPNotReady" // startup status, manager is not ready yet
|
||||
#define kIAPReady @"IAPReady" //no data
|
||||
#define kIAPPurchaseTrying @"IAPPurchaseTrying" //data: product_id
|
||||
#define kIAPPurchaseCancelled @"IAPPurchaseCancelled" //data: product_id
|
||||
#define kIAPPurchaseFailed @"IAPPurchaseFailed" //data: product_id, error_msg
|
||||
#define kIAPPurchaseSucceeded @"IAPPurchaseSucceeded" //data: product_id, expires_date
|
||||
#define kIAPPurchaseExpired @"IAPPurchaseExpired" //data: product_id, expires_date
|
||||
#define kIAPRestoreFailed @"IAPRestoreFailed" //data: error_msg
|
||||
#define kIAPRestoreSucceeded @"IAPRestoreSucceeded" //no data
|
||||
#define kIAPReceiptFailed @"IAPReceiptFailed" //data: error_msg
|
||||
#define kIAPReceiptSucceeded @"IAPReceiptSucceeded" //no data
|
||||
typedef NSString* IAPPurchaseNotificationStatus;
|
||||
|
||||
// InAppProductsManager take care of any in app purchase accessible within Linphone
|
||||
|
|
@ -51,21 +52,31 @@ typedef NSString* IAPPurchaseNotificationStatus;
|
|||
NSString *latestReceiptMD5;
|
||||
}
|
||||
|
||||
// needed because request:didFailWithError method is already used by SKProductsRequestDelegate...
|
||||
@property (nonatomic, retain) InAppProductsXMLRPCDelegate *xmlrpc;
|
||||
|
||||
@property (nonatomic, retain) IAPPurchaseNotificationStatus status;
|
||||
@property (nonatomic, strong) NSMutableArray *productsAvailable;
|
||||
@property (nonatomic, strong) NSMutableArray *productsIDPurchased;
|
||||
|
||||
// TRUE when in app purchase capability is available - not modified during runtime
|
||||
@property (readonly) BOOL enabled;
|
||||
// TRUE when manager is correctly set up - must first retrieve products available and validate current receipt on our server
|
||||
@property (readonly) BOOL initialized;
|
||||
// TRUE if manager is available for usage - will be FALSE if an operation is already in progress or if not initialized or not enabled
|
||||
@property (readonly) BOOL available;
|
||||
|
||||
- (BOOL)isPurchasedWithID:(NSString*)productId;
|
||||
- (void)purchaseAccount:(NSString*)sipURI withPassword:(NSString*)password;
|
||||
// Purchase an account. You should not use this if manager is not available yet.
|
||||
- (BOOL)purchaseAccount:(NSString *)phoneNumber withPassword:(NSString *)password andEmail:(NSString*)email;
|
||||
// Purchase a product. You should not use this if manager is not available yet.
|
||||
- (BOOL)purchaseWitID:(NSString *)productID;
|
||||
|
||||
// restore user purchases. Must be at first launch or a user action ONLY.
|
||||
- (void)restore;
|
||||
- (void)retrievePurchases;
|
||||
// restore user purchases. You should not use this if manager is not available yet. Must be at a user action ONLY.
|
||||
- (BOOL)restore;
|
||||
// retrieve purchases on our server. You should not use this if manager is not available yet.
|
||||
// Warning: on first run, this will open a popup to user to provide iTunes Store credentials
|
||||
- (BOOL)retrievePurchases;
|
||||
|
||||
|
||||
// internal API only due to methods conflict
|
||||
- (void)XMLRPCRequest:(XMLRPCRequest *)request didReceiveResponse:(XMLRPCResponse *)response;
|
||||
// internal API only due to methods conflict
|
||||
|
|
|
|||
|
|
@ -31,10 +31,13 @@
|
|||
#import "PhoneMainView.h"
|
||||
#import "InAppProductsViewController.h"
|
||||
|
||||
@implementation InAppProductsManager {
|
||||
NSString *accountCreationSipURI;
|
||||
NSString *accountCreationPassword;
|
||||
}
|
||||
@interface InAppProductsManager()
|
||||
@property (retain, nonatomic) NSDictionary *accountCreationData;
|
||||
// needed because request:didFailWithError method is already used by SKProductsRequestDelegate...
|
||||
@property (nonatomic, retain) InAppProductsXMLRPCDelegate *xmlrpc;
|
||||
@end
|
||||
|
||||
@implementation InAppProductsManager
|
||||
|
||||
// LINPHONE_CAPABILITY_INAPP_PURCHASE must be defined in Linphone Build Settings
|
||||
#if LINPHONE_CAPABILITY_INAPP_PURCHASE && !TARGET_IPHONE_SIMULATOR
|
||||
|
|
@ -42,12 +45,12 @@
|
|||
- (instancetype)init {
|
||||
if ((self = [super init]) != nil) {
|
||||
_enabled = (([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) && ([SKPaymentQueue canMakePayments]) && ([[LinphoneManager instance] lpConfigBoolForKey:@"enabled" forSection:@"in_app_purchase"]));
|
||||
_initialized = false;
|
||||
_available = false;
|
||||
|
||||
if (_enabled) {
|
||||
LOGE(@"Todo: //waiting for parent approval");
|
||||
LOGE(@"Todo: if cancel date, no purchase");
|
||||
_xmlrpc = [[InAppProductsXMLRPCDelegate alloc] init];
|
||||
_status = IAPNotReadyYet;
|
||||
self.xmlrpc = [[InAppProductsXMLRPCDelegate alloc] init];
|
||||
_status = kIAPNotReady;
|
||||
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
|
||||
[self loadProducts];
|
||||
}
|
||||
|
|
@ -70,54 +73,68 @@
|
|||
return false;
|
||||
}
|
||||
|
||||
- (SKProduct*) productIDAvailable:(NSString*)productID {
|
||||
if (!_enabled) return nil;
|
||||
for (SKProduct *product in _productsAvailable) {
|
||||
if ([product.productIdentifier compare:productID options:NSLiteralSearch] == NSOrderedSame) {
|
||||
return product;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
- (BOOL)purchaseWitID:(NSString *)productID {
|
||||
if (!_enabled) return FALSE;
|
||||
if (!_enabled||!_initialized||!_available) {
|
||||
NSDictionary* dict = @{@"product_id":productID, @"error_msg": NSLocalizedString(@"In apps not ready yet", nil)};
|
||||
[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
|
||||
return FALSE;
|
||||
}
|
||||
SKProduct *prod = [self productIDAvailable:productID];
|
||||
if (prod) {
|
||||
NSDictionary* dict = @{@"product_id": productID};
|
||||
[self postNotificationforStatus:IAPPurchaseTrying withDict:dict];
|
||||
[self postNotificationforStatus:kIAPPurchaseTrying withDict:dict];
|
||||
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:prod];
|
||||
_available = false;
|
||||
[[SKPaymentQueue defaultQueue] addPayment:payment];
|
||||
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
|
||||
return TRUE;
|
||||
} else {
|
||||
NSDictionary* dict = @{@"product_id": productID, @"error_msg": @"Product not available"};
|
||||
[self postNotificationforStatus:IAPPurchaseFailed withDict:dict];
|
||||
[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)purchaseAccount:(NSString *)sipURI withPassword:(NSString *)password {
|
||||
if (!_enabled) return;
|
||||
NSString* productID = [[LinphoneManager instance] lpConfigStringForKey:@"paid_account_id" forSection:@"in_app_purchase"];
|
||||
accountCreationSipURI = [sipURI retain];
|
||||
accountCreationPassword = [password retain];
|
||||
if ([self purchaseWitID:productID]) {
|
||||
accountCreationPassword = nil;
|
||||
accountCreationSipURI = nil;
|
||||
- (BOOL)purchaseAccount:(NSString *)phoneNumber withPassword:(NSString *)password andEmail:(NSString*)email {
|
||||
if (phoneNumber) {
|
||||
NSString* productID = [[LinphoneManager instance] lpConfigStringForKey:@"paid_account_id" forSection:@"in_app_purchase"];
|
||||
self.accountCreationData = @{ @"phoneNumber":[phoneNumber retain], @"password":[password retain], @"email":[email retain] };
|
||||
|
||||
if (![self purchaseWitID:productID]) {
|
||||
self.accountCreationData = nil;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
-(void)restore {
|
||||
if (!_enabled) return;
|
||||
-(BOOL)restore {
|
||||
if (!_enabled||!_initialized||!_available) {
|
||||
NSDictionary* dict = @{@"error_msg": NSLocalizedString(@"In apps not ready yet", nil)};
|
||||
[self postNotificationforStatus:kIAPRestoreFailed withDict:dict];
|
||||
return FALSE;
|
||||
}
|
||||
LOGI(@"Restoring user purchases...");
|
||||
//force new query of our server
|
||||
latestReceiptMD5 = nil;
|
||||
_available = false;
|
||||
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
- (void)retrievePurchases {
|
||||
if (!_enabled) return;
|
||||
[self validateReceipt:nil];
|
||||
- (BOOL)retrievePurchases {
|
||||
if (!_enabled|!_initialized|!_available) {
|
||||
NSDictionary* dict = @{@"error_msg": NSLocalizedString(@"In apps not ready yet", nil)};
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark ProductListLoading
|
||||
|
|
@ -137,28 +154,34 @@
|
|||
_productsAvailable = [[NSMutableArray arrayWithArray: response.products] retain];
|
||||
|
||||
LOGI(@"Found %lu products available", (unsigned long)_productsAvailable.count);
|
||||
|
||||
_initialized = true;
|
||||
if (response.invalidProductIdentifiers.count > 0) {
|
||||
for (NSString *invalidIdentifier in response.invalidProductIdentifiers) {
|
||||
LOGW(@"Found product Identifier with invalid ID '%@'", invalidIdentifier);
|
||||
}
|
||||
NSDictionary* dict = @{@"error_msg": NSLocalizedString(@"Invalid products identifier", nil)};
|
||||
[self postNotificationforStatus:IAPAvailableFailed withDict:dict];
|
||||
} else {
|
||||
[self postNotificationforStatus:IAPAvailableSucceeded withDict:nil];
|
||||
_available = true;
|
||||
[self postNotificationforStatus:kIAPReady withDict:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
|
||||
LOGE(@"Impossible to retrieve list of products: %@", [error localizedFailureReason]);
|
||||
NSDictionary* dict = @{@"error_msg": error ? [error localizedDescription] : NSLocalizedString(@"Product not available", commit)};
|
||||
[self postNotificationforStatus:IAPAvailableFailed withDict:dict];
|
||||
LOGE(@"Impossible to retrieve list of products: %@", [error localizedDescription]);
|
||||
//well, let's retry...
|
||||
[self loadProducts];
|
||||
}
|
||||
|
||||
#pragma mark Other
|
||||
|
||||
- (SKProduct*) productIDAvailable:(NSString*)productID {
|
||||
if (!_enabled||!_initialized) return nil;
|
||||
for (SKProduct *product in _productsAvailable) {
|
||||
if ([product.productIdentifier compare:productID options:NSLiteralSearch] == NSOrderedSame) {
|
||||
return product;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)requestDidFinish:(SKRequest *)request {
|
||||
if([request isKindOfClass:[SKReceiptRefreshRequest class]]) {
|
||||
|
|
@ -188,47 +211,48 @@
|
|||
return;
|
||||
}
|
||||
|
||||
LOGI(@"Found appstore receipt");
|
||||
receiptBase64 = [[NSData dataWithContentsOfURL:receiptURL] base64EncodedStringWithOptions:0];
|
||||
LOGI(@"Found appstore receipt %@", [receiptBase64 md5]);
|
||||
|
||||
//only check the receipt if it has changed
|
||||
if (latestReceiptMD5 == nil || ! [latestReceiptMD5 isEqualToString:[receiptBase64 md5]]) {
|
||||
if (latestReceiptMD5 == nil || ![latestReceiptMD5 isEqualToString:[receiptBase64 md5]]) {
|
||||
// We must validate the receipt on our server
|
||||
NSURL *URL = [NSURL URLWithString:[[LinphoneManager instance] lpConfigStringForKey:@"receipt_validation_url" forSection:@"in_app_purchase"]];
|
||||
|
||||
XMLRPCRequest *request = [[XMLRPCRequest alloc] initWithURL: URL];
|
||||
|
||||
// Happen when restoring user purchases at application start or if user click the "restore" button
|
||||
if (transaction == nil) {
|
||||
[request setMethod: @"get_expiration_date" withParameters:[NSArray arrayWithObjects:
|
||||
@"",
|
||||
receiptBase64,
|
||||
@"",
|
||||
@"apple",
|
||||
nil]];
|
||||
} else if ([transaction.payment.productIdentifier isEqualToString:[[LinphoneManager instance] lpConfigStringForKey:@"paid_account_id" forSection:@"in_app_purchase"]]) {
|
||||
// transaction is null when restoring user purchases at application start or if user clicks the "restore" button
|
||||
if (!transaction || [transaction.payment.productIdentifier isEqualToString:[[LinphoneManager instance] lpConfigStringForKey:@"paid_account_id" forSection:@"in_app_purchase"]]) {
|
||||
//buying for the first time: need to create the account
|
||||
if ([transaction.transactionIdentifier isEqualToString:transaction.originalTransaction.transactionIdentifier]) {
|
||||
//if ([transaction.transactionIdentifier isEqualToString:transaction.originalTransaction.transactionIdentifier]) {
|
||||
if (self.accountCreationData.count == 3) {
|
||||
[request setMethod: @"create_account_from_in_app_purchase" withParameters:[NSArray arrayWithObjects:
|
||||
@"",
|
||||
accountCreationSipURI,
|
||||
accountCreationPassword,
|
||||
[_accountCreationData objectForKey:@"phoneNumber"],
|
||||
receiptBase64,
|
||||
@"",
|
||||
@"apple",
|
||||
[_accountCreationData objectForKey:@"email"],
|
||||
nil]];
|
||||
accountCreationSipURI = nil;
|
||||
accountCreationPassword = nil;
|
||||
//simply renewing
|
||||
self.accountCreationData = nil;
|
||||
// otherwise simply renewing
|
||||
} else {
|
||||
if ([[self getPhoneNumber] length] > 0) {
|
||||
[request setMethod: @"get_expiration_date" withParameters:[NSArray arrayWithObjects:
|
||||
@"",
|
||||
[self getPhoneNumber],
|
||||
receiptBase64,
|
||||
@"",
|
||||
@"apple",
|
||||
nil]];
|
||||
} else {
|
||||
LOGW(@"No SIP URI configured, doing nothing");
|
||||
_available = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGE(@"Hum, not handling product with ID %@", transaction.payment.productIdentifier);
|
||||
_available = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -240,9 +264,26 @@
|
|||
[request release];
|
||||
} else {
|
||||
LOGW(@"Not checking receipt since it has already been done!");
|
||||
_available = true;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)getPhoneNumber {
|
||||
NSString * phoneNumber = @"";
|
||||
LinphoneProxyConfig *config = linphone_core_get_default_proxy_config([LinphoneManager getLc]);
|
||||
if (config) {
|
||||
const char *identity = linphone_proxy_config_get_identity(config);
|
||||
if (identity) {
|
||||
LinphoneAddress *addr = linphone_address_new(identity);
|
||||
if (addr) {
|
||||
phoneNumber = [NSString stringWithUTF8String:linphone_address_get_username(addr)];
|
||||
linphone_address_destroy(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
|
||||
for(SKPaymentTransaction * transaction in transactions) {
|
||||
switch (transaction.transactionState) {
|
||||
|
|
@ -250,19 +291,31 @@
|
|||
break;
|
||||
case SKPaymentTransactionStatePurchased:
|
||||
case SKPaymentTransactionStateRestored: {
|
||||
[self validateReceipt: transaction];
|
||||
if (!_initialized) {
|
||||
LOGW(@"Pending transactions before end of initialization, not verifying receipt");
|
||||
} else {
|
||||
[self validateReceipt: transaction];
|
||||
}
|
||||
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
|
||||
break;
|
||||
}
|
||||
case SKPaymentTransactionStateDeferred:
|
||||
//waiting for parent approval
|
||||
LOGI(@"Waiting for parent approval...");
|
||||
//could do some UI stuff
|
||||
break;
|
||||
case SKPaymentTransactionStateFailed: {
|
||||
NSString* errlast = [NSString stringWithFormat:@"Purchase of %@ failed: %@.",transaction.payment.productIdentifier,transaction.error.localizedDescription];
|
||||
LOGE(@"SKPaymentTransactionStateFailed: %@", errlast);
|
||||
NSDictionary* dict = @{@"product_id": transaction.payment.productIdentifier, @"error_msg": errlast};
|
||||
[self postNotificationforStatus:IAPPurchaseFailed withDict:dict];
|
||||
_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 of %@ failed: %@.",transaction.payment.productIdentifier,transaction.error.localizedDescription];
|
||||
LOGE(@"SKPaymentTransactionStateFailed: %@", errlast);
|
||||
NSDictionary* dict = @{@"product_id": transaction.payment.productIdentifier, @"error_msg": errlast};
|
||||
[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -278,7 +331,7 @@
|
|||
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
|
||||
if (error.code != SKErrorPaymentCancelled) {
|
||||
NSDictionary* dict = @{@"error_msg": [error localizedDescription]};
|
||||
[self postNotificationforStatus:IAPRestoreFailed withDict:dict];
|
||||
[self postNotificationforStatus:kIAPRestoreFailed withDict:dict];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,52 +341,51 @@
|
|||
|
||||
-(void)postNotificationforStatus:(IAPPurchaseNotificationStatus)status withDict:(NSDictionary*)dict {
|
||||
_status = status;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:status object:self userInfo:dict];
|
||||
LOGI(@"Triggering notification for status %@", status);
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:status object:self userInfo:dict];
|
||||
}
|
||||
|
||||
- (void)XMLRPCRequest:(XMLRPCRequest *)request didReceiveResponse:(XMLRPCResponse *)response {
|
||||
if (!_enabled) return;
|
||||
|
||||
_available = true;
|
||||
|
||||
LOGI(@"XMLRPC response %@: %@", [request method], [response body]);
|
||||
NSString* productID = [[LinphoneManager instance] lpConfigStringForKey:@"paid_account_id" forSection:@"in_app_purchase"];
|
||||
|
||||
// validation succeeded
|
||||
if(! [response isFault] && [response object] != nil) {
|
||||
if([[request method] isEqualToString:@"get_expiration_date"]) {
|
||||
//first remove it from list
|
||||
if(([[request method] isEqualToString:@"get_expiration_date"])
|
||||
|| ([[request method] isEqualToString:@"create_account_from_in_app_purchase"])) {
|
||||
[_productsIDPurchased removeObject:productID];
|
||||
|
||||
double expirationTime = [[response object] doubleValue] / 1000;
|
||||
NSDate * expirationDate = [NSDate dateWithTimeIntervalSince1970:expirationTime];
|
||||
NSDate *now = [[NSDate alloc] init];
|
||||
if (([expirationDate earlierDate:now] == expirationDate) || (expirationTime < 1)) {
|
||||
LOGI(@"Account has expired");
|
||||
[[PhoneMainView instance] changeCurrentView:[InAppProductsViewController compositeViewDescription]];
|
||||
expirationDate = [NSDate dateWithTimeIntervalSince1970:0];
|
||||
} else {
|
||||
[_productsIDPurchased addObject:productID];
|
||||
}
|
||||
NSDictionary* dict = @{@"product_id": productID, @"expires_date": expirationDate};
|
||||
[self postNotificationforStatus:IAPReceiptSucceeded withDict:dict];
|
||||
} else if([[request method] isEqualToString:@"create_account_from_in_app_purchase"]) {
|
||||
[_productsIDPurchased removeObject:productID];
|
||||
|
||||
double timeinterval = [[response object] doubleValue] / 1000;
|
||||
if (timeinterval != -2) {
|
||||
NSDate *expirationDate = [NSDate dateWithTimeIntervalSince1970:timeinterval];
|
||||
// response object can either be expiration date (long long number or an error string)
|
||||
double timeinterval = [[response object] doubleValue];
|
||||
if (timeinterval != 0.0f) {
|
||||
NSDate *expirationDate = [NSDate dateWithTimeIntervalSince1970:timeinterval/1000];
|
||||
NSDate *now = [[NSDate alloc] init];
|
||||
NSDictionary* dict = @{@"product_id": productID, @"expires_date": expirationDate};
|
||||
if ([expirationDate earlierDate:now] == expirationDate) {
|
||||
LOGI(@"Account has expired");
|
||||
[[PhoneMainView instance] changeCurrentView:[InAppProductsViewController compositeViewDescription]];
|
||||
expirationDate = [NSDate dateWithTimeIntervalSince1970:0];
|
||||
LOGW(@"Account has expired");
|
||||
[self postNotificationforStatus:kIAPPurchaseExpired withDict:dict];
|
||||
} else {
|
||||
[_productsIDPurchased addObject:productID];
|
||||
[self postNotificationforStatus:kIAPPurchaseSucceeded withDict:dict];
|
||||
}
|
||||
NSDictionary* dict = @{@"product_id": productID, @"expires_date": expirationDate};
|
||||
[self postNotificationforStatus:IAPPurchaseSucceeded withDict:dict];
|
||||
} else {
|
||||
NSDictionary* dict = @{@"product_id": productID, @"error_msg": @"Unknown error"};
|
||||
[self postNotificationforStatus:IAPPurchaseFailed withDict:dict];
|
||||
NSString *error = [response object];
|
||||
LOGE(@"Failed with error %@", error);
|
||||
NSString *errorMsg;
|
||||
if ([error isEqualToString:@"ERROR_ACCOUNT_ALREADY_EXISTS"]) {
|
||||
errorMsg=NSLocalizedString(@"You have already registered an account.", nil);
|
||||
} else if ([error isEqualToString:@"ERROR_ACCOUNT_DOESNT_EXIST"]) {
|
||||
errorMsg=NSLocalizedString(@"You have already purchased an account but it does not exist anymore.", nil);
|
||||
} else if ([error isEqualToString:@"ERROR_PURCHASE_CANCELLED"]) {
|
||||
errorMsg=NSLocalizedString(@"You cancelled your account.", nil);
|
||||
} else {
|
||||
errorMsg=[NSString stringWithFormat:NSLocalizedString(@"Unknown error (%@).", nil), error];
|
||||
}
|
||||
NSDictionary* dict = @{@"product_id": productID, @"error_msg": NSLocalizedString(errorMsg, nil)};
|
||||
[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -354,13 +406,15 @@
|
|||
|
||||
latestReceiptMD5 = nil;
|
||||
NSDictionary* dict = @{@"error_msg": errorString};
|
||||
[self postNotificationforStatus:IAPReceiptFailed withDict:dict];
|
||||
[self postNotificationforStatus:kIAPPurchaseFailed withDict:dict];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)XMLRPCRequest:(XMLRPCRequest *)request didFailWithError:(NSError *)error {
|
||||
if (!_enabled) return;
|
||||
|
||||
_available = true;
|
||||
|
||||
LOGE(@"Communication issue (%@)", [error localizedDescription]);
|
||||
NSString *errorString = [NSString stringWithFormat:NSLocalizedString(@"Communication issue (%@)", nil), [error localizedDescription]];
|
||||
UIAlertView* errorView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Communication issue", nil)
|
||||
|
|
@ -372,19 +426,24 @@
|
|||
[errorView release];
|
||||
latestReceiptMD5 = nil;
|
||||
NSDictionary* dict = @{@"error_msg": errorString};
|
||||
[self postNotificationforStatus:IAPReceiptFailed withDict:dict];
|
||||
[self postNotificationforStatus:kIAPReceiptFailed withDict:dict];
|
||||
}
|
||||
#else
|
||||
- (void)purchaseAccount:(NSString *)sipURI withPassword:(NSString *)password { LOGE(@"Not supported"); }
|
||||
- (void)purchaseWithID:(NSString *)productId { LOGE(@"Not supported"); }
|
||||
- (void)restore { LOGE(@"Not supported"); }
|
||||
- (void)XMLRPCRequest:(XMLRPCRequest *)request didFailWithError:(NSError *)error { LOGE(@"Not supported"); }
|
||||
- (void)XMLRPCRequest:(XMLRPCRequest *)request didReceiveResponse:(XMLRPCResponse *)response { LOGE(@"Not supported"); }
|
||||
- (BOOL)isPurchasedWithID:(NSString *)productId { LOGE(@"Not supported"); return FALSE; }
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { LOGE(@"Not supported"); }
|
||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { LOGE(@"Not supported"); }
|
||||
- (void)retrievePurchases { LOGE(@"Not supported"); }
|
||||
- (BOOL)purchaseWitID:(NSString *)productID { return FALSE; }
|
||||
-(void)postNotificationforStatus:(IAPPurchaseNotificationStatus)status {
|
||||
_status = status;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:status object:self userInfo:nil];
|
||||
LOGE(@"Not supported, triggering %@", status);
|
||||
}
|
||||
- (BOOL)purchaseAccount:(NSString *)phoneNumber withPassword:(NSString *)password andEmail:(NSString*)email { [self postNotificationforStatus:kIAPPurchaseFailed]; return false; }
|
||||
- (BOOL)restore { [self postNotificationforStatus:kIAPRestoreFailed]; return false; }
|
||||
- (BOOL)retrievePurchases { [self postNotificationforStatus:kIAPRestoreFailed]; return false; }
|
||||
- (BOOL)purchaseWitID:(NSString *)productID { [self postNotificationforStatus:kIAPPurchaseFailed]; return FALSE; }
|
||||
- (BOOL)isPurchasedWithID:(NSString *)productId { return FALSE; }
|
||||
|
||||
- (void)XMLRPCRequest:(XMLRPCRequest *)request didFailWithError:(NSError *)error { }
|
||||
- (void)XMLRPCRequest:(XMLRPCRequest *)request didReceiveResponse:(XMLRPCResponse *)response { }
|
||||
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { }
|
||||
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { }
|
||||
#endif
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
for (NSString* notification in [NSArray arrayWithObjects:IAPAvailableSucceeded, IAPRestoreSucceeded, IAPPurchaseSucceeded, IAPReceiptSucceeded, IAPPurchaseTrying, nil]) {
|
||||
for (NSString* notification in [NSArray arrayWithObjects:kIAPReady, kIAPRestoreSucceeded, kIAPPurchaseSucceeded, kIAPReceiptSucceeded, kIAPPurchaseTrying, nil]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(onIAPPurchaseNotification:)
|
||||
name:notification
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
for (NSString* notification in [NSArray arrayWithObjects:IAPAvailableSucceeded, IAPRestoreSucceeded, IAPPurchaseSucceeded, IAPReceiptSucceeded, IAPPurchaseTrying, nil]) {
|
||||
for (NSString* notification in [NSArray arrayWithObjects:kIAPReady, kIAPRestoreSucceeded, kIAPPurchaseSucceeded, kIAPReceiptSucceeded, kIAPPurchaseTrying, nil]) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:notification
|
||||
object:nil];
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
- (void)onIAPPurchaseNotification:(NSNotification*)notif {
|
||||
InAppProductsManager *iapm = [[LinphoneManager instance] iapManager];
|
||||
[[_tableController tableView] reloadData];
|
||||
[_waitView setHidden:([[iapm productsAvailable] count] != 0 && ![notif.name isEqualToString:IAPPurchaseTrying])];
|
||||
[_waitView setHidden:([[iapm productsAvailable] count] != 0 && ![notif.name isEqualToString:kIAPPurchaseTrying])];
|
||||
}
|
||||
|
||||
#pragma mark - UICompositeViewDelegate Functions
|
||||
|
|
|
|||
|
|
@ -293,11 +293,12 @@ struct codec_name_pref_table codec_pref_table[]={
|
|||
//set default values for first boot
|
||||
if (lp_config_get_string(configDb,LINPHONERC_APPLICATION_KEY,"debugenable_preference",NULL)==NULL){
|
||||
#ifdef DEBUG
|
||||
[self lpConfigSetBool:TRUE forKey:@"debugenable_preference"];
|
||||
[self lpConfigSetBool:TRUE forKey:@"debugenable_preference"];
|
||||
#else
|
||||
[self lpConfigSetBool:FALSE forKey:@"debugenable_preference"];
|
||||
#endif
|
||||
}
|
||||
|
||||
_iapManager = [[InAppProductsManager alloc] init];
|
||||
|
||||
[self migrateFromUserPrefs];
|
||||
|
|
@ -1232,7 +1233,6 @@ void networkReachabilityCallBack(SCNetworkReachabilityRef target, SCNetworkReach
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
static LinphoneCoreVTable linphonec_vtable = {
|
||||
|
|
@ -1370,8 +1370,6 @@ static LinphoneCoreVTable linphonec_vtable = {
|
|||
}
|
||||
linphone_core_enable_video(theLinphoneCore, FALSE, FALSE);
|
||||
}
|
||||
// Query our in-app server when core is ready in order to retrieve InApp purchases
|
||||
[_iapManager retrievePurchases];
|
||||
|
||||
LOGW(@"Linphone [%s] started on [%s]", linphone_core_get_version(), [[UIDevice currentDevice].model cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
|
||||
|
|
@ -1483,6 +1481,7 @@ static BOOL libStarted = FALSE;
|
|||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionInterrupted:) name:AVAudioSessionInterruptionNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(globalStateChangedNotificationHandler:) name:kLinphoneGlobalStateUpdate object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(configuringStateChangedNotificationHandler:) name:kLinphoneConfiguringStateUpdate object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inappReady:) name:kIAPReady object:nil];
|
||||
|
||||
/*call iterate once immediately in order to initiate background connections with sip server or remote provisioning grab, if any */
|
||||
linphone_core_iterate(theLinphoneCore);
|
||||
|
|
@ -2261,7 +2260,10 @@ static void audioRouteChangeListenerCallback (
|
|||
}
|
||||
}
|
||||
|
||||
#pragma InApp Purchase
|
||||
|
||||
#pragma mark - InApp Purchase events
|
||||
|
||||
- (void)inappReady:(NSNotification*)notif {
|
||||
// Query our in-app server to retrieve InApp purchases
|
||||
[_iapManager retrievePurchases];
|
||||
}
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -172,15 +172,15 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(inAppPurchaseNotification:)
|
||||
name:IAPPurchaseSucceeded
|
||||
name:kIAPPurchaseSucceeded
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(inAppPurchaseNotification:)
|
||||
name:IAPPurchaseTrying
|
||||
name:kIAPPurchaseTrying
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(inAppPurchaseNotification:)
|
||||
name:IAPPurchaseFailed
|
||||
name:kIAPPurchaseFailed
|
||||
object:nil];
|
||||
}
|
||||
|
||||
|
|
@ -200,13 +200,13 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
name:UIKeyboardWillHideNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:IAPPurchaseFailed
|
||||
name:kIAPPurchaseFailed
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:IAPPurchaseTrying
|
||||
name:kIAPPurchaseTrying
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:IAPPurchaseSucceeded
|
||||
name:kIAPPurchaseSucceeded
|
||||
object:nil];
|
||||
}
|
||||
|
||||
|
|
@ -905,11 +905,11 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
NSString *paidAccountID = [[LinphoneManager instance] lpConfigStringForKey:@"paid_account_id" forSection:@"in_app_purchase"];
|
||||
if (wasWaitingForInApp
|
||||
&& [paidAccountID isEqualToString:[notification.userInfo objectForKey:@"product_id"]]) {
|
||||
if ([notification.name isEqual:IAPPurchaseTrying]) {
|
||||
if ([notification.name isEqual:kIAPPurchaseTrying]) {
|
||||
[waitView setHidden:false];
|
||||
} else if ([notification.name isEqual:IAPPurchaseFailed]) {
|
||||
} else if ([notification.name isEqual:kIAPPurchaseFailed]) {
|
||||
[waitView setHidden:true];
|
||||
} else if ([notification.name isEqual:IAPPurchaseSucceeded]) {
|
||||
} else if ([notification.name isEqual:kIAPPurchaseSucceeded]) {
|
||||
[waitView setHidden:true];
|
||||
//now that the purchase is made, let's create the account.
|
||||
[self onPurchaseAccountClick:self];
|
||||
|
|
@ -933,7 +933,7 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
NSString *identity = [self identityFromUsername:username];
|
||||
[self checkUserExist:identity];
|
||||
} else {
|
||||
[iapm purchaseAccount:username withPassword:password];
|
||||
[iapm purchaseAccount:username withPassword:password andEmail:email];
|
||||
// inAppPurchaseNotification will take care of bringing us to the next view now
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue