Parking Spot Swap
Give a Spot, Get a Spot - A community-powered parking spot exchange app.
Overview
Parking Spot Swap is a free mobile app that helps drivers find parking by connecting those leaving spots with those looking for them. Users earn credits by offering their parking spots and spend credits to locate available parking nearby.
Core Features
Authentication & Onboarding
- Sign Up/Sign In: Email-based authentication with password
- Two-Factor Authentication (2FA): Optional 2FA setup during signup using authenticator apps (Google Authenticator, Authy)
- Scannable QR code generated using TOTP URI format (
otpauth://totp/...)
- Manual secret key entry option with copy-to-clipboard
- 8 backup codes for account recovery
- Note: In production, the TOTP secret should be generated server-side and verification should be done via backend API
- Password Reset: Forgot password flow with email reset link
- Referral Code Input: New users can enter a friend’s referral code during sign-up
- Collapsible “Have a referral code?” section on sign-up form
- Real-time validation with visual feedback (green checkmark/red X)
- When valid code is entered, the referrer receives 3 credits and a notification
- New user does not receive credits (only the referrer is rewarded)
- Terms & Privacy: Clickable links during sign-up open full-screen modals displaying complete Terms of Use and Privacy Policy content - accessible without requiring sign-in
- Onboarding Guide: 4-step tutorial explaining how to offer, find, drop pins, and use credits
- New users start with 0 credits - earn credits by offering spots or dropping pins
Email Workflows
The app includes a comprehensive email notification system:
Email Categories:
- Transactional: Password resets, security alerts, 2FA confirmations, account status (always enabled)
- Activity: Spot offered/claimed/expired, credit earned notifications
- Summary: Monthly reports (weekly summary has been deprecated)
- Marketing: New feature announcements, promotional offers, referral updates, follow-up ratings
Automatic Email Triggers:
- Welcome Email: Sent immediately after email verification on signup
- Password Reset: Sent when user requests password reset via forgot password flow
- Follow-up Rating: Automatically sent 1 week after user’s account creation to request app feedback
- Account Suspended/Restored: Sent by admin when suspending or restoring user accounts
- Transaction Receipts: Sent for all monetary transactions (payments, payouts, refunds)
- Monthly Report: Sent at the start of each month (opt-in via Email Settings)
Deep Links:
All email links use web fallback URLs (https://parkingspotswap.com/app/...) that:
- Open the app directly on mobile devices with the app installed (Universal Links)
- Show a web page with download links for users without the app
- Work properly when clicked from desktop email clients
Web Fallback Pages:
Static HTML pages are provided in /public/app/ for desktop users who click email links:
/app/reset-password/ - Password reset confirmation page
/app/rate/ - Rating feedback confirmation (reads ?score= parameter)
/app/activity/ - Activity history redirect
/app/settings/ - Account settings redirect
/app/home/ - Main app landing page
/app/verify/ - Email verification confirmation
/app/security/ - Security alert notification
/app/referral/ - Referral invitation page (reads ?code= parameter)
/app/unsubscribe/ - Email preferences redirect
/app/transaction/ - Transaction receipt confirmation (reads ?amount= and ?type= parameters)
/app/credit/ - Credit update confirmation (reads ?amount= and ?type= parameters)
/app/account-status/ - Account status update notification
Each page displays a branded confirmation message with App Store download link.
Important: These pages must be deployed to https://parkingspotswap.com/app/ for email links to work on desktop.
Monthly Report Emails:
The app automatically tracks user activity and can send monthly summary emails:
- Automatic Tracking: All transactions (spots offered, credits earned/spent, hold payments, referrals) are automatically tracked per month
- Reliability Score: Changes in reliability score are monitored throughout the month
- Report Generation: At the start of each new month, the system checks if a report should be sent for the previous month
- Smart Insights: Reports include personalized insights based on user activity (e.g., “Top Contributor!” for 10+ spots offered)
- User Preference: Monthly reports are opt-in - users can enable them in Email Settings
Report Contents:
- Spots offered and found during the month
- Credits earned vs spent (net change)
- Pin drops and successful parkings
- Hold payment earnings and spending
- Reliability score changes
- Referral activity
- Personalized monthly insights
Transaction Receipt Emails:
The app automatically sends Stripe-branded receipt emails for all monetary transactions:
- Payment Receipts: Sent when users pay for spot holds (includes amount, payment method, fee breakdown)
- Payout Receipts: Sent when spot holders receive earnings (includes gross amount, platform fee, net payout)
- Refund Receipts: Sent when payments are refunded
- Credit Transaction Emails: Sent for credit-based transactions (earned/spent) when enabled in preferences
- All receipts use the same branding as other app emails with Parking Spot Swap styling
- Receipts include transaction ID, date, description, and status
Features:
- Email Preferences: Granular control over which emails to receive
- Digest Frequency: Choose instant, daily, or weekly email delivery
- Email History: View all sent emails with status (queued, sent, delivered, opened, failed)
- Smart Triggers: Emails automatically sent on key actions (signup, spot claimed, credits earned, etc.)
Screens:
/email-settings - Configure email preferences
/email-history - View email history and status
- Accessible from Profile > Email Preferences
Email Provider (Mailrelay):
To enable real email sending, configure Mailrelay in the ENV tab:
EXPO_PUBLIC_MAILRELAY_API_KEY - Your Mailrelay API key (from Settings > API Keys in Mailrelay dashboard)
EXPO_PUBLIC_MAILRELAY_ACCOUNT - Your Mailrelay account subdomain (e.g., “mycompany” from mycompany.ipzmarketing.com)
EXPO_PUBLIC_MAILRELAY_FROM_EMAIL - Default sender email address (e.g., “noreply@parkingspotswap.com”)
EXPO_PUBLIC_MAILRELAY_FROM_NAME - Default sender name (e.g., “Parking Spot Swap”)
Without these credentials, emails are logged locally but not sent.
Credit Economy
- Offer a Spot: Earn 1 credit by offering your parking spot (1-minute verification required)
- Find a Spot: Costs 1 credit when clicking “Navigate to Spot”
- Cancel Navigation: If you haven’t opened maps yet, you can cancel and get your credit refunded - useful for finding a different spot
- Exit Protection: If you try to close the navigation screen, you’ll be asked to confirm whether you parked or if the spot was unavailable - preventing accidental credit loss
- Successful Parking: Credit is kept by the system
- Spot Unavailable: Credit refunded + 1 bonus credit (+2 total) when legitimately reporting
- Drop a Pin: Mark open spots you see; earn 1 credit when someone parks there
- Referrals: Earn 3 credits for each friend who signs up
- Power User Bonus: +2 credits for 10+ successful parkings per month
Spot Notes
When offering a spot or dropping a pin, users can add optional notes to help others find the exact location:
- Garage Details: “Level 3, Section B, near stairs” or “Level P2, by elevator”
- Street Parking: “Street parking by coffee shop” or “Next to red truck”
- Space Numbers: “Space #127” or “Meter #42”
- Parking Limits: “2-hour limit until 6pm” or “Free after 5pm”
- Accessibility: “Handicap adjacent, extra wide” or “Compact car spot”
- Vehicle Info: “Blue Honda leaving” or “Behind white SUV”
Notes are displayed on the spot card and navigation screen to help drivers find the exact spot.
Key Screens
- Auth Screen: Sign in/sign up with email and password
- Onboarding: Step-by-step guide for new users
- Offer Tab (Default): 1-minute countdown timer to verify stationary position, plus “See an Open Spot?” feature
- Visible During Countdown: Spots appear on the map as “pending” during the 1-minute countdown period
- Instant Notification: Offering users are notified immediately when someone selects their spot
- Find Tab: Interactive map showing available spots with custom markers
- Instant Claiming: When you select a spot to navigate, it’s immediately claimed and removed from the map for other users
- Spot Owner Notification: Offering users are notified when someone selects their spot during the countdown period
- Smart Hold Prompting: Hold request prompt appears when:
- You’re over 1 mile away from an offered spot, OR
- Spot is still in countdown (<30 seconds left) AND you’re more than 500 feet away
- Pin Drops: No hold prompt for pin drops (only for offered spots)
- Activity Tab: Credit balance, transaction history, reliability score, and unread activity badge
- Transaction Logging: All hold payments are tracked and displayed in activity history
- Monetary Transactions: Hold payments shown with dollar amounts (e.g., “-$6.00” for payment sent, “+$5.70” for payment received)
- Credit vs. Cash: Credits shown as whole numbers, hold transactions shown with $ prefix and two decimals
- Profile Tab: User stats, editable name/profile image, referral code, and settings
Paid Hold System
- Request a Hold: Pay to reserve a spot while you drive there
- Base Rate: $2/minute for the hold service
- Duration Options: 1, 3, 5, or 10 minute quick select buttons, plus custom duration input for extended holds
- Custom Hold Periods: Enter any custom hold time beyond 10 minutes at the same base rate
- Early Arrival Discount: Users are only charged for actual time used - arrive early and save money!
- Real-Time Fee Calculation: System tracks hold start time and calculates actual minutes used when user parks
- Transparent Billing: Shows original reserved time vs. actual time used with savings breakdown
- Timer Display: Pink countdown badge shows remaining hold time
- Global Hold Banner: When a hold is active, a persistent banner appears at the top of ALL screens showing:
- Countdown timer with time remaining
- Driver name and earnings for spot holders
- Spot holder name and cost for requesters
- Vehicle information when driver arrives
- Banner pulses amber when under 30 seconds remaining
- Tapping the banner navigates to the relevant hold management screen
- Hold Confirmation: Spot owner must accept the hold request
- Visual Indicators: Map changes to pink theme when hold is active
- Push Notifications for Holds:
- Hold period ended (for both holder and requester)
- Extension request received (for spot holders)
- Driver is nearby (within 100 feet)
- Driver has arrived at your location
Pricing & Fees Structure
When a user requests a hold, the following fees apply:
Charged to Hold Requester:
| Fee Type | Rate | Example (5 min hold in NYC) |
|———-|——|———————|
| Base Hold Fee | $2.00/minute | $10.00 |
| Service Fee | 5% of base | $0.50 |
| Taxes | Varies by location (destination-based) | $0.93* |
| Total Charged | | $11.43* |
*Tax rate varies by state, county, and municipality. Example uses NYC rate of 8.875%.
Spot Holder Earnings:
| Item | Rate | Example (5 min hold) |
|——|——|———————|
| Base Amount | $2.00/minute | $10.00 |
| Platform Fee | -5% of base | -$0.50 |
| Net Earnings | | $9.50 |
Platform Revenue:
- Service Fee (5% of base)
- Taxes (destination-based, varies by location)
- Platform Fee from Spot Holder (5% of base)
Payment Validation for Holds
Both parties must have payment methods set up to use the paid hold feature:
For Hold Requesters (Drivers):
- Must have a payment method (credit/debit card) added before requesting a hold
- If no payment method exists, a prompt appears explaining why it’s needed
- “Set Up” button navigates directly to Payment Settings
- Card is only charged when the driver successfully parks
For Spot Holders:
- Must have a bank account connected to receive hold payments
- If a hold request comes in without bank account setup:
- Spot holder receives an in-app notification prompting them to set up payments
- “Earn Money from Holds!” modal explains the benefits ($1.90/minute earnings)
- “Set Up Now” button navigates to Payment Settings
- The requesting driver sees a “Waiting for Spot Holder” screen with a 2-minute countdown
- If the spot holder doesn’t complete setup in time, the hold request expires
- Requesting driver can cancel anytime and find another spot
Hold Extension System
When a hold is about to expire, the requesting user can request an extension:
- Extension Prompt: Shows automatically when countdown reaches 30 seconds or less
- Only appears if the requester is NOT within 100 feet of the spot
- Can be dismissed and will re-appear after 5 seconds if still below 30 seconds
- Multiple Extensions: Requesters can request multiple extensions throughout the hold
- Each time the countdown dips below 30 seconds and they’re not within 100 feet, the prompt appears
- After an extension is approved, the system resets to allow future extension requests
- Duration Options: Request 1, 2, 3, or 5 additional minutes
- Spot Holder Approval: The spot holder must approve the extension request
- Extension Pricing: Same $2/minute rate with 5% platform fee
- Real-Time Notifications: Both parties receive notifications about extension status
- Partial Payment on Decline: If the spot holder declines the extension:
- The hold ends immediately
- The requesting user pays 50% of the rate for time already waited
- The spot holder receives their earnings (minus 5% platform fee)
- Both parties are notified of the outcome
- Extension History: All extension requests are tracked in the spot’s history
Hold Monitoring & Dispute Resolution
The app monitors spot holders during active holds and handles disputes:
Spot Holder Movement Monitoring:
- Location Tracking: During active holds, spot holder’s location is monitored
- Movement Warning: If holder moves more than 100 feet from the spot, a warning modal appears with 30-second countdown
- Confirmation Options: Holder can confirm “I’m Still Here” (verifies GPS) or “Yes, I’m Leaving”
- Auto-Cancel: If no response within 30 seconds, hold is automatically cancelled
- No Payment if Left: If holder leaves during hold, payment is cancelled and requester is refunded
Proximity Alerts:
- 100 Feet Threshold: When requester gets within 100 feet of the spot, both parties are notified
- Vehicle Info Shared: Spot holder receives requester’s vehicle details (color, make, model) for identification
- Arrival Notification: Both see “Almost There!” and “Driver Is Nearby!” notifications
Dispute Resolution (Spot Reported Unavailable During Hold):
- Dispute Initiation: When requester reports “Spot Taken” during/after hold, dispute process begins
- Location Verification: Spot holder is asked to confirm their location
- 50% Payment Rule: If holder’s location is verified (stayed at spot), requester pays 50% for wait time
- No Payment if Left: If holder left the spot, requester is fully refunded with bonus credit
- Automatic Notifications: Both parties receive notifications about dispute outcome
Payment Split Logic (50% for Wait Time):
- If requester drove away after reporting: 50% payment to spot holder for waiting
- If requester stayed but didn’t park: 50% payment to spot holder
- If requester never arrives and hold expires: 50% payment if holder verified they stayed
- If spot holder leaves during hold: 0% payment, full refund to requester
When claiming an offered spot or requesting a hold:
- Immediate Sharing: Vehicle info is requested as soon as you claim the spot (during the 1-minute countdown)
- This allows the spot holder to know who’s coming while they’re still waiting
- Prompted to share vehicle details (color, make, model required; license plate optional)
- Multi-Vehicle Support: Users can save multiple vehicles to their profile
- First vehicle added becomes the default, but users can change their default vehicle anytime
- Saved vehicles auto-fill on future claims for quick sharing
- Vehicle details include optional nickname (e.g., “My Tesla”, “Work Car”)
- Users can add, edit, and delete saved vehicles in Profile settings
- Vehicle information is displayed to spot holder immediately after claiming
- Arrival Notifications: When the requesting user arrives (within 100 feet):
- Spot holder is notified that the driver has arrived
- Vehicle information is displayed prominently for easy identification in the parking area
- Driver can see “Driver Has Arrived!” status with vehicle details on offer screen
- Before arrival, spot holder sees “Driver En Route” status
- Transaction Logging: All hold payments are automatically logged in the Activity tab:
- Hold Request: Initial authorization transaction created when hold is confirmed
- Final Payment: When user parks, three transactions are logged:
- Final charge to requesting user (based on actual time used)
- Payment received by spot owner (95% of actual charge)
- Platform fee transaction (5% of actual charge)
Payment Settings
- Payment Methods: Add credit/debit cards for paying to hold spots (secured via Stripe)
- Bank Account: Connect bank account via Stripe Connect to receive payments from holds
- Auto-Accept Holds: Toggle to automatically accept all hold requests at $2/minute
- Minimum Hold Duration: Set minimum time (in minutes) you’ll hold spots
- Payment Security: All payment data encrypted and processed securely
- Payouts: Receive $1.90/minute when others hold your spots (after 5% platform fee)
- Payout Processing: Bank transfers processed within 2-3 business days
- Test Mode Indicator: Shows when running with test Stripe keys (no real charges)
Stripe Connect Integration
The app uses Stripe Connect for marketplace payments between users:
Payment Flow:
- Authorization: When a hold is confirmed, payment is authorized (not captured)
- Capture: Payment is only captured when the driver successfully parks
- Prorated Billing: Early arrival means lower charges - only pay for actual time used
- Cancellation: No charge if hold is cancelled or spot is unavailable
For Drivers (Requesters):
- Add credit/debit cards via Stripe’s secure payment collection
- Cards are stored securely with Stripe (not on device)
- Only charged when successfully parking
For Spot Holders:
- Connect bank account via Stripe Connect Express onboarding
- Receive 95% of hold fees (5% platform fee)
- View earnings and manage payouts via Stripe Dashboard
- Payouts processed within 2-3 business days
Environment Variables Required:
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_... (or pk_live_... for production)
EXPO_PUBLIC_STRIPE_SECRET_KEY=sk_test_... (backend only, never expose client-side)
EXPO_PUBLIC_API_URL=https://your-backend.com (for production API calls)
Test Mode:
- Test keys (pk_test_/sk_test_) enable sandbox mode
- No real charges are made in test mode
- Test mode badge shown in Payment Settings
Admin Panel (Role-Based Access)
- Role System: Users have roles (user, admin, owner) for permission-based access
- Admin Dashboard: Only visible to users with admin or owner roles
- Role Assignment: Development tool to assign admin/owner roles for testing
- Admin Features:
- User Management: View all users, manage credits, suspend/unsuspend accounts
- Credit Management: Grant or remove credits from users with reason notes
- Fraud Reports: Review and take action on fraud reports
- System Metrics: View analytics (total users, active users, transactions, etc.)
- Activity Monitoring: Track recent user actions and system events
- Security: All admin actions would be validated by backend and logged for accountability
- Access Control: Admin Dashboard only appears in Profile settings for admin/owner users
- Development: Uses local mock data; production would connect to backend API
Trust & Safety
- GPS verification ensures users are stationary when offering spots
- Movement detection prevents fraudulent spot offers
- Pin Drop Validation:
- Must be within 500m of current location
- 2-minute cooldown between pin drops
- Minimum 20% reliability score required
- Spot Unavailable Validation:
- Must be within 100 feet of spot to report it as unavailable
- 30-second verification period after reporting spot as taken
- If user stays near the spot (doesn’t drive away), system assumes they parked and credit is used
- If user drives away (>200 feet at >10 mph), spot report is validated: credit refunded + 1 bonus credit
- Prevents false reports from users who actually found parking nearby
- Fraud Reporting: Users can report fraudulent spots after finding them unavailable
- Locale-Based Units: Distance displayed in feet/miles (US) or meters/km based on user’s region
- Parking Detection: App detects when user returns from Maps and asks if they parked
- Reliability scores track user trustworthiness
- Fraud flags can lead to account suspension
- Spam Detection & Account Restrictions:
- Automatic detection of suspicious activity patterns
- Activity types tracked: rapid offers, rapid cancellations, excessive reports, rapid hold requests, movement during offers, GPS spoofing, false pin drops
- Spam score calculated based on weighted activity (0-100 scale)
- Accounts automatically restricted when spam score reaches 70+
- Restricted users see notification explaining temporary restriction
- Admin dashboard shows flagged accounts with full activity history
- Admins can lift restrictions (reduces spam score by 30) or ban users permanently
Technical Stack
Frontend (Mobile App)
- Framework: Expo SDK 53 with React Native 0.76.7
- Navigation: Expo Router (file-based routing)
- State Management: Zustand with AsyncStorage persistence, React Query for server state
- User Data Persistence: User-specific data (credits, vehicles, transactions, payment methods) is stored separately by user ID, ensuring data persists across login/logout cycles
- Styling: NativeWind (Tailwind CSS for React Native)
- Typography: Finlandica (Helsinki-style) for app branding, Archivo for body text
- Animations: react-native-reanimated v3
- Maps: react-native-maps with Google Maps provider
- Location: expo-location for GPS tracking
- Haptics: expo-haptics for comprehensive tactile feedback throughout the app
- UI Components:
- ClearableTextInput: Reusable input component with emerald green cursor for visibility on dark backgrounds and “X” clear button that appears when text is entered
Backend (Server)
- Runtime: Bun (JavaScript/TypeScript runtime)
- Framework: Express.js
- Database: SQLite (via Bun’s native bun:sqlite)
- Authentication: JWT tokens with role-based access control
- Password Hashing: bcryptjs
- Payments: Stripe SDK for Connect marketplace payments
- API: RESTful endpoints for auth, users, admin, and payments
Backend Architecture
The server is located in the /server directory and provides the following:
API Endpoints
Authentication (/api/auth)
POST /signup - Register new user
POST /signin - Sign in and receive JWT token
GET /verify - Verify JWT token validity
Users (/api/users) - Requires authentication
GET /me - Get current user profile
GET /me/transactions - Get user’s transaction history
Admin (/api/admin) - Requires admin/owner role
GET /metrics - System metrics (total users, active users, transactions, fraud reports)
GET /users - List all users with pagination
GET /users/:userId - Get specific user details
POST /users/:userId/credits - Grant or deduct credits (with reason)
POST /users/:userId/suspend - Suspend/unsuspend user account
POST /users/:userId/role - Change user role (owner only)
GET /fraud-reports - List fraud reports with filtering
Stripe Payments (/api/stripe)
POST /customers - Create Stripe customer for a user
POST /setup-intents - Create SetupIntent for adding payment methods
GET /customers/:customerId/payment-methods - List saved payment methods
POST /customers/default-payment-method - Set default payment method
DELETE /payment-methods/:paymentMethodId - Remove a payment method
POST /connect/accounts - Create Stripe Connect Express account
GET /connect/accounts/:accountId - Get Connect account status
POST /connect/onboarding-link - Get onboarding URL
POST /connect/dashboard-link - Get dashboard URL
POST /hold/create - Create hold PaymentIntent (authorization)
POST /hold/capture - Capture payment when user parks
POST /hold/cancel - Cancel authorization
POST /extension/create - Create extension PaymentIntent
POST /extension/capture - Capture extension payment
POST /extension/decline - Handle declined extension (partial capture)
POST /extension/cancel - Cancel extension
POST /webhooks - Stripe webhook handler
GET /platform-fees - Platform fee analytics (see below)
GET /audit-logs - View admin action audit trail
Platform Fees Analytics (GET /api/admin/platform-fees)
Query params: ?range=7d|30d|90d|all (default: 30d)
Returns comprehensive platform fee data:
- Summary: Total count, total fees, average/max/min fee
- Periods: Today, this week, this month breakdowns
- Running Averages: 7-day and 30-day daily averages
- Daily Breakdown: Fee totals by date
- Top Fees: Highest fee transactions with user details
- Recent Transactions: Latest platform fee transactions
- Distribution: Hourly and day-of-week activity patterns
User Roles
- user: Standard app user
- admin: Can manage users, credits, and fraud reports
- owner: Full access including role assignment and system configuration
Database Schema
users - User accounts with roles and stats
id, email, password_hash, display_name, role
credits, reliability_score, total_spots_offered, total_spots_found
total_pins_dropped, successful_parkings, monthly_parkings
is_power_user, referral_code, fraud_flags, is_suspended
created_at, updated_at
transactions - Credit and monetary transactions
id, user_id, type, amount, monetary_amount
description, related_spot_id, related_user_id, created_at
fraud_reports - User fraud reports
id, reporter_id, reported_user_id, spot_id, reason
status, admin_notes, reviewed_by, created_at, updated_at
audit_logs - Admin action logs
id, admin_id, action, target_user_id, details, created_at
stripe_customers - Stripe customer records (for hold requesters)
id, user_id, stripe_customer_id, email, created_at
stripe_connect_accounts - Stripe Connect accounts (for spot holders)
id, user_id, stripe_account_id, email, status, created_at, updated_at
hold_payments - Payment tracking for holds
id, payment_intent_id, spot_id, requester_customer_id, holder_account_id
requested_minutes, actual_minutes_used, authorized_amount, captured_amount
platform_fee, holder_earnings, status, cancellation_reason, created_at, updated_at
Running the Backend
cd server
bun install # Install dependencies
bun run db:init # Initialize database tables
bun run db:seed-admin # Seed initial admin users
bun run dev # Start development server
Default Admin Credentials
After seeding, the following admin account is created:
- Owner: info@parkingspotswap.com (OwnerPassword123!)
Production Deployment Steps
- Change Admin Password: Edit
server/src/db/seed-admin.ts and update the password:
{
email: 'info@parkingspotswap.com',
password: 'YourSecurePasswordHere!', // Use a strong password
displayName: 'System Owner',
role: 'owner',
}
- Set JWT Secret: Create a
.env file in the server folder or set the environment variable:
JWT_SECRET=your-very-long-random-secret-key-here
Generate a secure secret with: openssl rand -base64 64
- Initialize Production Database:
cd server
bun run db:init
bun run db:seed-admin
-
Connect Mobile App: Update the mobile app to point to your production backend URL by setting the API base URL in your app configuration.
- Configure Stripe (required for payments):
# Add to server/.env
STRIPE_SECRET_KEY=sk_live_... # Your Stripe secret key
STRIPE_WEBHOOK_SECRET=whsec_... # Webhook signing secret
APP_URL=https://your-app-scheme:// # Deep link URL scheme
Stripe Dashboard Setup:
- Create a Stripe account at stripe.com
- Enable Stripe Connect in your dashboard
- Set up a webhook endpoint at
https://your-api.com/api/stripe/webhooks
- Subscribe to events:
account.updated, payment_intent.succeeded, payment_intent.payment_failed
- Get your webhook signing secret
Mobile App Configuration:
# Add to mobile app .env
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
EXPO_PUBLIC_API_URL=https://your-api.com
- Secure the Database: In production, consider:
- Using PostgreSQL instead of SQLite for better concurrency
- Setting up database backups
- Using HTTPS for all API endpoints
Haptic Feedback
The app uses haptic feedback throughout for a premium feel:
| Haptic Type |
Usage |
| Light Impact |
Close buttons, navigation links, tab switching, toggle switches, sort filters |
| Medium Impact |
Share actions, spot selection on map, image picker |
| Heavy Impact |
Primary action buttons (Find Spot, Navigate, Start Offering) |
| Selection |
Filter/sort option changes |
| Success |
Parking confirmed, spot offer completed, pin drop submitted |
| Warning |
Spot reported as taken |
| Error |
Not enough credits, movement detected during offer |
Project Structure
src/
├── app/ # Expo Router screens
│ ├── (tabs)/ # Tab navigation
│ │ ├── index.tsx # Find screen
│ │ ├── offer.tsx # Offer spot screen (default landing)
│ │ ├── activity.tsx # Activity/credits screen
│ │ └── profile.tsx # Profile screen
│ ├── auth.tsx # Sign in/sign up screen
│ ├── onboarding.tsx # New user onboarding guide
│ ├── find-spot.tsx # Find parking flow
│ ├── navigate-spot.tsx # Navigate to spot
│ ├── drop-pin.tsx # Drop pin for open spot (with validation)
│ ├── payment-settings.tsx # Payment methods and bank account setup
│ ├── vehicle-management.tsx # Manage saved vehicles
│ ├── admin-dashboard.tsx # Admin panel for user/system management
│ ├── dev-role-assign.tsx # Development tool to assign admin roles
│ ├── terms.tsx # Terms of Service
│ ├── privacy.tsx # Privacy Policy
│ └── help.tsx # Help & FAQ
├── components/ # Reusable UI components
└── lib/
├── state/ # Zustand stores
│ ├── types.ts # TypeScript interfaces
│ ├── user-store.ts # User & credits state
│ └── spot-store.ts # Parking spots state
└── cn.ts # className merge utility
Credit Transactions
| Action |
Credits |
| Offer parking spot |
+1 |
| Navigate to found spot |
-1 |
| Report spot unavailable (legitimate) |
+2 (refund + bonus) |
| Pin drop reward |
+1 |
| Referral signup |
+3 |
| Power user bonus |
+2 |
Future Enhancements
App Store Readiness
Compliance Checklist
- ✅ Privacy Policy: Full policy at
/privacy with GDPR/CCPA compliance
- ✅ Terms of Service: Complete terms at
/terms
- ✅ Location Permissions: Proper foreground permission requests with clear purpose
- ✅ No Paywalls: Free to use credit-based economy
- ✅ Abuse Prevention: Comprehensive spam detection and account restrictions
- ✅ Custom Modals: No Alert.alert() for main flows (App Store guideline)
- ✅ Haptic Feedback: Premium feel throughout the app
- ✅ Safe Area Handling: Proper insets for all screen sizes
- ✅ RevenueCat Integration: Ready for in-app purchases when needed
- ✅ Dev Tools Hidden: Production-ready without debug UI
App Store Submission Checklist
Before Submission:
- ☐ Host external legal documents (see
/legal folder):
legal/terms-of-service.html → Your website URL
legal/privacy-policy.html → Your website URL
- ☐ Add URLs to App Store Connect:
- Privacy Policy URL
- Terms of Service URL
- ☐ Update app.json (via Vibecode settings):
- App name: “Parking Spot Swap”
- Add
NSLocationWhenInUseUsageDescription: “Your location is used to show nearby parking availability and verify parking spot contributions.”
- ☐ Prepare App Store metadata:
- App description (see below)
- Screenshots (real app UI)
- Keywords
Recommended App Store Description:
Parking Spot Swap - Give a Spot, Get a Spot
Find parking faster with community help. When you're leaving a spot, let others know. When you need parking, see what's available nearby.
HOW IT WORKS:
• Offer Your Spot - Leaving? Share your spot and earn credits
• Find a Spot - Browse nearby available parking on the map
• Drop a Pin - See an empty spot? Mark it for others
EARN & SPEND CREDITS:
• Earn credits by helping others find parking
• Spend credits to navigate to available spots
• No subscriptions, no paywalls - just community sharing
OPTIONAL PAID HOLDS:
• Need extra time? Pay to hold a spot while you drive there
• $2/minute with transparent pricing
• Only pay for actual time used
TRUST & SAFETY:
• Reliability scores show trustworthy contributors
• GPS verification prevents false reports
• Community-powered fraud prevention
Start earning credits today by sharing your parking spot!
Apple Review Notes (copy this):
Parking Spot Swap is a community-powered parking availability app. Users earn credits by contributing parking information (offering their spot when leaving or dropping pins for open spots they see). Users may optionally spend credits to navigate to available spots. For offered spots during verification countdown, users may optionally pay other users to hold the spot at $2/min with a 5% platform fee. No subscriptions or paywalls are used. Credits cannot be purchased and have no cash value.
User Feedback System
- Follow-up Email: Sent 1 week after signup asking for app rating
- High Ratings (4-5): Prompts App Store/Play Store review
- Low Ratings (1-3): Shows feedback form to capture issues
- Rating Screen:
/rate handles deep links from email
Running the App
The app runs automatically on port 8081 via Expo. View it through the Vibecode platform.
QC/QA - Production Readiness
Recent Quality Improvements (v1.0.0)
- ✅ Error Boundary: Global error handling prevents app crashes
- ✅ Rate Limiting: Email/password updates rate-limited (3 per 5 minutes)
- ✅ Input Validation: Centralized validation utilities with consistent rules
- ✅ Type Safety: Fixed
any types, improved type annotations
- ✅ Performance: Memoized selectors and computations
- ✅ Badge Overflow: Activity badge shows “99+” for large counts
Architecture Strengths
- State Management: Zustand with proper selectors, AsyncStorage persistence
- Navigation: File-based Expo Router with proper screen registration
- Code Organization: Clear separation of concerns (stores, components, utilities)
- Styling: Consistent NativeWind usage throughout
Known Limitations (Future Improvements)
- Backend Required: Email/password updates simulate API calls - connect to real backend for production
- Mock Data: Some screens use hardcoded San Francisco coordinates for demo
- Email Tokens: Client-side token generation should be server-side for production security
- Network Detection: No offline mode or network state management yet
Legal
- Terms of Service: Full Terms of Use available at
/terms with detailed sections on:
- Credit system rules and non-monetary value
- User responsibilities and prohibited conduct
- Reliability score mechanics
- Account termination policies
- Liability limitations
- Privacy Policy: Complete Privacy Policy at
/privacy covering:
- Information collection (account, location, usage data)
- Location privacy and when GPS is accessed
- Data sharing policies and what we don’t share
- Data security and encryption
- User rights (access, correction, deletion)
- Data retention periods
- Credits have no cash value and cannot be exchanged for money
- No guarantee of parking spot availability
- Users are responsible for complying with local parking regulations
- Contact: All inquiries should be sent to info@parkingspotswap.com
Production Readiness Guide
This section outlines everything needed to take the app from development to production.
What’s Implemented (Frontend Ready)
| Feature |
Status |
Notes |
| All UI/UX screens |
✅ Complete |
Fully polished mobile interface |
| API Client Layer |
✅ Complete |
src/lib/api/ with JWT auth, error handling |
| Authentication Flow |
✅ Complete |
Login, signup, 2FA verification via API |
| 2FA Setup & QR Code |
✅ Complete |
Real TOTP QR codes for authenticator apps |
| Push Notifications |
✅ Complete |
Registration, handling, deep linking |
| Email Integration |
✅ Complete |
SendGrid configured and sending |
| Stripe Client |
✅ Complete |
Payment method management, Connect flows |
| Local Data Persistence |
✅ Complete |
Zustand + AsyncStorage |
| Vehicle Management |
✅ Complete |
Add/edit/delete vehicles |
| Spam Detection |
✅ Complete |
Activity tracking and scoring |
| Admin Dashboard UI |
✅ Complete |
Full interface built |
What Needs Backend Implementation
The frontend API layer is complete with mock responses for development. To go live, you need to implement the actual backend endpoints.
YOUR TASKS (Owner’s Action Items)
1. Set Environment Variables (Required)
Go to the ENV tab in Vibecode and add these variables:
# Required for production
EXPO_PUBLIC_API_URL=https://your-backend-api.com
# Stripe (already set for test mode, update for production)
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_your_key_here
# Push Notifications (Expo)
# These are set up automatically when you build for production via EAS
2. Build Your Backend API
You need a backend server that implements these endpoints. Here’s what each endpoint should do:
Authentication Endpoints (/api/auth/)
| Endpoint |
Method |
Description |
Request Body |
Response |
/register |
POST |
Create new user |
{name, email, password, referralCode?} |
{user, accessToken, refreshToken} |
/login |
POST |
Sign in user |
{email, password} |
{user, accessToken, refreshToken, requires2FA?, tempToken?} |
/verify-2fa |
POST |
Verify 2FA code |
{tempToken, code, isBackupCode?} |
{user, accessToken, refreshToken} |
/2fa/setup |
POST |
Generate 2FA secret |
- |
{secret, qrCodeUri, backupCodes} |
/2fa/confirm |
POST |
Confirm 2FA setup |
{code} |
{success, backupCodes} |
/2fa/disable |
POST |
Disable 2FA |
{password, code} |
{success} |
/refresh |
POST |
Refresh tokens |
{refreshToken} |
{accessToken, refreshToken} |
/password-reset/request |
POST |
Request reset email |
{email} |
{success, message} |
/password-reset/confirm |
POST |
Reset password |
{token, newPassword} |
{success} |
/change-password |
POST |
Change password |
{currentPassword, newPassword} |
{success} |
/logout |
POST |
Invalidate tokens |
- |
{success} |
User Endpoints (/api/users/)
| Endpoint |
Method |
Description |
/me |
GET |
Get current user profile |
/me |
PATCH |
Update profile (displayName, profileImage) |
/me/email |
POST |
Update email (requires password) |
/me/delete |
POST |
Delete account (requires password) |
Spots Endpoints (/api/spots/)
| Endpoint |
Method |
Description |
/ |
GET |
Search nearby spots (query: lat, lng, radius, limit) |
/ |
POST |
Create/offer a new spot |
/:id |
GET |
Get spot by ID |
/:id/verify |
POST |
Verify spot availability |
/:id/claim |
POST |
Claim a spot |
/:id/report-unavailable |
POST |
Report spot unavailable |
/:id/confirm-parking |
POST |
Confirm successful parking |
/:id/cancel |
POST |
Cancel spot offer |
/mine |
GET |
Get user’s offered spots |
/claimed |
GET |
Get user’s claimed spots |
Hold Requests (/api/hold-requests/)
| Endpoint |
Method |
Description |
/:id/accept |
POST |
Accept hold request |
/:id/decline |
POST |
Decline hold request |
/:id/complete |
POST |
Complete hold (user parked) |
/:id/cancel |
POST |
Cancel hold request |
/:id/extend |
POST |
Request extension |
/mine |
GET |
Get my hold requests |
/incoming |
GET |
Get incoming hold requests |
Referrals (/api/referrals/)
| Endpoint |
Method |
Description |
/validate |
POST |
Validate referral code |
/claim |
POST |
Claim referral bonus |
/my-code |
GET |
Get user’s referral code |
/stats |
GET |
Get referral statistics |
Notifications (/api/notifications/)
| Endpoint |
Method |
Description |
/register |
POST |
Register push token |
/unregister |
POST |
Unregister push token |
/preferences |
GET/PATCH |
Get/update notification preferences |
/history |
GET |
Get notification history |
/:id/read |
POST |
Mark notification read |
/read-all |
POST |
Mark all as read |
Admin (/api/admin/)
| Endpoint |
Method |
Description |
/stats |
GET |
Get platform statistics |
/flagged-users |
GET |
Get flagged users list |
/users/:id |
GET |
Get user details |
/users/:id/clear-restriction |
POST |
Clear user restriction |
/users/:id/suspend |
POST |
Suspend user |
/users/:id/unsuspend |
POST |
Unsuspend user |
/users/:id/role |
POST |
Update user role |
/users/:id/credits |
POST |
Adjust user credits |
/issues |
GET |
Get issue reports |
/issues/:id |
PATCH |
Update issue status |
/audit-log |
GET |
Get audit log |
/users/search |
GET |
Search users |
3. Set Up External Services
Stripe (Payments)
- Create a Stripe account at stripe.com
- Enable Stripe Connect for marketplace payments
- Get your API keys from the Stripe Dashboard
- Set up webhooks at
https://your-api.com/api/stripe/webhooks
- Subscribe to events:
account.updated
payment_intent.succeeded
payment_intent.payment_failed
Backend environment variables needed:
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
Push Notifications (Expo)
- Your Expo project is already configured
- When you build for production via EAS, push notifications work automatically
- For custom server-side sending, use the Expo Push API:
SendGrid (Emails)
Already configured! Make sure these are set in ENV tab:
EXPO_PUBLIC_SENDGRID_API_KEY=SG.xxx
EXPO_PUBLIC_SENDGRID_FROM_EMAIL=noreply@parkingspotswap.com
EXPO_PUBLIC_SENDGRID_FROM_NAME=Parking Spot Swap
4. Database Requirements
Your backend database needs these tables:
-- Users
CREATE TABLE users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
display_name TEXT,
role TEXT DEFAULT 'user',
credits INTEGER DEFAULT 0,
reliability_score INTEGER DEFAULT 100,
referral_code TEXT UNIQUE,
spam_score INTEGER DEFAULT 0,
is_restricted BOOLEAN DEFAULT FALSE,
is_suspended BOOLEAN DEFAULT FALSE,
two_fa_enabled BOOLEAN DEFAULT FALSE,
two_fa_secret TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Spots
CREATE TABLE spots (
id TEXT PRIMARY KEY,
latitude REAL NOT NULL,
longitude REAL NOT NULL,
status TEXT DEFAULT 'pending',
offered_by TEXT REFERENCES users(id),
claimed_by TEXT REFERENCES users(id),
notes TEXT,
expires_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Referrals
CREATE TABLE referrals (
id TEXT PRIMARY KEY,
referrer_id TEXT REFERENCES users(id),
referred_user_id TEXT REFERENCES users(id),
status TEXT DEFAULT 'pending',
credits_awarded INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Push Tokens
CREATE TABLE push_tokens (
id TEXT PRIMARY KEY,
user_id TEXT REFERENCES users(id),
token TEXT NOT NULL,
platform TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Add indexes for performance
CREATE INDEX idx_spots_location ON spots(latitude, longitude);
CREATE INDEX idx_spots_status ON spots(status);
CREATE INDEX idx_users_email ON users(email);
5. Security Checklist
Before going live:
6. Testing Checklist
Before submitting to App Store:
Quick Start for Backend Development
If you’re building the backend yourself, here’s a recommended tech stack:
Option 1: Node.js + Express
npm init -y
npm install express cors bcryptjs jsonwebtoken stripe
Option 2: Firebase
- Use Firebase Auth for authentication
- Use Firestore for database
- Use Cloud Functions for API endpoints
Option 3: Supabase
- Built-in auth with JWT
- PostgreSQL database
- Edge Functions for custom logic
The frontend is already set up to work with any REST API. Just implement the endpoints described above and set EXPO_PUBLIC_API_URL to your server URL.
Summary
| Category |
Status |
Your Action |
| Frontend App |
✅ Complete |
None needed |
| API Client |
✅ Complete |
None needed |
| Push Notifications |
✅ Complete |
Works automatically with EAS builds |
| Email Service |
✅ Complete |
Already configured |
| Supabase Backend |
✅ Connected |
Tables created, API integrated |
| Stripe Connect |
✅ Connected |
ENV variable set |
| Database |
✅ Complete |
Supabase PostgreSQL |
Backend Integration Status (COMPLETED)
The app is now connected to your Supabase backend!
EXPO_PUBLIC_SUPABASE_URL = Your Supabase project URL
EXPO_PUBLIC_SUPABASE_ANON_KEY = Your Supabase anon key
EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY = Your Stripe Connect key
What’s Connected
| Service | Status | Details |
|———|——–|———|
| Authentication | ✅ Live | Supabase Auth (email/password) |
| User Profiles | ✅ Live | Auto-created on signup with referral codes |
| Parking Spots | ✅ Live | Create, search, claim spots |
| Transactions | ✅ Live | Credit tracking with history |
| Referrals | ✅ Live | Validate codes, award credits |
| Hold Requests | ✅ Live | Create, accept, decline, complete |
| Vehicles | ✅ Live | Save and manage vehicles |
Database Tables Created
All tables are set up in your Supabase project:
profiles - User accounts and settings
spots - Parking spot listings
transactions - Credit history
referrals - Referral tracking
hold_requests - Paid hold requests
vehicles - Saved vehicles
push_tokens - Push notification tokens
Row Level Security (RLS)
RLS is enabled on all tables with policies for:
- Users can read/update their own profile
- Anyone can view available spots
- Users can create and update their own spots
- Users can manage their own vehicles and transactions
What’s Ready for Production
- ✅ Users can sign up and sign in
- ✅ Users can offer parking spots
- ✅ Users can find and claim spots
- ✅ Credits are tracked and persisted
- ✅ Referral codes work
- ✅ Hold requests are saved
Demo Mode for App Store Review
The app includes mock parking spots for testing purposes. All users can see and interact with these mock spots to test the app’s functionality.
Mock Spots (Visible to All Users):
| Spot | Type | Status | Hold Available | Description |
|——|——|——–|—————-|————-|
| Demo Alex | Offered | pending | Yes | Within 1-minute hold period - “Request Paid Hold” button visible |
| Demo Sarah | Offered | pending | Yes | Within 1-minute hold period - “Request Paid Hold” button visible |
| Demo Mike | Offered | available | No | Past 1-minute hold period - Navigate only |
| Demo Chris | Pin Drop | available | No | Pin drops never have hold option |
| Demo Jordan | Offered | available | No | Past 1-minute hold period - Navigate only |
Demo Account (For App Store Reviewers):
- Email:
demo@parkingspotswap.com
- Password:
DemoReview2024!
- Demo users get pre-configured mock payment methods and bank accounts for testing the paid hold feature
Demo Mode Features for Demo Account:
- Pre-configured mock Stripe payment method (Visa ending in 4242)
- Pre-configured mock Stripe Connect bank account
- Simulated hold request toggle on the Offer screen
How Hold a Spot Works:
- Offering User Flow: When someone offers their parking spot, a 1-minute countdown starts
- During Countdown (status: pending): Requesting users who select this spot see the “Request Paid Hold ($2/min)” button
- After Countdown (status: available): The spot becomes generally available; Hold is no longer an option
- Payment Requirement: Both parties must have payment set up - requester needs a card, holder needs a bank account
Demo Disclaimer Banner:
- When navigating to a demo spot, a blue info banner explains the Hold a Spot feature
- Different explanations shown for
pending spots (Hold available) vs available spots (Hold expired)
- This helps reviewers understand the time-sensitive nature of the Hold feature
Production Status - ALL COMPLETE ✅
1. Stripe Production Setup ✅
| Component | Status | Details |
|———–|——–|———|
| Publishable Key | ✅ LIVE | pk_live_51SjWVt... |
| Secret Key | ✅ LIVE | sk_live_51SjWVt... |
| Webhook | ✅ Active | playful-jubilee endpoint configured |
| Edge Functions | ✅ Deployed | All Stripe functions deployed to Supabase |
| Secrets | ✅ Set | STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET configured |
Real card payments are now enabled in production.
2. Email System ✅
| Component | Status | Details |
|———–|——–|———|
| SendGrid API | ✅ Connected | API key configured |
| Domain Auth | ✅ Verified | em9522.parkingspotswap.com |
| Link Branding | ✅ Verified | url6643.parkingspotswap.com |
| Sender Email | ✅ Ready | no-reply@parkingspotswap.com |
| Supabase Template | ✅ Correct | Uses `` |
Email confirmations are working in production.
3. Database & Auth ✅
| Component | Status |
|———–|——–|
| Supabase Connection | ✅ Connected |
| User Authentication | ✅ Working |
| RLS Policies | ✅ Enabled |
| All Tables | ✅ Created |
4. Ready for App Store
- Click “Share” on top right in Vibecode App → “Submit to App Store”
Production Checklist - COMPLETE