In my app I have an in-app-purchase where I allow to purchase some items. (with mkstorekit)
A think that I notice in iOS 7 is this:
if In general setting of my iPhone I'm logged with my Apple id and I proceed to purchase an item I have an error "Error Domain=SKErrorDomain Code=0" and the code finish in "canceled" block.
Then I logout from my apple id, I start again the app and when I must purchase an item it ask me id and password and its correct (naturally I insert a tester user data that I created in iTunes Connect)
At this time I stop my app and in my general setting is stored the tester user, then I start again my app and when I must purchase an item I have not the problem that I had with my personal apple id and it work fine.
In iOS 6 it didn't happen because if I was logged with my apple id in general settings and I tried to purchase an item, it showed me my apple id and asked me to insert the password (naturally it didn't work)
Finally this is not a big problem, I solve it because I used my tester account, but when I release my app in App Store can I have the same problem? If an user is logged in general setting the mkstorekit sto purchase and go in "canceled" block?
I show the method where is the cancel block:
- (void)purchaseFeature:(NSString *)productID
{
[[MKStoreManager sharedManager] buyFeature:productID
onComplete:^(NSString* purchasedFeature,
NSData* purchasedReceipt,
NSArray* availableDownloads)
{
[HUD hide:YES];
/*** CODE TO OPEN VIEW IF THE PURCHASE IS DONE ****/
}
onCancelled:^
{
NSLog(#"User Cancelled Transaction");
[HUD hide:YES];
/*** HERE FINISH MY CODE IF I'M JUST LOGGED IN GENERAL SETTING WITH MY APPLE ID ***/
}];
}
Related
I am using the library RMStore - here's what I have currently.
1) Purchase auto renewable subscription & verify the returned receipt.
[[RMStore defaultStore]addPayment:[Environment environment].premiumProductIAPId success:^(SKPaymentTransaction *transaction) {
[[RMStore defaultStore].receiptVerificator verifyTransaction:transaction success:^{
//enable premium service
} failure:^(NSError *error) {
}];
} failure:^(SKPaymentTransaction *transaction, NSError *error) {
}];
2) On each app launch check the subscription is active for the date and enable the premium service if it is
RMAppReceipt *appReceipt = [RMAppReceipt bundleReceipt];
if (appReceipt){
NSInteger isActive = [appReceipt containsActiveAutoRenewableSubscriptionOfProductIdentifier:[Environment environment].premiumProductIAPId forDate:[NSDate date]];
//enable premium service if active
}
3) If user launches app on another device allow them to restore purchases by refreshing the receipt if it exists and checking if there is an active subscription in the purchases.
"In most cases, all your app needs to do is refresh its receipt and deliver the products in its receipt."
- That's from the guide. Here's the code:
[[RMStore defaultStore]refreshReceiptOnSuccess:^{
if ([receipt containsActiveAutoRenewableSubscriptionOfProductIdentifier:[Environment environment].premiumProductIAPId forDate:[NSDate date]]){
//enable
}else{
//no longer active
}
} failure:^(NSError *error) {
}];
My questions:
When RMStore checks if the subscription is active it can return no, I look in the receipt and it is correct and I am assuming it hasn't been auto renewed. When I go to purchase another subscription I get a message from itunes saying I'm already subscribed. On subsequent launch I see the new receipt. This indicates the receipt needed to be refreshed on launch, but I don't want to refresh it as it brings up the username & password pop up which is unnecessary. What is the best practice here?
Am I restoring the subscriptions for another device the right way? It seems to sometimes take more than one attempt to restore the subscriptions.
Is there any need apart from record keeping to store the subscriptions on my server?
I'm going to try and answer my question.
There may be a renewal which is not detected first thing on launch hence the subscription appears inactive.
I added an observer to listen for finished transactions (RMStore extends this StoreKit functionality).
Each time I receive this notification I check the (now updated) receipt for an active subscription and enable the premium service if there is one.
- (void)storePaymentTransactionFinished:(NSNotification*)notification
{
BOOL isActive = [[RMAppReceipt bundleReceipt] containsActiveAutoRenewableSubscriptionOfProductIdentifier:[Environment environment].premiumProductIAPId forDate:[NSDate date]];
if (isActive){
//enable premium
}
}
This seems to be working. If anyone has any other suggestions let me know.
My app starts and checks for the receipt. Because it is sandbox, the first time the app runs from Xcode, it needs to ask the App Store for the receipt. So I use SKReceiptRefreshRequest to request it.
A window pops up, asking for the App Store credentials. If I type the credentials, then the app loads the receipt, I validate it, and the app runs fine.
The problem starts if I cancel that credential window.
Then I have the first problem. At this time the app has no receipt, so I cannot validate to see if the copy is pirate. What to do? I tried the following approach: instead of disabling the application, when the user tries to use the app, I show a window saying "could not validate the application, type OK to validate now".
When the user types OK, I trigger SKReceiptRefreshRequest a second time. Again a credential window pops up, I type the valid credentials and nothing happens. After 2 or 3 minutes of nothingness, a windows pops up saying "cannot connect to App Store".
The strange part is that none request:didFailWithError: or requestDidFinish: methods of SKReceiptRefreshRequest delegate are called during this failure. Receipt retrieval fails without triggering any delegate method and yes, the delegate is assigned.
The code for the receipt retrieval is the traditional one, that is
SKReceiptRefreshRequest *refreshReceiptRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
refreshReceiptRequest.delegate = self;
[refreshReceiptRequest start];
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"ERROR");
}
- (void)requestDidFinish:(SKRequest *)request {
if([request isKindOfClass:[SKReceiptRefreshRequest class]])
{
NSLog(#"App Receipt exists after refresh");
} else {
NSLog(#"Receipt request done but there is no receipt");
}
}
Apparently this is a bug of SKReceiptRefreshRequest. If the user cancels the first credential box, the application will not able to retrieve the receipt a second time, at least not in sandbox mode. Because this will not work on sandbox mode, you cannot test and this will also not work when Apple review your app and your app will be rejected.
Also, killing the app from the task bar will not help to make the credential box appear a second time.
The only solution is to present an alert, telling the user to remove and download your app again from the store and to not cancel the credential box when the app asks for the apple ID/password.
I get the error:
Error Domain=GKErrorDomain Code=27 "The requested operation could not be completed because you are not signed in to iCloud" UserInfo=0x1889f160 {NSLocalizedDescription=The requested operation could not be completed because you are not signed in to iCloud}
when I'm trying to save game data using this obvious code:
[[GKLocalPlayer localPlayer] saveGameData:serializedData withName:#"SaveName" completionHandler:^(GKSavedGame *savedGame, NSError *error) {
if (savedGame != nil) {
NSLog(#"Player data saved to GameCenter: %#", savedGame);
} else {
NSLog(#"Player data NOT saved to GameCenter, error: %#", error.description);
}
}];
It's even more strange because it worked two weeks ago, so I commented it out and started coding other stuff: leaderboards, in-app purchase – and all these work.
I'm testing it on my iPhone (iOS 8.1.3) logged to Game Center, to iCloud, I've got iCloud Drive turned on, but saving doesn't work. I even set up a new test account, but with the same result.
Could testing all these functionalities on same test account make this problem or it's time to contact Apple?
I´ve had the same problem.
try to log into icloud with your standard account(not TestUser).
activate iCloud > iCloud Documents in Capabilities. Turn ON iCloud Drive in Phone Settings (Device) and it should work.
Had the same problem. I did add iCloud Capabilities, but only in ReleaseForProfiling mistakenly. I should do it in ALL sections.
I'm trying to save game's data to iCloud via next code:
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
[localPlayer saveGameData:[NSData dataWithBytes:rawData->data() length:rawData->size()]
withName:[NSString stringWithUTF8String:fileName.c_str()]
completionHandler:^(GKSavedGame *savedGame, NSError *error) {
if (error == nil) {
DebugLog(#"Snapshot successfully saved");
} else {
DebugLog(#"saveSnapshot error: %#", error.description);
}
}];
But getting error message: The requested operation could not be completed because you are not signed in to iCloud
I've tried to do this in iOS Simulator and on device but didn't succeed.
And tried to use test apple ID, dev apple ID, new apple ID, but didn't succeed too.
User is logged in to iCloud in iOS Settings and logged in to Game Center.
Any suggestions?
I had a similar issue. After I signed in with my iCloud account on my device's settings, the exact same error was still showing up, and it only stopped when I enabled the iCloud Drive for my account, otherwise it will never work.
So, it looks like the user must be using iCloud Drive, otherwise GKSavedGame will never work. Apple docs never mention that as far as I'm aware.
Prerequisite:
You must have an Apple ID.
Check you have your Capabilities set.
Capabilities > iCloud > ON
(You must have iCloud Documents checked.)
Capabilities > Game Center > ON
Go to iCloud and create your account
From your computer browser, just login with your Apple ID and accept the terms and conditions. Like here: https://www.icloud.com/#settings
Now you can log in with your device or simulator, in Settings > iCloud.
I have got one big problem with an IAP purchase testing.
There are next steps which I have made:
Create App ID (com.domainname.appname)
Create Development Provisioning Profile with an App ID (com.domainname.appname)
Create App on iTunes Connect (Bundle ID com.domainname.appname)
Manage IAP. Create non-consumable products with Product ID com.testitem1, com.testitem2, com.testitem3 (there is Ready to Submit status for each products)
Configurate XCode project settings
Bundle identifier: com.domainname.appname
Code Signing Identity: Development Provisioning Profile that I have made on the step 2
Deployment target: 4.0
Device
iPod Touch 4gen iOS 5.1.1
Write source code
My class support SKProductsRequestDelegate
in - viewDidLoad callback method I have wrote these lines:
if ([SKPaymentQueue canMakePayments])
{
NSLog(#"YES, I CAN MAKE PAYMENTS");
}
else
{
NSLog(#"NO, I CAN NOT MAKE PAYMENTS");
}
On this stage everything is okay. I can make payments.
Next, I'm try to create request for my products:
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers: [NSSet setWithObjects:#"com.testitem1", #"com.testitem2", #"com.testitem3", nil]];
request.delegate = self;
[request start];
On this stage everything is okay also and on next stage my request object invoke callback method below:
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *myProduct = response.products;
NSLog(#"%#", myProduct);
}
But there are not any products in response and I can not understand why.
I already have this problem before (i just reset my device and everything worked good), but in this case I can not figure out what's wrong.
I have went through this guide:
https://developer.apple.com/library/ios/#technotes/tn2259/_index.html
In my case this is was related that I have not got Banking Information. If you want to test your IAP you must have an iOS Paid Applications contract (iOS) with a your tax information.
This happens because you haven't yet uploaded the app's binary in iTunes Connect. Go to iTunes Connect, upload the app's binary. Once it's uploaded, reject it straight away so that it's marked as "Developer Rejected".
This is a necessary step for you to receive SKProductResponse if it's the first version of your app. It won't affect your app's review.