Simplicity is the ultimate sophistication

Newsstand and Subscriptions

| Comments

Two different concepts

In my previous article on the subject I explained how to create an app that supports the Newsstand feature introduced by Apple with iOS 5. The concepts explained in that post provide the technical references needed to build an app that is able to retrieve and download any new content, even in the background, and update the cover-like icon in the Newsstand app. Unfortunately while technically an app build in this way is completely functional, it wouldn’t be approved by Apple if sent for review. The reason for this is that Apple requires that each app compatible with Newsstand must offer its contens using an iTunes managed subscription.

Subscriptions are a rather new concept. They have been initially introduced with iOS 4 but their support has been greatly improved by Apple after a long discussion taken with publishers in order to build a business model acceptable for everybody. In particular the subscription concept has been enlarged by adding auto-renewable subscriptions and with the introduction of Newsstand - and its concept of issues auto-download - also free subcriptions have been added.

Before entering in the detail I would to emphasize the fact that one of the most interesting features of Newsstand, that is background auto-downloading of a new newspaper or magazine issue, is triggered by the special push notification I talked about in the previous part of this tutorial. The In App Purchase concepts are related to auto-download but there is no relation at all, from coding point of view, between the two concepts. As soon as the pubsliher has some new content, he can push this information to the application passing the content-available message together will all info required by the app to start automatic download. The In App Purchase subscriptions concept enters in this scheme from the publisher side by answering a simple question: “did the user subscribe to receive new content from us”? This means that the publisher is not allowed to push any new content to the user if he didn’t previously subscribed. That’s why Apple requires that each Newsstand application is paired with a corresponding In App Subcription service: because the customer must explicitly request for getting automatically the content (even if for free) and at the same time they want to reduce extra bandwith and device resources usage if the contents are not required.

Adding your Newsstand In App Purchases

I will not enter in a detailed explanation of how to setup your application for In App Purchase. This is well explained in Apple’s iTunesConnect Developer Guide and In App Purchase Programming Guide. I strictly recommend you read these guides, especially the second one, and exercise your developer account by creating a test app on iTunesAccount. In any case it is worth to summarize the main steps:

  • Enter in your application page inside the iTunesConnect portal and click on the Manage In-App Purchases button
  • For each type of subscription you want to setup, generate a specific In App Purchase product. In the following picture you can see that I listed three possible choices: one free subscription (note that free subscriptions don’t expire) and two auto-renewable subcriptions, both linked to the same product but with different duration and price.
  • Generate a shared secret and take note of it. You will need it later.
  • Note that the portal page has another sections called Newsstand. It will be used by the App Store only for marketing purposes and allows the publisher to keep his App Store page uptodate with the latest cover, consider for example the iTunes link of the New Yorker app: you will see the latest cover displayed instead of the app icon. An extra option given by Apple is the possibility to link this page to a Newsstand Atom Feed URL for automatic update. We’ll not talk anymore about this option in this tutorial.

A free subscription doesn’t require much data to be inserted: as it is an In App Purchase product, you must provide a unique product identifier; recommended practice is to use the reverse DNS convention, e.g. com.viggiosoft.NewsstandTutorial.subscription.free is enough self explaining; you will also add a display name (that is a title for the subscription) and a description, both localized. Auto-renewable subscriptions are a bit more complex. Infact when you define a subscription you will define a sort of reference product container that will open a family of similar In App Purchase products: this is an In App Purchase product that will not contain any reference to the duration of the subscription and will be used as a base for the single durations. This means that immediately after you have added your reference product, specifying its ID (still in reverse DNS), display name, description and publication name (all localized), you will be asked to add each specific product ID linked to the required duration: one week, one month, and so on up to one year. Note that the display name and description of these sub-products is taken directly from their reference, so the only information that you will be required to add is the duration, the price and of course the unique product ID.

Once this task has been accomplished, you can finally exit iTunesConnect and enter in the XCode domain.

Store Kit implementation

In our simple implementation we can imagine the app as in part I where we added three extra subscription buttons. Note that we are not implementing here any server-side capability, we’ll give some hints at the end and the app will emulate a basic receipt validation mechanism only for the purpose of completeness. The first button will allow the user to subscribe for free to a specific set of magazines. The other two options will ask the user to subscribe to the two one-month and one-year auto-renewable offers that we previously defined in iTunesConnect. Technically we are doing in all three cases a real In App Purchase transaction: in one case it will be free, in the other two cases a monetary transaction will occur.

Below you can see a set of snapshots taken from the app while used in the testing (sandbox) environment. You will notice that more or less the flow is the same of standard In App Purchase with a few noticeable differences:

  1. The title will change to Confirm Subscription
  2. Note that for the paid case, the 1 month term is automatically generated by the system based on the duration of the selected subscription. The only term customizable by the user is the Super Magazine Subscription definition that we set in iTunesConnect without any explicit reference to the duration.
  3. Immediately after the purchase, the system will ask for the explicit permission to send some personal data to the publisher. This is another compromise between Apple and the publishers that accepted to provide discounted subscriptions but in return of the possibility to control this kind of data (useful for advertising purposes).
  4. In case you already subscribed, the system will recall the user that he/she already subscribed and that the subsription will renew automatically at expiration. Read below to know how to avoid while developing the app to wait 1 month or 1 year to test your app after you did the first subscription…

At this point the code is not different from the usual code you may have written in the past. Let’s summarize here the main steps with the required code. For simplicity we have merged together the Store Kit communicationinside the main app view controller. Probably in a more complex application you want to separate the view controller from the store logic. As basic setup, don’t forget to link your code to the StoreKit library and import the framework header:

1
#import <StoreKit/StoreKit.h&gt>

Besides our view controller will need to declare itself as conform to SKPaymentTransactionObserver, SKRequestDelegate and SKProductsRequestDelegate protocols. Finally our application delegate must register our view controller to be a transaction observer:

1
2
3
4
5
6
7
8
9
10
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // other initialization code goes here
    // in particular we assume our "store" view controller has been already initialized here

    // StoreKit transaction observer
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self.store];

    // other initialization code
}

Immediately after clicking on one of the subscription buttons, we’ll call a method that will start the purchase procedure (eventually updating the UI) based on the product ID we pass to it: what we do here is to use a variable called purchasing to avoid two or more purchase operations going together (note that In App Purchase is highly asynchronous with unpredictable delays and trying to place together more purchases would be messy for the user). Then we create our StoreKit product request and submit to the App Store:

1
2
3
4
5
6
7
8
9
10
- (IBAction)subscription:(NSString *)productId {
    if(purchasing_==YES) {
        return;
    }
    purchasing_=YES;
    // product request
    SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:productId]];
    productsRequest.delegate=self;
    [productsRequest start];
}

The request can fail, and in such case we reset our UI (not displayed here) and unset the purchasing flag. If the request succeeds, we’ll get the product IDs returned from the request and we’ll add them to the payment queue, effectively starting the operation. For completeness we should hilight the fact that in most cases the initial prouct request can be done independently on the effective user request of doing a purchase: the reason for this is that doing a product request is the only way to retrieve the exact product price localized to the user iTunes Store he’s currently connected. This means that we should never hard code a price in our app but we must retrieve it dynamically using a product request. In this case I simply skipped this extra step, not necessary for the purpose of the tutorial, by avoiding to mention any price in the subscription buttons! but be awave that showing the price together with a “buy” button is more than a welcome addition. Returning to our code, you will see below that we’ll loop through the returned responses (we shouldn’t get more than one due to the logic of the app) and for each of them we submit a payment request.

1
2
3
4
5
6
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
   for(SKProduct *product in response.products) {
        SKPayment *payment = [SKPayment paymentWithProduct:product];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
}

As we previously registered this view controller instance as store kit transaction observer, we’ll get a few calls to the paymentQueue:updatedTransaction: protocol method. We must follow the right logic according to the effective state of the transaction. In our case we’re of course interested to the “success” case, managed via a call to our internal finishedTransaction: method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for(SKPaymentTransaction *transaction in transactions) {
       switch (transaction.transactionState) {
            case SKPaymentTransactionStateFailed:
                [self errorWithTransaction:transaction];
                break;
            case SKPaymentTransactionStatePurchasing:
               break;
            case SKPaymentTransactionStatePurchased:
            case SKPaymentTransactionStateRestored:
                [self finishedTransaction:transaction];
                break;
            default:
                break;
        }
    }
}

To terminate the transaction, we’ll inform the transaction queue that we declared our transaction finished. Now in a standard In App Purchase environment, based on typical consumable or non-consumable product, we should immediately provide the purchased product to the customer. For the subscription case infact we’re not providing a specific product but a subscription to a set of future products. Typically we’ll immediately unlock the access to the latest magazine (and as we’re in Newsstand doing this is quite easy…) but the most important stuff is that we must inform our server of the transaction data. The code below is a possible example of this, but we’ll explain the mechanism in more detail in the next paragraph. For the moment just notice that apart a few alerts displayed with some logging information we are sending the transaction receipt received from the store to a specific class called ReceiptCheck:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
-(void)finishedTransaction:(SKPaymentTransaction *)transaction {
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Subscription done"
                                                    message:[NSString stringWithFormat:@"Receipt to be sent: %@\nTransaction ID: %@",transaction.transactionReceipt,transaction.transactionIdentifier]
                                                   delegate:nil
                                          cancelButtonTitle:@"Close"
                                          otherButtonTitles:nil];
    [alert show];
    [alert release];
    // save receipt
    [[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier forKey:@"receipt"];
    // check receipt
    [self checkReceipt:transaction.transactionReceipt];
}

-(void)errorWithTransaction:(SKPaymentTransaction *)transaction {
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Subscription failure"
                                                    message:[transaction.error localizedDescription]
                                                   delegate:nil
                                          cancelButtonTitle:@"Close"
                                          otherButtonTitles:nil];
    [alert show];
    [alert release];
}

-(void)checkReceipt:(NSData *)receipt {
    ReceiptCheck *checker = [ReceiptCheck validateReceiptWithData:receipt completionHandler:^(BOOL success,NSString *answer){
        if(success==YES) {
            NSLog(@"Receipt has been validated: %@",answer);
        } else {
            NSLog(@"Receipt not validated! Error: %@",answer);
        };
        [checker release];
    }];
    [checker retain];
}

Server-side subscriptions management

We previously said that after a user completed a subscription purchase, we are not able to provide the full content immediately, but we still need to know when this content must be provided for the future. The current case is easily managed. If we call our 1-month subscription com.viggiosoft.NewsstandTutorial.subscription.1month then from the purchase receipt we know the purchase date and simply parsing the product ID we are able to define the expiration date as the transaction date + 1 month. This is easy and can be stored inside the app, at least for UI management. But there are many things that could happen in the subscription management and that the app is not necessarily aware: e.g. the subscription is renewed, or it has not been renewed by user choice, or it has not been renewed just because before the expiration we increased the price of our subscription (Apple stops any automatic renew in such event) or finally we defined a marketing bonus, that is provide e.g. 1 extra weekly subscription for each 1 month purchase (this is a possibility that is explicitly provided by iTunesConnect and is called marketing opt-in incentive. This means that a set of transactions may occur outside the control of the application: at this point the data we stored in the app immediately after the purchase couldn’t be reliable and we need to contact the App Store for the uptodate information. The best way to do this is to send the receipts to our server and let the server to systematically call Apple to maintain its subscription status for each user aligned with the App Store; in our case our app can simply use a basic API towards our server to lock or unlock certain magazines. But there is another reason why the server must take this information and it is strongly related to Newsstand: in order to push new content to the user device, the server must know for each new issue if the user is subscribed to it or not. So every time the publisher releases a new copy of his daily newspaper or a new issue of his weekly magazine, the subscription information of all users must be queried and the new content must be pushed to their device only if their subscription is still valid. Clearly we need to understand how this information is managed and can be retrieved.

In our example app we decided to implement a basis scheme of receipt validation directly inside the app. As we stated before, this is not the right method to do it and we simply added this extra code only for the needs of this tutorial. That’s why we decided to move this receipt validation scheme in an external class.

The end of the payment transaction is represented by an instance of the SKPaymentTransaction class which contains all the data required to keep track of the paymen: the ID of the purchased product, its quantity, the price paid, the date, a transaction identifier and most of important of all - for server based delivery of In App Purchases - the transaction receipt. This receipt is a chunk of data, internally coded by Apple, that can be decoded by sending an appropriate JSON message to iTunes’ receipt verification API (there are two different addresses, one for production and one for sandbox testing, see Apple documentation for a complete description of this API and the returned messages and error codes):

1
2
3
4
{
    "receipt-data":"base64-data-encoding-of-receipt-data"
    "password":"your-autorenewable-shared-secret-from-itunesconnect"
}

In return Apple server will give us a full description of the receipt. A real example taken from the tutorial app of a possible receipt of the free subscription is shown below. .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    "status":0,
    "receipt":{
        "bvrs":"1.0",
        "item_id":"473670104",
        "bid":"com.viggiosoft.tutorial.NewsstandTutorial",
        "product_id":"com.viggiosoft.tutorial.NewsstandTutorial.001",
        "purchase_date":"2011-10-29 19:49:02 Etc/GMT",
        "original_transaction_id":"1000000011535741",
        "quantity":"1",
        "original_purchase_date":"2011-10-29 17:14:33 Etc/GMT",
        "transaction_id":"1000000011544007"
    }
}

For a auto-renewable transaction the returned data contains more data. Initially you must check for the status key, if it is 0 then all went OK, if not 0 then you must understand the error message (the auto-renewable subscriptions possible status codes are summarized in Apple documentation). In addition you will get the expiration date (inclusive of the opt-in incentive) and the original transaction ID, that is the ID of the transaction that originated the subscription. In case the transaction has been auto-renewed and we are still sending to our server the original receipt, iTunes will return as an extra field, called latest_receipt_inf which will contain the latest renewed receipt together with the base64 version of this receipt. It makes sense in our case to replace our original receipt data with the latest one for the next queries.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
    "status":0,
    "receipt":{
        "bvrs":"1.0",
        "item_id":"476937514",
        "bid":"com.viggiosoft.tutorial.NewsstandTutorial",
        "product_id":"com.viggiosoft.tutorial.NewsstandTutorial.1month",
        "purchase_date":"2011-10-29 20:30:07 Etc/GMT",
        "expires_date_formatted":"2011-10-29 20:35:07 Etc/GMT",
        "expires_date":"1319920507692",
        "quantity":"1",
        "original_transaction_id":"1000000011540367",
        "original_purchase_date":"2011-10-29 18:41:32 Etc/GMT",
        "transaction_id":"1000000011546189"
    },
    "latest_receipt_info":{
        "bvrs":"1.0",
        "item_id":"476937514",
        "bid":"com.viggiosoft.tutorial.NewsstandTutorial",
        "product_id":"com.viggiosoft.tutorial.NewsstandTutorial.1month",
        "purchase_date":"2011-10-29 20:30:07 Etc/GMT",
        "expires_date_formatted":"2011-10-29 20:35:07 Etc/GMT",
        "expires_date":"1319920507692",
        "quantity":"1",
        "original_transaction_id":"1000000011540367",
        "original_purchase_date":"2011-10-29 18:41:32 Etc/GMT",
        "transaction_id":"1000000011546189"},
        "latest_receipt":"base64-encode-of-latest-receipt"
     }
}

As you can see the receipt doesn’t contain any reference to the Apple ID used to make the purchase. The only way for us to match the specific device with the returned receipt is to send to our server both the Apple Push token (APNS token) and store both in the same record of our server-side database. In this way when we have new content available to be pushed to our customer Newsstand we’ll be able to process all of our APNS tokens, check the receipts and if the new content (magazine or newsspaper) is still covered by the user subscription we’ll then send the Newsstand push notification to trigger the download on the user device.

What happens if the user wants to use the same subscription data in another device? in such case we must provide the typical transaction restore process as required by the Store Kit guidelines. Typically the restore button is placed somewhere in your application UI and his only purpose is to call this method:

1
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

All consumable and non-consumable transactions will be restored in the usual way: the SKPaymentTransactionStateRestored transaction update will be notified to our Store Kit observer and from the originalTransaction property of our SKPaymentTransaction we can get the product ID and then unlock it on the new device (don’t forget that the user can simply buy single issues from our app). For subscriptions the history is a little bit different, infact the system will return us the full set of transaction associated to the subscription history: infact each time a subscription is automatically renewed a new transaction is generated and it is stored in Apple servers. But why we need to know the whole history of user subscriptions? because as soon as we restore the subscription on a different device, we need to know for which dates the user was subscribed and only knowing the whole subscription history we’ll be able to unlock the right content: nothing more, nothing less than the user purchased for. Typically these receipt will be of the form explained above, that is a json message composed of the two fields: “receipt” and “latest_receipt_info”. The latter will all refer to the latest valid receipt, while the former will be different and related to each of the transactions: we must parse the purchase and expiration dates to create the valid subscription date range and provide the content accordingly.

Time compression

Before delivering our application we must carefully test all possible subscription cases: add a subscription, auto-renew, new content push notifications, subscription expiration, and so on. Clearly it would be impossible to wait for days or weeks or months to do these tests only. In Sandbox testing Apple compresses this time by giving you the possibility to test your application in minutes or hours. Looking at the receipts above, you will see that for a subscription purchased on Oct 29 at 20:30 (8:30 PM) there is a corresponding expiration date of only 5 minutes (same date at 20:35). This is because in the sandbox environment a 1 month subscription is compressed to a 5 minutes time scale. The full table of subscription duration / sandbox time scale is given in the figure. Besides auto-renewal will stop after 6 renewals thus giving you the possibility to manage the end-of subscription case (to avoid delivering content the user didn’t pay for): don’t forget that with a In App Purchase test account you cannot connect to the real iTunes store and so you have no way to manage subscriptions manually.

To better visualize this effect I added in my example application a “Receipts” button. After each purchase I store in a file the receipt data. When the user clicks this button, this file is opened and all stored receipts are sent to Apple for validation. The returned JSONs are then displayed as an alert. So if you try for example to purchase a 1 month subscription, and then immediately after the purchase you query for the receipts, you will get the purchase receipt only. Then try after 5/10 minutes (corresponding to 1 month in the time compression scale) to reopen the app and query for receipts again. The system will return you 2 or more receipts, each one corresponding to a different auto-renewal subscription: note the expiration date, it will be the same in the latest receipt field, but it will change in the receipt field as it is associated to each 1 month (5 minutes) renewal. Then close the app, wait for 40 minutes and try again: you will not get more than 6 receipts, as by convention after the 6th renewal attempt the subscription will automatically expire. You can improve this code by doing some real subscription date range detection and show it clearly to the user. Even more interesting is that as a transaction is automatically generated for each auto-renew, if you keep your app running in the foreground during an autorenewal, your app will be notified through the transaction observer mechanism and the receipt will be immediately validated.

Conclusions

This second part of the Newsstand Tutorial talks a bit of Newsstand and much more of In App Purchase and subscriptions. Of course you can stil make your app to be subscription based only, without any Newsstand integration, and probably you can manage all subscriptions directly inside your device by saving receipts locally and querying for their validity when needed. The ReceiptCheck class in my application example is done for this. What you cannot to do is to release a Newsstand app without In App Purchase. As one of the most appreciated Newsstand capabilities is to have the device download every new content in the background and at night-time, you must keep a note of each subscription and associate it to a push notification token: you can’t do this without a server, so in such case moving the whole receipt validation mechanism on your server is required.

The source code for this two part tutorial is hosted on GitHub. Note that this is not completely useful if you don’t do the whole setup for push notifications, In App Purchase, shared secrect and so on. My suggestion is: don’t try to run it, don’t use it as production code, but only as reference to better follow this tutorial and get some help if you’re stuck.

Comments