Reputation: 1871
I have an in-app purchase that is an auto-renewing subscription. From the completed in-app purchase I need to be able to retrieve the subscription expiration date so that I know when the subscription is no longer in effect. My understanding is that this means (now as of iOS 7) starting with appStoreReceiptURL, getting the data behind it, and then somehow from there I can access elements about the in-app purchase.
But from there it's foggy, and none of the code samples I find online seem to work. A lot of them are out of date for iOS 7. Most of them do not deal with subscriptions at all. What I really need is some sample code that goes from appStoreReceiptURL to subscription expiration date in a straight line, with the fewest lines of code in-between. For some reason this seems to be extremely difficult, but I don't understand why. Often times people are incorporating receipt validation into this, which seems like a good idea, but it also adds another enormous level of complexity that is obscuring my main objective, and this receipt validation code always seems to not work. And where I seem to always hit a wall is where I take the NSData and base64 encode it. It seems it should look like this:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSLog(@"receiptURL:%@", receiptURL);
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
NSLog(@"receiptData:%@", receiptData);
NSString *receiptString = [self base64forData:receiptData];
NSLog(@"receiptString:%@", receiptString);
Where the base64forData method looks like this:
// from http://stackoverflow.com/questions/2197362/converting-nsdata-to-base64
+ (NSString*)base64forData:(NSData*)theData {
const uint8_t* input = (const uint8_t*)[theData bytes];
NSInteger length = [theData length];
static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t* output = (uint8_t*)data.mutableBytes;
NSInteger i;
for (i=0; i < length; i += 3) {
NSInteger value = 0;
NSInteger j;
for (j = i; j < (i + 3); j++) {
value <<= 8;
if (j < length) {
value |= (0xFF & input[j]);
}
}
NSInteger theIndex = (i / 3) * 4;
output[theIndex + 0] = table[(value >> 18) & 0x3F];
output[theIndex + 1] = table[(value >> 12) & 0x3F];
output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
output[theIndex + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
}
return [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
}
In the output, I get back a valid receiptURL, but only if I execute the code in the app delegate. If I try to execute it in a subclass like my store observer it always returns nil. I'm ignoring this for now and just executing it in the app delegate. Then if the receiptURL is valid then I get a valid receiptData block, and then receiptString is nil. So it seems as though the base64forData method is not working, or maybe the whole approach is wrong?
What is the simplest way to get the subscription expiration date from a completed SKPaymentTransaction that is not deprecated in iOS 7?
Upvotes: 4
Views: 5433
Reputation: 386
We use Apple's app store receipt validation service. There is great sample code in Tom Kincaid's answer to this question:-
StackOverflow app store receipt question
Upvotes: 1