HomeCase studiesA Subscription Membership App for Local Dining...

Case study · Dubai, United Arab Emirates

A Subscription Membership App for Local Dining Discounts

Hospitality and consumer loyalty — paid membership discount schemes for local dining venues

Discount clubs have always had the same weakness: the moment the card is printed, the list of participating venues is out of date, and neither the member nor the operator can see what is actually happening. Our team rebuilt the model as a mobile membership — a native iOS application over a Laravel REST API and administrative back office, with location-aware venue discovery, an in-app membership identity code in place of a plastic card, member reviews, a referral programme, and a server-held subscription record. Delivered as a first-release build establishing the platform foundation, with an automated deployment pipeline across two environments from day one.

Industry
Hospitality and consumer loyalty — paid membership discount schemes for local dining venues
Solution
A consumer subscription product: a native iOS application, a token-authenticated REST API, and a web-based administrative back office.
Platforms
iOS · Web & API
Stack
Laravel · PHP · jQuery · Bootstrap · MySQL · Swift
Location
Dubai, United Arab Emirates · delivered remotely
Role
Engineering, with the team behind for mobile and QA

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

Project Overview

Industry: Hospitality and consumer loyalty — paid membership discount schemes for local dining venues.

Type of solution: A consumer subscription product: a native iOS application, a token-authenticated REST API, and a web-based administrative back office.

Business context: Discount club schemes have traditionally been sold as a physical card and a printed directory of participating venues. That model is structurally limited. The venue list ages immediately. The member has no way to find which participating venues are near them at the moment they are deciding where to eat. And the operator, having taken a recurring payment, has almost no visibility of who holds a live membership or how the scheme is being used. Rebuilding the scheme as a mobile membership addresses all three at once.

General users: Consumer members using the mobile application, and platform administrators managing venues, members and content through the back office.

General purpose: To replace a printed card and directory with a live, location-aware membership that members carry on their phone — and to give the operator a single administrable record of venues, members, membership status and content.

Scope note: This engagement delivered the first release of the platform — the account, membership, discovery, review and administration foundation, together with the deployment pipeline that subsequent releases build on. It is presented here at that scope.

The Business Challenge

A printed directory is stale on the day it ships. Venues join and leave a scheme continuously. Any medium that cannot be updated after distribution guarantees that members will occasionally arrive somewhere that is no longer participating — which is the fastest way to lose a subscriber.

Members decide where to eat by location, not by list. The question a member actually asks is "what is near me right now?". A directory sorted alphabetically cannot answer it. Proximity has to be a first-class property of the data, computed and ordered by the platform.

A plastic card proves nothing useful. It shows the member has a card. It does not show whether the membership is current, and it gives the operator nothing to work with. Membership status needs to be a server-held record the application reads, not a physical object.

Legal and policy content changes faster than app releases. Terms, privacy policy and scheme rules change on the operator's schedule, not the App Store's. Baking that text into the binary means a release cycle every time a clause moves.

Growth in this category is word-of-mouth. Members recommend a discount scheme to people they eat with. If the platform cannot attribute a recommendation, the operator cannot reward it — and loses the cheapest acquisition channel available.

Venue content is heavy. Every venue carries multiple photographs, and members browse them on mobile connections. Naive image handling makes the product feel slow at exactly the moment it needs to feel effortless.

Two environments, from the first week. A product with a paying membership needs a staging environment that genuinely mirrors production, and a way to promote changes to both without manual server work.

Our Approach

Make proximity a property of the query, not the client. Rather than shipping the full venue list to the device and sorting it there, the API computes great-circle distance from the member's supplied position directly in the query and returns it as a field on every venue. The client receives data that is already ordered by relevance, and the same endpoint serves both the list view and the map without a second call.

Hold membership on the server, present it on the device. Membership is a record with an activation date, a status and a cancellation date, owned by the API. The application asks the platform what the member's status is rather than deciding locally, and renewal dates are derived server-side from the activation date so the client never computes entitlement.

Replace the card with a code the member can show and share. Each member holds a personal code, rendered in-app as a scannable image and shareable through the standard system share sheet — which is what turns the identity code into the referral mechanism as well.

Model the referral graph on the member record itself. Every member carries both their own code and the code of whoever introduced them. Attribution then becomes a single self-join rather than a separate subsystem — proportionate engineering for a first release, and correct without being elaborate.

Move policy content out of the binary. Terms, privacy policy and scheme rules are managed as content records in the back office and rendered inside the application through a web view. The operator edits them and they are live, with no release cycle involved.

Make configuration a runtime concern. Mail delivery, storage and messaging configuration live in a database-backed settings store with a cached accessor and a back-office editor, so operational changes do not require a code change or a deployment.

Design the image path around the layout, not the other way round. The photo endpoint returns intrinsic dimensions alongside every image URL, so the client's gallery layout can be computed before any image has been downloaded — no reflow, no jumping, no waiting for the first byte to know where things go.

Automate deployment before it is inconvenient not to. Two Capistrano stages driven from CI, with release directories, retained previous releases for rollback, and environment configuration and key material provisioned as shared links rather than baked into a release artefact.

The Solution

Member accounts. Registration and login issuing OAuth2 access tokens, with device and push identifiers captured at registration and refreshed on every sign-in.

Account management. Profile editing with photo upload, a confirmed email-change flow requiring the current address, password change requiring the current password, and tokenised password reset delivered by email and completed through a web form.

Location-aware venue discovery. A venue directory returning distance from the member's position as a sortable field, with free-text search across venue name and description.

Interactive map. Venues rendered as custom map annotations with remotely loaded imagery, over the device's native mapping and location services, with a tap-through to the venue.

Venue profiles. Description, address, contact details, the offers available at that venue, member reviews, and a photo gallery with a full-screen browser.

Member reviews and ratings. Members submit a titled review with a star rating; submissions are upserted so a member holds a single, editable opinion per venue rather than accumulating duplicates.

Membership lifecycle. Activate, retrieve and cancel a membership, with cancellation recording its own date and the renewal date derived server-side from activation.

Membership identity code. A personal member code rendered as a scannable in-app image, shareable through the system share sheet.

Referral programme. Referral codes captured at registration — typed or scanned with the device camera — with a member-facing list of everyone they have introduced.

Editable policy content. Terms, privacy policy and scheme content managed as records in the back office and rendered in-app, updatable without an application release.

Administrative back office. Management of venues, members and administrators, content pages, and a runtime settings area covering mail, storage and messaging configuration, with log inspection available to operators.

Progressive web delivery. A web manifest and service worker supporting installable, offline-capable delivery of the web tier.

Key Features

Distance-ranked venue discovery Great-circle distance from the member's position is computed by the API and returned on every venue, so results arrive already ordered by relevance and one endpoint serves both list and map.

Native map with custom venue annotations Venues plotted on the device's native map with custom annotation views and asynchronously loaded imagery, with direct navigation into the venue profile.

Server-held membership status Membership is a platform record with activation, status and cancellation dates, and a server-derived renewal date — the application reads entitlement rather than deciding it.

In-app membership identity code A personal, scannable member code replacing the physical card, shareable directly from the app.

Referral attribution built into the member record Every member carries both their own code and their introducer's, making the referral graph a single self-join and attribution a query rather than a subsystem.

Camera-based code capture at registration New members scan an introducer's code with the device camera instead of transcribing it, using native camera and metadata capture.

Single-opinion member reviews Titled reviews with star ratings, upserted per member per venue so opinions are editable rather than duplicated.

Layout-aware photo galleries The API returns intrinsic image dimensions with every photo URL so the client's gallery layout resolves before the first image loads, with a full-screen browser for detail.

Content updatable without a release Policy and scheme content managed in the back office and rendered in-app, decoupling legal changes from the app release cycle.

Runtime configuration store Mail, storage and messaging settings held in a cached, database-backed store and edited from the back office, so operational changes need no deployment.

Technical Architecture

Mobile client. A native iOS application built with UIKit over storyboards and XIBs, structured as a side-menu shell wrapping an animated tab bar. Screens are organised into account, discovery, membership and profile modules over a shared base view controller. Device capabilities include native mapping and location services, camera-based code scanning through the system capture stack, and camera and photo-library image selection. Presentation uses a custom waterfall collection layout for galleries, a full-screen photo browser, carousels and slideshows for venue imagery, star-rating components for reviews, and vector animation for membership states.

API layer. A token-authenticated REST API with a dedicated OAuth2 guard, controllers partitioned into consumer, administrative and authentication trees over a shared base controller that provides a uniform success, error and validation-failure envelope so the client has one response shape to handle.

Administrative tier. A server-rendered back office in the same application, session-authenticated and route-group isolated from the API, covering venue, member, administrator, content and settings management, with log inspection for operators.

Data layer. A relational database with member, venue, offer, review, membership, content and settings tables, evolved through versioned migrations. Reference data that is read constantly and written rarely is served through a model-level cache.

Configuration layer. A key/value settings store with a cached accessor, letting mail, storage and messaging configuration be changed at runtime from the back office rather than through code or environment changes.

Storage layer. A filesystem abstraction over image and document handling, so media can be relocated from the application server to object storage without changes to application code.

Delivery pipeline. Capistrano stages for staging and production driven from continuous integration, performing release-directory deployment, database migration, cache clearing, dependency installation and application-server reload, with previous releases retained for rollback and environment configuration and key material provisioned as shared links outside the release artefact.

Flow: Native iOS app → OAuth2 token-authenticated REST API → Laravel application tier → relational database with cached reference reads → server-rendered administrative back office → CI-driven Capistrano deployment to staging and production

Technology Stack

Category Technology
Backend language PHP
Backend framework Laravel
Database MySQL with Eloquent; Doctrine DBAL for schema evolution
API authentication OAuth2 access tokens (Laravel Passport)
Query caching Model-level caching layer
Admin front-end Laravel UI, Bootstrap, jQuery, Sass, compiled through Laravel Mix / webpack
Admin forms & tables Laravel Collective HTML, DataTables
Web delivery Progressive Web App manifest and service worker
Cross-origin handling Dedicated CORS middleware
HTTP client Guzzle
Operations tooling In-application log viewer
Deployment Capistrano with a Laravel deployment plugin, two stages
Continuous integration CircleCI pipeline driving deploy, migrate, cache clear and dependency steps
Code style Automated PHP and asset style enforcement
Mobile platform Native iOS — Swift, UIKit, storyboards and XIBs
Mobile dependency management CocoaPods
Mobile networking Alamofire with multipart upload support
Mobile imaging SDWebImage and AlamofireImage for cached remote loading
Mobile mapping & location MapKit and CoreLocation
Mobile camera & scanning AVFoundation metadata capture; camera and photo-library picker
Mobile presentation Custom waterfall collection layout, full-screen photo browser, carousel and slideshow components, animated tab bar, vector animation
Mobile ratings UI Star-rating components
Mobile utilities Keyboard management, progress indicators, network reachability

Technical Challenges & Solutions

Challenge Our Approach
Members search by proximity, but the answer has to arrive already ordered Great-circle distance is computed inside the query from the member's supplied position and returned as a field on every venue, so the API returns a relevance-ordered result and one endpoint serves both the list and the map rather than the client fetching everything and sorting it.
Membership status must be authoritative, not assumed by the device Membership is a server-owned record with activation, status and cancellation dates, and the renewal date is derived server-side from activation. The client asks the platform what the member is entitled to instead of computing it locally, so entitlement logic exists in exactly one place.
Attributing referrals without building a separate subsystem Each member record carries both its own code and the code that introduced it, so the referral graph is a self-join on a single table. Attribution becomes a query rather than a service — proportionate to a first release and correct without over-building.
Legal and scheme content changing faster than app releases Content is managed as records in the back office and rendered inside the application through a web view, decoupling policy changes from the store release cycle entirely.
Photo-heavy venue profiles feeling slow on mobile connections The API returns intrinsic dimensions alongside every image URL so the gallery layout resolves before the first image downloads, combined with cached asynchronous image loading on the client — the layout never reflows and the screen never waits to know its own shape.
Operational configuration requiring a code change Mail, storage and messaging settings live in a database-backed store with a cached accessor and a back-office editor, so operators change configuration at runtime without a deployment.
Serving a mobile API and a browser back office from one codebase Route-group and controller-tree partitioning with separate authentication guards — token-based for the API, session-based for the back office — over shared models, keeping one deployable artefact without blurring the two audiences.
Giving the client a single response shape to handle A shared API base controller emitting a uniform envelope for success, error and validation failure, so the mobile networking layer has one contract to decode rather than per-endpoint special cases.
Promoting changes to two environments without manual server work Capistrano stages driven from CI performing release-directory deployment, migration, cache clearing and application-server reload, with previous releases retained for rollback and environment configuration provisioned as shared links outside the release.

Security & Reliability

Token-based authentication. The mobile API authenticates through OAuth2 access tokens issued at registration and login, with explicit token revocation on sign-out.

Separated authentication domains. The token-authenticated mobile API and the session-authenticated administrative back office are separate route groups with separate guards, so a credential valid for one surface is not automatically valid for the other.

Verified credential changes. Password changes require the current password, and email changes require confirmation of the current address alongside a matched new address — neither can be performed from a session alone.

Single-use password reset. Password reset issues a token delivered by email and consumed on use, with the reset record removed once the password has been changed.

Server-side validation on every write. Every endpoint that accepts input validates it before persistence and returns validation failures through the same uniform response envelope as every other outcome.

Configuration outside the release artefact. Environment configuration and cryptographic key material are provisioned at deploy time as shared linked files rather than being included in a release, so promoting a build never moves secrets with it.

Rollback-capable deployment. Release-directory deployment retains previous releases, so a problematic change can be reverted without a rebuild.

Recoverable content. Content records use soft deletion, so removals from the back office are reversible rather than destructive.

Operational visibility. Log inspection is available to operators from within the back office, so production behaviour can be examined without server access.

Scalability & Performance

Cached reference reads. Settings and member reference data are read on nearly every request and change rarely, making them the natural candidates for a model-level cache and removing a large share of repeated queries.

Stateless application tier. Token authentication and externalised configuration keep application instances free of local state, so the API tier scales horizontally without session affinity.

Relevance computed once, at the source. Distance ordering happens in the database rather than on the device, so the client transfers and renders a bounded, already-ordered result instead of a full directory.

Client-side image caching. Venue imagery is loaded asynchronously through a caching image layer with in-memory and on-disk retention, which matters in a product where every venue carries a gallery and members browse repeatedly.

Layout resolved before download. Server-supplied image dimensions let the client compute its gallery geometry before any image arrives, eliminating layout passes triggered by image decoding.

Storage abstraction. Media handling goes through a filesystem abstraction, so imagery can be relocated to object storage as volume grows without changes to application code.

Runtime configuration. Because operational settings are read from a cached store rather than compiled in, tuning production behaviour does not require a deployment cycle.

Automated, repeatable releases. CI-driven deployment across two environments keeps promotion consistent and low-effort, which is what makes frequent small releases practical rather than risky.

Business Outcomes

  • Members can find participating venues near them, ordered by proximity, at the moment they are deciding where to eat — the question a printed directory could never answer.
  • Membership became a live server-held record with activation, status and cancellation dates, rather than a physical object that proves nothing about whether the subscription is current.
  • The venue directory is updatable in real time, so members are working from what participates today rather than what participated at print time.
  • Recommendations are attributable, because every member record carries the code of whoever introduced them — turning word-of-mouth into something the operator can see and act on.
  • Members carry their identity code in the app, and can share it directly, which makes the same artefact serve both entry and referral.
  • Member opinion is captured in the platform, with a single editable review per member per venue rather than accumulated duplicates.
  • Policy and scheme content changes without an app release, removing a store cycle from what should be an editorial decision.
  • Operators administer the platform without developers, covering venues, members, content and runtime configuration from the back office.
  • Changes reach staging and production through one automated pipeline, with rollback available — established at the start of the engagement rather than retrofitted.

Why it worked

First releases are their own discipline. The temptation is to build the whole imagined product; the correct move is to build the parts that everything else will stand on, build them properly, and leave the rest deliberately open. A consumer membership product lives or dies on three things — whether the member can find something useful near them, whether the platform knows who they are, and whether the operator can change the content without calling a developer. Those are the things this release was built to get right.

Our team works that way on purpose. We put proximity in the query rather than the client, because relevance is the product's core value and it belongs where the data is. We made membership a server-owned record so entitlement has exactly one home. We modelled referral attribution on the member record itself rather than inventing a subsystem for it, because proportionate engineering at MVP stage is a judgement call that pays for itself later. We moved policy content and operational configuration out of the binary and out of the code, because the operator should not need a release to change a sentence. And we automated deployment to two environments in the first week, because the alternative — manual promotion — is a habit that becomes expensive to break.

Our teams work across Laravel and modern PHP, REST API and OAuth2 design, native iOS in Swift and UIKit, location-aware and camera-driven mobile features, and CI-driven deployment automation — with the product judgement to know which parts of a first release must be built to last, and which are better left simple until the business has learned more.

Final Summary

A discount club had been sold the way discount clubs have always been sold: a card and a printed list of participating venues. Members could not tell which of those venues were near them, could not tell whether a venue still participated, and could not do anything with the card except carry it. The operator, having taken a recurring payment, had almost no visibility of the scheme they were running.

Our team built the first release of a platform that changes the shape of that. A native iOS application over a token-authenticated Laravel API gives members a directory ordered by how close each venue actually is, plotted on a native map, with photo galleries that resolve their layout before the images arrive and reviews that capture a single editable opinion per venue. Membership became a server-held record with its own lifecycle, and the plastic card became an in-app identity code the member can show or share — which is also what makes referrals attributable, since every member record carries the code of whoever introduced them.

Behind it sits the part that determines whether a product like this can be run rather than merely launched: an administrative back office covering venues, members and content; policy text that changes without an app release; operational configuration held in a cached runtime store rather than in code; and a CI-driven deployment pipeline reaching staging and production with rollback, in place from the beginning. It is a foundation built to be extended — which, for a first release, is the only thing worth optimising for.

01 — Questions

asked about this kind of project

How do you build a subscription membership app?

Start with the three things everything else depends on: an authoritative server-held record of who is a member and whether they are current, a way for the member to prove identity in the app, and a way for the operator to change what members see without a developer. Get those right and the rest — discovery, content, engagement features — can be layered on. Get them wrong and every later feature inherits the problem.

Should membership status be checked on the device or the server?

On the server, without exception. The device can cache what it was told, but it must never be the thing that decides. Entitlement logic that exists in two places will diverge, and the copy running on hardware you do not control is the one that will be wrong. One authoritative source, read by the client, is the only arrangement that stays correct as the product grows.

How do you implement location-based discovery in a mobile app?

Compute relevance where the data lives. Distance from the user's position should be calculated in the query and returned as a field on each result, so the API hands back an already-ordered list and the same endpoint serves both the list view and the map. Sending the full catalogue to the device to sort locally works in a demo and fails as soon as the catalogue grows. At larger volumes, move to spatial column types and spatial indexing rather than computing trigonometry per row.

What is the right way to add a referral programme?

Usually the simplest one that is actually correct. If each member record carries their own code and the code of whoever introduced them, the referral graph is a self-join and attribution is a query — no separate service, no extra tables, no synchronisation. Build the elaborate version when the business has learned enough to know which elaborate version it needs.

How do you keep legal and policy content updatable without app releases?

Manage it as content in the back office and render it in the app rather than compiling it into the binary. Terms and privacy text change on the business's schedule, and tying those changes to a store review cycle means either slow compliance or rushed releases. Content that changes for non-engineering reasons should never require an engineering event.

Should image galleries load before or after layout?

Layout should never wait for images. If the API returns intrinsic dimensions alongside each image URL, the client can compute its geometry immediately and fill it in as images arrive — no reflow, no content jumping under the user's thumb. It is a small contract change on the server that removes an entire class of perceived-performance problem on the client.

How much should be automated in a first release?

Deployment, always. Everything else is negotiable. Manual promotion to staging and production is tolerable for about a month and then becomes a habit that is genuinely expensive to break, because by then it is load-bearing. Release-directory deployment with retained previous releases and CI-driven promotion costs very little at the start of a project and a great deal to retrofit.

What belongs in an MVP and what should wait?

The data model, the authentication and entitlement foundation, and the operator's ability to run the product without developers. Those are the things that are painful to change later. Feature breadth is not — it can be added continuously once the foundation is right. The most common MVP mistake is inverting that: shipping a wide feature surface on a foundation that has to be rebuilt to support the second release.

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.