I am new in iOS development, I want to implement In-App purchase for my application.
The scenario is :
1) Product added by Admin side (Admin Panel) , that why I am not able to register each product separately.
2) For that I have used one iTunes id like "com.company.asss.prod1" and purchase type "Consumable"
3)Use same Product Identifier for each Product (com.company.asss.prod1)
4) when I buy first item it will work fine ...
5) when i try to purchase second item , it will call [ - (void)productPurchased:(NSNotification *)notification ] method Two time for First and Second Item.
6) when I try to purchase Third item , it will call (productPurchased: ) method three times for First, Second and third Item.......
Code :
You should set some integer a boolean value for this also you can use NSString. Now you have to check that if first product is purchased then not to call in app purchase for first product and so on..
Please provide some more details. The info provided is not enough to understand the scenario that you face.
For the proper dynamic implementation of the consumable purchase:
Once transaction is completed, following delegate method of SKPaymentTransactionObserver is called:
-(void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
if ([transaction.payment.productIdentifier isEqualToString:#"com.company.asss.prod1"])
{
// WRITE YOUR PIECE OF CODE HERE
}
}
Related
I have integrated IAP functionality in my flutter app. I wants to know when user has clicked the "Cancel" button on IAP popup.
There are many solution found here but that all code is in native.
It will be very helpful If I get the trigger of cancel button click in flutter iOS.
On the cancel button click I need to do other functionalities.
I have followed the IAP integration code from this link:
https://fireship.io/lessons/flutter-inapp-purchases/
and the IAP popup is shown as default popup not a custom.
Please give some suggestion. Any help would be appreciated.
Thank you.
I created a loop to look for failed transactions and then finish them accordingly.
I am still experimenting with where to do this to try to avoid a user error. I am also going to try to do a similar process in the stream listener that processes purchases.
There are StoreKit Wrappers included with IAP plugin:
Note: I looked at SK info for XCode and it seems fine to do this with a failed transaction. They said it is a 'no no' to do it to a pending one.
Future<Null> _removeFailedPendingPurchases() async {
debugPrint(':::: SWIPE WAR ::::: _removeFailedPendingPurchases()');
final SKPaymentQueueWrapper paymentWrapper = SKPaymentQueueWrapper();
final List<SKPaymentTransactionWrapper> transactions =
await paymentWrapper.transactions();
debugPrint(
':::: SWIPE WAR ::::: transactions ${transactions.length} ${transactions.toString()}');
for (SKPaymentTransactionWrapper transaction in transactions) {
debugPrint('TRANSACTION everything : ${transaction.toString()}');
debugPrint('TRANSACTION STATE : ${transaction.transactionState}');
if (transaction.transactionState ==
SKPaymentTransactionStateWrapper.failed)
await paymentWrapper.finishTransaction(transaction);
}
return;
}
When you press cancel button the PurchaseDetails.status will be equal to PurchaseStatus.error. So you check it:
if (purchaseDetails.status == PurchaseStatus.error) {
//Change UI state to reflect this problem
}
Is there a way to know when CANCEL is pressed in the Apple Pay view? I tried to look for a delegate, but didn't find one.
If you have the PKPaymentAuthorizationViewControllerDelegate methods setup, you can catch the cancel in this method:
- (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller
I set a boolean (applePaymentDidSucceed) to FALSE before I call Apple Pay, then set it to true if the "PKPaymentAuthorizationStatus" in this method is true:
- (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didAuthorizePayment...
if ( ! applePaymentDidSucceed), the user cancelled.
Yes there is!
You can use session.oncancel event
More info here:
- https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession
- https://developer.apple.com/documentation/apple_pay_on_the_web/applepaysession/1778029-oncancel
You will get an error. There are several error codes defined, one is for the user cancelling the payment, another for a user having paid purchases disabled in their settings, and all kinds of other errors (for example, you can't make purchases without internet access).
So no particular case for cancellation, just an error.
I am currently implementing the restore functionality for my app. I have several In-App Purchase products (non-consumable) and I want to allow the user to restore only one or two, so that I don't download more content than needed. In the In-App Purchase Programming Guide its stated that I can do that like this:
NSMutableArray *productIDsToRestore = <# From the user #>;
SKPaymentTransaction *transaction = <# Current transaction #>;
if ([productIDsToRestore containsObject:transaction.transactionIdentifier]) {
// Re-download the Apple-hosted content, then finish the transaction
// and remove the product identifier from the array of product IDs.
} else {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
However, in my experience, the product ID doesn't match the transaction identifier. By product ID I understand the string used to retrieve the products from the App Store (something like "com.mydomain.myproduct"). The transaction identifier seems to be a long number, even if it's received as a string.
What am I missing and how to implement this?
Thanks!
I have in-app purchases in my app, and new to iOS 8 are "deferred" transactions, partially described in a tech note
I understand what it does and that I need to not block the UI, and update my UI to reflect that the transaction state is deferred. But what am I supposed to place in the method -(void)transactionDeferred:(SKPaymentTransaction *)transaction to disregard the transaction for the time being?
Do I only have update the UI? Also what should the content of the UI be? Do I need to replace the price label with something like "Your purchase is deferred"? I don't think there is a way to test this, at least I haven't seen anything about it with my sandbox test account. If there was a way to go through the process and see how it works, it would make a lot more sense to me.
What I am doing is:
Stopping indicator animation
Activating buy and restore buttons
Showing an alert:
Waiting For Approval
Thank you! You can continue to use Altershot while your purchase is pending an approval from your parent.
I watched WWDC 14 video. Apple says you should't block UI and allow to click on buy button again. I think we need this in case parent miss alert, so child can send one more.
What I know is that we should not call following method for deferred transactions:
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
The code bellow will allow you to check if the product ID you want to sell is in deferred mode. Use it to update the UI accordingly.
if ([[SKPaymentQueue defaultQueue].transactions count] > 0) {
for (SKPaymentTransaction *transaction in [SKPaymentQueue defaultQueue].transactions) {
if ([#"your.product.id" isEqualToString:transaction.payment.productIdentifier]) {
if (transaction.transactionState == SKPaymentTransactionStateDeferred) {
// update UI that you are still waiting for parent approval. You'll get "PURCHASED" if parent approved or "FAILD" if parent declined or 24 hours passed since request.
}
break;
}
}
}
In my previous apps, there is only one IAP, so I didn't realized there was problem in my code. Today I have an app with multiple IAP products, and I found the problem.
I followed Troy's IAP tutorial, and pretty much copied his code. That worked well for my previous apps, as there was only one IAP product in each of those apps. The tutorial uses one IAP product as example, as well. I was trying to post at his blog to ask my question, but my post would be too long and no formatting, plus I think experts here can help me understand more and check if I misunderstood something or missed something.
Now two things I have to handle (unless I missed, the tutorial didn't mention cases of multiple IAP products).
1, I don't know if I misunderstood, in the tutorial, there's a comment for loadStore method, like this
//
// call this method once on startup
//
- (void)loadStore {
// restarts any purchases if they were interrupted last time the app was open
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// get the product description (defined in early sections)
[self requestProUpgradeProductData];
}
So I call this method at the beginning of my app in the ViewController.m file. My understanding is that this method needs check if there was any previous transaction hanging over there and need take care of it at the moment app re-starts. That's the first statement of this loadStore method. The method continues to call requestProUpgradeProductData, which like this:
- (void)requestProUpgradeProductData {
NSSet *productIdentifiers = [NSSet setWithObject:#"com.runmonster.runmonsterfree.upgradetopro" ];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
// we will release the request object in the delegate callback
}
Notice here, the first statement in this method is to get IAP product ID. This is all fine for one-IAP-product apps, since the product ID is fixed, and it's okay to call loadStore at the beginning of running the app. Actually, loadStore probably has to run at the beginning of running the app, because, here is the second problem.
2, If loadStore runs at the moment a user wants to buy IAP, rather than at the beginning of the app, the catch is when [productRequest start] runs, the following method will run:
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
proUpgradeProduct = [products count] == 1 ? [[products firstObject] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
// finally release the reqest we alloc/init’ed in requestProUpgradeProductData
[productsRequest release]; //I didn't do this because of ARC
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}
This will run and return response with products. then I need initiate payment, but I AM NOT SURE HRER HOW SHOULD FOLLOWING METHOD BE CALLED. This is the method initiating IAP payment. As I mentioned, once loadStore runs at the beginning of the app, there are "plenty" time for productRequest....response method (right above) to finish and sending back notification, given that users click here and there for a second. And as I said, for one-IAP-product app, that's fine, I can specify the product ID at the beginning. Now for an app with multiple-IAP-product, I need present multiple products to users, and users choose one of them. Right after this, I need two things in order: A, specify which IAP product, via loadStore -> productRequest method (right above), and B, call the following method to initiate payment.
- (void)purchaseProUpgrade
{
SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
However, productRequest.....response method may not return yet at the moment I call the payment initiating method. I would not be able to initiate payment. I was thinking to put a delay function in between with a second or so, but I think this approach would be fundamentally wrong. However, how to solve the problem?
Can anyone help here?
The key thing is that you cannot allow the user to purchase anything until you get the response back from the productsRequest. First, initWithProductIdentifiers: takes a set of identifiers. So, on app startup or any other time prior to presenting the purchase option to the user, you call it with the identifiers/skus for ALL products that you may allow the user to purchase. Calling it on startup seems wasteful to me but it should also work and may be the only option for some apps.
Only after you get the productsRequest:didReceiveResponse: callback containing the product objects do you allow the user to click/touch or otherwise invoke the purchase action for that product. You have to check the responses to ensure that the product is present before allowing the user to attempt the purchase. I have a purchase screen and when the user enters it, I invoke the productsRequest with all products relevant to the application's current state (e.g. omitting already purchase items or restricting products by level). I also create the purchase screen but I do not populate it with any products. In the response delegate, I actually populate the screen with the purchasable objects. That way, the user cannot attempt to buy an invalid product.
When the user clicks/taps, you use the product object from the response to create the payment object using :
[SKPayment paymentWithProduct:product]
Note that this is a change from your current setup which uses paymentWithProductIdentifier: - a deprecated method.