This document outlines the essential API endpoints for PattPay's MVP backend implementation. The API serves a Web3 payment gateway built on Solana, enabling recurring payments and subscription management.
- Content Creators: Manage fan subscriptions
- Web3 SaaS Companies: Automate billing cycles
- DAOs & Projects: Collect recurring contributions
- Freelancers: Create payment links for services
PattPay supports two authentication methods that are mutually exclusive:
- Traditional: Email/Password authentication
- Web3: Solana wallet (Phantom) authentication
Users must choose one method during registration and cannot switch between them.
POST /api/auth/register
POST /api/auth/login
POST /api/auth/solana-signin-data
POST /api/auth/solana-verify
GET /api/auth/meUser Registration (Email/Password)
POST /api/auth/register
Content-Type: application/json
{
"authMethod": "email_password",
"email": "user@example.com",
"password": "securePassword123",
"name": "John Doe"
}
Response: {
"user": {
"id": "uuid",
"email": "user@example.com",
"name": "John Doe",
"authMethod": "email_password",
"createdAt": "ISO8601"
},
"token": "jwt_token"
}User Login (Email/Password)
POST /api/auth/login
Content-Type: application/json
{
"authMethod": "email_password",
"email": "user@example.com",
"password": "securePassword123"
}
Response: {
"user": User,
"token": "jwt_token"
}Get Sign-In Data
POST /api/auth/solana-signin-data
Content-Type: application/json
{
"walletAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
}
Response: {
"signInData": {
"domain": "pattpay.com",
"statement": "Please sign this message to authenticate with PattPay.",
"version": "1",
"nonce": "random_nonce_string",
"chainId": "mainnet",
"issuedAt": "2025-01-18T10:30:00.000Z",
"resources": ["https://pattpay.com"]
}
}Verify Sign-In Output
POST /api/auth/solana-verify
Content-Type: application/json
{
"signInData": {
"domain": "pattpay.com",
"statement": "Please sign this message to authenticate with PattPay.",
"version": "1",
"nonce": "random_nonce_string",
"chainId": "mainnet",
"issuedAt": "2025-01-18T10:30:00.000Z",
"resources": ["https://pattpay.com"]
},
"signInOutput": {
"account": {
"address": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
"publicKey": "base64_encoded_public_key"
},
"signature": "base64_encoded_signature"
},
"name": "John Doe" // Optional, for first-time users
}
Response: {
"user": {
"id": "uuid",
"walletAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
"name": "John Doe",
"authMethod": "solana_wallet",
"createdAt": "ISO8601"
},
"token": "jwt_token"
}Get Current User
GET /api/auth/me
Authorization: Bearer <token>
Response: {
"user": {
"id": "uuid",
"email": "user@example.com", // Only for email_password users
"walletAddress": "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", // Only for solana_wallet users
"name": "John Doe",
"authMethod": "email_password" | "solana_wallet",
"createdAt": "ISO8601"
}
}- User fills registration form with email/password
- Backend creates user with
authMethod: "email_password" - User logs in with email/password
- Backend verifies credentials and issues JWT
- User connects Phantom wallet
- Frontend requests sign-in data from
/api/auth/solana-signin-data - User signs SIWS message with wallet using
wallet.signIn(signInData) - Frontend sends
signInDataandsignInOutputto/api/auth/solana-verify - Backend verifies using
verifySignIn(signInData, signInOutput)and issues JWT
- Users cannot authenticate with different method than their registration method
- Solana signatures are verified using
@solana/wallet-standard-utilwith SIWS standard - Passwords are hashed using bcrypt with salt rounds
- JWT tokens expire after 24 hours
- Rate limiting: 5 attempts/minute per IP for auth endpoints
- SIWS nonce prevents replay attacks
- Domain validation ensures requests come from authorized sources
GET /api/links
POST /api/links
GET /api/links/:id
PUT /api/links/:id
DELETE /api/links/:idList Payment Links
GET /api/links?page=1&limit=20&status=active&isRecurring=all
Authorization: Bearer <token>
Query Parameters:
- page: number (default: 1)
- limit: number (default: 20, max: 100)
- status: 'active' | 'inactive' | 'all'
- isRecurring: boolean | 'all'
- search: string (name or URL)
- datePreset: 'last-7-days' | 'last-30-days' | 'last-90-days' | 'custom'
Response: {
"links": CheckoutLink[],
"pagination": {
"page": number,
"limit": number,
"total": number,
"totalPages": number
},
"stats": {
"totalActive": number,
"totalCreated": number,
"averageConversion": number,
"totalRevenue": number,
"totalRevenueUSD": number
}
}Create Payment Link
POST /api/links
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "string",
"description": "string?",
"amount": number, // SOL
"amountUSD": number,
"isRecurring": boolean,
"redirectUrl": "string?",
"expiresAt": "ISO8601?",
"maxUses": number?
}
Response: {
"link": CheckoutLink,
"url": "string" // Full checkout URL
}Update Payment Link
PUT /api/links/:id
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "string?",
"description": "string?",
"amount": number?,
"amountUSD": number?,
"status": "active" | "inactive",
"redirectUrl": "string?",
"expiresAt": "ISO8601?",
"maxUses": number?
}
Response: {
"link": CheckoutLink
}Delete Payment Link
DELETE /api/links/:id
Authorization: Bearer <token>
Response: {
"success": true,
"message": "Link deleted successfully"
}PattPay supports two payment flows:
- Recurring Subscriptions -
POST /api/subscribe - One-Time Payments -
POST /api/payment-executions
📖 For detailed implementation guide, see PAYMENT_FLOWS.md
Recurring Subscription:
POST /api/subscribe (Public endpoint)One-Time Payment:
POST /api/payment-executions (Public endpoint)View Payment History (Merchant only):
GET /api/payment-executions (Authenticated)
GET /api/payment-executions/:id (Authenticated)GET /api/payments
GET /api/payments/:idList Payments
GET /api/payments?page=1&limit=20&status=success&datePreset=last-30-days
Authorization: Bearer <token>
Query Parameters:
- page: number (default: 1)
- limit: number (default: 20, max: 100)
- status: 'success' | 'pending' | 'failed' | 'all'
- datePreset: 'last-7-days' | 'last-30-days' | 'last-90-days' | 'custom'
- dateFrom: ISO8601 (if custom)
- dateTo: ISO8601 (if custom)
- amountMin: number
- amountMax: number
- search: string (hash or wallet)
Response: {
"payments": Payment[],
"pagination": {
"page": number,
"limit": number,
"total": number,
"totalPages": number
},
"stats": {
"totalToday": number,
"volumeToday": number,
"volumeTodayUSD": number,
"averageTicket": number,
"averageTicketTrend": number
}
}Payment Details
GET /api/payments/:id
Authorization: Bearer <token>
Response: {
"payment": Payment,
"relatedTransactions": Transaction[],
"link": CheckoutLink | null
}GET /api/subscriptions
GET /api/subscriptions/:id
PUT /api/subscriptions/:id/cancelList Subscriptions
GET /api/subscriptions?page=1&limit=20&status=active&datePreset=last-30-days
Authorization: Bearer <token>
Query Parameters:
- page: number (default: 1)
- limit: number (default: 20, max: 100)
- status: 'active' | 'cancelled' | 'expired' | 'all'
- datePreset: 'last-7-days' | 'last-30-days' | 'last-90-days' | 'custom'
- amountMin: number
- amountMax: number
- search: string (payer name, wallet, plan name)
- tokenMint: string (USDT, USDC, etc.)
Response: {
"subscriptions": Subscription[],
"pagination": {
"page": number,
"limit": number,
"total": number,
"totalPages": number
},
"stats": {
"activeSubscriptions": number,
"mrr": number,
"mrrUSD": number,
"arr": number,
"arrUSD": number,
"arrTrend": number,
"newSubscriptions": number,
"cancelledSubscriptions": number
}
}Subscription Details
GET /api/subscriptions/:id
Authorization: Bearer <token>
Response: {
"subscription": Subscription,
"plan": Plan,
"payer": Payer,
"paymentHistory": PaymentExecution[],
"nextPayment": {
"dueAt": "ISO8601",
"amount": number,
"amountUSD": number
}
}Cancel Subscription
PUT /api/subscriptions/:id/cancel
Authorization: Bearer <token>
Content-Type: application/json
{
"reason": "string?" // Optional cancellation reason
}
Response: {
"subscription": Subscription,
"cancelledAt": "ISO8601"
}GET /api/dashboard/overview
GET /api/dashboard/charts/transactions
GET /api/dashboard/charts/mrrDashboard Overview
GET /api/dashboard/overview
Authorization: Bearer <token>
Response: {
"stats": {
"activeSubscriptions": number,
"totalRevenue": number, // SOL
"totalRevenueUSD": number,
"paymentsToday": number,
"mrr": number, // Monthly Recurring Revenue
"mrrUSD": number
},
"recentTransactions": Payment[],
"activeLinks": CheckoutLink[]
}Transaction Charts
GET /api/dashboard/charts/transactions?period=30d
Authorization: Bearer <token>
Response: {
"data": [
{
"date": "YYYY-MM-DD",
"volume": number, // SOL
"volumeUSD": number,
"count": number,
"change": number // % change
}
]
}MRR Charts
GET /api/dashboard/charts/mrr?period=30d
Authorization: Bearer <token>
Response: {
"data": [
{
"date": "YYYY-MM-DD",
"mrr": number, // SOL
"mrrUSD": number,
"change": number // % change
}
]
}GET /api/healthHealth Check
GET /api/health
Response: {
"status": "healthy" | "degraded" | "unhealthy",
"timestamp": "ISO8601",
"services": {
"database": "healthy" | "degraded" | "unhealthy",
"solana": "healthy" | "degraded" | "unhealthy"
},
"version": "string"
}interface User {
id: string;
name: string;
authMethod: "email_password" | "solana_wallet";
email?: string; // Only for email_password users
walletAddress?: string; // Only for solana_wallet users
createdAt: string;
updatedAt: string;
}
interface Payment {
id: string;
hash: string;
amount: number; // SOL
amountUSD: number;
status: "success" | "pending" | "failed";
from: string;
to: string;
linkId?: string;
linkName?: string;
block: number;
confirmations: number;
fee: number;
createdAt: string;
confirmedAt?: string;
}
interface CheckoutLink {
id: string;
name: string;
amount: number; // SOL
amountUSD: number;
status: "active" | "inactive";
url: string;
isRecurring: boolean;
redirectUrl?: string;
description?: string;
createdAt: string;
totalPayments: number;
conversions: number;
views: number;
}
interface Subscription {
id: string;
planId: string;
payerId: string;
payerName: string;
payerWallet: string;
planName: string;
planDescription?: string;
tokenMint: string;
tokenDecimals: number;
amount: number;
amountUSD: number;
status: "active" | "cancelled" | "expired";
nextDueAt: string;
lastPaidAt: string;
totalApprovedAmount: number;
durationMonths: number;
periodSeconds: number;
createdAt: string;
updatedAt: string;
}
interface Plan {
id: string;
receiverId: string;
name: string;
description?: string;
durationMonths: number;
periodSeconds: number;
createdAt: string;
updatedAt: string;
}
interface Receiver {
id: string;
walletAddress: string;
name: string;
description?: string;
tokenAccountUSDT: string;
tokenAccountUSDC: string;
createdAt: string;
updatedAt: string;
}- All endpoints (except health checks) require JWT authentication
- JWT tokens expire after 24 hours
- Wallet signature verification for registration/login
- General API: 1000 requests/hour per user
- Payment Operations: 100 requests/hour per user
- Authentication: 5 attempts/minute per IP
Access-Control-Allow-Origin: https://pattpay.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400- Authentication - User registration/login with wallet
- Payment Links - CRUD operations for payment links
- Payments - List, filter, and view payment details
- Subscriptions - Full subscription management
- Dashboard - Overview stats and charts
- Health - System monitoring endpoints
- All timestamps are in ISO 8601 format
- All monetary amounts are in SOL (with USD equivalents)
- Pagination uses page-based approach (not cursor-based)
- All endpoints return consistent error format
- Database uses PostgreSQL with Prisma ORM
- Smart contracts are deployed on Solana mainnet/devnet
- Rate limiting implemented with Redis
Next Steps: Begin implementation with Phase 1 endpoints, starting with authentication and payment links.