Non consumable in-app purchse using MKstorekit4? - iphone

I have an Iphone application in which i am using two types of purchases.Non consumable products and a subscription.For subscription i have to use mkstorekit4 anyway.So i need to implement the nonconsumable also in the storekit4.Anybody knows the steps to integrate nonconsumable products with mkstorekit4.Can anybody help me.

As form me,if you've already managed how to add auto renew in your app, you are a guru of in-app,so adding a non-consumble would as easy as a pie,even without a MkStoreKit.
You just make a purchase and save it to UserDefaults.
And with MK you just add it to non-consumable part of your MK plist and add a couple of lines of code-and it's done)
Just a button,that makes a purchase,i think)
Update
This article just confirms, what i told.
http://blog.mugunthkumar.com/coding/using-mkstorekit-in-your-apps/
Configuring for Non consumables :
Non-consumables need no extra configuration. Just enter the list of product ids in the plist file.
For checking whether the user has purchased the feature, you can call a function like,
if([MKStoreManager isFeaturePurchased:kFeatureID]) { //unlock it }
To purchase a feature, just call
[[MKStoreManager sharedManager] buyFeature:kFeatureAId onComplete:^(NSString* purchasedFeature) { NSLog(#"Purchased: %#", purchasedFeature); } onCancelled:^ { NSLog(#"User Cancelled Transaction"); }];

Related

Remove ad banner after completing in-app purchase within iPhone app

How is it possible to remove chartboost banner after in-App purchase ?
use something like this
set bool key isPurchase YES in your NSUserDefaults when you purchase App
if(![userDefaults boolForKey:#"isPurchase"])
{
NSLog(#"Enter add start ");
[[Chartboost sharedChartboost] showInterstitial];
}
[userDefaults synchronize];
}
The easiest way is to save the information in user Defaults as mentioned in above post but this won't be secure because user defaults can easily be accessed by many softwares and one can edit/add your Key unless it is kind of big one and secret OR no body post it as a Hack.
The best thing is to store this information at Server (if you have it) or store this information in Key Chain because it is secured.
How to do that easily with keychain follow below post
iOS: How to store username/password within an app?
Chartboost offers a delegate method -(BOOL) shouldDisplayInterstitial:(NSString*)location
Returning "false" in this method will prevent an ad from showing; remember to return "true" if you do want an ad to show. Just compare against a boolean stored locally to track if they bought the IAP or not.
The reason recommend this method rather than hardcoding is because you might want to leave a few locations specific for cross-promotion campaigns promoting your own apps that will still be able to show ads in the future.
This way when you release a new app you can run a limited promotion and make sure that your most loyal fans who bought the no-ads IAP can still see a cross-promo ad for your new game - a great way to funnel your highest quality users to your new app! If you make the ad art right, they won't even know it's an advertisement and you can make it so they only see it once.
Then you can add showInterstitial:#"cross-promo" wherever you might want to show this and disable all publishing campaigns just for that location. Then, in the future when you have a new app, add a new cross-promotion campaign in that location promoting your new app and EVERYONE will see it - even the people who bought IAP!
an example of this implementation:
-(BOOL) shouldDisplayInterstitial:(NSString*) location {
if(_userBoughtNoAdsIAP && location != #"cross_promo"){
return FALSE;
}
return TRUE;
}
And don't forget to set the [Chartboost sharedChartboost].delegate = self; to make sure the delegate methods function properly!
Full Disclosure: I work for Chartboost

MKStoreKit not storing purchased Products

I'm using MKStoreKit to implement an IAP into my application. Everything does as expected when purchasing the product, no error messages at all. The product is purchased using the following code..
[[MKStoreManager sharedManager] buyFeature:#"pro_upgrade"
onComplete:^(NSString* purchasedFeature)
{
NSLog(#"Purchased: %#", purchasedFeature);
// provide your product to the user here.
// if it's a subscription, allow user to use now.
// remembering this purchase is taken care of by MKStoreKit.
}
onCancelled:^
{
// User cancels the transaction, you can log this using any analytics software like Flurry.
}];
After the product has been purchased I recieve the notification (via the code below) which confirms the purchase..
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector (subscriptionPurchased:)
name: kSubscriptionsPurchasedNotification
object:nil];
After the purchase I execute the following code (as documented) to determine if the product has been purchased..
if([MKStoreManager isFeaturePurchased:#"pro_upgrade"]) {
NSLog(#"This feature is purchased.");
} else {
NSLog(#"This feature is not purchased");
}
It constantly returns NO for the product ID. This happens when running the app in the same instance as when the product was purchased as well as closing the app and opening it. Running the app using Development or Distribution certificates make no difference at all. Further more I can't find where exactly MKStoreKit stores the product purchased BOOL that it seems to be looking for (it's pretty deep in the code so i'm not saying it does or doesn't, i just can't find it). My app is storing other information using NSUserDefaults so that isn't the issue.
Any help you can offer will be extremely appreciated, thanks for your time.
MKStoreKit doesn't store in NSUserDefaults.
I use keychain instead. Place a breakpoint in the method
storeData:forKey: (writing this method name straight from my head
without looking at code, search for similarly named methods) and try
debugging it. This is the place I save your purchases.
Do remember that purchases are remembered even after deleting and
reinstalling the app.

SFProductsRequest always returning zero

I'm trying to implement in app purchases in a free application.
I have created a productd id "test1" within the in app purchases manager in itunes connect portal.
When I make the product request in the following way:
- (id)init {
NSSet *productIdentifiers = [NSSet setWithObjects:
#"test1",
nil];
if ((self = [self initWithProductIdentifiers:productIdentifiers])) {
}
return self;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers
{
if ((self = [super init]))
{
// Store product identifiers
_productIdentifiers = [productIdentifiers retain];
// Check for previously purchased products
NSMutableSet * purchasedProducts = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers)
{
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased)
{
[purchasedProducts addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
}
NSLog(#"Not purchased: %#", productIdentifier);
}
self.purchasedProducts = purchasedProducts;
}
return self;
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Received products results...");
self.products = response.products;
self.request = nil;
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:_products];
NSLog(#"%d",[self.products count]);
NSEnumerator *e = [self.products objectEnumerator];
id object;
while(object=[e nextObject])
{
NSLog(#"item");
NSLog(#"%s",(char*)object);
}
}
- (void)requestProducts {
self.request = [[[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers] autorelease];
_request.delegate = self;
[_request start];
}
The response is always 0. I don't understand what am I doing wrong. This code came from a tutorial. The documentation regarding in app purchases tend to be quite confusing and the whole process in itunes connect doesnt give me confidence.
I thought the application needed to be online for sale for in app purchases to be working. However, I decided not to included in app purchases, but let the in app purchase in itunes connect for review. During the review process, the application was rejected because it should be working with the in app purchases for testing.
But how do I test in app purchases if the product listing comes always at zero?
If someone with more experience could give me an advice on this, since i'm already getting crazy with it!
Thanks,
With my best regards,
Nuno
The most coherent solution I found to this problem was this checklist. It should be wide spreaded in order to avoid anyone passing by the same problem which is really time consuming and desperating:
Have you enabled In-App Purchases for your App ID?
Have you checked Cleared for Sale for your product?
Have you submitted (and optionally rejected) your application binary?
Does your project’s .plist Bundle ID match your App ID?
Have you generated and installed a new provisioning profile for the new App ID?
Have you configured your project to code sign using this new provisioning profile?
Are you building for iPhone OS 3.0 or above?
Are you using the full product ID when when making an SKProductRequest?
Have you waited several hours since adding your product to iTunes Connect?
Are your bank details active on iTunes Connect? (via Mark)
Have you tried deleting the app from your device and reinstalling? (via Hector, S3B, Alex O, Joe, and Alberto)
Is your device jailbroken? If so, you need to revert the jailbreak for IAP to work. (via oh my god, Roman, and xfze)
Are you logged out from real iTunes account?
Have you tried restarting device?
Are you on Device? (Will not work on Simulator)
Credits go to Troy Brant
Have a look here, that answered all of my questions (and the framework is simple to use, too :-):
http://blog.mugunthkumar.com/coding/iphone-tutorial-%E2%80%93-in-app-purchases/
But I have to say the whole in-app purchase thing is a PITA - my app just got released, and of course I downloaded it and checked the in-app purchase screen. Guess what, it came up completely empty!
After some reading up it seems that even if all is accepted and ready for sale, the in-app purchase products still need a while to become available online - after 3 hours it finally worked ...
EDIT:
You need to create the in-app purchase for your app and set it to clear for sale in itunes connect. You do not need to upload a screenshot yet or have it already reviewed in order to be able to test it in development mode.
How did you name the purchase in itunes connect? Normally you should use a com.companyname.productname.purchasename name, and the name you request from your app ha to be exactly the same.

regarding inapp purchase

I have developed one app on iphone.
I have loaded its lite version on app store.
Now i cant to load full version from app itself, how it can be done.
I know this is possible but how this can be done.
You can do it in two ways. You have have lite and full as two separate apps, or you can have in-app upgrade. Depending on how you implement the restrictions of Lite, either one or the other might be a better choice.
In the two-app scenario, you develop Full as a separate bundle, with a separate bundle id. Xcode targets and conditional compilation might come in handy for this. In the Lite version, you want to hard-code a link to the full version in the App Store. Find out the link by copy-pasting from iTunes Connect.
In the second scenario, full/lite is a run-time setting, and the upgrade package is an in-app purchase. Implement StoreKit API, provide the hard-coded in-app product ID. Once the upgrade purchase is noticed (it's an asynchronous process), flip the setting to Full.
I'd recommend the second approach. For one thing, all reviews would go under Lite version, as opposed to splitting between Lite and Full. Also, if your app has settings or data files, those won't be lost on upgrade.
EDIT: here's how implementation of in-app purchases might look like. Somewhere in the app there's an Upgrade button (link, image, whatever) that goes like this:
if(![SKPaymentQueue canMakePayments])
{
//Display the "cannot pay here" message
return;
}
#try
{
[[SKPaymentQueue defaultQueue]
addPayment:[SKPayment paymentWithProductIdentifier: #"com.mydomain.myproduct.full"]];
}
#catch (NSException * e)
{
//Sorry...
}
Meanwhile, you need a class in your app that handles the StoreKit notifications. I mentioned that it's async; well, it still is. In my case, I used the AppDelegate for that:
#interface MyAppDelegate : NSObject <..., SKPaymentTransactionObserver>
And you designate the app delegate as one:
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
//...
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}
And then you implement the transaction processing method:
- (void)paymentQueue:(SKPaymentQueue *)q updatedTransactions:(NSArray *)tr
{
int i, n = [tr count];
for(i=0;i<n;i++)
{
SKPaymentTransaction *t = [tr objectAtIndex:i];
if(t.transactionState == SKPaymentTransactionStatePurchased)
[self ProcessPurchase: t.payment.productIdentifier];
//Support for restored transactions
if(t.transactionState == SKPaymentTransactionStateRestored)
[self ProcessPurchase: t.originalTransaction.payment.productIdentifier];
//Failed/purchased/restored
if(t.transactionState != SKPaymentTransactionStatePurchasing)
[q finishTransaction:t];
}
-(void)ProcessPurchase:(NSString*)ProgID
{
if([ProgID compare:#"com.mydomain.myproduct.full"] == 0)
{
//It's an upgrade! Change the settings, enable hidden content, and stuff...
}
}
Something like this.
You need to create separate bundle id for your full version. as these are two different application.

iPhone consumable product is behaving like a non-consumable product (already purchased...)

Our app has a list of locked products that share the same consumable product id (i.e. one consumable product id for many products). Our server provides me with a list of products and the product id associated with them:
item name="itemA" iphoneProductId="consumable.test.1"
item name="itemB" iphoneProductId="consumable.test.1"
item name="itemC" iphoneProductId="consumable.test.1"
We chose consumable because our items are created dynamically and need to be available to the user instantly (please don't reply suggesting that we use non-consumable, there are a lot of other reasons that are too hard to explain without me giving away private details about the company we are working with, as to why we are using consumable). This allows us to have multiple products share the same price.
When the user purchases itemA (for example), the item is unlocked. However, sometimes, when the user then tries to be itemB, Apple return with 'You have already purchased this but it hasn't been downloaded. Tap OK to download it now’. This should surely never happen for a consumable item. I know our system is quite complex but as far as the apple store kit is concerned, are simply just buying the same product again.
Could this just be a sandbox issue? We can't test in live as the app isn't released yet. In fact, this whole problem is holding off the release as our client is as concerned as we are about this problem.
I've followed the same code from the iphone documentation and the few in app purchase tutorials out there. I see that a lot of people on the forums seem to have witnessed the 'already purchased' dialog above for consumable products, but none of them ever get answered.
Please help! Thanks
The problem is that you are never finishing the transaction. You need to remove it from the queue.
Like:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
I'll assume you've called -[SKPaymentQueue finishTransaction:].
Off the top of my head, there are a few possibilities:
The store hasn't received the "finish" message yet (I don't know what ordering guarantees there are).
You're trying to purchase a second item before calling finishTransaction: on the first, and the App Store thinks it's a replay.
The App Store detects when you appear to be purchasing the same item repeatedly, and assumes that something must have gone wrong.
A hack is to cycle through a list of several (10? 100?) consumables, assuming that when you get back to one earlier in the list, it will have finished processing.
An alternative solution is to pre-allocate a lot of non-consumables ("product.1", "product.2", ..., "product.100") and assign ones with the appropriate price to products on the server. Changing prices can then be done on iTunes Connect, or by assigning additional product IDs as necessary.
Thanks for your quick response, but my app does seem to be finishing the transaction, etc.
My project's in app purchase classes have become quite complex so I reverted to creating a new basic project with a test consumable product and the standard implementation of in app purchase manager/observer taken from this open source:
http://blog.mugunthkumar.com/coding/iphone-tutorial-%E2%80%93-in-app-purchases/#idc-cover
The same problem occurs. This is the order:
1. Buy the consumable product for the first time (below is the debug I printed out)
_MKStoreManager: buyFeature:test.consumable.1
__MKStoreObserver: SKPaymentTransactionStatePurchased
__MKStoreObserver: completeTransaction
_MKStoreManager: provideContent
2. "Thank you for your purchase" dialog is displayed by Apple
3. Buy the consumable product for the second time:
_MKStoreManager: buyFeature:test.consumable.1
__MKStoreObserver: SKPaymentTransactionStatePurchased
__MKStoreObserver: completeTransaction
_MKStoreManager: provideContent
__MKStoreObserver: SKPaymentTransactionStateFailed
__MKStoreObserver: failedTransaction
4. "You've already purchased this but it hasn't been downloaded. Tap OK to download it now. [Environment: Sandbox]" dialog is displayed by apple.
It just doesn't make sense in step 3 that the transaction is both purchased then failed at the same time. Do you have any ideas.
It is not worth using consumable product for non-cosumable, because Apple will reject it. Here is what happened to me:
I have had the same issue. The message explaining that you have already purchased the item appears only once in a while and mostly if you purchase a lot of things - one after the other immeadiately.
We have put it for review in the App Store anyway and got the following answer:
.....
We have completed the review of your in-app purchase but cannot post it to the App Store because the Purchasability Type is not set correctly. For information on Purchasing and Currency guidelines, please see section 11 of the App Store Review Guidelines [ https://developer.apple.com/appstore/resources/approval/guidelines.html ].
.....
The purchase of a [magazine issue] is set to "consumable", however based on product functionality it should be set as non-consumable instead.
.........
You are required to create a new in-app purchase product with the correct purchasability type.
..........
I hope this saves someone time and headaches.
I was facing the same problems. The mistake I did was deallocating the purchase before the finishTransaction was sent. Make sure you handle the transaction result after finishing the transaction
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
BOOL success = YES;
for (SKPaymentTransaction *transaction in transactions){
switch (transaction.transactionState){
case SKPaymentTransactionStatePurchased:
success = YES;
break;
case SKPaymentTransactionStateFailed:
if (transaction.error.code == SKErrorPaymentCancelled){
if(DEBUG) NSLog(#"Transaction failed => Payment cancelled.");
}else if (transaction.error.code == SKErrorPaymentInvalid){
if(DEBUG) NSLog(#"Transaction failed => Payment invalid.");
}else if (transaction.error.code == SKErrorPaymentNotAllowed){
if(DEBUG) NSLog(#"Transaction failed => Payment not allowed.");
}else if (transaction.error.code == SKErrorClientInvalid){
if(DEBUG) NSLog(#"Transaction failed => client invalid.");
}else if (transaction.error.code == SKErrorUnknown){
if(DEBUG) NSLog(#"Transaction failed => unknown error.");
}else{
if(DEBUG) NSLog(#"I have no idea.");
}
success = NO;
break;
case SKPaymentTransactionStateRestored:
success = YES;
break;
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(#"transaction finished: %#", transaction);
}
if(success){
// do something
}
}
Hope that helps some of you.
Cheers,
K.
You should put this line [[SKPaymentQueue defaultQueue] finishTransaction:transaction] after the transaction over whether you are doing fresh purchase or restore purchase.
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
make sure you have added this method on the controller handling requests.