iOS7 xCode5 how to resolve "invalid product ID" for in app purchases in 2014? - ios7

I'm trying to implement in-app purchases in one of my apps, and have an issue where I get no products returned when I send a product request for sandbox testing. I see a lot of very old posts, like this one (invalid product id from 2010). What am I doing wrong with my in app purchase setup? Is there any recent tutorials on how to configure xCode5 to use in app purchasing?
- (void)requestProUpgradeProductData
{
NSSet *productIdentifiers = [NSSet setWithObject:self.productID ];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
// we will release the request object in the delegate callback
}
Here's the callback for the product request:
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray *products = response.products;
for(id object in products)
{
//handle valid products
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
[hud hide:YES];
}
Using this option created a duplicate app id with the same number, but different name on hte developer portal. I cannot delete that app ID.
This is the first consumable in app purchase, no need to host content. The app is in "ready to upload binary stage". It has been over 24 hours since I created the in app purchase via itunesconnect

Found a solution here:
http://www.raywenderlich.com/21081/introduction-to-in-app-purchases-in-ios-6-tutorial
Turns out when I created the product in iTunesConnect, I called it "product", when it should've been "com.mysite.product"

Related

how to get checkout details from paddle in mac app?

I have integrated my mac app with paddle this what i followed from documentation and integrated but what i want when user purchases from here i want all the order details.
// Your Paddle SDK Config from the Vendor Dashboard:
NSString* myPaddleProductID = #"12345";
NSString *myPaddleVendorID = #"56791";
NSString* myPaddleAPIKey = #"abc123def345hij678";
// Populate a local object in case we're unable to retrieve data
// from the Vendor Dashboard:
PADProductConfiguration *defaultProductConfig = [[PADProductConfiguration alloc] init];
defaultProductConfig.productName = #"My v4 Product";
defaultProductConfig.vendorName = #"My Company";
// Initialize the SDK Instance with Seller details:
Paddle *paddle = [Paddle sharedInstanceWithVendorID:myPaddleVendorID
apiKey:myPaddleAPIKey
productID:myPaddleProductID
configuration:defaultProductConfig];
// Initialize the Product you'd like to work with:
PADProduct *paddleProduct = [[PADProduct alloc] initWithProductID:myPaddleProductID productType:PADProductTypeSDKProduct configuration:nil];
// Ask the Product to get it's latest state and info from the Paddle Platform:
[paddleProduct refresh:^(NSDictionary * _Nullable productDelta, NSError * _Nullable error) {
// Launch the "Product Info" gatekeeper UI with buy, activate, etc:
[paddle showProductAccessDialogWithProduct:paddleProduct];
however in documentation for custom implementation by using below block we can get but i want by using showProductAccessDialogWithProduct
[paddle showCheckoutForProduct:paddleProduct options:nil checkoutStatusCompletion:^(PADCheckoutState state, NSDictionary * _Nullable checkoutData) {
// Examine checkout state to determine the checkout result
}];
Any Suggestions ?
Thanks In Advance !!
So i got the answer till V4.0.9 we cannot get the details it is included after v4.0.10.
All you need is PADProductDelegate
-(void)productPurchased:(PADCheckoutData *)checkoutData
using this delegate method you can get checkoutdata and if you need order details
get checkout_id from checkoutdata and pass it on below api.
https://checkout.paddle.com/api/1.0/order?checkout_id=xxxxxxxxx

can't load Safari contentBlocker. because can't access app group's NSUserDefault

I am making iOS 9 Safari AdBlocker app.
I am using the module of AdBlockPlusSafari.
App works good on simulator.
But when try to run it on device(iPhone6), it fails to reload contentBlocker.
[SFContentBlockerManager
reloadContentBlockerWithIdentifier:self.contentBlockerIdentifier
completionHandler:^(NSError *error) {
if (error) {
NSLog(#"Error in reloadContentBlocker: %#", error);
}
dispatch_async(dispatch_get_main_queue(), ^{
wSelf.reloading = NO;
[wSelf checkActivatedFlag];
if (completion) {
completion(error);
}
});
}];
it gives error
Error Domain=ContentBlockerErrorDomain Code=3 "(null)"
It caused by accessing the values in NSUserDefault (App Group).
- (instancetype)init
{
if (self = [super init])
{
_bundleName = [[[[[NSBundle mainBundle] bundleIdentifier] componentsSeparatedByString:#"."] subarrayWithRange:NSMakeRange(0, 2)] componentsJoinedByString:#"."];
NSString *group = [NSString stringWithFormat:#"group.%#.%#", _bundleName, #"AdBlockerPro"];
NSLog(#"Group name: %#", group);
_adblockProDetails = [[NSUserDefaults alloc] initWithSuiteName:group];
[_adblockProDetails registerDefaults:
#{ AdblockProActivated: #NO,
AdblockProEnabled: #YES
}];
_enabled = [_adblockProDetails boolForKey:AdblockProEnabled];
_activated = [_adblockProDetails boolForKey:AdblockProActivated];
}
return self;
}
The App Group name in host app and safari extension is same.
But in Safari extension, when app accesses the setting in NSUserDefault, it gives me the error.
In Project setting/Capabilities, I did all for App Group. In app id, it involves app group name exactly.
This happens on only device. On simulator, it works good. I can't find the reason of this error.
Please help me if you are experienced this.
Looking forward to your help.
I found the reason myself.
I have put something (NSMutableDictionary) in app group container and did something to write file to extension bundle.
It is prohibited by Apple.
So I deleted all from AdBlockManager (interacts with app group) except flag variables (Boolean type).
And I proceeded the file management using NSFileManager.
http://www.atomicbird.com/blog/sharing-with-app-extensions
Finally, app extension is working for me on device.
Good luck!

Trouble getting the original app version that the user installed (receipt validation)?

I have an app that I recently updated to work with in app purchases. The previous version (paid but with no in app purchases) was 1.0 and the current version is 1.1.
As the in app purchase essentially unlocks all features (which were included in the paid version 1.0), I wanted a way for users that had originally downloaded version 1.0 to be upgraded if they pressed my restore purchases button.
To do this, I first try to restore purchases and if the response to:
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
If this provides me with a queue with a transactions count of 0, I check the receipt to see if the original version installed was 1.0.
The code to get the receipt is as per Apple's documentation
- (void)tryRestoreFromOriginalPurchase
{
// Load the receipt from the app bundle
NSError *error;
NSData *receipt = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
if (receipt == nil) {
[self restoreFromOriginalVersionWithReceipt:nil];
return;
}
// Create the JSON object that describes the request
NSDictionary *requestContents = #{#"receipt-data": [receipt base64EncodedStringWithOptions:0]};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:&error];
if (!requestData) {
[self restoreFromOriginalVersionWithReceipt:nil];
return;
}
// Create a POST request with the receipt data
NSURL *storeURL = [NSURL URLWithString:#"https://buy.itunes.apple.com/verifyReceipt"];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:#"POST"];
[storeRequest setHTTPBody:requestData];
// Make a connection to the iTunes Store on a background queue
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!connectionError) {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (jsonResponse) [self restoreFromOriginalVersionWithReceipt:jsonResponse];
else [self restoreFromOriginalVersionWithReceipt:nil];
} else {
[self restoreFromOriginalVersionWithReceipt:nil];
}
}];
}
This then calls the following method:
- (void)restoreFromOriginalVersionWithReceipt:(NSDictionary *)receipt
{
if (receipt == nil) {
// CALL METHOD TO HANDLE FAILED RESTORE
} else {
NSInteger status = [[receipt valueForKey:#"status"] integerValue];
if (status == 0) {
NSString *originalApplicationVersion = [[receipt valueForKey:#"receipt"] valueForKey:#"original_application_version"];
if (originalApplicationVersion != nil && [originalApplicationVersion isEqualToString:#"1.0"]) {
// CALL METHOD TO HANDLE SUCCESSFUL RESTORE
} else {
// CALL METHOD TO HANDLE FAILED RESTORE
}
} else {
// CALL METHOD TO HANDLE FAILED RESTORE
}
}
}
Now this doesn't work. When someone installs version 1.1 and taps restore purchases, it restores successfully when it shouldn't.
I've just realized that in my Info.plist, my CFBundleShortVersionString is 1.1 but my CFBundleVersion is 1.0.
This may be a really daft question, but is the receipt providing an original_application_version of 1.0 (due to the wrong CFBundleVersion) even though the update is versioned 1.1?
So if I release a new update with this corrected to say version 1.2 (for both the CFBundleShortVersionString and CFBundleVersion), will the problem be resolved?
-- UPDATE --
So I just uploaded a new version to the app store with both the CGBundleVersion and CFBundleShortVersionString equal to 1.2. However, I'm still facing the same problem - users downloading version 1.2 for the first time and tapping on restore purchases are being upgraded for free (due to the receipt check outlined above). It seems the original_application_version is always coming to 1.0.
Note: I am downloading the app under a new iTunes account that has not previously downloaded the app.
Here is the receipt I have if I install from the app store and then try to get the receipt through Xcode
2014-08-27 08:46:42.858 AppName[4138:1803] {
environment = Production;
receipt = {
"adam_id" = AppID;
"application_version" = "1.0";
"bundle_id" = "com.CompanyName.AppName";
"download_id" = 94004873536255;
"in_app" = (
);
"original_application_version" = "1.0";
"original_purchase_date" = "2014-08-26 22:30:49 Etc/GMT";
"original_purchase_date_ms" = 1409092249000;
"original_purchase_date_pst" = "2014-08-26 15:30:49 America/Los_Angeles";
"receipt_type" = Production;
"request_date" = "2014-08-26 22:46:42 Etc/GMT";
"request_date_ms" = 1409093202544;
"request_date_pst" = "2014-08-26 15:46:42 America/Los_Angeles";
};
status = 0;
}
Any thoughts?
I stumbled upon the same issue - I converted my app from paid to freemium and tried to use original_application_version in the app receipt to decide who to unlock the new freemium features for. I, too, was unsuccessful.
However, what I found out was that I was using original_application_version incorrectly. The name misled me into thinking that this string corresponds to the version number of the app. On iOS, it does not. original_application_version is actually the build number of the app.
Original Application Version
This corresponds to the value of CFBundleVersion (in iOS) or
CFBundleShortVersionString (in macOS) in the Info.plist file when the
purchase was originally made. In the sandbox environment, the value of this field is always “1.0”.
Source: https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
I think this could be the reason why you are getting a number you are not expecting.
Using original_purchase_date, as you did in the end, is a reliable alternative though.
It's been a while since this question was asked, but it raises some very important points:
The original app version field in the receipt corresponds to CFBundleVersion, not CFBundleShortVersionString. In a sandbox (developer build) environment, the value string is always "1.0".
Note that when converting from a paid app to freemium, if an original (paid version) user uninstalls the app, then reinstalls from iTunes, there will be no local receipt. Calling SKPaymentQueue restoreCompletedTransactions will not cause a new receipt to be downloaded if that user has never made an in-app purchase. In this situation, you need ask for a receipt refresh using SKReceiptRefreshRequest and not rely on restore functionality.

How to specify the app name user was using to post - (via app name) using SDK 3.1

Using the new Facebook SDK 3.1 and iOS 6 there are 2 (actually 3) ways to post.
(Seems the new trend is to have more options to make it more simple??) OMG!!
Here is one:
SLComposeViewController *fbPost = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
[fbPost addURL:[NSURL URLWithString:href]];
[self presentViewController:fbPost animated:YES completion:nil];
And this is another way using native dialogs:
[FBNativeDialogs presentShareDialogModallyFrom:self
initialText: nil
image: nil
url: [NSURL URLWithString:href]
handler:^(FBNativeDialogResult result, NSError *error) {
if (error) {
}
else
{
switch (result) {
case FBNativeDialogResultSucceeded:
break;
case FBNativeDialogResultCancelled:
break;
case FBNativeDialogResultError:
break;
}
}
}];
We, developers, think this is cool because we give a nice functionality to the user and also because our app name appears in the post and that can make some promotion of the app.
The funny thing is that latest implementations are not allowing to specify the app name was posting, the name appears after 'via'.
I tried aswell using SLRequest:
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *fbType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
(options)[#"ACFacebookAppIdKey"] = kFacebookAppID;
(options)[#"ACFacebookPermissionsKey"] = #[#"publish_stream"];
(options)[#"ACFacebookAudienceKey"] = ACFacebookAudienceFriends;
[store requestAccessToAccountsWithType:fbType options:options completion:^(BOOL granted, NSError *error) {
if(granted) {
// Get the list of Twitter accounts.
NSArray *fbAccounts = [store accountsWithAccountType:fbType];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
(params)[#"link"] = href;
// (params)[#"picture"] = picture;
// (params)[#"name"] = name;
(params)[#"actions"] = #"{\"name\": \"Go Gabi\", \"link\": \"http://www.gogogabi.com\"}";
//Set twitter API call
SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodPOST
URL:[NSURL URLWithString:#"https://www.facebook.com/dialog/feed"] parameters:params];
//Set account
[postRequest setAccount: [fbAccounts lastObject]];
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if(error)
{
NSLog(#"%#", error.description);
}
else
{
NSLog(#"%#", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]);
}
}];
} else {
}
}];
Unfortunatelly to share that name is not so trivial anymore, I wonder why and who was designing the new implementation...
I would appreciate to get some help on that, thanks in advance.
I try to make my questions funny because is soo boring spend time in so trivial topics...
When you use the SLComposeViewController, it's actually the system presenting to you their controller, and it's the user who sends using the post button. Therefore on Facebook it appears as "via iOS".
There's no way to change that.
Using the Facebook SDK 3.1, under the hood it is also using the iOS 6 native integration, so when you're calling the FBNativeDialogs, on iOS 6, it's using SLComposeViewController.
Facebook continued to develop their SDK because they provide a couple of nice modules to use "out of the box" - this includes friends list selector etc... But I believe the biggest reason for Facebook to continue supporting their SDK it for backward compatibility. Under the hood if you're not on iOS 6, it falls back to it's library, and if you are on iOS 6, it uses the system integration.
Facebook is a big thing, and now it's natively available a lot of developers will be using it, just like Twitter's integration last year. The problem of course is at that point the developer has the option to drop older iOS support, or... have a lot of duplicate code, in the sense that they will check for SLComposeViewController and if it's not available (iOS 5) then use the old Facebook SDK... You can imagine how this would become very messy very quickly.
So, the Facebook SDK (3.1) is using iOS system Facebook integration if available, or if not, it's own. In a nutshell, unless you really want the Facebook SDK goodies (friend picket to name one), and you're not planning on supporting iOS < 6 then you don't need to worry about their SDK, just use the Social framework.
So, back to your question, there are 3 ways to post to Facebook ? Actually taking into consideration what I mentioned, there are 2 ways in iOS 6: SLComposeViewController or, SLRequest. On older iOS versions, only 1: Facebook SDK.
Since the SLComposeViewController is owned by the system, not your app, it will always share as "via iOS".
On the other hand SLRequest will show your apps name. When you specify an account for your SLRequest, that account was acquired via the ACAccountStore as a result of passing in some options including ACFacebookAppIdKey, which will be used to determine your Facebook apps name to post onto the users feed as part of the post.
Hope this helps.

No restore button for in app purchase causes rejection

I am implementing an application using in app purchase with non-consumables items, it was rejected by apple and the reason is:
We found that your app offers In-App Purchase/s that can be restored but it does not include a "Restore" feature to allow users to restore the previously purchased In-App Purchase/s.
To restore previously purchased In-App Purchase products, it would be appropriate to provide a "Restore" button and initiate the restore process when the "Restore" button is tapped.
For more information about restoring transactions and verifying store receipts, please refer to the
and there is no link to refer to, I have already implemented the:
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
with
SKPaymentTransactionStateRestored case.
but I didnt implement:
`restoreCompletedTransactions` or `paymentQueueRestoreCompletedTransactionsFinished`
are these methods necessary for the in app purchase to be approved, or what is the exact problem.
Thanks
Use the following to restore the products ID's that user did purchased from your app
- (void) checkPurchasedItems
{
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}// Call This Function
//Then this delegate Function Will be fired
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
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];
}
}
the purchasedItemIDs will contain all the product IDs that the user purchased it .. you could put a button to call this function when it finished you show all these products to enable the user to download it again.