This commit is contained in:
Gautier Pelloux-Prayer 2015-04-17 15:11:05 +02:00
parent aefc92a0dd
commit 5c02755992
7 changed files with 88 additions and 28 deletions

View file

@ -21,28 +21,28 @@
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="title" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="6" id="Np2-5N-fTR" userLabel="ptitle">
<rect key="frame" x="0.0" y="6" width="55" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" tag="1" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="description..." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" id="4s8-UV-skT" userLabel="pdescription">
<rect key="frame" x="119" y="6" width="136" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" tag="2" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="$0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" id="jzs-nJ-jrK" userLabel="pprice">
<rect key="frame" x="63" y="6" width="48" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" id="Bcv-Vz-Tq1" userLabel="ppurchased">
<rect key="frame" x="263" y="6" width="79" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
</switch>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>

View file

@ -30,14 +30,16 @@ extern NSString *const kLinphoneIAPurchaseNotification;
#define IAPPurchaseSucceeded @"IAPPurchaseSucceeded"
#define IAPRestoreFailed @"IAPRestoreFailed"
#define IAPRestoreSucceeded @"IAPRestoreSucceeded"
#define IAPReceiptFailed @"IAPReceiptFailed"
#define IAPReceiptSucceeded @"IAPReceiptSucceeded"
typedef NSString* IAPPurchaseNotificationStatus;
@property (nonatomic, retain) IAPPurchaseNotificationStatus status;
@property (nonatomic, copy) NSString *errlast;
@property (nonatomic, strong) NSMutableArray *productsAvailable;
@property (nonatomic, strong) NSMutableArray *productsPurchased;
@property (nonatomic, strong) NSMutableArray *productsRestored;
@property (nonatomic, strong) NSMutableArray *productsIDPurchased;
- (void)loadProducts;
- (BOOL)isPurchased:(SKProduct*)product;

View file

@ -19,6 +19,7 @@
#import "InAppProductsManager.h"
#import "Utils.h"
#import "LinphoneManager.h"
NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotification";
@ -29,6 +30,7 @@ NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotificati
- (instancetype)init {
if ((self = [super init]) != nil) {
ready = false;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[self loadProducts];
}
return self;
@ -40,6 +42,7 @@ NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotificati
}
//TODO: move this list elsewhere
NSArray * list = [[[NSArray alloc] initWithArray:@[@"test.auto_renew_7days", @"test.non_renew", @"test.one_time", @"test.auto_renew_1month_withfree"]] autorelease];
_productsIDPurchased = [[NSMutableArray alloc] initWithCapacity:0];
SKProductsRequest *productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:[NSSet setWithArray:list]];
@ -49,7 +52,7 @@ NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotificati
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response {
_productsAvailable= [[NSMutableArray arrayWithArray: response.products] retain];
_productsAvailable = [[NSMutableArray arrayWithArray: response.products] retain];
LOGI(@"Found %lu products available", (unsigned long)_productsAvailable.count);
@ -64,8 +67,8 @@ NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotificati
}
- (BOOL)isPurchased:(SKProduct*)product {
for (SKProduct *prod in _productsPurchased) {
if (prod == product) {
for (NSString *prod in _productsIDPurchased) {
if ([prod isEqual: product.productIdentifier]) {
bool isBought = true;
LOGE(@"%@ is %s bought.", product.localizedTitle, isBought?"":"NOT");
return isBought;
@ -87,11 +90,55 @@ NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotificati
-(void)restore {
LOGI(@"Restoring user purchases...");
if (! [SKPaymentQueue canMakePayments]) { LOGF(@"Not allowed to do in app purchase!!!"); }
_productsRestored = [[NSMutableArray alloc] initWithCapacity:0];
if (! [SKPaymentQueue canMakePayments]) {
LOGF(@"Not allowed to do in app purchase!!!");
}
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void)checkReceipt: (SKPaymentTransaction*)transaction {
NSData *receiptData = nil;
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
// Test whether the receipt is present at the above URL
if(![[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) {
receiptData = [NSData dataWithContentsOfURL:receiptURL];
} else {
LOGI(@"Could not find any receipt in application, using the transaction one!");
receiptData = transaction.transactionReceipt;
}
// We must validate the receipt on our server
NSURL *storeURL = [NSURL URLWithString:[[LinphoneManager instance] lpConfigStringForKey:@"inapp_receipt_validation_url"]];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:receiptData];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest
queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (data == nil) {
LOGE(@"Server replied nothing, aborting now.");
} else if (connectionError) {
LOGE(@"Could not verify the receipt, aborting now: (%d) %@.", connectionError.code, connectionError);
} else {
NSError *jsonError;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError == nil) {
// Hourray, we can finally unlock purchase stuff in app!
LOGI(@"In apps purchased are %@", jsonResponse);
LOGE(@"To do: [_productsIDPurchased addObject:transaction.payment.productIdentifier];");
[self postNotificationforStatus:IAPReceiptSucceeded];
return;
} else {
LOGE(@"Impossible to parse receipt JSON response, aborting now because of %@: %@.", jsonError, [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}
}
// Something failed when checking the receipt
[self postNotificationforStatus:IAPReceiptFailed];
}];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for(SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState) {
@ -99,9 +146,24 @@ NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotificati
break;
case SKPaymentTransactionStatePurchased:
case SKPaymentTransactionStateRestored:
[_productsPurchased addObject:transaction];
[self checkReceipt: transaction];
[self completeTransaction:transaction forStatus:IAPPurchaseSucceeded];
break;
// case SKPaymentTransactionStatePurchasing:
// break;
// case SKPaymentTransactionStateDeferred:
// [self transactionDeferred:transaction];
// break;
// case SKPaymentTransactionStatePurchased:
// [self completeTransaction:transaction];
// break;
// case SKPaymentTransactionStateRestored:
// [self restoreTransaction:transaction];
// break;
// case SKPaymentTransactionStateFailed:
// [self failedTransaction:transaction];
// break;
default:
_errlast = [NSString stringWithFormat:@"Purchase of %@ failed.",transaction.payment.productIdentifier];
[self completeTransaction:transaction forStatus:IAPPurchaseFailed];
@ -112,7 +174,7 @@ NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotificati
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
for(SKPaymentTransaction * transaction in transactions) {
NSLog(@"%@ was removed from the payment queue.", transaction.payment.productIdentifier);
LOGI(@"%@ was removed from the payment queue.", transaction.payment.productIdentifier);
}
}
@ -123,19 +185,8 @@ NSString *const kLinphoneIAPurchaseNotification = @"LinphoneIAProductsNotificati
}
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
LOGI(@"All restorable transactions have been processed by the payment queue.");
// for (SKPayment *payment in queue) {
// [queue transactions]
// [_productsRestored addObject:payment.productIdentifier];
// }
for (SKPaymentTransaction *transaction in queue.transactions) {
NSString *productID = transaction.payment.productIdentifier;
[_productsRestored addObject:productID];
NSLog (@"product id is %@" , productID);
}
}
-(void)postNotificationforStatus:(IAPPurchaseNotificationStatus)status {

View file

@ -25,6 +25,8 @@
selector:@selector(onIAPPurchaseNotification:)
name:kLinphoneIAPurchaseNotification
object:nil];
[[self tableView] reloadData];
}
- (void)viewWillDisappear:(BOOL)animated {
@ -36,7 +38,9 @@
}
- (void)onIAPPurchaseNotification:(NSNotification*)notif {
if ([[iapm status] isEqual: IAPAvailableSucceeded]) {
if ([[iapm status] isEqual: IAPAvailableSucceeded]
|| [[iapm status] isEqualToString: IAPRestoreSucceeded]
|| [[iapm status] isEqualToString: IAPPurchaseSucceeded]) {
[[self tableView] reloadData];
}
}

View file

@ -27,7 +27,7 @@
</tableView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="XrD-Mn-6EM" userLabel="restoreButton">
<rect key="frame" x="0.0" y="0.0" width="320" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES" flexibleMaxY="YES"/>
<state key="normal" title="Restore">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>

View file

@ -53,4 +53,5 @@ display_filter_auto_rotate=0
[app]
#contact_display_username_only=1
#contact_filter_on_default_domain=1
#contact_filter_on_default_domain=1
inapp_receipt_validation_url=https://linphone.org/inapp/veriyReceipt

View file

@ -51,4 +51,6 @@ stun=stun.linphone.org
[video]
display_filter_auto_rotate=0
[app]
inapp_receipt_validation_url=https://linphone.org/inapp/veriyReceipt