GET /projects/loven200 OK

LOVEN

// an art marketplace app that brings local artists into one place

my role
Backend lead, and the Flutter HTTP client
team
5 engineers
period
Mar 2026 – Jul 2026
stack
Python, Flask, PostgreSQL, SQLAlchemy, Alembic, Firebase, Moyasar, Docker, Flutter
status
In development. The API runs on a Render staging environment, with payments in Moyasar test mode.
▶ watch the demo

The problem

Local artists in Saudi Arabia are scattered across Instagram, TikTok, Haraj and other platforms. That makes them hard to discover, and makes buying their work harder still. LOVEN brings them into one platform, where artists can show their work, either for sale or just to share it, and art lovers can discover and buy it in one place.

What I built

Architecture

flowchart TB
    App["Flutter app"] -->|"HTTPS + JWT"| R
    subgraph API["Flask REST API (/api/v1)"]
        R["Routes"] --> F["Facades"] --> S["Services"] --> Rep["Repositories"] --> M["Models"]
    end
    M --> PG[("PostgreSQL")]
    S --> EXT
    subgraph EXT["External services"]
        FB["Firebase: Auth, Storage, Cloud Messaging"]
        MY["Moyasar payments"]
    end

The API is split into layers, and each layer has one job. Routes handle HTTP: they check the request and shape the response. Facades hold the business logic for each feature and coordinate the services and repositories it needs. Repositories talk to the database. External services, like Moyasar and Firebase, live behind their own clients, so they can be replaced or mocked in tests without touching the rest of the code.

Journey 1: Signing in

sequenceDiagram
    participant App as Flutter app
    participant FB as Firebase Auth
    participant API as LOVEN API
    participant DB as PostgreSQL
    App->>FB: Sign up
    FB-->>App: ID token + verification email
    App->>API: POST /auth/firebase/register-sync
    Note over API: Verify ID token
    API->>DB: Create user + profile
    API-->>App: 201 Created, no session yet
    Note over App,FB: User verifies email
    App->>FB: Sign in
    FB-->>App: ID token
    App->>API: POST /auth/firebase/login
    Note over API: Verify token + email
    API->>DB: Find user, check active
    API-->>App: Access + refresh tokens

Decisions

Journey 2: Paying for an order

sequenceDiagram
    participant App as Flutter app
    participant API as LOVEN API
    participant M as Moyasar
    participant DB as PostgreSQL
    App->>API: POST /payments/orders/{id}/initiate
    API->>DB: Create pending payment
    API-->>App: Expected amount (halalah)
    App->>M: Pay with Moyasar SDK
    Note over App,M: Card details go to Moyasar only
    M-->>App: Payment ID
    App->>API: POST /payments/orders/{id}/verify
    API->>M: Fetch payment (secret key)
    M-->>API: Status, amount, currency
    Note over API: Compare with order total
    API->>DB: Mark paid, fulfill order
    API-->>App: 200 Payment verified

Decisions

Journey 3: Notifications

flowchart TB
    E["Order or payment event"] --> S["Notification service"]
    J["Scheduled job"] --> S
    S --> D{"Already sent?"}
    D -->|"Yes"| X["Skip"]
    D -->|"No"| DB[("Save in-app notification")]
    DB --> P["Push via Firebase Cloud Messaging"]

Decisions

Connecting the app: staying signed in

sequenceDiagram
    participant S as App screens
    participant C as ApiClient
    participant API as LOVEN API
    S->>C: 5 requests at the same time
    C->>API: Requests (expired token)
    API-->>C: 401 Unauthorized
    C->>API: POST /refresh (only once)
    API-->>C: New access token
    C->>API: Retry each request once
    API-->>C: 200 OK

I wrote the Flutter HTTP client that sits between every screen and the API. It attaches the token to each request, and when the token expires, it refreshes it once and retries the original request, so the user never notices. When several requests fail at the same moment, they all wait for a single refresh instead of sending five. Tokens are kept in the phone's secure storage (Keychain on iOS, Keystore on Android), and the user's role is never stored on the device.

Testing and deployment

Before launch

What I learned

← all projects