In App Purchase Objective C Status from appStoreReceiptURL - ios

I am new in Objective c I found
NSURL* url = [[NSBundle mainBundle] appStoreReceiptURL];
NSLog(#"receiptUrl %#",[url path]);
for check In App Purchase Status for validation or something like this.
how can I get this : as NSString ( INITIAL_BUY,CANCEL)
.INITIAL_BUY Initial purchase of the subscription.
.CANCEL Subscription was canceled by Apple customer support.
.RENEWAL Automatic renewal was successful for an expired subscription.
.INTERACTIVE_RENEWAL Customer renewed a subscription interactively after it lapsed, either by using your app’s interface or on the App Store in account settings.
.DID_CHANGE_RENEWAL_PREFERENCE Customer changed the plan that takes affect at the next subscription renewal.

The url you have, is just the location of the receipt, so you first need to read the receipt file:
- (NSData *)loadReceipt
{
LogMethodCall
NSURL *url = NSBundle.mainBundle.appStoreReceiptURL;
if (!url) return nil;
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:url options:0 error:&error];
if (data) return data;
NSLog(#"Error loading receipt data: %#", error.localizedDescription);
return nil;
}
Now you have the receipt data, you can convert it to NSDictionary and view its contents:
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:[self loadReceipt]options:0 error:&error];
if (!error) {
NSLog(#"%#", json);
} else {
NSLog(#"%#", error);
}

Related

Get receipt information of renewable in app product purchase in ios

How to get latest receipt information of a renewable in app product in ios and how to check whether receipt is expire or not?
My code is :
- For getting receipt
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions
{
hide_HUD
SKPaymentTransaction *transaction = transactions.lastObject;
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchased:
{
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
NSLog(#"Order id ======>> %#",transaction.transactionIdentifier);
NSData *recData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
NSString* receiptString = [[NSString alloc] initWithData:recData encoding:NSUTF8StringEncoding];
[self getReceiptFromAppStore:recData Transaction:transaction isBackground:NO];
break;
}
case SKPaymentTransactionStateFailed:
NSLog(#"Purchase failed ");
break;
case SKPaymentTransactionStateRestored:
[[SKPaymentQueue defaultQueue]finishTransaction:transaction];
break;
default:
//NSLog(#"Purchase failed ");
break;
}
}
For getting receipt
-(void)getReceiptFromAppStore:(NSData*)ReceiptData Transaction:(SKPaymentTransaction*)AutoTransaction isBackground:(BOOL)isBackground
{
NSData *receipt=ReceiptData; // Sent to the server by the device
// Create the JSON object that describes the request
NSError *error;
NSDictionary *requestContents = #{
#"receipt-data": [receipt base64EncodedStringWithOptions:0],
#"password" : #“*********************”
};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (!requestData) { /* ... Handle error ... */ }
// Create a POST request with the receipt data.
NSURL *storeURL = [NSURL URLWithString:#"https://sandbox.itunes.apple.com/verifyReceipt"]; //for Testing
// NSURL *storeURL = [NSURL URLWithString:#"https://buy.itunes.apple.com/verifyReceipt"];//for live
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) {
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (error == nil && [jsonResponse valueForKey:#"latest_receipt_info"] != nil && [[jsonResponse valueForKey:#"latest_receipt_info"] count] > 0) {
/*
What I have to do here to check receipt is expire.
My goal is to user can not redirect to my
app’s home page if he unsubscribe.
For example if user purchased 1 month .
renewable plan on 20-1-2017 and on 25-1-2017
he/she unsubscribe for that plan and after 1 month (on 22-2-2017),
if he/she open my app , he should not get
redirected to my app’s home page.
*/
}
else{
NSLog(#"%#",error);
}
}
}];
}
I have got receipt but I don't know how to get data from receipt and how to use it for preventing user to go to app's home page.
Kindly read about receipt fields, You will have a all information like expire, cancelled by apple, billing info, cancellation and so on..
Link
You have to access latest object from latest_receipt_info array and check for field expires_date
The expiration date for the subscription, expressed as the number of milliseconds since January 1, 1970, 00:00:00 GMT.
ASN.1 Field Type 1708
ASN.1 Field Value IA5STRING, interpreted as an RFC 3339 date
JSON Field Name expires_date
JSON Field Value string, interpreted as an RFC 3339 date
This key is only present for auto-renewable subscription receipts. Use this value to identify the date when the subscription will renew or expire, to determine if a customer should have access to content or service. After validating the latest receipt, if the subscription expiration date for the latest renewal transaction is a past date, it is safe to assume that the subscription has expired.

Validating App store receipt gives DrmInvalidArgumentException

I'm following the documentation here.
https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
I have a receipt-data from my team that I'm trying to validate, they're getting error code 21002 which was malformed JSON. It looks like they had extra parameters appended to the base64 data, so I tried removing those and sending:
- (void)viewDidLoad {
[super viewDidLoad];
NSData *receipt; // Sent to the server by the device
// Create the JSON object that describes the request
NSError *error;
NSDictionary *requestContents = #{
#"receipt-data": #"<<$mybase64data>>", #"password" : #"<<$thepassword>>"};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:&error];
if (!requestData) { /* ... Handle error ... */ }
// 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) {
/* ... Handle error ... */
NSLog(#"conerror %#", connectionError);
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSLog(#"hello %#", jsonResponse);
NSLog(#"error %#", error);
if (!jsonResponse) {
}
}
}];
}
result:
2017-03-03 22:45:47.454 receipttest[89851:352604] hello {
exception = "com.apple.jingle.mzfairplay.validators.DrmInvalidArgumentException";
status = 21002;
}
2017-03-03 22:45:47.455 receipttest[89851:352604] error (null)
Something to keep in mind: In this example the data is sent to Apple straight from the App, but you might do this from a server too. When you testing your server App don't use NSLog() to print your base64 data, NSLog truncates data.
I had this issue when using a receipt from a test user that was a couple days old with a yearly auto-renewable subscription.
I checked the above helpful responses about extra characters etc (I also checked I had supplied my app secret for auto-renewable) with no joy.
In the end I tried creating A NEW SANDBOX user and it worked first time
with no other changes other than the new Receipt!
Hope this helps someone.
I also received the same error response for serveral receipts. The solution for me was to remove all occurrences of \r\n.
Maybe you have the same issue. I still haven't figured out when and why these chars are inserted.
Pay attention to URLENCODE:
- (NSString *)URLEncodedString
{
// CharactersToBeEscaped = #":/?&=;+!##$()~',*";
// CharactersToLeaveUnescaped = #"[].";
NSString *unencodedString = self;
NSString *encodedString = (NSString *)
CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)unencodedString,
NULL,
(CFStringRef)#"!*'();:#&=+$,/?%#[]",
kCFStringEncodingUTF8));
return encodedString;
}

Returns many transactions on iOS In-App-Purchases receipt validation

My app contains consumable IAP products, returns more than one transactions when I call validation receipt with this code:
[[NSBundle mainBundle] appStoreReceiptURL];
Is there any way to return only last transaction?
Is it related about restoring transactions?
I checked this Multiple receipt count for restoreCompletedTransaction inapp purchasing and this iOS in-app-purchase restore returns many transactions.
I tried to restore all purchases but it didn't work.
I'm using these lines for calling receipt:
- (void) checkReceipt {
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
if(!receipt) {
}
NSError *error;
NSDictionary *requestContents = #{#"receipt-data": [receipt base64EncodedStringWithOptions:0]};
NSLog(#"requestContents:%#", requestContents);
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (!requestData) { }
NSURL *storeURL = [NSURL URLWithString:#"https://sandbox.itunes.apple.com/verifyReceipt"];
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:#"POST"];
[storeRequest setHTTPBody:requestData];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError) {
} else {
}
}];
}
Note: This app supports iOS 8+.
It's not related to restoring transactions, it is because apple responds with array of all in-app transactions made by the user when making a validation request. The same information is contained in the receipt if you decode it locally.
If you are looking for the last transaction made you can sort the array ascending by the purchase_date_ms and take the last one.
My objective-c is not so hot so I can't help you with sorting but this document may help: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Collections/Articles/Arrays.html

iOS 7: in app purchase receipt validation and verification

I am new to receipt validation in iOS. I have implemented in-app purchase successfully. Now I wish to include receipt validation part in this in-app puchase.
My in-app purchase is for 3 products. I want that before purchase of each single product in my app, receipt validation should be performed.
For this I followed the following link: official apple developer tutorial
I managed to get the receipt data, but I am puzzled as what to do after getting the receipt data. How do I send it back to apple server for verification and then start the in-app purchase process?
Following is my code:
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
NSLog(#"transactions count : %d",transactions.count);
BOOL flgIsProductRestorable=NO;
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing:
{
// show wait view here
//statusLabel.text = #"Processing...";
NSLog(#"Processing...");
}
break;
case SKPaymentTransactionStatePurchased:
{
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[SVProgressHUD dismiss];
// remove wait view and unlock feature 2
//statusLabel.text = #"Done!";
NSLog(#"Success : %# = %#",transaction.payment.productIdentifier,transaction.transactionIdentifier);
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
NSLog(#"%#",receipt);
NSError *error;
NSDictionary *requestContents = #{
#"receipt-data": [receipt base64EncodedStringWithOptions:0]
};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (!requestData) { /* ... Handle error ... */ }
// Create a POST request with the receipt data.
NSURL *storeURL = [NSURL URLWithString:#"https://sand.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) {
/* ... Handle error ... */
} else {
NSError *error;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSLog(#"response : %#",jsonResponse);
if (!jsonResponse) { /* ... Handle error ...*/ }
/* ... Send a response back to the device ... */
}
}];
[self WS_EbookDetail:transaction.transactionIdentifier];
}...
I have just copy pasted the receipt retrieval code from the apple link I have given in this question.
What do I do next?
Keep in mind Apple recommends that you securely send the receipt data to your server and then call their servers. Calls to Apple's servers are not secure.
You can also do local receipt validation in your app. See Validating Receipts Locally for information on how to do this.
There is also a great WWDC video from 2014 "Preventing Unauthorized Purchases with Receipts" which goes into detail about implementing on device receipt validation.

what receipt for auto-renewable subscription should i validate?

i'm developing the server side of an app with IAP, i know that i need to send the receipt from the app to my server when the user do a subscription, then validate the receipt with the app store for the status, date expiration, etc, & give the user the content.
But my question is, what receipt do i need to check for the renew status? i mean, the first time i check the receipt the app store give me back a receipt, status and a latest receipt, this latest receipt is the one that should i use to check the status the next time or should i use always the original receipt? i been testing with both of them and they give me the same status from the app store but i'm not sure of what is the correct way to doing.
Thanks
Today, I have trouble with this problem.
Follow Apple doc here, I used this way to check subscription is expired or not.
+ (BOOL)checkInAppPurchaseStatus
{
// Load the receipt from the app bundle.
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
if (receipt) {
BOOL sandbox = [[receiptURL lastPathComponent] isEqualToString:#"sandboxReceipt"];
// Create the JSON object that describes the request
NSError *error;
NSDictionary *requestContents = #{
#"receipt-data": [receipt base64EncodedStringWithOptions:0],#"password":#"SHARE_SECRET_CODE"
};
NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
options:0
error:&error];
if (requestData) {
// Create a POST request with the receipt data.
NSURL *storeURL = [NSURL URLWithString:#"https://buy.itunes.apple.com/verifyReceipt"];
if (sandbox) {
storeURL = [NSURL URLWithString:#"https://sandbox.itunes.apple.com/verifyReceipt"];
}
NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
[storeRequest setHTTPMethod:#"POST"];
[storeRequest setHTTPBody:requestData];
BOOL rs = NO;
//Can use sendAsynchronousRequest to request to Apple API, here I use sendSynchronousRequest
NSError *error;
NSURLResponse *response;
NSData *resData = [NSURLConnection sendSynchronousRequest:storeRequest returningResponse:&response error:&error];
if (error) {
rs = NO;
}
else
{
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:resData options:0 error:&error];
if (!jsonResponse) {
rs = NO;
}
else
{
NSLog(#"jsonResponse:%#", jsonResponse);
NSDictionary *dictLatestReceiptsInfo = jsonResponse[#"latest_receipt_info"];
long long int expirationDateMs = [[dictLatestReceiptsInfo valueForKeyPath:#"#max.expires_date_ms"] longLongValue];
long long requestDateMs = [jsonResponse[#"receipt"][#"request_date_ms"] longLongValue];
NSLog(#"%lld--%lld", expirationDateMs, requestDateMs);
rs = [[jsonResponse objectForKey:#"status"] integerValue] == 0 && (expirationDateMs > requestDateMs);
}
}
return rs;
}
else
{
return NO;
}
}
else
{
return NO;
}
}
Hope this help.
after completion of your transaction you will get tansaction & trasaction.reciept for this transaction reciept you want to provide base64 here is the code for this
NSString *jsonObjectString = [self encodeBase64:(uint8_t*)transaction.transactionReceipt.bytes
length:transaction.transactionReceipt.length];
this jsonObjectString will save it to the server and while verifying the receipt you want to provide sharedsecret for this verify this link

Resources