Skip to main content

Stripe Integration Guide

Overview

Pivot integrates with Stripe to manage subscription billing for companies. This integration automatically tracks subscription status changes and updates company records in Firebase Realtime Database.

What Does the Stripe Integration Do?

  • Track subscription lifecycle: Monitor when subscriptions are created, updated, cancelled, or renewed
  • Update company status: Automatically enable/disable company access based on subscription status
  • Sync subscription data: Keep company records up-to-date with current subscription information
  • Handle payment events: Process subscription-related payment events from Stripe

Architecture

System Overview

Complete Subscription Lifecycle

Data Flow - New Subscription

Data Flow - Payment Failure & Company Lock

Data Flow - Payment Success & Company Unlock

Environment Configuration

Pivot uses three environments for Stripe integration, each with isolated Stripe accounts and Firebase projects.

Environment Mapping

EnvironmentFirebase ProjectStripe AccountWebhook URL
Developmentpivot-dev-59310Test Modehttps://us-central1-pivot-dev-59310.cloudfunctions.net/stripeWebHookHandler
Stagingpivot-not-production-projectTest Modehttps://us-central1-pivot-not-production-project.cloudfunctions.net/stripeWebHookHandler
Productionpivot-incLive Modehttps://us-central1-pivot-inc.cloudfunctions.net/stripeWebHookHandler

Environment Secrets

Each environment requires two Stripe credentials stored as GitHub Actions secrets:

  1. STRIPE_API_KEY: Used by Firebase Functions to make API calls to Stripe
  2. STRIPE_WEBHOOK_SECRET: Used to verify webhook signatures

Development Environment

# GitHub Secret: FIREBASE_ENV_DEV
STRIPE_API_KEY=sk_test_51SUqwV... # Full secret key
STRIPE_WEBHOOK_SECRET=whsec_0cXpeHoO...

Staging Environment

# GitHub Secret: FIREBASE_ENV_STAGING
STRIPE_API_KEY=sk_test_51SVxTY... # Full secret key
STRIPE_WEBHOOK_SECRET=whsec_BcDX8iXm...

Production Environment

# GitHub Secret: FIREBASE_ENV_PRODUCTION
STRIPE_API_KEY=rk_live_51QFcV3... # Restricted key (read-only)
STRIPE_WEBHOOK_SECRET=whsec_XGRKhEIt...

Security Note: Production uses a restricted API key with only customers:read and subscriptions:read permissions for enhanced security.

Webhook Configuration

Webhook Events

The integration listens to these Stripe events:

  • customer.subscription.created - New subscription created
  • customer.subscription.updated - Subscription modified (plan change, renewal, etc.)
  • customer.subscription.deleted - Subscription cancelled or expired

Webhook Setup Process

Webhooks are configured using the create-webhook.sh script in the pivot-devops repository:

# Location: pivot-devops/github/stripe/create-webhook.sh

# Example: Create staging webhook
./create-webhook.sh \
"sk_test_xxx" \
"https://us-central1-pivot-not-production-project.cloudfunctions.net/stripeWebHookHandler" \
customer.subscription.created \
customer.subscription.updated

The script:

  1. Creates a webhook endpoint in Stripe
  2. Configures the events to listen for
  3. Returns the webhook signing secret (whsec_...)
  4. This secret must be added to the environment variables

Webhook Security

Each webhook request includes a signature in the Stripe-Signature header. The function verifies this using STRIPE_WEBHOOK_SECRET to ensure the request came from Stripe.

Data Updates

Company Record Structure

When a subscription event occurs, the following company data is updated in Firebase:

interface Company {
// Stripe Integration Fields
stripeSubscriptionId: string; // Stripe subscription ID (e.g., "sub_1ABC123")
subscriptionStatus: string; // Subscription status from Stripe
isSuspended: boolean | null; // null = active, true = locked

// Other company fields...
name: string;
email: string;
// ...
}

Subscription Status Values

StatusDescriptionCompany AccessisSuspended Value
activeSubscription is active and paidUnlockednull
trialingFree trial periodUnlockednull
past_duePayment failed, retryingLockedtrue
canceledSubscription cancelledLockedtrue
incomplete_expiredInitial payment failed after retry periodLockedtrue

Update Logic

Database Path

Company subscription data is stored directly on the company record:

/Companies/{companyId}/

Example - Active Subscription:

{
"Companies": {
"-ABC123": {
"name": "Acme Restaurant",
"email": "billing@acme.com",
"stripeSubscriptionId": "sub_1ABC123xyz",
"subscriptionStatus": "active",
"isSuspended": null
}
}
}

Example - Failed Payment (Company Locked):

{
"Companies": {
"-ABC123": {
"name": "Acme Restaurant",
"email": "billing@acme.com",
"stripeSubscriptionId": "sub_1ABC123xyz",
"subscriptionStatus": "canceled",
"isSuspended": true
}
}
}

Implementation Details

Cloud Function

Location: pivot/functions/integrations/stripe/stripe-webhook-handler.ts

Function Name: stripeWebHookHandler

Deployment: Automatically deployed via GitHub Actions when code is merged to main/staging branches

Local Testing

For local development, use the Stripe CLI to forward webhooks:

# Start Firebase emulators
firebase emulators:start --only functions,database

# In another terminal, forward webhooks
stripe listen --forward-to \
http://localhost:5001/pivot-dev-59310/us-central1/stripeWebHookHandler

# Copy the webhook signing secret (whsec_...) displayed
# Update functions/integrations/stripe/stripe-webhook-handler.ts temporarily

Trigger test events:

stripe trigger customer.subscription.created
stripe trigger customer.subscription.updated
stripe trigger customer.subscription.deleted

Managing Stripe Credentials

Creating Restricted API Keys (Production)

For production, use restricted keys with minimal permissions:

Required Permissions:

  • customers → Read
  • subscriptions → Read

Steps:

  1. Go to Stripe Dashboard → Developers → API Keys
  2. Click "Create restricted key"
  3. Name: Pivot Production - Subscription Webhook Handler
  4. Set permissions:
    • Customers: Read
    • Subscriptions: Read
  5. Save and copy the rk_live_... key

Note: Restricted keys cannot be created via API - they must be created through the Stripe Dashboard.

Updating Secrets in pivot-devops

Secrets are managed in the pivot-devops repository:

pivot-devops/github/secrets/
├── env.dev # Development secrets
├── env.default # Staging secrets
└── env.production # Production secrets

After updating these files, apply with Terraform:

cd pivot-devops/github
terraform apply -target=github_actions_secret.firebase_env_production

This updates the GitHub Actions secrets that are used during deployment.

Monitoring and Troubleshooting

Checking Webhook Status

In Stripe Dashboard:

  1. Go to Developers → Webhooks
  2. Click on the webhook endpoint
  3. View recent deliveries and their status

Common Issues

Webhook Signature Verification Failed

Symptoms: Stripe shows 401 responses

Causes:

  • Wrong STRIPE_WEBHOOK_SECRET in environment variables
  • Environment variable not deployed to Firebase Functions

Solution:

  1. Verify the secret in pivot-devops/github/secrets/env.*
  2. Redeploy functions: firebase deploy --only functions:stripeWebHookHandler

Company Not Found

Symptoms: Function logs show "Company not found" errors

Causes:

  • Company record missing customerId field in database
  • Mismatch between Stripe customer ID and database

Solution:

  1. Check company record in Firebase Console
  2. Verify customerId matches Stripe customer ID
  3. Manually update if needed: /companies/{companyId}/customerId

Subscription Not Updating

Symptoms: Company status doesn't change when subscription changes

Causes:

  • Function not deployed
  • Wrong Stripe API key (different account)
  • Webhook not configured in Stripe

Solution:

  1. Check function logs in Firebase Console
  2. Verify webhook is configured for correct environment
  3. Test webhook delivery from Stripe Dashboard

Logs

View function logs:

# Firebase Console
firebase functions:log --only stripeWebHookHandler

# Or in Firebase Console UI:
# Functions → stripeWebHookHandler → Logs tab

Look for:

  • [Stripe Webhook] Received webhook request
  • [Stripe Webhook] Event verified: {...}
  • [Stripe Webhook] Processing subscription: {...}
  • [Stripe Webhook] Database updated successfully

Deployment

GitHub Actions Workflow

Deployment is automated via GitHub Actions in the pivot repository:

Workflow File: .github/workflows/deploy.yml

Deployment Targets:

  • Dev: Merges to dev branch → Deploy to pivot-dev-59310
  • Staging: Merges to staging branch → Deploy to pivot-not-production-project
  • Production: Merges to main branch → Deploy to pivot-inc

The workflow:

  1. Checks out code
  2. Loads environment secrets from GitHub
  3. Deploys functions to Firebase
  4. Stripe credentials are injected as environment variables

Manual Deployment

If needed, deploy manually:

# Ensure you're using the correct Firebase project
firebase use staging # or production

# Deploy only the Stripe webhook function
firebase deploy --only functions:stripeWebHookHandler

Security Best Practices

API Key Security

  • DO: Use restricted keys in production
  • DO: Rotate keys periodically
  • DO: Store keys in GitHub Secrets, never in code
  • DON'T: Commit keys to version control
  • DON'T: Use production keys in development
  • DON'T: Share keys in Slack or email

Webhook Security

  • DO: Always verify webhook signatures
  • DO: Use HTTPS endpoints only
  • DO: Keep webhook secrets secure
  • DON'T: Skip signature verification
  • DON'T: Expose webhook secrets in logs

Reference

Scripts

Located in pivot-devops/github/stripe/:

  • create-webhook.sh - Creates webhook endpoints via Stripe API
  • create-restricted-key.sh - Documentation for creating restricted keys (requires Dashboard)

Getting Help

For issues:

  • Stripe API errors: Check Stripe Dashboard → Developers → Logs
  • Webhook delivery failures: Check Stripe Dashboard → Developers → Webhooks → Event log
  • Function errors: Check Firebase Console → Functions → Logs
  • Deployment issues: Check GitHub Actions workflow logs

Contact:

  • Stripe account issues: Account owner or billing admin
  • Technical issues: Development team
  • Security concerns: DevOps team