Mobile App

The Omega Shop mobile app is an Expo (SDK 54) React Native client that talks to the Laravel REST API at /api/v1.

Overview

The app lives in the mobile/ directory of the Omega Shop repository. It uses expo-router for file-based navigation, NativeWind (Tailwind) for styling, TanStack Query for server state, and Zustand for client state.

  • Catalog — Featured products on the home tab, product search with category and sort filters, category listings, product details, ratings, and seller pages.
  • Cart & Wishlist — Server-side cart with a live badge on the Cart tab, and a wishlist with move-to-cart.
  • Checkout — Select a billing detail (and a delivery address when needed), then pay with the Stripe payment sheet.
  • Orders — Order list with status filter, order details, and invoice receipt links.
  • Account — Registration, login, password reset, profile and password updates, billing details, and delivery addresses.
  • Session Persistence — The Sanctum token is kept in the device secure store and restored on launch.

Prerequisites

  • Node.js and npm
  • A running Omega Shop backend (see Getting Started) reachable from your device or simulator
  • Xcode (iOS simulator) or Android Studio (Android emulator), or the Expo Go app on a physical device
  • A Stripe publishable key, if you want to test payments

Installation

cd mobile
npm install

Environment Variables

Copy .env.example to .env inside mobile/ and fill in the values. Variables prefixed with EXPO_PUBLIC_ are inlined into the app bundle, so never put secrets in them.

  • Name
    EXPO_PUBLIC_API_URL
    Type
    string
    Description

    Base URL of the Laravel API including the /api/v1 prefix. Read in src/api/client.ts; falls back to http://localhost:8000/api/v1 when empty.

  • Name
    EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY
    Type
    string
    Description

    Stripe publishable key passed to StripeProvider in app/_layout.tsx. Without it, the provider is not mounted.

Running the App

Against a local backend

Start the backend, then point the app at it:

# In the repository root
php artisan serve

# In mobile/
npm start

localhost only works from a simulator or emulator on the same machine. On a physical device, set EXPO_PUBLIC_API_URL to an address the device can reach (for example your computer's LAN IP, with php artisan serve --host=0.0.0.0). Restart Expo after changing .env.

app.json allows plain HTTP for development: iOS has NSAllowsLocalNetworking plus an insecure-HTTP exception for omega-shop.test (and subdomains), and Android sets usesCleartextTraffic.

Expo Go vs development build

npm start

Expo Go is enough for browsing, cart, and account features. Payments are not available there, because the Stripe native module is not bundled in Expo Go. The app detects this with Constants.appOwnership === "expo", skips loading @stripe/stripe-react-native, and checkout shows an "Unavailable" alert asking for a development build. npm run ios and npm run android run expo run:ios / expo run:android, which compile a native development build (an ios/ project is present in the repository).

Authentication

  1. On launch, AuthProvider calls loadStoredAuth, which reads the auth_token entry from expo-secure-store and, if present, fetches GET /auth/user.
  2. The Axios client in src/api/client.ts adds Authorization: Bearer <token> to every request.
  3. The (tabs) layout redirects unauthenticated users to /(auth)/login once loading finishes.
  4. Login and registration post to /auth/login and /auth/register with a device_name; the returned token is stored in the secure store and the user in the Zustand auth store.
  5. Any 401 response deletes the stored token. A failed restore on launch clears the session.
  6. Logout calls /auth/logout, ignoring API errors, and clears the local session.

The API module also defines password reset (/auth/forgot-password, /auth/reset-password) and a social-login call (/auth/social-login/{provider}), but no screen currently uses social login.

Payments

Checkout (app/checkout/index.tsx) posts the selected billing_detail_id and optional delivery_address_id to POST /checkout. The response carries a client_secret, which is used to initialise and present the Stripe payment sheet. After a successful payment the app calls GET /checkout/{purchaseId}/confirm to verify the order. Google Pay is enabled through the @stripe/stripe-react-native config plugin in app.json.

Building and Updates

app.json configures expo-updates with "runtimeVersion": "exposdk:54.0.0", so over-the-air updates are matched to builds on Expo SDK 54. The app uses bundle identifier and Android package com.omega.shop.

Testing

The mobile project has no test script and no test files; package.json only defines start, android, ios, and web. Type checking is available through TypeScript (npx tsc --noEmit).

Project Structure

mobile/
├── app/                  # expo-router routes
│   ├── _layout.tsx       # Providers (Stripe, Query, Auth) and root stack
│   ├── (auth)/           # login, register, forgot-password
│   ├── (tabs)/           # index, search, cart, purchases, profile
│   ├── product/[slug].tsx
│   ├── category/[slug].tsx
│   ├── seller/[id].tsx
│   ├── checkout/index.tsx
│   ├── purchase/[id].tsx
│   ├── wishlist.tsx
│   ├── billing-details/index.tsx
│   └── delivery-addresses/index.tsx
├── src/
│   ├── api/              # Axios client and per-resource modules
│   ├── components/       # ProductCard, EmptyState
│   ├── hooks/            # useRefresh
│   ├── providers/        # QueryProvider, AuthProvider
│   ├── store/            # Zustand: auth, cart badge
│   ├── types/
│   └── utils/            # format helpers
├── app.json              # Expo config
├── global.css            # NativeWind entry
└── ios/                  # Native iOS project

TanStack Query is configured with a 5-minute staleTime and 2 retries. The auth user and the cart badge count live in Zustand stores.

Was this page helpful?