A GPS-first Progressive Web App for registering Bitcoin-accepting merchants to BTCMap and OpenStreetMap. Built for circular economy organizations with ODK Collect-level GPS precision.
🌍 Open Source & Community-Deployable - Any Bitcoin circular economy can deploy their own instance with custom branding, validators, and regional settings.
This app is designed for Bitcoin circular economies worldwide to map their merchant networks:
- 🇸🇻 Bitcoin Beach (El Salvador) - Map merchants accepting Bitcoin in El Zonte
- 🇳🇱 Arnhem Bitcoin City (Netherlands) - Track Bitcoin adoption across Arnhem
- 🇨🇷 Bitcoin Jungle (Costa Rica) - Document merchants in Uvita and beyond
- 🇨🇭 Bitcoin Lake (Switzerland) - Map Bitcoin acceptance around Lake Lugano
- Your Community - Deploy your own instance in minutes
Each deployment is fully independent with:
- Custom organization ID and branding
- Separate user management and role assignments
- Regional default locations
- Community-specific OSM OAuth credentials
- Independent database and configuration
- High-precision location capture with weighted averaging and outlier filtering
- Sub-5 meter accuracy with stability detection
- Multiple GPS readings averaged for reliability
- Works with both GPS and network location providers
- Full support for Lightning addresses
- On-chain and contactless payment options
- BTCMap-compatible tags auto-generated
currency:XBT=yesandpayment:bitcoin=yestags
- Login with your OpenStreetMap account
- Create nodes directly in OSM
- Proper changeset management
- Automatic tag generation
- Automatic sync verification
- Check if merchants appear on BTCMap
- Nearby merchant discovery
- Status tracking dashboard
- Works offline with service worker caching
- Install to home screen
- Background sync for offline submissions
- Push notification ready
- GPS-based proximity verification
- Validators must physically visit locations
- Photo evidence support
- Multi-step approval workflow
- Node.js 18+
- npm or yarn
- OpenStreetMap account (for publishing)
# Clone the repository
git clone https://github.com/your-org/btcreg.git
cd btcreg/btcmap-merchant-app
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env.local
# Initialize the database
npx prisma db push
# Start development server
npm run devCreate a .env.local file with:
# Database
DATABASE_URL="file:./dev.db"
# OSM OAuth2 (register at https://www.openstreetmap.org/oauth2/applications)
OSM_CLIENT_ID="your-client-id"
OSM_CLIENT_SECRET="your-client-secret"
OSM_REDIRECT_URI="http://localhost:3000/api/auth/osm/callback"
# App
NEXT_PUBLIC_APP_URL="http://localhost:3000"
SESSION_SECRET="your-random-32-char-secret"- Go to OSM OAuth2 Applications
- Create a new application
- Set redirect URI to
http://localhost:3000/api/auth/osm/callback - Request scopes:
read_prefs,write_api - Copy client ID and secret to
.env.local
btcmap-merchant-app/
├── prisma/
│ └── schema.prisma # Database schema
├── public/
│ ├── manifest.json # PWA manifest
│ ├── sw.js # Service worker
│ └── icons/ # App icons
├── src/
│ ├── app/ # Next.js App Router pages
│ │ ├── api/ # API routes
│ │ │ ├── auth/ # OSM OAuth endpoints
│ │ │ ├── btcmap/ # BTCMap integration
│ │ │ ├── merchants/ # Merchant CRUD
│ │ │ ├── osm/ # OSM publishing
│ │ │ └── verify/ # Verification API
│ │ ├── login/ # Login page
│ │ ├── register/ # Merchant registration
│ │ ├── status/ # Status dashboard
│ │ └── verify/ # Verification flow
│ ├── components/
│ │ ├── forms/ # Form components
│ │ ├── gps/ # GPS components
│ │ ├── pwa/ # PWA components
│ │ └── ui/ # UI primitives
│ ├── hooks/ # React hooks
│ ├── lib/
│ │ ├── btcmap/ # BTCMap API client
│ │ ├── offline/ # Offline storage
│ │ └── osm/ # OSM API client
│ └── types/ # TypeScript types
- GPS Capture - High-precision location with averaging
- Business Details - Name, category, contact info
- Payment Methods - Bitcoin, Lightning, contactless
- Photo Evidence - Storefront and signage
- Submit - Saved locally and queued for sync
- Select Merchant - From pending queue
- Navigate to Location - GPS proximity check
- Verify Details - Confirm business exists
- Confirm Bitcoin - Verify acceptance
- Submit Verification - Update status
- Admin Login - OSM OAuth2 authentication
- Review Merchant - Verified submissions only
- Publish to OSM - Create node with tags
- BTCMap Sync - Automatic indexing (up to 24h)
GET /api/auth/osm- Initiate OAuth loginGET /api/auth/osm/callback- OAuth callbackGET /api/auth/session- Get current sessionPOST /api/auth/logout- Clear session
GET /api/merchants- List merchantsPOST /api/merchants- Create merchantGET /api/merchants/[id]- Get merchantPATCH /api/merchants/[id]- Update merchant
GET /api/verify/[id]- Get merchant for verificationPOST /api/verify/[id]- Submit verification
POST /api/osm/publish- Publish to OpenStreetMap
GET /api/btcmap/check/[id]- Check BTCMap statusGET /api/btcmap/nearby- Find nearby merchants
- Framework: Next.js 16 with App Router
- Language: TypeScript
- Database: Prisma + SQLite (dev) / PostgreSQL (prod)
- Styling: Tailwind CSS
- Icons: Lucide React
- Maps: Leaflet
- PWA: Custom service worker
- Offline: IndexedDB
# Run development server
npm run dev
# Type checking
npm run type-check
# Linting
npm run lint
# Build for production
npm run build
# Start production server
npm start
# Database commands
npx prisma studio # GUI for database
npx prisma db push # Push schema changes
npx prisma generate # Regenerate clientBTCReg is designed for multi-tenant deployment where each Bitcoin circular economy runs their own independent instance. No shared infrastructure, no central control.
REGISTRAR (Field Agents)
- Submit new merchant registrations
- Capture GPS coordinates and photos
- Default role for new users
VALIDATOR (Verifiers)
- Review pending submissions
- Approve or reject merchants
- Publish verified merchants to OpenStreetMap
- Includes all REGISTRAR permissions
ADMIN (Community Owner)
- Configure organization settings
- Manage user roles
- Set validators and regional defaults
- Includes all VALIDATOR permissions
- Deploy the app (Vercel/Docker/self-hosted)
- Configure .env.local with your community details
- First login via OSM → automatically becomes ADMIN
- Set up validators at
/admin/users- promote trusted users - Configure organization at
/admin/config- branding, region, rules - Invite field agents - they register via OSM and get REGISTRAR role
git clone https://github.com/your-org/btcreg.git
cd btcreg/btcmap-merchant-app
npm installCopy .env.example to .env.local and configure:
# Your community identifier (lowercase, no spaces)
ORGANIZATION_ID="bitcoin-beach"
ORGANIZATION_NAME="Bitcoin Beach"
# Your region's center point (from OpenStreetMap)
DEFAULT_LATITUDE="13.3560"
DEFAULT_LONGITUDE="-89.0330"
DEFAULT_RADIUS_KM="25"
# OSM OAuth2 (register at https://www.openstreetmap.org/oauth2/applications)
OSM_CLIENT_ID="your-osm-client-id"
OSM_CLIENT_SECRET="your-osm-client-secret"
NEXT_PUBLIC_OSM_REDIRECT_URI="https://merchants.bitcoinbeach.org/api/auth/osm/callback"
# Your deployed URL
NEXT_PUBLIC_APP_URL="https://merchants.bitcoinbeach.org"
# Generate with: openssl rand -base64 32
SESSION_SECRET="your-secure-random-string"
# Database (choose one)
DATABASE_URL="postgresql://user:pass@host:5432/btcreg"
# Or Turso: DATABASE_URL="libsql://your-db.turso.io?authToken=token"
# Optional: Your BTCMap community page
BTCMAP_COMMUNITY="bitcoin-beach-el-zonte"
# Workflow
VERIFICATION_REQUIRED="true"
AUTO_PUBLISH="false"Visit OSM OAuth2 Applications
- Name:
[Your Community] Merchant Registration - Redirect URI:
https://merchants.yourdomain.org/api/auth/osm/callback - Scopes:
read_prefs,write_api
Copy the Client ID and Secret to your .env.local
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Add environment variables in Vercel dashboard
# Settings → Environment Variables → paste your .env.local values
# Deploy production
vercel --prod# Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
COPY prisma ./prisma/
RUN npm ci
COPY . .
RUN npx prisma generate
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["npm", "start"]# Build and run
docker build -t btcreg .
docker run -p 3000:3000 --env-file .env.local btcreg# Install Node.js 18+
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs
# Clone and setup
git clone https://github.com/your-org/btcreg.git
cd btcreg/btcmap-merchant-app
npm install
# Configure environment
cp .env.example .env.local
nano .env.local # Edit with your values
# Build
npm run build
# Run with PM2 (process manager)
sudo npm install -g pm2
pm2 start npm --name "btcreg" -- start
pm2 save
pm2 startup
# Setup reverse proxy (nginx/caddy)
# Point your domain to localhost:3000Visit your deployed app and click "Login with OpenStreetMap". The first user to login automatically gets ADMIN role.
- Login as admin
- Go to
/admin/users - Find trusted community members
- Click "Change Role" → Select "VALIDATOR"
Validators can now:
- Review pending merchants at
/verify - Approve/reject submissions
- Publish to OpenStreetMap at
/status
Visit /admin/config to customize:
- Organization name and branding
- Logo and primary color
- Default region center point
- GPS precision requirements
- Auto-publish rules
- BTCMap community linkage
SQLite (Development)
DATABASE_URL="file:./dev.db"PostgreSQL (Production - Recommended)
DATABASE_URL="postgresql://user:password@host:5432/btcreg?sslmode=require"Turso (Serverless SQLite - Low Cost)
# Install Turso CLI
curl -sSfL https://get.tur.so/install.sh | bash
# Create database
turso db create btcreg
# Get connection URL
turso db show btcreg --url
turso db tokens create btcreg
# Add to .env.local
DATABASE_URL="libsql://your-db.turso.io?authToken=your-token"Supabase / Neon / PlanetScale also work with PostgreSQL connection string.
Development: Local filesystem (automatic)
Production (choose one):
# Vercel Blob (included with Vercel)
BLOB_READ_WRITE_TOKEN="vercel_blob_token"
# Cloudflare R2 (free egress)
R2_ACCESS_KEY_ID="your-key"
R2_SECRET_ACCESS_KEY="your-secret"
R2_BUCKET_NAME="btcreg-photos"
R2_ENDPOINT="https://account-id.r2.cloudflarestorage.com"
# AWS S3
AWS_ACCESS_KEY_ID="your-key"
AWS_SECRET_ACCESS_KEY="your-secret"
AWS_REGION="us-east-1"
AWS_BUCKET_NAME="btcreg-photos"npm install -g vercel
vercelFROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["npm", "start"]DATABASE_URL="postgresql://..."
OSM_CLIENT_ID="prod-client-id"
OSM_CLIENT_SECRET="prod-client-secret"
OSM_REDIRECT_URI="https://yourdomain.com/api/auth/osm/callback"
NEXT_PUBLIC_APP_URL="https://yourdomain.com"
SESSION_SECRET="production-secret-32-chars"- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
MIT License - see LICENSE for details.
Built for the Bitcoin circular economy community.
- BTCMap - Bitcoin merchant mapping
- OpenStreetMap - Open geographic data
- Next.js - React framework