API Documentation
Base URL
- Development:
http://localhost:8080 - Production:
https://your-api-domain.com
Authentication
Most endpoints require JWT authentication:
Authorization: Bearer <token>
Public Endpoints
GET /app-downloads
Returns app download links and "coming soon" flags for the public Download Apps page. No auth required.
Response:
{
"windows_shopkeeper_url": "https://...",
"android_customer_url": "https://...",
"ios_customer_url": "https://...",
"windows_coming_soon": false,
"android_coming_soon": false,
"ios_coming_soon": true
}
GET /delete-data
Serves a public HTML page for the Google Play "Delete data" link requirement (GDPR). Explains how users can delete their data (login and use Delete Account, or contact support).
POST /register
Register a new user.
Request:
{
"full_name": "John Doe",
"username": "johndoe",
"email": "john@example.com",
"phone": "+1234567890",
"password": "securepassword",
"role": "customer",
"shop_name": "My Shop",
"lat": 12.9716,
"long": 77.5946,
"address": "123 Main St"
}
POST /login
Login and receive JWT token.
Request:
{
"username": "johndoe",
"password": "securepassword"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"role": "customer",
"username": "johndoe"
}
POST /forgot-password
Request password reset email.
Request:
{
"email": "john@example.com"
}
POST /reset-password
Reset password using token.
Request:
{
"token": "reset-token-from-email",
"password": "newpassword"
}
Protected Endpoints
GET /profile
Get current user's profile.
Response:
{
"id": 1,
"username": "johndoe",
"full_name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"role": "customer",
"shop_name": null,
"address": null,
"is_open": false
}
PUT /profile
Update current user's profile.
Request:
{
"username": "newusername",
"address": "456 New St",
"password": "newpassword"
}
DELETE /profile
Delete the current user's account. Requires password confirmation.
Request:
{
"password": "currentpassword"
}
Response:
{
"message": "Account deleted successfully"
}
Notes:
- Requires password confirmation for security
- Deletes all associated data (files, reset tokens, payouts)
- Nullifies payment order references (preserves financial records)
- Clears authentication cookie
- Admin accounts cannot be deleted through this endpoint
POST /upload
Upload a file for printing.
Supported file types: PDF (.pdf), images (.png, .jpg, .jpeg), Word (.doc, .docx), PowerPoint (.ppt, .pptx). Max 20MB per file, 100MB total per batch.
Headers: Authorization: Bearer <token>, Content-Type: multipart/form-data
Form Data:
files[]orfile: File(s) to uploadprint_type:"private"or"queue"copies: Number of copiesprint_mode:"single"or"double"color_mode:"bw"or"color"paper_size:"A4","Letter", etc.shop_id: Shopkeeper ID (required for queue prints)comment: Optional customer notes
Response:
{
"file_id": 123,
"code": "aB3xY9",
"num_pages": 5,
"total_cost": 25.00,
"queue_position": 3
}
GET /file/:code
Download file by unique code.
Parameters:
code(path): 6-character unique code
GET /file/:code/status
Check file status.
Parameters:
code(path): 6-character unique code
Response:
{
"status": "uploaded"
}
GET /shops
Get nearest shopkeepers.
Query Params: lat, long (optional)
Response:
[
{
"id": 2,
"username": "shop1",
"shop_name": "Print Shop 1",
"lat": 12.9716,
"long": 77.5946,
"distance": 0.05,
"is_open": true,
"address": "123 Main St"
}
]
GET /queue
Get shopkeeper's print queue.
POST /calculate-cost
Calculate print cost before payment. Accepts the same file types as upload (PDF, images, Word, PPT). Send files as multipart/form-data with files[], copies, print_mode, color_mode, paper_size.
POST /create-payment-order
Create a payment order. Supports both Razorpay and Wallet payment methods.
Request:
{
"amount": 50.00,
"use_wallet": true,
"print_type": "private",
"copies": 1,
"print_mode": "single",
"color_mode": "bw",
"paper_size": "A4",
"shopkeeper_id": 123,
"comment": "Optional comment",
"platform_commission": 0.0
}
Request Fields:
amount(required): Payment amount in INRuse_wallet(optional): Boolean to enable wallet payment if sufficient balanceprint_type(required):"private"or"queue"copies(optional): Number of copies (default: 1)print_mode(optional):"single"or"double"(default: "single")color_mode(optional):"bw"or"color"(default: "bw")paper_size(optional): Paper size (default: "A4")shopkeeper_id(required for queue prints): Shopkeeper user IDcomment(optional): Customer notesplatform_commission(optional): Platform commission percentage (0-1, default: 0)
Response:
{
"id": 123,
"order_id": "order_xxx",
"amount": 50.00,
"status": "paid",
"payment_method": "wallet",
"wallet_amount": 50.00,
"razorpay_amount": 0.00,
"key_id": "rzp_test_xxx",
"shopkeeper_id": 123,
"shopkeeper_amount": 50.00,
"skip_payment": true
}
Payment Methods:
"wallet": Full payment from wallet (instant, no Razorpay needed)"razorpay": Payment via Razorpay gateway"hybrid": Partial wallet + Razorpay payment
Payment Flow:
- If
use_walletistrue, system checks wallet balance - Sufficient balance: Deducts from wallet, sets
payment_method: "wallet",skip_payment: true - Insufficient balance: Creates Razorpay order, sets
payment_method: "razorpay" - Partial balance: Deducts wallet portion, creates Razorpay order for remainder, sets
payment_method: "hybrid"
GET /payment-order/:orderId/status
Check payment status.
Parameters:
orderId(path): Payment order ID
Response:
{
"id": 123,
"user_id": 1,
"order_id": "order_xxx",
"payment_id": "pay_xxx",
"amount": 50.00,
"status": "paid",
"payment_method": "wallet",
"wallet_amount": 50.00,
"created_at": "2024-01-17T20:00:00Z",
"paid_at": "2024-01-17T20:00:01Z"
}
POST /payment/webhook
Razorpay webhook endpoint (public, signature verified). Handles payment confirmations and wallet top-ups.
GET /csrf-token
Get CSRF token for state-changing requests.
Response:
{
"csrfToken": "token-string"
}
POST /logout
Logout and clear authentication cookie.
POST /forgot-username
Request username recovery email.
Request:
{
"email": "john@example.com"
}
POST /file/:code/confirm
Confirm private print download (shopkeeper use).
GET /queue/:orderGroupId/files
Get files for a specific order group in the queue.
GET /queue/download/:fileId
Download a specific file from the queue (shopkeeper use).
POST /queue/:fileId/confirm
Confirm queue print completion.
GET /my-files
Get current user's uploaded files.
GET /my-orders
Get current user's payment orders (for customer order history).
GET /shop/history
Get shop's print history (shopkeeper use).
POST /shop/status
Toggle shop open/closed status.
Request:
{
"is_open": true
}
POST /shop/heartbeat
Shopkeeper app sends a heartbeat to keep the shop marked as "open". Used with the backend sweeper: if both app heartbeat and web activity are stale, the shop is auto-closed. No request body required.
POST /withdraw/:fileId
Withdraw a print job and receive refund.
GET /shopkeeper/payouts
Get shopkeeper's payout history.
Wallet Service Endpoints
The wallet service is implemented. Customers can pre-load money, pay instantly (wallet or hybrid with Razorpay), and receive instant refunds on withdrawal.
GET /wallet/balance
Get current wallet balance for the authenticated user.
Response:
{
"balance": 500.00
}
POST /wallet/topup
Add money to wallet via Razorpay.
Request:
{
"amount": 500.00
}
Request Fields:
amount(required): Top-up amount in INR (minimum: ₹10, maximum: ₹10,000)
Response:
{
"order_id": "order_xxx",
"key_id": "rzp_test_xxx",
"amount": 500.00
}
Flow:
- Creates Razorpay payment order for top-up
- After successful payment (via webhook), adds amount to wallet balance
- Creates wallet transaction record
GET /wallet/transactions
Get wallet transaction history.
Query Parameters:
limit(optional): Number of transactions to return (default: 50, max: 100)offset(optional): Pagination offset (default: 0)
Response:
{
"transactions": [
{
"id": 1,
"type": "topup",
"amount": 500.00,
"balance_before": 0.00,
"balance_after": 500.00,
"status": "completed",
"description": "Wallet top-up",
"created_at": "2024-01-17T20:00:00Z"
},
{
"id": 2,
"type": "payment",
"amount": -50.00,
"balance_before": 500.00,
"balance_after": 450.00,
"status": "completed",
"description": "Print payment",
"payment_order_id": 123,
"created_at": "2024-01-17T21:00:00Z"
},
{
"id": 3,
"type": "refund",
"amount": 50.00,
"balance_before": 450.00,
"balance_after": 500.00,
"status": "completed",
"description": "Refund for withdrawal",
"payment_order_id": 123,
"created_at": "2024-01-17T22:00:00Z"
}
],
"total": 25
}
Transaction Types:
"topup": Money added to wallet"payment": Money spent from wallet"refund": Money refunded to wallet"withdrawal": Money withdrawn from wallet (optional feature)
POST /wallet/withdraw (Optional)
Withdraw wallet balance to bank account. Requires bank account verification.
Request:
{
"amount": 200.00,
"bank_account": "1234567890",
"ifsc_code": "BANK0001234",
"account_holder_name": "John Doe"
}
Response:
{
"withdrawal_id": 123,
"status": "pending",
"amount": 200.00,
"processing_time": "2-3 business days"
}
Wallet Service Implementation Details
Database Schema
Users Table Update
ALTER TABLE users ADD COLUMN wallet_balance DECIMAL(10,2) DEFAULT 0.00;
Wallet Transactions Table
CREATE TABLE wallet_transactions (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id) NOT NULL,
transaction_type TEXT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
balance_before DECIMAL(10,2) NOT NULL,
balance_after DECIMAL(10,2) NOT NULL,
payment_order_id INT REFERENCES payment_orders(id),
razorpay_payment_id TEXT,
razorpay_refund_id TEXT,
status TEXT DEFAULT 'completed',
description TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
Payment Orders Table Update
ALTER TABLE payment_orders ADD COLUMN payment_method TEXT DEFAULT 'razorpay';
ALTER TABLE payment_orders ADD COLUMN wallet_amount DECIMAL(10,2) DEFAULT 0.00;
Payment Flow with Wallet
Scenario A: Full Wallet Payment
1. Customer creates payment order with use_wallet: true
2. System checks wallet balance (e.g., ₹200)
3. Payment amount: ₹50
4. Balance sufficient → Deducts ₹50 from wallet
5. Payment order status: "paid" (immediate)
6. skip_payment: true (no Razorpay checkout needed)
7. File can be uploaded immediately
Scenario B: Insufficient Wallet Balance
1. Customer creates payment order with use_wallet: true
2. System checks wallet balance (e.g., ₹30)
3. Payment amount: ₹50
4. Balance insufficient → Creates Razorpay order for ₹50
5. Customer pays via Razorpay
6. Payment order status: "paid" (after webhook)
7. File can be uploaded
Scenario C: Hybrid Payment (Partial Wallet)
1. Customer creates payment order with use_wallet: true
2. System checks wallet balance (e.g., ₹30)
3. Payment amount: ₹50
4. Deducts ₹30 from wallet
5. Creates Razorpay order for remaining ₹20
6. Customer pays ₹20 via Razorpay
7. Payment order status: "paid" (after webhook)
8. payment_method: "hybrid", wallet_amount: 30.00, razorpay_amount: 20.00
Refund Flow with Wallet
When a customer withdraws a print job:
If payment was via wallet:
- Refund goes to wallet instantly
- Balance updated immediately
- No Razorpay refund needed
If payment was via Razorpay:
- Refund via Razorpay (5-7 business days)
- Or customer can choose wallet refund (instant)
Wallet Limits
- Minimum Top-up: ₹10
- Maximum Top-up: ₹10,000 per transaction
- Maximum Wallet Balance: ₹50,000 (optional limit)
- Minimum Withdrawal: ₹100 (if withdrawal enabled)
Security Considerations
- Wallet balance cannot go negative
- All transactions are logged in
wallet_transactionstable - Database transactions ensure atomicity
- Row-level locking prevents race conditions
- Rate limiting on top-ups (prevent abuse)
- Server-side validation of all amounts
Admin Endpoints
All admin endpoints require admin role and (optional) IP whitelist when ALLOWED_ADMIN_IPS is set. CSRF required for state-changing requests.
GET /admin/dashboard/stats
Get dashboard statistics.
GET /admin/users
Get all users (paginated).
GET /admin/users/details
Get detailed info for a specific user (e.g. for admin user management).
POST /admin/users/delete
Delete a user account (admin-initiated). Request body: { "user_id": 123 }.
GET /admin/orders
Get all payment orders.
GET /admin/payouts
Get all shopkeeper payouts.
PUT /admin/payouts/update
Update a single payout status.
PUT /admin/payouts/bulk-update
Bulk update payout statuses.
GET /admin/payouts/export
Export payouts (e.g. CSV).
PUT /admin/app-downloads
Update app download links and "coming soon" flags (Windows shopkeeper, Android customer, iOS customer). These appear on the public Download Apps page.
Request:
{
"windows_shopkeeper_url": "https://...",
"android_customer_url": "https://...",
"ios_customer_url": "https://...",
"windows_coming_soon": false,
"android_coming_soon": false,
"ios_coming_soon": true
}
For a quick reference, see the API Reference.