I am trying in app purchases for the first time. My question, why do I get no available product?
I have seen this asked before with either no answers or with answers referencing bad links.
I have a product configured on the iTunes with bundle id.
I have an in app purchase configured for that app.
In my code I do:
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productidentifiers];
productsRequest.delegate = self;
[productsRequest start] ;
I call that with the bundle ID for that in app purchase (i've tried the app ID too).
This delegate function gets called
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
but products is an empty array.
This does not get called:
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
This does:
- (void)requestDidFinish:(SKRequest *)request
What do I need to check to get the producs?
You have to use a real device, and you must also set up a test account in iTunes Connect.
you should use the IAP product ID instead of app id(or bundle ID). the IAP product ID is created by itune connect. "products is an empty array." means this id(whatever you're use) you requested from itunes store does not exisit.
here's my working code:
#define ProductID_IAPUNLOCKALL #"unlockall.myapp"
NSArray *product=[[NSArray alloc] initWithObjects:ProductID_IAPUNLOCKALL,nil];
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];
request.delegate=self;
[request start];
the "unlockall.myapp" is a uniq string defined in itunes connect.
Related
I am implementing inApp features with download hosted content feature. All products are free for unlimited time. Each product have one download associated and that is audio file.
Is possible to start download without user see dialog to buy free product. First user must give his apple id password, than he see dialog to buy free product and than dialog that product is succesfully purchased.Than download can be requested.
But since all products are free can somehow download be reuested without user see confiramtion dialogs and messages and start download imedietly.
If item was previously be purchased/downloaded than user see message that will buy free item again. I know for restore functionalities, but do I need to track previously downloaded items and in that case start download from restore state.
Thanks!
1) Yes, You can download without dialogs. get product detail using store kit, and if product price is free (0), download item without processing purchase request from Storekit.
Code to request product detail:
KProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithArray:productIdentifiers]];
// Keep a strong reference to the request.
self.request = productsRequest;
productsRequest.delegate = self;
[productsRequest start];
// SKProductsRequestDelegate protocol method
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
self.products = response.products;
for (NSString *invalidIdentifier in response.invalidProductIdentifiers) {
// Handle any invalid product identifiers. or check that product you are looking for dozens has its identifier here
}
for (SKProduct *product in self.products)
{
//for example, product you are looking for has identifier "com.product.free"
if([product.productIdentifier isEqualToString:#"com.product.free"] && [product.price compareWithInt:0])
{
// Startdownloading without purchase
}
}
}
2) If you have non-consumable inappt purchase, you must provide option for restore. for downloading, you can write your own logic. check if item is downloaded and re-download only if its not available.
I'm doing the In-App purchase feature. Today I get a weird issue.
I try to get list products by SKProductsRequest. The problem is: sometimes I received invalidProductIdentifiers, but sometimes I received valid products.
SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObjects:objects]];
productsRequest.delegate = self;
[productsRequest start];
Ex: I send a request to get info of 30 products: sometimes get 10 products are valid, sometimes 0, sometimes 30, ... So weird.
In the past, it works well. But one day the problem occurs.
I tried (searched and asked my co-workers) a day but still can not figure out why.
Please help.
Thank you.
First of all check your application id should not be with wild card (*), it cannot be enabled for In-App Purchases so make sure you should create a new App ID.
If above scenario is not your case, then you will have to wait for maximum 24 hours after creating new product id from your developer account, within this time your product id will be fetched from your application. Because some times apple takes time to enabled product ids.
If above both are not your cases then at last you will have to check your product id’s are valid or invalid with below code.
- (void)productsRequest:(SKProductsRequest )request didReceiveResponse:(SKProductsResponse )response {
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
}
If you found your product ids are invalid please go through with http://troybrant.net/blog/2010/01/invalid-product-ids/
Cheers.....
Thank you all.
"But one day the problem occurs". And one day, it disappeared.
I think it's the Apple's bug.
If I wipe the data in my app, then re-purchase some managed IAP that I know the account already owns, iOS gives me the native "are you sure you wish to repurchase this item you will not be charged" dialog. That's as expected.
When the purchase returns to my app, I get the receipt with [[NSBundle mainBundle] appStoreReceiptURL]. I then attempt to verify the SKPaymentTransaction's transactionIdentifier with the receipt via my server.
However the receipt's transaction ID for this IAP, because I've already purchased it long ago, is not the same as the SKPaymentTransaction.
How should I be verifying that this is a valid repurchase? Can I get a signed receipt for this repurchase somehow?
All the purchases will be in a single receipt. You do have to check all iAPs there and look for the needed ones.
If you want the id of the original iAP transaction, there is Original Transaction Identifier
For a transaction that restores a previous transaction, the transaction identifier of the original transaction. Otherwise, identical to the transaction identifier.
More information about receipt fields is here: https://developer.apple.com/library/prerelease/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
Anyway, does it really matter whether this is an original purchase or a repurchase? My checks of this are usually the same.
As far as I understand it you should check the originalTransaction to check that this is valid for transactions that are SKPaymentTransactionStateRestored.
[[NSBundle mainBundle] appStoreReceiptURL] is for the receipt of the main application, not the IAP.
You can use RMStore library, and check all purchases:
RMStoreAppReceiptVerificator *verificator = [[RMStoreAppReceiptVerificator alloc] init];
if ([verificator verifyAppReceipt])
{
NSArray *inAppPurchases = [RMAppReceipt bundleReceipt].inAppPurchases;
for (RMAppReceiptIAP *inAppPurchase in inAppPurchases)
{
NSLog(#"productIdentifier %# originalPurchaseDate %#", inAppPurchase.productIdentifier, inAppPurchase.originalPurchaseDate);
}
}
If your receipt is nil you can refresh receipt:
[[RMStore defaultStore] refreshReceiptOnSuccess:^{
//get purchases
} failure:^(NSError *error) {
}];
I have an iOS app which has in-app purchase and restore purchases functionality.
When I was testing the app in sandbox mode I use to get the alert all times asking for the password of iTunes account (UserName was already populated).
Now my app is live and I installed it and did the in-app purchase and then restored the purchases, So I am not in sandbox mode still I keep getting the alerts asking for password of iTunes account.
Following is a code which gives the ProductIds that I have purchased earlier,Then I pass those productIds to delegate which changes the status of those from 'Buy' to 'Purchased'
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
//NSLog(#"Restored Transactions are once again in Queue for purchasing %#",[queue transactions]);
NSMutableArray *purchasedItemIDs = [[NSMutableArray alloc] init];
//NSLog(#"received restored transactions: %i", queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
NSString *productID = transaction.payment.productIdentifier;
[purchasedItemIDs addObject:productID];
// NSLog (#"product id is %#" , productID);
}
if ( mDelegate != nil && [mDelegate respondsToSelector:#selector(purchasedProductList:)] ){
[mDelegate purchasedProductList:purchasedItemIDs];
}
[purchasedItemIDs release];
}
Any idea how to avoid this?
Thanks
You need to ensure that finishTransaction: is called. It must always be called and calling it on a transaction multiple times is fine. If it isn't called, the transaction remains in the queue and the app will try to process it again later. It doesn't matter how the transaction was added to the queue, once it has been acted upon it should be 'finished'.
Note that when restoring new transactions are created, which are effectively wrappers around the original transactions, and they need to be 'finished'. From the restore docs:
The payment queue will deliver a new transaction for each previously completed transaction that can be restored. Each transaction includes a copy of the original transaction.
No email is outputted from the Facebook request. How do I get this? I need it for my login/signup process
- (void)request:(FBRequest *)request didLoad:(id)result{
NSLog(#"FACEBOOK RESULT %# ", result);
}
This is the initial request:
[facebook requestWithGraphPath:#"me" andDelegate:self];
The email property cannot be obtained without requesting additional permission to obtain it, see https://developers.facebook.com/docs/reference/login/email-permissions/.
My application uses the following code to first check whether the Facebook session is valid (self.facebook is a Facebook object initialized with initWithAppId):
if (![self.facebook isSessionValid]) {
NSArray* permissions = [NSArray arrayWithObjects: #"email", nil];
[self.facebook authorize:permissions];
} else {
// Since we have a valid session we can get the userinfo
[self getFacebookUserInfo];
}
Your application prompt will indicate "Using this app requires: * Your basic info * Your e-mail address" If the user authorizes you to obtain this information your access token returned will have bits in set to allow you to obtain the e-mail address, and the following will work:
-(void)getFacebookUserInfo {
[self.facebook requestWithGraphPath:#"me" andDelegate:self];
}
assuming your -(void)request:(FBRequest *)request didLoad:(id)result method is available (which it appears that it is).
Note that the full flow of SSO (Single Sign On) is not given here in this post, you'll need to go through https://developers.facebook.com/docs/tutorials/ios-sdk-tutorial/#implementsso in detail.
Note also that you may be using a deprecated API for iOS 6, you should look at https://developers.facebook.com/docs/tutorial/iossdk/upgrading-from-2.0-to-3.1/ before going further.
With 2.4 api, parameters are used in order to specify which user data items you are interested in receiving.
E.g.:
FBSDKGraphRequestConnection *connection2 = [[FBSDKGraphRequestConnection alloc] init];
FBSDKGraphRequest *selfRequest = [[FBSDKGraphRequest alloc] initWithGraphPath:#"me?"
parameters:#{#"fields":#"id,name,first_name,last_name,verified,email"}];
[connection2 addRequest:selfRequest
completionHandler:^(FBSDKGraphRequestConnection *innerConnection, NSDictionary *result, NSError *error) {
if (error) {
NSLog(#"%#", error);
return;
}
if (result) {
// get user data
NSMutableDictionary *facebookUserData = [NSMutableDictionary dictionaryWithDictionary:result];
NSLog(#"result=%#", result);
HOWEVER, they apparently messed up big time, as email is NOT CURRENTLY being returned for DEVELOPERS, and currently in development Apps.
Incidentally, it is IMPOSSIBLE to submit bugs on Apps in development, as they require approved apps in the feedback system...
I wonder how do they think we are going to make sure that something works BEFORE submitting an app for approval, or what is the logic of asking for an App Store id of an App which can't be approved BEFORE Facebook integration can be tested to the fullest.
Besides, who is the genius at Facebook who decides to break working code one day from another?