Processing AppStore auto renewable subscriptions in PHP

Dec 4, 2013, 4:49:15 PM

in-app-purchases

First all be aware of Apple's policy as for auto renewable subscription and application types that could use it (your app could be rejected if reviewers will decide you that you are missusing subscriptions).

11.15 Apps may only use auto renewing subscriptions for periodicals (newspapers, magazines), business Apps (enterprise, productivity, professional creative, cloud storage) and media Apps (video, audio, voice), or the App will be rejected

Auto renewable subscriptions flow

Basically it works as all AppStore in-app purchases - iOS makes purchase on client side, receives Receipt and gives it server for validation. An issue appears when you need to renew subscription - unfortunately there is no something like IPN in PayPal and AppStore does not "push inform" us about prolongation or cancellation of subscription (as PayPal do). We need to do it ourselves.

As for auto-renew subscriptions we should make such steps:

  • Take initial receipt, validate it on server side (what should be validated except Apple response - is a topic for another big post)
  • Store initial receipt in DB (assign to user internally and save renew date)
  • CRON job should make periodic check of records by renew date (frequency depends on your logic, but once in 1-6 hours could be fine)
  • If it is time to check some subscription - take previously saved Receipt and make a validate request to Apple server as usually
  • If subscription was continued and money booked from user - you will receive response with new Receipt (and old one also), if not renewed - you will receive "Expired" response.
  • If subscription was continued and success response received - save it to DB, and use in next subscription validation round
  • If not - process your app logic on expired subscription (close some services for this user or something)

Implementation

I suggest to use some verification library like AppStoreClient It allows to make everything clear as

const PASSWORD = 'secredsharedpassword',
        SANDBOX = true,
        RECEIPT = 'MySECReT5u8sCR1Pti0NRece1Pt=';

$AppStore = new \AppStore\Client\AppStoreClient();
$AppStore->setPassword(PASSWORD)
    ->setSandbox(SANDBOX);
var_dump($AppStore->verifyReceipt(RECEIPT));

Remember that Subscription validation require a Password filed (products in-app purchases does not require password to verify) - it should be provided to you by iOS app developer (it is set in iOS dev profile).

After calling Apple verification server you'll receive json with receipt and latest_receipt field.
latest_receipt is what you need to process (check) and save to DB - if there will be code = 21006 then subscription is exported, and code = 0 is ok.

Sandbox subscription duration - less then you thought

you could miss it easily and became surprised (like I did :) ), but in official documentation there is a note about reduced time of subscription in sandbox.
It is made for you to be able to test renew of subscriptions.
Time table looks like this

Actual duration Sandbox duration
1 week 3 minutes
1 month 5 minutes
2 months 10 minutes
3 months 15 minutes
6 months 30 minutes
1 year 1 hour

So if you are testing 1 months subscription - on sandbox you will get "Expired" response in 5 minutes, and could get successful response only within these 5 minutes.

Create new test sandbox account for "clean" tests

It is not a must, but I have faced strange behavior on sandbox user that already had subscription and then you try to re-subscribe it.
To have a clean test for a "new user" assume to create really a new user.

Automatic fallback from Production to Sandbox check

As you will develop and test on Sandbox server and then after submit of app it should work with Production validation url. But Apple tester test you app with SANDBOX accounts. We have had issues with app rejection because of this.

Solution is to make automatic switch in server side code to work in any case.

  • Always validate receipt with Production validation server first
  • If production validation failed, re-validate the same receit on Sandbox server then

In such way you will never mix or forgot to switch server and it do not cause any performance issues on production (as we check on production first) and on sandbox it is not bad to loose 1-2 seconds on extra check (this approach recommended in Apple docs).

Cancel AppStore subscription

You can't do it from server side. Here is manual how to do it on iOS side http://support.apple.com/kb/ht4098

comments powered by Disqus
Ievgen
Kuzminov "iJackUA"
Web Team Lead
at MobiDev (Kharkiv, Ukraine)
Code in PHP and Ruby, play with JS/Node.JS, evaluate Elixir, explore databases, use Ubuntu and MacOS, think about IT people and management

Notes