Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,31 @@ ADMIN_PASSWORD=your_secure_password_here

# Optional: PostHog configuration for analytics
# NEXT_PUBLIC_POSTHOG_KEY=
# NEXT_PUBLIC_POSTHOG_HOST=
# NEXT_PUBLIC_POSTHOG_HOST=

# ============================================
# Receipt Scanning Configuration (Optional)
# ============================================
# Receipt scanning will use mock data if not configured
# Choose one provider and set its API key

# OCR Provider - Choose one: "google" (default), "openai", or "anthropic"
# OCR_PROVIDER=google

# Google Gemini API Key (if using Google as provider)
# Get your key from: https://aistudio.google.com/app/apikey
# GOOGLE_GENERATIVE_AI_API_KEY=your_gemini_api_key_here

# OpenAI API Key (if using OpenAI as provider)
# Get your key from: https://platform.openai.com/api-keys
# OPENAI_API_KEY=your_openai_api_key_here

# Anthropic API Key (if using Anthropic as provider)
# Get your key from: https://console.anthropic.com/settings/keys
# ANTHROPIC_API_KEY=your_anthropic_api_key_here

# Optional: Override the default model for your chosen provider
# Google default: gemini-2.0-flash
# OpenAI default: gpt-4o
# Anthropic default: claude-sonnet-4-20250514
# OCR_MODEL=gemini-2.0-flash
9 changes: 8 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ permissions:
jobs:
test:
runs-on: ubuntu-latest
env:
REDIS_URL: redis://localhost:6379
NEXT_PUBLIC_POSTHOG_KEY: test-key
NEXT_PUBLIC_POSTHOG_HOST: https://app.posthog.com
OCR_PROVIDER: google
GOOGLE_API_KEY: placeholder
NODE_ENV: test

strategy:
matrix:
Expand Down Expand Up @@ -143,4 +150,4 @@ jobs:
uses: github/codeql-action/upload-sarif@v3
if: always() && hashFiles('snyk.sarif') != ''
with:
sarif_file: snyk.sarif
sarif_file: snyk.sarif
4 changes: 2 additions & 2 deletions INFRASTRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ export const trackPerformance = (metric: string, value: number) => {
```typescript
// Redis caching configuration
export const CACHE_CONFIG = {
// Bill data cache (30 days)
// Bill data cache (~6 months)
BILL_TTL: 30 * 24 * 60 * 60,

// Admin session cache (24 hours)
Expand Down Expand Up @@ -496,7 +496,7 @@ const securityHeaders = [
### Data Privacy

- **No personal data collection**: Anonymous user IDs only
- **Temporary storage**: 30-day automatic expiration
- **Temporary storage**: auto-deletes after ~6 months
- **Input masking**: Sensitive data masked in session recordings
- **GDPR compliant**: No persistent user tracking

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A modern, collaborative bill-splitting app with real-time sync and universal sha
* **Shareable Links**: Generate unique URLs that work for anyone, anywhere
* **Real-time Collaboration**: Multiple people can edit the same bill simultaneously
* **Cloud Sync**: Automatic syncing with visual status indicators
* **No Accounts Required**: Anonymous, temporary bill storage (30-day expiration)
* **No Accounts Required**: Anonymous, temporary bill storage (auto-deletes after ~6 months)

### 💰 **Smart Bill Management**
* **Intelligent Status System**: Draft → Active → Closed workflow with contextual actions
Expand Down
65 changes: 65 additions & 0 deletions README.simple.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# SplitSimple — Quick Start

SplitSimple is a modern billsplitting tool built with Next.js, TypeScript, Tailwind, and Redis. It keeps everyone in sync while you divide receipts line-by-line.

## Features

- Per-item splitting (even, shares, percent, exact) with penny-safe math
- Tax/tip/discount allocation (proportional or even)
- Auto-save to local storage + optional cloud share links (Redis)
- Undo/redo history, keyboard shortcuts, and CSV/export summaries
- Responsive UI with a dedicated mobile workflow

## Requirements

- Node.js 18+
- pnpm 9+
- Redis URL (for sharing) stored in `.env.local` as `REDIS_URL`

## Develop

```bash
pnpm install
pnpm dev
# open http://localhost:3000
```

Helpful scripts:

- `pnpm lint` – ESLint/Next checks
- `pnpm typecheck` – TypeScript
- `pnpm test` – Jest suite (`pnpm test:coverage` for coverage)
- `pnpm dev:clean` – Clear `.next` cache before starting dev

## Environment

```ini
REDIS_URL="redis://..."
NEXT_PUBLIC_POSTHOG_KEY="optional analytics"
NEXT_PUBLIC_POSTHOG_HOST="https://app.posthog.com"
OCR_PROVIDER="google" # or openai/anthropic
```

If OCR keys are missing the app falls back to mock data.

## Deploy

1. Provision Redis (Vercel KV or any managed Redis)
2. Set env vars above
3. `pnpm build && pnpm start` (or deploy via Vercel/GitHub Actions)

## Project Structure

- `components/` – UI (desktop + mobile-specific views)
- `contexts/` – `BillContext` reducer/history, sync helpers
- `lib/` – calculations, validation, sharing/export helpers
- `app/api/` – Next.js route handlers for sharing
- `tests/` – Jest helpers + MSW mocks

## CI

`.github/workflows/test.yml` runs lint, typecheck, unit tests, and Codecov upload; integration tests spin up Redis and run targeted suites.

## License

© SplitSimple team — redistribute under the repository’s LICENSE.
198 changes: 198 additions & 0 deletions RECEIPT_API_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Backend API Integration Plan: Gemini Vision OCR

## Overview
Replace the mock OCR service with a real backend API call to Google Gemini Vision API (gemini-3 model) for receipt scanning.

## Architecture

```
┌─────────────────┐
│ ReceiptScanner │ (Client Component)
│ Component │
└────────┬────────┘
│ POST /api/receipt/scan
│ (multipart/form-data)
┌─────────────────┐
│ /api/receipt/ │ (Next.js API Route)
│ scan │
└────────┬────────┘
├─► Validate file (size, type)
├─► Convert to base64
┌─────────────────┐
│ Gemini Vision │ (Google AI SDK)
│ API │
└────────┬────────┘
├─► Send image + prompt
├─► Receive JSON response
┌─────────────────┐
│ Response │
│ Parser │
└────────┬────────┘
├─► Extract items (name, price, qty)
├─► Validate & sanitize
┌─────────────────┐
│ Return Items │
│ to Client │
└─────────────────┘
```

## Implementation Steps

### 1. Environment Setup
- **File**: `.env.local` (add to `.env.example`)
- **Variable**: `GEMINI_API_KEY`
- **Validation**: Update `lib/env-validation.ts` to check for this key

### 2. Install Dependencies
```bash
npm install @google/generative-ai
```

### 3. Create API Route
- **File**: `app/api/receipt/scan/route.ts`
- **Method**: POST
- **Input**: multipart/form-data with `file` field
- **Output**: JSON with `items` array

### 4. Gemini Integration Service
- **File**: `lib/gemini-ocr.ts`
- **Functions**:
- `scanReceiptImage(imageBase64: string): Promise<OCRResult>`
- `parseGeminiResponse(response: string): OCRResult['items']`
- `validateAndSanitizeItems(items: any[]): OCRResult['items']`

### 5. Update Client Code
- **File**: `lib/mock-ocr.ts`
- **Change**: Replace `simulateOCR` with real API call
- **File**: `components/ReceiptScanner.tsx`
- **Change**: Update `processImage` to call new API endpoint

### 6. Error Handling
- Network failures → Show retry option
- API errors → Fallback to mock (development) or show error
- Invalid responses → Graceful degradation
- Rate limiting → User-friendly message

### 7. Testing Strategy
- Unit tests for response parsing
- Integration tests for API route
- Mock Gemini responses for development
- Error scenario testing

## API Route Specification

### Endpoint
`POST /api/receipt/scan`

### Request
- **Content-Type**: `multipart/form-data`
- **Body**:
- `file`: Image file (JPG, PNG, HEIC)
- Max size: 5MB

### Response (Success)
```json
{
"success": true,
"items": [
{
"name": "Garlic Naan",
"price": "4.50",
"quantity": 1
},
{
"name": "Butter Chicken",
"price": "16.00",
"quantity": 2
}
],
"confidence": "high"
}
```

### Response (Error)
```json
{
"success": false,
"error": "Invalid file format",
"code": "INVALID_FILE"
}
```

## Gemini Prompt Engineering

### System Prompt
```
You are a receipt OCR system. Extract all line items from this receipt image.

For each item, identify:
1. Item name (clean, no special characters)
2. Price (numeric value only, as string)
3. Quantity (default to 1 if not specified)

Return ONLY a valid JSON array in this exact format:
[
{"name": "Item Name", "price": "12.99", "quantity": 1},
{"name": "Another Item", "price": "5.50", "quantity": 2}
]

Do not include:
- Tax lines
- Tip lines
- Subtotal/total lines
- Store information
- Dates/times

If you cannot identify items clearly, return an empty array [].
```

## Error Codes

- `INVALID_FILE`: File type not supported
- `FILE_TOO_LARGE`: File exceeds 5MB
- `GEMINI_API_ERROR`: Gemini API returned an error
- `PARSE_ERROR`: Could not parse Gemini response
- `NO_ITEMS_FOUND`: No items detected in receipt
- `NETWORK_ERROR`: Network request failed

## Fallback Strategy

1. **Development Mode**: If `GEMINI_API_KEY` not set, use mock data
2. **API Failure**: Show error with "Try Again" button
3. **Empty Results**: Suggest manual entry or text paste
4. **Rate Limiting**: Queue requests or show "Please wait" message

## Security Considerations

- Validate file types server-side
- Enforce file size limits
- Sanitize API responses
- Never expose API key to client
- Rate limiting (future enhancement)

## Performance Optimizations

- Compress images before sending (if > 1MB)
- Cache common receipt formats (future)
- Stream responses for large receipts (future)
- Optimize Gemini prompt for faster responses

## Future Enhancements

- Batch processing multiple receipts
- Receipt format learning/adaptation
- Confidence scores per item
- Support for multiple currencies
- Receipt metadata extraction (date, store name)


Loading
Loading