Over the past few months, I’ve been doing a deep delve into auto-renewable subscriptions for one of our client projects. It’s been a roller coaster ride as we took on an established product with nearly 10,000 (mostly monthly) subscribers. You really don’t want to be responsible for that support call informing you that customers are unable to access the product they have paid for.
Unfortunately, in the past month, we had been noticing more and more support cases reporting customers being locked out of their paid content. Our initial investigation into what could have changed to trigger the problem just confirmed that we hadn’t made any changes in that area, but the number of issues kept on increasing. Over some time, we increased the logging in the relevant areas and rolled out a few investigative fixes to see if we could pin down potential causes.
While we seemed to be pinning down that area in which the problem was hiding, the number of support cases continued to increase. Bizarrely, reaching a critical mass in support requests allowed us to finally pin down the exact area that was causing the problem. The app was calling back to the server with the Apple receipt to verify their subscription was still active, and it was this call that was (incorrectly) reporting that the subscription had expired and was locking the user our of their content. This code hadn’t changed since the inception of the application. It had always worked and continued to work for the majority of users. However, more and more customers were being affected).
Adding a little more logging in this area identified that the order of transactions in the verified receipt was not consistent. The majority of the receipts had the transactions ordered oldest to newest. Still, more and more receipts were being seen with transactions in the opposite order, and some even had what appeared to be a completely random order. As the API code was written to assume the last item was the most recent transaction, in certain situations, it was getting a historic transaction, which was why the check was then failing.
Light Bulb Moment
Digging around the internet, I came across an article which contained this gem:
With regards to the ordering of the items in the in_app array, I would make no assumption that the items are in chronological order. I would say the same holds true for the contents of the latest_receipt_info section.https://developer.apple.com/forums/thread/69531
Luckily it was a quick and easy change to the API to read the array, and order on the transaction date, programmatically ensuring that the system was working with the most recent transaction. It was really satisfying to then deploying this change to live and watch the issue instantly disappear for the affected users.
Just recently I’ve been to a few talks about Kotlin and been reading up about Android Architectural Components. I’ve also had an idea for a really basic app playing around in my mind for a while, so it seems a perfect opportunity to kill two birds with one stone. I’ve already had a play with LiveData / ViewModel, updating the Big Nerd Ranch Quiz, which went really well. For this project, I’m also going to . . .
On iOS devices within accessibility section of general settings you can change the text size, making it (much) larger or smaller. I hadn’t come across this before, but to get your application to work with the user’s settings you use ‘Dynamic Type’. These are “UIFontTextStyle” elements which will automatically size around the user’s text size setting. The settings available and their relative sizes are: Large Title – 44pt Title 1 – 38pt Title 2 – . . .