API Routes & RBAC Matrix

Complete REST API specification with role-based access control for all 80+ endpoints.

Public / Discovery APIs No authentication required

Method Route Description Auth
GET /health Health check None
GET /api/v1/tenants/discover Search tenants by name/city/state None
GET /api/v1/tenants/:slug/public Tenant public profile + branding None
GET /api/v1/tenants/:slug/causes Public donation catalog None
GET /api/v1/tenants/:slug/trust-profile Public trust info (visibility-gated) None
GET /api/v1/tenants/:slug/events Public events list None
GET /api/v1/tenants/:slug/gallery Public gallery albums None
GET /api/v1/tenants/:slug/gau-mahima Public gau mahima content None
GET /api/v1/tenants/:slug/trustees Public trustees list None
GET /api/v1/tenants/qr/:qrToken QR / deeplink tenant resolution None
GET /api/v1/platform/plans List public SaaS plans None

App User Auth (Mobile)

Method Route Description
POST /api/v1/auth/app/request-otp Send OTP to phone/email
POST /api/v1/auth/app/verify-otp Verify OTP โ†’ JWT tokens
POST /api/v1/auth/app/google Google OAuth login
POST /api/v1/auth/app/apple Apple Sign-In
POST /api/v1/auth/app/refresh Refresh access token
POST /api/v1/auth/app/logout Invalidate tokens
GET /api/v1/auth/app/me Get current user profile
PUT /api/v1/auth/app/me Update profile (name/email/PAN/addr)
POST /api/v1/auth/app/fcm-token Register/update FCM token

Tenant Admin Auth

Method Route Description
POST /api/v1/auth/admin/login Email/password login
POST /api/v1/auth/admin/verify-mfa MFA verification (TOTP)
POST /api/v1/auth/admin/refresh Refresh token
POST /api/v1/auth/admin/logout Logout
POST /api/v1/auth/admin/forgot-password Password reset request
POST /api/v1/auth/admin/reset-password Reset password with token
GET /api/v1/auth/admin/me Current admin user + permissions
POST /api/v1/auth/admin/change-password Change password

Platform Admin Auth

Method Route Description
POST /api/v1/auth/platform/login Platform admin login
POST /api/v1/auth/platform/verify-mfa MFA verification
POST /api/v1/auth/platform/refresh Refresh token
GET /api/v1/auth/platform/me Current platform admin
POST /api/v1/auth/platform/impersonate/:tenantAdminId Impersonate tenant admin (audited)
DELETE /api/v1/auth/platform/impersonate End impersonation session

Donation Workflow APIs

Method Route Description Auth
GET /api/v1/t/causes List active causes for tenant App User
GET /api/v1/t/causes/:id Cause detail + options App User
POST /api/v1/t/donations/initiate Create donation order (idempotency key) App User
POST /api/v1/t/donations/:id/verify Verify payment signature App User
GET /api/v1/t/donations Donor's donation history App User
GET /api/v1/t/donations/:id/receipt Download PDF receipt / 80G cert App User
POST /api/v1/t/recurring/subscribe Create recurring mandate (eNACH/UPI) App User
GET /api/v1/t/recurring List active recurring plans App User
PATCH /api/v1/t/recurring/:id/pause Pause recurring plan App User
DELETE /api/v1/t/recurring/:id Cancel recurring plan App User
GET /api/v1/t/gau-coins/balance Donor's coin balance App User
GET /api/v1/t/gau-coins/transactions Coin earn/redeem history App User
POST /api/v1/t/gau-coins/redeem Apply coins to donation App User

Cow Adoption

GET /api/v1/t/cows List available/adopted cows
GET /api/v1/t/cows/:id Cow detail + sponsor info
POST /api/v1/t/cows/:id/adopt Initiate cow adoption payment
GET /api/v1/t/adoptions Donor's adoption list
GET /api/v1/t/adoptions/:id/updates Cow health/photo updates

Membership

GET /api/v1/t/membership/plans List membership tiers
POST /api/v1/t/membership/subscribe Purchase membership
GET /api/v1/t/membership/my Current membership + card
POST /api/v1/t/membership/renew Renew membership

Volunteers

POST /api/v1/t/volunteers/register Volunteer registration
GET /api/v1/t/volunteers/shifts Available volunteer shifts
POST /api/v1/t/volunteers/shifts/:id/book Book a shift
GET /api/v1/t/volunteers/my-hours Volunteer hours log

Gallery & CMS

GET /api/v1/t/gallery List gallery albums
GET /api/v1/t/gallery/:albumId/media Album media items
GET /api/v1/t/gau-mahima Gau Mahima articles/videos
GET /api/v1/t/gau-mahima/:id Single content item

Platform Admin APIs Requires: Platform Admin JWT

Method Route Description
GET /api/v1/platform/tenants List all tenants (paginated, filterable)
POST /api/v1/platform/tenants Onboard new tenant
GET /api/v1/platform/tenants/:id Tenant detail with full config
PUT /api/v1/platform/tenants/:id Update tenant settings
POST /api/v1/platform/tenants/:id/suspend Suspend tenant
POST /api/v1/platform/tenants/:id/activate Activate / unsuspend tenant
GET /api/v1/platform/subscriptions List all SaaS subscriptions
POST /api/v1/platform/subscriptions Create subscription for tenant
POST /api/v1/platform/subscriptions/:id/upgrade Upgrade plan (prorated)
GET /api/v1/platform/invoices Platform invoices list
POST /api/v1/platform/invoices/:id/retry Retry failed invoice payment
GET /api/v1/platform/analytics Cross-tenant analytics
GET /api/v1/platform/audit-logs Platform audit log trail
GET /api/v1/platform/plans List/manage SaaS plans
PUT /api/v1/platform/plans/:id Update plan limits & pricing

Inbound Webhook Endpoints

/webhooks/razorpay/:tenantSlug Razorpay
payment.capturedpayment.failedrefund.createdsubscription.charged
/webhooks/payu/:tenantSlug PayU
payment.successpayment.failurerefund.initiated
/webhooks/platform-billing Platform
invoice.paidinvoice.failedsubscription.renewed

Idempotency & Processing

1
Signature Verification
HMAC-SHA256 signature validated against gateway secret
2
Idempotency Check
Check processed_webhooks table for duplicate event IDs
3
Tenant Resolution
Resolve tenant from route param or payload metadata
4
Event Processing
Update transaction status, trigger receipt generation
5
GauCoin Award
Award coins if payment successful per earn rules
6
Push Notification
Send FCM notification to donor device
7
Audit Log
Record full event in audit_logs table

RBAC Permission Matrix

โœ… Full Access   ๐Ÿ“– Read Only   โœ๏ธ Own Records   โŒ No Access

Resource / Action Platform Admin Tenant Admin Finance Mgr Content Mgr App User
Tenant CRUD โœ…โŒโŒโŒโŒ
SaaS Plans โœ…๐Ÿ“–โŒโŒโŒ
Platform Billing โœ…๐Ÿ“–โŒโŒโŒ
Impersonation โœ…โŒโŒโŒโŒ
Platform Audit Logs โœ…โŒโŒโŒโŒ
Donation Causes CRUD โœ…โœ…โŒโŒโŒ
View Transactions โœ…โœ…โœ…โŒโœ๏ธ
Export Reports โœ…โœ…โœ…โŒโŒ
Payment Gateway Config โœ…โœ…โŒโŒโŒ
Refund Donations โœ…โœ…โœ…โŒโŒ
Gau Coins Config โœ…โœ…โŒโŒโŒ
Cow Adoptions CRUD โœ…โœ…โŒโŒโŒ
Membership Plans โœ…โœ…โŒโŒโŒ
Events CRUD โœ…โœ…โŒโœ…โŒ
Gallery Upload โœ…โœ…โŒโœ…โŒ
CMS Content โœ…โœ…โŒโœ…โŒ
Trust & Compliance โœ…โœ…๐Ÿ“–โŒโŒ
User & RBAC Mgmt โœ…โœ…โŒโŒโŒ
Notifications Send โœ…โœ…โŒโœ…โŒ
Tenant Audit Logs โœ…โœ…๐Ÿ“–๐Ÿ“–โŒ
Own Donations โœ…โœ…โœ…โŒโœ๏ธ
Own Profile โœ…โœ…โœ…โœ…โœ๏ธ
PDF Receipts โœ…โœ…โœ…โŒโœ๏ธ
GauCoins Balance โœ…โœ…โœ…โŒโœ๏ธ

Tenant Resolver Middleware (TypeScript)

import { Request, Response, NextFunction } from 'express';
import { redis } from '../config/redis';
import { db } from '../config/database';

export async function tenantResolverMiddleware(
  req: Request & { tenant?: any },
  res: Response,
  next: NextFunction
) {
  try {
    // 1. Extract tenant identifier
    const subdomain = req.hostname.split('.')[0]; // trust1.gaudaan.in
    const headerTenantId = req.headers['x-tenant-id'] as string;
    const identifier = headerTenantId || subdomain;

    if (!identifier || identifier === 'api' || identifier === 'admin') {
      return next(); // Platform-level route
    }

    // 2. Check Redis cache (TTL: 5 minutes)
    const cacheKey = `tenant:${identifier}`;
    const cached = await redis.get(cacheKey);
    if (cached) {
      req.tenant = JSON.parse(cached);
      return next();
    }

    // 3. Database lookup
    const tenant = await db.query(
      `SELECT id, slug, name, status, plan_id, settings
       FROM tenants WHERE slug = $1 OR id::text = $1 LIMIT 1`,
      [identifier]
    );

    if (!tenant.rows[0]) {
      return res.status(404).json({ error: 'Tenant not found' });
    }

    if (tenant.rows[0].status === 'SUSPENDED') {
      return res.status(403).json({ error: 'Tenant suspended' });
    }

    // 4. Cache and attach
    await redis.setex(cacheKey, 300, JSON.stringify(tenant.rows[0]));
    req.tenant = tenant.rows[0];
    next();
  } catch (err) {
    next(err);
  }
}

RBAC Guard Middleware

import { Request, Response, NextFunction } from 'express';

type Role = 'PLATFORM_ADMIN' | 'TENANT_ADMIN' | 'FINANCE_MANAGER' | 'CONTENT_MANAGER' | 'APP_USER';

const PERMISSIONS: Record<string, Role[]> = {
  'tenant:create':   ['PLATFORM_ADMIN'],
  'tenant:update':   ['PLATFORM_ADMIN', 'TENANT_ADMIN'],
  'donation:read':   ['PLATFORM_ADMIN', 'TENANT_ADMIN', 'FINANCE_MANAGER', 'APP_USER'],
  'donation:export': ['PLATFORM_ADMIN', 'TENANT_ADMIN', 'FINANCE_MANAGER'],
  'gateway:config':  ['PLATFORM_ADMIN', 'TENANT_ADMIN'],
  'event:create':    ['PLATFORM_ADMIN', 'TENANT_ADMIN', 'CONTENT_MANAGER'],
  'audit:read':      ['PLATFORM_ADMIN', 'TENANT_ADMIN'],
};

export function requirePermission(permission: string) {
  return (req: Request & { user?: any }, res: Response, next: NextFunction) => {
    const userRole: Role = req.user?.role;
    const allowed = PERMISSIONS[permission];
    
    if (!userRole || !allowed?.includes(userRole)) {
      return res.status(403).json({ 
        error: 'Insufficient permissions',
        required: permission 
      });
    }
    next();
  };
}

PaymentProvider Interface

export interface PaymentProvider {
  name: string;
  createOrder(params: CreateOrderParams): Promise<OrderResult>;
  verifySignature(params: VerifyParams): boolean;
  initiateRefund(params: RefundParams): Promise<RefundResult>;
  createRecurringMandate(params: MandateParams): Promise<MandateResult>;
  cancelMandate(mandateId: string): Promise<void>;
  processWebhook(payload: unknown, signature: string): Promise<WebhookEvent>;
}

// Razorpay implementation
export class RazorpayProvider implements PaymentProvider {
  name = 'razorpay';
  private keyId: string;
  private keySecret: string;

  constructor(private config: GatewayConfig) {
    // Keys are decrypted from DB on instantiation
    this.keyId = decrypt(config.key_id_encrypted);
    this.keySecret = decrypt(config.key_secret_encrypted);
  }

  async createOrder(params: CreateOrderParams): Promise<OrderResult> {
    const razorpay = new Razorpay({ key_id: this.keyId, key_secret: this.keySecret });
    const order = await razorpay.orders.create({
      amount: params.amount * 100, // paise
      currency: 'INR',
      receipt: params.idempotencyKey,
    });
    return { orderId: order.id, amount: params.amount };
  }

  verifySignature(params: VerifyParams): boolean {
    const expectedSig = crypto
      .createHmac('sha256', this.keySecret)
      .update(`${params.orderId}|${params.paymentId}`)
      .digest('hex');
    return expectedSig === params.signature;
  }
}