Auto-Renewable Subscription is probably the most difficult of all types of In-App Purchase in iOS, and it is not easy to implement it right from beginning to end, and even after going through this difficult path, you may be faced with the refusal of the censors to accept your application.
In this post, I will try to take you through all the stages of subscription implementation and, perhaps, I can dissuade you from this idea.
What is Auto-Renewable Subscription?
This type of In-App allows you to offer the user to subscribe to the content you provide with one action, and then forget about any payments - the money will be charged off monthly (you can choose a subscription period arbitrarily), and he will no longer need to think about that the subscription period comes to an end, and you don’t have to remind him about it once more - the money will be withdrawn from his account and sent to yours.
')
Also, you do not have to build in the ability to unsubscribe - this is possible only in the iOS settings - it is convenient, and the refusal button doesn’t call up the user.
Why can I refuse?
It would seem that such an ideal scheme, I will do a month trial, and then let them subscribe to access to the application for a dollar a month. But it's not that simple. The key words here are that Auto-Renewable Subscription is intended to provide Digital Content, and these words are very vague.
For example, our application provides the user with several new words every day, and we thought it was quite a “digital content”. The guys at Apple didn't think so. As a result, I had to redo everything on Non-renewing subscriptions.
Therefore, before you begin to introduce this type of shopping, think about whether you really fit what Apple put into the meaning of this mechanism.
Well, then move on to the implementation
The process of introducing Auto-Renewable Subscription consists of three steps:
- Add and configure in iTunes Connect
- Configuring the server side for validation
- The implementation of in-app purchases
Add and configure in iTunes Connect
First we need to get a
shared secret , we will use it on the server side when accessing Apple servers. Go to the section Manage In-App Purchases and generate it.
In the same place, we add a new In-App of the appropriate type, select the duration, the name and fill in all the fields.
We are especially interested in
Product ID - we will request it from the application.
We will also be asked to add a link to our Privcay Policy, without it it is impossible. What is there to write? Yes, it is not so important, we wrote something like this:
www.easy10.com/privacyWell, we got the
shared secret , there is a
Product ID , go to the server part.
Configuring the server side for validation
This stage is necessary for us to check and decrypt the receipt that comes from Apple upon purchase and at the time when we are wondering if the subscription has been updated in the new month.
This mechanism is implemented as follows:
- In the application we make a purchase request
- We get a receipt from Apple
- We code it in base64
- Sent to our server (these steps can be swapped)
- From our server we send to the server Apple
- They decrypt it and send it to us.
- We retrieve the necessary information and tell the application what the subscription status is
If you do not have your own server, steps 4-6 can be done using the service
www.beeblex.comIf you want to have full control over the process, then in step 5 you should send to
buy.itunes.apple.com/verifyReceipt JSON the following content
{ "receipt-data" : "(receipt bytes here)", "password" : "(shared secret bytes here)"// shared secret }
In response to step 6, we get the following JSON
{ "status" : 0, "receipt" : { (receipt here) }, "latest_receipt" : "(base-64 encoded receipt here)", "latest_receipt_info" : { (latest receipt info here) } }
If
status = 0, then everything is fine. If it is 21006, then our user has not renewed the subscription and we need to stop providing content. All other codes can be viewed here:
Status codes for auto-renewable subscriptionsIn the field of
receipt we will be most interested in the value of
expires_date - we compare it with the current one and on the basis of this we make some decisions. If the user has not canceled your subscription, it will be automatically updated.
And how to test all this?
To do this, we need to create a test user in iTunes Connect and on the server send a request to check receipt at
sandbox.itunes.apple.com/verifyReceiptI want to draw your attention to the fact that in test mode the subscription will be automatically renewed
only six times . And that's all. This is almost not written anywhere, and I spent sleepless nights, not understanding what was the reason that expires_date was not changed by me. It is in this. Therefore, you will have to create a new user to test this particular moment of self-renewal.
In addition, the validity period in test mode is much shorter than it actually is, for example, a monthly subscription is compressed to 5 minutes.
And lastly, I will give the code of purchases in the application itself. Even though it was written in hundreds of places, since I decided to put it all together, I cannot do without this part.
The implementation of in-app purchases
For this, iOS is designed for StoreKit.framework. It contains everything you need to make a purchase, track the progress of a transaction and restore purchases from a new device.
First, we will need to add a transaction browser, which should support the SKPaymentTransactionObserver protocols - this is necessary to ensure that the transaction is not lost when the user has suddenly turned off the phone or the Internet.
Therefore , it is best to do it somewhere in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
Create a PaymentsOberver class that will support all the protocols we need.
#import <Foundation/Foundation.h> #import <StoreKit/StoreKit.h> @interface PaymentsObserver : NSObject <SKPaymentTransactionObserver,SKProductsRequestDelegate,SKRequestDelegate> - (void)requestProductData:(NSString*)ofProduct; - (BOOL)validateReceipt; @end
First, we must form a request for the product, specifying the Product ID that we need, we can request an unlimited number of products at once.
- (void)requestProductData:(NSString*)ofProduct { if ([SKPaymentQueue canMakePayments])
Upon completion of the request, we will receive a call to one of the SKProductRequestDelegate methods, if successful, this
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { NSArray *myProduct = response.products; if ([response.products count]) { SKPayment *payment = [SKPayment paymentWithProduct:[myProduct lastObject]]; [[SKPaymentQueue defaultQueue] addPayment:payment]; } }
In it, we add the resulting product to the transaction queue. Apple deals with it further, throwing the user out of the window asking if he is sure of the purchase. At the end of this process, we have a call to SKPaymentTransactionObserver, in which, depending on whether the transaction has passed, we continue our process, passing the receipt to the server for verification.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchased: [self completeTransaction:transaction]; break; case SKPaymentTransactionStateFailed: [self failedTransaction:transaction]; break; case SKPaymentTransactionStateRestored: [self restoreTransaction:transaction]; default: break; } } }
In case of successful verification of our receipt, we must provide the user with the content and, very importantly, remove the transaction from the queue.
- (void)completeTransaction:(SKPaymentTransaction *)transaction { if ([self validateReceipt:transaction.transactionReceipt]) { [self provideContent: transaction.payment.productIdentifier];
At the end of the subscription period, you will simply need to send a request to the server and check whether it is extended or not.
This is all, to me, in the end, it was not useful in this project, but I hope that some of you will be luckier!