HomeCase studiesA Subscription Mobile Game Platform with Full...

Case study · Abu Dhabi, United Arab Emirates

A Subscription Mobile Game Platform with Full Store Lifecycle Management

Consumer mobile gaming and sports performance tracking

Subscription apps fail quietly, in billing. Renewals, cancellations, refunds, grace periods and billing failures all happen at the app stores while the app is closed, and an app that cannot reconcile that state either gives away access it is not paid for or cuts off customers who are. Our team built a subscription-based mobile sports game with a complete store lifecycle implementation across both platforms — server-side receipt verification, real-time notification handling from Apple and Google, queued verification and a scheduled reconciliation sweep — alongside a configurable game engine whose difficulty and scoring rules are tuned as data rather than code.

Industry
Consumer mobile gaming and sports performance tracking
Solution
A cross-platform mobile application over a REST API and administrative platform, monetised through recurring in-app subscriptions on both major app stores.
Platforms
Cross-platform · Web & API
Stack
Laravel · PHP · React Native · React · MySQL · Firebase
Location
Abu Dhabi, United Arab Emirates · delivered remotely
Role
Engineering, with the team behind for mobile and QA

Delivered remotely for a business based in Abu Dhabi, United Arab Emirates. Names withheld by agreement.

Project Overview

Industry: Consumer mobile gaming and sports performance tracking.

Type of solution: A cross-platform mobile application over a REST API and administrative platform, monetised through recurring in-app subscriptions on both major app stores.

Business context: Subscription is now the default model for consumer mobile products, and it moves the hardest part of the business outside the application. The stores own the billing relationship. They renew, they retry failed payments, they issue refunds, they apply grace periods, they process cancellations that take effect weeks later — and they do all of it whether or not the user has opened the app since. An application that treats entitlement as something the client tells it will be wrong regularly, in both directions, and every instance of being wrong is either lost revenue or a support ticket from someone who has paid.

General users: Players, who configure and play rounds against virtual opponents and track their scoring; and administrators, who tune game rules, manage virtual opponents, oversee subscriptions and manage content.

General purpose: To deliver a satisfying, tunable sports game experience on mobile, funded by a subscription model that reconciles accurately with both app stores.

The Business Challenge

Entitlement lives outside the product. The application cannot know from its own data whether a user is currently entitled to access. That truth sits with Apple and Google, changes without any user action, and must be actively reconciled.

Two stores, two completely different mechanisms. Apple sends server-to-server notifications to a webhook. Google publishes to a cloud messaging topic requiring a consumer to pull from it. The data shapes, event vocabularies and delivery semantics differ. Both have to end up as one internal subscription state.

Client-reported purchases cannot be trusted. Anything the app claims about a purchase must be validated server-side against the issuing platform, or the entitlement model is only as strong as the device it runs on.

Notifications get missed. Webhooks fail, consumers fall behind, messages arrive out of order. An event-driven design alone will drift out of sync, and the drift will be invisible until a customer complains.

Expiry is a timezone problem. Comparing an expiry timestamp in the wrong timezone produces access decisions that are wrong by hours — always at the boundary, always noticed.

The game had to be tunable. Difficulty, distances, scoring and putting outcomes needed adjusting repeatedly after real players arrived. Every adjustment requiring a code change and store review would have made balancing the game impossible in practice.

Opponents needed to be credible. A single-player game needs opposition that is competitive but beatable, and that varies by game format — otherwise it is either discouraging or trivial.

Registration had to be low-friction but verified. Both email and phone sign-in with one-time codes, without letting SMS delivery become a single point of failure at the front door of the product.

Our Approach

Build subscription reconciliation as a subsystem, not a feature. We designed the subscription layer as its own architectural component with multiple independent entry points converging on one state model: purchase verification at the moment of sale, platform notification handling from both stores, queued asynchronous verification, and a scheduled sweep that reconciles regardless of what arrived by other routes. Redundancy here is deliberate — each path covers the others' failure modes.

Verify server-side, always. Receipts are validated against Apple and Google directly, through dedicated verification services per platform. The application's entitlement decision never depends on what the client reports.

Normalise two platforms into one model. Apple's webhook events and Google's published notifications are translated into a single internal subscription representation carrying platform, platform subscription identifier, original transaction identifier, renewal state, status and expiry — so the rest of the application reasons about subscriptions without knowing which store they came from.

Keep the evidence. Every transaction retains the full raw platform payload alongside typed fields. When a billing question arises months later, the original response is available rather than reconstructed.

Store expiry with its timezone. A small decision that eliminates an entire category of access bugs that are invisible in testing.

Make the game rules data. Difficulty tiers, distances, pars, terrain, break and putting outcome tables are all administratively managed reference data. Game balance became something the operator tunes based on how real players perform, in minutes, rather than a development and store review cycle.

Model opponents as data too. Virtual opponents carry their own scoring profiles, applicable game formats, status and presentation, so new opposition can be introduced without a release.

Build redundancy into the front door. Two SMS providers back the one-time code path, because a delivery failure at registration is a permanently lost user rather than an inconvenience.

The Solution

Flexible game configuration. Players set up a round by choosing the number of players and rounds, difficulty, course context and format, with the configuration determining how scoring and opponents behave.

Virtual opponents. Configurable computer opponents with their own scoring profiles across the front nine, back nine and total, applicable to specific game formats, presented with their own identity — providing structured competition in a single-player experience.

Detailed shot and putt modelling. Scoring accounts for the factors that decide real outcomes — distance, terrain, break and difficulty — combined through administratively maintained reference data so results feel plausible and can be tuned.

Hole-by-hole scoring and scorecards. Play is recorded at hole level, producing scorecards per round with result determination per game and history preserved rather than overwritten.

Multiple game formats. Game type variants apply across scoring, pars and opponents, so the same underlying engine supports several ways to play.

Subscription access. Access is governed by recurring subscription purchased through the app stores, with plans retrieved from the platforms rather than hard-coded, so store-side changes do not require an app release.

Accurate entitlement. Subscription state is reconciled continuously through platform notifications, verification at purchase, asynchronous re-verification and a scheduled sweep — so what the app grants matches what the store says.

Dual sign-in with one-time codes. Registration and login by email or phone number with international number handling and one-time code verification, backed by two independent SMS providers.

Profile management. Profile creation and editing with image upload and cropping.

Administrative tuning platform. Administrators manage every reference data set — difficulty, distances, pars, putting terrain, break and result tables — plus virtual opponents, plans, users, content and dashboards. Game balance is operational configuration rather than engineering work.

Key Features

Multi-path subscription reconciliation Purchase verification, Apple server notifications, Google real-time notifications, queued re-verification and a scheduled sweep all converge on one subscription state, so no single failed delivery causes entitlement drift.

Server-side receipt verification for both stores Dedicated verification services validate purchases directly with Apple and Google, so entitlement never depends on what the client claims.

Full platform payload retention Every transaction stores the raw platform response alongside typed fields including original transaction identifier and notification type, preserving evidence for later billing questions.

Timezone-correct expiry handling Expiry stored with its timezone, eliminating boundary access errors that testing rarely catches.

Store-fetched subscription plans Plans are retrieved from the platforms rather than hard-coded, so pricing and product changes made store-side take effect without an app release.

Data-driven game balance Difficulty, distances, pars, terrain, break and outcome tables are administratively managed, making game tuning a configuration task rather than a development cycle.

Configurable virtual opponents Opponents carry their own scoring profiles, applicable formats and presentation, so new competition can be added as data.

Hole-level scoring with preserved history Scoring recorded per hole with soft deletion, producing scorecards and result determination while retaining play history.

Dual authentication with redundant delivery Email and phone sign-in with one-time codes and international phone handling, backed by two independent SMS providers so registration does not depend on a single vendor.

Multiple game formats on one engine Game type variants applied across scoring, pars and opponents, supporting several ways to play without duplicating the underlying model.

Technical Architecture

Mobile client. A cross-platform application with a predictable state container, screen-per-flow navigation, environment-based configuration, and dedicated components for one-time code entry, international phone input, media capture with cropping and in-app messaging.

API layer. A token-authenticated REST API with controllers separated between player-facing operations and administrative reference data management, over shared base classes.

Subscription subsystem. The architectural centrepiece, composed of platform-specific verification services, a plan fetcher, a cloud messaging consumer, a webhook endpoint, a queued verification job and a scheduled reconciliation command — multiple independent entry points resolving to one subscription state model.

Game engine layer. Scoring and outcome resolution driven by reference data tables rather than hard-coded constants, with virtual opponent behaviour likewise held as data.

Data layer. A relational database holding users, subscriptions and transactions alongside game configuration, reference data, scores and scorecards, with soft deletion preserving play history.

Asynchronous processing. Queued jobs for verification work and scheduled commands for reconciliation and notification consumption, keeping this work off the request path.

External services. Both app store platforms for purchase verification and notifications, a cloud messaging service as the notification transport for one platform, cloud object storage for media, and two SMS providers for one-time codes.

Flow: Mobile app → Token-authenticated API → Game engine over reference data → Relational database → Subscription subsystem ⇄ App store verification & notifications (webhook + cloud messaging) → Queued verification + scheduled reconciliation → Entitlement state

Technology Stack

Category Technology
Backend language PHP 8.1+
Backend framework Laravel 10
Database MySQL with Doctrine DBAL
API authentication Laravel Passport (OAuth2), Sanctum; Breeze for the web auth surface
Tokens Firebase PHP-JWT
Store integration Apple receipt verification and server notifications; Google Play verification and real-time developer notifications
Notification transport Google Cloud Pub/Sub with a pull consumer
Object storage Google Cloud Storage
SMS / OTP Twilio and Vonage (redundant providers)
Asynchronous processing Laravel queued jobs and scheduled console commands
Diagnostics Log viewer
Mobile framework React Native with React 19
Mobile state Redux Toolkit, React Redux
Mobile navigation React Navigation (native stack)
Mobile networking Axios
Mobile components OTP entry, international phone input with country picker, image crop picker, date/time pickers, select components, blur, flash messaging, SVG
Configuration Environment-based configuration per build
Testing & quality PHPUnit, Faker, Mockery, Laravel Pint

Technical Challenges & Solutions

Challenge Our Approach
Subscription state living at the app stores, changing while the app is closed A subscription subsystem with five independent entry points — purchase verification, Apple webhook notifications, Google notification consumption, a queued verification job and a scheduled sweep — all converging on one state model, so no single failure path causes entitlement drift.
Two stores with entirely different notification mechanisms and vocabularies Platform-specific verification and notification handling normalised into a single internal subscription representation carrying platform, platform identifiers, renewal state, status and expiry, so application logic is platform-agnostic.
Client-reported purchase state being untrustworthy Server-side receipt validation against each platform through dedicated verification services; the entitlement decision is never derived from what the device claims.
Notifications that can be missed, delayed or arrive out of order A scheduled reconciliation sweep operating independently of event delivery, so subscription state is corrected even when a notification never arrives.
Access decisions wrong by hours at expiry boundaries Expiry persisted with its timezone rather than as a naive timestamp, eliminating a bug class that testing rarely surfaces but users always notice.
Billing disputes arising long after the transaction Full raw platform payload retained on every transaction alongside typed fields including original transaction identifiers, so the original evidence is available months later.
Game balance needing frequent adjustment after real players arrived Difficulty, distance, par, terrain, break and outcome tables held as administratively managed reference data, making balance changes a configuration task rather than a development and store review cycle.
Store-side pricing and product changes forcing app releases Subscription plans fetched from the platforms rather than hard-coded, so store-side changes take effect without shipping a build.
SMS delivery failure blocking registration entirely Two independent SMS providers configured on the one-time code path, so a single vendor outage does not close the product's front door.

Security & Reliability

Authentication. Token-based OAuth2 authentication with both email and phone sign-in paths, each verified — email through a verification flow and phone through one-time codes.

Entitlement integrity. Purchases are validated server-side against the issuing platform. The application never grants access on the basis of client-reported purchase state.

Platform notification handling. Server-to-server notifications from both stores are consumed and applied to subscription state, keeping entitlement current without depending on user activity.

Reconciliation redundancy. A scheduled sweep operates independently of notification delivery, so missed or delayed events are corrected rather than persisting as silent drift.

Evidence retention. Raw platform payloads and platform transaction identifiers are stored, providing a verifiable record for billing investigation and dispute resolution.

Surface separation. Player-facing and administrative controllers are kept separate, so reference data and user management are not reachable from the player API.

Non-destructive history. Soft deletion on scoring records preserves play history rather than discarding it.

Delivery redundancy. Two SMS providers on the verification path reduce dependence on a single vendor at the most critical moment in the user journey.

Scalability & Performance

Asynchronous verification. Subscription verification runs as queued work rather than inside user-facing requests, so purchase flows stay responsive regardless of platform API latency.

Buffered notification transport. Platform notifications are consumed from a cloud messaging service, which absorbs delivery spikes rather than pushing them directly into the application.

Scheduled batch reconciliation. Periodic subscription checking runs as a scheduled command, keeping this work entirely off the request path.

Small, cacheable reference data. Game rules live in compact reference tables that are read frequently and change rarely — inexpensive to serve and simple to cache.

Externalised media storage. Profile imagery is held in cloud object storage rather than on application servers, keeping instances stateless.

Single mobile codebase. One cross-platform application serves both platforms, so feature delivery and store lifecycle handling are implemented once.

Business Outcomes

  • Revenue is protected in both directions — the platform does not grant access it is not paid for, and does not revoke access from customers who are paying.
  • Entitlement stays current without user activity, because platform notifications and scheduled reconciliation keep subscription state accurate whether or not the app is opened.
  • Billing questions can be answered from evidence, since raw platform responses and transaction identifiers are retained.
  • Store-side pricing changes need no app release, because plans are fetched from the platforms.
  • The game can be balanced against real player behaviour, in minutes, because rules are configuration rather than code.
  • New opponents and formats ship without a release, since both are data-driven.
  • Registration is resilient, with two independent SMS providers on the one-time code path.
  • One codebase serves both platforms, keeping a subscription consumer product economically viable.

Why it worked

Most subscription apps implement the happy path: the user buys, the app checks a receipt once, and access is granted. That works until the first renewal, the first refund, the first failed payment, the first cancellation that takes effect in three weeks. Then the product starts being wrong about who is entitled — sometimes losing money, sometimes losing customers — and nobody notices until the support queue fills up.

Our team builds the part that most teams discover too late. We treat subscription reconciliation as a subsystem with redundant entry points, because platform notifications fail and event-driven designs drift. We verify server-side, because client-reported entitlement is not entitlement. We retain the raw platform evidence, because billing disputes arrive long after the transaction. And we handle the unglamorous details — timezone-correct expiry, platform-specific notification vocabularies, original transaction identifiers — that separate a subscription implementation that reconciles from one that approximately reconciles.

The same instinct shaped the product side: game rules and opponents built as configurable data rather than code, because a consumer game's balance is discovered from real player behaviour after launch, and any design that requires a store review cycle to adjust will not be adjusted often enough to matter.

Final Summary

A subscription consumer app has two products inside it. One is the experience users see. The other is the billing relationship, and it lives entirely outside the application — at the app stores, where renewals succeed, payments fail, refunds are issued, grace periods apply and cancellations take effect on a schedule nobody in the business controls. Most subscription apps implement the purchase and assume the rest, then spend years being subtly wrong about who is entitled to what.

Our team built both properly. The subscription subsystem reconciles through five independent paths — verification at purchase, server notifications from Apple, real-time notifications consumed from Google through a cloud messaging transport, queued asynchronous re-verification, and a scheduled sweep that corrects whatever the other paths missed. Receipts are validated server-side against each platform, raw payloads are retained for later dispute resolution, expiry is stored with its timezone, and plans are fetched from the stores so pricing changes need no release.

The game itself was built on the same principle of keeping decisions changeable: difficulty, distances, scoring, terrain and putting outcomes are administratively managed reference data, and virtual opponents carry their own scoring profiles as records rather than code. Balance is tuned against how real players actually perform, in minutes rather than release cycles. Delivered as one cross-platform mobile codebase over a Laravel API, it is a consumer subscription product where both the experience and the revenue behind it were engineered with equal care.

01 — Questions

asked about this kind of project

How do you implement in-app subscriptions correctly?

Verify every purchase server-side against the issuing platform, store the subscription with its platform identifiers and original transaction identifier, consume the platform's server-to-server notifications to track renewals and cancellations, and run a scheduled reconciliation sweep as a safety net for missed events. Verifying the receipt once at purchase and assuming the rest is the most common subscription implementation, and it is wrong from the first renewal onward.

Why isn't client-side purchase validation enough?

Because the client is not a trustworthy source of entitlement. The application must confirm with the platform itself that a purchase is genuine and currently active. Beyond the obvious tampering concern, client-side state simply cannot know about the events that matter most — renewals, refunds, billing failures and cancellations all happen without the app being involved.

How do Apple and Google subscription notifications differ?

Apple posts server-to-server notifications to an endpoint you provide. Google publishes real-time developer notifications to a cloud messaging topic, which your backend consumes with a subscriber. The event vocabularies and payload shapes differ substantially. Both need normalising into a single internal subscription model so the rest of the application does not carry per-platform logic in every entitlement check.

What happens if a subscription notification is missed?

Without a fallback, entitlement drifts silently — a cancelled user keeps access, or a renewed user loses it. That is why a scheduled reconciliation sweep is essential alongside event handling: it periodically re-verifies subscription state directly with the platforms regardless of what arrived by notification. Event-driven handling gives you speed; the sweep gives you correctness.

Should subscription plans be hard-coded in the app?

No. Fetch them from the platforms. Pricing, product availability and regional variations change store-side, and hard-coded plans mean an app release — and a review cycle — for every commercial adjustment. Fetching plans dynamically also avoids the mismatch where the app displays one price and the store charges another.

How do you make a mobile game tunable after launch?

Hold the rules as data rather than code. Difficulty tiers, scoring parameters, outcome tables and opponent profiles should be reference records an administrator can edit, not constants compiled into the application. Game balance is discovered from real player behaviour after launch, and any balance change that requires a store review will happen far less often than it should.

Why store subscription expiry with a timezone?

Because an expiry compared in the wrong timezone produces access decisions that are wrong by hours, exactly at the boundary where users are paying attention. It is a small implementation detail that eliminates an entire category of support tickets, and it is almost never caught in testing because test subscriptions rarely expire at an inconvenient moment.

How long does it take to build a subscription mobile app?

The application itself follows normal estimation. The subscription layer is what people underestimate — a correct implementation covering both platforms, server-side verification, notification handling, reconciliation and dispute evidence is a substantial piece of work in its own right, and it should be scoped explicitly rather than treated as part of "add payments". It is also the part that most directly determines revenue.

03 — Similar project?

describe what is different about yours

Build yours.

Tell me what exists, what you need and the deadline. Fixed price for a defined scope, or hourly from $10 with a written estimate first.

Ahmedabad, India · IST (UTC+5:30) · --:-- IST · Mon–Fri 09:00–18:00 IST · US & EU overlap daily

No newsletter, no CRM. Just a reply.