Security
PayForge implements defense-in-depth security controls covering encryption, authentication, authorization, and audit logging — aligned with PCI DSS 4.0 requirements.
Authentication
PayForge uses JWT-based authentication with short-lived tokens. All API requests require a Bearer token in the Authorization header.
JWT Tokens
Stateless tokens signed with RS256. Configurable expiration with automatic refresh. Revocation via server-side denylist.
API Keys
Long-lived keys for server-to-server integration. Hashed with bcrypt before storage — plaintext keys are never stored.
Multi-Factor Auth
TOTP-based MFA for dashboard access. Required for sensitive operations like API key management and user administration.
Session Management
JWT revocation via denylist on sign-out. Automatic token expiry. Account lockout after 5 failed login attempts.
Encryption
Sensitive data is encrypted at multiple layers using industry-standard algorithms. Watch the encryption pipeline in action:
| Layer | Method | Protects |
|---|---|---|
| Transport | TLS 1.3 | All API traffic in transit |
| Application | Lockbox (AES-256-GCM) | PII fields: email, card data, webhook URLs |
| Search | Blind Index (HMAC) | Searchable encrypted field lookups |
| Database | PostgreSQL encryption | All data at rest on disk |
| Secrets | Rails encrypted credentials | API keys, signing keys, master keys |
class Merchant < ApplicationRecord
# PII encrypted with AES-256-GCM using Lockbox
has_encrypted :email
blind_index :email # Enables searching encrypted fields
# Webhook URL also encrypted (contains merchant secrets)
has_encrypted :webhook_url
end
# Transparent usage — encryption/decryption is automatic
merchant = Merchant.find_by(email: "merchant@example.com")
# → Queries blind_index column, decrypts email on readAuthorization
Role-based access control is enforced at every endpoint using Action Policy. All resources are automatically scoped to the authenticated merchant — there is no way to access another merchant's data.
| Role | Permissions |
|---|---|
| Viewer | Read transactions, view analytics and dashboards |
| Manager | All viewer + process payments, manage webhooks, review fraud cases |
| Admin | All manager + manage API keys, users, merchant settings |
| Super Admin | All admin + manage merchants, system configuration, full CRUD |
Rate Limiting
API requests are rate-limited using Rack::Attack with a sliding window counter to prevent abuse and ensure fair usage:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1709726400Security Headers
All responses include security headers configured via secure_headers:
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 0
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()Audit Trail
Every mutation is tracked with PaperTrail versioning and domain events. The audit trail captures:
Integration Best Practices
Use test keys in development
Test keys (sk_test_...) work identically to live keys but don't process real payments. Never use production keys in development.
Verify webhook signatures
Always verify the X-PayForge-Signature header before processing webhook events. See the Webhooks documentation for implementation examples.
Use idempotency keys
Include X-Idempotency-Key on all POST requests to prevent duplicate processing during retries or network issues.
Store keys securely
Use environment variables or a secrets manager. Never commit API keys to source control. Rotate keys regularly.
Implement exponential backoff
When rate-limited (429), back off exponentially. Start with 1 second and double on each retry, up to a maximum of 30 seconds.