# Patreon Backend Service

This repo now includes a standalone backend service under `backend/` for Patreon account linking and tier lookup.

## What it does

- Starts a link session for a game player ID.
- Redirects the player through Patreon OAuth on the backend, not in the game client.
- Exchanges the OAuth code on the server.
- Fetches the user's Patreon membership entitlement for your campaign.
- Stores the entitlement server-side.
- Exposes a game-facing API that returns the linked player's current Patreon tier/status.

This keeps the Patreon client secret out of the Ren'Py game.

## Files

- `backend/run.py`: development entrypoint
- `backend/patreon_service/app.py`: Flask routes
- `backend/patreon_service/patreon_client.py`: Patreon OAuth/API client
- `backend/patreon_service/storage.py`: SQLite persistence
- `backend/.env.example`: required configuration
- `backend/requirements.txt`: Python dependencies

## Environment

Copy `backend/.env.example` into your own environment configuration and set:

- `FLASK_SECRET_KEY`: Flask session secret
- `GAME_API_KEY`: shared secret the game uses to call backend endpoints
- `PATREON_CLIENT_ID`: Patreon OAuth client ID
- `PATREON_CLIENT_SECRET`: Patreon OAuth client secret
- `PATREON_REDIRECT_URI`: Patreon callback URI registered with Patreon
- `PATREON_CAMPAIGN_ID`: your Patreon campaign ID
- `DATABASE_PATH`: SQLite file path
- `PUBLIC_BASE_URL`: externally reachable base URL for this service

## Install

```bash
cd backend
pip install -r requirements.txt
```

## Run

```bash
cd backend
python run.py
```

By default it runs on `http://localhost:5000`.

## API flow

### 1. Create a link session

`POST /api/link-sessions`

Headers:

- `X-Game-Api-Key: <GAME_API_KEY>`

Body:

```json
{
  "player_id": "save-slot-1"
}
```

Response:

```json
{
  "player_id": "save-slot-1",
  "link_token": "...",
  "auth_url": "http://localhost:5000/auth/patreon/start?link_token=...",
  "status_url": "http://localhost:5000/api/link-sessions/..."
}
```

The game should open `auth_url` in the browser.

### 2. Player completes Patreon login

The browser is redirected through:

- `GET /auth/patreon/start`
- Patreon OAuth
- `GET /auth/patreon/callback`

On success, the backend stores the entitlement and marks the link session as `linked`.

### 3. Poll link status

`GET /api/link-sessions/<link_token>`

Headers:

- `X-Game-Api-Key: <GAME_API_KEY>`

Returns `pending`, `linked`, or `error`. When linked, it includes the entitlement payload.

### 4. Fetch current entitlement later

`GET /api/players/<player_id>/entitlement`

Headers:

- `X-Game-Api-Key: <GAME_API_KEY>`

This endpoint refreshes Patreon tokens when they are near expiry and returns the latest cached entitlement.

## Entitlement payload

Example:

```json
{
  "linked": true,
  "entitlement": {
    "patreon_user_id": "123456",
    "patreon_user_name": "Example Patron",
    "campaign_id": "999999",
    "is_entitled": true,
    "patron_status": "active_patron",
    "last_charge_status": "Paid",
    "last_charge_date": "2026-06-09T12:34:56+00:00",
    "currently_entitled_amount_cents": 500,
    "tier_ids": ["111", "222"],
    "tiers": [
      {
        "id": "111",
        "title": "Silver",
        "amount_cents": 500,
        "description": "Example tier",
        "discord_role_ids": []
      }
    ]
  }
}
```

## Ren'Py integration sketch

1. Game calls `POST /api/link-sessions` with its local player/save identifier.
2. Game opens the returned `auth_url` in the platform browser.
3. Game polls `GET /api/link-sessions/<link_token>` until status becomes `linked` or `error`.
4. Game gates content on:
   - `entitlement.is_entitled`
   - `entitlement.patron_status`
   - `entitlement.tiers[*].title`
   - `entitlement.currently_entitled_amount_cents`

## Notes

- This service assumes Patreon API v2 OAuth and the `identity` + `identity.memberships` scopes.
- The membership lookup is done against the user identity response and matched to `PATREON_CAMPAIGN_ID`.
- If you deploy this publicly, put it behind HTTPS.
- `GAME_API_KEY` is only a lightweight game-to-service secret. It prevents anonymous polling but does not replace full user auth for a website.
