FreelancerPay402 - DevOps & Infrastructure Guide
Architecture, Security, Scalability, and Deployment Documentation
Architecture Overview
Environment Strategy
Infrastructure Setup
Security Best Practices
Deployment Pipeline
Scalability Strategy
Monitoring & Observability
Disaster Recovery
Cost Optimization
┌─────────────────────────────────────┐
│ CLOUDFLARE │
│ (DDoS Protection, WAF, CDN) │
└─────────────────┬───────────────────┘
│
┌─────────────────────────────────┼─────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ VERCEL │ │ VERCEL │ │ VERCEL │
│ (Frontend) │ │ (API Routes) │ │ (Webhooks) │
│ lancepay.com │ │ /api/* │ │ /api/webhooks │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
│ │ │
└─────────────────────────────────┼─────────────────────────────────┘
│
┌────────────────────────────────┼────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ NEON │ │ UPSTASH │ │ ALCHEMY │
│ (PostgreSQL) │ │ (Redis) │ │ (Base RPC) │
│ Serverless │ │ Rate Limiting │ │ Blockchain │
└─────────────────┘ │ Session Cache │ └─────────────────┘
└─────────────────┘
┌────────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
├─────────────────┬────────────────┬────────────────┬────────────┤
│ PRIVY │ MOONPAY │ YELLOW CARD │ RESEND │
│ (Google Auth + │ (Onramp) │ (Offramp) │ (Email) │
│ Embedded Wallet)│ │ │ │
└─────────────────┴────────────────┴────────────────┴────────────┘
Layer
Technology
Why
Frontend + API
Vercel (Next.js)
Edge functions, auto-scaling, zero config
Database
Neon (PostgreSQL)
True serverless, branching, global edge
Auth + Wallets
Privy
Google OAuth + embedded wallets in one SDK
Cache/Rate Limit
Upstash Redis
Serverless, pay-per-request
Blockchain RPC
Alchemy
Reliable, enhanced APIs, webhooks
CDN/Security
Cloudflare
DDoS protection, WAF, caching
Secrets
Vercel Environment Variables
Encrypted, per-environment
Monitoring
Sentry + Vercel Analytics
Errors + performance
Email
Resend
Developer-friendly, reliable
Aspect
Neon
Supabase
Serverless
✅ True serverless, scales to zero
⚠️ Pauses on free tier
DB Branching
✅ Branch per PR (great for testing)
❌ Not available
Cold Starts
✅ Faster cold starts
⚠️ Slower wake-up
Built-in Auth
❌ (we use Privy anyway)
✅ (not needed)
Focus
Pure database, no bloat
Full platform
Why Privy for Auth + Wallets?
Privy handles both authentication AND embedded wallet creation in one integration:
User clicks "Sign in with Google"
│
▼
Privy handles Google OAuth
│
▼
Embedded wallet auto-created
(tied to Google account)
│
▼
User logged in + has wallet
(never sees crypto complexity)
No need for separate auth provider. One SDK does both.
Environment
Purpose
Database
External APIs
Development
Local dev, feature branches
Neon dev branch
Sandbox/Mock
Staging
Pre-production testing
Neon staging branch
Sandbox APIs
Production
Live users
Neon main branch
Production APIs
main (production)
│
├── staging (auto-deploys to staging.lancepay.com)
│ │
│ └── feature/xyz (PR preview deployments + Neon DB branch)
│
└── hotfix/abc (emergency fixes → main)
Environment Variables Structure
# .env.local (development)
# .env.staging (staging - in Vercel)
# .env.production (production - in Vercel)
# ═══════════════════════════════════════════════════════════════
# APPLICATION
# ═══════════════════════════════════════════════════════════════
NEXT_PUBLIC_APP_URL=https://lancepay.com
NEXT_PUBLIC_APP_ENV=production # development | staging | production
# ═══════════════════════════════════════════════════════════════
# DATABASE (Neon)
# ═══════════════════════════════════════════════════════════════
DATABASE_URL=" postgresql://user:pass@ep-xxx.us-east-1.aws.neon.tech/lancepay?sslmode=require"
# ═══════════════════════════════════════════════════════════════
# PRIVY (Google Auth + Embedded Wallets)
# ═══════════════════════════════════════════════════════════════
NEXT_PUBLIC_PRIVY_APP_ID=your-privy-app-id
PRIVY_APP_SECRET=your-privy-app-secret
PRIVY_WEBHOOK_SECRET=your-webhook-secret
# ═══════════════════════════════════════════════════════════════
# BLOCKCHAIN
# ═══════════════════════════════════════════════════════════════
ALCHEMY_API_KEY=your-alchemy-key
NEXT_PUBLIC_BASE_RPC_URL=https://base-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}
USDC_CONTRACT_ADDRESS=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
# ═══════════════════════════════════════════════════════════════
# ONRAMP (MoonPay)
# ═══════════════════════════════════════════════════════════════
MOONPAY_API_KEY=pk_live_xxx
MOONPAY_SECRET_KEY=sk_live_xxx
MOONPAY_WEBHOOK_SECRET=whsec_xxx
# ═══════════════════════════════════════════════════════════════
# OFFRAMP (Yellow Card)
# ═══════════════════════════════════════════════════════════════
YELLOWCARD_API_KEY=your-api-key
YELLOWCARD_SECRET_KEY=your-secret-key
YELLOWCARD_WEBHOOK_SECRET=your-webhook-secret
# ═══════════════════════════════════════════════════════════════
# CACHE (Upstash Redis)
# ═══════════════════════════════════════════════════════════════
UPSTASH_REDIS_REST_URL=https://xxx.upstash.io
UPSTASH_REDIS_REST_TOKEN=xxx
# ═══════════════════════════════════════════════════════════════
# EMAIL (Resend)
# ═══════════════════════════════════════════════════════════════
RESEND_API_KEY=re_xxx
# ═══════════════════════════════════════════════════════════════
# MONITORING
# ═══════════════════════════════════════════════════════════════
SENTRY_DSN=https://xxx@sentry.io/xxx
SENTRY_AUTH_TOKEN=xxx
Vercel Project Configuration
// vercel.json
{
"framework" : " nextjs" ,
"regions" : [" iad1" , " lhr1" ],
"headers" : [
{
"source" : " /api/(.*)" ,
"headers" : [
{ "key" : " X-Content-Type-Options" , "value" : " nosniff" },
{ "key" : " X-Frame-Options" , "value" : " DENY" },
{ "key" : " X-XSS-Protection" , "value" : " 1; mode=block" }
]
}
],
"rewrites" : [
{
"source" : " /pay/:invoiceId" ,
"destination" : " /pay/[invoiceId]"
}
]
}
# 1. Create project at neon.tech
# 2. Copy connection string
# 3. Initialize Prisma
npx prisma init
# 4. Run migrations
npx prisma migrate dev --name init
// lib/db.ts
import { PrismaClient } from '@prisma/client'
import { neonConfig } from '@neondatabase/serverless'
// Enable connection pooling for serverless
neonConfig . fetchConnectionCache = true
const globalForPrisma = global as unknown as { prisma : PrismaClient }
export const prisma =
globalForPrisma . prisma ||
new PrismaClient ( {
log : process . env . NODE_ENV === 'development' ? [ 'query' ] : [ ] ,
} )
if ( process . env . NODE_ENV !== 'production' ) globalForPrisma . prisma = prisma
Privy Setup (Google Auth + Embedded Wallets)
// app/providers.tsx
'use client' ;
import { PrivyProvider } from '@privy-io/react-auth' ;
export function Providers ( { children } : { children : React . ReactNode } ) {
return (
< PrivyProvider
appId = { process . env . NEXT_PUBLIC_PRIVY_APP_ID ! }
config = { {
// Auth methods
loginMethods : [ 'google' , 'email' ] ,
// Appearance
appearance : {
theme : 'light' ,
accentColor : '#111827' ,
logo : '/logo.png' ,
} ,
// Embedded wallets - auto-create on login
embeddedWallets : {
createOnLogin : 'all-users' ,
noPromptOnSignature : true ,
} ,
// Default chain
defaultChain : base ,
supportedChains : [ base ] ,
} }
>
{ children }
</ PrivyProvider >
) ;
}
// components/AuthButton.tsx
'use client' ;
import { usePrivy } from '@privy-io/react-auth' ;
export function AuthButton ( ) {
const { login, logout, authenticated, user } = usePrivy ( ) ;
if ( authenticated ) {
// User is logged in AND has embedded wallet
const walletAddress = user ?. wallet ?. address ;
return (
< div >
< span > Welcome, { user ?. google ?. name || user ?. email ?. address } </ span >
< button onClick = { logout } > Sign Out</ button >
</ div >
) ;
}
return (
< button onClick = { login } >
Sign in with Google
</ button >
) ;
}
// lib/redis.ts
import { Redis } from '@upstash/redis'
export const redis = new Redis ( {
url : process . env . UPSTASH_REDIS_REST_URL ! ,
token : process . env . UPSTASH_REDIS_REST_TOKEN ! ,
} )
// Rate limiting
export async function rateLimit ( ip : string , limit = 10 , window = 60 ) {
const key = `rate_limit:${ ip } `
const current = await redis . incr ( key )
if ( current === 1 ) {
await redis . expire ( key , window )
}
return current <= limit
}
4. Security Best Practices
Authentication with Privy
Privy handles auth securely. You verify the session server-side:
// lib/auth.ts
import { PrivyClient } from '@privy-io/server-auth' ;
const privy = new PrivyClient (
process . env . NEXT_PUBLIC_PRIVY_APP_ID ! ,
process . env . PRIVY_APP_SECRET !
) ;
export async function verifyAuth ( authToken : string ) {
try {
const claims = await privy . verifyAuthToken ( authToken ) ;
return { userId : claims . userId , walletAddress : claims . walletAddress } ;
} catch {
return null ;
}
}
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
const PUBLIC_PATHS = [ '/api/pay' , '/api/webhooks' , '/api/health' ]
export async function middleware ( request : NextRequest ) {
const path = request . nextUrl . pathname
// Allow public paths
if ( PUBLIC_PATHS . some ( p => path . startsWith ( p ) ) ) {
return NextResponse . next ( )
}
// Check for Privy auth token
const authToken = request . headers . get ( 'authorization' ) ?. replace ( 'Bearer ' , '' )
if ( ! authToken ) {
return NextResponse . json ( { error : 'Unauthorized' } , { status : 401 } )
}
// Token verification happens in the route handler
return NextResponse . next ( )
}
export const config = {
matcher : '/api/:path*' ,
}
// app/api/webhooks/privy/route.ts
import { headers } from 'next/headers'
import crypto from 'crypto'
export async function POST ( request : Request ) {
const body = await request . text ( )
const signature = headers ( ) . get ( 'privy-signature' )
// Verify webhook signature
const expectedSignature = crypto
. createHmac ( 'sha256' , process . env . PRIVY_WEBHOOK_SECRET ! )
. update ( body )
. digest ( 'hex' )
if ( signature !== expectedSignature ) {
return Response . json ( { error : 'Invalid signature' } , { status : 401 } )
}
const event = JSON . parse ( body )
// Handle user.created - store wallet address
if ( event . type === 'user.created' ) {
const { id, wallet } = event . data
await prisma . user . create ( {
data : {
privyId : id ,
walletAddress : wallet ?. address ,
}
} )
}
return Response . json ( { received : true } )
}
Security Measure
Implementation
Authentication
Privy (Google OAuth + session tokens)
Rate Limiting
Upstash Redis (10 req/min for auth, 100/min for API)
Input Validation
Zod schemas on all inputs
SQL Injection
Parameterized queries (Prisma)
XSS
React auto-escapes, CSP headers
Secrets
Vercel encrypted env vars
HTTPS
Enforced by Vercel
Webhook Auth
HMAC signature verification
# .github/workflows/ci.yml
name : CI/CD Pipeline
on :
push :
branches : [main, staging]
pull_request :
branches : [main, staging]
jobs :
lint-and-test :
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v4
- name : Setup Node.js
uses : actions/setup-node@v4
with :
node-version : ' 20'
cache : ' npm'
- name : Install dependencies
run : npm ci
- name : Run linter
run : npm run lint
- name : Run type check
run : npm run type-check
- name : Run tests
run : npm run test
env :
DATABASE_URL : ${{ secrets.TEST_DATABASE_URL }}
deploy-production :
if : github.ref == 'refs/heads/main'
needs : [lint-and-test]
runs-on : ubuntu-latest
environment : production
steps :
- uses : actions/checkout@v4
- name : Deploy to Vercel
uses : amondnet/vercel-action@v25
with :
vercel-token : ${{ secrets.VERCEL_TOKEN }}
vercel-org-id : ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id : ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args : ' --prod'
Database Migrations with Neon Branching
# .github/workflows/preview.yml
name : Preview Deployment
on :
pull_request :
types : [opened, synchronize]
jobs :
create-neon-branch :
runs-on : ubuntu-latest
steps :
- uses : neondatabase/create-branch-action@v4
with :
project_id : ${{ secrets.NEON_PROJECT_ID }}
branch_name : preview-${{ github.event.pull_request.number }}
api_key : ${{ secrets.NEON_API_KEY }}
id : branch
- name : Set preview DATABASE_URL
run : echo "DATABASE_URL=${{ steps.branch.outputs.db_url }}" >> $GITHUB_ENV
Vercel handles scaling automatically:
Edge Functions : Run in 30+ regions globally
Serverless Functions : Scale to zero, burst to 1000s
No cold starts for Edge Runtime
Traffic Level
Neon Tier
Features
MVP (< 1K users)
Free
0.5 GB storage, auto-suspend
Growth (< 10K users)
Launch ($19/mo)
10 GB storage, no suspend
Scale (< 100K users)
Scale ($69/mo)
Autoscaling, read replicas
Enterprise
Custom
Dedicated resources
// Cache frequently accessed data
export async function getExchangeRate ( ) {
const cached = await redis . get ( 'exchange_rate:USD_NGN' )
if ( cached ) return JSON . parse ( cached )
const rate = await fetchFromYellowCard ( )
await redis . setex ( 'exchange_rate:USD_NGN' , 300 , JSON . stringify ( rate ) )
return rate
}
7. Monitoring & Observability
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'
Sentry . init ( {
dsn : process . env . SENTRY_DSN ,
environment : process . env . NEXT_PUBLIC_APP_ENV ,
tracesSampleRate : 0.1 ,
} )
// app/api/health/route.ts
export async function GET ( ) {
const checks = {
database : await checkDatabase ( ) ,
redis : await checkRedis ( ) ,
blockchain : await checkBlockchainRPC ( ) ,
}
const healthy = Object . values ( checks ) . every ( c => c . status === 'ok' )
return Response . json ( checks , { status : healthy ? 200 : 503 } )
}
Data
Backup Frequency
Retention
Method
PostgreSQL
Continuous
7 days (PITR)
Neon automatic
Configs
On change
Unlimited
Git
Secrets
On change
N/A
Vercel encrypted
# 1. Database restore (Neon)
# Use Neon console → Branches → Restore to point in time
# 2. Rollback deployment (Vercel)
vercel rollback [deployment-url]
Estimated Monthly Costs (MVP → Scale)
Service
MVP (< 1K users)
Growth (< 10K)
Scale (< 100K)
Vercel
Free
$20/mo
$150/mo
Neon
Free
$19/mo
$69/mo
Privy
Free (< 1K MAU)
$99/mo
$299/mo
Upstash
Free
$10/mo
$50/mo
Alchemy
Free
Free
$49/mo
Sentry
Free
Free
$26/mo
Cloudflare
Free
Free
$20/mo
Resend
Free
$20/mo
$50/mo
Total
$0
~$170/mo
~$715/mo
Week 3: Production Readiness
Document Version: 2.0
Last Updated: December 2024
Author: FreelancerPay402 DevOps