JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 9
  • Score
    100M100P100Q65605F
  • License MIT

Backend functions for DoNotDev Framework - Firebase, Vercel, and platform-agnostic implementations for auth, billing, CRUD, and OAuth

Package Exports

  • @donotdev/functions
  • @donotdev/functions/firebase
  • @donotdev/functions/shared
  • @donotdev/functions/supabase
  • @donotdev/functions/vercel

Readme

@donotdev/functions

Unified functions package supporting both Firebase Cloud Functions and Vercel Functions with clean, simple architecture. Each function is one file, with shared algorithms only for complex business logic.

๐Ÿ“ฆ Package vs Templates

IMPORTANT: This directory contains the published @donotdev/functions NPM package, not templates.

Location What It Is Purpose
/functions/ (this directory) Published NPM package Framework code you import
/packages/cli/templates/functions-* CLI scaffolding templates Example usage (.example files)

How they relate:

@donotdev/functions (published package)
    โ†“ imported by
Your App Functions (scaffolded by CLI)
    โ†“ uses your config
Framework Implementation + Your Business Logic

Example:

// apps/your-app/functions/src/billing/createCheckoutSession.ts
import { createCheckoutSession as generic } from '@donotdev/functions/firebase';
import { stripeBackConfig } from '../config/stripeBackConfig';

// Your app's function = framework + your config
export const createCheckoutSession = generic(stripeBackConfig);

See Functions Package Architecture for detailed explanation.

๐ŸŽฏ Design Principles

  • 1 File Per Function: Simple, maintainable functions
  • Direct Implementation: Easy functions coded directly in Firebase/Vercel files
  • Shared Algorithms: Complex functions use shared business logic
  • One Utils Package: Centralized utilities with internal/external separation
  • Framework Integration: Functions can be scaffolded into consuming apps

๐Ÿ“ Package Structure

functions/src/
โ”œโ”€โ”€ shared/
โ”‚   โ”œโ”€โ”€ utils/               # ONE utils package
โ”‚   โ”‚   โ”œโ”€โ”€ internal/        # Functions-only utilities
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ firebase.ts  # Firebase Admin initialization
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ errors.ts    # handleError, DoNotDevError
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ auth.ts      # assertAuthenticated, assertAdmin
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ validation.ts # validateStripeEnvironment
โ”‚   โ”‚   โ”œโ”€โ”€ external/        # Framework utilities
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ subscription.ts # getTierFromPriceId, etc.
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ metadata.ts  # createMetadata, updateMetadata
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ date.ts      # toISOString
โ”‚   โ”‚   โ””โ”€โ”€ index.ts         # Export everything
โ”‚   โ”œโ”€โ”€ billing/             # Shared billing algorithms
โ”‚   โ”‚   โ”œโ”€โ”€ createCheckout.ts # createCheckoutAlgorithm()
โ”‚   โ”‚   โ”œโ”€โ”€ processPayment.ts # processPaymentAlgorithm()
โ”‚   โ”‚   โ””โ”€โ”€ webhook.ts       # webhookAlgorithm()
โ”‚   โ””โ”€โ”€ oauth/               # Shared OAuth algorithms
โ”‚       โ”œโ”€โ”€ exchangeToken.ts # exchangeTokenAlgorithm()
โ”‚       โ””โ”€โ”€ grantAccess.ts   # grantAccessAlgorithm()
โ”œโ”€โ”€ firebase/                # Firebase Cloud Functions
โ”‚   โ”œโ”€โ”€ auth/                # Simple auth functions
โ”‚   โ”‚   โ”œโ”€โ”€ getUserClaims.ts # Direct Firebase call
โ”‚   โ”‚   โ”œโ”€โ”€ setUserClaims.ts # Direct Firebase call
โ”‚   โ”‚   โ””โ”€โ”€ removeUserClaims.ts # Direct Firebase call
โ”‚   โ”œโ”€โ”€ billing/             # Complex billing functions
โ”‚   โ”‚   โ”œโ”€โ”€ createCheckout.ts # Uses shared/billing/createCheckout
โ”‚   โ”‚   โ”œโ”€โ”€ processPayment.ts # Uses shared/billing/processPayment
โ”‚   โ”‚   โ””โ”€โ”€ webhook.ts       # Uses shared/billing/webhook
โ”‚   โ”œโ”€โ”€ crud/                # Simple CRUD functions
โ”‚   โ”‚   โ”œโ”€โ”€ create.ts        # Direct Firestore call
โ”‚   โ”‚   โ”œโ”€โ”€ get.ts           # Direct Firestore call
โ”‚   โ”‚   โ””โ”€โ”€ list.ts          # Direct Firestore call
โ”‚   โ””โ”€โ”€ config/
โ”‚       โ””โ”€โ”€ constants.ts     # Firebase function configs
โ””โ”€โ”€ vercel/api/              # Vercel API Routes
    โ”œโ”€โ”€ auth/                # Simple auth endpoints
    โ”‚   โ”œโ”€โ”€ getUserClaims.ts # Direct Firebase call
    โ”‚   โ”œโ”€โ”€ setUserClaims.ts # Direct Firebase call
    โ”‚   โ””โ”€โ”€ removeUserClaims.ts # Direct Firebase call
    โ”œโ”€โ”€ billing/             # Complex billing endpoints
    โ”‚   โ”œโ”€โ”€ createCheckout.ts # Uses shared/billing/createCheckout
    โ”‚   โ”œโ”€โ”€ processPayment.ts # Uses shared/billing/processPayment
    โ”‚   โ””โ”€โ”€ webhook.ts       # Uses shared/billing/webhook
    โ”œโ”€โ”€ crud/                # Simple CRUD endpoints
    โ”‚   โ”œโ”€โ”€ create.ts        # Direct Firestore call
    โ”‚   โ”œโ”€โ”€ get.ts           # Direct Firestore call
    โ”‚   โ””โ”€โ”€ list.ts          # Direct Firestore call
    โ””โ”€โ”€ config/
        โ””โ”€โ”€ constants.ts     # Vercel function configs

๐Ÿ—๏ธ Function Architecture

Simple Functions (Direct Implementation)

Example: Get User Claims

// functions/src/firebase/auth/getUserClaims.ts
import { onCall } from 'firebase-functions/v2/https';
import { getAuth } from 'firebase-admin/auth';
import { handleError } from '../../shared/utils';
import { AUTH_CONFIG } from '../config/constants';

export const getUserClaims = onCall(AUTH_CONFIG, async (request) => {
  try {
    const { userId } = request.data;
    const user = await getAuth().getUser(userId);
    return user.customClaims || {};
  } catch (error) {
    throw handleError(error);
  }
});

Complex Functions (Shared Algorithms)

Example: Create Checkout Session

// functions/src/firebase/billing/createCheckout.ts
import { onCall } from 'firebase-functions/v2/https';
import { handleError } from '../../shared/utils';
import { createCheckoutAlgorithm } from '../../shared/billing/createCheckout';
import { STRIPE_CONFIG } from '../config/constants';

export const createCheckout = onCall(STRIPE_CONFIG, async (request) => {
  try {
    return await createCheckoutAlgorithm(request.data, firebaseProvider);
  } catch (error) {
    throw handleError(error);
  }
});

Shared Algorithm:

// functions/src/shared/billing/createCheckout.ts
export async function createCheckoutAlgorithm(request, provider) {
  // Complex billing logic that works with both Firebase and Vercel
  // Uses provider interface for platform-specific operations
}

๐ŸŽฏ Framework Integration

Scaffolding Functions into Apps

When creating a new app, the framework can scaffold functions based on the chosen platform:

# Create app with Firebase functions
bun create-dndev-app my-app --platform firebase

# Create app with Vercel functions
bun create-dndev-app my-app --platform vercel

Generated Structure:

my-app/
โ”œโ”€โ”€ functions/                 # Scaffolded from @donotdev/functions
โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”œโ”€โ”€ firebase/         # OR vercel/api/ based on choice
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ auth/
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ getUserClaims.ts
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ setUserClaims.ts
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ billing/
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ createCheckout.ts
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ webhook.ts
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ crud/
โ”‚   โ”‚   โ”‚       โ”œโ”€โ”€ create.ts
โ”‚   โ”‚   โ”‚       โ””โ”€โ”€ get.ts
โ”‚   โ”‚   โ””โ”€โ”€ shared/           # Copied shared utilities
โ”‚   โ”‚       โ”œโ”€โ”€ utils/
โ”‚   โ”‚       โ”œโ”€โ”€ billing/
โ”‚   โ”‚       โ””โ”€โ”€ oauth/
โ”‚   โ””โ”€โ”€ package.json
โ””โ”€โ”€ src/
    โ””โ”€โ”€ components/
        โ””โ”€โ”€ CheckoutButton.tsx # Uses scaffolded functions

Function Customization

Developers can:

  • Use as-is: Functions work out of the box
  • Customize: Modify scaffolded functions for specific needs
  • Remove: Delete unused functions
  • Replace: Implement custom business logic

Example Customization:

// my-app/functions/src/firebase/auth/getUserClaims.ts
// Developer can modify this scaffolded function
export const getUserClaims = onCall(AUTH_CONFIG, async (request) => {
  try {
    const { userId } = request.data;
    const user = await getAuth().getUser(userId);

    // Custom business logic
    const customClaims = user.customClaims || {};
    const filteredClaims = filterSensitiveClaims(customClaims);

    return filteredClaims;
  } catch (error) {
    throw handleError(error);
  }
});

๐Ÿš€ Quick Start

1. Framework Development

# Install dependencies
bun install

# Build all platforms
bun run build

2. Environment Variables

Firebase Functions:

STRIPE_SECRET_KEY=<stripe_secret_key>
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_API_VERSION=2025-08-27.basil  # REQUIRED - No fallback

Vercel Functions:

STRIPE_SECRET_KEY=<stripe_secret_key>
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_API_VERSION=2025-08-27.basil  # REQUIRED - No fallback
FIREBASE_ADMIN_PRIVATE_KEY="<firebase_private_key>"
FIREBASE_PROJECT_ID=your-project-id
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-...@your-project.iam.gserviceaccount.com

3. Development

# Firebase development
bun run dev:firebase

# Vercel development
bun run dev:vercel

4. Deployment

# Deploy to Firebase
bun run deploy:firebase

# Deploy to Vercel
bun run deploy:vercel

๐Ÿ” Authentication Functions

Firebase Cloud Functions

Create Checkout Session

// Called from client with Firebase Auth token
// Direct Stripe integration - no Firebase Functions needed
import { loadStripe } from '@stripe/stripe-js';

const stripe = await loadStripe(process.env.VITE_STRIPE_PUBLISHABLE_KEY);
const { error } = await stripe.redirectToCheckout({
  priceId: 'price_pro_monthly',
  userId: user.uid,
  userEmail: user.email,
  metadata: { plan: 'pro' },
  allowPromotionCodes: true,
});

// Redirect to Stripe Checkout
window.location.href = result.data.sessionUrl;

Stripe Webhook

  • URL: https://your-project.cloudfunctions.net/stripeWebhook
  • Events: customer.subscription.*, invoice.payment.*, checkout.session.completed
  • Purpose: Updates Firebase custom claims with subscription data

Refresh Subscription Status

const refreshSubscription = httpsCallable(
  functions,
  'refreshSubscriptionStatus'
);

const result = await refreshSubscription({
  userId: user.uid,
});

console.log(result.data.subscription); // Updated subscription data

Vercel API Routes

Create Checkout Session

// POST /api/auth/create-checkout-session
const response = await fetch('/api/auth/create-checkout-session', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${await user.getIdToken()}`,
  },
  body: JSON.stringify({
    priceId: 'price_pro_monthly',
    userId: user.uid,
    userEmail: user.email,
    metadata: { plan: 'pro' },
  }),
});

const { sessionUrl } = await response.json();
window.location.href = sessionUrl;

Stripe Webhook

  • URL: https://your-domain.vercel.app/api/auth/stripe-webhook
  • Events: Same as Firebase version
  • Purpose: Updates Firebase custom claims with subscription data

Refresh Subscription Status

// POST /api/auth/refresh-subscription
const response = await fetch('/api/auth/refresh-subscription', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${await user.getIdToken()}`,
  },
  body: JSON.stringify({
    userId: user.uid,
  }),
});

const { subscription } = await response.json();

๐ŸŽฏ Subscription Management

How It Works

  1. Purchase Flow:

    • User clicks subscribe button
    • Client calls Stripe directly
    • User completes payment on Stripe
    • Stripe webhook updates Firebase custom claims
  2. Subscription Data Storage:

    // Stored in Firebase Auth custom claims
    {
      subscription: {
        tier: 'pro' | 'ai' | 'free',
        subscriptionId: 'sub_1234...',
        customerId: 'cus_1234...',
        status: 'active' | 'canceled' | ...,
        subscriptionEnd: 1640995200000, // Unix timestamp
        cancelAtPeriodEnd: false,
        updatedAt: 1640995200000,
      }
    }
  3. Client-Side Access:

    import { useSubscription } from '@donotdev/auth';
    
    const { subscription, loading } = useSubscription();
    
    if (subscription?.tier === 'pro') {
      // Show pro features
    }

Tier Mapping

Configure your Stripe price IDs in src/shared/utils/index.ts:

const tierMapping: Record<string, SubscriptionTier> = {
  price_pro_monthly: 'pro',
  price_pro_yearly: 'pro',
  price_ai_monthly: 'ai',
  price_ai_yearly: 'ai',
};

๐Ÿ”ง Configuration

Firebase Functions

  1. Set Environment Variables:

    firebase functions:config:set stripe.secret_key="<stripe_secret_key>"
    firebase functions:config:set stripe.webhook_secret="whsec_..."
  2. Deploy:

    bun run deploy:firebase
  3. Configure Stripe Webhook:

    • URL: https://your-project.cloudfunctions.net/stripeWebhook
    • Events: Select all customer.subscription.* and invoice.payment.* events

Vercel Functions

  1. Set Environment Variables in Vercel dashboard:

    • STRIPE_SECRET_KEY
    • STRIPE_WEBHOOK_SECRET
    • FIREBASE_ADMIN_PRIVATE_KEY
    • FIREBASE_PROJECT_ID
    • FIREBASE_CLIENT_EMAIL
  2. Deploy:

    bun run deploy:vercel
  3. Configure Stripe Webhook:

    • URL: https://your-domain.vercel.app/api/auth/stripe-webhook
    • Events: Same as Firebase version

๐Ÿงช Testing

# Run all tests
bun test

# Test specific platform
bun run test:firebase
bun run test:vercel

# Type checking
bun run typecheck

Testing Webhooks Locally

Firebase Emulator

# Terminal 1: Start emulator
bun run dev:firebase

# Terminal 2: Forward webhooks
stripe listen --forward-to localhost:5001/your-project/europe-west1/stripeWebhook

Vercel

# Terminal 1: Start Vercel dev
bun run dev:vercel

# Terminal 2: Forward webhooks
stripe listen --forward-to localhost:3000/api/auth/stripe-webhook

๐Ÿ“ Usage Examples

Client-Side Integration

SPA (Firebase Functions)

import { getFunctions, httpsCallable } from 'firebase/functions';
import { useAuth } from '@donotdev/auth';

function CheckoutButton({ priceId }: { priceId: string }) {
  const { user } = useAuth();
  const functions = getFunctions();

  const handleCheckout = async () => {
    // Direct Stripe integration
    const stripe = await loadStripe(process.env.VITE_STRIPE_PUBLISHABLE_KEY);
    const { error } = await stripe.redirectToCheckout({
      priceId,
      userId: user.uid,
      userEmail: user.email,
    });

    window.location.href = result.data.sessionUrl;
  };

  return (
    <button onClick={handleCheckout}>
      Subscribe
    </button>
  );
}

Next.js (Vercel Functions)

import { useAuth } from '@donotdev/auth';

function CheckoutButton({ priceId }: { priceId: string }) {
  const { user } = useAuth();

  const handleCheckout = async () => {
    const response = await fetch('/api/auth/create-checkout-session', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${await user.getIdToken()}`,
      },
      body: JSON.stringify({
        priceId,
        userId: user.uid,
        userEmail: user.email,
      }),
    });

    const { sessionUrl } = await response.json();
    window.location.href = sessionUrl;
  };

  return (
    <button onClick={handleCheckout}>
      Subscribe
    </button>
  );
}

๐Ÿšจ Security Notes

Firebase Functions

  • Uses Firebase Admin SDK for token verification
  • Automatic user authentication through callable functions
  • Environment variables managed through Firebase config

Vercel Functions

  • Uses Firebase Admin SDK with service account credentials
  • Proper ID token verification using auth.verifyIdToken()
  • Service account credentials managed through Vercel environment variables
  • All authentication is production-ready and secure

๐Ÿ“š API Reference

Shared Types

  • SubscriptionTier: 'free' | 'pro' | 'ai'
  • SubscriptionStatus: Stripe subscription statuses
  • SubscriptionClaims: Firebase custom claims structure
  • CreateCheckoutSessionRequest: Checkout session parameters
  • RefreshSubscriptionRequest: Subscription refresh parameters

Shared Utilities

  • updateUserSubscription(): Updates Firebase custom claims
  • cancelUserSubscription(): Resets user to free tier
  • getUserSubscription(): Gets user's current subscription
  • getTierFromPriceId(): Maps Stripe price to tier
  • assertAuthenticated(): Validates user authentication
  • DoNotDevError: Custom error class

๐ŸŽฏ Best Practices

  1. Function Design:

    • Keep simple functions simple (direct implementation)
    • Use shared algorithms only for complex business logic
    • One file per function for maintainability
  2. Utils Organization:

    • Internal utils: Functions-only utilities
    • External utils: Framework-wide utilities
    • Clear separation of concerns
  3. Error Handling:

    • Use handleError() for consistent error responses
    • Use DoNotDevError for custom error types
  4. Environment Variables:

    • Always use environment variables for secrets
    • Platform-specific configuration in constants files
  5. Security:

    • Verify Firebase tokens properly in production
    • Use webhook secrets for security
    • Handle authentication consistently
  6. Testing:

    • Test both platforms with the same business logic
    • Test shared algorithms independently
  7. Scaffolding:

    • Functions should work out of the box when scaffolded
    • Allow easy customization and removal
    • Maintain clear interfaces for provider abstraction

๐Ÿ”ง Troubleshooting

Common Issues

  1. "Missing Firebase Admin SDK":

    • Ensure Firebase is initialized in shared utilities
    • Check environment variables
  2. "Webhook signature verification failed":

    • Verify webhook secret matches Stripe dashboard
    • Check raw body handling
  3. "Permission denied":

    • Verify Firebase Auth token is valid
    • Check user has correct permissions
  4. "Subscription not updating":

    • Check webhook URL is correctly configured
    • Verify Firebase custom claims are being set

Debug Mode

Enable debug logging:

# Firebase
export DEBUG=firebase-functions:*

# Vercel
export DEBUG=1

๐Ÿ“„ License

All rights reserved. The DoNotDev framework and its premium features are the exclusive property of Ambroise Park Consulting.

ยฉ Ambroise Park Consulting โ€“ 2025